Volver al blog
Artículo

Un finde con un ESP32

5 min lectura

ESP32 con avatar en pantalla

No sabía soldar. No sabía qué era un riel de protoboard. Terminé la sesión con un asistente de shopping funcionando, con Claude de cerebro y un avatar reactivo en pantalla completa.

Esto es lo que construí y cómo funciona.

El hardware

El kit Keyestudio "Xiaozhi" trae una ESP32-S3, un micrófono INMP441 (I2S), un amplificador MAX98357A con parlante, y una pantalla LCD. Vine pre-flasheado — no tuve que escribir una línea de C++.

Armé todo en una protoboard. El primer problema fue un mal contacto invisible en el micrófono: el firmware corría bien, pero el micro no captaba nada. Solución: reubicar el módulo y reiniciar el aparato para que el I2S se re-inicialice. Sin debug sofisticado. Solo física.

La arquitectura

[ Persona habla ]
       mic I2S
      
   ESP32-S3  ──WebSocket──►  Server Docker (Mac)
                               VAD  STT  LLM  TTS
             ◄──────────────  audio respuesta
                                     docker logs -f
                                    
                           Puente SSE (Next.js)
                                    
                                    
                           Asistente full-screen (Next.js)
                           cara + cards del local

El ESP32 hace una sola cosa: captura voz y reproduce audio. El cerebro corre en un servidor Docker en la Mac. El avatar es una app Next.js aparte.

Esa última decisión fue la más importante del proyecto.

El cerebro y la pantalla

El stack de voz:

  • VAD: SileroVAD (local)
  • STT: Whisper de OpenAI (gpt-4o-mini-transcribe) — el STT local no soporta español
  • LLM: Claude claude-haiku-4-5 vía endpoint OpenAI-compatible de Anthropic
  • TTS: OpenAI tts-1, voz nova, speed 1.2

Un problema que costó entender: Claude respondía en chino. El prompt por defecto del firmware era en mandarín, y la voz española no puede leer chino. Cambiar el prompt a español arregló el idioma y el TTS de una sola vez.

El avatar (Next.js + Tailwind v4 + framer-motion): Una cara con ojos y boca, 4 estados: idle, listening, thinking, speaking. Animaciones spring para que ninguna transición sea seca. El estado no viene del server — viene de parsear los logs de Docker en tiempo real, via SSE.

Cuando Claude recomienda un local, el avatar muestra su card: logo real, foto real, piso y rubro. Hasta 3 cards, con entrada stagger.

El caso real

No era una demo. El personaje conoce los 91 locales reales de un shopping real (nombre, rubro, foto, logo, piso). Los bajé con curl porque la web bloqueaba scrapers. Los rubros eran genéricos (Indumentaria = ropa + calzado + lencería), así que investigué cada local con 5 agentes en paralelo y les agregué subrubro, productos y género. Eso hace que Claude pueda recomendar preciso: "zapatillas" va a calzado, no a hogar.

El asistente saluda, pregunta qué busca la persona, recomienda con piso, y cierra.

Lo que aprendí

El avatar en la LCD del ESP32 no era viable sin recompilar el firmware en C++. La pantalla grande (web full-screen) me dio control total, velocidad de iteración, y es el frente real del dispositivo en producción.

La latencia está dominada por la cadena STT → LLM → TTS, no por el hardware. Con los tres por API ronda los 2-3 segundos.

Hoy esto es un prototipo. Ya estamos trabajando en llevarlo a escala.


El código del servidor no es nuestro. El firmware tampoco. Lo que construimos es la capa que los conecta: los personajes, los datos, el avatar, la lógica de negocio.

Eso es el producto.