ESPHome  2023.5.5
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  // restore set points
18  auto restore = this->restore_state_();
19  if (restore.has_value()) {
20  restore->to_call(this).perform();
21  } else {
22  // restore from defaults, change_away handles those for us
23  if (supports_heat_() && supports_cool_()) {
25  } else if (supports_cool_()) {
27  } else if (supports_heat_()) {
29  }
31  }
32 }
34  if (call.get_mode().has_value())
35  this->mode = *call.get_mode();
36  if (call.get_target_temperature().has_value())
38 
39  // If switching to off mode, set output immediately
40  if (this->mode == climate::CLIMATE_MODE_OFF)
41  this->write_output_(0.0f);
42 
43  this->publish_state();
44 }
49 
51  if (supports_cool_())
53  if (supports_heat_())
55  if (supports_heat_() && supports_cool_())
57 
59  return traits;
60 }
62  LOG_CLIMATE("", "PID Climate", this);
63  ESP_LOGCONFIG(TAG, " Control Parameters:");
64  ESP_LOGCONFIG(TAG, " kp: %.5f, ki: %.5f, kd: %.5f, output samples: %d", controller_.kp_, controller_.ki_,
65  controller_.kd_, controller_.output_samples_);
66 
67  if (controller_.threshold_low_ == 0 && controller_.threshold_high_ == 0) {
68  ESP_LOGCONFIG(TAG, " Deadband disabled.");
69  } else {
70  ESP_LOGCONFIG(TAG, " Deadband Parameters:");
71  ESP_LOGCONFIG(TAG, " threshold: %0.5f to %0.5f, multipliers(kp: %.5f, ki: %.5f, kd: %.5f), output samples: %d",
72  controller_.threshold_low_, controller_.threshold_high_, controller_.kp_multiplier_,
73  controller_.ki_multiplier_, controller_.kd_multiplier_, controller_.deadband_output_samples_);
74  }
75 
76  if (this->autotuner_ != nullptr) {
77  this->autotuner_->dump_config();
78  }
79 }
80 void PIDClimate::write_output_(float value) {
81  this->output_value_ = value;
82 
83  // first ensure outputs are off (both outputs not active at the same time)
84  if (this->supports_cool_() && value >= 0)
85  this->cool_output_->set_level(0.0f);
86  if (this->supports_heat_() && value <= 0)
87  this->heat_output_->set_level(0.0f);
88 
89  // value < 0 means cool, > 0 means heat
90  if (this->supports_cool_() && value < 0)
91  this->cool_output_->set_level(std::min(1.0f, -value));
92  if (this->supports_heat_() && value > 0)
93  this->heat_output_->set_level(std::min(1.0f, value));
94 
95  // Update action variable for user feedback what's happening
96  climate::ClimateAction new_action;
97  if (this->supports_cool_() && value < 0) {
99  } else if (this->supports_heat_() && value > 0) {
100  new_action = climate::CLIMATE_ACTION_HEATING;
101  } else if (this->mode == climate::CLIMATE_MODE_OFF) {
102  new_action = climate::CLIMATE_ACTION_OFF;
103  } else {
104  new_action = climate::CLIMATE_ACTION_IDLE;
105  }
106 
107  if (new_action != this->action) {
108  this->action = new_action;
109  this->do_publish_ = true;
110  }
111  this->pid_computed_callback_.call();
112 }
114  float value;
115  if (std::isnan(this->current_temperature) || std::isnan(this->target_temperature)) {
116  // if any control parameters are nan, turn off all outputs
117  value = 0.0;
118  } else {
119  // Update PID controller irrespective of current mode, to not mess up D/I terms
120  // In non-auto mode, we just discard the output value
121  value = this->controller_.update(this->target_temperature, this->current_temperature);
122 
123  // Check autotuner
124  if (this->autotuner_ != nullptr && !this->autotuner_->is_finished()) {
125  auto res = this->autotuner_->update(this->target_temperature, this->current_temperature);
126  if (res.result_params.has_value()) {
127  this->controller_.kp_ = res.result_params->kp;
128  this->controller_.ki_ = res.result_params->ki;
129  this->controller_.kd_ = res.result_params->kd;
130  // keep autotuner instance so that subsequent dump_configs will print the long result message.
131  } else {
132  value = res.output;
133  }
134  }
135  }
136 
137  if (this->mode == climate::CLIMATE_MODE_OFF) {
138  this->write_output_(0.0);
139  } else {
140  this->write_output_(value);
141  }
142 
143  if (this->do_publish_)
144  this->publish_state();
145 }
146 void PIDClimate::start_autotune(std::unique_ptr<PIDAutotuner> &&autotune) {
147  this->autotuner_ = std::move(autotune);
148  float min_value = this->supports_cool_() ? -1.0f : 0.0f;
149  float max_value = this->supports_heat_() ? 1.0f : 0.0f;
150  this->autotuner_->config(min_value, max_value);
151  this->autotuner_->set_autotuner_id(this->get_object_id());
152 
153  ESP_LOGI(TAG,
154  "%s: Autotune has started. This can take a long time depending on the "
155  "responsiveness of your system. Your system "
156  "output will be altered to deliberately oscillate above and below the setpoint multiple times. "
157  "Until your sensor provides a reading, the autotuner may display \'nan\'",
158  this->get_object_id().c_str());
159 
160  this->set_interval("autotune-progress", 10000, [this]() {
161  if (this->autotuner_ != nullptr && !this->autotuner_->is_finished())
162  this->autotuner_->dump_config();
163  });
164 
166  ESP_LOGW(TAG, "%s: !!! For PID autotuner you need to set AUTO (also called heat/cool) mode!",
167  this->get_object_id().c_str());
168  }
169 }
170 
172 
173 } // namespace pid
174 } // 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
output::FloatOutput * cool_output_
Definition: pid_climate.h:88
bool supports_cool_() const
Definition: pid_climate.h:81
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:51
void write_output_(float value)
Definition: pid_climate.cpp:80
float target_temperature
The target temperature of the climate device.
Definition: climate.h:172
const optional< ClimateMode > & get_mode() const
Definition: climate.cpp:263
void setup() override
Definition: pid_climate.cpp:9
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:164
float current_temperature
The current temperature of the climate device, as reported from the integration.
Definition: climate.h:168
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:95
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:33
The climate device is actively heating.
Definition: climate_mode.h:37
const optional< float > & get_target_temperature() const
Definition: climate.cpp:264
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition: climate.cpp:377
The climate device is off.
Definition: climate_mode.h:12
climate::ClimateTraits traits() override
Return the traits of this controller.
Definition: pid_climate.cpp:45
void set_supports_action(bool supports_action)
sensor::Sensor * sensor_
The sensor used for getting the current temperature.
Definition: pid_climate.h:87
bool supports_heat_() const
Definition: pid_climate.h:82
void dump_config() override
Definition: pid_climate.cpp:61
output::FloatOutput * heat_output_
Definition: pid_climate.h:89
Definition: a4988.cpp:4
PIDController controller_
Definition: pid_climate.h:90
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:314
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:92
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:93
ClimateAction action
The active state of the climate device.
Definition: climate.h:166
bool state
Definition: fan.h:34