Arduino: MQTT
MQTT e Arduino
O suporte para MQTT para Arduíno é provido por uma biblioteca que pode ser obtida em: https://github.com/knolleary/pubsubclient .
Uma boa descrição do uso da biblioteca PubSubClient.h pode ser encontrada em: [1]
Para interagir com a rede é necessário um shild Ethernet colocado sobre a placa Arduíno.
- Limitações do MQTT para Arduíno
- Suporta somente publicações com QoS=0 e subscrições com QoS=0 ou QoS=1.
- Suporta somente CleanSessions devido a limitação de memória.
- Tamanho máximo de mensagem com 128 bytes por default.
- Intervalo de keep alive de 15 s por default.
- Os dois últimos parâmetros podem ser alterados na biblioteca PubSubClient.h:
- MQTT_MAX_PACKET_SIZE
- MQTT_KEEPALIVE
- Hardware compatível
- Shield Ethernet
- Arduíno Ethernet
- Arduíno Yun
- Arduíno Wifi
- ESP8266
- ESP32
Experimento: MQTT e Arduino
Neste experimento foi utilizado um Arduino Leonardo com um Shield Ethernet, rodando a biblioteca PubSubClient.h para interagir um com brocker Mosquitto e clientes publicadores e subscritores através do protocolo MQTT.
A comunicação foi realizada através de uma rede local residencial e foram utilizados os seguintes endereços IP:
- brocker Mosquito: 192.168.0.13/24
- Arduino: 192.168.0.30/24
- Clientes (mosquitto_pub e mosquitto_sub): 192.168.0.21/24
O código Arduino abaixo descreve o cenário da aplicação e ilustra o uso das funções da biblioteca PubSubClient.h para implementar a comunicação MQTT.
/*
Baseado no exemplo da bilbioteca PubSubClient.h: Basic MQTT example
Exemplo: MQTT com Arduíno/Shield Ethernet e Mosquitto:
Anunciar status do Arduino, receber comando para led e publicar status do led.
Este sketch demonstra a comunicação de um Arduino com um brocker Mosquitto usando MQTT:
- conecta ao brocker e informa lastWill para o tópico "status" com mensagem "off-line"
(será publicada caso o Arduino seja desconectado involuntariamente);
- publica para o tópico "status" a mensagem "on-line" anunciando que está ativo;
- subscreve o tópico "led" para receber comandos para um led;
- caso receba mensagem para o tópico "led", aciona o led conforme comando recebido
e publica para o tópico "ledStatus" o estado do led após o comando.
*/
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
// Endereçamento IP utilizado para o cliente e servidor
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
IPAddress ip(192, 168, 0, 30);
IPAddress server(192, 168, 0, 13);
EthernetClient ethClient;
PubSubClient client(ethClient);
//Função callback chamada quando uma mensagem for recebida para subscrições:
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i=0;i<length;i++) {
Serial.print((char)payload[i]);
}
Serial.println();
//comanda o "led" e publica "status"
if (length == 2 and !strncmp((char*)payload, "on", length)) {
digitalWrite(LED_BUILTIN, HIGH);
client.publish("status", "ON_LINE: led ON");
} else if (length == 3 and !strncmp((char*)payload, "off", length)) {
digitalWrite(LED_BUILTIN, LOW);
client.publish("status", "ON_LINE: led OFF");
} else {
client.publish("status", "ON_LINE: led UNCHANGED");
}
}
void setup()
{
Serial.begin(57600);
client.setServer(server, 1883);
client.setCallback(callback);
Ethernet.begin(mac, ip);
delay(5000); // Allow the hardware to sort itself out
delay(10000);
}
void loop(){
// Aguarda conexão
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
//Mensagem lastWill
byte willQoS = 0;
const char* willTopic = "status";
const char* willMessage = "OFF_LINE";
boolean willRetain = true;
//Conexão
if (client.connect("arduinoClient", willTopic, willQoS, willRetain, willMessage)) {
Serial.println("connected");
//Uma vez conectado publica status
char* message = "ON_LINE";
int length = strlen(message);
boolean retained = true; //Retain message
client.publish("status", (byte*)message, length, retained);
// ... and resubscribe
client.subscribe("led");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
//Uma vez conectado client.loop() deve ser chamada periodicamente para manter conexão
//e aguardar recebimento de mensagens
client.loop();
}
Análise da troca de mensagens com Wireshark
- Análise das mensagens trocadas
- Na marca de tempo 5.6... um cliente se conectou ao brocker e subscreveu o tópico "status".
- Na marca de tempo 36.3... o Arduíno se conectou ao brocker e informou tópico/mensagem lastWill ("status = OFF_LINE"), publicou o tópico "status = ON_LINE" e subscreveu o tópico "led". O brocker também publicou o tópico "status" para o cliente.
- Na marca de tempo 42.6... outro cliente se conectou ao brocker e publicou o tópico "led = ON". O brocker também publicou o tópico "led = ON" ao Arduino (que acionou um led) e publicou ao cliente o tópico "status = ON_LINE: led ON"
- Em seguida o Arduíno foi desligado.
- Na marca de tempo 65.6..., como o brocker não recebeu do Arduíno um PINGREC, ele supos que o Arduino foi desconectado involuntariamente e publicou ao cliente o tópico "status = OFF_LINE".
- Observação
- As mensagens em preto, com indicação de TCP ACKED unseen segment, como indicado em [2], "se referem a pacotes que foram transferidos e reconhecidos, mas que não foram capturados pelo Wireshark. Usualmente isto acontece quando o dispositivo não é rápido o suficiente". O que pede ser o caso do Arduino.
MQTT e ESP8266
O suporte para MQTT para o ESP8266 é provido por uma biblioteca que pode ser obtida em: https://github.com/knolleary/pubsubclient .
Neste experimento foi utilizado um NodeMCU 8266 utilizando a biblioteca PubSubClient.h para interagir um com brocker Mosquitto e clientes publicadores e subscritores através do protocolo MQTT.
A comunicação foi realizada através de uma rede local residencial e foram utilizados os seguintes endereços IP:
- brocker Mosquito: 192.168.0.14/24
- Arduino: 192.168.0.20/24
- Clientes (mosquitto_pub e mosquitto_sub): 192.168.0.14/24
O código abaixo é baseado no exemplo mqtt_esp8266 da biblioteca PubSubClient.h:
<syntaxhighlight lang="c"> /*
Basic ESP8266 MQTT example This sketch demonstrates the capabilities of the pubsub library in combination with the ESP8266 board/library. It connects to an MQTT server then: - publishes "hello world" to the topic "outTopic" every two seconds - subscribes to the topic "inTopic", printing out any messages it receives. NB - it assumes the received payloads are strings not binary - If the first character of the topic "" is an 1, switch ON the ESP Led, else switch it off It will reconnect to the server if the connection is lost using a blocking reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to achieve the same result without blocking the main loop. To install the ESP8266 board, (using Arduino 1.6.4+): - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs": http://arduino.esp8266.com/stable/package_esp8266com_index.json - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266" - Select your ESP8266 in "Tools -> Board"
- /
- include <ESP8266WiFi.h>
- include <PubSubClient.h>
// Update these with values suitable for your network.
const char* ssid = "BAMBUZAL 2.4G"; const char* password = "aaaaabbbbb"; const char* mqtt_server = "192.168.0.14"; //"broker.mqtt-dashboard.com";
WiFiClient espClient; PubSubClient client(espClient); unsigned long lastMsg = 0;
- define MSG_BUFFER_SIZE (50)
char msg[MSG_BUFFER_SIZE]; int value = 0;
void setup_wifi() {
delay(10); // We start by connecting to a WiFi network Serial.println(); Serial.print("Connecting to "); Serial.println(ssid);
WiFi.mode(WIFI_STA); WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
randomSeed(micros());
Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println();
// Switch on the LED if an 1 was received as first character if ((char)payload[0] == '1') { digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level // but actually the LED is on; this is because // it is active low on the ESP-01) } else { digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH }
}
void reconnect() {
// Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Create a random client ID String clientId = "ESP8266Client-"; clientId += String(random(0xffff), HEX); // Attempt to connect if (client.connect(clientId.c_str())) { Serial.println("connected"); // Once connected, publish an announcement... client.publish("outTopic", "hello world"); // ... and resubscribe client.subscribe("inTopic"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } }
}
void setup() {
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output Serial.begin(115200); setup_wifi(); client.setServer(mqtt_server, 1883); client.setCallback(callback);
}
void loop() {
if (!client.connected()) { reconnect(); } client.loop();
unsigned long now = millis(); if (now - lastMsg > 2000) { lastMsg = now; ++value; snprintf (msg, MSG_BUFFER_SIZE, "hello world #%ld", value); Serial.print("Publish message: "); Serial.println(msg); client.publish("outTopic", msg); }
} </syntaxhighlight lang>
Referências
- ↑ Nick O’Leary. Arduino PubSubClient - MQTT Client Library Encyclopedia, September 13, 2015 https://www.hivemq.com/blog/mqtt-client-library-encyclopedia-arduino-pubsubclient/
- ↑ https://osqa-ask.wireshark.org/questions/46134/tcp-acked-unseen-segment
Evandro.cantu (discussão) 10h27min de 16 de abril de 2020 (-03)