ESP32-C6 fade
Breathes an LED on GPIO 10 with analogWrite, so the waveform comes out of the LEDC hardware and the core sleeps between steps. The whole build, an ESP32-C6 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 $14.15. On Mokxi they're free.
What you'll learn
- PWM brightness with analogWrite
- Hardware PWM that runs without the loop
- Lookup tables for smooth fades
- Any GPIO can do PWM on this board
- Difficulty: Beginner
- Time: About 10 minutes
- Board: ESP32-C6-DevKitC-1
- Parts: 4
How it works
An LED on GPIO 10 breathes slowly, fading up and down about once every two seconds. The LED sits in series with a 220 ohm resistor, and its other leg goes to ground.
The sketch walks through a table of 64 brightness values, one every 30 ms, and calls analogWrite with each. The table follows a smooth curve, so the fade looks like breathing and not a straight ramp.
The actual PWM waveform is made by the chip's LEDC hardware. The code only changes a number and waits, so the LED stays smooth between steps.
Parts list
4 parts, plus the jumper wires. They retail for about $14.15; on Mokxi they're free.
- 1 × ESP32-C6-DevKitC-1
- 1 × Full-size breadboard
- 1 × Resistor, 220 ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × LED, yellowIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
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 pin | Connects to |
|---|---|
| Resistor, 220 Ω pin 1 | ESP32-C6-DevKitC-1 pin 10 |
| LED, yellow pin C | ESP32-C6-DevKitC-1 pin GND |
| LED, yellow pin A | Resistor, 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.
// Fade: a breathing LED on hardware PWM.
//
// LEDC, the chip's LED PWM controller, makes the waveform; the sketch only
// changes a number thirty-three times a second and sleeps in between. The core
// is parked in `wfi` for almost all of the two seconds a breath takes, and the
// edges still land where they should, because the PWM is in the hardware and
// not in the loop.
//
// analogWrite(pin, 0..255) is all this needs. Any GPIO will do: LEDC reaches
// the pins through the GPIO matrix, so there is no fixed set of `~` pins on
// this board.
//
// Wire an LED and a 220 R resistor from GPIO 10 to ground.
const uint8_t LED = 10;
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]);
delay(STEP_MS);
}
}
Code walkthrough
The key functions of the sketch, and what each one does.
setup sketch.ino
It is empty on purpose. There is no pin setup call, because the first analogWrite attaches GPIO 10 to a free PWM channel by itself.
void setup() {
// Nothing to do: the first analogWrite attaches the pin to a free channel.
}loop sketch.ino
It steps through the breath table from 0 to STEPS minus one. Each step writes that value to the LED with analogWrite, then waits STEP_MS, which is 30 ms. The table rises to 255 and falls back, so one pass is one full breath.
void loop() {
for (int step = 0; step < STEPS; step++) {
analogWrite(LED, breath[step]);
delay(STEP_MS);
}
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The LED does not light. This project
Check the LED direction. The anode (A) goes to the resistor on GPIO 10 and the cathode (C) goes to GND. - The fade is too fast or too slow. This project
ChangeSTEP_MS. A smaller number speeds up the breath and a larger one slows it down. - 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. - A 5 V signal damages or confuses a pin. ESP32-C6-DevKitC-1
All ESP32-C6 GPIOs are 3.3 V and not 5 V tolerant. Level shift 5 V sensors. Do not feed 5 V into any GPIO. - The board will not boot or enter run mode as expected. ESP32-C6-DevKitC-1
GPIO 4, 5, 8, 9 and 15 are strapping pins read at reset. Do not hold them at odd levels at power up. GPIO 8 also drives the RGB LED.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.