Configuración de GPIO
En ESP-IDF, la función gpio_config()
es la forma principal de configurar los pines GPIO. Esta guía explica cómo usarla correctamente y qué limitaciones debes conocer.
Introducción: ¿Qué es un Pin GPIO?
¡Bienvenido al fascinante mundo de la electrónica y los sistemas embebidos con el ESP32! Si alguna vez has querido que un microcontrolador interactúe con el mundo real, estás en el lugar correcto. El primer paso para lograrlo es dominar los pines GPIO.
Piensa en un pin GPIO (General Purpose Input/Output o Entrada/Salida de Propósito General) como un conector multifuncional que tu ESP32 puede controlar. Puede actuar como un interruptor de luz que tu programa enciende y apaga (Salida), o como un sensor que lee si una ventana está abierta o cerrada (Entrada).
El chip ESP32 cuenta con 34 pines GPIO físicos (con números que van del 0 al 39, aunque no todos los números en ese rango están disponibles), lo que le otorga una increíble versatilidad para conectar LEDs, botones, sensores y todo tipo de componentes.
En esta guía, aprenderás los fundamentos para configurar y controlar estos pines. Comencemos por el paso más importante: elegir el pin correcto para tu primer proyecto.

¡Cuidado! Eligiendo el Pin GPIO Correcto para tu Proyecto
Aunque el ESP32 ofrece muchos pines, no todos son iguales. Algunos tienen funciones especiales durante el arranque o están reservados para tareas internas. Para un principiante, es crucial evitar estos pines para no encontrarse con comportamientos inesperados.
Aquí tienes una guía de los pines que debes tratar con precaución en tus primeros experimentos:
- Pines de Strapping (GPIO 0, 2, 5, 12, 15): Estos pines son especiales porque el ESP32 lee su estado al arrancar para determinar su modo de funcionamiento (por ejemplo, el modo de programación). Si conectas algo que altere su estado al encender el microcontrolador, podrías impedir que se inicie correctamente.
- Pines de la Memoria Flash (GPIO 6-11, 16-17): Estos pines ya están ocupados. Se utilizan internamente para comunicarse con la memoria flash donde se almacena tu programa. ¡No los uses para ningún otro propósito!
- Pines de Depuración (JTAG) (GPIO 12-15): Estos pines están designados para herramientas de depuración avanzadas que permiten analizar el código en tiempo real. Aunque se pueden usar como GPIO, es mejor dejarlos libres para su propósito original.
- Pines de Solo Entrada (GPI) (GPIO 34-39): Como su nombre indica, estos pines son únicamente para entrada. No puedes usarlos para encender un LED o activar un relé. Además, una limitación muy importante es que no tienen resistencias pull-up o pull-down internas, un concepto que exploraremos más adelante.
El Corazón de la Configuración: La Estructura
Para configurar los pines en el entorno de desarrollo ESP-IDF, la herramienta principal es la función gpio_config(). Su gran ventaja es que permite configurar uno o varios pines a la vez de forma organizada y eficiente.
Esta función no recibe parámetros sueltos, sino que utiliza una estructura llamada gpio_config_t para agrupar todas las opciones de configuración. A continuación, desglosamos sus miembros más importantes.
Parámetro | Tipo de Valor | Descripción Sencilla |
---|---|---|
pin_bit_mask |  Máscara de bits (un número) | Sirve para seleccionar los pines que quieres configurar. Cada bit representa un número de pin. Se pueden configurar varios a la vez |
mode |  GPIO_MODE_INPUT, GPIO_MODE_OUTPUT | Define si el pin actuará como una entrada (para leer datos) o como una salida (para enviar señales) |
pull_up_en |  1 (para activar) o 0 (para  desactivar) | Activa una resistencia interna que conecta el pin a un nivel de voltaje alto (pull-up).pull_down_en 1 (para activar) o 0 (para desactivar) |
pull_down_en |  1 (para activar) o 0 (para  desactivar) | Activa una resistencia interna que conecta el pin a un nivel de voltaje alto (pull_down_en) 1 (para activar) o 0 (para desactivar) |
Con esta estructura en mente, ya estamos listos para ponerla en práctica. Empecemos configurando un pin como salida..
gpio_config_t io_conf = {};
io_conf.pin_bit_mask = (1ULL << GPIO_NUM_2,); // Selecciona GPIO2
io_conf.mode = GPIO_MODE_OUTPUT; // Modo salida
io_conf.intr_type = GPIO_INTR_DISABLE; // Sin interrupciones
io_conf.pull_up_en = GPIO_PULLUP_DISABLE; // Sin pull-up
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; // Sin pull-down
gpio_config(&io_conf);
En caso que quisieramos configurar varios pines a la vez. Añadimos los pines necesarios..
io_conf.pin_bit_mask = (1ULL << GPIO_NUM_2,)
| (1ULL << GPIO_NUM_3,)
| (1ULL << GPIO_NUM_4,) ; // Selecciona GPIO2 GPIO03 y GPIO4
gpio_config()
sobrescribe todas las configuraciones previas del pin. Úsala con cuidado si el pin tiene múltiples funciones (ej. GPIO + ADC).
En este ejemplo, hemos configurado el GPIO2 como una salida simple sin resistencias pull-up o pull-down. La máscara de bits (pin_bit_mask) utiliza un desplazamiento de bits para seleccionar el pin específico que queremos configurar.
Verificación de configuración
Usa gpio_dump_io_configuration()
para depurar:
// Imprimir configuración de GPIO2
gpio_dump_io_configuration(NULL, (1ULL << GPIO_NUM_2));
Tutorial Práctico: Configurar un Pin como SALIDA
Encender un LED
Nuestro objetivo será configurar un pin para que pueda enviar una señal de "encendido" (nivel alto) o "apagado" (nivel bajo), perfecto para controlar un LED.:
- Estamos trabajando en lenguaje C por lo tanto lo primero que vemos en el código son los archivos de cabecera, los cuales se definen con la directiva "#include" seguida del nombre del archivo de cabecera entre comillas "......" o corchetes <.......> en el caso de los sistemas enbebidos se pueden utilizar indistintamente.
- Seguidamente va la funcion principal del codigo "C" la funcion app_main() en ESP-IDF es el punto de entrada de la aplicación, similar a la función main() en un programa de C estándar.
- Definimos la Configuración: Declaramos una variable del tipo gpio_config_t.
gpio_config_t io_conf = {};
io_conf.pin_bit_mask = (1ULL << GPIO_NUM_2,); // Selecciona GPIO2 io_conf.mode = GPIO_MODE_OUTPUT; // Modo salida io_conf.intr_type = GPIO_INTR_DISABLE; // Sin interrupciones io_conf.pull_up_en = GPIO_PULLUP_DISABLE; // Sin pull-up io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; // Sin pull-down
- Aplicar la Configuración: Una vez que nuestra estructura io_conf está lista, se la pasamos a la función gpio_config()para que aplique los cambios a los pines seleccionados.
- Creamos el bucle infinito,con el buclewhile(true){codigo que se repite} Imagina que el bucle while es como "mientras ("while" en ingles) algo sea verdad "(true) verdad en ingles"", sigue haciendo esto".
- Controlamos el Nivel Lógico: Con el pin ya configurado como salida, usamos la función gpio_set_level(para poner el pin en el nivel que necesitamos en este caso alto 1 )gpio_set_level() para controlar su estado. Le pasamos el número del pin y el nivel deseado: 1 para alto (encender) y 0 para bajo (apagar).
- Con vTaskDelay() detenemos la tarea actual. Es la forma de ESP-IDF de hacer pausas. No es un delay normal en C. Es la forma MÁS fácil y correcta de decir: "Espera 1 segundo" 1000 TICKS = 1 segundo por lo tanto 1 TICK equvaldrá a 1 milisegundo
- Volvemos a usar la función gpio_set_level() para poner el pin en el nivel que necesitamos en este otro caso bajo 0 )
- Con vTaskDelay() detenemos la tarea actual. 1000 TICKS = 1 segundo por lo tanto 1 TICK equivaldrá a 1 milisegundo
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
void app_main(void) {
gpio_config(&io_conf); // Como se ha explicado antes es lafuncion que aplique los cambios a los pines seleccionados
while(true) {codigo del loop}; // Repetimos el bloque de codigo contenido entre corchetes indefinidamente
gpio_set_level(GPIO_NUM_2, 1); // Pone GPIO2 en estado alto (enciende el LED)
vTaskDelay(pdMS_TO_TICKS(1000));// Espera 1 segundo
gpio_set_level(GPIO_NUM_2, 0); // Pone GPIO2 en estado bajo (apaga el LED)
vTaskDelay(pdMS_TO_TICKS(1000));// Espera 1 segundo
Codigo completo operativo 1
Con vTaskDelay() detenemos la tarea actual. 1000 TICKS = 1 segundo por lo tanto 1 TICK equivaldrá a 1 milisegundo
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
extern "C" void app_main(void){
gpio_config_t io_config = {};
io_config.pin_bit_mask = (1ULL << GPIO_NUM_2);// Selecciona GPIO2
io_config.mode = GPIO_MODE_OUTPUT; // Modo salida
io_config.intr_type = GPIO_INTR_DISABLE;// Sin interrupciones
io_config.pull_up_en = GPIO_PULLUP_DISABLE;// sin
io_config.pull_down_en = GPIO_PULLDOWN_DISABLE;// Sin pull-down
// Aplicar la configuración
gpio_config(&io_config);
while(true){
//Encendemos el LED (nivel del pin número 2 alto = 1)
gpio_set_level(GPIO_NUM_2, 1);
vTaskDelay(pdMS_TO_TICKS(1000));// Esperar 1 segundo
//Apagamos el LED (nivel del pin número 2 bajo = 0)
gpio_set_level(GPIO_NUM_2, 0);
vTaskDelay(pdMS_TO_TICKS(1000));// Esperar 1 segundo
}
}
Configurar un pin como entrada
Leer estado de un botón
Vamos a añadir un botón para controlar el estado del LED. El botón cambiará el estado del LED cada vez que se presione. Para ello necesitamos:
- Configurar un pin como entrada para el botón
- Leer el estado del botón y cambiar el estado del LED cuando se presione
Una cosa a tener en cuenta es el rebote (debounce en ingles) para evitar multiples detecciones por una sola pulsación
En este ejemplo, vamos a hacer un control básico sin debounce muy elaborado, solo con un retardo simple.
Vamos a conectar el botón en un pin (por ejemplo GPIO_NUM_0) y usaremos la resistencia pull-up interna, por lo que cuando se presione el botón, leeremos un nivel bajo.Para ello vamos a realizar los siguientes pasos:
- Configurar el pin del botón como entrada con resistencia pull-up interna que incluye el propio esp32. Colocarlo a continuacion de la configuracione del pin del LED
- Creamos una variable entera para poner el estado del led a 0 o sea apagado
- En el bucle "while()" Leer el estado del botón.
- Si (condicional if()) el botón está presionado (nivel bajo) cambiar el estado del led y esperar un poco par evitar rebotes.
- Realizamos una breve espera antes de leer el botón otra vez
//Configurar el botón como entrada con resistencia pull-up
gpio_config_t btn_config = {};
btn_config.pin_bit_mask = (1ULL << BUTTON_PIN); // Selecciona el GPIO2
btn_config.mode = GPIO_MODE_INPUT;//Modo entrada
btn_config.intr_type = GPIO_INTR_DISABLE; //Sin interrupciones
btn_config.pull_up_en = GPIO_PULLUP_ENABLE;//Activar el pull-up interno
btn_config.pull_down_en = GPIO_PULLDOWN_DISABLE;//Sin pull-down
//Aplicamos la configuración
gpio_config(&btn_config);
int led_state = 0; // Variable para el estado del LED (0 = apagado, 1 = encendido)
int button_state = gpio_get_level(BUTTON_PIN); // Leer el estado del botón
if (button_state == 0) { // Botón presionado (nivel bajo)
led_state = !led_state; // Cambiar el estado del LED
gpio_set_level(LED_PIN, led_state); // Actualizar el estado del LED
vTaskDelay(pdMS_TO_TICKS(300)); // Retardo para evitar rebotes
}
vTaskDelay(pdMS_TO_TICKS(50)); // Pequeña espera antes de la siguiente lectura
Codigo completo operativo 2
El código completo quedaría así:
#include
#include
#include
#define LED_PIN GPIO_NUM_2 //pin del LED
#define BUTTON_PIN GPIO_NUM_0 //pin del botón
extern "C" void app_main (void){
//Configurar LED como salida
gpio_config_t io_config = {};
io_config.pin_bit_mask = (1ULL << LED_PIN); //Selecciona GPIO2
io_config.mode = GPIO_MODE_OUTPUT;//Modo Salida
io_config.intr_type = GPIO_INTR_DISABLE;//Sin interrupciones
io_config.pull_up_en = GPIO_PULLUP_DISABLE;//Sin pull-up
io_config.pull_down_en =GPIO_PULLDOWN_DISABLE;// Sin Ipull-down
//Aplicamos la configurción
gpio_config(&io_config);
//Configurar el botón como entrada con resistencia pull-up
gpio_config_t btn_config = {};
btn_config.pin_bit_mask = (1ULL << BUTTON_PIN); // Selecciona el GPIO2
btn_config.mode = GPIO_MODE_INPUT;//Modo entrada
btn_config.intr_type = GPIO_INTR_DISABLE; //Sin interrupciones
btn_config.pull_up_en = GPIO_PULLUP_ENABLE;//Activar el pull-up interno
btn_config.pull_down_en = GPIO_PULLDOWN_DISABLE;//Sin pull-down
//Aplicamos la configuración
gpio_config(&btn_config);
int led_state = 0; //Variable para el estado del LED (0 = apagado, 1 = encendido)
while (true){
int button_state = gpio_get_level(BUTTON_PIN); //Leer el estado del botón
if(button_state == 0){ // botón presionado (nivel bajo)
led_state = !led_state; //Cambiamos el estado del boton
gpio_set_level(LED_PIN, led_state); //Actualizamos el estado del LED
vTaskDelay(pdMS_TO_TICKS(300)); //Retardo para evitar rebotes
}
vTaskDelay(pdMS_TO_TICKS(50)); //Pequeña esper aantes de la siguiente lectura
}
}