Map / template PWM to servo movement for heating

Hi Guys,

I am trying to use the climate PID for heating, I have a valve that uses a servo for opening and closing the heat from a heatpump.
I can manually control the servo from ESPhome with the servo component.
And I thought I can just place that ID of the servo in the heat_output: of the climate PID as it is also a 0 to 100% value I use for the servo. But I get this error:

‘ID ‘my_servo’ of type servo::Servo doesn’t inherit from output::FloatOutput. Please double check your ID is pointing to the correct value.’

When I try it with a PWM output it does not give me the error. But of course that wont turn the servo, on the pin I selected in the PWM output and connected the servo to.

So how can I use the servo as the PWM in the climate controller?

I tried some things with the number component I use for controlling the servo, using that ID. But no luck, I am not a programmer. So I am now hitting a wall, and no idea on how to get it working. I have a feeling it can’t be that hard… if you know how ofcourse…

Any help would be appreciated!

Can do something like this:

output:
  - platform: template
    id: heat_output_float
    type: float
    write_action:
      - servo.write:
          id: my_servo
          level: !lambda |-
            return 2*state - 1; // converts range [0,1] to [-1,1]    

servo:
  - id: my_servo
    ...

climate:
  - platform: ...
    heat_output: heat_output_float

It is using a template output to hold the value emitted by heat_output. When a new value is written, the write_action will be invoked. Here you can control the servo with servo.write.

I don’t know what the output range is from heat_output. Default seems to be 0%…100% (which translates to 0.0…1.0). The servo range is -1.0…1.0 so, the code maps the heat output value to the servo’s range.

If the range is different, you can calculate it with the following linear conversion:
((state - heat_output_min) / (heat_output_max - heat_output_min)) * (servo_max - servo_min) + servo_min

My hero! :slight_smile:
I am going to experiment with it this weekend.

But I think it will work now.

Will circle back!

I have now this code, but my servo is not 100% ok. So waiting on a new servo head. But maybe this helps others, or maybe somebody spot’s some improvement points.

esphome:
  name: PID_Control

esp8266:
  board: d1_mini

# Enable logging
logger:
  
# Enable Home Assistant API
api:
  encryption:
    key: ""
    
ota:
  password: ""

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password


web_server:
  port: 80

dallas:
  - pin: D2

number:

  - platform: template
    name: Servo Control
    min_value: -100
    initial_value: 0
    max_value: 100
    step: 1
    optimistic: true
    mode: box
    set_action:
      then:
        - servo.write:
            id: my_servo
            level: !lambda 'return x / 100.0;'


sensor:

  - platform: template
    name: servo sensor position
    id: servo_sensor
  
  - platform: dallas
    address: 0x483c01e076aeaf28
    name: "Temperature"
    id: temperature
  

  - platform: template
    name: p term
    id: p_term
    unit_of_measurement: "%"
    accuracy_decimals: 2

  - platform: template
    name: i term
    id: i_term
    unit_of_measurement: "%"
    accuracy_decimals: 2

  - platform: template
    name: d term
    id: d_term
    unit_of_measurement: "%"
    accuracy_decimals: 2

  - platform: template
    name: output value
    unit_of_measurement: "%"
    id: o_term
    accuracy_decimals: 2

  - platform: template
    name: error value
    id: e_term
    accuracy_decimals: 2

  - platform: template
    name: is in deadband
    id: in_deadband_term
    accuracy_decimals: 0

  # Take the "HEAT" value of the pid and send 
  # it to the frontend to graph the output voltage

  - platform: pid
    name: "HEAT Output PWM"
    climate_id: console_thermostat
    type: HEAT


output:


  - platform: esp8266_pwm
    id: pwm_output
    pin: D3
    frequency: 50 Hz

  - platform: template
    id: heat_output_float
    type: float
    write_action:
      - servo.write:
          id: my_servo
          level: !lambda |-
            return 2*state - 1; // converts range [0,1] to [-1,1]
      - sensor.template.publish:
           id: servo_sensor
           state: !lambda |-
            return 2*state - 1; 

servo:
  - id: my_servo
    output: pwm_output



climate:
  - platform: pid
    name: "Thermostat"
    id: console_thermostat
    sensor: temperature

    
    default_target_temperature: 25°C
    heat_output: heat_output_float
    


    # ON state change, publish the values to the x_term numbers defined 
    # above, so that they can be viewed in HA
    on_state:
      - sensor.template.publish:
          id: p_term
          state: !lambda 'return -id(console_thermostat).get_proportional_term() * 100.0;'
      - sensor.template.publish:
          id: i_term
          state: !lambda 'return -id(console_thermostat).get_integral_term()* 100.0;'
      - sensor.template.publish:
          id: d_term
          state: !lambda 'return -id(console_thermostat).get_derivative_term()* 100.0;'
      - sensor.template.publish:
          id: o_term
          state: !lambda 'return -id(console_thermostat).get_output_value()* 100.0;'
      - sensor.template.publish:
          id: in_deadband_term
          state: !lambda 'return id(console_thermostat).in_deadband();'
      - sensor.template.publish:
          id: e_term
          state: !lambda 'return -id(console_thermostat).get_error_value();'
        
    # The extents of the HA Thermostat
    visual:
      min_temperature: 10 °C
      max_temperature: 30 °C
  

    control_parameters:
      kp: 0.6
      ki: 0.0015
      kd: 0
      max_integral: 0.0
      output_averaging_samples: 1
      derivative_averaging_samples: 5

switch:
  # Expose an ESP restart button to HA
  - platform: restart
    name: ESP Restart
    id: pid_restart


    
1 Like