2024-05-12 21:02:52 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "esphome/core/automation.h"
|
2024-05-27 15:06:53 +00:00
|
|
|
#include "esphome/components/output/binary_output.h"
|
2024-06-04 06:42:37 +00:00
|
|
|
#include "Arduino.h"
|
2024-05-12 21:02:52 +00:00
|
|
|
|
|
|
|
static const char *const TAG = "chlorine_pump";
|
|
|
|
|
|
|
|
namespace esphome {
|
|
|
|
namespace chlorine_pump {
|
|
|
|
|
|
|
|
class ChlorinePump : public Component {
|
|
|
|
protected:
|
|
|
|
|
2024-05-24 14:00:34 +00:00
|
|
|
sensor::Sensor *sensor_;
|
2024-05-27 15:06:53 +00:00
|
|
|
output::BinaryOutput *pump_out;
|
2024-05-12 21:02:52 +00:00
|
|
|
int cycle_time;
|
|
|
|
int prop_band;
|
|
|
|
bool state = true;
|
|
|
|
unsigned long last_action;
|
|
|
|
bool disable_clock_ = false;
|
|
|
|
CallbackManager<void(bool, int)> callback_pump_;
|
|
|
|
CallbackManager<void(int, int)> callback_cycle_;
|
|
|
|
int tOn = 0;
|
|
|
|
int tOff = 0;
|
2024-05-27 12:43:10 +00:00
|
|
|
float target_ = -1;
|
2024-05-24 14:00:34 +00:00
|
|
|
float tOn_divider_;
|
2024-05-27 12:43:10 +00:00
|
|
|
std::function<float()> target_lambda_ = nullptr;
|
2024-05-12 21:02:52 +00:00
|
|
|
|
2024-06-04 06:42:37 +00:00
|
|
|
int min_on_time_ = 0;
|
|
|
|
int max_on_time_ = -1;
|
|
|
|
float on_time_multiplier_ = 1;
|
|
|
|
int on_time_offset_ = 0;
|
|
|
|
int ignore_max_x_below_target_ = -1;
|
|
|
|
|
2024-05-12 21:02:52 +00:00
|
|
|
public:
|
|
|
|
|
|
|
|
ChlorinePump(){
|
|
|
|
last_action = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void disable_clock(bool disable_clock){
|
|
|
|
this->disable_clock_ = disable_clock;
|
|
|
|
}
|
2024-05-24 14:00:34 +00:00
|
|
|
void set_sensor(sensor::Sensor *sensor){
|
2024-05-12 21:02:52 +00:00
|
|
|
this->sensor_ = sensor;
|
|
|
|
}
|
2024-05-24 14:00:34 +00:00
|
|
|
void set_pump_time_divivder(float pump_time_divider){
|
|
|
|
this->tOn_divider_ = pump_time_divider;
|
|
|
|
}
|
2024-05-27 15:06:53 +00:00
|
|
|
void set_pump_out(output::BinaryOutput *pump_out){
|
2024-05-12 21:02:52 +00:00
|
|
|
this->pump_out = pump_out;
|
|
|
|
}
|
|
|
|
void set_cycle_time(int time_in_sec){
|
|
|
|
this->cycle_time = time_in_sec;
|
|
|
|
}
|
|
|
|
void set_prop_band(int prop_band){
|
|
|
|
this->prop_band = prop_band;
|
|
|
|
}
|
2024-05-27 15:06:53 +00:00
|
|
|
void set_target(float target){
|
|
|
|
this->target_ = target;
|
2024-05-12 21:02:52 +00:00
|
|
|
}
|
|
|
|
|
2024-06-04 06:42:37 +00:00
|
|
|
// Cycle Time Modifiers
|
|
|
|
void set_min_on_time(int min_on_time){
|
|
|
|
this->min_on_time_ = min_on_time;
|
|
|
|
}
|
|
|
|
void set_max_on_time(int max_on_time){
|
|
|
|
this->max_on_time_ = max_on_time;
|
|
|
|
}
|
|
|
|
void set_time_multiplier(float time_multi){
|
|
|
|
this->on_time_multiplier_ = time_multi;
|
|
|
|
}
|
|
|
|
void set_time_offset(int offset){
|
|
|
|
this->on_time_offset_ = offset;
|
|
|
|
}
|
|
|
|
void set_ignore_max_x_below_target(int set_below){
|
|
|
|
this->ignore_max_x_below_target_ = set_below;
|
|
|
|
}
|
|
|
|
|
2024-05-12 21:02:52 +00:00
|
|
|
void setup() override {
|
|
|
|
last_action = millis();
|
|
|
|
if(disable_clock_ && sensor_ != nullptr){
|
2024-05-24 14:00:34 +00:00
|
|
|
sensor_->add_on_state_callback([=](float val) -> void {
|
2024-05-12 21:02:52 +00:00
|
|
|
this->tick_time(val);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2024-05-27 12:43:10 +00:00
|
|
|
|
|
|
|
void set_target_lambda(std::function<float()> callback){
|
|
|
|
target_lambda_ = std::move(callback);
|
|
|
|
}
|
|
|
|
|
2024-05-12 21:02:52 +00:00
|
|
|
void prime() {
|
|
|
|
tOn = 30;
|
|
|
|
tOff = 2;
|
|
|
|
this->pump_out->turn_on();
|
|
|
|
}
|
|
|
|
void start() {
|
|
|
|
this->state = true;
|
|
|
|
}
|
|
|
|
void stop() {
|
|
|
|
this->state = false;
|
|
|
|
tOn = 0;
|
|
|
|
tOff = 0;
|
|
|
|
this->pump_out->turn_off();
|
|
|
|
}
|
|
|
|
bool get_state(){
|
|
|
|
return state;
|
|
|
|
}
|
2024-05-27 12:43:10 +00:00
|
|
|
|
2024-05-12 21:02:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
void setMillisPrecies(unsigned long waitPeriod){
|
|
|
|
|
|
|
|
if(last_action + waitPeriod + waitPeriod > millis()){
|
|
|
|
last_action += waitPeriod;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
ESP_LOGW(TAG, "Reset millis(). Did the system experience a halt for some reason?");
|
|
|
|
last_action = millis();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int calculatePumpTimeSeconds(float last_mesurement){
|
2024-05-27 12:43:10 +00:00
|
|
|
|
|
|
|
float used_target = -1;
|
|
|
|
if(target_ != -1){
|
|
|
|
used_target = target_;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
used_target = target_lambda_();
|
|
|
|
}
|
2024-05-12 21:02:52 +00:00
|
|
|
|
2024-05-27 12:43:10 +00:00
|
|
|
if(last_mesurement >= used_target) return 0;
|
2024-05-12 21:02:52 +00:00
|
|
|
|
2024-05-27 12:43:10 +00:00
|
|
|
float currentVal = used_target - last_mesurement;
|
2024-05-12 21:02:52 +00:00
|
|
|
float proportionalValue = (currentVal / (float) prop_band);
|
|
|
|
float timeInSeconds = proportionalValue * (float) cycle_time;
|
|
|
|
|
|
|
|
if(timeInSeconds > cycle_time) timeInSeconds = cycle_time;
|
|
|
|
|
|
|
|
return (int) timeInSeconds;
|
|
|
|
}
|
|
|
|
|
2024-06-04 06:42:37 +00:00
|
|
|
int calculate_changed_on_time(int tOn, float last_mesurement){
|
|
|
|
|
|
|
|
if(ignore_max_x_below_target_ != -1 && (float)(target_ - ignore_max_x_below_target_) >= last_mesurement){
|
|
|
|
return cycle_time;
|
|
|
|
}
|
|
|
|
tOn = (int)((float) tOn * on_time_multiplier_);
|
|
|
|
tOn += on_time_offset_;
|
|
|
|
|
|
|
|
if(tOn < min_on_time_) tOn = min_on_time_;
|
|
|
|
if(max_on_time_ != -1 && tOn > max_on_time_) tOn = max_on_time_;
|
|
|
|
|
|
|
|
return tOn;
|
|
|
|
}
|
2024-05-12 21:02:52 +00:00
|
|
|
|
|
|
|
void add_on_pump_callback(std::function<void(bool, int)> &&callback){
|
|
|
|
this->callback_pump_.add(std::move(callback));
|
|
|
|
}
|
|
|
|
void add_on_cycle_callback(std::function<void(int, int)> &&callback){
|
|
|
|
this->callback_cycle_.add(std::move(callback));
|
|
|
|
}
|
|
|
|
|
|
|
|
void tick_time(float last_mesurement){
|
|
|
|
|
|
|
|
if(tOn == 0 && tOff == 0 && state){
|
|
|
|
int seconds = calculatePumpTimeSeconds(last_mesurement);
|
|
|
|
|
2024-06-04 06:42:37 +00:00
|
|
|
if(seconds != 0)
|
|
|
|
tOn = calculate_changed_on_time(seconds, last_mesurement);
|
|
|
|
else
|
|
|
|
tOn = 0;
|
2024-05-24 14:00:34 +00:00
|
|
|
tOff = cycle_time - tOn;
|
|
|
|
|
2024-05-12 21:02:52 +00:00
|
|
|
if(seconds == 0){
|
|
|
|
tOn = 0;
|
2024-05-24 14:00:34 +00:00
|
|
|
tOff = 360;
|
2024-05-12 21:02:52 +00:00
|
|
|
}
|
|
|
|
ESP_LOGD(TAG, "Time => tOn: %d, tOff: %d", tOn, tOff);
|
|
|
|
|
|
|
|
this->callback_cycle_.call(tOn, tOff);
|
|
|
|
}
|
|
|
|
else if(tOn > 0) {
|
|
|
|
this->callback_pump_.call(true, tOn);
|
|
|
|
--tOn;
|
|
|
|
pump_out->turn_on();
|
|
|
|
}
|
|
|
|
else if(tOff > 0) {
|
|
|
|
this->callback_pump_.call(false, tOff);
|
|
|
|
--tOff;
|
|
|
|
pump_out->turn_off();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void loop() override{
|
|
|
|
|
|
|
|
if(disable_clock_) return;
|
|
|
|
|
|
|
|
if(millis() - last_action > 1000){
|
|
|
|
|
2024-05-24 14:00:34 +00:00
|
|
|
if(sensor_->has_state())
|
|
|
|
tick_time(sensor_->get_state());
|
2024-05-12 21:02:52 +00:00
|
|
|
|
|
|
|
setMillisPrecies(1000);
|
|
|
|
}
|
|
|
|
}
|
2024-05-27 12:43:10 +00:00
|
|
|
|
|
|
|
void dump_config() override {
|
2024-05-27 15:06:53 +00:00
|
|
|
ESP_LOGCONFIG(TAG, "Chlorine_pump controller");
|
|
|
|
ESP_LOGCONFIG(TAG, " Ticker: %s", disable_clock_ ? "using on_value from sensor" : "using internal");
|
|
|
|
//LOG_BINARY_OUTPUT(pump_out);
|
|
|
|
ESP_LOGCONFIG(TAG, " Cycle Time: %ds", cycle_time);
|
|
|
|
ESP_LOGCONFIG(TAG, " Proportional Band: %d", prop_band);
|
|
|
|
if(target_ != -1) ESP_LOGCONFIG(TAG, " Target: %.0fmV", target_);
|
|
|
|
else ESP_LOGCONFIG(TAG, " Target: Using get_target lambda");
|
2024-05-27 12:43:10 +00:00
|
|
|
}
|
2024-05-12 21:02:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class ChlorSensorOutputTrigger : public Trigger<bool, int> {
|
|
|
|
public:
|
|
|
|
explicit ChlorSensorOutputTrigger(ChlorinePump *parent) {
|
|
|
|
parent->add_on_pump_callback([this](bool output_state, int value) { this->trigger(output_state, value); });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
class ChlorSensorCycleTrigger : public Trigger<int, int> {
|
|
|
|
public:
|
|
|
|
explicit ChlorSensorCycleTrigger(ChlorinePump *parent) {
|
|
|
|
parent->add_on_cycle_callback([this](int on_time, int off_time) { this->trigger(on_time, off_time); });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename... Ts> class ChlorinePrime : public Action<Ts...> {
|
|
|
|
public:
|
|
|
|
ChlorinePrime(ChlorinePump *pump) : pump_(pump) {}
|
|
|
|
|
|
|
|
void play(Ts... x) override { this->pump_->prime(); }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
ChlorinePump *pump_;
|
|
|
|
};
|
|
|
|
template<typename... Ts> class ChlorineStart : public Action<Ts...> {
|
|
|
|
public:
|
|
|
|
ChlorineStart(ChlorinePump *pump) : pump_(pump) {}
|
|
|
|
|
|
|
|
void play(Ts... x) override { this->pump_->start(); }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
ChlorinePump *pump_;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename... Ts> class ChlorineStop : public Action<Ts...> {
|
|
|
|
public:
|
|
|
|
ChlorineStop(ChlorinePump *pump) : pump_(pump) {}
|
|
|
|
|
|
|
|
void play(Ts... x) override { this->pump_->stop(); }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
ChlorinePump *pump_;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename... Ts> class ChlorineSetTarget : public Action<Ts...> {
|
|
|
|
public:
|
|
|
|
ChlorineSetTarget(ChlorinePump *pump) : pump_(pump) {}
|
|
|
|
TEMPLATABLE_VALUE(float, value)
|
|
|
|
|
|
|
|
void play(Ts... x) override {
|
|
|
|
this->pump_->set_target((int) this->value_.value(x...));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
ChlorinePump *pump_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|