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