MQTT gerenciado na nuvem

Conecte um ESP32 a um Broker MQTT na Nuvem via TLS

Guia passo a passo para migrar um projeto ESP32 de um broker Mosquitto local para um broker MQTT na nuvem via TLS, com username, senha e certificado validado.

Ilustração de broker MQTT na nuvem com ESP32 mostrando uma placa ESP32 enviando telemetria criptografada via TLS para um broker na nuvem
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

Conectar um ESP32 a um broker MQTT na nuvem via TLS é o passo que transforma um protótipo de bancada em algo que você pode implantar fora da sua rede doméstica, seja em um galpão de logística, em uma estação de bombeamento de saneamento ou em uma fazenda. Neste tutorial você vai migrar um sketch ESP32 de um broker Mosquitto local na porta 1883 para um broker Serverless da ConnectMQ na porta 8883, com validação de certificado adequada, credenciais, um topic de status e reconexões confiáveis.

O Que Muda Quando Você Deixa o Broker Local

A maioria dos projetos com ESP32 começa com o Mosquitto em um Raspberry Pi ou notebook: TCP simples na 1883, sem autenticação, acessível apenas na sua LAN. Migrar para um broker MQTT na nuvem muda quatro coisas:

Aspecto Broker local Broker na nuvem
Host 192.168.1.20 broker.connectmq.com
Porta 1883 (TCP simples) 8883 (TLS)
Autenticação Muitas vezes nenhuma Username, senha, client ID
Cliente de rede WiFiClient WiFiClientSecure
Certificado Nenhum Certificado do servidor validado contra uma CA
Alcance Somente LAN Qualquer lugar com internet

A lógica MQTT em si, topics, publicações e assinaturas, continua a mesma. Se você nunca conectou um ESP32 ao MQTT antes, comece pelo tutorial de MQTT com ESP32 e depois volte aqui.

Pré-requisitos

  • Uma placa de desenvolvimento ESP32 e um cabo USB.
  • Arduino IDE 2.x ou PlatformIO com o core Arduino do ESP32 instalado (veja a documentação oficial do core Arduino da Espressif).
  • A biblioteca PubSubClient, de Nick O'Leary.
  • Um broker Serverless gratuito. Cadastre-se no portal, escolha o plano Serverless (Free), crie um broker e copie o username, a senha e o client ID gerados.

Passo 1: Obtenha o Certificado da CA Raiz

O broker serve um certificado Let's Encrypt. O ESP32 precisa confiar na raiz que assinou a cadeia. A cadeia do Let's Encrypt sobe até a ISRG Root X1, então esse é o certificado a embutir. Você pode confirmar a cadeia por conta própria em qualquer máquina com OpenSSL:

openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -showcerts </dev/null

Observe as linhas issuer no topo da saída. Em seguida, baixe a ISRG Root X1 em formato PEM na página de certificados do Let's Encrypt e cole-a no seu sketch como uma raw string. Embutir a raiz de longa duração, em vez do certificado do próprio servidor, significa que você não precisa regravar os dispositivos quando o certificado do servidor for renovado, a cada poucos meses.

Passo 2: O Sketch Completo

Este sketch conecta ao Wi-Fi, sincroniza o horário (a validação de certificados TLS precisa de um relógio correto), conecta ao broker via TLS com credenciais, publica um status online retido com um Last Will, assina um topic de comandos e publica telemetria a cada dez segundos.

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

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

// ---- ConnectMQ ----
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";

const char* TOPIC_TELEMETRY = "devices/esp32-01/telemetry";
const char* TOPIC_STATUS    = "devices/esp32-01/status";
const char* TOPIC_COMMAND   = "devices/esp32-01/cmd";

// ISRG Root X1 (cole aqui o PEM completo do Let's Encrypt)
static const char ROOT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
PASTE_ISRG_ROOT_X1_PEM_CONTENT_HERE
-----END CERTIFICATE-----
)EOF";

WiFiClientSecure net;
PubSubClient mqtt(net);

unsigned long lastPublish = 0;
unsigned long lastReconnectAttempt = 0;
unsigned long reconnectDelay = 1000;

void connectWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.printf("\nWi-Fi connected, IP %s\n", WiFi.localIP().toString().c_str());
}

void syncTime() {
  configTime(0, 0, "pool.ntp.org", "time.google.com");
  Serial.print("Syncing time");
  time_t now = time(nullptr);
  while (now < 1700000000) {
    delay(500);
    Serial.print(".");
    now = time(nullptr);
  }
  Serial.println(" done");
}

