ESP32 e Arduino

Guia da Biblioteca ArduinoMqttClient: API, Polling, QoS e Mensagens Will

Um guia completo da biblioteca oficial ArduinoMqttClient: a API de conexão, por que o poll() importa, publicação e recepção em stream, flags de QoS e retain, mensagens will e todos os códigos de connectError().

Editor de código mostrando um sketch ArduinoMqttClient ao lado de uma placa Arduino publicando mensagens 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

O ArduinoMqttClient é a biblioteca MQTT oficial mantida pela Arduino e roda em tudo, do UNO R4 WiFi e do Nano 33 IoT às placas MKR e ao ESP32. Este guia percorre a API do ArduinoMqttClient na ordem em que você vai usá-la: configurar, conectar, fazer poll, publicar, receber e definir uma mensagem will. Todos os exemplos se conectam a um broker real, para você testar enquanto lê.

Por Que Escolher o ArduinoMqttClient

O ArduinoMqttClient envolve qualquer objeto Client do Arduino, como WiFiClient, WiFiSSLClient, EthernetClient ou um cliente celular, e acrescenta MQTT 3.1.1 por cima. Sua escolha de design mais marcante é tratar tanto as mensagens enviadas quanto as recebidas como streams, então você nunca precisa de um buffer do tamanho da sua maior mensagem.

Recurso ArduinoMqttClient
Protocolo MQTT 3.1.1
QoS de publicação 0, 1 e 2
QoS de inscrição 0, 1 e 2
Retained messages Sim
Mensagem will Sim, em stream com beginWill() / endWill()
Tratamento do payload Em stream (print() / read())
Modelo de eventos Polling com poll(), callback ao receber mensagem
Mantenedor Arduino

Se você já conhece o PubSubClient, o tutorial do PubSubClient para Arduino é um bom ponto de comparação; as duas bibliotecas resolvem o mesmo problema com escolhas diferentes.

Instalando a Biblioteca

Abra Tools → Manage Libraries na Arduino IDE, procure por "ArduinoMqttClient" e instale. Você também precisa da biblioteca de rede da sua placa:

Placa Biblioteca de rede Cliente sem criptografia Cliente TLS
UNO R4 WiFi WiFiS3 WiFiClient WiFiSSLClient
Nano 33 IoT, MKR WiFi 1010 WiFiNINA WiFiClient WiFiSSLClient
ESP32 WiFi (core) WiFiClient WiFiClientSecure
Placas com shield Ethernet Ethernet EthernetClient Não incluso

Configurando o Cliente

Crie primeiro o cliente de rede e depois passe-o para o MqttClient:

#include <WiFiNINA.h>          // ou WiFiS3.h, WiFi.h ...
#include <ArduinoMqttClient.h>

WiFiClient net;
MqttClient mqttClient(net);

Antes de chamar connect(), defina os parâmetros da sessão:

mqttClient.setId("acm-51e09b");                       // client ID, nunca vazio
mqttClient.setUsernamePassword("YOUR_USERNAME", "YOUR_PASSWORD"); // opcional
mqttClient.setKeepAliveInterval(30 * 1000L);          // milissegundos!
mqttClient.setConnectionTimeout(10 * 1000L);          // milissegundos
mqttClient.setCleanSession(true);

Atenção às unidades: diferentemente do PubSubClient, os valores de keep alive e de timeout são em milissegundos. Passar 30 em vez de 30000 vira um keep alive de zero segundos após a divisão inteira, o que desativa completamente o keep alive; assim, conexões mortas passam despercebidas pelo broker e a sua mensagem will nunca é disparada.

Conectando e Lendo o connectError()

connect(host, port) retorna 1 em caso de sucesso e 0 em caso de falha. Quando falha, connectError() informa o motivo:

connectError() Constante Significado e solução
-2 MQTT_CONNECTION_REFUSED O socket não pôde ser aberto: problema de Wi-Fi, DNS, porta ou TLS
-1 MQTT_CONNECTION_TIMEOUT Sem CONNACK a tempo: tipo de porta errado ou link ruim
0 MQTT_SUCCESS Conectado
1 MQTT_UNACCEPTABLE_PROTOCOL_VERSION O broker rejeitou o MQTT 3.1.1 (raro)
2 MQTT_IDENTIFIER_REJECTED Client ID vazio ou não permitido
3 MQTT_SERVER_UNAVAILABLE O broker não está aceitando conexões agora
4 MQTT_BAD_USER_NAME_OR_PASSWORD Credenciais erradas
5 MQTT_NOT_AUTHORIZED Cliente sem permissão, por exemplo quando o client ID não corresponde às credenciais

Uma função de conexão completa e reutilizável:

