ESP32 servo: How do I keep the position after restarting or de-energizing the board?

How can I make sure that the sensor is not reset after a reboot and the servo does not return to the 0 position? I noticed that if I specify restore_state: false at the switch, then after restarting the esp or de-energizing.

If I enable restore after reboot restore_state: true

switch:
  - platform: template
    id: servo_control_switch
    name: servo start/end
    restore_state: true

Before the esp reboot

After restarting the esp sensors will show 0 and the servo will return to the 0 position

If I turn off restore after reboot restore_state: false

switch:
  - platform: template
    id: servo_control_switch
    name: servo start/end
    restore_state: false

Before the esp reboot

After restarting the esp, the sensor values will be NA, the servo will not return to the 0 position

Below is the full code. How can I make sure that after restarting the esp, the sensors are restored and the servo is not reset to the 0 position?

esphome:
  name: servo_smartbath

esp32:
  board: esp32dev
  framework:
    type: arduino

#Wi-Fi credentials for connecting the board to the home network
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: on

#If there is no connection with WiFi, then the access point will rise
  ap:
    ssid: !secret ap_esp_ssid
    password: !secret ap_esp_password

#Web server
web_server:
  port: 80

#Logging
logger:


#OTA и API
ota:
  password: "esphome"
api:
  password: "esphome"
  services:
   - service: control_servo
     variables:
       level: float
     then:
       - servo.write:
           id: servo_control
           level: !lambda 'return level / 100.0;'
       - sensor.template.publish:
           id: servo_sensor
           state: !lambda 'return (float)level;'

#Output Platform
output:
  - platform: ledc
    id: pwm_output
    pin: 13
    frequency: 50 Hz

servo:
  - id: servo_control
    output: pwm_output
    auto_detach_time: 0s
    transition_length: 5s

#Sensors
sensor:
#WiFi Signal Sensor
  - platform: wifi_signal
    name: gateway wifi signal
    update_interval: 30s
    unit_of_measurement: "dBa"
    accuracy_decimals: 0

#Servomotor position sensor
  - platform: template
    name: servo sensor position
    id: servo_sensor
    on_value:
      then:
        - script.execute: script_servo

#Switches
switch:
#Restart esp
  - platform: restart
    name: gateway restart

#Servo switch with start and end position
  - platform: template
    id: servo_control_switch
    name: servo start/end
    icon: mdi:bathtub
    restore_state: true
    turn_on_action:
      - sensor.template.publish:
          id: servo_sensor
          state: !lambda 'return id(servo_start).state;'
      - switch.template.publish:
         id: servo_control_switch
         state: true
      - servo.write:
         id: servo_control
         level: !lambda 'return (id(servo_start).state/100);'
    turn_off_action:
      - sensor.template.publish:
          id: servo_sensor
          state: !lambda 'return id(servo_stop).state;'
      - switch.template.publish:
         id: servo_control_switch
         state: false
      - servo.write:
         id: servo_control
         level: !lambda 'return (id(servo_stop).state/100);'


#script_servo script
script:
  - id: script_servo
    then:
      - if:
          condition:
            - lambda: 'return id(servo_sensor).state == id(servo_start).state;'
          then:
            - sensor.template.publish:
               id: servo_sensor
               state: !lambda 'return (id(servo_start).state/100);'
            - switch.turn_on: servo_control_switch
      - if:
          condition:
            and:
              - lambda: 'return id(servo_sensor).state == id(servo_stop).state;'
          then:
            - sensor.template.publish:
               id: servo_sensor
               state: !lambda 'return id(servo_stop).state;'
            - switch.turn_off: servo_control_switch

#Adding a slider to control the servo
number:
  - platform: template
    name: servo position
    min_value: -100
    max_value: 100
    update_interval: 5s 
    mode: slider #slider/box
    lambda: 'return id(servo_sensor).state;' 
    step: 1
    set_action:
      then:
        - servo.write:
           id: servo_control
           level: !lambda 'return x / 100.0;'
        - sensor.template.publish:
           id: servo_sensor
           state: !lambda 'return x;'

