Voltar

Robô de mesa fofo: um rosto que escuta no ESP-VoCat, com seu emulador no navegador

A maioria dos gadgets de mesa mostra informações. Eu queria um que reagisse às pessoas: um robô que olha para você quando você fala, responde e faz algo divertido quando ninguém está por perto. O Espressif ESP-VoCat v1.2 tem o hardware para isso. Ele combina um SoC ESP32-S3, uma tela redonda sensível ao toque de 360×360, dois microfones, um alto-falante e uma bateria em uma carcaça com orelhas de gato.

VoCat Eyes é o rosto que escrevi para ele, e o primeiro robô de uma série chamada Cute Desktop Robot. Suas principais características são:

O firmware, o emulador e as ferramentas são de código aberto no repositório cute-desktop-robot.

O robô VoCat Eyes na visão 3D do emulador: a carcaça ESP-VoCat com orelhas de gato em azul, com o rosto do robô em sua tela redonda

A imagem acima é a visão 3D do emulador: o modelo da carcaça que a Espressif publicou, com a tela ao vivo do robô emulado dentro dele.

E este é o robô real em uma mesa, executando o mesmo firmware:

O robô no hardware real: assista no YouTube.

Experimente o robô no seu navegador

O emulador abaixo executa o código C++ do robô compilado para WebAssembly: o mesmo motor de rosto, máquina de estados e telas que rodam no ESP32-S3. Apenas a camada de hardware é substituída por peças simuladas. Não precisa de conta nem de instalação. Somente a conversa por voz precisa do robô real, porque o emulador não tem rede.

Se o quadro estiver muito pequeno, abra o emulador em uma janela inteira.

A janela tem três partes. À esquerda está o robô em 3D, abaixo dele está a máquina de estados, e à direita está um painel com o gráfico de emoções e os sensores. Para experimentar:

  1. Toque na tela do robô. Clique uma vez na tela redonda do robô em 3D. O robô reproduz sua próxima animação.
  2. Abra as configurações. Clique e segure na tela até que o relógio e a bateria apareçam, depois clique uma vez no relógio. Um menu de configurações se abre. Clique na metade esquerda de uma linha para diminuir um valor e na metade direita para aumentá-lo. Clique e segure para fechar o menu.
  3. Fale de um lado. No painel à direita, em SENSORS, arraste o controle deslizante ao lado de from para um ângulo, depois clique em talk [V]. Um ponto roxo no chão mostra de onde vem a voz simulada, e os olhos do robô se voltam para ele.
  4. Observe a máquina de estados. Cada vez que o robô muda sua atividade, a caixa correspondente se acende na máquina de estados, e as barras na parte inferior mostram os temporizadores que podem mudá-la em seguida.
  5. Gire o robô. Arraste em qualquer lugar fora da tela para girar a visão 3D, e use a roda do mouse para dar zoom. Pressione M para alternar para uma visão plana da tela.

O teclado também funciona depois que você clica no emulador: 1 a 9 mostram expressões, Y acena com a cabeça, U balança a cabeça e H mostra um balão de fala. O toque e as configurações funcionam da mesma forma no robô real.

A próxima captura de tela mostra a janela inteira do emulador como ela aparece em um PC de mesa:

O emulador completo de desktop: o robô em 3D, o gráfico de emoções, os sensores com seus controles deslizantes e a máquina de estados com seus temporizadores

O hardware

O ESP-VoCat vem montado, com a carcaça, a bateria e a tela no lugar. O guia do usuário do ESP-VoCat descreve a placa em detalhes. Um módulo ESP32-S3 faz todo o trabalho. Ele tem 16 MB de memória flash e 16 MB de PSRAM (RAM externa). A PSRAM torna possível um rosto de 360×360: um quadro completo em cores de 16 bits ocupa 253 KB, mais do que a RAM interna do SoC consegue armazenar.

O diagrama a seguir mostra as peças que o firmware usa e o barramento que conecta cada uma ao ESP32-S3:

Diagrama de blocos: o ESP32-S3 controla o LCD redondo ST77916 via QSPI, lê o toque CST816S e o medidor de bateria BQ27220 via I2C, recebe os microfones através do ES7210 e reproduz através do ES8311 e seu amplificador via I2S TDM, e conversa com o Gemini Live via Wi-Fi

Duas peças precisaram de trabalho extra:

Como o robô desenha seu rosto

O robô redesenha seu rosto a cada 33 ms, cerca de 30 vezes por segundo. O diagrama abaixo acompanha um desses quadros desde os sensores até a tela:

