Disclosure: Some of the links on this site are affiliate links. This means that, at zero cost to you, I will earn an affiliate commission if you click through the link and finalize a purchase.

Home Assistant & ESPHome Servo Example (Step-by-Step guide)

Looking to integrate a servo with Home Assistant?

This step-by-step tutorial will guide you through the process of building a wireless servo and integrating it with Home Assistant!

For the example we will be using a readily available Wemos D1 Mini and RC servo, however you can use any compatible ESP device and servo.

By utilizing an ESP device and the ESPHome platform, adding a servo to your smart home is a piece of cake!

Table of Contents

  1. Prerequisite
  2. Wiring
  3. Configure ESPHome
    1. Create a device
    2. Add the PWM output
    3. Add the servo component
    4. Add the native API component
  4. Configure Home Assistant
    1. Add an input
    2. Add automation
  5. Add to the UI and Test the servo
  6. Adding multiple servos
    1. Second servo in ESPHome
    2. Second servo in Home Assistant
  7. Conclusion

Prerequisite

For this tutorial we will assume that you have Home Assistant up and running already.

A very basic knowledge of configuration.yaml would also be advantageous, but we will cover as much detail as possible.

If you are completely new to Home Assistant then you should probably first check out my beginners guide to YAML, as well as my tutorials on automation and scripts.

You will also need to configure the ESPHome add-on in Home Assistant and have a compatible ESP device such as the Wemos D1 Mini.

If you haven’t installed the ESP Home add-on yet, go ahead and check out my tutorial on how to add ESPHome to Home Assistant.

Wiring

A standard analogue RC servo usually has three wires and operates from 5 volts.

The red and black wire should be connected to +5V and ground respectively. The third lead is the control signal and is usually yellow or orange.

Servos are usually shipped with some variant of a standard pin header style connection with 2.54mm pin spacing.

For testing you can connect the servo to the ESP device using jumper wires.

The red positive lead is usually connected to the middle pin of the connector to prevent damage occurring if the connecter is inserted backwards.

As we are not connecting our RC servo to a standard RC receive, we need to take care that the connections are correct.

On our ESP device we need to connect the orange signal lead to one of the GPIOs. For this example I will connect the signal lead to D3 on the Wemos D1 Mini.

If you are using a different device and/or choose a different pin, don’t forget to amend the example code as necessary.

The black lead needs to be connected to ground, labelled as ‘G’ on the Wemos D1 Mini.

The red lead needs to be connected to 5 volts, labeled as ‘5V’ on the Wemos D1 Mini.

Note that larger servos and/or servos under a lot of load may draw more current than the USB port can supply.

If in doubt you should use an external 5 volt power supply.

If you are using an external power supply, the +5V connection should be made to the power supply and the ground terminals should be tied together.

The signal lead should be connected as normal, between the ESP device and servo.

Configure ESPHome

So now hopefully you have a servo connected to an ESP device as we are ready to start the configuration!

As previously mentioned you will need to have the ESPHome add-on installed in Home Assistant. Alternatively you can write your configurations with a text editor and upload them manually.

Create a device

First you should add a new device in ESPHome if you have not done so already.

When your new device is available in the user interface, go ahead and click edit and you should be presented with something like the following template.

Note that the parameters will reflect your device configuration.

esphome:
  name: little_wemos
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: "MyWiFiRouter"
  password: "************"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Little Wemos Fallback Hotspot"
    password: "************"

captive_portal:

# Enable logging
logger:

api:

ota:

Add the PWM output

First we will add an output component that tells ESPHome to send data to our servo.

As our servo requires a PWM signal for control we will use the esp8266_pwm platform. Note that you should change this if you are using an ESP32 board.

We need to specify the output pin that the servo’s signal lead is connected to. I am using pin D4 (GPIO02) but you should change this accordingly if you are using a different pin or device.

We also need to specify a PWM frequency of 50 Hz. We will give this output the ID pwm_output, which will be used to receive the data.

esphome:
  name: little_wemos
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: "MyWiFiRouter"
  password: "************"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Little Wemos Fallback Hotspot"
    password: "************"

captive_portal:

# Enable logging
logger:

api:

ota:

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

Add the servo component

Next we will add the servo component. This will act as the link between the output component that we just created and the incoming data from Home Assistant.

We will specify the output attribute as pwm_output in order to pass the data to the output.

We will also assign the ID attribute my_servo, which will be used to receive the data from Home Assistant.

