Indietro

Cute Desktop Robot — un volto che ascolta sull'ESP-VoCat, con il suo emulatore nel browser

La maggior parte dei gadget da scrivania mostra informazioni. Io ne volevo uno che reagisse alle persone — un robot che ti guarda quando parli, ti risponde e fa qualcosa di divertente quando non c’è nessuno in giro. L’Espressif ESP-VoCat v1.2 ha l’hardware per farlo. Combina un SoC ESP32-S3, uno schermo tondo touch da 360×360, due microfoni, un altoparlante e una batteria in una custodia con le orecchie da gatto.

VoCat Eyes è il volto che ho scritto per lui, e il primo robot di una serie chiamata Cute Desktop Robot. Le sue caratteristiche principali sono:

Il firmware, l’emulatore e gli strumenti sono open source nel repository cute-desktop-robot.

Il robot VoCat Eyes nella vista 3D dell'emulatore — la custodia ESP-VoCat con le orecchie da gatto in blu, con il volto del robot sul suo schermo tondo

L’immagine sopra è la vista 3D dell’emulatore — il modello della custodia pubblicato da Espressif, con lo schermo live del robot emulato al suo interno.

E questo è il robot reale su una scrivania, che esegue lo stesso firmware:

Il robot sull’hardware reale — guardalo su YouTube.

Prova il robot nel tuo browser

L’emulatore qui sotto esegue il codice C++ del robot compilato in WebAssembly — lo stesso motore del volto, la stessa macchina a stati e le stesse schermate che girano sull’ESP32-S3. Solo il livello hardware è sostituito da componenti simulati. Non richiede account né installazione. Solo la conversazione vocale richiede il robot reale, perché l’emulatore non ha rete.

Se il riquadro è troppo piccolo, apri l’emulatore in una finestra intera.

La finestra ha tre parti. A sinistra c’è il robot in 3D, sotto c’è la macchina a stati, e a destra c’è un pannello con il grafo delle emozioni e i sensori. Per provarlo:

  1. Tocca lo schermo del robot. Clicca una volta sullo schermo tondo del robot 3D. Il robot esegue la sua animazione successiva.
  2. Apri le impostazioni. Clicca e tieni premuto sullo schermo finché non compaiono l’orologio e la batteria, poi clicca una volta sull’orologio. Si apre un menu delle impostazioni. Clicca la metà sinistra di una riga per abbassare un valore e la metà destra per alzarlo. Clicca e tieni premuto per chiudere il menu.
  3. Parla da un lato. Nel pannello di destra, sotto SENSORS, trascina lo slider accanto a from su un angolo, poi clicca talk [V]. Un punto viola sul pavimento mostra da dove proviene la voce simulata, e gli occhi del robot si girano verso di essa.
  4. Osserva la macchina a stati. Ogni volta che il robot cambia attività, la casella corrispondente si illumina nella macchina a stati, e le barre in basso mostrano i timer che possono cambiarla successivamente.
  5. Ruota il robot. Trascina in un punto qualsiasi fuori dallo schermo per ruotare la vista 3D, e usa la rotella del mouse per lo zoom. Premi M per passare a una vista piatta dello schermo.

La tastiera funziona anch’essa una volta cliccato l’emulatore — da 1 a 9 mostrano le espressioni, Y annuisce, U scuote la testa e H mostra un fumetto. Il tocco e le impostazioni funzionano allo stesso modo sul robot reale.

Lo screenshot successivo mostra l’intera finestra dell’emulatore come appare su un PC desktop:

L'emulatore desktop completo — il robot in 3D, il grafo delle emozioni, i sensori con i loro slider e la macchina a stati con i suoi timer

L’hardware

L’ESP-VoCat arriva già assemblato, con la custodia, la batteria e lo schermo al loro posto. La guida utente dell’ESP-VoCat descrive la scheda in dettaglio. Un singolo modulo ESP32-S3 fa tutto il lavoro. Ha 16 MB di memoria flash e 16 MB di PSRAM (RAM esterna). La PSRAM rende possibile un volto da 360×360 — un frame completo a colori a 16 bit occupa 253 KB, più di quanto la RAM interna del SoC possa contenere.

Il diagramma seguente mostra i componenti utilizzati dal firmware e il bus che collega ciascuno di essi all’ESP32-S3:

Diagramma a blocchi — l'ESP32-S3 pilota l'LCD tondo ST77916 via QSPI, legge il touch CST816S e il misuratore di batteria BQ27220 via I2C, riceve i microfoni tramite l'ES7210 e riproduce tramite l'ES8311 e il suo amplificatore via I2S TDM, e comunica con Gemini Live via Wi-Fi

Due componenti hanno richiesto lavoro extra:

Come il robot disegna il suo volto

Il robot ridisegna il suo volto ogni 33 ms, circa 30 volte al secondo. Il diagramma seguente segue uno di questi frame dai sensori allo schermo:

