Retour

Cute Desktop Robot — un visage à l'écoute sur l'ESP-VoCat, avec son émulateur dans votre navigateur

La plupart des gadgets de bureau affichent des informations. Je voulais un objet qui réagisse aux gens — un robot qui vous regarde quand vous parlez, vous répond, et fait quelque chose d’amusant quand personne n’est là. L’Espressif ESP-VoCat v1.2 dispose du matériel pour cela. Il combine un SoC ESP32-S3, un écran tactile rond de 360×360, deux microphones, un haut-parleur et une batterie dans un boîtier avec des oreilles de chat.

VoCat Eyes est le visage que j’ai écrit pour lui, et le premier robot d’une série appelée Cute Desktop Robot. Ses principales caractéristiques sont :

Le firmware, l’émulateur et les outils sont open source dans le dépôt cute-desktop-robot.

Le robot VoCat Eyes dans la vue 3D de l'émulateur — le boîtier ESP-VoCat aux oreilles de chat en bleu, avec le visage du robot sur son écran rond

L’image ci-dessus est la vue 3D de l’émulateur — le modèle du boîtier publié par Espressif, avec l’écran en direct du robot émulé à l’intérieur.

Et voici le vrai robot sur un bureau, exécutant le même firmware :

Le robot sur le matériel réel — regardez-le sur YouTube.

Essayez le robot dans votre navigateur

L’émulateur ci-dessous exécute le code C++ du robot compilé en WebAssembly — le même moteur de visage, la même machine à états et les mêmes écrans que ceux qui tournent sur l’ESP32-S3. Seule la couche matérielle est remplacée par des composants simulés. Il ne nécessite ni compte ni installation. Seule la conversation vocale nécessite le vrai robot, car l’émulateur n’a pas de réseau.

Si le cadre est trop petit, ouvrez l’émulateur dans une fenêtre plein écran.

La fenêtre comporte trois parties. À gauche se trouve le robot en 3D, en dessous la machine à états, et à droite un panneau avec le graphe des émotions et les capteurs. Pour l’essayer :

  1. Tapotez sur l’écran du robot. Cliquez une fois sur l’écran rond du robot en 3D. Le robot joue son animation suivante.
  2. Ouvrez les réglages. Cliquez et maintenez sur l’écran jusqu’à ce que l’horloge et la batterie apparaissent, puis cliquez une fois sur l’horloge. Un menu de réglages s’ouvre. Cliquez sur la moitié gauche d’une ligne pour diminuer une valeur et sur la moitié droite pour l’augmenter. Cliquez et maintenez pour fermer le menu.
  3. Parlez depuis un côté. Dans le panneau de droite, sous SENSORS, faites glisser le curseur à côté de from vers un angle, puis cliquez sur talk [V]. Un point violet sur le sol indique d’où vient la voix simulée, et les yeux du robot se tournent vers elle.
  4. Observez la machine à états. Chaque fois que le robot change d’activité, la case correspondante s’allume dans la machine à états, et les barres en bas montrent les minuteries qui peuvent la changer ensuite.
  5. Tournez le robot. Faites glisser n’importe où en dehors de l’écran pour faire pivoter la vue 3D, et utilisez la molette de la souris pour zoomer. Appuyez sur M pour passer à une vue à plat de l’écran.

Le clavier fonctionne aussi une fois que vous avez cliqué sur l’émulateur — 1 à 9 affichent des expressions, Y fait hocher la tête, U fait secouer la tête, et H affiche une bulle de dialogue. Le tapotement et les réglages fonctionnent de la même manière sur le vrai robot.

La capture d’écran suivante montre la fenêtre complète de l’émulateur telle qu’elle apparaît sur un PC de bureau :

L'émulateur de bureau complet — le robot en 3D, le graphe des émotions, les capteurs avec leurs curseurs, et la machine à états avec ses minuteries

Le matériel