#Specify the initial position of the servo 
  - platform: template
    name: servo start position
    id: servo_start
    min_value: -100
    max_value: 100
    mode: box #slider/box
    step: 1
    optimistic: true
    restore_value: true
 
#Specify the final position of the servo
  - platform: template
    name: servo end position
    id: servo_stop
    min_value: -100
    max_value: 100
    mode: box #slider/box
    step: 1
    optimistic: true
    restore_value: true


Are you certain that this action isn’t being executed on startup?

What do the logs say?

You would think that it would set to the servo state but maybe not?

If you remove the switches and sensors and leave only this option, then after restarting the esp or de-energizing, nothing is reset and remains in place. As soon as I add a switch and a sensor, then after a reboot everything goes wrong. I was thinking of using a number instead of a sensor to store the position in memory and restore the position from the number after a reboot, I can’t do it

number:
  - platform: template
    name: servo memory
    id: servo_memory
    min_value: -100
    max_value: 100
    mode: box #slider/box
    step: 1
    optimistic: true
    restore_value: true

  - platform: template
    name: servo position
    min_value: -100
    max_value: 100
    update_interval: 5s 
    mode: slider #slider/box
    lambda: 'return id(servo_memory).state;' 
    step: 1
    set_action:
      then:
        - servo.write:
           id: servo_control
           level: !lambda 'return x / 100.0;'
        - number.set:
           id: servo_memory
           value: !lambda 'return x;'

Trying to find solution for the same problem. Not an expert in HA/ESPHome, but as far as I understand how modules work retaining often changing value is not feasible solution because flash will be killed in no time. Only rarely changing config values should be stored. Since servo kind of retains position till pwm signal is energized/sent - bigger load/force will move servo anyway - I would try to send update of last known state from HA when module becomes available after reset. Of course there might be some issues with sync if servo position is changed while HA is offline for some reason.
Since you are using esp32 maybe easier solution would be servo with analog position feedback hooked to adc input (for eg FS90-FB Micro Servo)?

You write correctly

that the servo keeps the position until a pwm signal is applied/sent - a large load/force will move the servo anyway - I would try to send an update of the last known state from HA when the module becomes available after reset.

I just want to provide emergency recovery of the last position of the servo after de-energizing the board, since the board will not de-energize, but only the servo itself. Why do I need this? You can watch my video where I build a smart bath. There I built a servo into the siphon and the servo rotates the siphon twister and opens or closes the drain valve. The servo is switched off so that you can manually turn the twister and close or open the drain valve. I am a little satisfied with the fact that if the power is reset and then the servo returns to the 0 position, but still I would like to be able to make it so that after losing power the servo does not return to 0. You can use Home Assistant, but there is no feedback, which I implemented in ESPHome, and this is important very

I found a solution how to save the state of the servo position. The position remains even if the board is reset or de-energized

substitutions:
  board_name: servo_smartbath
  node_name: servo_smartbath

esphome:
  name: ${node_name}
  comment: D1 Mini Servo Smartbath
  on_boot:
    priority: -100
    then:
       - sensor.template.publish:
           id: servo_sensor
           state: !lambda "return id(saved_position);"

esp8266:
  board: d1_mini
  framework:
    version: recommended
  restore_from_flash: true

preferences:
  flash_write_interval: 1min

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

  ap:
    ssid: !secret ap_esp_ssid
    password: !secret ap_esp_password

web_server:
  port: 80

logger:
  level: ERROR

ota:
  password: "esphome"

api:
  encryption:
    key: !secret api_key
  services:
   - service: control_servo
     variables:
       level: float
     then:
       - servo.write:
           id: servo_control
           level: !lambda 'return level / 100.0;'
       - sensor.template.publish:
           id: servo_sensor
           state: !lambda 'return (float)level;'

