GPIO & PWM on ESP32
GPIO & PWM on ESP32
Control outputs, read inputs, and use LEDC for smooth PWM control.
GPIO on the ESP32
The ESP32 has up to 34 GPIO pins, though the exact number depends on the package and module. Not all pins are equal ߀” some have restrictions:
| Pin range | Notes |
|---|---|
| GPIO 0 | Boot mode select ߀” avoid as output; has pull-up |
| GPIO 1, 3 | UART0 TX/RX ߀” used for USB serial |
| GPIO 6߀“11 | Connected to internal SPI flash ߀” DO NOT USE |
| GPIO 34߀“39 | Input only ߀” no internal pull-up/down |
| GPIO 2 | Built-in LED on most dev boards |
Basic Digital I/O
Works the same as Arduino:
void setup() {
pinMode(2, OUTPUT);
pinMode(15, INPUT_PULLUP);
}
void loop() {
if (digitalRead(15) == LOW) { // Button pressed (active-low)
digitalWrite(2, HIGH);
} else {
digitalWrite(2, LOW);
}
}
Analog Input ߀” ADC
The ESP32 has two ADCs. ADC1 (GPIO 32߀“39) is reliable. ADC2 (GPIO 0, 2, 4, 12߀“15, 25߀“27) conflicts with WiFi ߀” ADC2 cannot be used when WiFi is active.
int raw = analogRead(34); // 0߀“4095 (12-bit ADC)
float voltage = raw * (3.3 / 4095.0);
The ESP32 runs at 3.3 V ߀” never connect inputs above 3.3 V.
ADC Non-linearity
The ESP32 ADC has known non-linearity at the extremes (below ~100 mV and above ~3.1 V). Use analogSetAttenuation() to shift the input range:
| Attenuation | Useful range |
|---|---|
ADC_0db |
0 ߀“ 1.1 V |
ADC_2_5db |
0 ߀“ 1.5 V |
ADC_6db |
0 ߀“ 2.2 V |
ADC_11db |
0 ߀“ 3.9 V (default) |
DAC Output
GPIO 25 and 26 have true 8-bit DAC outputs (0߀“3.3 V):
dacWrite(25, 128); // ~1.65 V (half of 3.3 V)
dacWrite(25, 0); // 0 V
dacWrite(25, 255); // 3.3 V
PWM with LEDC
The ESP32 does not use analogWrite(). Instead it has the LEDC (LED Control) peripheral ߀” 16 independent PWM channels with configurable frequency and resolution.
const int ledPin = 2;
const int channel = 0;
const int freq = 5000; // Hz
const int resolution = 8; // bits (0߀“255)
void setup() {
ledcSetup(channel, freq, resolution);
ledcAttachPin(ledPin, channel);
}
void loop() {
for (int duty = 0; duty <= 255; duty++) {
ledcWrite(channel, duty);
delay(10);
}
for (int duty = 255; duty >= 0; duty--) {
ledcWrite(channel, duty);
delay(10);
}
}
Interrupts
Any GPIO can trigger an interrupt on RISING, FALLING, or CHANGE:
volatile bool triggered = false;
void IRAM_ATTR onButton() {
triggered = true;
}
void setup() {
pinMode(15, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(15), onButton, FALLING);
}
void loop() {
if (triggered) {
triggered = false;
Serial.println("Button pressed!");
}
}
The IRAM_ATTR attribute places the ISR in IRAM so it runs correctly even if the flash cache is busy.
Touch Pins
GPIO 4, 12, 13, 14, 15, 27, 32, 33 have capacitive touch sensing:
int touchValue = touchRead(4); // Lower value = finger touching
if (touchValue < 40) {
Serial.println("Touch detected!");
}