L’ESP-VoCat est livré assemblé, avec le boîtier, la batterie et l’écran en place. Le guide utilisateur de l’ESP-VoCat décrit la carte en détail. Un seul module ESP32-S3 fait tout le travail. Il dispose de 16 Mo de mémoire flash et de 16 Mo de PSRAM (RAM externe). La PSRAM rend possible un visage de 360×360 — une image complète en couleur 16 bits occupe 253 Ko, plus que la RAM interne du SoC ne peut en contenir.

Le schéma suivant montre les composants utilisés par le firmware et le bus qui relie chacun d’eux à l’ESP32-S3 :

Schéma fonctionnel — l'ESP32-S3 pilote l'écran LCD rond ST77916 via QSPI, lit le tactile CST816S et la jauge de batterie BQ27220 via I2C, reçoit les microphones via l'ES7210 et joue via l'ES8311 et son amplificateur en I2S TDM, et communique avec Gemini Live via Wi-Fi

Deux composants ont demandé un travail supplémentaire :

Comment le robot dessine son visage

Le robot redessine son visage toutes les 33 ms, soit environ 30 fois par seconde. Le schéma ci-dessous suit l’une de ces images, des capteurs à l’écran :

Le pipeline d'une image — les Sensors produisent un SensorState pour le Brain, le Brain choisit une expression pour le FaceEngine, le FaceEngine dessine des formes dans le Canvas, le Canvas identifie les tuiles modifiées de 24 pixels et VocatLcd n'envoie que celles-ci via QSPI. La tâche Listener alimente les Sensors en voix, en bruits et en mot d'éveil, et la conversation alimente le Brain en set_face et play_act

Chaque bloc du schéma est un module du firmware :

Toutes les expressions et animations

L’émulateur inclut un outil en ligne de commande, facetool, qui rend l’écran du robot sans ouvrir de fenêtre. Les vidéos ci-dessous en sont issues. Chacune joue toutes les faces d’un groupe en même temps — d’abord le passage vers chaque face, puis la face elle-même.

Expressions

Calme et réflexion (16) — normal, curieux, concentré, sceptique, ennuyé, confus et à l’écoute, entre autres.

Joyeux (15) — du petit sourire au rire, à l’amour et aux yeux étoilés.

Triste (8) — inquiet, suppliant, en pleurs, solitaire et coupable, entre autres.

En colère (6) — de l’agacement à la fureur, et le dégoût.

Surpris et en alerte (7) — surpris, effrayé, étourdi, assommé et hypnotisé, entre autres.

Somnolent (4) — somnolent, bâillant, endormi et fatigué.

Animations

Les animations, appelées acts dans le code, sont de courtes scènes, dont beaucoup comportent un objet dessiné à côté des yeux — un nuage de pluie, une tasse de café, un écran radar. Le Brain les joue lorsque le robot est inactif, et un appui sur l’écran lance la suivante.

Langage corporel (9) — hocher la tête, secouer la tête, lever les yeux au ciel, éternuer.

Effets d’écran (5) — un glitch, un redémarrage, une ligne de scanner, du code qui tombe.

Avec des objets (18) — des cœurs, de la pluie, des feux d’artifice, des bulles, une balle, de la danse.

Le quotidien (9) — un livre, un casque, du café, une fusée, de la neige.

Jeux et effets sur écran rond (6) — Pac-Man, Pong, un arc-en-ciel, un radar, un aquarium.

Les mêmes grilles, sous forme de GIF animés, se trouvent dans le README du dépôt.

Comment le robot parle

La conversation utilise l’API Gemini Live, qui reçoit un flux audio et répond par un flux audio sur une seule connexion WebSocket. Au moment de la rédaction, le robot utilise le modèle gemini-2.5-flash-native-audio. Le schéma ci-dessous montre le trajet de l’audio :