O pipeline de quadros: os Sensores produzem um SensorState para o Brain, o Brain escolhe uma expressão para o FaceEngine, o FaceEngine desenha formas no Canvas, o Canvas encontra os blocos de 24 pixels alterados e o VocatLcd envia apenas esses via QSPI. A tarefa Listener alimenta voz, batidas e a palavra de ativação nos Sensores, e a conversa alimenta set_face e play_act no Brain

Cada bloco do diagrama é um módulo do firmware:

Todas as expressões e animações

O emulador inclui uma ferramenta de linha de comando, facetool, que renderiza a tela do robô sem abrir uma janela. Os vídeos abaixo vêm dela. Cada um reproduz todos os rostos de um grupo ao mesmo tempo: primeiro a transição para cada rosto, depois o próprio rosto.

Expressões

Calmo e pensativo (16): normal, curioso, concentrado, cético, entediado, confuso e ouvindo, entre outros.

Feliz (15): de um sorriso discreto a gargalhada, amor e encantado.

Triste (8): preocupado, suplicante, chorando, solitário e culpado, entre outros.

Irritado (6): de incomodado a furioso, e nojo.

Surpreso e alerta (7): surpreso, assustado, tonto, nocauteado e hipnotizado, entre outros.

Sonolento (4): sonolento, bocejando, dormindo e cansado.

Animações

As animações, chamadas de atos no código, são cenas curtas, muitas com um objeto desenhado ao lado dos olhos: uma nuvem de chuva, uma xícara de café, uma tela de radar. O Brain as reproduz quando o robô está ocioso, e um toque na tela reproduz a próxima.

Linguagem corporal (9): acenar com a cabeça, balançar a cabeça, revirar os olhos, espirrar.

Efeitos de tela (5): um glitch, um reboot, uma linha de scanner, código caindo.

Com objetos (18): corações, chuva, fogos de artifício, bolhas, uma bola, dançando.

Coisas do dia a dia (9): um livro, fones de ouvido, café, um foguete, neve.

Jogos e efeitos de tela redonda (6): Pac-Man, Pong, um arco-íris, um radar, um aquário.

As mesmas grades, como GIFs animados, estão no README do repositório.

Como o robô conversa

A conversa usa a Gemini Live API, que recebe um fluxo de áudio e responde com um fluxo de áudio por uma única conexão WebSocket. No momento em que este texto foi escrito, o robô usa o modelo gemini-2.5-flash-native-audio. O diagrama abaixo mostra o caminho do áudio:

O pipeline de voz: os microfones ES7210 alimentam a tarefa Listener a 16 kHz, a tarefa GeminiLive envia blocos de 100 ms para a Gemini Live API e recebe áudio a 24 kHz, que é reamostrado para o Player e o alto-falante. As chamadas de ferramentas vão para o Talk, que muda o rosto. O Player silencia o Listener enquanto reproduz

O robô envia o som de um microfone como áudio PCM de 16 kHz, em blocos de 100 ms. O Gemini responde com fala de 24 kHz, que o robô converte para 16 kHz e reproduz.

A conversa é half duplex: o robô ou escuta ou fala, nunca as duas coisas ao mesmo tempo. O alto-falante fica a poucos centímetros dos microfones, então o robô silencia o fluxo do microfone enquanto reproduz uma resposta. Caso contrário, o Gemini ouviria a própria voz. O Gemini decide do seu lado quando a pessoa terminou de falar.

O Gemini também recebe quatro ferramentas, funções que ele pode chamar durante a conversa: set_face, play_act, look e show_text. Quando a conversa fica triste, o Gemini pode chamar set_face("sad"). Quando você pede que o robô concorde, ele pode chamar play_act("yes") e o robô acena com a cabeça. Cada sessão começa com uma mensagem de texto curta que contém a hora e a data locais, para que o Gemini possa responder perguntas sobre elas.

Três problemas consumiram a maior parte do tempo de desenvolvimento:

  1. A conexão segura. O robô se conecta ao Gemini via TLS. A função setInsecure() da biblioteca ArduinoWebsockets não tem efeito no ESP32-S3, então o handshake TLS falhava. O firmware agora inclui os dois certificados raiz do Google (GTS Root R1 e R4, válidos até 2036). Ele também espera o relógio sincronizar via NTP, porque verificar um certificado exige a data atual.
  2. RAM interna. A palavra de ativação “Hi ESP” roda no ESP-SR, a biblioteca de reconhecimento de fala da Espressif, que mantém cerca de 100 KB de RAM interna. A biblioteca TLS precisa de RAM interna para o handshake, e as primeiras tentativas falharam com SSL - Memory allocation failed. O robô agora para o ESP-SR quando uma conversa começa, o que libera 107 KB, e o carrega novamente quando a conversa termina. Carregar o ESP-SR leva cerca de 1,2 segundos.
  3. O fluxo de microfone ausente. Por um dia, o robô cumprimentava o usuário e depois ignorava tudo o que vinha a seguir. Seus olhos ainda seguiam minha voz, então os microfones claramente funcionavam. A causa era que o firmware nunca habilitava o fluxo de áudio para o Gemini. O console serial agora imprime a transcrição do Gemini do que ele ouviu, então o mesmo problema ficaria visível imediatamente.

