Volver

Cute Desktop Robot: una cara que escucha en el ESP-VoCat, con su emulador en tu navegador

La mayoría de los gadgets de escritorio muestran información. Yo quería uno que reaccionara a las personas: un robot que te mire cuando hablas, te responda y haga algo divertido cuando no hay nadie cerca. La Espressif ESP-VoCat v1.2 tiene el hardware para ello. Combina un SoC ESP32-S3, una pantalla táctil redonda de 360×360, dos micrófonos, un altavoz y una batería en una carcasa con orejas de gato.

VoCat Eyes es la cara que escribí para él, y el primer robot de una serie llamada Cute Desktop Robot. Sus características principales son:

El firmware, el emulador y las herramientas son de código abierto en el repositorio cute-desktop-robot.

El robot VoCat Eyes en la vista 3D del emulador: la carcasa ESP-VoCat con orejas de gato en azul, con la cara del robot en su pantalla redonda

La imagen de arriba es la vista 3D del emulador: el modelo de la carcasa que publicó Espressif, con la pantalla en vivo del robot emulado dentro.

Y este es el robot real sobre un escritorio, ejecutando el mismo firmware:

El robot en el hardware real: míralo en YouTube.

Prueba el robot en tu navegador

El emulador de abajo ejecuta el código C++ del robot compilado a WebAssembly: el mismo motor de cara, máquina de estados y pantallas que se ejecutan en el ESP32-S3. Solo la capa de hardware se sustituye por piezas simuladas. No necesita cuenta ni instalación. Solo la conversación por voz necesita el robot real, porque el emulador no tiene red.

Si el marco es demasiado pequeño, abre el emulador en una ventana completa.

La ventana tiene tres partes. A la izquierda está el robot en 3D, debajo está la máquina de estados, y a la derecha hay un panel con el grafo de emociones y los sensores. Para probarlo:

  1. Toca la pantalla del robot. Haz clic una vez en la pantalla redonda del robot en 3D. El robot reproduce su siguiente animación.
  2. Abre los ajustes. Haz clic y mantén pulsado en la pantalla hasta que aparezcan el reloj y la batería, luego haz clic una vez en el reloj. Se abre un menú de ajustes. Haz clic en la mitad izquierda de una fila para bajar un valor y en la mitad derecha para subirlo. Haz clic y mantén pulsado para cerrar el menú.
  3. Habla desde un lado. En el panel de la derecha, bajo SENSORS, arrastra el deslizador junto a from hasta un ángulo, luego haz clic en talk [V]. Un punto morado en el suelo muestra de dónde viene la voz simulada, y los ojos del robot giran hacia ella.
  4. Observa la máquina de estados. Cada vez que el robot cambia de actividad, la casilla correspondiente se ilumina en la máquina de estados, y las barras de abajo muestran los temporizadores que pueden cambiarla a continuación.
  5. Gira el robot. Arrastra en cualquier punto fuera de la pantalla para rotar la vista 3D, y usa la rueda del ratón para hacer zoom. Pulsa M para cambiar a una vista plana de la pantalla.

El teclado también funciona una vez que haces clic en el emulador: 1 a 9 muestran expresiones, Y asiente, U niega con la cabeza, y H muestra un bocadillo de diálogo. El toque y los ajustes funcionan igual en el robot real.

La siguiente captura muestra toda la ventana del emulador tal como se ve en un PC de escritorio:

El emulador de escritorio completo: el robot en 3D, el grafo de emociones, los sensores con sus deslizadores, y la máquina de estados con sus temporizadores

El hardware

El ESP-VoCat viene montado, con la carcasa, la batería y la pantalla en su sitio. La guía de usuario del ESP-VoCat describe la placa en detalle. Un único módulo ESP32-S3 hace todo el trabajo. Tiene 16 MB de memoria flash y 16 MB de PSRAM (RAM externa). La PSRAM hace posible una cara de 360×360: un fotograma completo en color de 16 bits ocupa 253 KB, más de lo que puede contener la RAM interna del SoC.

El siguiente diagrama muestra las piezas que usa el firmware y el bus que conecta cada una al ESP32-S3:

Diagrama de bloques: el ESP32-S3 controla la pantalla LCD redonda ST77916 por QSPI, lee el táctil CST816S y el medidor de batería BQ27220 por I2C, recibe los micrófonos a través del ES7210 y reproduce a través del ES8311 y su amplificador por I2S TDM, y habla con Gemini Live por Wi-Fi

Dos piezas necesitaron trabajo extra:

Cómo dibuja el robot su cara

El robot redibuja su cara cada 33 ms, unas 30 veces por segundo. El diagrama de abajo sigue uno de esos fotogramas desde los sensores hasta la pantalla:

El canal de fotogramas: los Sensors producen un SensorState para el Brain, el Brain elige una expresión para el FaceEngine, el FaceEngine dibuja formas en el Canvas, el Canvas encuentra los mosaicos de 24 píxeles que han cambiado y VocatLcd envía solo esos por QSPI. La tarea Listener alimenta los Sensors con voz, golpes y la palabra de activación, y la conversación alimenta el Brain con set_face y play_act

