Tutoriais de IoT

Tutorial ESP32 MQTT: Publique Dados de Sensores e Controle um LED com a Arduino IDE

Conecte um ESP32 a um broker MQTT com a Arduino IDE e o PubSubClient, publique leituras de temperatura em JSON, controle o LED da placa pela linha de comando e migre para TLS com credenciais do portal.

Placa de desenvolvimento ESP32 enviando dados de temperatura via MQTT em um tutorial de ESP32 MQTT
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

Este tutorial de ESP32 MQTT leva você de um sketch Arduino vazio a uma placa que publica telemetria em JSON, recebe comandos e liga e desliga o LED embutido. Primeiro você vai se conectar à área pública gratuita do broker.connectmq.com, o servidor MQTT público da ConnectMQ, e depois migrar o mesmo sketch para TLS na porta 8883, com credenciais do portal do ConnectMQ. Cada etapa é verificada com mosquitto_sub e mosquitto_pub a partir do seu computador.

O Que Você Vai Construir

O dispositivo final faz três coisas:

  1. Conecta ao Wi-Fi e a um broker MQTT, com reconexão automática.
  2. Publica uma mensagem JSON como {"temperature":23.41,"uptime":125} a cada cinco segundos.
  3. Assina um topic de comandos e liga ou desliga o LED embutido ao receber ON, OFF ou TOGGLE.

A estrutura de topics mantém tudo sob um único prefixo:

Topic Direção Payload de exemplo
connectmq/demo/<deviceId>/telemetry O ESP32 publica {"temperature":23.41,"uptime":125}
connectmq/demo/<deviceId>/led/set O ESP32 assina ON, OFF, TOGGLE
connectmq/demo/<deviceId>/led/state O ESP32 publica (retido) ON ou OFF

Se termos MQTT como topic, QoS ou retained message são novidade para você, leia primeiro O Que É um Broker MQTT?.

Pré-requisitos

  • Uma placa de desenvolvimento ESP32 (qualquer ESP32 DevKit serve; a maioria tem um LED azul no GPIO 2).
  • Arduino IDE 2.x com o pacote de placas esp32 instalado pelo Boards Manager.
  • A biblioteca PubSubClient, de Nick O'Leary, instalada pelo Library Manager.
  • Uma rede Wi-Fi de 2,4 GHz (o ESP32 não suporta 5 GHz).
  • Opcional: os clientes de linha de comando do Mosquitto (mosquitto_sub, mosquitto_pub) no seu computador, para testes.
  • Opcional: um sensor DHT22. O sketch simula a temperatura por padrão, então nenhum hardware extra é necessário.

Endpoints do Broker para ESP32 MQTT

O broker.connectmq.com expõe quatro listeners. O ESP32 usa os dois primeiros; as portas WebSocket são para navegadores.

Protocolo Porta Uso neste tutorial
MQTT (TCP) 1883 Parte 1, área pública anônima
MQTT sobre TLS 8883 Parte 2, credenciais do portal
WebSocket 8083 Não usado (clientes de navegador)
WebSocket seguro 8084 Não usado (clientes de navegador)

A área pública não exige conta: conecte sem usuário nem senha e com um client ID único e não vazio. Lembre-se de que tudo o que é publicado ali pode ser lido por qualquer pessoa, então envie apenas dados de demonstração, nunca dados de produção. A área pública permite 64 KB por mensagem, 10 mensagens por segundo e 10 assinaturas por cliente, e até 8 níveis de topic, o que é mais do que suficiente para este projeto. Veja o guia do servidor MQTT público (gratuito) para mais detalhes.

Parte 1: ESP32 MQTT sobre TCP Simples (Porta 1883)

Gere um client ID único a partir do endereço MAC

Client IDs precisam ser únicos: se dois clientes se conectam com o mesmo ID, o broker desconecta o mais antigo. Todo ESP32 tem um endereço MAC gravado de fábrica, o que o torna uma fonte perfeita para um ID estável e único. O sketch monta algo como esp32-a1b2c3d4e5f6 e o reutiliza no prefixo dos topics.

O sketch completo

Crie um novo sketch, cole o código abaixo e defina o SSID e a senha do seu Wi-Fi.

#include <WiFi.h>
#include <PubSubClient.h>

// ---- Wi-Fi ----
const char* WIFI_SSID     = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";

// ---- MQTT ----
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;

const int LED_PIN = 2;                 // LED embutido na maioria dos ESP32 DevKits
const unsigned long PUBLISH_INTERVAL_MS = 5000;

WiFiClient netClient;
PubSubClient mqtt(netClient);

String deviceId;
String topicTelemetry, topicLedSet, topicLedState;
unsigned long lastPublish = 0;
unsigned long lastReconnectAttempt = 0;
bool ledOn = false;

void connectWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.print("\nWi-Fi connected, IP: ");
  Serial.println(WiFi.localIP());
}

String makeDeviceId() {
  uint8_t mac[6];
  WiFi.macAddress(mac);
  char buf[20];
  snprintf(buf, sizeof(buf), "esp32-%02x%02x%02x%02x%02x%02x",
           mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  return String(buf);
}

void publishLedState() {
  mqtt.publish(topicLedState.c_str(), ledOn ? "ON" : "OFF", true);  // retido
}

void onMessage(char* topic, byte* payload, unsigned int length) {
  String msg;
  for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
  msg.trim();
  Serial.printf("Message on %s: %s\n", topic, msg.c_str());

  if (String(topic) != topicLedSet) return;
  if (msg.equalsIgnoreCase("ON"))          ledOn = true;
  else if (msg.equalsIgnoreCase("OFF"))    ledOn = false;
  else if (msg.equalsIgnoreCase("TOGGLE")) ledOn = !ledOn;
  else return;                             // ignora qualquer outra coisa

  digitalWrite(LED_PIN, ledOn ? HIGH : LOW);
  publishLedState();
}

bool connectMqtt() {
  Serial.printf("Connecting to MQTT as %s ... ", deviceId.c_str());
  if (mqtt.connect(deviceId.c_str())) {     // sem usuário/senha: área pública
    Serial.println("connected");
    mqtt.subscribe(topicLedSet.c_str(), 1);
    publishLedState();
    return true;
  }
  Serial.printf("failed, state=%d\n", mqtt.state());
  return false;
}

float readTemperature() {
  // Sensor simulado: 22-25 °C com um pouco de ruído
  return 23.5 + 1.5 * sin(millis() / 60000.0) + random(-20, 20) / 100.0;
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  connectWiFi();
  deviceId = makeDeviceId();
  String base = "connectmq/demo/" + deviceId;
  topicTelemetry = base + "/telemetry";
  topicLedSet    = base + "/led/set";
  topicLedState  = base + "/led/state";
  Serial.println("Topic prefix: " + base);

  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  mqtt.setCallback(onMessage);
  mqtt.setBufferSize(512);   // o padrão é 256 bytes, veja abaixo
  mqtt.setKeepAlive(30);
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) {
    connectWiFi();
  }

  if (!mqtt.connected()) {
    unsigned long now = millis();
    if (now - lastReconnectAttempt > 5000) {   // nova tentativa não bloqueante a cada 5 s
      lastReconnectAttempt = now;
      if (connectMqtt()) lastReconnectAttempt = 0;
    }
    return;
  }

  mqtt.loop();   // processa mensagens recebidas e o keepalive

  if (millis() - lastPublish >= PUBLISH_INTERVAL_MS) {
    lastPublish = millis();
    char json[96];
    snprintf(json, sizeof(json), "{\"temperature\":%.2f,\"uptime\":%lu}",
             readTemperature(), millis() / 1000);
    mqtt.publish(topicTelemetry.c_str(), json);
    Serial.printf("Published %s\n", json);
  }
}

Selecione a placa e a porta, faça o upload e abra o Serial Monitor em 115200 baud. Você deve ver o endereço IP, o prefixo dos topics e uma linha Published a cada cinco segundos. Copie o device ID do prefixo; você vai precisar dele no próximo passo.

Como funciona a lógica de reconexão

O sketch nunca fica bloqueado para sempre esperando o broker. Se a conexão cair, o loop() tenta de novo a cada cinco segundos enquanto o resto do programa continua rodando. Dois detalhes importam:

  • mqtt.loop() precisa rodar com frequência. Ele lê os pacotes recebidos e envia os pings de keepalive; se você chamar delay(10000) no loop, o broker vai considerar o cliente morto.
  • Depois de reconectar, o sketch assina os topics de novo. Com a clean session padrão, as assinaturas não sobrevivem a uma desconexão, então reassinar dentro de connectMqtt() é essencial.

mqtt.state() retorna um código numérico em caso de falha: -2 geralmente indica um problema de rede (host errado, sem internet), enquanto valores positivos vêm do CONNACK do broker, por exemplo 2 para identificador rejeitado.

Usando um sensor DHT22 de verdade

Para usar dados reais, instale a "DHT sensor library" da Adafruit, ligue o pino de dados do sensor ao GPIO 4 e substitua readTemperature():

#include <DHT.h>
DHT dht(4, DHT22);   // chame dht.begin() no setup()

float readTemperature() {
  float t = dht.readTemperature();
  return isnan(t) ? -127.0 : t;   // envia um valor sentinela em vez de NaN
}

