Ir al contenido principal

Programación con ESP32

Qué es el ESP32

El ESP32 es un chip de la empresa china Espressif que reúne en una sola pieza lo que en el Arduino Uno eran tres cosas separadas: el microcontrolador, la memoria y la conexión inalámbrica. Eso lo convierte en la placa con la que se construyen los proyectos de Internet de las cosas: un sensor que manda sus datos a Internet, una luz que se prende desde el celular, un tablero que publica la temperatura del aula.

Conviene separar dos cosas que se nombran igual:

  • El chip ESP32 es el componente, una plaqueta negra cuadrada de unos 5 mm con contactos en el borde.
  • Las placas que lo usan son las que se compran: ESP32 DevKit, NodeMCU-32S, WROOM-32. Traen el chip, el regulador de 3,3 V, el conector USB y los pines en dos filas.

Lo importante para nosotros: se programa exactamente igual que el Arduino. El mismo IDE, el mismo lenguaje, las mismas funciones setup(), loop(), pinMode(), digitalWrite() y Serial. Cambia la placa, no la forma de programar. Todo lo de Programación en Arduino sigue valiendo.

  Arduino Uno ESP32 (placa DevKit)
Procesador ATmega328P, 16 MHz, un núcleo 240 MHz, dos núcleos
Tensión de trabajo 5 V 3,3 V
Memoria 2 KB de RAM y 32 KB de programa 520 KB de RAM y 4 MB de programa
Conexión Ninguna Wi-Fi y Bluetooth integrados
Entradas analógicas 6, de 10 bits (0 a 1023) Varias, de 12 bits (0 a 4095)
PWM Solo en los pines marcados con ~ En cualquier pin, con más control
Para qué conviene Electrónica básica, sensores de 5 V, motores, taller Proyectos conectados a Internet, sensores, baterías

Instalar el soporte del ESP32 en el IDE

El IDE de Arduino no conoce al ESP32 de fábrica: hay que agregarle el core de Espressif, que es el paquete que trae las herramientas de compilación y las librerías de la placa. Se hace una sola vez:

  1. En el IDE, abrir Archivo → Preferencias.
  2. En el campo "Additional Boards Manager URLs" (Gestor de URLs adicionales de tarjetas), pegar esta dirección:
    https://espressif.github.io/arduino-esp32/package_esp32_index.json
  3. Aceptar y abrir el Gestor de tarjetas (el ícono de la plaquita en la barra lateral, o Herramientas → Placa → Gestor de tarjetas).
  4. Buscar esp32 y instalar el paquete de Espressif Systems.
  5. Reiniciar el IDE y elegir la placa en Herramientas → Placa → ESP32 Arduino. Para la placa más común se elige ESP32 Dev Module.

La descarga es grande (varios cientos de megabytes) y demora. En la primera clase conviene hacerla en una sola computadora y dejar el IDE ya configurado, o compartir la carpeta del core.

Los dos ajustes que dan problemas

  • El puerto. En el menú Herramientas → Puerto aparece el nombre del puerto. El nombre depende del chip que trae la placa para hablar por USB: las que usan CH340 o CP2102 suelen dar /dev/ttyUSB0 en Linux (y COM3 en Windows); las que tienen USB nativo dan /dev/ttyACM0. Si el puerto no aparece, falta el controlador del chip USB o el cable no transmite datos.
  • Los permisos del puerto (Linux). Si el IDE dice que no puede abrir el puerto, el usuario no pertenece al grupo dialout. Se arregla con sudo usermod -a -G dialout $USER y hay que cerrar la sesión y volver a entrar. Ese comando lo corre el docente.

Si la carga falla con un error del programador, muchas placas ESP32 piden que se mantenga apretado el botón BOOT de la placa justo mientras dice "Subiendo...", y que se suelte después. Otras lo hacen solas. Es la primera cosa que hay que probar cuando una placa nueva no recibe el programa.