bool connectMqtt(const char* host, int port) {
  Serial.print("Connecting to ");
  Serial.print(host);
  Serial.print(':');
  Serial.println(port);

  if (!mqttClient.connect(host, port)) {
    Serial.print("MQTT connect failed, connectError = ");
    Serial.println(mqttClient.connectError());
    return false;
  }
  Serial.println("MQTT connected");
  return true;
}

Para testes, use o broker.connectmq.com na porta 1883, o servidor MQTT público (gratuito), pensado para testes e protótipos. Ele aceita conexões anônimas com qualquer client ID único e não vazio. Mantenha seus topics sob um prefixo único, como connectmq/demo/<your-id>/, e não publique nada sensível.

Por Que o poll() Importa

O poll() é o coração do ArduinoMqttClient. Cada chamada:

  1. Lê os bytes recebidos do cliente de rede e interpreta os pacotes completos.
  2. Chama o seu callback onMessage (ou disponibiliza a mensagem para parseMessage()).
  3. Responde aos handshakes de QoS 1 e QoS 2 das mensagens recebidas.
  4. Envia um PINGREQ quando o intervalo de keep alive está prestes a expirar.

Se o poll() não for chamado por mais tempo que o intervalo de keep alive, o broker fecha a conexão. Evite chamadas de delay() mais longas que uma fração do keep alive e substitua-as por temporização baseada em millis():

unsigned long lastSend = 0;

void loop() {
  mqttClient.poll();
  if (millis() - lastSend >= 10000) {
    lastSend = millis();
    // publique aqui
  }
}

Publicando Mensagens

Publicar é um stream em três etapas:

// Mensagem simples com QoS 0
mqttClient.beginMessage("connectmq/demo/acm-51e09b/temperature");
mqttClient.print(22.7);
mqttClient.endMessage();

// Retida, QoS 1
mqttClient.beginMessage("connectmq/demo/acm-51e09b/status", true, 1);
mqttClient.print("online");
mqttClient.endMessage();

A assinatura completa é beginMessage(topic, retain = false, qos = 0, dup = false). Há também uma sobrecarga que recebe o tamanho do payload de antemão, beginMessage(topic, size, retain, qos, dup). Quando você souber o tamanho exato, use-a: assim a biblioteca pode gravar os dados direto na rede, em vez de armazenar a mensagem inteira primeiro, o que faz diferença com payloads grandes em placas com pouca RAM.

endMessage() retorna 1 em caso de sucesso. Para QoS 1 e 2, a biblioteca trata os pacotes de confirmação nas chamadas seguintes de poll().

Como print() aceita números, strings e floats, você pode montar JSON sem buffer temporário:

mqttClient.beginMessage(topicTelemetry);
mqttClient.print("{\"t\":");
mqttClient.print(temperature, 2);
mqttClient.print(",\"h\":");
mqttClient.print(humidity, 1);
mqttClient.print("}");
mqttClient.endMessage();

Recebendo Mensagens

Inscreva-se após uma conexão bem-sucedida e de novo a cada reconexão, quando você usa clean session:

mqttClient.onMessage(onMqttMessage);
mqttClient.subscribe("connectmq/demo/acm-51e09b/cmd/#", 1);

O callback recebe apenas o tamanho da mensagem. Você lê o topic e consome o payload como stream:

void onMqttMessage(int messageSize) {
  String topic = mqttClient.messageTopic();
  Serial.print("Topic: ");
  Serial.print(topic);
  Serial.print(", QoS ");
  Serial.print(mqttClient.messageQoS());
  Serial.print(", retained ");
  Serial.println(mqttClient.messageRetain());

  char payload[128];
  int n = 0;
  while (mqttClient.available() && n < (int)sizeof(payload) - 1) {
    payload[n++] = (char)mqttClient.read();
  }
  payload[n] = '\0';
  Serial.println(payload);
}

Os bytes que você não ler são descartados quando a próxima mensagem chega. Isso torna seguras as mensagens grandes demais: basta parar de ler no limite do seu buffer.

Como alternativa ao callback, você pode chamar int size = mqttClient.parseMessage(); em loop() e tratar as mensagens ali mesmo quando size for maior que zero. Use um estilo ou o outro, não os dois.

Mensagens Will

Uma mensagem will (Last Will and Testament) é publicada pelo broker se o seu dispositivo se desconectar sem se despedir, por exemplo após uma queda de energia na fazenda ou perda do Wi-Fi no galpão. Configure-a antes de connect():

const char willTopic[] = "connectmq/demo/acm-51e09b/status";

mqttClient.beginWill(willTopic, true, 1);   // retida, QoS 1
mqttClient.print("offline");
mqttClient.endWill();

if (connectMqtt("broker.connectmq.com", 1883)) {
  mqttClient.beginMessage(willTopic, true, 1);
  mqttClient.print("online");
  mqttClient.endMessage();
}