output:
  - platform: esp8266_pwm
    id: pwm_output
    pin: D6
    frequency: 50 Hz

globals:
  - id: saved_position
    type: int
    initial_value: '0'
    restore_value: true

servo:
  - id: servo_control
    output: pwm_output
    auto_detach_time: 5s
    transition_length: 5s
    restore: true

sensor:
  - platform: wifi_signal
    name: gateway wifi signal
    update_interval: 60s
    unit_of_measurement: "dBa"
    accuracy_decimals: 0

  - platform: template
    name: servo sensor position
    id: servo_sensor
    on_value:
      then:
        - script.execute: script_servo
        - script.execute: record_servo_position

switch:
  - platform: template
    id: servo_control_switch
    name: servo start/end
    icon: mdi:bathtub
    restore_state: false
    turn_on_action:
      - sensor.template.publish:
          id: servo_sensor
          state: !lambda 'return id(servo_start).state;'
      - switch.template.publish:
         id: servo_control_switch
         state: true
      - servo.write:
         id: servo_control
         level: !lambda 'return (id(servo_start).state/100);'
    turn_off_action:
      - sensor.template.publish:
          id: servo_sensor
          state: !lambda 'return id(servo_stop).state;'
      - switch.template.publish:
         id: servo_control_switch
         state: false
      - servo.write:
         id: servo_control
         level: !lambda 'return (id(servo_stop).state/100);'

script:
  - id: script_servo
    then:
      - if:
          condition:
            - lambda: 'return id(servo_sensor).state == id(servo_start).state;'
          then:
            - sensor.template.publish:
               id: servo_sensor
               state: !lambda 'return (id(servo_start).state/100);'
            - switch.turn_on: servo_control_switch
      - if:
          condition:
            and:
              - lambda: 'return id(servo_sensor).state == id(servo_stop).state;'
          then:
            - sensor.template.publish:
               id: servo_sensor
               state: !lambda 'return id(servo_stop).state;'
            - switch.turn_off: servo_control_switch

  - id: record_servo_position
    then:
      - globals.set:
          id: saved_position
          value: !lambda 'return id(servo_sensor).state;'

number:
  - platform: template
    name: servo position
    min_value: -100
    max_value: 100
    update_interval: 5s 
    mode: slider #slider/box
    lambda: 'return id(servo_sensor).state;' 
    step: 1
    set_action:
      then:
        - servo.write:
           id: servo_control
           level: !lambda 'return x / 100.0;'
        - sensor.template.publish:
           id: servo_sensor
           state: !lambda 'return x;'

  - platform: template
    name: servo start position
    id: servo_start
    min_value: -100
    max_value: 100
    mode: box #slider/box
    step: 1
    optimistic: true
    restore_value: true
 
  - platform: template
    name: servo end position
    id: servo_stop
    min_value: -100
    max_value: 100
    mode: box #slider/box
    step: 1
    optimistic: true
    restore_value: true

button:
  - platform: restart
    name: ${board_name} Restart
    icon: mdi:restart
            
text_sensor:
  - platform: wifi_info
    ip_address:
      name: ${board_name} IP

Hi Divan
I used your code for servo control thanks for share.i have problem i used actually final code on page but it’s still resetting servo motor position after energize deenergize .And its give error message on restore_state to change restore_mode .Do you have solution for resetting or could you share final code with me ?
Thanks a lot :pray:

Judging by what you use in the restore_mode code, it means you are using ESP 32, and the ESP8266s have restore_from_flash

The ESP8266 has no memory and does not remember, so this code is used

preferences:
  flash_write_interval: 1min

It should be understood that the state is not recorded in memory immediately, but after a specified time. If you specified 1 minute, and you set the position of the servo and immediately de-energized, then this will not be stored in memory and the state will be reset. Set the position of the servo and wait for the time when it will be recorded in memory, and then you can de-energize