Le pipeline vocal — les microphones ES7210 alimentent la tâche Listener à 16 kHz, la tâche GeminiLive envoie des blocs de 100 ms à l'API Gemini Live et reçoit de l'audio à 24 kHz, rééchantillonné pour le Player et le haut-parleur. Les appels d'outils vont à Talk, qui change le visage. Le Player coupe le Listener pendant la lecture

Le robot envoie le son d’un microphone sous forme d’audio PCM à 16 kHz, par blocs de 100 ms. Gemini répond avec de la parole à 24 kHz, que le robot convertit en 16 kHz et lit.

La conversation est half duplex — le robot écoute ou parle, jamais les deux à la fois. Le haut-parleur se trouve à quelques centimètres des microphones, donc le robot coupe le flux du microphone pendant qu’il joue une réponse. Sinon, Gemini entendrait sa propre voix. Gemini décide de son côté quand la personne a fini de parler.

Gemini reçoit aussi quatre outils, des fonctions qu’il peut appeler pendant la conversation — set_face, play_act, look et show_text. Quand la conversation devient triste, Gemini peut appeler set_face("sad"). Quand vous demandez au robot d’acquiescer, il peut appeler play_act("yes") et le robot hoche la tête. Chaque session commence par un court message texte contenant l’heure et la date locales, pour que Gemini puisse répondre aux questions à leur sujet.

Trois problèmes ont pris la majeure partie du temps de développement :

  1. La connexion sécurisée. Le robot se connecte à Gemini via TLS. La fonction setInsecure() de la bibliothèque ArduinoWebsockets n’a aucun effet sur l’ESP32-S3, donc la poignée de main TLS échouait. Le firmware inclut désormais les deux certificats racine de Google (GTS Root R1 et R4, valides jusqu’en 2036). Il attend aussi que l’horloge se synchronise via NTP, car la vérification d’un certificat nécessite la date courante.
  2. La RAM interne. Le mot-clé de réveil « Hi ESP » tourne sur ESP-SR, la bibliothèque de reconnaissance vocale d’Espressif, qui occupe environ 100 Ko de RAM interne. La bibliothèque TLS a besoin de RAM interne pour la poignée de main, et les premières tentatives échouaient avec SSL - Memory allocation failed. Le robot arrête désormais ESP-SR au début d’une conversation, ce qui libère 107 Ko, et le recharge à la fin de la conversation. Le chargement d’ESP-SR prend environ 1,2 seconde.
  3. Le flux de microphone manquant. Pendant une journée, le robot saluait l’utilisateur puis ignorait tout ce qui suivait. Ses yeux suivaient toujours ma voix, donc les microphones fonctionnaient clairement. La cause était que le firmware n’activait jamais le flux audio vers Gemini. La console série affiche désormais la transcription par Gemini de ce qu’il a entendu, pour que le même problème soit visible immédiatement.

L’utilisation de Gemini nécessite une clé API depuis Google AI Studio, et l’API est facturée à l’usage selon la tarification de l’API Gemini de Google. La clé reste dans un fichier local secrets.h qui n’est jamais commité dans le dépôt.

Réglages sur l’écran du robot

Le robot n’a besoin d’aucun ordinateur une fois configuré. Un appui long sur l’écran affiche l’horloge, le niveau de batterie et l’état du Wi-Fi. Un appui sur l’horloge ouvre les réglages, où vous pouvez modifier :

La ligne Wi-Fi ouvre une liste des réseaux à portée et un clavier à l’écran avec majuscules, chiffres et symboles. L’ESP32-S3 ne peut pas scanner les réseaux pendant qu’il se connecte à l’un d’eux, donc le firmware suspend les tentatives de connexion pendant le scan et les reprend ensuite. Si le mot de passe est incorrect, l’écran de l’horloge l’indique comme raison. L’ESP32-S3 ne prend en charge que les réseaux 2,4 GHz.

Une seule base de code pour le robot, le PC et le navigateur

