Proportional Mixer Valve with PID Controller?

Dear Forum,
This is my first post but let me thank you all for 100’s of interesting and helpful articles I was reading through before.
In the last month I made to build my heating, mostly from discrete components.
For the management I decided to use home assistant with ESP home.
I’m using 3 way valves, powered by 24 V and controlled by a signal 0 - 10 V.
With that signal I can directly set the valves to any position between 0 and 100 %.
To get the output signal, I connected a GP8403 DAC to the I2C output of an ESP32.
(Which allows me to control all 3 valves over 2 wires.)
The heater is a wood burner that consumes logs or pellets. Meaning also, that the water in the return should not be below 60°C (degree Celsius).

TH: Hot water, returning from heater
TR: Water return into heater
TI: Water from buffer

Signal 0 V means: TR = TI
Signal 10 V means: TR = TH

For everything in between: TR = x * TH + (1-x) * TI
or: x = (TR - TI) / (TH - TI)

TR can be set through a slider.
Putting this formula into a template sensor was easy and the whole things works pretty fine.
The temperature sensors (DS18B20) are updated every 15 seconds and the heater is not rapidly moving. So the sensor is pretty stable and only moving slowly over the time.
Another sensor is a little more “nervous” because in temperature sensors are not measuring that precise and I see that the target temperature is jumping around its targeted value.
So I thought a PID controller would do the trick.
Unfortunately, I found no clue to get started with that. All posts I found are about switching an output on or off.
Maybe someone of you was facing the same issue?
Any help would be highly appreciated.

PS: If anyone is interested in the rest of the installation, I’m happy to share what I’ve done. E.g. using Danfos Alpha3 BLE connection, using an ESP32 as bluetooth proxy or adopting to the real mixer curves with a templated GP4803 output.

Have you seen the PID Climate component?

Hi Ben,
Thanks for your response.
Yes, saw that while nearly reading every post about my topic.
What I found is that PID Climate will help to switch on and off an output / valve.
Measuring the difference between setpoint and sensor is the error, which result in longer or shorter on-times.
My setup are two temperatures and a proportion to mix them.
Maybe I’m wrong (or just too stupid), but I found no example matching my needs using the PID Climate.

What you’re describing is more or less a Bang Bang controller:

A/the PID controller uses a proportional output (0-100%) to more finely control things. I can’t provide you an example specific to your setup (I’ve only used the PID component to control a coffee roaster by varying the heating coil output).

Edit: Here’s an excerpt from the relevant bits of my YAML:

climate:
  - platform: pid
    name: PID Controller
    id: pid_controller
    sensor: temp_sensor
    default_target_temperature: 15.5556°C
    heat_output: heater_pwm
    control_parameters:
      kp: 0.07276 #0.05009
      ki: 0.00364 #0.00230
      kd: 0.36376 #0.27302
    visual:
      min_temperature: 15.5556°C
      max_temperature: 270°C
      temperature_step: 1°C

output:
  - platform: slow_pwm
    pin: GPIO17
    id: heater_pwm
    period: 15s

As long as your output for your valve is setup as a float output (so not binary: on/off), you should be able to use what you already have (In my case, my output was connected to a solid state relay controlling a heating element). And then your temperature sensor would be the return water temperature you’re trying to keep at a minimum value. Unless you’re VERY comfortable with the maths involved, I’d also highly recommend using the autotuning feature in ESPhome (that’s how I arrived at my control parameter values).

Thanks Ben,

after another sleepless night ( :wink: ), I found a solution that works for me without using the PID Climate.
One of the endless articles I was reading through pointed me in the direction of esphome sensor filters.
To get my hands on this, I created for every of the sensors I need a templated filter.

- platform: template
    id: heating_t17_filter_ema
    name: "Heating T17 Filter EMA"
    unit_of_measurement: "°C"
    icon: "mdi:thermometer-plus"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 2
    lambda: |-
      return (id(heating_t17).state);
    filters:
      - exponential_moving_average:
          alpha: 0.1
          send_first_at: 5
          send_every: 6
    update_interval: ${update_interval}

The Exponential Moving Average (EMA) gave me the behavior I wanted to see.
Maybe I’m going to play around with the parameters, but the ones I picked are doing the trick for now.
No more spikes in the mixing %-value, a little running over directly measured at the heater is OK for me, the radiators will average this out.

{{ 
  (
    (
      (states('sensor.heating_ble_proxy_heating_t17_filter_ema') | float 
      -      
      states('input_number.sollwert_heizkoerper_vorlauf') | float)
      /
     (states('sensor.heating_ble_proxy_heating_t17_filter_ema') | float 
     - 
     states('sensor.heating_ble_proxy_heating_t14_filter_ema') | float)
    )
     *100
  ) | round(0)
}}

Again, thanks for you support.