Thermostat with PID controller

Combining both for a room thermostat is indeed tricky and could result in instability. I think it might however be useful is some specific cases. I wonder if it could be beneficial to combine both for a room with slow lagging heating systems, like low temp wood covered floor heatings? Weather mode directly compensates for a large part the heat loss and PID controller fine-tunes the last part of the temperature error. I haven’t done any testing myself, maybe that some papers can be found about it.

I don’t use this combination for the room thermostats either, only for the main valve (heat supply to the total system) and only by weather mode + proportional, no integral or derative.

That’s exactly what I was suspecting. I own two Netatmo thermostats, in On/Off mode, as the Netatmo can’t modulate the power of my boiler (it’s not an OpenTherm system) ; and I kept the external temperature sensor used with the original Expert Control modulating thermostat from Chaffoteaux. It allowed me to configure the boiler to adjust the water output temperature for the two heating circuits according to the external temperature (adjustable gain + offset linear compensation). That’s very efficient, but the boiler controls the water temperature (the “available heating power”) while the PID controls the PWM (doing the modulation).
I’m afraid if the external temperature compensates the PID output, it may lead to unpredictable results with the anti-windup of the integral part. Implementations I found using external temperature are only based on proportional regulation of the error, with no derivative nor integration.

In the mean time, I updated my code and built a beta version to put back the min_cycle_duration option and try to improve the autotuner (as it didn’t work at all).

You could have look into the multizone thermostat version of (I think about) march 2021. I scraped all mods from forks relating autotune. It also includes the option to start autotune via preset mode and saved results directly in the PID controller.

I just removed this option as I doubted the effectiveness of this tuning, especially for slow varying systems with relatively large external effects (outdoor temp, cooking, sun radiation etc)


Maybe it’s a stupid question, but do you have a simple query (sql or influx) to extract data that can be easily import in pidtuner ?

1 Like

You can use something like that :

SELECT mean("current_temperature") AS "mean_current_temperature", mean("control_output") AS "mean_control_output", mean("temperature") AS "mean_temperature" FROM "home_assistant"."autogen"."state" WHERE time > :dashboardTime: AND time < :upperDashboardTime: AND "entity_id"='salle_de_bain' GROUP BY time(:interval:) FILL(null)

I generated a new beta release with services to set the PID gains, set the preset modes temperatures and clear the integral component of the PID.
These changes will ease the tuning of the PID, allowing to adjust the gains without requiring a restart.

It also adds a unique_id parameter to be able to customize the entity through UI.

Hey guys, any idea how to export the data from home assistant to use them in ?

If you have an external SQL database, you can export it in its web visualisation tool (like chronograf for InfluxDB).

Otherwise you may use an automation to write a CSV file at each status update of the thermostat, as explained here :

Is this smart thermostat also suitable for multi zone heating?
I have underfloor heating (hot water in concrete) and currently I am running 7 generic thermostats. I have a little overshoot (0,5 degree celsius), usually reaching that after 1,5-2 hours after reaching target temperature.
So for instance, if I have a schedule to heat to 22, if I reach 22 at 6 PM, usually between 7-8 PM, it would rise up to 22,5 degree celsius.
Anyhow, the way I have my thermostats defined right now is that for the heater - I specify switch that opens/closes acutators to let the hot water in to the zone. Those valves themselves take 2-3 minutes to fully open, around the same time to close. On the top of it, I run automation every minute to check if any of the zones is opened and if yes, give signal to heat pump.
The problem is that heatpump has its own cycles. If you send the signal for heat, it won’t start immediately so for instance if I have zone opened for one hour, that does not mean that pump is delivering 100% of power for one hour as there is water pump that cycles water, checks inlet water temperature and compares it to set value.
I have replaced those thermostats with smart thermostats, without any further checking/tuning, and what I ended up with was zones opening ocasionally for few minutes, resulting in giving signal to heat pump to start heating, which took between 1-9 minutes, which isn’t ideal.

So I am wondering if anyone here is using it in combination with multi zone heating and “master switch” to give signal to heater.

Hello, it should work, but you should set the min_cycle_duration parameter to more than 10 minutes to ensure the system will heat up.
You may also use min_off_cycle_duration to differentiate if the time to switch off is shorter or longer than the time to switch on.

You may also give a try to Multizone Thermostat
which is similar but that can drive a master.

Thanks adrien.b, I looked at that too but so far it seems less “trustworthy” to me :slight_smile:
Thinking out loud, if I set min_cycle_duration to 30 minutes, it means that should zone require heat (setpoint is higher than actual temperature), it will open valves for zone for 30 minutes (or more), and in the same manner min_off_cycle_duration will specify how long it is off and won’t turn back on.
What will the end result be in terms of “hvac_action”? Will it go to idle, or stay in “heating”.
I am asking to find the best fit for that extra automation.

For example. 8 AM, I have scheduler to set target temperature to 24, and current temperature is 21. It should open valves for zone and keep them opened for at least 30 minutes. From experience I know that temperature won’t rise significantly after 30 minutes, so I might have 21,2 degrees.
In theory then valves should remain open and hvac_action will still be “heating” (until all this dynamic magic determines that they should be closed). Once that decision is made, and valves close, hvac action will result in “idle” (with or without target temperature reached yet). At that point, valves will remain closed for at least 30 minutes. My extra automation will turn off heater because zone will close).