Publicar um online retido no mesmo topic logo após conectar dá a todos os dashboards um indicador de presença preciso: online enquanto conectado e offline assim que o broker detecta que o dispositivo sumiu, o que acontece após cerca de um intervalo e meio de keep alive.

Um Padrão Completo e Seguro para Reconexões

Junte as peças com este checklist:

  1. Conecte o Wi-Fi e espere um endereço IP válido.
  2. Defina ID, credenciais, keep alive e will.
  3. Conecte; em caso de falha, imprima connectError() e tente de novo após um intervalo que cresce a cada falha.
  4. Em caso de sucesso, registre o onMessage, faça as inscrições e publique online retido.
  5. Em loop(), verifique o Wi-Fi, verifique mqttClient.connected(), chame poll() e publique com um timer baseado em millis().

Versões deste padrão específicas para cada placa estão no tutorial Arduino UNO R4 WiFi MQTT e no guia de MQTT com Nano 33 IoT e WiFiNINA. Para um projeto mais amplo, o tutorial IoT com ESP32 mostra como o lado do dispositivo se integra a dashboards e alertas.

Erros Comuns com o ArduinoMqttClient

  • Chamar beginWill() depois de connect(). O will faz parte do pacote CONNECT, então precisa ser configurado antes.
  • Inscrever-se apenas no setup(). Após uma reconexão com clean session, o broker esqueceu suas inscrições. Inscreva-se na sua função de conexão.
  • Reutilizar um client ID em várias placas. O broker desconecta a sessão mais antiga toda vez que outra placa se conecta, gerando um loop de reconexão sem fim.
  • Publicar de dentro do onMessage. Funciona, mas termine primeiro de ler o payload recebido; caso contrário, os bytes não lidos se perdem.
  • Esquecer o endMessage(). Sem ele, nada é enviado, e a próxima chamada de beginMessage() falha.

Produção: TLS e Credenciais

Em implantações reais, troque para um cliente TLS e a porta 8883 e autentique-se com suas próprias credenciais. Um broker MQTT gerenciado como o ConnectMQ emite usuário, senha e client ID para cada broker que você cria no portal; o client ID fica vinculado a essas credenciais, então defina-o exatamente com setId(). O certificado do ConnectMQ vem da Let's Encrypt, em que os módulos Wi-Fi das placas Arduino atuais confiam de fábrica. Revise as boas práticas de segurança MQTT antes de colocar em produção.

Comece a Usar o ConnectMQ

O ConnectMQ é um broker MQTT gerenciado, ou seja, um broker MQTT na nuvem com MQTT 3.1.1 e 5.0, TLS e WebSockets, além de um portal que inclui um cliente MQTT no navegador, um explorador de topics ao vivo e alertas por limite. É a forma mais fácil de ver o que o seu sketch com ArduinoMqttClient está realmente enviando. Crie um broker Serverless gratuito em app.connectmq.com, sem precisar de cartão de crédito.

Perguntas frequentes

O ArduinoMqttClient é melhor que o PubSubClient?

Nenhum dos dois é melhor em todos os casos. O ArduinoMqttClient suporta publicação com QoS 1 e 2 e envia payloads em stream, enquanto o PubSubClient é menor e tem uma enorme base de exemplos. Escolha o ArduinoMqttClient para placas Arduino oficiais e quando precisar de QoS.

O ArduinoMqttClient suporta MQTT 5?

Não, ele implementa MQTT 3.1.1. Brokers modernos atendem clientes 3.1.1 e 5.0 lado a lado, então o seu dispositivo ainda pode compartilhar topics com aplicações MQTT 5.

Por que minha conexão com o ArduinoMqttClient cai a cada minuto?

Normalmente o poll() não é chamado com frequência suficiente, o keep alive foi definido em segundos em vez de milissegundos ou outro cliente usa o mesmo client ID.

Qual é o tamanho máximo de uma mensagem?

A biblioteca envia os payloads em stream, então o limite é principalmente o do seu broker. O servidor MQTT público em broker.connectmq.com aceita até 64 KB por mensagem; o plano Serverless gratuito permite 256 KB.

Artigos relacionados

Exemplo de MQTT com ESP-IDF: Monte um Cliente esp-mqtt do Zero

Monte um projeto ESP-IDF a partir de uma pasta vazia com o componente esp-mqtt: configuração do Wi-Fi em modo station, o handler de eventos MQTT, publicação e inscrição, propriedades do MQTT 5 e TLS com o bundle de certificados.

8 min de leitura

Usando os Recursos do MQTT 5 do esp-mqtt no ESP32 com ESP-IDF

Um projeto ESP-IDF prático que ativa o MQTT 5.0 no esp-mqtt e usa user properties, expiração de mensagens, topic aliases, expiração de sessão e response topics com um broker MQTT 5 real.

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.