void onMessage(char* topic, byte* payload, unsigned int length) {
  String msg;
  for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
  Serial.printf("Command on %s: %s\n", topic, msg.c_str());
  if (msg == "led:on")  digitalWrite(LED_BUILTIN, HIGH);
  if (msg == "led:off") digitalWrite(LED_BUILTIN, LOW);
}

bool connectMqtt() {
  Serial.print("Connecting to ConnectMQ... ");
  bool ok = mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS,
                         TOPIC_STATUS, 1, true, "offline");
  if (!ok) {
    Serial.printf("failed, state=%d\n", mqtt.state());
    return false;
  }
  Serial.println("connected");
  mqtt.publish(TOPIC_STATUS, "online", true);
  mqtt.subscribe(TOPIC_COMMAND, 1);
  return true;
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  connectWiFi();
  syncTime();

  net.setCACert(ROOT_CA);
  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  mqtt.setCallback(onMessage);
  mqtt.setKeepAlive(30);
  mqtt.setBufferSize(512);
}

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

  if (!mqtt.connected()) {
    unsigned long now = millis();
    if (now - lastReconnectAttempt > reconnectDelay) {
      lastReconnectAttempt = now;
      if (connectMqtt()) {
        reconnectDelay = 1000;
      } else {
        reconnectDelay = min(reconnectDelay * 2, 60000UL);
      }
    }
    return;
  }

  mqtt.loop();

  if (millis() - lastPublish > 10000) {
    lastPublish = millis();
    char payload[128];
    snprintf(payload, sizeof(payload),
             "{\"uptime_s\":%lu,\"rssi\":%d,\"heap\":%u}",
             millis() / 1000, WiFi.RSSI(), ESP.getFreeHeap());
    mqtt.publish(TOPIC_TELEMETRY, payload);
    Serial.println(payload);
  }
}

Observações sobre as escolhas:

  • Status retido mais Last Will. O broker publica offline em seu nome se o ESP32 cair sem uma desconexão limpa, e novos assinantes sempre veem o último status. Mais sobre esse padrão em Last Will MQTT no ESP32.
  • Backoff não bloqueante. As tentativas de reconexão dobram de um segundo até um minuto, para que uma frota não martele o broker após uma indisponibilidade.
  • Tamanho do buffer. O buffer padrão de 256 bytes do PubSubClient descarta silenciosamente mensagens maiores; 512 bytes dão folga.
  • Algumas placas não têm LED_BUILTIN. Defina-o como o pino do LED da sua placa, muitas vezes o GPIO 2.

Passo 3: Verifique a Partir do Seu Computador

Abra o cliente no navegador ou o explorador de topics do portal e assine devices/#. Você deve ver telemetria a cada dez segundos e online retido no topic de status. Para enviar um comando pela linha de comando:

mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
  -u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID-cli \
  -t devices/esp32-01/cmd -m 'led:on' -q 1

Use um client ID diferente do usado pelo ESP32. Se o seu plano vincula um único client ID às credenciais, use o cliente no navegador do portal ou crie credenciais adicionais para as ferramentas.

Testando Sem Conta

Para experimentar o sketch antes de se cadastrar, use o servidor MQTT público gratuito, que serve apenas para testes: mantenha a porta 8883 e a mesma CA raiz, passe username e senha vazios (mqtt.connect(clientId)), escolha qualquer client ID único e não vazio e prefixe todos os topics com algo exclusivo, como connectmq/demo/x8q2m/esp32-01/.... A área pública pode ser lida por todos, então não publique nada sensível, e observe seus limites: 64 KB por mensagem e 10 mensagens por segundo por cliente.

Solução de Problemas

Sintoma Causa provável Correção
state=-2 imediatamente Falha no handshake TLS Verifique a CA raiz, a sincronização de horário e a porta 8883
Trava na sincronização de horário NTP bloqueado Libere a UDP 123 ou use um servidor NTP acessível
state=4 Username ou senha incorretos Copie novamente as credenciais do portal
state=5 Não autorizado O client ID não corresponde às credenciais emitidas
Conecta e cai em loop Client ID duplicado Garanta que cada dispositivo e ferramenta use o próprio ID
Mensagens grandes nunca chegam Buffer do PubSubClient pequeno demais Aumente o setBufferSize
Funciona em casa, falha no local Saída na 8883 bloqueada Peça à TI para liberar a 8883 ou use WSS na 8084 com um cliente MQTT sobre WebSocket

Somente durante o desenvolvimento, net.setInsecure() pula a validação do certificado e ajuda a isolar se o problema é a CA. Nunca entregue firmware com isso. Para problemas de TLS mais profundos, o guia de MQTT com SSL no ESP32 vai além.