Le code du robot n’appelle pas directement les pilotes d’écran, de tactile ou d’audio. Le visage, le Brain et les écrans passent par une fine couche matérielle dans src/hw et src/audio. L’émulateur remplace cette couche, donc le reste du code se compile sans modification pour trois cibles :

Le C++ partagé dans src/ se compile de trois façons — arduino-cli produit le firmware ESP32-S3, g++ avec des en-têtes Arduino simulés produit l'émulateur de bureau raylib, et em++ produit le même émulateur pour le navigateur

Dans l’émulateur, de petites versions de remplacement de Arduino.h, Wire.h et SPI.h écrivent dans une structure qui tient lieu de matériel. Elle contient la mémoire de l’écran, les registres du contrôleur tactile et les relevés de batterie. La fenêtre de l’émulateur, écrite avec la bibliothèque graphique raylib, dessine cette structure et vous permet de modifier les valeurs des capteurs.

La version navigateur est le même programme émulateur compilé avec Emscripten, qui transforme le C++ en WebAssembly. Trois changements ont rendu cela possible :

  1. raylib pour le web. raylib se compile avec Emscripten en une bibliothèque statique qui dessine via WebGL.
  2. Une image par appel. La version de bureau tournait dans une boucle infinie. Cette boucle est devenue une fonction qui dessine une image. La version de bureau l’appelle en boucle, et le navigateur l’appelle une fois par rafraîchissement d’écran.
  3. Une taille de fenêtre fixe. Dans le navigateur, l’émulateur dessine dans un canvas de 1380×900, et la page met le canvas à l’échelle pour l’adapter. Un canvas redimensionnable cassait la correspondance entre les coordonnées de la souris et celles du canvas.

Le boîtier 3D vient d’Espressif. ESP-College a publié le boîtier de l’ESP-VoCat (EchoEar) sous forme de fichiers STL sur OSHWHub sous licence GPL-3.0. Un court script Python convertit les fichiers STL en un seul fichier de modèle 3D et réduit son niveau de détail pour que le navigateur le charge rapidement. L’émulateur dessine l’écran en direct sur un disque dans la fenêtre ronde du boîtier. Quand vous cliquez sur l’écran 3D, l’émulateur trouve le point cliqué sur ce disque et le transforme en un toucher au pixel correspondant.

Construisez le vôtre

Pour construire VoCat Eyes, il vous faut une carte ESP-VoCat v1.2 et, au moment de la rédaction, la version 3.3 du core Arduino pour ESP32. Le README de VoCat Eyes liste les bibliothèques et les commandes exactes. En bref :

  1. Copiez secrets.example.h vers secrets.h et ajoutez votre clé API Gemini. Vous pouvez aussi y saisir le réseau Wi-Fi, ou le définir plus tard sur le robot.
  2. Compilez le sketch avec le schéma de partition esp_sr_16 et le fichier partitions.csv dans le dossier du sketch. Cette disposition donne 6 Mo de flash à l’application.
  3. Téléversez le firmware avec srmodels.bin, le fichier de modèle pour le mot-clé de réveil « Hi ESP ».
  4. Sur le robot, maintenez l’appui sur l’écran, appuyez sur l’horloge, choisissez Wi-Fi, sélectionnez votre réseau et saisissez son mot de passe.
  5. Dites « Hi ESP », ou appuyez sur le petit microphone en bas de l’écran, et commencez à parler.

Si vous concevez un firmware pour l’ESP32-S3 ou d’autres cartes, Velxio peut le simuler dans le navigateur avant que vous ne flashiez du matériel.


Partager sur :
David Montero

Écrit par

David Montero

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

GitHub velxio.dev

Articles liés


Article précédent
Sept projets d'Halloween à construire et à exécuter dans le navigateur
Article suivant
Un tableau de bord d'utilisation de Claude sur le M5Stack Basic — limites de forfait et dépenses API sur votre bureau