From fc01a70b65f770667ccf45870cde4e7f06036f1f Mon Sep 17 00:00:00 2001 From: Daniel Hyles Date: Sun, 8 Nov 2020 12:46:34 +1100 Subject: [PATCH] Hbridge christmas light (#1251) * Hbridge Christmas light component Can be used for Christmas lights that use 2 wires to run 2 different strings of lights using a hbridge driver. * Add Test NOTE: I am unable to test this via the docker image * Update hbridge_light_output.h * Update hbridge_light_output.h * Update hbridge_light_output.h * Update light.py * Fixed duty as white value bug fixed * lint changes * Name case change * thanks lint --- esphome/components/hbridge/__init__.py | 0 .../components/hbridge/hbridge_light_output.h | 76 +++++++++++++++++++ esphome/components/hbridge/light.py | 24 ++++++ tests/test3.yaml | 7 ++ 4 files changed, 107 insertions(+) create mode 100644 esphome/components/hbridge/__init__.py create mode 100644 esphome/components/hbridge/hbridge_light_output.h create mode 100644 esphome/components/hbridge/light.py diff --git a/esphome/components/hbridge/__init__.py b/esphome/components/hbridge/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/hbridge/hbridge_light_output.h b/esphome/components/hbridge/hbridge_light_output.h new file mode 100644 index 0000000000..03a5b3a88c --- /dev/null +++ b/esphome/components/hbridge/hbridge_light_output.h @@ -0,0 +1,76 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/output/float_output.h" +#include "esphome/components/light/light_output.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace hbridge { + +// Using PollingComponent as the updates are more consistent and reduces flickering +class HBridgeLightOutput : public PollingComponent, public light::LightOutput { + public: + HBridgeLightOutput() : PollingComponent(1) {} + + void set_pina_pin(output::FloatOutput *pina_pin) { pina_pin_ = pina_pin; } + void set_pinb_pin(output::FloatOutput *pinb_pin) { pinb_pin_ = pinb_pin; } + + light::LightTraits get_traits() override { + auto traits = light::LightTraits(); + traits.set_supports_brightness(true); // Dimming + traits.set_supports_rgb(false); + traits.set_supports_rgb_white_value(true); // hbridge color + traits.set_supports_color_temperature(false); + return traits; + } + + void setup() override { this->forward_direction_ = false; } + + void update() override { + // This method runs around 60 times per second + // We cannot do the PWM ourselves so we are reliant on the hardware PWM + if (!this->forward_direction_) { // First LED Direction + this->pinb_pin_->set_level(this->duty_off_); + this->pina_pin_->set_level(this->pina_duty_); + this->forward_direction_ = true; + } else { // Second LED Direction + this->pina_pin_->set_level(this->duty_off_); + this->pinb_pin_->set_level(this->pinb_duty_); + this->forward_direction_ = false; + } + } + + float get_setup_priority() const override { return setup_priority::HARDWARE; } + + void write_state(light::LightState *state) override { + float bright; + state->current_values_as_brightness(&bright); + + state->set_gamma_correct(0); + float red, green, blue, white; + state->current_values_as_rgbw(&red, &green, &blue, &white); + + if ((white / bright) > 0.55) { + this->pina_duty_ = (bright * (1 - (white / bright))); + this->pinb_duty_ = bright; + } else if (white < 0.45) { + this->pina_duty_ = bright; + this->pinb_duty_ = white; + } else { + this->pina_duty_ = bright; + this->pinb_duty_ = bright; + } + } + + protected: + output::FloatOutput *pina_pin_; + output::FloatOutput *pinb_pin_; + float pina_duty_ = 0; + float pinb_duty_ = 0; + float duty_off_ = 0; + bool forward_direction_ = false; +}; + +} // namespace hbridge +} // namespace esphome diff --git a/esphome/components/hbridge/light.py b/esphome/components/hbridge/light.py new file mode 100644 index 0000000000..abaf7152d7 --- /dev/null +++ b/esphome/components/hbridge/light.py @@ -0,0 +1,24 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import light, output +from esphome.const import CONF_OUTPUT_ID, CONF_PIN_A, CONF_PIN_B + +hbridge_ns = cg.esphome_ns.namespace('hbridge') +HBridgeLightOutput = hbridge_ns.class_('HBridgeLightOutput', cg.PollingComponent, light.LightOutput) + +CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({ + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(HBridgeLightOutput), + cv.Required(CONF_PIN_A): cv.use_id(output.FloatOutput), + cv.Required(CONF_PIN_B): cv.use_id(output.FloatOutput), +}) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) + yield cg.register_component(var, config) + yield light.register_light(var, config) + + hside = yield cg.get_variable(config[CONF_PIN_A]) + cg.add(var.set_pina_pin(hside)) + lside = yield cg.get_variable(config[CONF_PIN_B]) + cg.add(var.set_pinb_pin(lside)) diff --git a/tests/test3.yaml b/tests/test3.yaml index bdc93a036f..df103a4e14 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -761,6 +761,9 @@ output: id: out pin: D3 frequency: 50Hz + - platform: esp8266_pwm + id: out2 + pin: D4 - platform: custom type: binary lambda: |- @@ -809,6 +812,10 @@ light: uart_id: adalight_uart - e131: universe: 1 + - platform: hbridge + name: Icicle Lights + pin_a: out + pin_b: out2 servo: id: my_servo