PWM Servo control with ESPhome?

Ah thanks, I see. Yes I can imagine that without adjusting for more dead band, it would be devilishly hard to get it to stop. To make the feeder device he linked though, why would it need to spin 360 continuous, it looks like that one used a normal travel range ?
(a rhetorical question no need to answer :slight_smile:

I agree with you, probs not necessary to do a full 360. Hopefully @Khalid_Abu_Hamza got it sorted?

Got a feather huzzah32 and an sg90 servo; all I get is buzzing and some grinding when I move the servo control (as outlined on the esphome website).
Power is bypassing the esp (from the same wall wart 2.5a)…

“Experiment with” the values for min_level and max_level, but all it seemed to do is change the pitch at which the servo squealed (occasionally moving a bit).

Got this to work on the nodeMCU, but I need a small micro controller, tried the esp32 pico and couldn’t get it to work either (same deal)… is this a limitation on some controllers?

Any help would be greatly appreciated!

``
servo:

  • id: my_servo
    output: cerveau

output:

  • platform: ledc
    pin: GPIO21
    id: cerveau
    frequency: 50 Hz
    min_power: 2.5% # 5% at 50Hz is 1mS (20mS cycles) was 5
    max_power: 9% # 10% at 50Hz is 2mS (20mS cycles) was 10
    ``

Without giving your issue too much thought, the ‘grinding’ you describe sounds like what an SG90 does when it’s got stripped teeth on the main gear or spur. Might pay to take the 4 screws out of the bottom case and carefully inspect the gears just below the top case…

Thanks for responding!
I thought the same so I tried all the servos in the batch I got, and they all behave the same…
…if they are seizing what am I doing wrong?

Thanks again

Well not sure now. Although setting servo pulse outside the 1mS to 2mS range could have unknown effects on some of them.
Here’s my code for the ESP32 project I made, if it helps. Not been updated for a year or so.


# Using Light component to set a standard RC servo position
light:
  - platform: monochromatic
    output: gpio_21_servo
    name: "Servo actuator #1 - Servo Pos"
    default_transition_length: 0ms

output:
  - platform: ledc
    pin: GPIO21
    id: gpio_21_servo
    frequency: 50 Hz
    min_power: 5.0%    # 5% at 50Hz is 1mS  (20mS cycles)
    max_power: 10.0%   # 10% at 50Hz is 2mS (20mS cycles)

A snip of the IF/THEN action block...
          then:
            - output.turn_on: yellow_led
#            - output.turn_on: esp32_blue_led
            - output.set_level:
                id: gpio_21_servo
                level: 100.0% # 100% = 2mS as set by 'max_power'
            - delay: 3000ms #700ms     
            - output.set_level:
                id: gpio_21_servo
                level: 0.0%  # 0% = 1mS as set by 'min_power'

Hi

Is it possible to have 2 sg90 controlled by nodemcu ?
Only one works with my esphome config…

I did two. I put a delay so both weren’t pulling at the same time.
*Disclaimer: I had issues with connecting the servos to the blind shaft and haven’t had time to attempt to fix. These are not in production at the moment. One day my wife will kill me since those two blinds can’t be opened.
Also the level and open duration were me tweaking it so that it just opened the blinds enough because just a few milliseconds to many and it’d fall back closed the other way.
These were continuous rotation servos.

switch:
  - platform: gpio
    name: "Living Room Front Blinds Status Light"
    pin: 2
    inverted: True

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

# Example output platform
# On ESP32, use ledc output
output:
  - platform: esp8266_pwm
    id: pwm_output
    pin: D5
    frequency: 50 Hz

  - platform: esp8266_pwm
    id: pwm_output2
    pin: D6
    frequency: 50 Hz

cover:
  - platform: time_based
    name: "Living Room Front Blinds Left"
    id: cover1
    open_action:
#      then:
      - servo.write:
          id: my_servo
          level: -2.0%
    open_duration: 1.2sec

    close_action:
#      then:
      - servo.write:
          id: my_servo
          level: 20.0%
    close_duration: 2.3sec

    stop_action:
#      then:
      - servo.write:
          id: my_servo
          level: 0%
      - servo.detach: my_servo
###############################
  - platform: time_based
    name: "Living Room Front Blinds Right"
    id: cover2
    open_action:
#      then:
      - servo.write:
          id: my_servo2
          level: -2.0%
    open_duration: 3.5sec

    close_action:
#      then:
      - servo.write:
          id: my_servo2
          level: 20.0%
    close_duration: 1sec

    stop_action:
#      then:
      - servo.write:
          id: my_servo2
          level: 0%
      - servo.detach: my_servo2

HI thanks for your reply.

I tested that but sadly i have the same problem.

my esphome yaml :

# Example configuration entry
servo:
  - id: my_servo
    output: pwm_output
    transition_length: 1s
    auto_detach_time : 1s
  - id: my_servo2
    output: pwm_output2
    transition_length: 1s
    auto_detach_time: 1s
    
# Example output platform
# On ESP32, use ledc output
output:
  - platform: esp8266_pwm
    id: pwm_output
    pin: D5
    frequency: 50 Hz
  - platform: esp8266_pwm
    id: pwm_output2
    pin: D6
    frequency: 50 Hz
    
cover:
  - platform: time_based
    name: "Test1"
    id: cover1
    open_action:
#      then:
      - servo.write:
          id: my_servo
          level: -2.0%
    open_duration: 1.2sec

    close_action:
#      then:
      - servo.write:
          id: my_servo
          level: 20.0%
    close_duration: 2.3sec

    stop_action:
#      then:
      - servo.write:
          id: my_servo
          level: 0%
      - servo.detach: my_servo
###############################
  - platform: time_based
    name: "Test2"
    id: cover2
    open_action:
#      then:
      - servo.write:
          id: my_servo2
          level: -2.0%
    open_duration: 3.5sec

    close_action:
#      then:
      - servo.write:
          id: my_servo2
          level: 20.0%
    close_duration: 1sec

    stop_action:
#      then:
      - servo.write:
          id: my_servo2
          level: 0%
      - servo.detach: my_servo2    

and in debug logs :

[17:02:17][C][time_based.cover:012]: Time Based Cover 'Test2'
[17:02:17][C][time_based.cover:012]:   Assumed State: YES
[17:02:17][C][time_based.cover:013]:   Open Duration: 3.5s
[17:02:17][C][time_based.cover:014]:   Close Duration: 1.0s
[17:02:17][C][captive_portal:169]: Captive Portal:
[17:02:17][C][ota:029]: Over-The-Air Updates:
[17:02:17][C][ota:030]:   Address: 192.168.3.143:8266
[17:02:17][C][ota:032]:   Using Password.
[17:02:17][C][api:095]: API Server:
[17:02:17][C][api:096]:   Address: 192.168.3.143:6053
[17:02:17][D][debug:023]: ESPHome version 1.16.1
[17:02:17][D][debug:025]: Free Heap Size: 29896 bytes
[17:02:17][D][debug:053]: Flash Chip: Size=4096kB Speed=40MHz Mode=DIO
[17:02:17][D][debug:190]: Chip ID: 0x00D61960
[17:02:17][D][debug:191]: SDK Version: 2.2.2-dev(38a443e)
[17:02:17][D][debug:192]: Core Version: 2_7_4
[17:02:17][D][debug:193]: Boot Version=6 Mode=1
[17:02:17][D][debug:194]: CPU Frequency: 80
[17:02:17][D][debug:195]: Flash Chip ID=0x0016405E
[17:02:17][D][debug:196]: Reset Reason: Software/System restart
[17:02:17][D][debug:197]: Reset Info: Software/System restart
[17:02:19][VV][api.service:220]: on_ping_request: PingRequest {}
[17:02:19][VV][api.service:032]: send_ping_response: PingResponse {}
[17:02:22][VV][api.service:220]: on_ping_request: PingRequest {}
[17:02:22][VV][api.service:032]: send_ping_response: PingResponse {}
[17:02:22][VV][api.service:333]: on_execute_service_request: ExecuteServiceRequest {
  key: 2387861054
  args: ExecuteServiceArgument {
  bool_: NO
  legacy_int: 0
  float_: -22
  string_: ''
  int_: 0
}
}
[17:02:22][D][servo:059]: Servo new target: -0.220000
[17:02:23][D][servo:049]: Servo reached target
[17:02:24][D][servo:027]: Servo detached on auto_detach_time
[17:02:27][VV][api.service:220]: on_ping_request: PingRequest {}
[17:02:27][VV][api.service:032]: send_ping_response: PingResponse {}
[17:02:28][VV][api.service:333]: on_execute_service_request: ExecuteServiceRequest {
  key: 1275517864
  args: ExecuteServiceArgument {
  bool_: NO
  legacy_int: 0
  float_: 19
  string_: ''
  int_: 0
}
}
[17:02:28][D][servo:059]: Servo new target: 0.190000
[17:02:28][D][servo:049]: Servo reached target
[17:02:29][D][servo:027]: Servo detached on auto_detach_time
[17:02:32][VV][api.service:220]: on_ping_request: PingRequest {}

The first SG90 works perfectly but nothing with my second … and no trace in logs for ESP everything is good, it sends the order to motor but nothing turn …

i test some different SG90 and Nodemcu always the same result the first servo is ok the second nothing…

Try forcing a log entry to see if that portion of your code is working?

- logger.log: "Hello World"

yes traces helps :slight_smile:

but not in my case :
my code :

api:
  password: "xxxxxx"
  services:
    - service: control_servo
      variables:
        level: float
      then:
        - logger.log: "Servo 1"
        - servo.write:
            id: my_servo
            level: !lambda 'return level / 100.0;'
    - service: control_servo2
      variables:
        level: float
      then:
        - logger.log: "Servo 2"
        - servo.write:
            id: my_servo2
            level: !lambda 'return level / 100.0;'          

[18:44:41][VV][api.service:333]: on_execute_service_request: ExecuteServiceRequest {
  key: 2387861054
  args: ExecuteServiceArgument {
  bool_: NO
  legacy_int: 0
  float_: 8
  string_: ''
  int_: 0
}
}
[18:44:41][D][main:213]: Servo 1
[18:44:41][D][servo:059]: Servo new target: 0.080000
[18:44:41][D][servo:049]: Servo reached target
[18:44:42][D][servo:027]: Servo detached on auto_detach_time
[18:44:43][VV][api.service:220]: on_ping_request: PingRequest {}
[18:44:43][VV][api.service:032]: send_ping_response: PingResponse {}
[18:44:45][VV][api.service:333]: on_execute_service_request: ExecuteServiceRequest {
  key: 1275517864
  args: ExecuteServiceArgument {
  bool_: NO
  legacy_int: 0
  float_: 23
  string_: ''
  int_: 0
}
}
[18:44:45][D][main:381]: Servo 2
[18:44:45][D][servo:059]: Servo new target: 0.230000
[18:44:45][D][servo:049]: Servo reached target
[18:44:46][D][servo:027]: Servo detached on auto_detach_time
[18:44:48][VV][api.service:220]: on_ping_request: PingRequest {}
[18:44:48][VV][api.service:032]: send_ping_response: PingResponse {}
[18:44:49][VV][api.service:220]: on_ping_request: PingRequest {}
[18:44:49][VV][api.service:032]: send_ping_response: PingResponse {}
[18:44:53][VV][api.service:220]: on_ping_request: PingRequest {}
[18:44:53][VV][api.service:032]: send_ping_response: PingResponse {}
[18:44:54][VV][api.service:333]: on_execute_service_request: ExecuteServiceRequest {
  key: 1275517864
  args: ExecuteServiceArgument {
  bool_: NO
  legacy_int: 0
  float_: -32
  string_: ''
  int_: 0
}
}

So ESP call my control_servo2 but servo doesn’t react :confused:

I’ve tried to make it work with an ESP32-cam board.
Servo works perfect. Camera works perfect. But not together :face_with_symbols_over_mouth:
When I comment out the camera, the servo works, and when I comment out the servo, the camera works. When both settings are in - the servo and the camera wont work.
Maybe the camera needs all of the IO pins, or takes too much memory that the servo needs, which is weird because I’ve already made that board work with the built-in camera and a few other sensors (PIR sensor, light, TH)
Oh and I’ve tried almost all of the IO pins…

Anyway, here is my code for that board, maybe it will help someone, and maybe even someone has a solution for both to work - spent days on this S***… All I’ve ever wanted is a cheap camera with one cheap servo that connects to homeassistant and makes me some coffe and cookies. But I guess that’s too much to ask :wink:

esphome:
  name: office_camera
  platform: ESP32
  board: esp32cam
  
wifi:
  ssid: "****"
  password: "****"

captive_portal:

# Enable logging
logger:

# Enable Home Assistant API
api:
 services:
    - service: control_servo
      variables:
        level: float
      then:
        - servo.write:
            id: pwm_servoR
            level: !lambda 'return level / 100.0;'
ota:

# esp32_camera:
#   name: office camera
#   external_clock:
#     pin: GPIO0
#     frequency: 20MHz
#   i2c_pins:
#     sda: GPIO26
#     scl: GPIO27
#   data_pins: [GPIO5, GPIO18, GPIO19, GPIO21, GPIO36, GPIO39, GPIO34, GPIO35]
#   vsync_pin: GPIO25
#   href_pin: GPIO23
#   pixel_clock_pin: GPIO22
#   power_down_pin: GPIO32
#   vertical_flip: true
  
  
sensor:
  - platform: wifi_signal
    name: "WiFi Signal office cam"
    update_interval: 60s
    id: signalWgm

switch:
  - platform: restart
    name: "Restart office cam"
  
output:
  # Flashlight 
  - platform: gpio
    pin: GPIO4
    id: gpio_4
    
  # Servo Rotate : Using ledc output to control a standard 50Hz RC servo over the normal 1 to 2 mS pulse range
  - platform: ledc
    pin: GPIO14
    id: servoR
    frequency: 50 Hz
    
servo:
  - id: pwm_servoR
    output: servoR    
    
# Flashlight    
light:
  - platform: binary
    output: gpio_4
    name: "Office camera light"

Is there an amperage issue?
I got lucky and was able to use the 5V out but I made sure that I turned off the first servo after it was done, then on the second one, did the action, then turned it off.

Can confirm. on ESP32-CAM either the camera works or the servos. But not both. Looks like I have to hook up an additional esp8266 only for the servos.
[EDIT]
Well, it seems I didn’t read the docs completely. My bad.

Note

Camera uses PWM timer #1. If you need PWM (via the ledc platform) you need to manually specify a channel there (with the channel: 2 parameter)

@keithcroshaw @Khalid_Abu_Hamza @Olivier974 @mr.sneezy @Drew.B

Hi All,

I’m hoping for some help - I’m trying to control a servo from an esp32 and have followed the guides, but I must be doing something wrong as the servo doesn’t move. I’ve setup esphome and tested with turning on an led via home assistant, which works, so I believe I’m okay for pin numbers etc. I’ve also previously tested controlling the servo directly from the esp32 via micropython and that worked, so I’m pretty happy with my wiring up etc.

I’m using AZDelivery ESP32 NodeMCU Dev Kit C WiFi WLAN CP2102 ESP32-WROOM-32D from Amazon UK. It isn’t listed in the board types. I’ve tried a few, including nodemcu with no luck.

Code in ESPHome - robogarage.yaml

esphome:
  name: robogarage
  platform: ESP32
  board: esp32dev

# Enable logging
logger:

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

ota:
  password: "abcdefg"

wifi:
  ssid: "xxxxxxxx"
  password: "xxxxxxxx"

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

captive_portal:

servo:
  - id: my_servo
    output: pwm_output

output:
  - platform: ledc
    id: pwm_output
    pin: GPIO32
    frequency: 50 Hz

Then, in my configuration.yaml I have:

input_number:
  servo_control:
    name: 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.robogarage_control_servo
        data_template:
          level: '{{ trigger.to_state.state | int }}'

and finally, the lovelace card:

type: entities
entities:
  - input_number.servo_control

If anyone can see ANYTHING obvious (or not so obvious) that I have done incorrectly or misunderstood, I would be incredibly grateful for your thoughts.

Maybe you’re still using the LED platform?
It might not matter but that might be a typo you forgot to change back.
I’ve only ever seen the pins be like D5 in my example, but I’ve only ever used the one board type.
And you don’t have an end device declared like the cover in my example.
Again that might not be necessary but I’m just looking what’s different between our esphome config files.

Steal mine and see if you can at least get it to twitch or something then work backwards from there?
ESPHome has been 100% trial and error for me.
Good luck!

Hello,

look at my last post : https://community.home-assistant.io/t/pwm-servo-control-with-esphome/111762/23

it only work for me with " " not ’ ’

try

@Olivier974 & @keithcroshaw
Thank you both so much for your time and input. I tried all your suggestions but didn’t have any luck. However, I have now identified my embarrassing mistake - I put the automation code in the config yaml and not the automations one. :flushed: I guess we live and learn!
Thanks again,

2 Likes

lol!

nice you find your mistake :wink:

1 Like

Solved! (My issue anyways)

For me there were two issues.

  1. Feather huzzah 32 simply did not want to servo… Just nope :person_shrugging:

  2. Since there were other pwm devices, there was interference or something. Took me forever to figure this out but I had to add channels. What’s important is that the servo needed to be non adjacent to the following channels. So servo on chanel 1 skip a channel then the next device on channel 3. In the esphome docs there’s a line that says two adjacent channels share the same timer.

I hope this helps someone!

2 Likes