Cada bloque del diagrama es un módulo del firmware:

Todas las expresiones y animaciones

El emulador incluye una herramienta de línea de comandos, facetool, que renderiza la pantalla del robot sin abrir una ventana. Los vídeos de abajo provienen de ella. Cada uno reproduce todas las caras de un grupo al mismo tiempo — primero el cambio a cada cara, luego la cara en sí.

Expresiones

Tranquilo y pensativo (16) — normal, curioso, concentrado, escéptico, aburrido, confundido y escuchando, entre otras.

Feliz (15) — desde una pequeña sonrisa hasta la risa, el amor y el flechazo.

Triste (8) — preocupado, suplicante, llorando, solitario y culpable, entre otras.

Enfadado (6) — desde molesto hasta furioso, y asco.

Sorprendido y alerta (7) — sorprendido, asustado, mareado, noqueado e hipnotizado, entre otras.

Somnoliento (4) — somnoliento, bostezando, dormido y cansado.

Animaciones

Las animaciones, llamadas acts en el código, son escenas cortas, muchas con un objeto dibujado junto a los ojos — una nube de lluvia, una taza de café, una pantalla de radar. El Brain las reproduce cuando el robot está inactivo, y un toque en la pantalla reproduce la siguiente.

Lenguaje corporal (9) — asentir, negar con la cabeza, poner los ojos en blanco, estornudar.

Efectos de pantalla (5) — un glitch, un reinicio, una línea de escáner, código cayendo.

Con objetos (18) — corazones, lluvia, fuegos artificiales, burbujas, una pelota, bailar.

Cosas del día a día (9) — un libro, auriculares, café, un cohete, nieve.

Juegos y efectos de pantalla redonda (6) — Pac-Man, Pong, un arcoíris, un radar, un acuario.

Las mismas cuadrículas, como GIF animados, están en el README del repositorio.

Cómo habla el robot

La conversación utiliza la API Gemini Live, que recibe un flujo de audio y responde con un flujo de audio a través de una única conexión WebSocket. En el momento de escribir esto, el robot usa el modelo gemini-2.5-flash-native-audio. El siguiente diagrama muestra el recorrido del audio:

El canal de voz: los micrófonos ES7210 alimentan la tarea Listener a 16 kHz, la tarea GeminiLive envía fragmentos de 100 ms a la API Gemini Live y recibe audio a 24 kHz, que se remuestrea para el Player y el altavoz. Las llamadas a herramientas van a Talk, que cambia la cara. El Player silencia el Listener mientras reproduce

El robot envía el sonido de un micrófono como audio PCM de 16 kHz, en fragmentos de 100 ms. Gemini responde con voz a 24 kHz, que el robot convierte a 16 kHz y reproduce.

La conversación es semidúplex: el robot o escucha o habla, nunca ambas cosas a la vez. El altavoz está a pocos centímetros de los micrófonos, así que el robot silencia el flujo del micrófono mientras reproduce una respuesta. De lo contrario, Gemini oiría su propia voz. Gemini decide por su parte cuándo la persona ha terminado de hablar.

Gemini también recibe cuatro herramientas, funciones que puede llamar durante la conversación: set_face, play_act, look y show_text. Cuando la conversación se vuelve triste, Gemini puede llamar a set_face("sad"). Cuando le pides al robot que asienta, puede llamar a play_act("yes") y el robot asiente. Cada sesión comienza con un breve mensaje de texto que contiene la hora y la fecha locales, para que Gemini pueda responder preguntas sobre ellas.

Tres problemas acapararon la mayor parte del tiempo de desarrollo:

  1. La conexión segura. El robot se conecta a Gemini mediante TLS. La función setInsecure() de la biblioteca ArduinoWebsockets no tiene efecto en el ESP32-S3, así que el handshake TLS fallaba. El firmware ahora incluye los dos certificados raíz de Google (GTS Root R1 y R4, válidos hasta 2036). También espera a que el reloj se sincronice por NTP, porque verificar un certificado necesita la fecha actual.
  2. La RAM interna. La palabra de activación “Hi ESP” se ejecuta en ESP-SR, la biblioteca de reconocimiento de voz de Espressif, que ocupa unos 100 KB de RAM interna. La biblioteca TLS necesita RAM interna para el handshake, y los primeros intentos fallaban con SSL - Memory allocation failed. Ahora el robot detiene ESP-SR cuando empieza una conversación, lo que libera 107 KB, y lo vuelve a cargar cuando la conversación termina. Cargar ESP-SR tarda unos 1,2 segundos.
  3. El flujo de micrófono ausente. Durante un día, el robot saludaba al usuario y luego ignoraba todo lo que venía después. Sus ojos seguían mi voz, así que los micrófonos claramente funcionaban. La causa era que el firmware nunca habilitaba el flujo de audio hacia Gemini. Ahora la consola serie imprime la transcripción de Gemini de lo que oyó, así que el mismo problema sería visible de inmediato.

