respiration.ino
The hardware PWM (LEDC) generates the carrier by itself: analogWrite applies a 0-255 duty cycle and the LED breathes without burning CPU time. The chosen frequency avoids PWM whine.
Hardware required
An ESP32-S2 board (single-core Xtensa, Wi-Fi, native USB-OTG on GPIO19/20, 14 capacitive touch pads) such as a DevKitC-1 or QT Py, powered over USB. 1 LED + 330 Ω resistor (or common RGB LED + anode on the pin).
Wiring
LED on 2 through 330 Ω to GND. On pins also wired to flash memory, pick another GPIO.
How it works
analogWrite/analogWriteFrequency configure an LEDC channel; the loop only ramps the duty cycle 0-255 while the peripheral generates the signal. In the Arduino IDE (esp32 package), select « ESP32S2 Dev Module »; the S2 flashes straight over its native USB (double reset on some boards to enter the bootloader).
Going further
Three phase-shifted LEDC channels for an RGB LED, or a small MOSFET stage to drive a power LED strip.
respiration.ino
// Respiration LED par le contrôleur LEDC (PWM matériel du SoC).
const int LED = 2;
const int FREQ = 5000; // 5 kHz : inaudible, pas de sifflement
const int RESOLUTION = 8; // 256 niveaux
int niveau = 0;
int sens = 4; // pas par étape
void setup() {
analogWriteResolution(RESOLUTION);
analogWriteFrequency(FREQ);
analogWrite(LED, 0);
}
void loop() {
niveau += sens;
if (niveau >= 255 || niveau <= 0) sens = -sens;
analogWrite(LED, niveau);
delay(8); // 255/4*8 ms ≈ 0,5 s par rampe
}