mirror of
https://github.com/esphome/esphome.git
synced 2024-11-21 22:48:10 +01:00
Add H-Bridge fan component (#2212)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
37f322585e
commit
03190611bb
8 changed files with 222 additions and 1 deletions
|
@ -52,6 +52,8 @@ esphome/components/globals/* @esphome/core
|
||||||
esphome/components/gpio/* @esphome/core
|
esphome/components/gpio/* @esphome/core
|
||||||
esphome/components/gps/* @coogle
|
esphome/components/gps/* @coogle
|
||||||
esphome/components/havells_solar/* @sourabhjaiswal
|
esphome/components/havells_solar/* @sourabhjaiswal
|
||||||
|
esphome/components/hbridge/fan/* @WeekendWarrior
|
||||||
|
esphome/components/hbridge/light/* @DotNetDann
|
||||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||||
esphome/components/homeassistant/* @OttoWinter
|
esphome/components/homeassistant/* @OttoWinter
|
||||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
|
||||||
|
hbridge_ns = cg.esphome_ns.namespace("hbridge")
|
70
esphome/components/hbridge/fan/__init__.py
Normal file
70
esphome/components/hbridge/fan/__init__.py
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome import automation
|
||||||
|
from esphome.automation import maybe_simple_id
|
||||||
|
from esphome.components import fan, output
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_DECAY_MODE,
|
||||||
|
CONF_SPEED_COUNT,
|
||||||
|
CONF_PIN_A,
|
||||||
|
CONF_PIN_B,
|
||||||
|
CONF_ENABLE_PIN,
|
||||||
|
)
|
||||||
|
from .. import hbridge_ns
|
||||||
|
|
||||||
|
|
||||||
|
CODEOWNERS = ["@WeekendWarrior"]
|
||||||
|
|
||||||
|
|
||||||
|
HBridgeFan = hbridge_ns.class_("HBridgeFan", fan.FanState)
|
||||||
|
|
||||||
|
DecayMode = hbridge_ns.enum("DecayMode")
|
||||||
|
DECAY_MODE_OPTIONS = {
|
||||||
|
"SLOW": DecayMode.DECAY_MODE_SLOW,
|
||||||
|
"FAST": DecayMode.DECAY_MODE_FAST,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Actions
|
||||||
|
BrakeAction = hbridge_ns.class_("BrakeAction", automation.Action)
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_ID): cv.declare_id(HBridgeFan),
|
||||||
|
cv.Required(CONF_PIN_A): cv.use_id(output.FloatOutput),
|
||||||
|
cv.Required(CONF_PIN_B): cv.use_id(output.FloatOutput),
|
||||||
|
cv.Optional(CONF_DECAY_MODE, default="SLOW"): cv.enum(
|
||||||
|
DECAY_MODE_OPTIONS, upper=True
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1),
|
||||||
|
cv.Optional(CONF_ENABLE_PIN): cv.use_id(output.FloatOutput),
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"fan.hbridge.brake",
|
||||||
|
BrakeAction,
|
||||||
|
maybe_simple_id({cv.Required(CONF_ID): cv.use_id(HBridgeFan)}),
|
||||||
|
)
|
||||||
|
async def fan_hbridge_brake_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(
|
||||||
|
config[CONF_ID],
|
||||||
|
config[CONF_SPEED_COUNT],
|
||||||
|
config[CONF_DECAY_MODE],
|
||||||
|
)
|
||||||
|
await fan.register_fan(var, config)
|
||||||
|
pin_a_ = await cg.get_variable(config[CONF_PIN_A])
|
||||||
|
cg.add(var.set_pin_a(pin_a_))
|
||||||
|
pin_b_ = await cg.get_variable(config[CONF_PIN_B])
|
||||||
|
cg.add(var.set_pin_b(pin_b_))
|
||||||
|
|
||||||
|
if CONF_ENABLE_PIN in config:
|
||||||
|
enable_pin = await cg.get_variable(config[CONF_ENABLE_PIN])
|
||||||
|
cg.add(var.set_enable_pin(enable_pin))
|
85
esphome/components/hbridge/fan/hbridge_fan.cpp
Normal file
85
esphome/components/hbridge/fan/hbridge_fan.cpp
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#include "hbridge_fan.h"
|
||||||
|
#include "esphome/components/fan/fan_helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace hbridge {
|
||||||
|
|
||||||
|
static const char *const TAG = "fan.hbridge";
|
||||||
|
|
||||||
|
void HBridgeFan::set_hbridge_levels_(float a_level, float b_level) {
|
||||||
|
this->pin_a_->set_level(a_level);
|
||||||
|
this->pin_b_->set_level(b_level);
|
||||||
|
ESP_LOGD(TAG, "Setting speed: a: %.2f, b: %.2f", a_level, b_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
// constant IN1/IN2, PWM on EN => power control, fast current decay
|
||||||
|
// constant IN1/EN, PWM on IN2 => power control, slow current decay
|
||||||
|
void HBridgeFan::set_hbridge_levels_(float a_level, float b_level, float enable) {
|
||||||
|
this->pin_a_->set_level(a_level);
|
||||||
|
this->pin_b_->set_level(b_level);
|
||||||
|
this->enable_->set_level(enable);
|
||||||
|
ESP_LOGD(TAG, "Setting speed: a: %.2f, b: %.2f, enable: %.2f", a_level, b_level, enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
fan::FanStateCall HBridgeFan::brake() {
|
||||||
|
ESP_LOGD(TAG, "Braking");
|
||||||
|
(this->enable_ == nullptr) ? this->set_hbridge_levels_(1.0f, 1.0f) : this->set_hbridge_levels_(1.0f, 1.0f, 1.0f);
|
||||||
|
return this->make_call().set_state(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HBridgeFan::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Fan '%s':", this->get_name().c_str());
|
||||||
|
if (this->get_traits().supports_oscillation()) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Oscillation: YES");
|
||||||
|
}
|
||||||
|
if (this->get_traits().supports_direction()) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Direction: YES");
|
||||||
|
}
|
||||||
|
if (this->decay_mode_ == DECAY_MODE_SLOW) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Decay Mode: Slow");
|
||||||
|
} else {
|
||||||
|
ESP_LOGCONFIG(TAG, " Decay Mode: Fast");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void HBridgeFan::setup() {
|
||||||
|
auto traits = fan::FanTraits(this->oscillating_ != nullptr, true, true, this->speed_count_);
|
||||||
|
this->set_traits(traits);
|
||||||
|
this->add_on_state_callback([this]() { this->next_update_ = true; });
|
||||||
|
}
|
||||||
|
void HBridgeFan::loop() {
|
||||||
|
if (!this->next_update_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->next_update_ = false;
|
||||||
|
|
||||||
|
float speed = 0.0f;
|
||||||
|
if (this->state) {
|
||||||
|
speed = static_cast<float>(this->speed) / static_cast<float>(this->speed_count_);
|
||||||
|
}
|
||||||
|
if (speed == 0.0f) { // off means idle
|
||||||
|
(this->enable_ == nullptr) ? this->set_hbridge_levels_(speed, speed)
|
||||||
|
: this->set_hbridge_levels_(speed, speed, speed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->direction == fan::FAN_DIRECTION_FORWARD) {
|
||||||
|
if (this->decay_mode_ == DECAY_MODE_SLOW) {
|
||||||
|
(this->enable_ == nullptr) ? this->set_hbridge_levels_(1.0f - speed, 1.0f)
|
||||||
|
: this->set_hbridge_levels_(1.0f - speed, 1.0f, 1.0f);
|
||||||
|
} else { // DECAY_MODE_FAST
|
||||||
|
(this->enable_ == nullptr) ? this->set_hbridge_levels_(0.0f, speed)
|
||||||
|
: this->set_hbridge_levels_(0.0f, 1.0f, speed);
|
||||||
|
}
|
||||||
|
} else { // fan::FAN_DIRECTION_REVERSE
|
||||||
|
if (this->decay_mode_ == DECAY_MODE_SLOW) {
|
||||||
|
(this->enable_ == nullptr) ? this->set_hbridge_levels_(1.0f, 1.0f - speed)
|
||||||
|
: this->set_hbridge_levels_(1.0f, 1.0f - speed, 1.0f);
|
||||||
|
} else { // DECAY_MODE_FAST
|
||||||
|
(this->enable_ == nullptr) ? this->set_hbridge_levels_(speed, 0.0f)
|
||||||
|
: this->set_hbridge_levels_(1.0f, 0.0f, speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace hbridge
|
||||||
|
} // namespace esphome
|
58
esphome/components/hbridge/fan/hbridge_fan.h
Normal file
58
esphome/components/hbridge/fan/hbridge_fan.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/components/output/binary_output.h"
|
||||||
|
#include "esphome/components/output/float_output.h"
|
||||||
|
#include "esphome/components/fan/fan_state.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace hbridge {
|
||||||
|
|
||||||
|
enum DecayMode {
|
||||||
|
DECAY_MODE_SLOW = 0,
|
||||||
|
DECAY_MODE_FAST = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
class HBridgeFan : public fan::FanState {
|
||||||
|
public:
|
||||||
|
HBridgeFan(int speed_count, DecayMode decay_mode) : speed_count_(speed_count), decay_mode_(decay_mode) {}
|
||||||
|
|
||||||
|
void set_pin_a(output::FloatOutput *pin_a) { pin_a_ = pin_a; }
|
||||||
|
void set_pin_b(output::FloatOutput *pin_b) { pin_b_ = pin_b; }
|
||||||
|
void set_enable_pin(output::FloatOutput *enable) { enable_ = enable; }
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
void loop() override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||||
|
|
||||||
|
fan::FanStateCall brake();
|
||||||
|
|
||||||
|
int get_speed_count() { return this->speed_count_; }
|
||||||
|
// update Hbridge without a triggered FanState change, eg. for acceleration/deceleration ramping
|
||||||
|
void internal_update() { this->next_update_ = true; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
output::FloatOutput *pin_a_;
|
||||||
|
output::FloatOutput *pin_b_;
|
||||||
|
output::FloatOutput *enable_{nullptr};
|
||||||
|
output::BinaryOutput *oscillating_{nullptr};
|
||||||
|
bool next_update_{true};
|
||||||
|
int speed_count_{};
|
||||||
|
DecayMode decay_mode_{DECAY_MODE_SLOW};
|
||||||
|
|
||||||
|
void set_hbridge_levels_(float a_level, float b_level);
|
||||||
|
void set_hbridge_levels_(float a_level, float b_level, float enable);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class BrakeAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
explicit BrakeAction(HBridgeFan *parent) : parent_(parent) {}
|
||||||
|
|
||||||
|
void play(Ts... x) override { this->parent_->brake(); }
|
||||||
|
|
||||||
|
HBridgeFan *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace hbridge
|
||||||
|
} // namespace esphome
|
|
@ -2,8 +2,10 @@ import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import light, output
|
from esphome.components import light, output
|
||||||
from esphome.const import CONF_OUTPUT_ID, CONF_PIN_A, CONF_PIN_B
|
from esphome.const import CONF_OUTPUT_ID, CONF_PIN_A, CONF_PIN_B
|
||||||
|
from .. import hbridge_ns
|
||||||
|
|
||||||
|
CODEOWNERS = ["@DotNetDann"]
|
||||||
|
|
||||||
hbridge_ns = cg.esphome_ns.namespace("hbridge")
|
|
||||||
HBridgeLightOutput = hbridge_ns.class_(
|
HBridgeLightOutput = hbridge_ns.class_(
|
||||||
"HBridgeLightOutput", cg.PollingComponent, light.LightOutput
|
"HBridgeLightOutput", cg.PollingComponent, light.LightOutput
|
||||||
)
|
)
|
|
@ -165,6 +165,7 @@ CONF_DAYS_OF_WEEK = "days_of_week"
|
||||||
CONF_DC_PIN = "dc_pin"
|
CONF_DC_PIN = "dc_pin"
|
||||||
CONF_DEASSERT_RTS_DTR = "deassert_rts_dtr"
|
CONF_DEASSERT_RTS_DTR = "deassert_rts_dtr"
|
||||||
CONF_DEBOUNCE = "debounce"
|
CONF_DEBOUNCE = "debounce"
|
||||||
|
CONF_DECAY_MODE = "decay_mode"
|
||||||
CONF_DECELERATION = "deceleration"
|
CONF_DECELERATION = "deceleration"
|
||||||
CONF_DEFAULT_MODE = "default_mode"
|
CONF_DEFAULT_MODE = "default_mode"
|
||||||
CONF_DEFAULT_TARGET_TEMPERATURE_HIGH = "default_target_temperature_high"
|
CONF_DEFAULT_TARGET_TEMPERATURE_HIGH = "default_target_temperature_high"
|
||||||
|
|
Loading…
Reference in a new issue