Lo que cambia respecto del Arduino Uno

El lenguaje es el mismo, pero la placa es otra. Estas son las diferencias que se notan apenas se empieza a conectar algo.

1. Trabaja a 3,3 V, no a 5 V

Es la diferencia que más veces termina en un pin quemado. En el ESP32, un HIGH son 3,3 V y los pines esperan como máximo 3,3 V. Un módulo que devuelve 5 V por su salida —algunos sensores, un adaptador de nivel, la señal de un shield de Arduino— puede dañar el pin si se conecta directo.

Cuando hay que conectar algo de 5 V, se usan dos soluciones simples: un divisor de tensión con dos resistencias, o un módulo adaptador de nivel (level shifter). Y la alimentación se toma siempre del pin 3V3, nunca del de 5 V para los componentes que van conectados a los pines.

2. Las entradas analógicas son de 12 bits

analogRead() devuelve un número entre 0 y 4095 en lugar de 0 a 1023, porque el conversor es de 12 bits. El dato que sorprende a todos es el techo: el conversor no llega a medir los 3,3 V completos. Con la configuración por defecto, el máximo medible ronda los 3,1 V, así que un sensor que entrega 3,3 V se lee pegado al valor máximo y no hay forma de distinguir un poco más de un mucho más.

Dos ayudas que trae el core: analogReadMilliVolts(pin) devuelve directamente el valor en milivoltios ya calibrados, y analogReadResolution(bits) permite bajar la resolución si se necesita menos precisión.

3. El PWM está en cualquier pin

En la Uno, PWM solo en los pines marcados con ~. En el ESP32 el PWM lo genera un periférico interno (se llama LEDC) y el core de Arduino lo expone con analogWrite(), que funciona en cualquier pin de salida. Además hay más control:

analogWrite(pin, 128);                    // de 0 a 255, como en el Arduino
analogWriteResolution(pin, 12);           // ahora de 0 a 4095, con más precisión
analogWriteFrequency(pin, 5000);          // la frecuencia de la señal, en hercios

La frecuencia importa cuando se maneja un motor o se quiere evitar el zumbido de una luz. Ojo con una limitación real: frecuencia y resolución están atadas, y no se pueden pedir las dos al máximo al mismo tiempo. Cuando la combinación no es posible, el programa no avisa de forma clara: directamente la salida queda mal configurada.

4. Hay pines con restricciones

La documentación de Espressif lo dice de frente: en el ESP32 algunos GPIO tienen restricciones especiales y no todos están disponibles en la placa; hay que mirar el dibujo de pines de la placa que se esté usando. En la práctica, eso significa que el pinout no se inventa ni se copia de otra placa: se mira la serigrafía de la propia placa o su diagrama, y recién después se conecta. Algunos pines, además, no se pueden usar si después se va a encender el Wi-Fi.

5. No tiene EEPROM, y tiene sueño profundo

  • Para guardar valores entre un encendido y el siguiente, la Uno tiene EEPROM; el ESP32 no. Se usa la librería Preferences, que guarda los datos en una zona reservada de la memoria flash.
  • Para ahorrar batería, el ESP32 puede entrar en deep sleep: se apaga casi por completo y se despierta solo al cabo de un tiempo. Un proyecto a batería que mida la temperatura cada 10 minutos puede durar meses en lugar de horas. Cuando duerme, el programa vuelve a empezar de cero al despertar: no continúa donde estaba.
  • La mayoría de los pines tienen además sensor táctil incorporado: se leen con touchRead(pin) y detectan el dedo sin ningún componente externo.

El mismo programa, en la otra placa

El primer titileo, idéntico al de la Uno salvo por el número de pin:

const int ledPin = 2;      // en la mayoria de las placas ESP32 el LED de la placa esta en el GPIO 2

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  digitalWrite(ledPin, HIGH);
  delay(1000);
  digitalWrite(ledPin, LOW);
  delay(1000);
}

