GPIO pins are the primary interface between the ESP32 and the physical world. Through GPIO, the ESP32 reads buttons, switches, and digital sensors, and controls LEDs, relays, motors, and other actuators.
Understanding how to configure and use GPIO correctly is the foundation for all embedded work on the ESP32.
What is GPIO?
GPIO (General Purpose Input/Output) pins are digital pins that can be individually configured as:
- Output — the firmware drives the pin HIGH (3.3V) or LOW (0V)
- Input — the firmware reads whether the pin is currently HIGH or LOW
The ESP32 has up to 34 accessible GPIO pins, most of which support both input and output. A small number (GPIO34–GPIO39) are input-only.
ESP32 GPIO Pin (Output Mode)
-----------------------------
ESP32 External Circuit
GPIO ---> 3.3V / 0V ---> LED + Resistor ---> GND
ESP32 GPIO Pin (Input Mode)
----------------------------
Button ---> GPIO (reads HIGH or LOW)
^
|
Pull-up resistor (to 3.3V) keeps pin HIGH when button is open
GPIO Configuration Using the Arduino Framework
The Arduino framework provides three simple functions for GPIO:
| Function | Description |
|---|---|
pinMode(pin, mode) | Configure a pin as INPUT, OUTPUT, or INPUT_PULLUP |
digitalWrite(pin, value) | Set an output pin HIGH or LOW |
digitalRead(pin) | Read the current state of an input pin |
Step 1: Configure GPIO Mode
Use pinMode() in the setup() function to configure each pin's direction before using it.
#define GPIO_OUTPUT_PIN 2 // GPIO2 as output (connected to onboard LED)
#define GPIO_INPUT_PIN 4 // GPIO4 as input (connected to a button)
void setup() {
pinMode(GPIO_OUTPUT_PIN, OUTPUT); // Configure GPIO2 as output
pinMode(GPIO_INPUT_PIN, INPUT); // Configure GPIO4 as input
}
Input Modes
| Mode | Behavior |
|---|---|
INPUT | Reads pin state, no internal pull resistor |
INPUT_PULLUP | Enables internal pull-up resistor (pin reads HIGH when floating) |
INPUT_PULLDOWN | Enables internal pull-down resistor (pin reads LOW when floating) |
For buttons connected between a GPIO and GND, use INPUT_PULLUP to avoid a floating input state:
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Button pressed: reads LOW (connected to GND)
// Button released: reads HIGH (pulled up to 3.3V)
Step 2: Set GPIO Output
Use digitalWrite() to drive an output pin:
digitalWrite(GPIO_OUTPUT_PIN, HIGH); // Set GPIO to 3.3V
digitalWrite(GPIO_OUTPUT_PIN, LOW); // Set GPIO to 0V (GND)
Step 3: Read GPIO Input
Use digitalRead() to check the current state of an input pin:
int level = digitalRead(GPIO_INPUT_PIN); // Returns HIGH (1) or LOW (0)
Complete GPIO Example: Button-Controlled LED
This example reads a button on GPIO4 and controls an LED on GPIO2:
#include <Arduino.h>
#define LED_PIN 2 // Onboard LED (also connected to boot strapping)
#define BUTTON_PIN 4 // Button connected between GPIO4 and GND
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT); // LED as output
pinMode(BUTTON_PIN, INPUT_PULLUP); // Button with internal pull-up
}
void loop() {
int button_state = digitalRead(BUTTON_PIN);
if (button_state == LOW) {
// Button is pressed (active LOW with pull-up)
digitalWrite(LED_PIN, HIGH); // Turn LED on
Serial.println("Button pressed - LED ON");
} else {
// Button is released
digitalWrite(LED_PIN, LOW); // Turn LED off
Serial.println("Button released - LED OFF");
}
delay(50); // Small debounce delay
}
Toggling a GPIO (Blink)
The classic blink example using digitalWrite():
#include <Arduino.h>
#define LED_PIN 2
void setup() {
pinMode(LED_PIN, OUTPUT);
}
void loop() {
digitalWrite(LED_PIN, HIGH); // LED on
delay(500);
digitalWrite(LED_PIN, LOW); // LED off
delay(500);
}
For faster toggling without manually tracking state, keep a variable:
bool led_state = false;
void loop() {
led_state = !led_state;
digitalWrite(LED_PIN, led_state ? HIGH : LOW);
delay(500);
}
GPIO Summary Table
| Operation | Code | Description |
|---|---|---|
| Set as output | pinMode(pin, OUTPUT) | Pin drives HIGH or LOW |
| Set as input | pinMode(pin, INPUT) | Pin reads external signal |
| Input with pull-up | pinMode(pin, INPUT_PULLUP) | Floating pin reads HIGH |
| Input with pull-down | pinMode(pin, INPUT_PULLDOWN) | Floating pin reads LOW |
| Drive HIGH | digitalWrite(pin, HIGH) | Pin outputs 3.3V |
| Drive LOW | digitalWrite(pin, LOW) | Pin outputs 0V |
| Read state | digitalRead(pin) | Returns HIGH or LOW |
GPIO Limitations on ESP32
| Limitation | Detail |
|---|---|
| 3.3V logic | ESP32 GPIO is not 5V tolerant — never connect directly to 5V signals |
| Max current per pin | ~12 mA source, ~28 mA sink — use a transistor for high-current loads |
| GPIO34–GPIO39 | Input only — cannot be configured as output |
| GPIO6–GPIO11 | Reserved for internal SPI flash — do not use |
| GPIO0, 2, 12, 15 | Boot-strapping pins — use with care |
Final Thoughts
GPIO is the most fundamental building block of ESP32 firmware. Every more complex peripheral — UART, I2C, SPI, ADC — ultimately controls physical voltage levels on GPIO pins.
Mastering pinMode(), digitalWrite(), and digitalRead() provides the foundation for:
- Driving LEDs and relays
- Reading buttons and limit switches
- Interfacing with digital sensors
- Building hardware state machines
GPIO is where firmware meets the physical world.