Usar o Gemini exige uma chave de API do Google AI Studio, e a API é cobrada por uso conforme a precificação da Gemini API do Google. A chave fica em um arquivo local secrets.h que nunca é enviado ao repositório.

Configurações na tela do robô

O robô não precisa de um computador depois de configurado. Um toque longo na tela mostra o relógio, o nível da bateria e o estado do Wi-Fi. Um toque no relógio abre as configurações, onde você pode alterar:

A linha do Wi-Fi abre uma lista das redes ao alcance e um teclado na tela com letras maiúsculas, números e símbolos. O ESP32-S3 não consegue escanear redes enquanto está se conectando a uma, então o firmware pausa as tentativas de conexão durante o escaneamento e as retoma depois. Se a senha estiver errada, a tela do relógio mostra isso como o motivo. O ESP32-S3 só suporta redes de 2,4 GHz.

Uma base de código para o robô, o PC e o navegador

O código do robô não chama os drivers de display, toque ou áudio diretamente. O rosto, o Brain e as telas passam por uma camada fina de hardware em src/hw e src/audio. O emulador substitui essa camada, então o restante do código compila sem alterações para três alvos:

O C++ compartilhado em src/ compila de três formas: arduino-cli gera o firmware do ESP32-S3, g++ com cabeçalhos Arduino simulados gera o emulador desktop raylib, e em++ gera o mesmo emulador para o navegador

No emulador, pequenas versões substitutas de Arduino.h, Wire.h e SPI.h escrevem em uma estrutura que representa o hardware. Ela contém a memória do display, os registradores do controlador de toque e as leituras da bateria. A janela do emulador, escrita com a biblioteca gráfica raylib, desenha essa estrutura e permite alterar os valores dos sensores.

A versão para navegador é o mesmo programa emulador compilado com Emscripten, que transforma C++ em WebAssembly. Três mudanças tornaram isso possível:

  1. raylib para a web. O raylib compila com Emscripten em uma biblioteca estática que desenha via WebGL.
  2. Um quadro por chamada. A versão desktop rodava em um loop infinito. Esse loop virou uma função que desenha um quadro. A versão desktop a chama em loop, e o navegador a chama uma vez por atualização de tela.
  3. Um tamanho de janela fixo. No navegador, o emulador desenha em um canvas de 1380×900, e a página escala o canvas para caber. Um canvas redimensionável quebrava o mapeamento entre as coordenadas do mouse e do canvas.

A carcaça 3D vem da Espressif. A ESP-College publicou o case do ESP-VoCat (EchoEar) como arquivos STL no OSHWHub sob a licença GPL-3.0. Um pequeno script Python converte os arquivos STL em um único arquivo de modelo 3D e reduz seu detalhamento para que o navegador o carregue rapidamente. O emulador desenha a tela ao vivo em um disco na janela redonda do case. Quando você clica na tela 3D, o emulador encontra o ponto clicado nesse disco e o converte em um toque no pixel correspondente.

Construa o seu

Para construir o VoCat Eyes você precisa de uma placa ESP-VoCat v1.2 e, no momento em que este texto foi escrito, da versão 3.3 do core Arduino para ESP32. O README do VoCat Eyes lista as bibliotecas e os comandos exatos. Em resumo:

  1. Copie secrets.example.h para secrets.h e adicione sua chave de API do Gemini. Você também pode informar a rede Wi-Fi ali, ou configurá-la depois no robô.
  2. Compile o sketch com o esquema de partições esp_sr_16 e o arquivo partitions.csv na pasta do sketch. Esse layout dá ao aplicativo 6 MB de flash.
  3. Envie o firmware junto com srmodels.bin, o arquivo de modelo para a palavra de ativação “Hi ESP”.
  4. No robô, pressione e segure a tela, toque no relógio, escolha Wi-Fi, selecione sua rede e digite a senha.
  5. Diga “Hi ESP”, ou toque no pequeno microfone na parte inferior da tela, e comece a falar.

Se você está projetando firmware para o ESP32-S3 ou outras placas, o Velxio pode simulá-lo no navegador antes de você gravar qualquer hardware.


Compartilhar em:
David Montero

Escrito por

David Montero

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

GitHub velxio.dev

Artigos relacionados


Artigo anterior
Sete projetos de Halloween que você pode montar e rodar no navegador
Próximo artigo
Um dashboard de uso do Claude no M5Stack Basic — limites do plano e gastos de API na sua mesa