Dos detalles prácticos:

  • El LED de la propia placa está, en la mayoría de las placas ESP32, en el GPIO 2. No es el 13 como en la Uno, y hay que confirmarlo en el pinout: si el LED no titila, casi siempre es eso.
  • Un LED externo se conecta igual que en la Uno, con su resistencia en serie, pero ahora la cuenta es sobre 3,3 V. Con la misma resistencia de 220 Ω que se usa en la Uno, el LED queda bastante más apagado, porque la corriente que pasa es aproximadamente la mitad. No se quema: simplemente alumbra menos.
  • La velocidad típica del monitor serie en el ESP32 es 115200, no 9600.

Wi-Fi: conectarse a una red

Acá empieza lo que la Uno no puede hacer. La conexión se pide con la librería WiFi.h, que viene con el core: se indica el nombre de la red y la contraseña, y el programa espera a que la conexión se complete antes de seguir.

#include <WiFi.h>

const char* red     = "Aula-Informatica";
const char* clave   = "la-clave-de-la-red";

void setup() {
  Serial.begin(115200);
  WiFi.begin(red, clave);
  Serial.print("Conectando");

  while (WiFi.status() != WL_CONNECTED) {   // espera hasta que se conecte
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.print("Conectado. Mi direccion IP es: ");
  Serial.println(WiFi.localIP());           // el numero que le dio el router
}

void loop() {
  // a partir de aca, el ESP32 tiene Internet
}
Secuencia de conexión del ESP32 a la red: se pide la conexión, el programa espera en un ciclo hasta que el estado sea conectado, y recién ahí recibe una dirección IP y puede usar Internet WiFi.begin() pide la conexión while (no conectado) espera 500 ms y vuelve a preguntar WiFi.localIP() el router le dio una dirección mientras no esté conectado, vuelve a preguntar Todo el programa queda esperando en ese ciclo: sin conexión, no sigue
La conexión no es instantánea: puede tardar varios segundos. Por eso el programa espera en un ciclo hasta que WiFi.status() devuelve WL_CONNECTED, y recién entonces obtiene su dirección IP.

Las credenciales no van en el programa

La contraseña de la red no se escribe dentro del sketch: si el archivo se comparte, se copia a un pendrive o se sube a un repositorio, la contraseña se va con él. Se pone en un archivo aparte que no se comparte, y el programa lo incluye:

// archivo datos-wifi.h  (NO se comparte ni se sube al repositorio)
#define RED_WIFI   "Aula-Informatica"
#define CLAVE_WIFI "la-clave-de-la-red"

Mandar datos y pedir datos

Con la conexión hecha, el ESP32 puede pedirle cosas a un servidor de Internet. Lo más simple es una petición HTTP, la misma que hace el navegador cuando se escribe una dirección:

#include <WiFi.h>
#include <HTTPClient.h>

void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin("http://ejemplo.com/api/temperatura?valor=23.5");

    int codigo = http.GET();              // 200 si salio bien, 404 no existe, 500 error del servidor
    String respuesta = http.getString();  // lo que contesto el servidor

    Serial.println(codigo);
    Serial.println(respuesta);
    http.end();                           // siempre cerrar la conexion
  }
  delay(60000);                           // una vez por minuto
}

Para mandar datos en lugar de pedirlos, se usa el método POST, que lleva la información en el cuerpo del mensaje:

HTTPClient http;
http.begin("http://ejemplo.com/api/guardar");
http.addHeader("Content-Type", "application/json");

String cuerpo = "{\"sensor\":\"aula3\",\"valor\":23.5}";
int codigo = http.POST(cuerpo);

El código de estado es la parte que no se puede saltear: un 200 significa que el servidor entendió y respondió; un 404, que la dirección no existe; un 500, que el servidor falló. Un programa que no mira el código cree que todo salió bien y sigue mandando datos a una dirección equivocada durante horas.

