Skip to content

Chapter 3: PWM Servo and ESC Control

3.1 Key Points

  • Standard RC PWM: 50 Hz period and 1000–2000 µs pulse width
  • Duty calculation with LEDC 14-bit resolution
  • ESC arming sequence: hold neutral for about two seconds
  • Dual-channel configuration for throttle and steering

3.2 Course Content

Both the ESC and steering servo use standard RC PWM. A 1000 µs pulse represents the minimum command, 1500 µs is neutral, and 2000 µs is the maximum command. OSRCORE uses LEDC TIMER0 with two channels: GPIO1 for throttle and GPIO2 for steering.

3.3 Basic Learning

RC PWM Timing

text
Period: 20 ms (50 Hz)
Pulse range: 1000 µs to 2000 µs
Neutral: 1500 µs

Duty Calculation

With an 80 MHz clock and 14-bit LEDC resolution:

text
period_ticks = 80_000_000 / 50 = 1_600_000
1 µs = 80 ticks
duty = pulse_us × 80

Examples:

text
1500 µs -> 120000
2000 µs -> 160000

A convenient macro is:

c
#define US_TO_DUTY(us) ((us) * 80)

ESC Arming

Many ESCs require a neutral input signal after power-up before accepting throttle commands. Holding 1500 µs for one to three seconds is a common arming sequence.

3.4 Program Study

Configure the timer and two PWM channels:

c
ledc_timer_config_t timer = {
    .speed_mode      = LEDC_LOW_SPEED_MODE,
    .duty_resolution = LEDC_TIMER_14_BIT,
    .timer_num       = LEDC_TIMER_0,
    .freq_hz         = 50,
    .clk_cfg         = LEDC_AUTO_CLK,
};
ledc_timer_config(&timer);

ledc_channel_config_t ch_throttle = {
    .gpio_num = 1,
    .channel = LEDC_CHANNEL_0,
    .timer_sel = LEDC_TIMER_0,
    .duty = US_TO_DUTY(1500),
};
ledc_channel_config(&ch_throttle);

ledc_channel_config_t ch_steering = {
    .gpio_num = 2,
    .channel = LEDC_CHANNEL_1,
    .timer_sel = LEDC_TIMER_0,
    .duty = US_TO_DUTY(1500),
};
ledc_channel_config(&ch_steering);

Set pulse width and arm the ESC:

c
static void set_pulse(ledc_channel_t ch, uint32_t pulse_us)
{
    ledc_set_duty(LEDC_LOW_SPEED_MODE, ch, US_TO_DUTY(pulse_us));
    ledc_update_duty(LEDC_LOW_SPEED_MODE, ch);
}

set_pulse(CH_THROTTLE, 1500);
set_pulse(CH_STEERING, 1500);
vTaskDelay(pdMS_TO_TICKS(2000));

3.5 Summary

This chapter covered RC PWM timing, LEDC 14-bit duty calculation, and ESC arming. The US_TO_DUTY() macro maps physical pulse width directly to the LEDC duty value used by later control loops.


Built for OSRCORE robot development board.