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.

Arquitectura de GPIO en ESP32

¡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:

Como habrás notado, algunos pines como el GPIO 12 y 15 aparecen en múltiples categorías. Esto subraya su naturaleza sensible y por qué es mejor que los principiantes los eviten para usos generales. Para evitar complicaciones, te recomendamos empezar con los siguientes pines, que son seguros y versátiles para la mayoría de los proyectos de iniciación: Pines Seguros para Principiantes: GPIO 4, GPIO 18, GPIO 19, GPIO 21, GPIO 22, GPIO 23, GPIO 25, GPIO 26, GPIO 27, GPIO 32, GPIO 33 Ahora que sabes qué pines elegir, veamos cómo configurarlos.

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
⚠️ Advertencia: 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.:

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:

  1. Configurar un pin como entrada para el botón
  2. 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:

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
        }

    }