esphome:
  name: little_wemos
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: "MyWiFiRouter"
  password: "************"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Little Wemos Fallback Hotspot"
    password: "************"

captive_portal:

# Enable logging
logger:

api:

ota:

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

servo:
  - id: my_servo
    output: pwm_output

Add the native API component

At the time of writing Home Assistant does not yet support the servo component directly (version 0.108). Therefore in order to communicate with Home Assistant we must set up the native API component in ESPHome.

The native API component is used to directly receive data from Home Assistant and it will allow us to create a custom service. We will then be able to reference the custom service in the Home Assistant setup.

Data will be passed from the custom service in Home Assistant to the native API component. It will then be passed from the native API to the output pin via the servo component.

First we will add a service under the api component and name it control_servo. We will also create a new float variable to store the value for the servo named level.

esphome:
  name: little_wemos
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: "MyWiFiRouter"
  password: "************"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Little Wemos Fallback Hotspot"
    password: "************"

captive_portal:

# Enable logging
logger:

api:
  services:
    - service: control_servo
      variables:
        level: float

ota:

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

servo:
  - id: my_servo
    output: pwm_output

Home Assistant will create a custom service named esphome.<your-device>_control_servo and the data will be passed from this service to the variable level within the ESPHome device.

Next we will write the value level to the servo component that we created earlier, named my_servo.

esphome:
  name: little_wemos
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: "MyWiFiRouter"
  password: "************"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Little Wemos Fallback Hotspot"
    password: "************"

captive_portal:

# Enable logging
logger:

api:
  services:
    - service: control_servo
      variables:
        level: float
      then:
        - servo.write:
            id: my_servo
            level: !lambda 'return level / 100.0;'

ota:

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

servo:
  - id: my_servo
    output: pwm_output

That completes the ESPHome configuration! Now you can go ahead and upload the code to your device.

Configure Home Assistant

Next we will configure Home Assistant by adding an automation to control to servo.

For this example we will link the automation to a slider on the user interface, but you can adapt the code to something more suited to your own application.

Add an input

Firstly we need to create a way to input a value in Home Assistant to send to our servo.

We will do this by adding a simple slider that will control the movement of the servo.

In order to add a slider we will create a new instance of input_number in the configuration.yaml file called servo_control.

We will give it minimum and maximum values of -100 and 100 respectively. We will also set the initial value to 0 and step value to 1.

input_number:
  servo_control:
    initial: 0
    min: -100
    max: 100
    step: 1
    mode: slider

Add automation

In order to link our slider to the ESPHome device we need to create a new automation instance in the configuration.yaml file.

The entity for the trigger should be set to the slider entity that we just created. The action will then be fired whenever the slider changes value.

In the action section we will set the service to our ESPHome custom service created by the native API.

You will find this in your list of services as esphome.<your-device>_control_servo.

In order to push the value from the slider to the native API via the service, we will use a data template.

This template fetches the value of the slider and puts it into our level variable within ESPHome.

automation:
  - alias: Write Servo Value to ESP
    trigger:
      platform: state
      entity_id: input_number.servo_control
    action:
      - service: esphome.little_wemos_control_servo
        data_template:
          level: '{{ trigger.to_state.state | int }}'

Add to the UI and Test the servo

Now we are ready to give the servo a test! Go ahead and add input_number.servo_control to the user interface.

You can add the switch for the automation too, if you would like the option to disable the servo movement.

Adding multiple servos

If multiple servos are needed, we can simply replicate each stage of the code using a unique name. Let’s add a second servo and call it servo2, first we will add it to ESPHome.

Second servo in ESPHome

First we can add a second instance to the api communication.

We must differentiate the new servo by giving it a unique name. In this example I have simply called it servo2.

esphome:
  name: little_wemos
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: "MyWiFiRouter"
  password: "************"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Little Wemos Fallback Hotspot"
    password: "************"

captive_portal:

# Enable logging
logger:

api:
  services:
    - service: control_servo
      variables:
        level: float
      then:
        - servo.write:
            id: my_servo
            level: !lambda 'return level / 100.0;'
    - service: control_servo2
      variables:
        level: float
      then:
        - servo.write:
            id: my_servo2
            level: !lambda 'return level / 100.0;'

ota:

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

servo:
  - id: my_servo
    output: pwm_output

Next we need to add a second PWM output. Here we can specify the pin that we wish to use for the second servo.

esphome:
  name: little_wemos
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: "MyWiFiRouter"
  password: "************"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Little Wemos Fallback Hotspot"
    password: "************"

