analogWrite does nothing on ESP32: use ledcWrite or the new core

On older ESP32 cores analogWrite does not exist. Use the LEDC functions instead.

What the message means

The ESP32 generates PWM with its LEDC peripheral. You choose the channel, frequency and resolution and attach it to a pin.

Fix it in this order

  1. Check the version of the ESP32 board package, since newer versions add analogWrite.
  2. For older cores call ledcSetup(channel, frequency, bits), then ledcAttachPin(pin, channel).
  3. Write the value with ledcWrite(channel, duty).
  4. Pick a frequency to suit the load: about 1 kHz for LEDs and 20 kHz for quiet motors.
  5. Check that the pin can be an output.
  6. The function names changed in version 3 of the core, so read the notes for your version.

Why it happens

The ESP32 produces PWM with a hardware block called LEDC rather than with fixed timer pins like the classic Uno. You choose a channel, a frequency and a resolution, and then connect that channel to a pin.

Older versions of the board package had no analogWrite, so code written for an Uno failed to compile or did nothing. Newer versions add it on top of the same hardware, so check your package version, and note that the function names for LEDC changed between major versions of the package.

If you see this, do this

You seeDo this
Compile error on analogWriteOlder core. Use ledc.
ledcSetup missingNewer core. Use the new calls.

PWM motor whine, ESP32 pinout.

Last reviewed: November 2026. These are the usual causes and the order that finds them fastest. Test on your own board; clones and cables differ.

Why this happens

The ESP32 has no digital-to-analog PWM in the way an Uno does. It has a hardware LED controller (LEDC) with a number of channels, each with its own frequency and resolution. In Arduino-ESP32 core 2.x there was no analogWrite at all, and you had to set up a channel with ledcSetup, attach a pin with ledcAttachPin and write with ledcWrite. Core 3.x changed the interface: ledcAttach takes a pin, a frequency and a resolution, and ledcWrite then takes the pin. Core 3.x also added analogWrite.

So the code that compiles depends on the core version installed in the Boards Manager, not on the board you bought. A sketch copied from a core 2.x tutorial may not compile on core 3.x, and the reverse.

How to confirm the cause

  • Open the Boards Manager and read the installed esp32 package version. Version 2.x or 3.x tells you which calls are valid.
  • Read the compiler error. ‘ledcAttach was not declared’ points to core 2.x; ‘ledcSetup was not declared’ points to core 3.x.
  • Check the pin. GPIO 34 to 39 are input only and cannot output PWM, and GPIO 6 to 11 are tied to the flash on most boards.
  • Check the duty value against the resolution: with 8 bits the range is 0 to 255, with 10 bits it is 0 to 1023.
  • Put a multimeter or an LED on the pin to see the average voltage change.

Worked example

The same 50 per cent output written for each core version. Use only the one that matches your core.

// core 3.x
ledcAttach(18, 5000, 8);      // pin, Hz, bits
ledcWrite(18, 128);           // about half duty

// core 2.x
ledcSetup(0, 5000, 8);        // channel, Hz, bits
ledcAttachPin(18, 0);         // pin, channel
ledcWrite(0, 128);

Common mistakes

  • Mixing the pin-based and channel-based calls in one sketch.
  • Using a duty value larger than the resolution allows.
  • Giving two channels different frequencies when they share a hardware timer, so one overrides the other.
  • Choosing an input-only or flash pin for the output.
  • Using a PWM frequency that the load cannot follow, such as a motor driver that expects a higher rate.

If this did not fix it

  • The Servo library or another library already uses the same LEDC channel.
  • A boot-strapping pin held by your circuit changes start-up behaviour.
  • The driver stage (transistor, MOSFET or motor driver) lacks power, so PWM is fine but nothing moves.
  • The wrong board selected in the IDE, so pin numbers differ from your hardware.