Energia e Térmica: manter o assistente de bancada rodando IA contínua

Ouvir

Carregando voz…

Energia e Térmica: manter o assistente de bancada rodando IA contínua

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.

Comentários

Deixe um comentário