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 :
- 56 expressions faciales et 47 courtes animations
- Une petite machine à états qui décide de ce que fait le robot — jouer, réagir, s’ennuyer, s’endormir
- Des yeux qui se tournent vers la personne qui parle
- Des conversations parlées via l’API Gemini Live, déclenchées par le mot d’éveil « Hi ESP » ou par un tapotement
- Les réglages Wi-Fi, volume, luminosité et voix sur l’écran du robot lui-même
- Un émulateur qui exécute le même code sur un PC et dans cette page
Le firmware, l’émulateur et les outils sont open source dans le dépôt cute-desktop-robot.

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 :
- Tapotez sur l’écran du robot. Cliquez une fois sur l’écran rond du robot en 3D. Le robot joue son animation suivante.
- 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.
- 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.
- 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.
- 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
Mpour 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 :

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 :
Deux composants ont demandé un travail supplémentaire :
- L’écran allumait son rétroéclairage mais restait noir. La séquence d’initialisation que j’avais adaptée du pilote du fabricant manquait de deux commandes finales — le format de pixel (
0x3A) et l’activation de l’affichage (0x29). - Les microphones ne renvoyaient que des zéros au début. L’ADC des microphones (ES7210) et le DAC du haut-parleur (ES8311) partagent les mêmes lignes d’horloge I2S. Le bus I2S doit donc utiliser le même format de trame dans les deux directions — TDM (multiplexage par répartition dans le temps) avec quatre créneaux de 16 bits. Avec ce changement, les deux microphones apparaissent dans les créneaux 0 et 2, et le créneau 1 transporte une copie du signal du haut-parleur pour la référence d’écho.
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 :
Chaque bloc du schéma est un module du firmware :
- Sensors rassemble les touchers, le niveau de batterie, l’heure et les événements sonores dans une seule structure.
- Brain est une machine à états avec les états awake, show, reacting, dozing, sleeping, info et manual. Chaque état est quitté sur des événements spécifiques, comme un toucher, un bruit fort, l’heure du jour ou une longue période de solitude. Le Brain suit aussi des humeurs lentes — un robot souvent caressé dérive vers des visages heureux, et un robot trop souvent piqué devient agacé.
- FaceEngine se déplace entre les expressions le long d’un graphe. Les 56 expressions en sont les nœuds, et un changement de visage suit le chemin le plus court, de sorte que le robot passe d’heureux à triste par des visages intermédiaires. Chaque œil est décrit par quelques paramètres, comme la taille, le rayon des coins, la pente, les paupières et la couleur, et le moteur les interpole.
- Canvas dessine l’image en PSRAM et enregistre quelles tuiles de 24×24 ont changé. Un clignement modifie quelques tuiles, donc le bus d’affichage envoie quelques kilo-octets au lieu des 253 Ko complets.
- VocatLcd envoie les tuiles modifiées à l’écran ST77916 via QSPI (un bus SPI à quatre lignes).
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 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 :
- 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. - 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. - 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 :
- Le volume et la luminosité de l’écran
- La voix de Gemini, parmi huit voix
- Le mot-clé de réveil « Hi ESP », activé ou désactivé
- Les sons qui accompagnent les expressions
- Si le robot joue et dort tout seul
- Combien de secondes de silence terminent une conversation
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 :
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 :
- raylib pour le web. raylib se compile avec Emscripten en une bibliothèque statique qui dessine via WebGL.
- 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.
- 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 :
- Copiez
secrets.example.hverssecrets.het ajoutez votre clé API Gemini. Vous pouvez aussi y saisir le réseau Wi-Fi, ou le définir plus tard sur le robot. - Compilez le sketch avec le schéma de partition
esp_sr_16et le fichierpartitions.csvdans le dossier du sketch. Cette disposition donne 6 Mo de flash à l’application. - Téléversez le firmware avec
srmodels.bin, le fichier de modèle pour le mot-clé de réveil « Hi ESP ». - 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.
- 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.