ESPHome  2024.4.1
pid_climate.cpp
Go to the documentation of this file.
1 #include "pid_climate.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace pid {
6 
7 static const char *const TAG = "pid.climate";
8 
10  this->sensor_->add_on_state_callback([this](float state) {
11  // only publish if state/current temperature has changed in two digits of precision
12  this->do_publish_ = roundf(state * 100) != roundf(this->current_temperature * 100);
13  this->current_temperature = state;
14  this->update_pid_();
15  });
16  this->current_temperature = this->sensor_->state;
17 
18  // register for humidity values and get initial state
19  if (this->humidity_sensor_ != nullptr) {
20  this->humidity_sensor_->add_on_state_callback([this](float state) {
21  this->current_humidity = state;
22  this->publish_state();
23  });
25  }
26 
27  // restore set points
28  auto restore = this->restore_state_();
29  if (restore.has_value()) {
30  restore->to_call(this).perform();
31  } else {
32  // restore from defaults, change_away handles those for us
33  if (supports_heat_() && supports_cool_()) {
35  } else if (supports_cool_()) {
37  } else if (supports_heat_()) {
39  }
41  }
42 }
44  if (call.get_mode().has_value())
45  this->mode = *call.get_mode();
46  if (call.get_target_temperature().has_value())
48 
49  // If switching to off mode, set output immediately
50  if (this->mode == climate::CLIMATE_MODE_OFF)
51  this->write_output_(0.0f);
52 
53  this->publish_state();
54 }
59 
60  if (this->humidity_sensor_ != nullptr)
62 
64  if (supports_cool_())
66  if (supports_heat_())
68  if (supports_heat_() && supports_cool_())
70 
72  return traits;
73 }
75  LOG_CLIMATE("", "PID Climate", this);
76  ESP_LOGCONFIG(TAG, " Control Parameters:");
77  ESP_LOGCONFIG(TAG, " kp: %.5f, ki: %.5f, kd: %.5f, output samples: %d", controller_.kp_, controller_.ki_,
78  controller_.kd_, controller_.output_samples_);
79 
80  if (controller_.threshold_low_ == 0 && controller_.threshold_high_ == 0) {
81  ESP_LOGCONFIG(TAG, " Deadband disabled.");
82  } else {
83  ESP_LOGCONFIG(TAG, " Deadband Parameters:");
84  ESP_LOGCONFIG(TAG, " threshold: %0.5f to %0.5f, multipliers(kp: %.5f, ki: %.5f, kd: %.5f), output samples: %d",
85  controller_.threshold_low_, controller_.threshold_high_, controller_.kp_multiplier_,
86  controller_.ki_multiplier_, controller_.kd_multiplier_, controller_.deadband_output_samples_);
87  }
88 
89  if (this->autotuner_ != nullptr) {
90  this->autotuner_->dump_config();
91  }
92 }
93 void PIDClimate::write_output_(float value) {
94  this->output_value_ = value;
95 
96  // first ensure outputs are off (both outputs not active at the same time)
97  if (this->supports_cool_() && value >= 0)
98  this->cool_output_->set_level(0.0f);
99  if (this->supports_heat_() && value <= 0)
100  this->heat_output_->set_level(0.0f);
101 
102  // value < 0 means cool, > 0 means heat
103  if (this->supports_cool_() && value < 0)
104  this->cool_output_->set_level(std::min(1.0f, -value));
105  if (this->supports_heat_() && value > 0)
106  this->heat_output_->set_level(std::min(1.0f, value));
107 
108  // Update action variable for user feedback what's happening
109  climate::ClimateAction new_action;
110  if (this->supports_cool_() && value < 0) {
111  new_action = climate::CLIMATE_ACTION_COOLING;
112  } else if (this->supports_heat_() && value > 0) {
113  new_action = climate::CLIMATE_ACTION_HEATING;
114  } else if (this->mode == climate::CLIMATE_MODE_OFF) {
115  new_action = climate::CLIMATE_ACTION_OFF;
116  } else {
117  new_action = climate::CLIMATE_ACTION_IDLE;
118  }
119 
120  if (new_action != this->action) {
121  this->action = new_action;
122  this->do_publish_ = true;
123  }
124  this->pid_computed_callback_.call();
125 }
127  float value;
128  if (std::isnan(this->current_temperature) || std::isnan(this->target_temperature)) {
129  // if any control parameters are nan, turn off all outputs
130  value = 0.0;
131  } else {
132  // Update PID controller irrespective of current mode, to not mess up D/I terms
133  // In non-auto mode, we just discard the output value
134  value = this->controller_.update(this->target_temperature, this->current_temperature);
135 
136  // Check autotuner
137  if (this->autotuner_ != nullptr && !this->autotuner_->is_finished()) {
138  auto res = this->autotuner_->update(this->target_temperature, this->current_temperature);
139  if (res.result_params.has_value()) {
140  this->controller_.kp_ = res.result_params->kp;
141  this->controller_.ki_ = res.result_params->ki;
142  this->controller_.kd_ = res.result_params->kd;
143  // keep autotuner instance so that subsequent dump_configs will print the long result message.
144  } else {
145  value = res.output;
146  }
147  }
148  }
149 
150  if (this->mode == climate::CLIMATE_MODE_OFF) {
151  this->write_output_(0.0);
152  } else {
153  this->write_output_(value);
154  }
155 
156  if (this->do_publish_)
157  this->publish_state();
158 }
159 void PIDClimate::start_autotune(std::unique_ptr<PIDAutotuner> &&autotune) {
160  this->autotuner_ = std::move(autotune);
161  float min_value = this->supports_cool_() ? -1.0f : 0.0f;
162  float max_value = this->supports_heat_() ? 1.0f : 0.0f;
163  this->autotuner_->config(min_value, max_value);
164  this->autotuner_->set_autotuner_id(this->get_object_id());
165 
166  ESP_LOGI(TAG,
167  "%s: Autotune has started. This can take a long time depending on the "
168  "responsiveness of your system. Your system "
169  "output will be altered to deliberately oscillate above and below the setpoint multiple times. "
170  "Until your sensor provides a reading, the autotuner may display \'nan\'",
171  this->get_object_id().c_str());
172 
173  this->set_interval("autotune-progress", 10000, [this]() {
174  if (this->autotuner_ != nullptr && !this->autotuner_->is_finished())
175  this->autotuner_->dump_config();
176  });
177 
179  ESP_LOGW(TAG, "%s: !!! For PID autotuner you need to set AUTO (also called heat/cool) mode!",
180  this->get_object_id().c_str());
181  }
182 }
183 
185 
186 } // namespace pid
187 } // namespace esphome
This class is used to encode all control actions on a climate device.
Definition: climate.h:33
The climate device is off (inactive or no power)
Definition: climate_mode.h:33
float current_humidity
The current humidity of the climate device, as reported from the integration.
Definition: climate.h:182
output::FloatOutput * cool_output_
Definition: pid_climate.h:91
bool supports_cool_() const
Definition: pid_climate.h:82
void add_on_state_callback(std::function< void(float)> &&callback)
Add a callback that will be called every time a filtered value arrives.
Definition: sensor.cpp:52
void set_interval(const std::string &name, uint32_t interval, std::function< void()> &&f)
Set an interval function with a unique name.
Definition: component.cpp:52
void write_output_(float value)
Definition: pid_climate.cpp:93
float target_temperature
The target temperature of the climate device.
Definition: climate.h:186
sensor::Sensor * humidity_sensor_
The sensor used for getting the current humidity.
Definition: pid_climate.h:90
const optional< ClimateMode > & get_mode() const
Definition: climate.cpp:273
void setup() override
Definition: pid_climate.cpp:9
void set_supports_current_humidity(bool supports_current_humidity)
This class contains all static data for climate devices.
The climate device is set to heat to reach the target temperature.
Definition: climate_mode.h:18
ClimateMode mode
The active mode of the climate device.
Definition: climate.h:173
float current_temperature
The current temperature of the climate device, as reported from the integration.
Definition: climate.h:179
bool has_value() const
Definition: optional.h:87
void start_autotune(std::unique_ptr< PIDAutotuner > &&autotune)
std::string get_object_id() const
Definition: entity_base.cpp:43
std::unique_ptr< PIDAutotuner > autotuner_
Definition: pid_climate.h:98
void set_level(float state)
Set the level of this float output, this is called from the front-end.
The climate device is set to cool to reach the target temperature.
Definition: climate_mode.h:16
float state
This member variable stores the last state that has passed through all filters.
Definition: sensor.h:131
void set_supported_modes(std::set< ClimateMode > modes)
ClimateAction
Enum for the current action of the climate device. Values match those of ClimateMode.
Definition: climate_mode.h:31
float update(float setpoint, float process_value)
The climate device is set to heat/cool to reach the target temperature.
Definition: climate_mode.h:14
void control(const climate::ClimateCall &call) override
Override control to change settings of the climate device.
Definition: pid_climate.cpp:43
The climate device is actively heating.
Definition: climate_mode.h:37
const optional< float > & get_target_temperature() const
Definition: climate.cpp:274
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition: climate.cpp:395
The climate device is off.
Definition: climate_mode.h:12
climate::ClimateTraits traits() override
Return the traits of this controller.
Definition: pid_climate.cpp:55
void set_supports_action(bool supports_action)
sensor::Sensor * sensor_
The sensor used for getting the current temperature.
Definition: pid_climate.h:88
bool supports_heat_() const
Definition: pid_climate.h:83
void dump_config() override
Definition: pid_climate.cpp:74
output::FloatOutput * heat_output_
Definition: pid_climate.h:92
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
PIDController controller_
Definition: pid_climate.h:93
The climate device is idle (monitoring climate but no action needed)
Definition: climate_mode.h:39
void set_supports_two_point_target_temperature(bool supports_two_point_target_temperature)
optional< ClimateDeviceRestoreState > restore_state_()
Restore the state of the climate device, call this from your setup() method.
Definition: climate.cpp:329
void set_supports_current_temperature(bool supports_current_temperature)
float output_value_
Output value as reported by the PID controller, for PIDClimateSensor.
Definition: pid_climate.h:95
The climate device is actively cooling.
Definition: climate_mode.h:35
void add_supported_mode(ClimateMode mode)
CallbackManager< void()> pid_computed_callback_
Definition: pid_climate.h:96
ClimateAction action
The active state of the climate device.
Definition: climate.h:176
bool state
Definition: fan.h:34