Memória, Energia e Ciclo de Vida do Certificado

O TLS em um ESP32 funciona bem, mas não é gratuito, e algumas decisões de design evitam problemas depois.

Uso de heap. Uma sessão TLS precisa de uma fatia considerável de RAM para buffers e para o handshake. Em um ESP32 comum isso é tranquilo para uma conexão, mas se o seu sketch também roda um servidor web, uma câmera ou documentos JSON grandes, acompanhe o ESP.getFreeHeap() no payload de telemetria acima. Um valor que cai continuamente geralmente aponta para um vazamento no seu próprio código, como concatenação de String dentro de um loop, e não na stack TLS.

Mantenha a conexão aberta. O handshake é a parte cara. Conectar, publicar uma mensagem e desconectar a cada poucos segundos custa muito mais energia e tempo do que manter uma sessão aberta com keep alive de 30 a 60 segundos.

Dispositivos a bateria. Se a placa, como um sensor de umidade do solo no campo, entra em deep sleep entre as leituras, cada despertar paga pela associação ao Wi-Fi, pelo NTP e por um handshake TLS. Reduza o custo acordando com menos frequência e agrupando várias leituras em uma única publicação, e considere guardar o último horário NTP na memória RTC para pular uma sincronização completa em sonos curtos.

Expiração do certificado raiz. Embutir a CA raiz em vez do certificado folha do servidor significa que as renovações de rotina nunca afetam seus dispositivos. As raízes acabam mudando, porém, então mantenha uma forma de atualizar o certificado em campo, seja por atualizações de firmware OTA, seja armazenando o PEM na flash, onde um fluxo de provisionamento possa substituí-lo.

Checklist de Produção

  1. Armazene as credenciais na NVS ou provisione-as na fabricação, nunca no controle de versão.
  2. Dê a cada dispositivo o seu próprio client ID.
  3. Use a CA raiz em vez de um certificado folha.
  4. Use backoff de reconexão com jitter.
  5. Publique um status retido com um Last Will.
  6. Mantenha os payloads pequenos e estruturados.
  7. Acompanhe os dispositivos no dashboard do portal e defina regras de alerta por limite para as métricas principais.

O panorama mais amplo de segurança está em boas práticas de segurança MQTT.

Coloque Seu ESP32 na ConnectMQ

Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito, cole as credenciais no sketch acima e seu ESP32 estará conversando com a nuvem via TLS. A ConnectMQ é um broker MQTT gerenciado, então certificados, patches e capacidade ficam por nossa conta enquanto você se concentra no firmware.

Perguntas frequentes

Por que meu ESP32 precisa do horário correto para o TLS?

A validação de certificados verifica as datas de validade. Sem NTP, o relógio do ESP32 começa em 1970, todo certificado parece inválido e o handshake falha.

Posso usar o AsyncMqttClient em vez do PubSubClient?

Sim. O AsyncMqttClient é não bloqueante e suporta TLS dependendo do seu core e das opções de build, enquanto o PubSubClient com WiFiClientSecure é a configuração mais simples e amplamente usada.

Qual certificado raiz devo embutir para a ConnectMQ?

A ISRG Root X1, a raiz do Let's Encrypt. Ela tem longa duração, então as renovações do certificado do servidor não exigem atualizações de firmware.

O TLS deixa o ESP32 perceptivelmente mais lento?

O handshake leva um momento e usa RAM extra, mas, uma vez conectado, o overhead por mensagem é pequeno. Mantenha a conexão aberta em vez de reconectar a cada publicação.

Artigos relacionados

Broker MQTT BYOC: Bring Your Own Cloud Explicado

Um broker MQTT BYOC roda na sua conta de nuvem, mas é operado por um provedor. Entenda como o modelo funciona, a divisão de responsabilidades e como preparar a sua conta para ele.

9 min de leitura

Alternativa ao Google Cloud IoT Core: Opções e Migração Após o Encerramento

O Google desativou o Cloud IoT Core em agosto de 2023. Este guia compara as alternativas realistas, mapeia os conceitos do IoT Core para MQTT puro e mostra como mover dispositivos para um broker MQTT gerenciado mantendo os dados fluindo para o Google Cloud.

9 min de leitura

Mensagens MQTT por Segundo: Como Estimar e Medir a Vazão

O planejamento de vazão em MQTT se resume a algumas fórmulas e a uma medição honesta. Aprenda a estimar mensagens de entrada e saída por segundo, considerar os picos e associar o resultado a um plano de broker.

8 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.