Built-in project · ESP32-C3-DevKitM-1

ESP32-C3 PWM fade

Breathes an LED on GPIO 4 and the on-board one on GPIO 8 with analogWrite, so the waveform comes out of the LEDC hardware and the core sleeps between steps. The whole build, an ESP32-C3 and 2 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Beginner About 10 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $13.15. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • PWM and duty cycle
  • Hardware PWM with analogWrite on an ESP32-C3
  • Why a fade table looks smoother than a linear ramp
  • Driving an LED with a resistor
  • Using an array as a lookup table
  • Difficulty: Beginner
  • Time: About 10 minutes
  • Board: ESP32-C3-DevKitM-1
  • Parts: 4

How it works

An LED on GPIO 4 breathes in and out, and so does the board's own LED on GPIO 8. The LED connects through a 220 ohm resistor to ground.

The sketch uses analogWrite, which hands the pin to the chip's hardware PWM controller. The waveform comes out of the hardware, so the code only has to change one number.

A table of 64 values shapes the fade like a cosine curve. Every 30 ms the sketch steps to the next value, so one full breath takes about two seconds.

Parts list

4 parts, plus the jumper wires. They retail for about $13.15; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
LED, yellow pin CESP32-C3-DevKitM-1 pin GND
Resistor, 220 Ω pin 1ESP32-C3-DevKitM-1 pin 4
LED, yellow pin AResistor, 220 Ω pin 2

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · ESP32-C3-DevKitM-1
// PWM fade: a breathing LED on hardware PWM.
//
// The `fade` example next door bit-bangs its waveform: it holds the pin high
// for a while and low for a while, and the core is awake for every microsecond
// of it. This one hands the pin to LEDC, the chip's LED PWM controller, and
// then does nothing but change a number thirty-three times a second. The
// waveform comes out of the hardware, so the core spends its life asleep and
// the edges still land where they should.
//
// analogWrite is the Arduino spelling and it is all this needs: 0 to 255 at
// 1 kHz on any GPIO, because LEDC reaches the pins through the GPIO matrix and
// not through a fixed set of `~` pins.
//
// Wire an LED and a resistor from GPIO 4 to ground to watch it; GPIO 8, the
// DevKitM-1's own LED, fades along with it.

const uint8_t LED = 4;
const int STEPS = 64;
const unsigned long STEP_MS = 30;  // a whole breath takes about two seconds

// One cycle of (1 - cos)/2 scaled to 0..255. A linear ramp looks wrong: the
// eye is roughly logarithmic, so an even climb in duty is an uneven climb in
// brightness, and this is the shape that reads as breathing.
const uint8_t breath[STEPS] = {
    0,   1,   2,   6,  10,  16,  22,  29,
   38,  47,  57,  68,  79,  91, 103, 116,
  128, 140, 153, 165, 177, 188, 199, 209,
  218, 226, 234, 240, 246, 250, 253, 255,
  255, 255, 253, 250, 246, 240, 234, 226,
  218, 209, 199, 188, 177, 165, 153, 140,
  128, 116, 103,  91,  79,  68,  57,  47,
   38,  29,  22,  16,  10,   6,   2,   1,
};

void setup() {
  // Nothing to do: the first analogWrite attaches the pin to a free channel.
}

void loop() {
  for (int step = 0; step < STEPS; step++) {
    analogWrite(LED, breath[step]);
    analogWrite(LED_BUILTIN, breath[step]);
    delay(STEP_MS);
  }
}

Code walkthrough

The key functions of the sketch, and what each one does.

setup sketch.ino

It is empty on purpose. The first analogWrite call attaches the pin to a free PWM channel, so no pin setup is needed.

void setup() {
  // Nothing to do: the first analogWrite attaches the pin to a free channel.
}

loop sketch.ino

It steps through the 64 entries of breath. For each one it writes the brightness to LED (GPIO 4) and to LED_BUILTIN, then waits STEP_MS (30 ms). When the last entry is done, it starts over, so the fade repeats forever.

void loop() {
  for (int step = 0; step < STEPS; step++) {
    analogWrite(LED, breath[step]);
    analogWrite(LED_BUILTIN, breath[step]);
    delay(STEP_MS);
  }
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The yellow LED stays dark This project
    Check the polarity: anode toward the resistor and GPIO 4, cathode to ground.
  • The fade looks jumpy or too slow This project
    Change STEP_MS. A smaller number makes each breath faster.
  • The LED is dim, burns out, or the pin reads wrong because the resistor value is off. Resistor
    Read the color bands again. A 220 ohm to 330 ohm resistor suits a typical LED on 5 V. 10k is a common pull-up or pull-down value. Check with a multimeter if unsure.
  • A pull-up or pull-down resistor is missing, so the input floats and reads random values. Resistor
    Tie the input to 5 V or ground through about 10k. Or use INPUT_PULLUP and wire the switch to ground.
  • The LED does not light, or it lights once and then dies. LED
    LEDs have polarity. The longer leg (anode) goes toward the positive side. Always add a series resistor, about 220 ohms to 1 k ohm on a 5 V pin.
  • A blue or white LED stays dark on 3.3 V. LED
    Blue and white LEDs drop about 3 V, so 3.3 V leaves almost nothing for the resistor. Use a 5 V supply, or a smaller resistor and a red or green LED.
  • Upload fails or the board is not found by the computer. ESP32-C3-DevKitM-1
    Pick the ESP32-C3 board in your tools and the right port. If it still fails, hold BOOT, tap RESET, then release BOOT to enter download mode and try again.
  • GPIO pins act strangely or the board resets. ESP32-C3-DevKitM-1
    GPIO is 3.3 V only, so never feed it 5 V. GPIO 18 and 19 are USB, and GPIO 2, 8 and 9 are boot strapping pins. Avoid loading them at reset.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.