captive_portal:

# Enable logging
logger:

api:
  services:
    - service: control_servo
      variables:
        level: float
      then:
        - servo.write:
            id: my_servo
            level: !lambda 'return level / 100.0;'
    - service: control_servo2
      variables:
        level: float
      then:
        - servo.write:
            id: my_servo2
            level: !lambda 'return level / 100.0;'

ota:

output:
  - platform: esp8266_pwm
    id: pwm_output
    pin: D3
    frequency: 50 Hz
  - platform: esp8266_pwm
    id: pwm_output2
    pin: D2
    frequency: 50 Hz

servo:
  - id: my_servo
    output: pwm_output

Finally we will add an instance for the second servo to tie the incoming data from the servo2 Home Assistant service to the physical PWM output.

esphome:
  name: little_wemos
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: "MyWiFiRouter"
  password: "************"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Little Wemos Fallback Hotspot"
    password: "************"

captive_portal:

# Enable logging
logger:

api:
  services:
    - service: control_servo
      variables:
        level: float
      then:
        - servo.write:
            id: my_servo
            level: !lambda 'return level / 100.0;'
    - service: control_servo2
      variables:
        level: float
      then:
        - servo.write:
            id: my_servo2
            level: !lambda 'return level / 100.0;'

ota:

output:
  - platform: esp8266_pwm
    id: pwm_output
    pin: D3
    frequency: 50 Hz
  - platform: esp8266_pwm
    id: pwm_output2
    pin: D2
    frequency: 50 Hz

servo:
  - id: my_servo
    output: pwm_output
  - id: my_servo2
    output: pwm_output2

Second servo in Home Assistant

Now we can add the second servo to our Home Assistant user interface. First let’s add a second input_number slider for controlling our second servo.

Again in this example I am simply naming it with a numerical sequence.

input_number:
  servo_control:
    name: Servo Control
    initial: 0
    min: -100
    max: 100
    step: 1
    mode: slider
    
  servo_control2:
    name: Servo Control
    initial: 0
    min: -100
    max: 100
    step: 1
    mode: slider

Now we can add a second automation. If you have configured ESPHome correctly, you should find a second service available in Home Assistant called esphome.<your-device-name>_control_servo2.

automation:
  - alias: Write Servo Value to ESP
    trigger:
      platform: state
      entity_id: input_number.servo_control
    action:
      - service: esphome.wemos_d1_mini_control_servo
        data_template:
          level: '{{ trigger.to_state.state | int }}'

  - alias: Write Servo2 Value to ESP
    trigger:
      platform: state
      entity_id: input_number.servo_control2
    action:
      - service: esphome.wemos_d1_mini_control_servo2
        data_template:
          level: '{{ trigger.to_state.state | int }}'

Finally we can add the slider and automation to the user interface in order to control the second servo. You can repeat this process in order to add even more servos. Awesome!

Conclusion

In this tutorial we learnt how to harness the power of ESPHome to make a bespoke integration within Home Assistant.

The ability to control servos adds many possibilities to the smart home ideas repertoire and could be further adapted for devices larger than an RC servo.

Why not go check out some of my other awesome Home Assistant tutorials to get some inspiration for your next ESPHome device!

Thanks so much for visiting my site! If this article helped you achieve your goal and you want to say thanks, you can now support my work by buying me a coffee. I promise I won't spend it on beer instead... 😏

