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 UNO 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.1.16/24
- Arduino: 192.168.1.50/24
- Clientes (mosquitto_pub e mosquitto_sub): 192.168.1.16/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 "arduino/status" com mensagem "off-line"
(será publicada caso o Arduino seja desconectado involuntariamente);
- publica para o tópico "arduino/status" a mensagem "on-line" anunciando que está ativo;
- subscreve o tópico "arduino/led" para receber comandos para um led;
- caso receba mensagem para o tópico "arduino/led", aciona o led conforme comando recebido.
*/
#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, 1, 50);
IPAddress server(192, 168, 1, 16);
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
if ((char)payload[0] == '1') {
digitalWrite(4, HIGH);
} else if ((char)payload[0] == '0') {
digitalWrite(4, LOW);
}
}
void setup()
{
Serial.begin(57600);
pinMode(4, OUTPUT);
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 = "arduino/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("arduino/status", (byte*)message, length, retained);
// ... and resubscribe
client.subscribe("arduino/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
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.18/24
- 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:
/*
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);
}
}
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)