Por qué este proyecto te conviene
Imagina que tienes varios ESP32 repartidos por la casa controlando luces, relés o motores, y quieres un control remoto físico. no una app en el celular. para activarlos desde la mesa de centro. Esa es exactamente la pieza que resuelve este tutorial: convertir la pantalla ESP32 CYD (Cheap Yellow Display, modelo ESP32-2432S028R) en un mando con interfaz táctil capaz de enviar órdenes a múltiples ESP32 receptores usando el protocolo ESP NOW.
La gracia de ESP NOW es que NO necesitas router WiFi: los ESP32 se hablan directamente entre sí en la banda de 2.4 GHz usando la dirección MAC del receptor como "destino". Latencia bajísima (~10 ms), alcance de hasta ~200 metros en línea de vista, y consumo mínimo. Para domótica casera donde no quieres depender de que el WiFi del hogar esté funcionando, es la herramienta ideal.
Lo que vas a aprender:
- Cómo construir una interfaz táctil con pestañas (tabs) en LVGL sobre el CYD
- Cómo identificar la dirección MAC de cada placa receptora
- Cómo enviar mensajes ESP NOW dirigidos desde el CYD a placas específicas
- Cómo extender el sistema a más placas o más GPIOs sin reescribir el código

Hardware necesario y arquitectura del sistema
El proyecto usa una topología 1-a-N: una placa CYD actúa de sender (controlador), y dos o más ESP32 actúan de receivers. Cada receiver tiene asignados GPIOs específicos que el sender controla por separado.
En este ejemplo controlamos:
- ESP32 #1 (DOIT DevKit V1): GPIO 2 y GPIO 4
- ESP32 #2 (ESP32-S3 / Freenove): GPIO 20 y GPIO 21
Cada GPIO lleva un LED con su resistencia de 220 Ω en serie. Si quisieras controlar relés, motores o tiras LED en vez de los LEDs de prueba, conectas la lógica de potencia respectiva al mismo GPIO. el sender no se entera de qué hay del otro lado, solo manda HIGH o LOW.

Componentes para replicar el tutorial
- 1× ESP32 CYD (ESP32-2432S028R). la pantalla táctil 2.8" 240×320 que funciona de control remoto
- 2× ESP32 DevKit V1 o equivalente. placas receptoras (puedes usar cualquier ESP32, incluso S3 o C3)
- 4× LED 5 mm (color a elección)
- 4× Resistencia 220 Ω (limita corriente del LED)
- Protoboard 830 puntos y jumpers macho macho para el cableado de prueba
- Cable USB-A a Micro USB o USB-C según tu placa, para alimentación y carga del firmware
Setup previo en Arduino IDE
Antes del código necesitas tres librerías instaladas desde el Library Manager de Arduino IDE 2:
- lvgl versión 9.X (de kisvegabor). la librería gráfica
- TFT_eSPI (de Bodmer). driver del display
- XPT2046_Touchscreen (de Paul Stoffregen). controlador del touch
Importante: tanto lv_conf.h (de LVGL) como User_Setup.h (de TFT_eSPI) deben estar configurados específicamente para el CYD. La configuración por defecto NO funciona. Sigue la guía de configuración LVGL para CYD que el tutorial original referencia en el GitHub.
Paso 1: Obtener la dirección MAC de cada placa receptora
ESP NOW direcciona mensajes por MAC, no por IP, así que necesitas conocer la MAC exacta de cada ESP32 receptor. Carga este sketch en cada placa receptora, abre el monitor serial a 115200 baudios y anota la MAC que imprime.

El resultado en el monitor serial se ve así:
[DEFAULT] ESP32 Board MAC Address: 30:ae:a4:f6:7d:4c
Repite para cada placa receptora. En el tutorial original quedan:
- ESP32 Receiver Board 1:
30:ae:a4:f6:7d:4c - ESP32 Receiver Board 2:
34:85:18:40:2f:cc
Las tuyas serán distintas. son únicas por placa. Guárdalas porque el sketch del sender las necesita.

Paso 2: Cableado de las placas receptoras
Cada receptor tiene dos LEDs (uno por GPIO controlado). El esquema típico es:
- GPIO → ánodo del LED (pata larga)
- Cátodo del LED (pata corta) → resistencia 220 Ω → GND

