O MQTT (Message Queuing Telemetry Transport) é um protocolo de comunicação leve e eficiente projetado para facilitar a troca de mensagens entre dispositivos e sistemas distribuídos.
O MQTT opera sob um modelo de publicação e assinatura, permitindo que dispositivos enviem mensagens (publicações) para tópicos específicos, enquanto outros dispositivos podem monitorar (assinar) de maneira eficiente. Essas características fazem do MQTT uma escolha popular em aplicações de Internet das Coisas (IoT).
Neste blog, vamos ver como montar uma rede de troca de mensagens utilizando o MQTT com ESP32. Um ESP32 irá escrever em um tópico e outro irá ler esse tópico e acionar relés com base nos valores.
Sumário
1. Topologia da rede MQTT
Para funcionar o MQTT precisa de uma entidade central, chamada de MQTT broker. Ele é um servidor que hospeda as informações utilizadas para realizar as trocas de mensagem entre os clientes MQTT. De modo que os clientes MQTT vão ler ou escrever nos tópicos armazenados no MQTT broker. Na figura a seguir podemos ver uma representação desta topologia.
Na figura, podemos ver 4 clientes trocando mensagens com o MQTT broker. Perceba que um deles está apenas assinando (lendo), enquanto dois estão publicando (escrevendo), e um está desempenhando ambas as funções. Durante o processo, qualquer cliente pode publicar ou assinar qualquer tópico. Isso permite que os dados sejam trocados livremente entre os clientes.
Por exemplo, um cliente pode publicar o valor lido por um sensor, enquanto outro está lendo esse valor para exibir em um display, e outro está utilizando o valor para acionar algum dispositivo.
É comum utilizarmos computadores ou dispositivos similares como MQTT broker. No entanto, neste blog, vamos mostrar como utilizar um ESP32 como MQTT broker. Dessa forma, eliminamos a necessidade de um computador no projeto, o que reduz consideravelmente os custos de implementação. Na figura a seguir podemos ver a topologia que será implementada.
Nessa topologia, um cliente (publisher) irá controlar os relés de outro cliente (subscriber). Para isso, o editor vai publicar no tópico os níveis lógicos que cada relé deve assumir. O cliente subscriber irá assinar esse tópico e, com base nos valores recebidos, ligar ou desligar os relés.
Também teremos um quarto dispositivo, denominado MQTT Client Viewer. Ele é um programa que será executado no computador e permitirá visualizar e publicar em qualquer tópico presente no MQTT broker. Neste projeto, o MQTT Client Viewer será uma excelente ferramenta para testar se cada dispositivo está funcionando corretamente.
2. MQTT Broker
Como ponto de partida para transformar o ESP32 em um MQTT Broker, vamos utilizar o exemplo simplebroker da biblioteca sMQTTBroker. O primeiro passo é instalar a biblioteca na IDE, para isso vá em Ferramentas e depois em Gerenciar Bibliotecas, como mostrado na figura a seguir.
Na janela que irá abrir pesquise por sMQTTBroker e clique em instalar.
Para abrir o exemplo que vamos utilizar de base vá em, Arquivo, depois Exemplos, sMQTTBroker e simplebroker. Como mostrado na figura a seguir.
No exemplo padrão o ESP32 irá conectar-se a rede WiFi, porém, com IP dinâmico. Nesse projeto vamos utilizar IP fixo para o MQTT Broker. Para isso vamos adicionar as seguintes linhas de código no exemplo.
IPAddress local_IP(192, 168, 8, 115);
IPAddress gateway(192, 168, 8, 1);
IPAddress subnet(255, 255, 0, 0);
O IP deve ser definido de acordo com o padrão da sua rede. Para saber o padrão você pode entrar no cmd e utilizar o comando ipconfig, ou então conectar o ESP32 na rede WiFi com IP dinâmico e utilizar o IP atribuir a ela como fixo na hora de programar.
Neste exemplo utilizamos o CMD, na figura a seguir podemos ver o valor obtido. Por isso, o IP definido para o broker foi 192.168.8.115.
Dentro da função setup utilizamos o seguinte código para fixar o IP do dispositivo.
if (!WiFi.config(local_IP, gateway, subnet)) {
Serial.println("STA Failed to configure");
}
Agora é necessário configurar a rede WiFi. Dentro do SETUP temos as seguintes linhas de código, onde devemos adicionar as informações da rede.
const char* ssid = "SSID"; // The SSID (name) of the Wi-Fi network...
const char* password = "PASSWORD"; // The password of the Wi-Fi network
O código completo do MQTT Broker pode ser visto a seguir.
#include"sMQTTBroker.h"
sMQTTBroker broker;
IPAddress local_IP(192, 168, x, x); //Defina o IP de acordo com a sua rede
IPAddress gateway(192, 168, x, x);
IPAddress subnet(255, 255, 0, 0);
void setup()
{
Serial.begin(115200);
if (!WiFi.config(local_IP, gateway, subnet)) {
Serial.println("STA Failed to configure");
}
const char* ssid = "SSID"; // The SSID (name) of the Wi-Fi network you want to connect
const char* password = "PASSWORD"; // The password of the Wi-Fi network
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
delay(1000);
}
Serial.println("Connection established!");
Serial.print("IP address:\t");
Serial.println(WiFi.localIP());
const unsigned short mqttPort = 1883;
broker.init(mqttPort);
// all done
}
void loop()
{
broker.update();
}
Após carregar o código no ESP32, se tudo estiver correto o seguinte resultado deve ser obtido no monitor serial.
Agora vamos configurar o MQTT Client Viewer, para testar se o broker está funcionando corretamente. Para isso utilizamos o MQTT Explorer, que pode ser baixado aqui. Descendo um pouco no site encontramos a seção de download, aqui você deve escolher a opção compatível com o seu sistema operacional. Utilizaremos a opção portable para windows, como mostrado na figura a seguir.
Agora, podemos executar o MQTT Explorer. O primeiro passo consiste em configurar a conexão com o broker MQTT. Para isso, precisamos fornecer ao programa o endereço IP do dispositivo e, em seguida, clicar no botão "Conectar", conforme ilustrado na figura abaixo.
Se tudo estiver correto devemos obter o resultado mostrado na figura a seguir.
Através da aba “Publish”, é possível realizar publicações no MQTT Broker. Para isso, primeiramente, informamos o nome do tópico e, em seguida, inserimos os dados que serão publicados neste tópico. Na figura a seguir, é apresentado um exemplo dessa operação.
Agora que o MQTT Broker já está funcionando podemos configurar os clientes.
3. MQTT Client Publisher (Publicador)
Agora vamos programar o ESP32 para ser um MQTT Client Publisher, que irá publicar em um tópico do MQTT Broker. Como base utilizamos o exemplo mqtt_esp8266 da biblioteca PubSubClient. Então o primeiro passo é instalar a biblioteca PubSubClient, da mesma forma que fizemos com sMQTTBroker.
Esse exemplo é para ser utilizado com o ESP8266, porém, é compatível também com ESP32. Para adequar ele ao ESP32, devemos trocar a biblioteca de WiFi pela mostrada a seguir.
#include <WiFi.h>
Agora que o programa já está compatível com o ESP32, precisamos configurar a conexão WiFi e indicar ao dispositivo o IP do MQTT Broker. A configuração da conexão WiFi é feita da mesma maneira que fizemos para o MQTT Broker, adicionando as informações da rede nas seguintes linhas do código.
const char* ssid = "........";
const char* password = "........";
Em seguida, é necessário fornecer o endereço IP do MQTT Broker (por essa razão, é importante que o MQTT Broker possua um IP fixo). Para fazer isso, vamos incluir o endereço IP do dispositivo na linha de código a seguir.
const char* mqtt_server = "broker.mqtt-dashboard.com";
Podemos manter o restante do código inalterado, faremos apenas alterações em algumas linhas da função loop. Vamos substituir o conteúdo da função loop com o seguinte código.
bool flag = 0;
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
unsigned long now = millis();
if (now - lastMsg > 2000) {
lastMsg = now;
if (flag == 0) {
client.publish("outTopic", "000");
}
else {
client.publish("outTopic", "111");
}
flag = !flag;
}
}
Desta forma, o MQTT Client vai publicar no tópico outTopic o valor 000 quando flag for igual a 0, e o valor 111 quando flag for igual a 1. O código completo do MQTT Client Publisher pode ser visualizado a seguir.
#include <WiFi.h>
#include <PubSubClient.h>
// Update these with values suitable for your network.
const char* ssid = "....."; // Ajuste estes valores a sua aplicação
const char* password = ".....";
const char* mqtt_server = "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;
bool flag = 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();
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP32";
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("exemple");
} 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() {
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;
if (flag == 0) {
client.publish("outTopic", "000");
}
else {
client.publish("outTopic", "111");
}
flag = !flag;
}
}
Podemos utilizar o MQTT Explorer para verificar se o MQTT Client está funcionando corretamente, o resultado pode ser visto na figura a seguir.
Agora que já estamos publicando no tópico outTopic do MQTT Broker, vamos programar outro ESP32 para assinar este tópico.
4. MQTT Client Subscriber (Assinante)
Para implementar o MQTT Client Subscriber no ESP32 vamos modificar o mesmo exemplo (mqtt_esp8266) utilizado para o MQTT Client Publisher. Primeiro é necessário alterar a biblioteca do WiFi e configurar as conexões com rede WiFi e com o MQTT Broker, da mesma forma que foi feito anteriormente.
#include <WiFi.h>
#include <PubSubClient.h>
// Update these with values suitable for your network.
const char* ssid = "....."; // The SSID (name) of the Wi-Fi network you want to connect to
const char* password = "....."; // The password of the Wi-Fi network
const char* mqtt_server = "broker.mqtt-dashboard.com";
Dentro da função setup vamos definir os pinos dos relés como saída. Além disso, o exemplo define qual função será executada para assinar o tópico, neste caso será callback. A seguir podemos ver como fica a função setup.
void setup() {
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
pinMode(32,OUTPUT);
pinMode(33,OUTPUT);
pinMode(4,OUTPUT);
}
Vamos fazer algumas alterações na função reconnect para informar a ela qual tópico será assinado. No exemplo a função está da seguinte forma.
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);
}
}
}
No exemplo, o programa publica a palavra “hello world” no tópico “outTopic” e assina o tópico “inTopic”. Vamos modificar essa configuração para que o programa não publique nada, mas sim assine o tópico “outTopic”. O resultado final pode ser observado abaixo.
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("outTopic");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
Agora, vamos ajustar a função de retorno de chamada (callback). Na versão atual do exemplo, essa função está subscrevendo um tópico, mostrando o valor no monitor serial e controlando um LED com base nesse valor. Vamos fazer uma modificação na função para que ela controle três relés de forma independente, com base nos valores de cada um dos bits dos dados no tópico “outTopic”. O resultado final pode ser visualizado abaixo.
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();
if ((char)payload[0] == '1') {
digitalWrite(32, LOW);
} else {
digitalWrite(32, HIGH);
}
if ((char)payload[1] == '1') {
digitalWrite(33, LOW);
} else {
digitalWrite(33, HIGH);
}
if ((char)payload[2] == '1') {
digitalWrite(4, LOW);
} else {
digitalWrite(4, HIGH);
}
}
Vamos alterar novamente a função loop, ela deverá ficar da seguinte forma.
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
}
A seguir temos o código completo utilizado para implementar o MQTT Client Subscriber no ESP32.
#include <WiFi.h>
#include <PubSubClient.h>
// Update these with values suitable for your network.
const char* ssid = "....."; // Ajuste estes valores a sua aplicação
const char* password = ".....";
const char* mqtt_server = "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();
if ((char)payload[0] == '1') {
digitalWrite(32, LOW);
} else {
digitalWrite(32, HIGH);
}
if ((char)payload[1] == '1') {
digitalWrite(33, LOW);
} else {
digitalWrite(33, HIGH);
}
if ((char)payload[2] == '1') {
digitalWrite(4, LOW);
} else {
digitalWrite(4, 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("outTopic");
} 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() {
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
pinMode(32, OUTPUT);
pinMode(33, OUTPUT);
pinMode(4, OUTPUT);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
}
Se tudo funcionar corretamente o MQTT Client Subscriber irá definir o nível lógico dos pinos 33, 32 e 4 com base no valor dos três bits do tópico “outTopic”.
Podemos ver que o MQTT é uma solução valiosa para facilitar a comunicação entre dispositivos distribuídos, e a implementação prática demonstrada aqui ressalta seu potencial na construção de sistemas IoT eficientes e interconectados. Para mais informações sobre MQTT veja o vídeo a seguir.
Autor: Thales Ferreira
Comments