Recibir órdenes: el ESP32 como servidor

El camino inverso también funciona: el ESP32 puede atender pedidos y mostrar una página web con sus propios datos. Al abrir en el celular la dirección IP que el programa imprime por el monitor serie, se ve la página que sirve la placa. Sobre eso se arma un encendido de luces desde el teléfono, sin ninguna aplicación:

#include <WiFi.h>
#include <WebServer.h>

WebServer servidor(80);                 // atiende en el puerto 80, el de las paginas web

void setup() {
  Serial.begin(115200);
  WiFi.begin(RED_WIFI, CLAVE_WIFI);
  while (WiFi.status() != WL_CONNECTED) { delay(500); }
  Serial.println(WiFi.localIP());

  servidor.on("/", []() {                        // que contestar cuando piden la raiz
    servidor.send(200, "text/html", "<h1>Hola desde el ESP32</h1>");
  });
  servidor.begin();
}

void loop() {
  servidor.handleClient();              // atiende los pedidos que van llegando
}

Las librerías WiFi, HTTPClient y WebServer vienen con el core del ESP32, no hay que instalarlas. Y los nombres de las librerías cambian entre versiones del core: si algo no compila, la salida más rápida es mirar los ejemplos que trae el IDE en Archivo → Ejemplos, que siempre coinciden con la versión instalada. En IoT (Internet of Things) está el panorama de qué se puede construir con esto.

Problemas típicos del Wi-Fi

El Wi-Fi agrega una lista de fallas que no existen en la Uno, porque no depende solo de la placa: depende de la red.

Qué pasa Por qué Qué hacer
Queda para siempre en "Conectando..." La red no está al alcance, la clave es incorrecta o es una red de 5 GHz. Revisar el nombre exacto de la red (distingue mayúsculas) y la clave. Probar con el celular como punto de acceso.
Conecta, pero la IP es 0.0.0.0 Todavía no terminó de conectarse cuando el programa imprimió el dato. Esperar a que WiFi.status() devuelva WL_CONNECTED antes de leer la IP.
Se conecta y a los minutos deja de responder El router le cortó la conexión por inactividad, o el programa se quedó esperando algo. Consultar el estado antes de usar la red y volver a llamar a WiFi.begin() si hace falta. El monitor serie tiene que seguir imprimiendo.
No aparece el puerto en el IDE Falta el controlador del chip USB o el cable solo carga. Instalar el controlador del chip (CH340 o CP2102) y probar con otro cable.
La placa se reinicia cuando enciende el Wi-Fi El Wi-Fi consume bastante corriente y la fuente no alcanza. Alimentar con una fuente mejor o un cable más corto y grueso; desconectar módulos durante la prueba.
En el monitor serie se ven caracteres raros La velocidad del monitor no coincide con la del programa. Elegir 115200 en el monitor serie, que es la que usa el ejemplo.

Resumen

  • El ESP32 es un microcontrolador con Wi-Fi y Bluetooth integrados, y se programa con el mismo IDE y el mismo lenguaje que el Arduino.
  • Para usarlo hay que instalar el core de Espressif una vez, con la dirección del gestor de tarjetas.
  • Trabaja a 3,3 V: lo que devuelva 5 V necesita divisor de tensión o adaptador de nivel.
  • El conversor analógico es de 12 bits (0 a 4095) y no mide los 3,3 V completos; analogReadMilliVolts() devuelve milivoltios calibrados.
  • El PWM funciona en cualquier pin, con resolución y frecuencia configurables.
  • Hay pines con restricciones: el pinout de la placa se mira, no se inventa.
  • No hay EEPROM: para guardar valores se usa Preferences. Y con deep sleep puede durar meses a batería.
  • Para Internet alcanza con tres librerías que vienen con el core: WiFi para conectarse, HTTPClient para pedir y mandar datos, y WebServer para atender pedidos.
  • Las credenciales van en un archivo aparte que no se comparte.