Si querés que el receptor controle un relé en vez de un LED, el GPIO va a la entrada IN del módulo relé (a través de optoacoplador) y la salida COM/NO/NC va al circuito de potencia.
Paso 3: Sketch del receptor
Carga este código en TODAS las placas receptoras. Es genérico: el sender es el que decide qué GPIO activar y con qué estado, así que el mismo binario sirve para cualquier receptor.
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-cyd-esp-now-control-multiple-boards/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
#include <esp_now.h>
#include <WiFi.h>
// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
int gpio;
bool state;
} struct_message;
// Create a struct_message called myData
struct_message myData;
// callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
memcpy(&myData, incomingData, sizeof(myData));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Setting GPIO: ");
Serial.println(myData.gpio);
Serial.print("state to: ");
Serial.println(myData.state);
// Control GPIO
pinMode(myData.gpio, OUTPUT);
digitalWrite(myData.gpio, myData.state);
Serial.println();
}
void setup() {
// Initialize Serial Monitor
Serial.begin(115200);
// Initialize WIFI STA interface
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
// Once ESPNow is successfully Init, we will register for recv CB to
// get recv packer info
esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));
}
void loop() {
}
Cómo funciona el sketch del receptor
El receptor declara una struct struct_message con dos campos: el número de GPIO (int gpio) y el estado (bool state). Esta estructura DEBE coincidir bit a bit con la del sender. si no, el memcpy deja basura en myData y los GPIOs activos van a ser aleatorios.
La función callback OnDataRecv se llama automáticamente cada vez que llega un paquete ESP NOW. Adentro:
- Copia los bytes recibidos al struct local con
memcpy. - Imprime debug en serial (útil para ver qué llegó).
- Hace
pinMode(myData.gpio, OUTPUT)cada vez. es defensivo: garantiza que el GPIO esté como salida aunque no lo hayas declarado antes. - Aplica el estado con
digitalWrite.
En setup() solo inicializa el WiFi en modo estación, arranca ESP NOW y registra el callback. loop() queda vacío porque toda la acción es asincrónica vía callback.
Paso 4: Sketch del CYD sender con LVGL y pestañas
Esta es la parte central. El CYD ejecuta una UI con dos tabs (uno por receptor) y dentro de cada tab dos toggle switches (uno por GPIO). Cuando tocas un switch, se dispara un evento LVGL que arma un struct struct_message con el GPIO y el estado, y lo despacha por ESP NOW a la MAC correspondiente.
Antes de compilar, reemplazá las MAC placeholder con las reales que anotaste en el paso 1:
// REPLACE WITH YOUR RECEIVER'S MAC Address
uint8_t board1Address[] = {0x30, 0xAE, 0xA4, 0xF6, 0x7D, 0x4C};
uint8_t board2Address[] = {0x34, 0x85, 0x18, 0x40, 0x2F, 0xCC};
El truco arquitectónico está en una segunda estructura, SwitchUserData, que el código guarda como user_data de cada switch LVGL:
typedef struct {
int gpio;
struct_message* data;
uint8_t* targetAddress;
} SwitchUserData;
Eso significa que cada switch sabe qué GPIO controla, qué struct de mensaje llenar y a qué MAC enviarlo. sin necesidad de un giant if/else en el handler. El toggle_switch_event_handler es genérico y reutilizable: lee el user_data del switch que disparó el evento, completa el struct y llama a sendEspNowMessage. Para agregar un tercer receptor, agregas un tab, dos switches y dos SwitchUserData más. no tocas el handler.

Calibración del touch (¡importante!)
El XPT2046 del CYD viene con resistencias de divisor de voltaje que necesitan calibración por unidad. Los valores alpha_x, beta_x, delta_x, alpha_y, beta_y, delta_y del código son los del tutorial original. Si en tu CYD el touch responde desplazado, recalibra siguiendo la guía de calibración táctil de Random Nerd Tutorials. En general, los valores por defecto funcionan razonablemente para la versión común del CYD.
Paso 5: Compilación y carga
En Arduino IDE, antes de subir el sketch del sender al CYD:
- Tools → Board → "ESP32 Dev Module"
- Tools → Partition Scheme → "Huge APP (3MB No OTA / 1MB SPIFFS)". sin esto, el binario LVGL + WiFi + ESP NOW no entra
- Tools → Port → el puerto USB del CYD
- Sube con el botón flecha derecha