25 thoughts on “Home Assistant & ESPHome Servo Example (Step-by-Step guide)”

  1. Hello! Thanks for your perfect how to..
    One question only, is it possible to control multiple servos? I am trying but no luck.. What part of the code needs to be added for a second servo?
    Thanks in advance!

      1. Thanks one more time for your help!! It works great!
        Your blog is very helpfull!! Keep up the perfect job!

  2. Hello,
    Congratulations on the page!
    Very didactic and easy to understand.
    When will I write the level of value in the servo component

    then:
             – servo.write:
                 id: my_servo
                 level:! lambda ‘return level / 100.0;’

    It gives the following error:
    unknown tag! at line 29, column 51:
          …! lambda ‘return level / 100.0;’
                                              ^
    Any idea what I’m doing wrong?
    I’ve done and reifz several times and it always gives this problem.
    Thank you very much for your attention

    1. Hey Mateus, thanks for visiting! It looks like you have ‘!’ in the wrong place, it should be next to the lambda…

      level:! lambda ‘return level / 100.0;’

      change to…

      level: !lambda ‘return level / 100.0;’

  3. hi, nice tutorial, but why i can’t add the automation on the service esphome
    it’s say : Failed to call service esphome/ultrasonic_control_servo. extra keys not allowed @ data[‘automation’]

      1. Hi Andrew, I would need some more info to help. Are you receiving any errors? Can you confirm that the servo works/have you tried it with a different servo? When I first set this up I could not get it to work for a while and it turned out that I was using a faulty servo! As soon as I swapped the servo, it worked fine. Are you supplying your servo with adequate power? A larger servo may require from an external power source.

  4. Hi Siytek,

    Nide . Your HA tutorials are helpful in my home automation project.

    I need one help. I am using a stepper/ servo motor to control my camera to rotate and capture a 180 degree view . But I am facing an issue with my stepper motor where if power is switched off for the motor and then I move the input slider to different position, the stepper position does not recognize and when it comes up back then the whole things takes a different position all together making the camera view angle totally different.

    Is there a way in servo motor (or stepper motor whichever is easier) where I can tell my motor to retain the position after boot according to my input slider in Home assistant.

  5. Hi! thank you for this tutorial very easy to understand for a noob like me.
    But, where can i locate “configuration.yaml file called servo_control” ?
    Is it the same /config/configuration.yaml ??

  6. Markus granberg

    Hi! This works great! However the servo is always active. Is there a way to add a “switch” to espeasy to attach/detach servo? It’s easy to connect the servo power to a pin on the esp but this should be possible in software.

    Keep up the good work!

  7. Is there any relationship between “servo_control” (as used in the input section) and the service called “control_servo” (as referenced in the ESPHOME code)?
    What is the relationship between these two – I thought it should be the same in both cases?

  8. I’m pulling my hair out with this.

    I’ve recreated the above instructions and keep getting an error.

    ‘The automation “Write Servo Value to ESP” (automation.write_servo_value_to_esp) has an action that calls an unknown service: esphome.little_wemos_control_servo’

    Any advice on what I should check?

    1. Try checking your services using developers tools, do a search for “esphome.” – what services do you see? You may also get some clues here: https://community.home-assistant.io/t/unexpected-service-call-check-for-automation-that-has-not-triggered/458257

      Sorry that you are having trouble, this article is fairly outdated at the moment. I am travelling at the moment so don’t have access to an ESP nor servo to text/update the article, however it is on my list of articles to update asap! Thanks

  9. Pieter O'Connell

    Hi. Thank you for a great video. I just want to know if the set up and config would be the same for a Pi Pico W connected to a Servo ? Im having issues with this, but suspect it could be power related? Any pointers would be highly appreciated

    1. Hi Pieter, yes I imagine the setup would be the same for the Pi Pico as they both use a 3V3 GPIO. The servo should be powered from its own 5V source, I don’t think the Pico has a 5V output anyway. Maybe put a current limiting resistor in series between the servo signal and GPIO, something like 10k should do it. If you have access to an oscilloscope, you can check if the PWM signal looks correct. More info about PWM here: https://siytek.com/how-does-led-dimming-work/#Duty-cycle

  10. Hello Simon
    Your template is great. I have a problem. Where do I put this?

    #Servo_control
    input_number:
    servo_control:
    initial: 0
    min: -100
    max: 100
    step: 1
    mode: slider

    automation:
    – alias: Write Servo Value to ESP
    trigger:
    platform: state
    entity_id: input_number.servo_control
    action:
    – service: esphome.esphome-web-6659be_control_servo
    data_template:
    level: ‘{{ trigger.to_state.state | int }}’

    In the configuration.yaml or to the automations?

    Thanks a lot!!

    Michael

  11. This bit:

    #Servo_control
    input_number:
    servo_control:
    initial: 0
    min: -100
    max: 100
    step: 1
    mode: slider

    goes in configeration.yaml
    and this bit:

    automation:
    – alias: Write Servo Value to ESP
    trigger:
    platform: state
    entity_id: input_number.servo_control
    action:
    – service: esphome.esphome-web-6659be_control_servo
    data_template:
    level: ‘{{ trigger.to_state.state | int }}’

    Goes in automations. but i had errors for mine. found it easier to jsut make a new automation with the visual editor, and create it that way. than for the servie part, switch it to the yaml editor and copy the code he used

Leave a Reply to Siytek Cancel Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

The reCAPTCHA verification period has expired. Please reload the page.

Scroll to Top