forked from esphome/esphome
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add H-Bridge switch component (esphome#7421)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
- Loading branch information
Showing
11 changed files
with
313 additions
and
186 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from esphome import pins | ||
import esphome.codegen as cg | ||
from esphome.components import switch | ||
import esphome.config_validation as cv | ||
from esphome.const import CONF_OPTIMISTIC, CONF_PULSE_LENGTH, CONF_WAIT_TIME | ||
|
||
from .. import hbridge_ns | ||
|
||
HBridgeSwitch = hbridge_ns.class_("HBridgeSwitch", switch.Switch, cg.Component) | ||
|
||
CODEOWNERS = ["@dwmw2"] | ||
|
||
CONF_OFF_PIN = "off_pin" | ||
CONF_ON_PIN = "on_pin" | ||
|
||
CONFIG_SCHEMA = ( | ||
switch.switch_schema(HBridgeSwitch) | ||
.extend( | ||
{ | ||
cv.Required(CONF_ON_PIN): pins.gpio_output_pin_schema, | ||
cv.Required(CONF_OFF_PIN): pins.gpio_output_pin_schema, | ||
cv.Optional( | ||
CONF_PULSE_LENGTH, default="100ms" | ||
): cv.positive_time_period_milliseconds, | ||
cv.Optional(CONF_WAIT_TIME): cv.positive_time_period_milliseconds, | ||
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, | ||
} | ||
) | ||
.extend(cv.COMPONENT_SCHEMA) | ||
) | ||
|
||
|
||
async def to_code(config): | ||
var = await switch.new_switch(config) | ||
await cg.register_component(var, config) | ||
|
||
on_pin = await cg.gpio_pin_expression(config[CONF_ON_PIN]) | ||
cg.add(var.set_on_pin(on_pin)) | ||
off_pin = await cg.gpio_pin_expression(config[CONF_OFF_PIN]) | ||
cg.add(var.set_off_pin(off_pin)) | ||
cg.add(var.set_pulse_length(config[CONF_PULSE_LENGTH])) | ||
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) | ||
if wait_time := config.get(CONF_WAIT_TIME): | ||
cg.add(var.set_wait_time(wait_time)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
#include "hbridge_switch.h" | ||
#include "esphome/core/log.h" | ||
|
||
#include <cinttypes> | ||
|
||
namespace esphome { | ||
namespace hbridge { | ||
|
||
static const char *const TAG = "switch.hbridge"; | ||
|
||
float HBridgeSwitch::get_setup_priority() const { return setup_priority::HARDWARE; } | ||
void HBridgeSwitch::setup() { | ||
ESP_LOGCONFIG(TAG, "Setting up H-Bridge Switch '%s'...", this->name_.c_str()); | ||
|
||
optional<bool> initial_state = this->get_initial_state_with_restore_mode().value_or(false); | ||
|
||
// Like GPIOSwitch does, set the pin state both before and after pin setup() | ||
this->on_pin_->digital_write(false); | ||
this->on_pin_->setup(); | ||
this->on_pin_->digital_write(false); | ||
|
||
this->off_pin_->digital_write(false); | ||
this->off_pin_->setup(); | ||
this->off_pin_->digital_write(false); | ||
|
||
if (initial_state.has_value()) | ||
this->write_state(initial_state); | ||
} | ||
|
||
void HBridgeSwitch::dump_config() { | ||
LOG_SWITCH("", "H-Bridge Switch", this); | ||
LOG_PIN(" On Pin: ", this->on_pin_); | ||
LOG_PIN(" Off Pin: ", this->off_pin_); | ||
ESP_LOGCONFIG(TAG, " Pulse length: %" PRId32 " ms", this->pulse_length_); | ||
if (this->wait_time_) | ||
ESP_LOGCONFIG(TAG, " Wait time %" PRId32 " ms", this->wait_time_); | ||
} | ||
|
||
void HBridgeSwitch::write_state(bool state) { | ||
this->desired_state_ = state; | ||
if (!this->timer_running_) | ||
this->timer_fn_(); | ||
} | ||
|
||
void HBridgeSwitch::timer_fn_() { | ||
uint32_t next_timeout = 0; | ||
|
||
while ((uint8_t) this->desired_state_ != this->relay_state_) { | ||
switch (this->relay_state_) { | ||
case RELAY_STATE_ON: | ||
case RELAY_STATE_OFF: | ||
case RELAY_STATE_UNKNOWN: | ||
if (this->desired_state_) { | ||
this->on_pin_->digital_write(true); | ||
this->relay_state_ = RELAY_STATE_SWITCHING_ON; | ||
} else { | ||
this->off_pin_->digital_write(true); | ||
this->relay_state_ = RELAY_STATE_SWITCHING_OFF; | ||
} | ||
next_timeout = this->pulse_length_; | ||
if (!this->optimistic_) | ||
this->publish_state(this->desired_state_); | ||
break; | ||
|
||
case RELAY_STATE_SWITCHING_ON: | ||
this->on_pin_->digital_write(false); | ||
this->relay_state_ = RELAY_STATE_ON; | ||
if (this->optimistic_) | ||
this->publish_state(true); | ||
next_timeout = this->wait_time_; | ||
break; | ||
|
||
case RELAY_STATE_SWITCHING_OFF: | ||
this->off_pin_->digital_write(false); | ||
this->relay_state_ = RELAY_STATE_OFF; | ||
if (this->optimistic_) | ||
this->publish_state(false); | ||
next_timeout = this->wait_time_; | ||
break; | ||
} | ||
|
||
if (next_timeout) { | ||
this->timer_running_ = true; | ||
this->set_timeout(next_timeout, [this]() { this->timer_fn_(); }); | ||
return; | ||
} | ||
|
||
// In the case where ON/OFF state has been reached but we need to | ||
// immediately change back again to reach desired_state_, we loop. | ||
} | ||
this->timer_running_ = false; | ||
} | ||
|
||
} // namespace hbridge | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#pragma once | ||
|
||
#include "esphome/core/component.h" | ||
#include "esphome/core/hal.h" | ||
#include "esphome/components/switch/switch.h" | ||
|
||
#include <vector> | ||
|
||
namespace esphome { | ||
namespace hbridge { | ||
|
||
enum RelayState : uint8_t { | ||
RELAY_STATE_OFF = 0, | ||
RELAY_STATE_ON = 1, | ||
RELAY_STATE_SWITCHING_ON = 2, | ||
RELAY_STATE_SWITCHING_OFF = 3, | ||
RELAY_STATE_UNKNOWN = 4, | ||
}; | ||
|
||
class HBridgeSwitch : public switch_::Switch, public Component { | ||
public: | ||
void set_on_pin(GPIOPin *pin) { this->on_pin_ = pin; } | ||
void set_off_pin(GPIOPin *pin) { this->off_pin_ = pin; } | ||
void set_pulse_length(uint32_t pulse_length) { this->pulse_length_ = pulse_length; } | ||
void set_wait_time(uint32_t wait_time) { this->wait_time_ = wait_time; } | ||
void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } | ||
|
||
// ========== INTERNAL METHODS ========== | ||
// (In most use cases you won't need these) | ||
float get_setup_priority() const override; | ||
|
||
void setup() override; | ||
void dump_config() override; | ||
|
||
protected: | ||
void write_state(bool state) override; | ||
void timer_fn_(); | ||
|
||
bool timer_running_{false}; | ||
bool desired_state_{false}; | ||
RelayState relay_state_{RELAY_STATE_UNKNOWN}; | ||
GPIOPin *on_pin_{nullptr}; | ||
GPIOPin *off_pin_{nullptr}; | ||
uint32_t pulse_length_{0}; | ||
uint32_t wait_time_{0}; | ||
bool optimistic_{false}; | ||
}; | ||
|
||
} // namespace hbridge | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
output: | ||
- platform: ${pwm_platform} | ||
pin: ${output1_pin} | ||
id: gpio_output1 | ||
- platform: ${pwm_platform} | ||
pin: ${output2_pin} | ||
id: gpio_output2 | ||
- platform: ${pwm_platform} | ||
pin: ${output3_pin} | ||
id: gpio_output3 | ||
- platform: ${pwm_platform} | ||
pin: ${output4_pin} | ||
id: gpio_output4 | ||
|
||
light: | ||
- platform: hbridge | ||
name: Icicle Lights | ||
pin_a: gpio_output3 | ||
pin_b: gpio_output4 | ||
|
||
fan: | ||
- platform: hbridge | ||
id: fan_hbridge | ||
speed_count: 4 | ||
name: H-bridge Fan with Presets | ||
pin_a: gpio_output1 | ||
pin_b: gpio_output2 | ||
preset_modes: | ||
- Preset 1 | ||
- Preset 2 | ||
on_preset_set: | ||
then: | ||
- logger.log: Preset mode was changed! | ||
|
||
switch: | ||
- platform: hbridge | ||
id: switch_hbridge | ||
on_pin: ${hbridge_on_pin} | ||
off_pin: ${hbridge_off_pin} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,17 @@ | ||
output: | ||
- platform: ledc | ||
pin: 14 | ||
id: gpio_output1 | ||
- platform: ledc | ||
pin: 15 | ||
id: gpio_output2 | ||
- platform: ledc | ||
pin: 12 | ||
id: gpio_output3 | ||
- platform: ledc | ||
pin: 13 | ||
id: gpio_output4 | ||
substitutions: | ||
pwm_platform: ledc | ||
output1_pin: "14" | ||
output2_pin: "15" | ||
output3_pin: "12" | ||
output4_pin: "13" | ||
hbridge_on_pin: "4" | ||
hbridge_off_pin: "5" | ||
|
||
light: | ||
- platform: hbridge | ||
name: Icicle Lights | ||
pin_a: gpio_output3 | ||
pin_b: gpio_output4 | ||
packages: | ||
common: !include common.yaml | ||
|
||
fan: | ||
- platform: hbridge | ||
id: fan_hbridge | ||
speed_count: 4 | ||
name: H-bridge Fan with Presets | ||
pin_a: gpio_output1 | ||
pin_b: gpio_output2 | ||
preset_modes: | ||
- Preset 1 | ||
- Preset 2 | ||
on_preset_set: | ||
then: | ||
- logger.log: Preset mode was changed! | ||
switch: | ||
- id: !extend switch_hbridge | ||
pulse_length: 60ms | ||
wait_time: 10ms | ||
optimistic: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,16 @@ | ||
output: | ||
- platform: ledc | ||
pin: 4 | ||
id: gpio_output1 | ||
- platform: ledc | ||
pin: 5 | ||
id: gpio_output2 | ||
- platform: ledc | ||
pin: 6 | ||
id: gpio_output3 | ||
- platform: ledc | ||
pin: 7 | ||
id: gpio_output4 | ||
substitutions: | ||
pwm_platform: "ledc" | ||
output1_pin: "4" | ||
output2_pin: "5" | ||
output3_pin: "6" | ||
output4_pin: "7" | ||
hbridge_on_pin: "2" | ||
hbridge_off_pin: "3" | ||
|
||
light: | ||
- platform: hbridge | ||
name: Icicle Lights | ||
pin_a: gpio_output3 | ||
pin_b: gpio_output4 | ||
packages: | ||
common: !include common.yaml | ||
|
||
fan: | ||
- platform: hbridge | ||
id: fan_hbridge | ||
speed_count: 4 | ||
name: H-bridge Fan with Presets | ||
pin_a: gpio_output1 | ||
pin_b: gpio_output2 | ||
preset_modes: | ||
- Preset 1 | ||
- Preset 2 | ||
on_preset_set: | ||
then: | ||
- logger.log: Preset mode was changed! | ||
switch: | ||
- id: !extend switch_hbridge | ||
wait_time: 10ms | ||
optimistic: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,15 @@ | ||
output: | ||
- platform: ledc | ||
pin: 4 | ||
id: gpio_output1 | ||
- platform: ledc | ||
pin: 5 | ||
id: gpio_output2 | ||
- platform: ledc | ||
pin: 6 | ||
id: gpio_output3 | ||
- platform: ledc | ||
pin: 7 | ||
id: gpio_output4 | ||
substitutions: | ||
pwm_platform: "ledc" | ||
output1_pin: "4" | ||
output2_pin: "5" | ||
output3_pin: "6" | ||
output4_pin: "7" | ||
hbridge_on_pin: "2" | ||
hbridge_off_pin: "3" | ||
|
||
light: | ||
- platform: hbridge | ||
name: Icicle Lights | ||
pin_a: gpio_output3 | ||
pin_b: gpio_output4 | ||
packages: | ||
common: !include common.yaml | ||
|
||
fan: | ||
- platform: hbridge | ||
id: fan_hbridge | ||
speed_count: 4 | ||
name: H-bridge Fan with Presets | ||
pin_a: gpio_output1 | ||
pin_b: gpio_output2 | ||
preset_modes: | ||
- Preset 1 | ||
- Preset 2 | ||
on_preset_set: | ||
then: | ||
- logger.log: Preset mode was changed! | ||
switch: | ||
- id: !extend switch_hbridge | ||
pulse_length: 60ms |
Oops, something went wrong.