Um assistente de bancada não é um projeto de dormir em deep sleep. Ele fica ligado ao lado do teclado por 8 a 12 horas por dia, com display aceso, microfone escutando e Wi-Fi em pé. É essa permanência que reordena as prioridades de engenharia: em vez de “quanto dura a bateria?”, a pergunta certa vira “qual o consumo sustentado e quanto calor isso gera dentro de uma caixa fechada?”. Este é o Projeto 8 da série Assistentes de IA de Bancada — o capítulo que costuma derrubar projetos bonitos de protoboard na hora em que entram na carcaça.
Conceito e arquitetura de energia
O erro clássico é projetar para o pico e ignorar a média. Um ESP32-S3 a 240 MHz consome algo na casa de 30–40 mA em CPU ativa, mas cada rajada de transmissão Wi-Fi puxa picos de 300–500 mA por alguns milissegundos. O display TFT com backlight pode somar 40–120 mA. O amplificador MAX98357A, em silêncio, fica na casa de poucos mA, mas em volume alto pode puxar centenas de mA. A gestão de energia tem que sobreviver à média e ao pico simultâneo de rádio + áudio + tela.
Modele o sistema como uma árvore de potência, não como “uma fonte de 3,3 V”. A entrada USB-C (5 V) alimenta um carregador LiPo com load sharing — quando o cabo está plugado, o sistema roda do barramento e a bateria carrega sem ciclar. A célula LiPo alimenta um regulador buck-boost que entrega 3,3 V estáveis mesmo com a bateria caindo de 4,2 V para 3,4 V. A rail de 3,3 V se divide em digital (MCU, rádio, display) e analógica (microfone I2S), com filtragem separada para o mic não herdar ruído da comutação.
Os “modos” que você precisa medir separadamente são quatro:
- Idle — tela ligada, rádio associado mas ocioso, CPU em frequência reduzida.
- Escuta — microfone I2S ativo e wake word rodando; custo baixo, mas contínuo.
- Inferência — na arquitetura híbrida, a placa quase não calcula: ela envia áudio/texto e espera o host. O custo aqui é rádio + buffer, não CPU.
- Rádio — rajadas de TX durante request e streaming da resposta; é o modo que define a espessura de cobre e o tamanho dos capacitores.
Lista de peças (BOM)
| Componente | Modelo exato | Função | Faixa de preço |
|---|---|---|---|
| Placa | ESP32-S3-DevKitC-1 (N16R8, 16 MB flash + 8 MB PSRAM octal) | MCU, Wi-Fi/BLE, PSRAM para buffers de áudio/UI | R$ 70–130 |
| Bateria | LiPo pouch 3,7 V 2000–3000 mAh (ou célula 18650 com suporte) | Reserva de energia para uso sem cabo | R$ 35–90 |
| Carregador + proteção | Módulo TP4056 (com DW01) ou Adafruit BQ24074 | Carga da LiPo, proteção contra sobrecarga/descarga e load sharing | R$ 8–45 |
| Regulador | Buck-boost TPS63020 (ou buck MP1584 para 5 V -> 3,3 V com fonte fixa) | Manter 3,3 V estáveis durante toda a descarga da célula | R$ 15–40 |
| Fuel gauge | MAX17048 (Adafruit MAX1704X) | Leitura de tensão e estado de carga por I2C, sem ADC ruidoso | R$ 30–55 |
| Sensor de corrente | INA219 ou shunt + INA226 | Medir consumo real de cada modo em bancada | R$ 15–35 |
| Passivos térmicos | Capacitor eletrolítico 470–1000 µF + 100 nF cerâmico; dissipador para o buck | Amortecer picos de TX e reduzir pontos quentes | R$ 5–25 |
Wiring e cuidados elétricos
| Sinal | Pino ESP32-S3 recomendado | Observação |
|---|---|---|
| I2C (SDA/SCL) do MAX17048 e INA219 | GPIO 8 (SDA) / GPIO 9 (SCL) | Pull-ups de 4,7 kΩ para 3,3 V; nunca para 5 V. |
| Divisor de tensão da bateria (fallback) | GPIO 4 (ADC1_CH3) | Só ADC1 com Wi-Fi ligado; ADC2 conflita com o rádio. Use fator ~2x e capacitor de 100 nF. |
| Status do carregador (CHRG / STDBY) | GPIO 5 / GPIO 6 | Entradas digitais com pull-up; indique carga/cheia na UI. |
| Microfone I2S (INMP441) | SCK 14 / WS 15 / SD 13 | Rail analógica separada; L/R amarrado ao GND (canal esquerdo). |
| Amplificador I2S (MAX98357A) | BCLK 16 / LRC 17 / DIN 18 | Alimente o amp direto da rail de 3,3 V com bulk cap próprio. |
| Strapping pins | GPIO 0, 45, 46 (e 3 do JTAG) | Não pendure nada que force nível errado no boot; evite botões que as puxem a GND no reset. |
Regras que evitam retrabalho: mantenha o terra comum entre lógica e potência, mas roteie o retorno do amplificador separado do retorno do mic; coloque o capacitor de bulk o mais perto possível do pino 3V3 da placa; e nunca alimente o servo/motor do projeto pela mesma rail não filtrada da MCU. Em placas S3, lembre que GPIO 19/20 são o USB nativo — se você usar USB-CDC, não os ocupe.
Código e firmware
Dois blocos resolvem 90% do monitoramento. O primeiro lê o fuel gauge e a tensão calibrada; o segundo mede corrente real com o INA219 e reduz frequência quando o sistema entra em ociosidade.
#include <Wire.h>
#include <Adafruit_MAX1704X.h>
#include <Adafruit_INA219.h>
#include "esp_adc_cal.h"
Adafruit_MAX17048 maxlipo;
Adafruit_INA219 ina219(0x40);
static esp_adc_cal_characteristics_t adc_cal;
#define VBAT_ADC_CHANNEL ADC1_CHANNEL_3 // GPIO4
#define DIVIDER_FACTOR 2.0f // R1/R2 = 1:1
float leTensaoBateria() {
// 12 leituras e media: o ADC do S3 e ruidoso sozinho
uint32_t mv = 0;
for (int i = 0; i < 12; i++) {
uint32_t raw = adc1_get_raw((adc1_channel_t)VBAT_ADC_CHANNEL);
mv += esp_adc_cal_raw_to_voltage(raw, &adc_cal);
}
return (mv / 12.0f) * DIVIDER_FACTOR / 1000.0f; // volts
}
void logEnergia() {
float pct = maxlipo.cellPercent(); // estado de carga (%)
float vbat = maxlipo.cellVoltage(); // tensao da celula
float vBus = ina219.getBusVoltage_V();
float mA = ina219.getCurrent_mA(); // corrente instantanea
Serial.printf("SOC %.1f%% Vbat %.2fV I %.1fmA P %.2fW\n",
pct, vbat, mA, vBus * (mA/1000.0f));
// Degrada a UI quando a celula aperta, ANTES do brownout
if (pct < 15.0f) reduzirBacklight(120); // escurece a tela
if (pct < 5.0f) desligaRadioQuandoOcioso();
}
O segundo bloco controla frequência de CPU conforme o modo, sem desligar o rádio (que precisa ficar associado para receber a resposta do host):
// Ajuste dinamico de clock conforme o estado do assistente
void aplicaPerfilEnergia(Estado e) {
switch (e) {
case OCIOSO:
setCpuFrequencyMhz(80); // cai de 240 para 80 MHz
break;
case ESCUTANDO:
setCpuFrequencyMhz(160); // VAD/wake word precisa de folga
break;
case RESPONDENDO:
setCpuFrequencyMhz(240); // streaming/renderizacao exige o maximo
break;
}
// O radio tem de ficar acordado: light sleep quebra o DTIM e derruba o socket
}
Para medir a temperatura interna do chip (proxy da saúde térmica) sem sensor externo, use o driver temperature_sensor do ESP-IDF. Um NTC colado no regulador complementa a leitura onde está o calor de verdade.
Resolução de problemas
- Brownout em rajada de Wi-Fi: reinício quando TX coincide com áudio. Causa: rail insuficiente. Solução: bulk cap de 470–1000 µF junto ao 3V3, buck em vez de LDO e traço curto e grosso. Desativar o brownout detector é máscara, não conserto.
- LDO fervendo: um AMS1117 com 3,7 V na entrada e 3,3 V na saída dissipa (3,7−3,3)×I. A 500 mA são ~0,2 W concentrados; acima disso, aquece e “desliga” a rail. Buck resolve.
- ADC lendo errado com Wi-Fi ligado: ADC2 é compartilhado com o rádio. Migre a leitura de bateria para ADC1 ou desista do ADC e use o fuel gauge I2C.
- Fuel gauge “congelado”: MAX17048 dorme e deixa de atualizar. Reative via registro de quick-start; não confie só no percentual — cruze com a tensão.
- Estalos no áudio: underrun de buffer I2S quando a CPU entra em economia no meio da reprodução. Nunca reduza clock durante o streaming.
- Amp esquentando em silêncio: ganho alto com entrada flutuante. Aterre o pino SD/EN conforme o datasheet e defina o ganho fixo.
Aplicações reais e o que isso muda
Com números medidos, o projeto deixa de ser fé. Um alvo realista de bancada: idle com tela acesa em ~120–180 mA, escuta em ~150–200 mA, rajadas de rádio chegando a ~400 mA por poucos milissegundos. Numa LiPo de 3.000 mAh, isso dá algo entre 12 e 18 horas de uso contínuo — mais do que um dia de trabalho, o que muda a arquitetura: você pode remover o cabo do caminho e ganhar a mesa limpa, sem abandonar o carregamento por USB quando voltar.
A térmica é o limite silencioso. Dentro de uma caixa impressa em 3D sem ventilação, o calor do buck + do rádio + do amp se somam. Se o S3 passa de ~80 °C de junção em regime, o rádio degrada e a latência de resposta sobe — o assistente “fica lento” sem erro de código. Meça com termopar em regime contínuo e dimensione a caixa (aberturas de convecção ou um pequeno exaustor) antes de fechá-la.
Nos próximos capítulos da série, a mesma placa ganha um firmware capaz de aguentar semanas ligado sem travar (FreeRTOS, watchdog, OTA e partições) e, depois, a divisão de trabalho com um host que roda o LLM. Se você quer o pano de fundo de IA local que alimenta esse host, vale cruzar com LLMs Locais: Sua IA, Seu Hardware, Suas Regras; para o recorte de inferência no silício, Edge AI: Inteligência na Ponta dos Dedos. O hub da editoria fica em tiolu.com.br/nucleo-hits.
Não some depois da matéria
Radar do hub: Núcleo Hits, Além do Oculto, Bobinho. Sem spray, sem lista comprada.
Radar do hub
Um e-mail quando sair matéria que importa — sem spray de newsletter genérica.

Deixe um comentário
Entre com Google ou Facebook para comentar. Nome e e-mail vêm da sua conta — sem preencher formulário.
Se o login falhar, configure Google e Facebook em Configurações → Nextend Social Login.
Outras opções de login