Montar un sistema de domótica con ESP32 y AWS IoT Core no es difícil por los conceptos. Lo difícil son los fallos silenciosos: el código compila, la librería dice que todo salió bien y aun así no pasa nada. Estos son los 13 errores que más horas me costaron, con el síntoma tal como lo vi, la causa real y la solución.
Cada error tiene su artículo propio con el detalle y el código; aquí va el resumen para encontrar rápido el tuyo.
Firmware del ESP32
1. "was not declared in this scope" con un struct que sí está declarado
Síntoma: el sketch no compila y el error apunta a un tipo que está definido unas líneas más arriba.
Causa: el IDE de Arduino genera prototipos de tus funciones y los inserta antes de la primera función que encuentra. Si esa "primera función" está antes de la definición del tipo (por ejemplo, una macro que se expande a una función), los prototipos usan un tipo que todavía no existe.
Solución: tipos en un .h incluido al principio y macros después de todos los #include. Detalle en el error de los prototipos automáticos de Arduino.
2. El ESP32-C3 se desconecta de AWS IoT un segundo después de conectar
Síntoma: conecta, AWS registra la desconexión con motivo CLIENT_ERROR y vuelve a empezar. El mismo firmware funciona en un ESP32-S3.
Causa: la pila (stack) de la tarea loop() se queda corta con TLS, MQTT y JSON a la vez, y la memoria se corrompe sin un reinicio que lo delate.
Solución: SET_LOOP_TASK_STACK_SIZE(16 * 1024);. Detalle en ESP32-C3 y CLIENT_ERROR por falta de stack.
3. publish() devuelve true y a AWS no llega nada
Síntoma: el monitor serie dice "publicado", pero el cliente de pruebas MQTT de AWS no recibe nada. La política, el tópico y el tamaño del búfer están bien.
Causa: con QoS 0, true solo significa que el mensaje se escribió en el búfer de red, no que llegó. En mi caso, PubSubClient con WiFiClientSecure en un ESP32-S3 perdía los mensajes.
Solución: cambiar a la librería MQTT de 256dpi y publicar con QoS 1. Detalle en ESP32 publica en MQTT pero no llega a AWS IoT.
4. softAPmacAddress() devuelve ceros en el ESP32-S3
Síntoma: el nombre de la red del portal de configuración sale con 0000 en vez del final de la MAC.
Causa: en el S3, la MAC del punto de acceso no está lista en el mismo instante en que llamas a WiFi.mode(WIFI_AP). En el C3 sí lo está, por eso el código "funcionaba".
Solución: una pausa corta antes de leerla:
WiFi.mode(WIFI_AP);
delay(100);
String mac = WiFi.softAPmacAddress();
Más sobre el portal en portal cautivo para configurar el Wi-Fi del ESP32.
ESP-NOW
5. esp_now_add_peer() falla a partir del dispositivo 21
Síntoma: los primeros 20 nodos funcionan; el siguiente no se puede registrar.
Causa: ESP-NOW admite como máximo 20 peers por dispositivo, y es un límite fijo.
Solución: broadcast, registrar peers solo al enviar o un segundo coordinador. Detalle en el límite de 20 dispositivos de ESP-NOW.
6. Un nodo que se reinicia pierde el último comando
Síntoma: apagas una luz desde la app mientras el nodo está sin corriente; cuando vuelve, enciende con el estado anterior.
Causa: al preguntar "¿cómo debo estar?", el coordinador respondía con el último estado confirmado, no con el último pedido.
Solución: guardar los dos estados y responder con el deseado. Detalle en comandos perdidos al reconectar un nodo ESP-NOW.
MQTT y Device Shadow
7. El WebSocket a AWS IoT se cierra con código 1006
Síntoma: el navegador abre el WebSocket y se cierra al instante, sin mensaje.
Causa: el token de sesión (X-Amz-Security-Token) se incluyó en la firma SigV4. En AWS IoT va fuera de la firma.
Solución: firmar sin el token y añadirlo al final de la URL. Detalle en WebSocket de AWS IoT cerrado con 1006.
8. Al iniciar sesión, todos los interruptores aparecen apagados
Síntoma: el estado real está bien en AWS, pero la app muestra todo apagado de vez en cuando.
Causa: la app publicaba la petición de estado antes de que la suscripción a la respuesta estuviera activa. La respuesta llegaba y nadie la escuchaba.
Solución: esperar el SUBACK. Detalle en respuesta MQTT perdida por no esperar el SUBACK.
9. La luz vuelve sola al estado anterior un segundo después
Síntoma: cambias la luz con el interruptor físico y, un segundo después, vuelve como estaba.
Causa: el dispositivo reportó el nuevo estado, pero desired seguía con el valor viejo. AWS generó un delta y el dispositivo lo "corrigió".
10. La app muestra encendido algo que nunca se encendió
Síntoma: con el dispositivo desconectado, el interruptor de la app se queda en "encendido" para siempre.
Causa: la app pintaba desired cuando no llegaba reported.
Solución de 9 y 10 en errores de desired y reported en el Device Shadow.
Login y web app
11. Cognito Identity Pool no entrega credenciales
Síntoma: el login con el User Pool funciona, pero el paso de pedir credenciales de AWS falla con NotAuthorizedException.
Causa: el User Pool no estaba vinculado a la Identity Pool como proveedor de identidad.
Solución: vincularlo con el ID del app client. Detalle en Identity Pool que no entrega credenciales.
12. "Olvidé mi contraseña" no envía el código
Síntoma: a un usuario creado por el administrador nunca le llega el código de recuperación.
Causa: sigue en estado FORCE_CHANGE_PASSWORD porque nunca completó su primer inicio de sesión.
Solución: contraseña permanente al crearlo o completar el reto NEW_PASSWORD_REQUIRED. Ver login con Cognito y FORCE_CHANGE_PASSWORD.
13. La PWA no se actualiza aunque publiques una versión nueva
Síntoma: despliegas y los usuarios siguen viendo la versión anterior durante días.
Causa: el registro por defecto del service worker se ejecuta una vez y nunca vuelve a comprobar.
Solución: registro explícito con chequeo periódico. Ver PWA con Vite que se actualiza.
Lo que aprendí de estos 13 errores
- "Éxito" en la librería no es éxito en el sistema. Comprueba el resultado desde el otro extremo: el cliente de pruebas MQTT de AWS, el Shadow con la CLI o los eventos de conexión.
- Si funciona en una placa y no en otra, sospecha de la memoria o del tiempo, no de tu lógica.
- Separa lo pedido de lo confirmado en todo el sistema: en el coordinador, en el Shadow y en la interfaz.
- Anota la causa real cuando lo resuelves. La mitad de estos errores reaparecieron meses después en otra parte del código.
Si estás empezando, la guía del ESP32 y la serie de domótica con ESP32 y AWS explican cómo encaja todo.
Preguntas frecuentes
¿Cómo veo por qué AWS IoT desconectó mi ESP32?
Activa los registros de AWS IoT en CloudWatch o suscríbete a los eventos de ciclo de vida ($aws/events/presence/disconnected/+). El campo disconnectReason dice el motivo: CLIENT_ERROR, DUPLICATE_CLIENTID, AUTH_ERROR, etc.
¿Estos errores pasan también con otros brokers MQTT?
Los de firmware, ESP-NOW y SUBACK, sí. Los del Shadow, la firma SigV4 y Cognito son propios de AWS.