La pipeline del frame — i Sensors producono un SensorState per il Brain, il Brain sceglie un'espressione per il FaceEngine, il FaceEngine disegna le forme nel Canvas, il Canvas individua i tile da 24 pixel modificati e VocatLcd invia solo quelli via QSPI. Il task Listener alimenta i Sensors con voce, colpi e wake word, e la conversazione alimenta il Brain con set_face e play_act

Ogni blocco del diagramma è un modulo del firmware:

Tutte le espressioni e le animazioni

L’emulatore include uno strumento da riga di comando, facetool, che renderizza lo schermo del robot senza aprire una finestra. I video qui sotto provengono da esso. Ognuno riproduce tutte le facce di un gruppo contemporaneamente — prima il passaggio a ciascuna faccia, poi la faccia stessa.

Espressioni

Calmo e pensieroso (16): normale, curioso, concentrato, scettico, annoiato, confuso e in ascolto, tra gli altri.

Felice (15): da un piccolo sorriso alla risata, all’amore e all’innamoramento.

Triste (8): preoccupato, supplichevole, in lacrime, solo e in colpa, tra gli altri.

Arrabbiato (6): da infastidito a furioso, e disgustato.

Sorpreso e allerta (7): sorpreso, spaventato, stordito, messo KO e ipnotizzato, tra gli altri.

Assonnato (4): assonnato, sbadigliante, addormentato e stanco.

Animazioni

Le animazioni, chiamate acts nel codice, sono brevi scene, molte con un oggetto disegnato accanto agli occhi — una nuvola di pioggia, una tazza di caffè, uno schermo radar. Il Brain le riproduce quando il robot è inattivo, e un tocco sullo schermo riproduce la successiva.

Linguaggio del corpo (9): annuire, scuotere la testa, roteare gli occhi, starnutire.

Effetti schermo (5): un glitch, un riavvio, una linea di scansione, codice che cade.

Con oggetti (18): cuori, pioggia, fuochi d’artificio, bolle, una palla, danza.

Cose di tutti i giorni (9): un libro, cuffie, caffè, un razzo, neve.

Giochi ed effetti per schermo rotondo (6): Pac-Man, Pong, un arcobaleno, un radar, un acquario.

Le stesse griglie, come GIF animate, sono nel README del repository.

Come parla il robot

La conversazione utilizza la Gemini Live API, che riceve uno stream audio e risponde con uno stream audio su un’unica connessione WebSocket. Al momento della scrittura, il robot usa il modello gemini-2.5-flash-native-audio. Il diagramma sottostante mostra il percorso dell’audio:

La pipeline vocale — i microfoni ES7210 alimentano il task Listener a 16 kHz, il task GeminiLive invia blocchi da 100 ms alla Gemini Live API e riceve audio a 24 kHz, che viene ricampionato per il Player e l'altoparlante. Le chiamate agli strumenti vanno a Talk, che cambia il volto. Il Player disattiva il Listener mentre riproduce

Il robot invia il suono di un microfono come audio PCM a 16 kHz, in blocchi da 100 ms. Gemini risponde con voce a 24 kHz, che il robot converte a 16 kHz e riproduce.

La conversazione è half duplex — il robot ascolta o parla, mai entrambe le cose contemporaneamente. L’altoparlante si trova a pochi centimetri dai microfoni, quindi il robot disattiva lo stream del microfono mentre riproduce una risposta. Altrimenti Gemini sentirebbe la propria voce. Gemini decide dal suo lato quando la persona ha finito di parlare.

Gemini riceve anche quattro strumenti, funzioni che può chiamare durante la conversazione — set_face, play_act, look e show_text. Quando la conversazione diventa triste, Gemini può chiamare set_face("sad"). Quando chiedi al robot di annuire, può chiamare play_act("yes") e il robot annuisce. Ogni sessione inizia con un breve messaggio di testo che contiene l’ora e la data locali, così Gemini può rispondere a domande su di esse.

Tre problemi hanno occupato la maggior parte del tempo di sviluppo:

  1. La connessione sicura. Il robot si connette a Gemini tramite TLS. La funzione setInsecure() della libreria ArduinoWebsockets non ha effetto sull’ESP32-S3, quindi l’handshake TLS falliva. Il firmware ora include i due certificati root di Google (GTS Root R1 e R4, validi fino al 2036). Attende anche che l’orologio si sincronizzi via NTP, perché verificare un certificato richiede la data corrente.
  2. La RAM interna. La wake word “Hi ESP” gira su ESP-SR, la libreria di riconoscimento vocale di Espressif, che occupa circa 100 KB di RAM interna. La libreria TLS necessita di RAM interna per l’handshake, e i primi tentativi fallivano con SSL - Memory allocation failed. Il robot ora arresta ESP-SR quando inizia una conversazione, liberando 107 KB, e lo ricarica quando la conversazione termina. Il caricamento di ESP-SR richiede circa 1,2 secondi.
  3. Lo stream del microfono mancante. Per un giorno, il robot salutava l’utente e poi ignorava tutto ciò che seguiva. I suoi occhi seguivano ancora la mia voce, quindi i microfoni funzionavano chiaramente. La causa era che il firmware non abilitava mai lo stream audio verso Gemini. La console seriale ora stampa la trascrizione di ciò che Gemini ha sentito, così lo stesso problema sarebbe visibile immediatamente.