Verifique com mosquitto_sub e mosquitto_pub

Abra um terminal e assine tudo sob o prefixo do seu dispositivo. Troque esp32-a1b2c3d4e5f6 pelo ID exibido no Serial Monitor.

mosquitto_sub -h broker.connectmq.com -p 1883 -i laptop-watch-81f3 \
  -t "connectmq/demo/esp32-a1b2c3d4e5f6/#" -v

A saída fica assim:

connectmq/demo/esp32-a1b2c3d4e5f6/led/state OFF
connectmq/demo/esp32-a1b2c3d4e5f6/telemetry {"temperature":23.41,"uptime":125}
connectmq/demo/esp32-a1b2c3d4e5f6/telemetry {"temperature":23.47,"uptime":130}

Agora mude o LED a partir de um segundo terminal:

mosquitto_pub -h broker.connectmq.com -p 1883 -i laptop-cmd-81f3 \
  -t "connectmq/demo/esp32-a1b2c3d4e5f6/led/set" -q 1 -m "TOGGLE"

O LED muda e o subscriber imprime o novo led/state retido. Como o estado é retido, qualquer dashboard que assine depois recebe imediatamente o valor atual. A assinatura usa QoS 1 para que o comando seja confirmado pelo broker; Níveis de QoS MQTT Explicados aborda os prós e contras.

Você também pode acompanhar o dispositivo entrando e saindo: assine $SYS/broker/connection/esp32-a1b2c3d4e5f6/state e você verá 1 quando ele se conectar e 0 quando se desconectar.

O Tamanho do Buffer do PubSubClient

O PubSubClient aloca um único buffer para pacotes recebidos e enviados, e por padrão ele tem apenas 256 bytes. Esse buffer precisa comportar o pacote MQTT inteiro: cabeçalho fixo, nome do topic e payload. Um topic longo mais um documento JSON de tamanho moderado ultrapassam esse limite rapidamente e, quando isso acontece, publish() retorna false silenciosamente e mensagens recebidas que não cabem são descartadas.

Chame setBufferSize() antes de conectar e verifique o valor de retorno de publish():

Tamanho do payload (aprox.) setBufferSize() sugerido
Até ~150 bytes 256 (padrão)
Até ~400 bytes 512
Até ~1.800 bytes 2048
Documentos maiores Divida a mensagem ou envie só os campos alterados

Cada byte extra sai da RAM do ESP32, então dimensione o buffer para as suas mensagens reais, e não para o máximo do broker.

Parte 2: ESP32 MQTT Seguro com TLS na Porta 8883

A área pública é perfeita para aprender, mas dispositivos de produção devem usar TLS e credenciais próprias. Entre em https://app.connectmq.com, crie um broker e copie o usuário, a senha e o client ID gerados. Clientes autenticados caem no seu próprio tenant isolado, e não na área pública.

Em conexões autenticadas, os client IDs ficam restritos ao ID emitido e às suas variantes com _sufixo. Dê a cada dispositivo seu próprio sufixo, por exemplo YOUR_CLIENT_ID_esp32a, e outro ao seu notebook.

Mudanças no sketch

Só mudam o cliente de rede, a porta, as credenciais e os topics. Baixe o certificado ISRG Root X1 (isrgrootx1.pem) da Let's Encrypt; a cadeia do certificado do broker chega até ele. Cole o conteúdo no sketch:

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <time.h>

const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 8883;
const char* MQTT_USER = "YOUR_USERNAME";
const char* MQTT_PASS = "YOUR_PASSWORD";
const char* MQTT_CLIENT_ID = "YOUR_CLIENT_ID_esp32a";

// ISRG Root X1 (cole o PEM completo de isrgrootx1.pem)
static const char ROOT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
...paste the certificate body here...
-----END CERTIFICATE-----
)EOF";

WiFiClientSecure netClient;
PubSubClient mqtt(netClient);

void syncClock() {
  configTime(0, 0, "pool.ntp.org", "time.nist.gov");
  time_t now = time(nullptr);
  while (now < 1700000000) { delay(250); now = time(nullptr); }
}

// no setup(), depois de connectWiFi():
//   syncClock();
//   netClient.setCACert(ROOT_CA);
//   mqtt.setServer(MQTT_HOST, MQTT_PORT);
//   base dos topics: "plant1/" + String(MQTT_CLIENT_ID) em vez de "connectmq/demo/..."

bool connectMqtt() {
  if (mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS)) {
    mqtt.subscribe(topicLedSet.c_str(), 1);
    publishLedState();
    return true;
  }
  return false;
}

