ESP32 deep sleep MQTT é o que faz um sensor a bateria durar meses em vez de dias. Neste tutorial você vai construir um nó que acorda por timer, reconecta ao Wi-Fi o mais rápido possível, publica uma leitura retida no broker.connectmq.com, confirma que a mensagem realmente chegou ao broker e volta a dormir. Como a leitura é retida, os dashboards sempre mostram o último valor, mesmo que o dispositivo passe quase todo o tempo offline.
O ciclo acordar, publicar, dormir
Em deep sleep, o ESP32 desliga as CPUs, a maior parte da RAM e o rádio. Só o domínio RTC continua ativo, consumindo uma fração mínima da corrente em atividade. Quando o timer dispara, o chip não continua de onde parou: ele reinicia e executa setup() desde o começo. Neste design, o loop() nunca roda.
Todo ciclo de despertar segue os mesmos passos:
- Inicializar e ler o sensor imediatamente, antes que o rádio aqueça a placa.
- Conectar ao Wi-Fi reutilizando o canal e o ponto de acesso salvos no ciclo anterior.
- Conectar ao broker MQTT e publicar a leitura como mensagem retida.
- Confirmar que o broker a recebeu.
- Desconectar de forma limpa, desligar o rádio e entrar em deep sleep.
O tempo gasto entre os passos 2 e 5 domina o consumo de energia, porque o rádio Wi-Fi é de longe o que mais puxa corrente. Toda otimização deste artigo busca encurtar essa janela ou pulá-la quando ela falharia de qualquer jeito.
| Fase | Consumo relativo | Como encurtar |
|---|---|---|
| Boot e leitura do sensor | Baixo a moderado | Ler o sensor primeiro, manter o código enxuto |
| Associação Wi-Fi e DHCP | Alto | Reutilizar canal e BSSID, IP estático opcional |
| Conexão MQTT e publicação | Alto | Conexão simples ou com sessão leve, uma única publicação |
| Espera pela confirmação | Alto | Timeout curto, desistir rápido |
| Deep sleep | Muito baixo | Projeto da placa: regulador, LEDs, chip USB |
A última linha costuma surpreender: muitas placas de desenvolvimento desperdiçam muito mais no regulador de tensão, no LED de alimentação e no chip USB-serial do que o próprio ESP32 consome em deep sleep. Para uma autonomia real, meça a placa, não o chip. O artigo complementar sobre sensores ESP32 alimentados por bateria aprofunda as escolhas de hardware e as medições de corrente.
Por que mensagens retidas são essenciais aqui
Um sensor que dorme publica uma vez e desaparece. Se um dashboard ou um app de celular se inscrever um minuto depois, uma mensagem normal já se foi. Com a flag retain ativada, o broker guarda a última mensagem daquele tópico e a entrega imediatamente a cada novo assinante. O dashboard mostra a leitura mais recente no instante em que abre, não importa quando o dispositivo acordou pela última vez.
Coloque um timestamp ou número de sequência no payload para que os consumidores saibam a idade do valor retido. Uma leitura de quatro horas atrás que parece atual é enganosa, e para um dispositivo que dorme "antigo" é o estado normal. Se mensagens retidas são novidade para você, a visão geral sobre o que um broker MQTT faz explica como o broker as armazena.
Confirmando a entrega sem publicação QoS 1
O PubSubClient só publica com QoS 0, o que significa "enviado", não "recebido". Em um dispositivo sempre ligado isso costuma ser aceitável. Em um dispositivo que dorme é um problema real: se você chamar esp_deep_sleep_start() logo após publish(), o rádio pode desligar com o pacote ainda no buffer, e a mensagem some sem aviso.
O sketch abaixo usa um truque simples e confiável. Ele se inscreve no próprio tópico de estado antes de publicar e depois espera o broker enviar a nova mensagem de volta. Ver a própria mensagem ecoada prova que o broker a recebeu e roteou. Um número de sequência guardado na memória RTC distingue a mensagem nova da antiga mensagem retida, que o broker também entrega na inscrição.
O sketch completo de ESP32 deep sleep MQTT
#include <WiFi.h>
#include <PubSubClient.h>
#include "esp_sleep.h"
const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* CLIENT_ID = "sleepy-3e7f10"; // unique
const char* TOPIC_STATE = "connectmq/demo/sleepy-3e7f10/state";
const uint64_t SLEEP_SECONDS = 300; // 5 minutes
const uint32_t WIFI_TIMEOUT_MS = 8000;
const uint32_t ACK_TIMEOUT_MS = 3000;
// Survives deep sleep (lost on power-off or reset)
RTC_DATA_ATTR uint32_t seq = 0;
RTC_DATA_ATTR uint32_t failures = 0;
RTC_DATA_ATTR uint8_t savedBssid[6];
RTC_DATA_ATTR int32_t savedChannel = 0;
RTC_DATA_ATTR bool haveSaved = false;
WiFiClient net;
PubSubClient mqtt(net);
volatile bool acked = false;
char expected[24];
void goToSleep() {
mqtt.disconnect();
net.stop();
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
// Back off when things keep failing, so a dead network does not drain the battery
uint64_t seconds = SLEEP_SECONDS * (failures > 3 ? 4 : 1);
Serial.printf("Sleeping %llu s (failures=%lu)\n", seconds, (unsigned long)failures);
Serial.flush();
esp_sleep_enable_timer_wakeup(seconds * 1000000ULL);
esp_deep_sleep_start();
}
float readSensor() {
// Replace with your sensor; simulated value keeps the example self-contained
return 20.0 + (esp_random() % 500) / 100.0;
}
void onMessage(char* topic, byte* payload, unsigned int length) {
// Our payload contains "seq":N; look for the one we just sent
String msg;
for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
if (msg.indexOf(expected) >= 0) acked = true;
}
bool connectWifi() {
WiFi.mode(WIFI_STA);
WiFi.persistent(false);
if (haveSaved) {
WiFi.begin(WIFI_SSID, WIFI_PASSWORD, savedChannel, savedBssid, true); // fast path
} else {
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
unsigned long start = millis();
while (WiFi.status() != WL_CONNECTED) {
if (millis() - start > WIFI_TIMEOUT_MS) {
haveSaved = false; // AP may have changed channel: full scan next time
return false;
}
delay(20);
}
memcpy(savedBssid, WiFi.BSSID(), 6);
savedChannel = WiFi.channel();
haveSaved = true;
return true;
}
void setup() {
unsigned long t0 = millis();
Serial.begin(115200);
float temperature = readSensor(); // read before the radio warms the board
seq++;
if (!connectWifi()) { failures++; goToSleep(); }
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setCallback(onMessage);
mqtt.setSocketTimeout(4);
if (!mqtt.connect(CLIENT_ID)) { failures++; goToSleep(); }
mqtt.subscribe(TOPIC_STATE, 0);
snprintf(expected, sizeof(expected), "\"seq\":%lu,", (unsigned long)seq);
char payload[128];
snprintf(payload, sizeof(payload),
"{\"seq\":%lu,\"temperature\":%.2f,\"rssi\":%d,\"awakeMs\":%lu,\"failures\":%lu}",
(unsigned long)seq, temperature, WiFi.RSSI(),
millis() - t0, (unsigned long)failures);
mqtt.publish(TOPIC_STATE, payload, true); // retained
unsigned long start = millis();
while (!acked && millis() - start < ACK_TIMEOUT_MS) {
mqtt.loop();
delay(5);
}
if (acked) failures = 0; else failures++;
Serial.printf("seq=%lu acked=%d awake=%lu ms\n", (unsigned long)seq, acked, millis() - t0);
goToSleep();
}
void loop() {
// Never reached: the ESP32 restarts in setup() after every wake-up
}
O que cada otimização faz
Cache de canal e BSSID. Um WiFi.begin() normal varre todos os canais para encontrar o ponto de acesso. Passar o canal e o BSSID salvos na memória RTC pula essa varredura, que costuma ser a maior economia isolada. Se o roteador mudar de canal, o caminho rápido estoura o tempo, o cache é limpo e o próximo despertar faz uma varredura completa.
Ler o sensor primeiro. O rádio aquece a placa de forma perceptível, o que distorce leituras de temperatura. Medir antes de conectar evita isso.
Backoff em caso de falha. Quando a rede está fora, cada despertar queima energia em tentativas que não podem dar certo. Após falhas repetidas, o nó dorme quatro vezes mais até que uma publicação volte a funcionar.
awakeMs no payload. Informar quanto tempo o dispositivo ficou acordado na fase anterior dá uma visão gratuita e contínua do custo de energia. Se esse número subir aos poucos, algo mudou na rede.
Sem Last Will. Um dispositivo que dorme se desconecta de propósito a cada ciclo, então um Last Will offline não teria sentido. Avalie se ele está vivo pela idade da última leitura retida.
Opcionalmente, um IP estático também economiza a troca DHCP. Chame WiFi.config(ip, gateway, subnet, dns) antes de WiFi.begin(), mas apenas em redes onde você controla a atribuição de endereços.
Verifique se funciona
Inscreva-se a partir do seu computador:
mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-3e7f10 -v -t "connectmq/demo/sleepy-3e7f10/#"
Reduza SLEEP_SECONDS para 20 durante os testes. Você deve ver uma mensagem por ciclo:
connectmq/demo/sleepy-3e7f10/state {"seq":7,"temperature":22.81,"rssi":-61,"awakeMs":1180,"failures":0}
Agora pare o assinante e inicie-o de novo enquanto o ESP32 dorme. A última leitura aparece imediatamente, que é exatamente o que um dashboard experimenta. Depois desligue o roteador por alguns ciclos: o monitor serial mostra failures crescendo, e a primeira mensagem após a recuperação informa quantos ciclos foram perdidos.
O broker.connectmq.com é um servidor MQTT público (gratuito) adequado para esse tipo de teste, mas não para dados de produção. As sessões dele expiram em no máximo 30 minutos, o que não importa aqui, porque o sketch conecta com uma sessão nova a cada despertar. Ao migrar para um broker ConnectMQ privado, o sketch só precisa de credenciais (e TLS, se você optar por ele); o único ponto de atenção é o limite de 25 conexões do plano gratuito, que uma frota de sensores que dormem raramente atinge, porque cada um fica conectado só por um instante a cada ciclo.
Checklist de autonomia da bateria
- Remova ou desative o LED de alimentação e use uma placa com regulador de baixa corrente quiescente, ou alimente o módulo diretamente.
- Meça a corrente em deep sleep com um multímetro em série; em uma placa bem projetada ela deve ficar bem abaixo de um miliampere.
- Mantenha a janela acordada curta: Wi-Fi em cache, timeouts curtos, uma publicação.
- Escolha um intervalo sensato. Reduzir o intervalo pela metade praticamente dobra a energia gasta com o rádio ligado; a maioria das leituras ambientais não precisa de resolução de minutos.
- Alimente o sensor por um GPIO e desligue-o antes de dormir, para que ele não drene a bateria em segundo plano.
- Evite TLS a cada despertar se a autonomia for crítica e o modelo de ameaça permitir isso em uma rede privada; quando precisar de criptografia, reserve tempo para o handshake extra e siga ESP32 MQTT SSL com WiFiClientSecure.
Solução de problemas
- As mensagens chegam só às vezes. Você está dormindo antes de a publicação sair pelo rádio. Mantenha a confirmação por eco e não reduza demais o
ACK_TIMEOUT_MS. seqvolta para 1 inesperadamente. A memória RTC sobrevive ao deep sleep, mas não a um corte de energia, a um brownout ou ao botão de reset. Uma tensão de bateria em queda causando brownouts é uma causa comum.- O dispositivo nunca acorda. Verifique se você chama
esp_sleep_enable_timer_wakeup()com microssegundos e um valor de 64 bits; um overflow de 32 bits gera um intervalo muito mais curto ou errado. - A saída serial é cortada. Chame
Serial.flush()antes de dormir, como o sketch faz. - Tempos acordado longos em alguns ciclos. O caminho rápido do Wi-Fi falhou e uma varredura completa foi executada. Verifique se o seu roteador usa um canal fixo.
Sensores que dormem em produção
Valores retidos são tão confiáveis quanto o broker que os guarda. Um broker MQTT gerenciado mantém esse endpoint disponível enquanto seus sensores dormem, e os mesmos tópicos retidos atendem seus dashboards, scripts e apps. Com o ConnectMQ, você cria credenciais por dispositivo no portal, vê o último valor retido de cada sensor no explorador de tópicos ao vivo e define regras de alerta por limite em leituras como temperatura. Um broker MQTT na nuvem também permite que sensores em uma fazenda, em uma adega e em um armazém reportem para um único endpoint sem nenhum servidor local. É novo na placa? Comece pelo tutorial de IoT com ESP32.
Comece com o ConnectMQ
Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito. Aponte seus sensores para ele com credenciais próprias e veja as leituras retidas aparecerem no portal conforme eles acordam.
Perguntas frequentes
Por que meu ESP32 perde mensagens MQTT antes do deep sleep?
A publicação QoS 0 retorna assim que os dados entram na fila, então dormir imediatamente pode cortá-la. Espere uma confirmação, como a técnica de eco deste sketch, e só então desconecte e durma.
Um sensor em deep sleep deve manter uma sessão MQTT?
Normalmente não. Uma conexão nova a cada despertar é mais simples e funciona com qualquer broker. Sessões persistentes só ajudam se o dispositivo precisar receber comandos enfileirados enquanto dormia.
Como um ESP32 que dorme pode receber comandos?
Publique os comandos como mensagens retidas em um tópico no qual o dispositivo se inscreve ao acordar. Ele recebe o comando mais recente imediatamente, executa e depois o limpa publicando uma mensagem retida vazia.
Qual é um bom intervalo de publicação para um sensor a bateria?
Depende da rapidez com que o valor medido muda. Sensores ambientais costumam funcionar bem com intervalos de cinco a quinze minutos; intervalos menores custam proporcionalmente mais energia.



