Aula de Laboratório — Logging no ESP32
Ferramentas: VS Code + PlatformIO + Wokwi
Biblioteca: esp32-hal-log.h (camada Arduino do Arduino-ESP32)
Duração estimada: 2 h
Objetivos de aprendizagem
Ao final desta aula você será capaz de:
- Explicar a diferença entre depurar com
Serial.print()e usar um sistema de log com níveis. - Configurar o nível de log em tempo de compilação no PlatformIO (
CORE_DEBUG_LEVEL). - Usar as macros
log_e,log_w,log_i,log_d,log_vcom formatação estiloprintf. - Interpretar o formato da saída de log (timestamp, nível, arquivo, linha, função).
- Simular o firmware no Wokwi diretamente pelo VS Code, sem placa física.
Pré-requisitos e instalação
- VS Code instalado.
- Extensão PlatformIO IDE (procure por "PlatformIO IDE" na aba de extensões).
- Extensão Wokwi for VS Code (procure por "Wokwi"). Na primeira simulação ela pede um login/licença gratuita — basta seguir o link que aparece.
- Conexão com a internet na primeira compilação (o PlatformIO baixa o toolchain do ESP32).
Tudo nesta aula roda no simulador Wokwi. Você não precisa de um ESP32 real.
Fundamentos: por que usar log em vez de Serial.print()?
Muita gente depura o ESP32 assim:
Serial.print("valor = ");
Serial.println(valor);
Funciona, mas tem problemas quando o projeto cresce:
- Não tem níveis. Um erro grave e uma mensagem de curiosidade têm a mesma aparência.
- Fica no binário para sempre. Você precisa comentar/apagar os
printna versão final, e isso é fácil de esquecer. - Não diz de onde veio. Em um projeto com 20 arquivos, "valor = 5" não ajuda a localizar a origem.
O sistema de log do Arduino-ESP32 resolve isso. Ele oferece cinco níveis de severidade e cada mensagem sai automaticamente com timestamp, nível, arquivo, linha e função de origem.
Os cinco níveis (do mais grave ao mais verboso)
| Macro | Nível | Valor | Quando usar |
|---|---|---|---|
log_e() | Error | 1 | Falhas que impedem a operação (sensor não responde). |
log_w() | Warning | 2 | Algo estranho, mas o programa continua (valor fora da faixa). |
log_i() | Info | 3 | Marcos normais de funcionamento ("WiFi conectado"). |
log_d() | Debug | 4 | Detalhes úteis durante o desenvolvimento. |
log_v() | Verbose | 5 | Tudo, inclusive laços internos e valores brutos. |
O valor 0 (NONE) desliga todos os logs.
A ideia-chave: o nível é decidido na compilação
Existe uma macro chamada CORE_DEBUG_LEVEL. Cada macro de log só gera código se o seu nível for menor ou igual a CORE_DEBUG_LEVEL. Ou seja:
- Se você compilar com
CORE_DEBUG_LEVEL=3(Info), entãolog_d()elog_v()desaparecem do binário — não ocupam memória nem gastam tempo de CPU. - Para gerar a versão "de produção", basta recompilar com
CORE_DEBUG_LEVEL=1(só erros) ou0(nenhum log). Você não apaga uma linha sequer do código.
As mensagens de debug ficam no código-fonte, documentando o programa, mas somem do produto final com uma única troca de configuração.
A API do esp32-hal-log.h
Formatação estilo printf
As macros aceitam os mesmos especificadores do printf do C:
int temp = 25;
float tensao = 3.31;
const char* estado = "OK";
log_i("Temperatura: %d C", temp);
log_i("Tensao: %.2f V", tensao);
log_i("Estado do sensor: %s", estado);
log_d("temp=%d tensao=%.2f estado=%s", temp, tensao, estado);
Principais especificadores: %d (inteiro), %u (inteiro sem sinal), %.2f (float com 2 casas), %s (string C), %c (caractere), %x (hexadecimal), %p (ponteiro), %% (o próprio símbolo de porcentagem).
Formato da saída no monitor serial
Uma chamada como log_i("Sistema iniciado") dentro de setup() produz algo assim:
[ 342][I][main.cpp:15] setup(): Sistema iniciado
Lendo da esquerda para a direita:
[ 342]— timestamp em milissegundos desde o boot.[I]— o nível (E, W, I, D ou V).[main.cpp:15]— arquivo e linha exatos de onde a mensagem saiu.setup():— a função que a chamou.Sistema iniciado— a sua mensagem.
Você não precisa escrever nada disso: o prefixo é montado automaticamente pela macro.
Dump de memória em hexadecimal
Para inspecionar buffers (ex.: pacotes de rede, leitura de sensor I2C), existem as variantes log_buf_*:
uint8_t pacote[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02};
log_buf_i(pacote, sizeof(pacote));
Isso imprime os bytes formatados em hexadecimal, útil para depurar protocolos.
Montando o projeto no PlatformIO
Criar o projeto
- Abra o VS Code → ícone do PlatformIO (cabeça de formiga) na barra lateral → PIO Home → New Project.
- Preencha:
- Name:
lab-log-esp32 - Board:
Espressif ESP32 Dev Module(ouDOIT ESP32 DEVKIT V1) - Framework:
Arduino
- Name:
- Clique em Finish e aguarde o download do toolchain.
Configurar o platformio.ini
Este é o passo mais importante da aula. Abra o platformio.ini na raiz do projeto e deixe-o assim:
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
; Velocidade do monitor serial. Precisa bater com o Serial.begin() do codigo.
monitor_speed = 115200
; CORE_DEBUG_LEVEL define ate que nivel de log sera COMPILADO:
; 0 = NONE | 1 = ERROR | 2 = WARN | 3 = INFO | 4 = DEBUG | 5 = VERBOSE
build_flags =
-DCORE_DEBUG_LEVEL=5
Se você esquecer o -DCORE_DEBUG_LEVEL, o valor padrão costuma ser 0 (NONE) e nada aparece, mesmo com o código correto. Se seus logs sumiram, comece a investigação por aqui.
O código-base (src/main.cpp)
#include <Arduino.h>
void setup() {
Serial.begin(115200);
delay(1000); // da tempo do monitor serial abrir e nao perder as 1as linhas
log_i("===== Laboratorio de Log ESP32 =====");
log_i("Sistema iniciado com sucesso");
}
void loop() {
// veremos o loop nos exercicios
}
Observe que usamos Serial.begin() normalmente. O sistema de log escreve no mesmo periférico serial; o Serial.begin continua sendo o que inicializa a UART e define a velocidade.
Configurando o Wokwi
O Wokwi simula o ESP32 usando o firmware já compilado pelo PlatformIO. Precisamos de dois arquivos na raiz do projeto.
- diagram.json
- wokwi.toml
O "circuito". Para começar, só a placa é suficiente:
{
"version": 1,
"author": "Aluno",
"editor": "wokwi",
"parts": [
{
"type": "board-esp32-devkit-c-v4",
"id": "esp",
"top": 0,
"left": 0,
"attrs": {}
}
],
"connections": [
["esp:TX0", "$serialMonitor:RX", "", []],
["esp:RX0", "$serialMonitor:TX", "", []]
]
}
As duas conexões ligam a UART do ESP32 ao monitor serial do simulador — é o que faz os logs aparecerem na tela do Wokwi.
Aponta para o firmware compilado:
[wokwi]
version = 1
firmware = '.pio/build/esp32dev/firmware.bin'
elf = '.pio/build/esp32dev/firmware.elf'
Se o seu ambiente no platformio.ini não se chamar esp32dev, ajuste o caminho .pio/build/<nome-do-env>/ de acordo.
O fluxo de trabalho
- Compile: ícone do PlatformIO na barra inferior (✓ "Build"), ou
Ctrl+Alt+B. Isso gera ofirmware.bin/.elf. - Simule: abra a paleta de comandos (
Ctrl+Shift+P) → Wokwi: Start Simulator. - Uma aba com a placa e o monitor serial do Wokwi aparece. Seus logs surgem ali.
O Wokwi só roda o que já foi compilado. Se você mudar o código, compile de novo antes de reiniciar o simulador, senão vai ver o firmware antigo.
Roteiro de laboratório (prática guiada)
Exercício 1 — Os cinco níveis de uma vez
Substitua o setup() por:
void setup() {
Serial.begin(115200);
delay(1000);
log_e("Isto e um ERRO (log_e)");
log_w("Isto e um AVISO (log_w)");
log_i("Isto e uma INFO (log_i)");
log_d("Isto e DEBUG (log_d)");
log_v("Isto e VERBOSE (log_v)");
}
Compile com CORE_DEBUG_LEVEL=5 e simule. Anote: você deve ver as cinco linhas, cada uma com seu prefixo de nível.
Exercício 2 — Filtrando por nível de compilação
Sem mudar o código do Exercício 1, edite o platformio.ini e troque para:
build_flags =
-DCORE_DEBUG_LEVEL=2
Compile de novo e simule. Observe: agora só aparecem log_e e log_w. As linhas de Info, Debug e Verbose foram removidas do binário na compilação — não é um filtro em tempo de execução, elas literalmente não existem mais no firmware.
Por que isso é melhor do que um if (nivel <= X) dentro do código?
Exercício 3 — Log com dados formatados
Volte para CORE_DEBUG_LEVEL=5 e use um loop() que simula a leitura de um sensor:
void loop() {
static int contador = 0;
float temperatura = 20.0 + (contador % 15); // valor fake que varia
log_d("Ciclo #%d iniciado", contador);
log_i("Leitura de temperatura: %.1f C", temperatura);
if (temperatura > 30.0) {
log_w("Temperatura alta: %.1f C (limite 30)", temperatura);
}
contador++;
delay(1000);
}
Simule e acompanhe o timestamp crescendo de ~1000 ms em ~1000 ms, e o aviso aparecendo só quando o valor passa de 30.
Exercício 4 — Simulando um erro real
Vamos fingir que um sensor falhou:
bool lerSensor(float* saida) {
static int tentativa = 0;
tentativa++;
if (tentativa % 4 == 0) { // a cada 4 leituras, "falha"
log_e("Falha ao ler sensor na tentativa %d", tentativa);
return false;
}
*saida = 25.5;
log_v("Sensor respondeu OK na tentativa %d", tentativa);
return true;
}
void loop() {
float t;
if (lerSensor(&t)) {
log_i("Temperatura valida: %.1f C", t);
} else {
log_w("Usando ultimo valor conhecido (leitura ignorada)");
}
delay(1000);
}
Objetivo pedagógico: perceber como os níveis contam uma história — o log_e marca a falha exata (com número da tentativa e linha do arquivo), o log_w mostra a recuperação, e o log_v só aparece se você quiser o detalhe fino.
Exercícios propostos (para entregar)
- Contador de erros. Modifique o Exercício 4 para contar quantas falhas ocorreram e, a cada 10 ciclos, emitir um
log_icom a taxa de falhas em porcentagem (%.1f%%). - Nível de produção. Compile o projeto com
CORE_DEBUG_LEVEL=1e descreva no relatório o que muda na saída. Explique por que essa seria a configuração para um produto entregue ao cliente. - Hexdump. Crie um
uint8_t buffer[8]preenchido com valores à sua escolha e imprima-o comlog_buf_i(). Cole a saída no relatório. - Desafio. Ligue um LED no diagrama do Wokwi (GPIO 2) e faça-o piscar, emitindo um
log_da cada troca de estado. Anexe odiagram.jsonmodificado.
Referência rápida
| Macro | Aparece quando CORE_DEBUG_LEVEL ≥ | Uso típico |
|---|---|---|
log_e("...", ...) | 1 | Erro |
log_w("...", ...) | 2 | Aviso |
log_i("...", ...) | 3 | Informação |
log_d("...", ...) | 4 | Debug |
log_v("...", ...) | 5 | Verbose |
log_buf_i(buf, tam) | 3 | Dump hexadecimal |
Em platformio.ini: build_flags = -DCORE_DEBUG_LEVEL=<0..5>
Solução de problemas (troubleshooting)
| Sintoma | Causa provável | Solução |
|---|---|---|
| Nenhum log aparece | CORE_DEBUG_LEVEL não definido ou 0 | Adicione -DCORE_DEBUG_LEVEL=5 em build_flags e recompile |
| Só aparecem erros e avisos | Nível de compilação baixo (1 ou 2) | Suba o CORE_DEBUG_LEVEL |
| Caracteres estranhos | Velocidade errada | monitor_speed deve bater com Serial.begin() (115200) |
| As primeiras linhas somem | Monitor abriu depois do boot | Adicione delay(1000) após Serial.begin() |
| Wokwi mostra código antigo | Simulou sem recompilar | Faça Build antes de Start Simulator |
| Wokwi não acha o firmware | Caminho errado no wokwi.toml | Confira o nome do env em .pio/build/<env>/ |
log_i "não existe" | Faltou #include <Arduino.h> | O header de log já vem junto do core Arduino; garanta o include |
Apêndice — Relação com o ESP-IDF
O esp32-hal-log.h é a camada Arduino. Por baixo, ele usa o sistema de log do ESP-IDF, cujas macros exigem uma TAG (um texto que identifica o módulo).
- Arduino (esta aula)
- ESP-IDF puro
// Sem TAG — mais simples
log_i("Leitura = %.1f", valor);
// Exige uma TAG que identifica o modulo
static const char* TAG = "SENSOR";
ESP_LOGI(TAG, "Leitura = %.1f", valor);
Para esta aula ficamos na camada Arduino (log_i e amigas), que é mais simples e não exige TAG. Quando você migrar para projetos ESP-IDF puros, a lógica de níveis e o CORE_DEBUG_LEVEL continuam valendo — muda só a forma de escrever a macro.