Um assistente de voz feito por você
Você fala, ele entende, pergunta a um modelo de IA e responde em voz alta, como a Alexa. Funciona com o ChatGPT, o Claude, o Gemini ou um modelo que roda de graça no seu computador. E você decide quando ele ouve e o que fica guardado.
Como funciona
Do microfone à resposta em 5 etapas
O assistente fica quieto até ouvir a palavra de ativação (ou até você apertar o botão). Só então ele grava alguns segundos, transforma a voz em texto, manda o texto para a IA e transforma a resposta em voz. Se o pedido for um comando da casa ("liga a luz"), ele vai direto para o controle da casa, sem gastar IA.
| Versão 1 · ESP32-S3 + servidor | Versão 2 · Raspberry Pi tudo em um | |
|---|---|---|
| Onde fica o "cérebro" | No seu PC ou num Raspberry Pi da casa. O ESP32 só ouve e fala. | Tudo no Raspberry Pi |
| Ativação | Botão (aperte para falar). Palavra de ativação é desafio avançado. | Palavra de ativação local (openWakeWord) |
| Custo das peças | R$ 120–180 (usa um PC que você já tem) | R$ 500–900 (Raspberry Pi 4 ou 5) |
| Bom para | Aprender eletrônica, áudio I2S e rede | Robô independente para levar à feira |
Versão 1
ESP32-S3 + servidor no PC
Peças
| Peça | Para que serve | Preço aprox. (out/2026, a conferir) |
|---|---|---|
| ESP32-S3 DevKit com PSRAM (N16R8) | Grava o áudio, envia por Wi-Fi e toca a resposta. A PSRAM guarda os segundos de áudio. | R$ 60–90 |
| Microfone I2S INMP441 | Ouve a voz já em formato digital, sem ruído de fio | R$ 15–25 |
| Amplificador I2S MAX98357A | Recebe o áudio digital e move o alto-falante | R$ 15–25 |
| Alto-falante 4–8 Ω, 3 W | A voz do robô | R$ 8–15 |
| LED + resistor 220 Ω | Aviso visível: aceso = gravando | R$ 2 |
| Micro servo SG90 (opcional) | Gira a "cabeça" quando ele é ativado | R$ 12–18 |
| Protoboard, jumpers, cabo USB | Ligações sem solda | R$ 20–30 |
| PC ou Raspberry Pi já existente | Roda o servidor: Whisper, IA e voz | o que você já tem |
Ligação
Passo a passo
- Instale o servidor no PC: Python 3.10+, depois
pip install flask faster-whisper openai python-dotenv requests, o Piper com a vozpt_BR-faber-mediume o ffmpeg. - Escolha a IA no arquivo
.env(veja abaixo). Para começar sem custo, use o Ollama no próprio PC. - Monte o circuito na protoboard, conferindo pino por pino com a figura.
- No Arduino IDE, instale o núcleo ESP32 versão 3 ou mais nova, escolha "ESP32S3 Dev Module" e ative PSRAM: OPI PSRAM.
- Crie o
segredos.hcom o nome e a senha do Wi-Fi e o endereço do servidor. Envie o código para a placa. - Teste: segure o botão BOOT, fale "que horas são no Japão?" e solte. O LED acende enquanto grava.
Código do ESP32-S3 (Arduino)
// Assistente de voz - ESP32-S3 (núcleo ESP32 3.x no Arduino IDE)
// Segure o botão BOOT e fale (até 4 s). A resposta sai no alto-falante.
#include <WiFi.h>
#include <HTTPClient.h>
#include <ESP_I2S.h>
#include "segredos.h" // WIFI_NOME, WIFI_SENHA, SERVIDOR
// Só dados da rede. Chave de IA NUNCA entra aqui.
const int BOTAO = 0; // botão BOOT da própria placa
const int LED = 2; // aceso = gravando
I2SClass mic, falante;
void setup() {
pinMode(BOTAO, INPUT_PULLUP);
pinMode(LED, OUTPUT);
WiFi.begin(WIFI_NOME, WIFI_SENHA);
while (WiFi.status() != WL_CONNECTED) delay(300);
mic.setPins(5, 4, -1, 6); // SCK, WS, (sem saída), SD
mic.begin(I2S_MODE_STD, 16000, I2S_DATA_BIT_WIDTH_32BIT,
I2S_SLOT_MODE_MONO, I2S_STD_SLOT_LEFT);
falante.setPins(16, 17, 18); // BCLK, LRC, DIN
falante.begin(I2S_MODE_STD, 16000, I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_MONO);
}
void loop() {
if (digitalRead(BOTAO) == HIGH) return; // só grava com o botão
digitalWrite(LED, HIGH);
size_t tam = 0;
uint8_t *wav = mic.recordWAV(4, &tam); // 4 s de áudio
digitalWrite(LED, LOW);
if (!wav) return;
HTTPClient http;
http.begin(SERVIDOR); // ex.: http://192.168.0.20:5000/falar
http.setTimeout(30000); // a IA pode levar alguns segundos
http.addHeader("Content-Type", "audio/wav");
int codigo = http.POST(wav, tam);
free(wav); // o áudio some da memória
if (codigo == 200) {
int n = http.getSize();
uint8_t *resp = (uint8_t *) ps_malloc(n);
if (resp) {
http.getStream().readBytes(resp, n);
falante.playWAV(resp, n); // toca a resposta
free(resp);
}
}
http.end();
}
Servidor no PC (Python)
# servidor.py - recebe o áudio, entende, pergunta à IA e devolve a voz.
import os, subprocess, tempfile, requests
from flask import Flask, request
from faster_whisper import WhisperModel
from openai import OpenAI # serve para qualquer IA "compatível com OpenAI"
from dotenv import load_dotenv
load_dotenv() # lê o .env, que NÃO vai para o GitHub
ia = OpenAI(base_url=os.environ["IA_URL"],
api_key=os.environ.get("CHAVE_IA", "local"))
MODELO = os.environ["IA_MODELO"]
CASA = os.environ.get("CASA_URL") # controle da casa (S7), se você tiver
ouvido = WhisperModel("small", device="cpu", compute_type="int8")
app = Flask(__name__)
def comando_da_casa(texto):
"""Comando fixo vai direto para a casa, sem gastar IA."""
t = texto.lower()
if CASA and "luz" in t:
estado = "0" if "desliga" in t else "1"
requests.get(f"{CASA}/rele?n=1&estado={estado}", timeout=3)
return "Pronto, luz " + ("desligada." if estado == "0" else "ligada.")
return None
def perguntar_ia(texto):
r = ia.chat.completions.create(model=MODELO, max_tokens=120, messages=[
{"role": "system", "content": "Você é o assistente de uma casa. "
"Responda em português, em no máximo duas frases curtas."},
{"role": "user", "content": texto}])
return r.choices[0].message.content
def falar(texto, saida):
bruto = saida + ".bruto.wav"
subprocess.run(["piper", "--model", "pt_BR-faber-medium.onnx",
"--output_file", bruto], input=texto.encode(), check=True)
subprocess.run(["ffmpeg", "-y", "-loglevel", "error", "-i", bruto,
"-ar", "16000", "-ac", "1", "-sample_fmt", "s16", saida],
check=True)
@app.post("/falar")
def receber():
with tempfile.TemporaryDirectory() as pasta: # apagada ao final
entrada = os.path.join(pasta, "pergunta.wav")
with open(entrada, "wb") as f:
f.write(request.data)
partes, _ = ouvido.transcribe(entrada, language="pt")
texto = " ".join(p.text for p in partes).strip()
if texto:
resposta = comando_da_casa(texto) or perguntar_ia(texto)
else:
resposta = "Não entendi. Pode repetir?"
saida = os.path.join(pasta, "resposta.wav")
falar(resposta, saida)
with open(saida, "rb") as f:
dados = f.read()
return dados, 200, {"Content-Type": "audio/wav"}
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
O comando /rele?n=1&estado=1 é um exemplo: use o endereço que o seu controle da casa (S7) aceitar. Assim "liga a luz da sala" funciona mesmo sem internet.
Qual IA usar
Troque de IA mudando duas linhas
O servidor fala com qualquer IA que aceite o formato da OpenAI. Basta mudar o endereço (IA_URL) e o nome do modelo (IA_MODELO) no arquivo .env. Nomes de modelo mudam com frequência: confira na documentação do provedor.
| IA | IA_URL | Custo |
|---|---|---|
| Ollama (modelo local) | http://localhost:11434/v1 | Grátis. Roda no seu PC, nada sai da casa. |
| ChatGPT (OpenAI) | https://api.openai.com/v1 | Pago por uso |
| Claude (Anthropic) | https://api.anthropic.com/v1/ | Pago por uso |
| Gemini (Google) | endereço "compatível com OpenAI" da documentação do Gemini | Tem faixa gratuita com limite |
# .env - fica SÓ no computador do servidor. Coloque ".env" no .gitignore.
IA_URL=http://localhost:11434/v1
IA_MODELO=qwen2.5:3b
# Para usar um serviço pago, troque as duas linhas acima e preencha:
# CHAVE_IA=cole-aqui-a-sua-chave
CASA_URL=http://192.168.0.30
.env do servidor. Nunca no código do ESP32 (dá para ler a memória da placa) e nunca no GitHub: robôs varrem os repositórios e usam chaves vazadas em minutos, e a conta vem para quem é dono da chave. Serviços pagos exigem conta de maior de idade: peça a um responsável, defina um limite de gasto no painel do provedor ou fique no Ollama, que é grátis.Quanto custa? Serviços on-line cobram por tamanho de texto (tokens). Uma pergunta curta com resposta de duas frases custa uma fração de centavo, mas mil perguntas por dia num estande somam. Respostas curtas (max_tokens=120) e comandos da casa resolvidos sem IA economizam.
Versão 2
Raspberry Pi tudo em um
Aqui não há ESP32: o Raspberry Pi ouve, detecta a palavra de ativação, transcreve, pergunta à IA e fala. Use um Raspberry Pi 4 ou 5 com 4 GB ou mais, um microfone USB e uma caixinha de som USB ou P2. No Pi, prefira o Whisper "base" (mais rápido que o "small") e uma IA on-line ou o Ollama rodando num PC da casa.
| Peça | Para que serve | Preço aprox. (out/2026, a conferir) |
|---|---|---|
| Raspberry Pi 4 ou 5 (4 GB+) | O cérebro inteiro | R$ 450–800 |
| Fonte oficial USB-C e cartão microSD 32 GB | Energia estável e sistema | R$ 60–100 |
| Microfone USB | Ouvir | R$ 25–60 |
| Caixinha de som USB ou P2 | Falar | R$ 30–60 |
# assistente_pi.py - roda no Raspberry Pi, junto com o servidor.py
# pip install openwakeword sounddevice soundfile numpy
import subprocess, tempfile
import sounddevice as sd, soundfile as sf
from openwakeword.model import Model
from openwakeword.utils import download_models
from servidor import ouvido, comando_da_casa, perguntar_ia, falar
download_models() # só na primeira vez
ativa = Model(wakeword_models=["hey_jarvis"]) # palavra pronta: "hey jarvis"
TAXA, BLOCO = 16000, 1280 # 80 ms de som por vez
with sd.InputStream(samplerate=TAXA, channels=1, dtype="int16",
blocksize=BLOCO) as mic:
while True:
bloco, _ = mic.read(BLOCO)
if max(ativa.predict(bloco[:, 0]).values()) < 0.5:
continue # nada é guardado antes da palavra
audio, _ = mic.read(TAXA * 4) # ouve 4 s depois da palavra
with tempfile.TemporaryDirectory() as pasta:
entrada, saida = pasta + "/p.wav", pasta + "/r.wav"
sf.write(entrada, audio, TAXA)
partes, _ = ouvido.transcribe(entrada, language="pt")
texto = " ".join(p.text for p in partes).strip()
if texto:
falar(comando_da_casa(texto) or perguntar_ia(texto), saida)
subprocess.run(["aplay", "-q", saida])
ativa.reset()
As palavras de ativação prontas do openWakeWord são em inglês ("hey jarvis", "alexa"). Treinar uma palavra própria em português, como o nome do seu robô, é um ótimo desafio de versão 3.
O corpo do robô
Caixa, luz e uma cabeça que gira
- Caixa: papelão grosso ou impressão 3D, cerca de 10 × 10 × 14 cm. Furo de 5 mm na frente para o microfone e furos pequenos embaixo ou atrás para o alto-falante.
- Microfone longe do alto-falante, de preferência em lados opostos. Se ficarem colados, o robô ouve a própria voz.
- Luz: o LED (ou um anel de LEDs) fica bem à vista. Aceso = ouvindo. É um aviso honesto para quem está por perto.
- Cabeça que "olha": na versão 1, o servo gira a parte de cima para a frente quando você aperta o botão. Para virar de verdade na direção de quem fala, a versão 3 usa dois microfones (esquerda e direita) e gira para o lado com som mais forte.
Teste e melhore
Checklist e erros comuns
- O LED acende só enquanto o botão está apertado (ou depois da palavra)?
- O servidor responde? Teste no PC:
curl -X POST --data-binary @teste.wav http://localhost:5000/falar -o r.wav - Quanto tempo leva entre soltar o botão e ouvir a resposta? Anote: esse é o seu número principal.
- Em 10 perguntas, quantas ele entendeu certo?
| O que acontece | Causa provável | O que mudar |
|---|---|---|
| Áudio gravado mudo ou só chiado | SCK e WS trocados, ou L/R solto | Confira os pinos; L/R no GND = canal esquerdo |
| Placa reinicia ao gravar | Placa sem PSRAM ou PSRAM desativada | Use ESP32-S3 N16R8 e ative OPI PSRAM |
| Resposta picotada ou acelerada | Taxa de amostragem diferente de 16 000 Hz | Confira o ffmpeg com -ar 16000 |
| Demora mais de 10 s | Whisper grande demais para o PC | Troque "small" por "base"; IA com respostas mais curtas |
| Entende palavras erradas | Microfone longe, ruído, eco do alto-falante | Fale a 30–50 cm, afaste o alto-falante do microfone |
| Placa não conecta ao servidor | Firewall do PC ou IP errado | Libere a porta 5000 na rede local; confira o IP do PC |
Registre a versão 1 → versão 2 no caderno de testes: tempo de resposta, acertos em 10 perguntas, o que mudou. Exemplo: "v1: 9 s, 6/10 · troquei Whisper small por base · v2: 5 s, 6/10".
Privacidade e segurança
Um assistente que respeita a casa
- Não grave o tempo todo. Antes do botão ou da palavra de ativação, nada sai do aparelho e nada é salvo.
- Apague depois de usar. O código usa pasta temporária: o áudio some assim que a resposta sai.
- Botão de mudo físico: uma chave que corta o 3V3 do microfone. Desligado de verdade, não por software.
- Energia: tudo por USB 5 V (carregador de celular ou power bank). Nada ligado direto na tomada. Se usar bateria Li-ion, só com placa de proteção, nunca carregue sem ninguém olhando e não guarde bateria solta junto de moedas ou chaves.
Para ir além
Próximos passos
- Ligue o assistente ao controle da casa (S7) para luz, ventilador e portão.
- Dê memória curta: guarde as últimas 3 perguntas para ele entender "e amanhã?".
- No curso aberto Robótica do Zero ao Expert do INEMA.CLUB, a trilha 5 trata de LLM como cérebro do robô e voz local.
- Veja também o robô vigia, que usa visão computacional no lugar da voz.