Usare Gemini richiede una chiave API da Google AI Studio, e l’API viene fatturata per utilizzo secondo i prezzi della Gemini API di Google. La chiave rimane in un file locale secrets.h che non viene mai committato nel repository.

Impostazioni sullo schermo del robot

Il robot non necessita di un computer una volta configurato. Una pressione prolungata sullo schermo mostra l’orologio, il livello della batteria e lo stato del Wi-Fi. Un tocco sull’orologio apre le impostazioni, dove puoi modificare:

La riga del Wi-Fi apre un elenco delle reti nel raggio d’azione e una tastiera su schermo con lettere maiuscole, numeri e simboli. L’ESP32-S3 non può scansionare le reti mentre si connette a una, quindi il firmware sospende i tentativi di connessione durante la scansione e li riprende dopo. Se la password è errata, la schermata dell’orologio lo mostra come motivo. L’ESP32-S3 supporta solo reti a 2,4 GHz.

Un’unica base di codice per il robot, il PC e il browser

Il codice del robot non chiama direttamente i driver del display, del touch o dell’audio. Il volto, il Brain e le schermate passano attraverso un sottile livello hardware in src/hw e src/audio. L’emulatore sostituisce quel livello, così il resto del codice si compila invariato per tre target:

Il C++ condiviso in src/ si compila in tre modi — arduino-cli produce il firmware per ESP32-S3, g++ con header Arduino simulati produce l'emulatore desktop raylib, ed em++ produce lo stesso emulatore per il browser

Nell’emulatore, piccole versioni sostitutive di Arduino.h, Wire.h e SPI.h scrivono in una struttura che rappresenta l’hardware. Contiene la memoria del display, i registri del controller touch e le letture della batteria. La finestra dell’emulatore, scritta con la libreria grafica raylib, disegna quella struttura e ti permette di modificare i valori dei sensori.

La versione per browser è lo stesso programma emulatore compilato con Emscripten, che trasforma il C++ in WebAssembly. Tre modifiche lo hanno reso possibile:

  1. raylib per il web. raylib si compila con Emscripten in una libreria statica che disegna tramite WebGL.
  2. Un frame per chiamata. La versione desktop girava in un ciclo infinito. Quel ciclo è diventato una funzione che disegna un frame. La versione desktop la chiama in un ciclo, e il browser la chiama una volta per ogni aggiornamento dello schermo.
  3. Una dimensione fissa della finestra. Nel browser, l’emulatore disegna su un canvas di 1380×900, e la pagina scala il canvas per adattarlo. Un canvas ridimensionabile rompeva la corrispondenza tra le coordinate del mouse e quelle del canvas.

L’involucro 3D proviene da Espressif. ESP-College ha pubblicato il case dell’ESP-VoCat (EchoEar) come file STL su OSHWHub con licenza GPL-3.0. Un breve script Python converte i file STL in un unico file di modello 3D e ne riduce il dettaglio affinché il browser lo carichi rapidamente. L’emulatore disegna lo schermo live su un disco nella finestra rotonda del case. Quando clicchi sullo schermo 3D, l’emulatore trova il punto cliccato su quel disco e lo trasforma in un tocco sul pixel corrispondente.

Costruisci il tuo

Per costruire VoCat Eyes ti servono una scheda ESP-VoCat v1.2 e, al momento della scrittura, la versione 3.3 del core Arduino per ESP32. Il README di VoCat Eyes elenca le librerie e i comandi esatti. In breve:

  1. Copia secrets.example.h in secrets.h e aggiungi la tua chiave API di Gemini. Puoi anche inserire lì la rete Wi-Fi, oppure impostarla successivamente sul robot.
  2. Compila lo sketch con lo schema di partizionamento esp_sr_16 e il file partitions.csv nella cartella dello sketch. Questa disposizione assegna all’applicazione 6 MB di flash.
  3. Carica il firmware insieme a srmodels.bin, il file del modello per la wake word “Hi ESP”.
  4. Sul robot, tieni premuto lo schermo, tocca l’orologio, scegli Wi-Fi, seleziona la tua rete e digita la password.
  5. Di’ “Hi ESP”, oppure tocca il piccolo microfono in fondo allo schermo, e inizia a parlare.

Se stai progettando firmware per l’ESP32-S3 o altre schede, Velxio può simularlo nel browser prima che tu flashi qualsiasi hardware.


Condividi su:
David Montero

Scritto da

David Montero

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

GitHub velxio.dev

Articoli correlati


Articolo precedente
Sette progetti di Halloween da costruire ed eseguire nel browser
Articolo successivo
Una dashboard per l'uso di Claude sull'M5Stack Basic — limiti del piano e spesa API sulla scrivania