What will happen after those 30 minutes if target temperature is not reached yet? Will it turn on another on cycle for next 30 minutes?

Or am I overshooting it with 30 minutes? That’s why I was wondering if anyone actually has any experience.
Also are there any recommended values to define for slow heating like concrete underfloor heating? I just used configuration from example

      seconds: 60
    away_temp: 14
    kp : 5
    ki : 0.01
    kd : 500
    pwm : 00:15:00

Please note that the min_cycle_duration should not be set to the time it takes for the temperature to rise, but to the time it takes for your heating system to settle. For your underfloor heating it would be for example the time for the water return to be at the expected temperature (for example, some circulators uses the delta-T between water output and return to adjust the speed), ensuring optimal heat distribution in the loops.
It may take 10 to 15 minutes only. Then the min_cycle_duration will ensure the heat will be distributed correctly.
The on time will then be adjusted by the PID based on the PWM duration. As the underfloor heating is very slow, you should increase the PWM to 30 to 60 minutes.
For example, using min_cycle_duration at 15mn and pwm to 60mn :

  • If PID output is 5%, meaning 0.05 * 60 = 3 minutes, the heater will switch on but the min_cycle_duration will force it to remain on during 15 minutes.
  • If PID output is 33%, meaning 0.33 * 60 = 20 minutes, the heater will switch on for 20 minutes.

You can see the min_cycle_duration will limit the lower power the thermostat will be able to modulate, that’s why it should be kept as low as possible.

For the Kp, Ki and Kd gains, you may look at the history charts you got with the generic thermostat to extract the amplitude and period, and compute by hand the ultimate and final gains as explained in this post on my Github :

It works quite well.

Thanks. I started with one room and will monitor how it goes:

- platform: smart_thermostat
  name: Office
  unique_id: office_smart_thermostat
  heater: switch.heating_4
  target_sensor: sensor.temperature_4
  min_temp: 17
  max_temp: 28
  ac_mode: false
  target_temp: 21
    minutes: 3
  away_temp: 17
  min_cycle_duration: 00:20:00
#  min_off_cycle_duration: 00:10:00
  kp : 7.493
  ki : 0.009366
  kd : 1498.6
  pwm : 01:00:00

First attempt wasn’t really ideal. As you can see from graph below, while last 2 days, heating usually started at 6 AM where I have my set temp changed from 21,5 to 22 - heating was on for 2,5 - 3 hours.
Today with config above, at 6 AM it started, and lasted until 1:30 PM (on and off). By heating I mean valves that were opening/closing. Also heating after temperature was already above set point. Any advice?

In my opinion, your minimum cycle duration is too long Vs your PWM. So you’ll only have 30% minimum power.
Try to double the PWM, and you should monitor the p, i and d attributes to see which one compensates each other. You may need to increase the kd for an early stop when the temperature starts to increase.

However, it’s not really recommended to decrease the temperature during the night on a floor heating, the inertia being huge, the little saving you gain during the night is compensated by the longer heating period to bring it back to comfort temperature in the morning.

You are right. The overshoot isn’t really huge, and as it can be seen from the graph, once heated in the morning, temperature lasts throughout the day.
But it keeps me entertained :slight_smile: I will double the pwm and observe results.

1°C amplitude between the minimum and the maximum is big. By experience, when sitting in the sofa, watching TV, a -0.5°C variation can make it really uncomfortable. That’s why I bought my Netatmo thermostat that features a PID. And this thermostat gave me the idea to use a PID in the generic thermostat from HA.

With a PID, and once correctly tuned, you can get a ±0.1°C oscillation around the set point, and it’s particularly interesting with systems with big inertia.

Strange issue with v2021.12.2-beta4:

The set temperature reverts to 22 after changing it. No presets active. Also, upon restart, set temperature dropped to 18.
Also “on” time is dubious.
Any thoughts?


- platform: smart_thermostat
  name: Heating
  unique_id: climate.heating
  heater: switch.heater
  target_sensor: sensor.temperature
  keep_alive: 60
  kp: 10
  ki: 0.01
  kd: 1000
  pwm: 600
  min_cycle_duration: 300
  min_off_cycle_duration: 120
  sampling_period: 300
  force_pid_refresh: 60
  target_temp_step: 0.1
  precision: 0.1
  min_temp: 7
  max_temp: 35
  ac_mode: false
  away_temp: 18
  eco_temp: 20
  boost_temp: 24
  comfort_temp: 22
  home_temp: 21.6
  sleep_temp: 21.8
  activity_temp: 21.4

Hello, the set temperature reverting to 22 is really strange. Is there a time commonality (reverting every X seconds) ? Did you try without the force_pid_refresh setting ?
Please check there is no scheduler or automation that could refresh the set point of the thermostat, too.

Regarding the on time, it’s due to the force_pid_refresh parameter that forces computing the PID, and as the PID output value is different, the PWM is refreshed more often. If you really need to refresh the PID (it’s to help working with very slow sensors), you should set it at least to half the sampling_period.

Here is today after changing PWM to 2 hours. Little less overshoot, heating shorter in few longer cycles, compared to yesterday.

Here is also PID data from last 24 hours

And different graph for temperature (set vs. measured, I am not sure how to put hvac_action there using influxDB as it is “undefined” because of being string I guess?)

I would appreciate advice, as I honestly have no clue how this works :slight_smile: I did increase kd from 1498.6 to 1900… just as a guess