Usar Gemini requiere una clave de API de Google AI Studio, y la API se factura por uso según los precios de la API Gemini de Google. La clave permanece en un archivo local secrets.h que nunca se sube al repositorio.

Ajustes en la pantalla del robot

El robot no necesita un ordenador una vez configurado. Una pulsación larga en la pantalla muestra el reloj, el nivel de batería y el estado del Wi-Fi. Un toque en el reloj abre los ajustes, donde puedes cambiar:

La fila de Wi-Fi abre una lista de las redes disponibles y un teclado en pantalla con mayúsculas, números y símbolos. El ESP32-S3 no puede escanear redes mientras se conecta a una, así que el firmware pausa los intentos de conexión durante el escaneo y los reanuda después. Si la contraseña es incorrecta, la pantalla del reloj lo muestra como motivo. El ESP32-S3 solo admite redes de 2,4 GHz.

Un solo código base para el robot, el PC y el navegador

El código del robot no llama directamente a los controladores de pantalla, táctil o audio. La cara, el Brain y las pantallas pasan por una fina capa de hardware en src/hw y src/audio. El emulador sustituye esa capa, así que el resto del código se compila sin cambios para tres objetivos:

El C++ compartido en src/ se compila de tres formas: arduino-cli genera el firmware del ESP32-S3, g++ con cabeceras de Arduino simuladas genera el emulador de escritorio raylib, y em++ genera el mismo emulador para el navegador

En el emulador, pequeñas versiones de reemplazo de Arduino.h, Wire.h y SPI.h escriben en una estructura que hace las veces de hardware. Contiene la memoria de la pantalla, los registros del controlador táctil y las lecturas de la batería. La ventana del emulador, escrita con la biblioteca gráfica raylib, dibuja esa estructura y te permite cambiar los valores de los sensores.

La versión para navegador es el mismo programa emulador compilado con Emscripten, que convierte C++ en WebAssembly. Tres cambios lo hicieron posible:

  1. raylib para la web. raylib se compila con Emscripten en una biblioteca estática que dibuja mediante WebGL.
  2. Un fotograma por llamada. La versión de escritorio se ejecutaba en un bucle infinito. Ese bucle se convirtió en una función que dibuja un fotograma. La versión de escritorio la llama en un bucle, y el navegador la llama una vez por cada refresco de pantalla.
  3. Un tamaño de ventana fijo. En el navegador, el emulador dibuja en un lienzo de 1380×900, y la página escala el lienzo para ajustarlo. Un lienzo redimensionable rompía la correspondencia entre las coordenadas del ratón y las del lienzo.

La carcasa 3D proviene de Espressif. ESP-College publicó la carcasa del ESP-VoCat (EchoEar) como archivos STL en OSHWHub bajo la licencia GPL-3.0. Un breve script de Python convierte los archivos STL en un único archivo de modelo 3D y reduce su detalle para que el navegador lo cargue rápido. El emulador dibuja la pantalla en vivo sobre un disco en la ventana redonda de la carcasa. Cuando haces clic en la pantalla 3D, el emulador encuentra el punto pulsado en ese disco y lo convierte en un toque en el píxel correspondiente.

Construye el tuyo

Para construir VoCat Eyes necesitas una placa ESP-VoCat v1.2 y, en el momento de escribir esto, la versión 3.3 del core de Arduino para ESP32. El README de VoCat Eyes enumera las bibliotecas y los comandos exactos. En resumen:

  1. Copia secrets.example.h a secrets.h y añade tu clave de API de Gemini. También puedes introducir allí la red Wi-Fi, o configurarla más tarde en el robot.
  2. Compila el sketch con el esquema de particiones esp_sr_16 y el archivo partitions.csv de la carpeta del sketch. Esta distribución da a la aplicación 6 MB de flash.
  3. Sube el firmware junto con srmodels.bin, el archivo de modelo para la palabra de activación “Hi ESP”.
  4. En el robot, mantén pulsada la pantalla, toca el reloj, elige Wi-Fi, selecciona tu red y escribe su contraseña.
  5. Di “Hi ESP”, o toca el pequeño micrófono en la parte inferior de la pantalla, y empieza a hablar.

Si estás diseñando firmware para el ESP32-S3 u otras placas, Velxio puede simularlo en el navegador antes de que flashees ningún hardware.


Compartir en:
David Montero

Escrito por

David Montero

Creator of Velxio, the open-source circuit and Arduino simulator.

GitHub velxio.dev

Artículos relacionados


Artículo anterior
Siete proyectos de Halloween que puedes construir y ejecutar en el navegador
Artículo siguiente
Un panel de uso de Claude en el M5Stack Basic: límites del plan y gasto de API en tu escritorio