Skip to content
Back to skills

Esp32 Pwm And Timing

ASecurity

Use when driving an LED, a motor, a servo or a buzzer with PWM on ESP32 - the frequency is wrong, the resolution collapses, the servo jitters, or the duty value does nothing. Covers LEDC's frequency/resolution trade-off.

  • 16 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added November 7, 2026
ai-agentsgoawsapi

Works with

  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned November 7, 2026

npx -y skills add Esmail-sarhadi/esp32-agent-skills --skill esp32-pwm-and-timing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Esp32 Pwm And Timing?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Esp32 Pwm And Timing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/esmail-sarhadi-esp32-pwm-and-timing/badge)](https://www.skillsdirectory.com/skills/esmail-sarhadi-esp32-pwm-and-timing)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: esp32-pwm-and-timing
description: Use when driving an LED, a motor, a servo or a buzzer with PWM on ESP32 - the frequency is wrong, the resolution collapses, the servo jitters, or the duty value does nothing. Covers LEDC's frequency/resolution trade-off.
license: MIT
---

# PWM and timing on ESP32

The ESP32 does not have "PWM pins". It has the LEDC peripheral: 4 timers and 16
channels, and every channel borrows a timer. The frequency and the resolution
are not independent, and that is the source of most surprises.

## The trade-off, in one line

```
frequency = 80 MHz / (divider × 2^resolution)
```

So at a fixed clock, resolution and frequency trade against each other:

| Resolution | Steps | Highest frequency |
|---|---|---|
| 8 bit | 256 | ~312 kHz |
| 10 bit | 1024 | ~78 kHz |
| 12 bit | 4096 | ~19.5 kHz |
| 16 bit | 65536 | ~1.2 kHz |

Ask for 16-bit resolution at 5 kHz and the driver cannot deliver it: it silently
lowers the resolution or the frequency, and the duty value you compute no longer
maps to the brightness you expected.

**Rule of thumb:** pick the frequency the load needs, then take the highest
resolution that still allows it.

## What different loads need

| Load | Frequency | Why |
|---|---|---|
| LED brightness | 1-5 kHz | above the flicker the eye sees when moving |
| RGB LED strip (WS2812) | not PWM | these are driven by a data protocol, not LEDC |
| Servo | 50 Hz | the pulse width, not the frequency, is the signal |
| DC motor | 20-25 kHz | above hearing, and smoother at low duty |
| Buzzer (passive) | the note frequency | 440 Hz for A4, and duty should be 50% |

A servo at 1 kHz will not work. A motor at 50 Hz will whine audibly.

## Duty is a count, not a percentage

The duty argument is `0 .. 2^resolution - 1`, not `0..100`:

```cpp
const int res = 10;                  // 1024 steps
const int maxDuty = (1 << res) - 1;  // 1023
ledcWrite(channel, (percent * maxDuty) / 100);
```

Writing `ledcWrite(channel, 100)` on a 10-bit channel is 10% brightness, not
100%. This is the single most common "the LED is dim and won't go brighter" bug.

## API changed in Arduino core 3.x

```cpp
// core 2.x
ledcSetup(channel, freq, resolution);
ledcAttachPin(pin, channel);

// core 3.x
ledcAttach(pin, freq, resolution);
ledcWrite(pin, duty);          // the pin, not the channel
```

Code written for one does not compile on the other. When a sketch "worked
yesterday", check which core version is installed before rewriting the logic.

## Servos

A servo wants a 1-2 ms pulse every 20 ms. With LEDC:

- frequency 50 Hz, resolution 16 bit (the divider works out),
- duty for 1 ms = `65536 × 1 ms / 20 ms ≈ 3277`,
- duty for 2 ms ≈ 6554.

If the servo jitters, the cause is usually the power supply, not the timing: a
servo draws a current spike at every move that will brown out a shared 5 V rail.

## Rules for the agent

- Always compute the duty from the resolution in use. Never hardcode a duty
  number without showing the resolution it assumes.
- State the frequency and the resolution in the code, next to the call, with the
  reason. "PWM at 5 kHz, 10 bit, so the motor does not whine" is a comment worth
  writing.
- If the user says the output is dim or does not change, check the resolution
  and the duty range before touching the wiring.
- Never drive a WS2812 with LEDC. It needs a 800 kHz data protocol, not a PWM
  duty cycle.

Files in this skill

  • LICENSE.txt857 B
  • SKILL.md3.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in ai-agents →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.