Algumas observações:

  • setCACert() faz o ESP32 verificar se está realmente falando com o broker.connectmq.com. Sincronizar o relógio via NTP antes permite que a pilha TLS confira as datas de validade do certificado.
  • setInsecure() pula a verificação por completo. É prático para um teste de cinco minutos, mas remove a proteção contra ataques man-in-the-middle, então nunca o coloque em produção.
  • O TLS usa bem mais RAM e o primeiro handshake leva um instante. Isso é normal no ESP32.
  • Mantenha as credenciais fora do seu repositório Git, por exemplo em um secrets.h separado listado no .gitignore. O checklist de segurança MQTT aborda o armazenamento de segredos nos dispositivos e outras medidas de proteção.

Verifique a conexão segura

Assine com suas próprias credenciais e um sufixo diferente. No Linux, você pode usar o repositório de CAs do sistema com --capath /etc/ssl/certs; em qualquer sistema, pode passar o certificado raiz baixado:

mosquitto_sub -h broker.connectmq.com -p 8883 --cafile isrgrootx1.pem \
  -u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID_laptop \
  -t "plant1/#" -v

Envie um comando da mesma forma:

mosquitto_pub -h broker.connectmq.com -p 8883 --cafile isrgrootx1.pem \
  -u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID_cli \
  -t "plant1/YOUR_CLIENT_ID_esp32a/led/set" -q 1 -m "ON"

Você também pode abrir o cliente MQTT no navegador ou o explorador de topics ao vivo no portal do ConnectMQ para acompanhar a telemetria sem instalar nada.

Solução de Problemas

Sintoma Causa provável Solução
state=-2 em loop Sem internet, host errado ou porta bloqueada Verifique o Wi-Fi, teste a porta 1883, confira o firewall
state=2 Client ID rejeitado Use um ID único e não vazio; com credenciais, use o ID emitido ou uma variante _suffix
state=4 ou 5 Credenciais inválidas Copie de novo o usuário e a senha do portal
As publicações param sem erro aparente Mensagem maior que o buffer Aumente setBufferSize(), verifique o retorno de publish()
Desconexões constantes mqtt.loop() não é chamado com frequência, ou outro cliente usa o mesmo ID Remova delay() longos, dê a cada cliente seu próprio ID
TLS falha, TCP simples funciona CA errada, relógio não ajustado Use o ISRG Root X1, sincronize a hora via NTP antes de conectar

Comece com o ConnectMQ

Agora você tem um ESP32 que envia telemetria e recebe comandos via MQTT, primeiro na área pública e depois via TLS com credenciais próprias. Quando estiver pronto para ir além dos dados de demonstração, crie um broker MQTT gerenciado no plano Serverless gratuito em https://app.connectmq.com — sem cartão de crédito — e use o dashboard, a visão de dispositivos e os alertas por limite do portal para ficar de olho na sua frota.

Perguntas frequentes

Qual biblioteca MQTT devo usar no ESP32 com a Arduino IDE?

O PubSubClient é a escolha mais usada: é pequeno, estável e funciona tanto com WiFiClient quanto com WiFiClientSecure. Ele suporta MQTT 3.1.1 com publicação em QoS 0 e assinaturas em QoS 0 ou 1, o que cobre a maioria dos projetos com sensores.

Por que a publicação do meu ESP32 falha sem nenhum erro?

Na maioria das vezes, o pacote é maior que o buffer padrão de 256 bytes do PubSubClient. Chame mqtt.setBufferSize(512) ou mais antes de conectar e verifique o booleano retornado por publish().

Preciso de uma conta para testar ESP32 MQTT com o broker.connectmq.com?

Não. Conecte na porta 1883 ou 8883 sem usuário e com um client ID único para usar a área pública. Para dados reais, crie uma conta gratuita e use as credenciais geradas.

Posso usar setInsecure() com WiFiClientSecure?

Só para testes rápidos. Ele criptografa o tráfego, mas pula a verificação do servidor, então um atacante poderia se passar pelo broker. Use setCACert() com o ISRG Root X1 em tudo o que for implantado.

Como dou a várias placas ESP32 seus próprios client IDs em um mesmo broker?

Acrescente um sufixo ao client ID emitido no portal, como YOUR_CLIENT_ID_esp32a e YOUR_CLIENT_ID_esp32b. O ConnectMQ aceita o ID emitido e suas variantes com _suffix em conexões autenticadas.

Artigos relacionados

Melhores Ferramentas CLI MQTT para Testes, Scripts e Depuração

Uma comparação prática entre mosquitto_pub/sub, MQTTX CLI, HiveMQ MQTT CLI, a CLI do MQTT.js, curl e mqttui, com o mesmo teste rodando no broker.connectmq.com, para você escolher a ferramenta certa para scripts e depuração.

9 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.