Carga los sketches receptores en las otras ESP32 (sin necesidad de cambiar particiones. son sketches livianos).
Paso 6: Demostración
Una vez todas las placas encendidas, en la pantalla del CYD vas a ver dos tabs ("Board #1" y "Board #2"). Cada tab tiene dos switches. Tocas un switch y al instante el LED del receptor correspondiente cambia de estado.

En el monitor serial del sender verás:
Packet to: 30:ae:a4:f6:7d:4c send status: Delivery Success
Si en cambio ves Delivery Fail, revisa:
- ¿La MAC en el código coincide exactamente con la real del receptor?
- ¿El receptor está encendido y con el sketch cargado?
- ¿Las placas están dentro del rango ESP NOW (típicamente <50 m con obstáculos)?
Variantes y mejoras
Una vez que tienes el esqueleto funcionando, hay muchas formas de extenderlo según tu caso de uso. Estas son ideas que el tutorial original no aborda y que valen la pena explorar:
Domótica con persistencia local: agrega un sensor BME280 al CYD vía I²C y muestra temperatura/humedad de la pieza junto a los switches. El CYD pasa de ser solo un control remoto a un dashboard ambiental + control.
Modo escenario (escenas predefinidas): agrega un tercer tab "Escenas" con botones tipo "Modo cine" (apaga todo menos LED ambiente), "Modo lectura" (LED principal al máximo), "Modo dormir" (todo apagado). Cada botón dispara una secuencia de mensajes ESP NOW a múltiples receptores. Es 30 líneas extra de código y multiplica el valor del proyecto.
Confirmación visual con feedback bidireccional: el receptor puede responder vía ESP NOW al sender confirmando que el GPIO efectivamente cambió. Pintas el toggle switch verde si llegó OK, rojo si falló. Útil para detectar si un receptor está caído.
Trigger por temporizador: combina con un módulo RTC DS3231 en el sender para que ciertos GPIOs se activen automáticamente a horarios definidos (ej. luces del jardín a las 19:30). El usuario sigue pudiendo override manual desde la pantalla.
Sleep agresivo en los receptores: si el receptor controla algo que rara vez cambia (ej. el portón eléctrico), entra en deep sleep y despierta con un timer ESP NOW para verificar mensajes pendientes. Consumo: pasa de 80 mA → 5 µA promedio.
Personalización para Chile
Todos los componentes electrónicos están disponibles en MechatronicStore (busca por nombre exacto en el catálogo):
- Placa ESP32 CYD (ESP32-2432S028R). el core del control remoto, con pantalla táctil 2.8" 240×320, LED RGB, slot microSD y todo el circuito de programación
- Placa ESP32 DevKit V1. alternativa económica para los receptores; cualquier ESP32 (incluso C3 o S3) funciona idéntico para el firmware receiver
- LED 5mm (rojo, verde o color a elección). 4 unidades
- Resistencia 220 Ω. 4 unidades, limita corriente del LED
- Protoboard 830 puntos. para prototipo y pruebas
- Jumpers macho macho 20cm. pack de ~40 unidades cubre todo el cableado
- Cable USB-A a Micro USB o Cable USB-C. uno por placa para programación y alimentación
Equivalencias respecto al tutorial original: Random Nerd Tutorials linkea a MakerAdvisor (Aliexpress/Amazon redirect). El CYD ESP32-2432S028R es exactamente el mismo módulo en cualquier proveedor. busca por "Cheap Yellow Display" o "ESP32-2432S028R" si no aparece por su nombre comercial. La DOIT DevKit V1 que usan ellos es funcionalmente equivalente a cualquier ESP32 DevKit del catálogo MS.
Recursos
- Tutorial original: ESP32 CYD with ESP NOW: Control Multiple ESP32 Boards (Random Nerd Tutorials)
- Guía de calibración táctil del CYD: Touchscreen Calibration
- Configuración LVGL para CYD: CYD LVGL Setup Guide
- Librería LVGL: lvgl/lvgl en GitHub
- Librería TFT_eSPI: Bodmer/TFT_eSPI
- Librería XPT2046_Touchscreen: PaulStoffregen/XPT2046_Touchscreen
- Documentación ESP NOW (Espressif): ESP NOW Overview
Versión chilena con componentes en stock local en MechatronicStore.





