We continue with the section dedicated to ESP8266 and ESP32 seeing how to send and receive messages via MQTT asynchronously thanks to the AsyncMqttClient library.
In the previous entry we started this series dedicated to MQTT on ESP8266 and ESP32, seeing how to use the PubSub library, which we had already seen in this post of the Arduino section.
The PubSub library is very popular and widely used. But we had already mentioned that there was another library to do communication asynchronously. This library is AsyncMqttClient and its code is available at https://github.com/marvinroger/async-mqtt-client.
That is, just as we had already seen how to do asynchronous web servers and asynchronous websockets, now we are going to do the same for MQTT and have asynchronous MQTT.
As a general rule, it is a preferred way over PubSub. In the rest of the entries of ESP8266/ESP32, unless expressly noted, it is the library we are going to use. But before moving on to more advanced topics, let’s see a simple introductory example of how to use AsyncMqttClient on ESP8266/ESP32.
To do this, we are going to do the same as we did in the previous example with the PubSub library. That is, a simple example that shows how to send and receive a numeric value via asynchronous MQTT. We will simply send the value of ‘millis()’, enough to see the basic operation and verify that everything works correctly.
So let’s do it.
First, the main sketch is basically identical to what we had in the previous example. We have only changed the include of the libraries to the equivalents in the new example.
#include <WiFi.h>
#include <AsyncMqttClient.h>
#include "config.h" // Replace with your network data
#include "MQTT.hpp"
#include "ESP8266_Utils.hpp"
#include "ESP32_Utils_MQTT_Async.hpp"
void setup()
{
Serial.begin(115200);
delay(500);
WiFi.onEvent(WiFiEvent);
InitMqtt();
ConnectWiFi_STA();
}
void loop()
{
delay(1000);
PublisMqtt(millis());
}
On the other hand, we have the new file ESP32_Utils_MQTT_Async, which replaces ESP32_Utils_MQTT from the previous example. Here we have functions related to the use of MQTT, but that are relatively independent of our project. Therefore, they are easy to reuse in other projects without the need to modify them.
There is not much here either. Basically, the only new thing is that two FreeRTOS timers are used to manage the reconnection of MQTT and WiFi in case of a connection failure.
TimerHandle_t mqttReconnectTimer;
TimerHandle_t wifiReconnectTimer;
void ConnectToMqtt()
{
Serial.println("Connecting to MQTT...");
mqttClient.connect();
}
void WiFiEvent(WiFiEvent_t event)
{
Serial.printf("[WiFi-event] event: %d\n", event);
switch(event)
{
case SYSTEM_EVENT_STA_GOT_IP:
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
ConnectToMqtt();
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Serial.println("WiFi lost connection");
xTimerStop(mqttReconnectTimer, 0); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
xTimerStart(wifiReconnectTimer, 0);
break;
}
}
void OnMqttConnect(bool sessionPresent)
{
Serial.println("Connected to MQTT.");
Serial.print("Session present: ");
Serial.println(sessionPresent);
SuscribeMqtt();
}
void OnMqttDisconnect(AsyncMqttClientDisconnectReason reason)
{
Serial.println("Disconnected from MQTT.");
if(WiFi.isConnected())
{
xTimerStart(mqttReconnectTimer, 0);
}
}
void OnMqttSubscribe(uint16_t packetId, uint8_t qos)
{
Serial.println("Subscribe acknowledged.");
Serial.print(" packetId: ");
Serial.println(packetId);
Serial.print(" qos: ");
Serial.println(qos);
}
void OnMqttUnsubscribe(uint16_t packetId)
{
Serial.println("Unsubscribe acknowledged.");
Serial.print(" packetId: ");
Serial.println(packetId);
}
void OnMqttPublish(uint16_t packetId)
{
Serial.println("Publish acknowledged.");
Serial.print(" packetId: ");
Serial.println(packetId);
}
void InitMqtt()
{
mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(ConnectToMqtt));
wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(5000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(ConnectWiFi_STA));
mqttClient.onConnect(OnMqttConnect);
mqttClient.onDisconnect(OnMqttDisconnect);
mqttClient.onSubscribe(OnMqttSubscribe);
mqttClient.onUnsubscribe(OnMqttUnsubscribe);
mqttClient.onMessage(OnMqttReceived);
mqttClient.onPublish(OnMqttPublish);
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
}
In the last file ‘MQTT.hpp’, we have saved the functions related to MQTT that are intimately related to our project. Thus, we have a function to subscribe to topics, one to publish messages, and a callback function to execute when receiving a message.
That is, basically we have the same example as in the previous entry, with the same program structure and operation, but with the functions adapted to AsyncMqttClient.
const IPAddress MQTT_HOST(192, 168, 1, 150);
const int MQTT_PORT = 1883;
AsyncMqttClient mqttClient;
String GetPayloadContent(char* data, size_t len)
{
String content = "";
for(size_t i = 0; i < len; i++)
{
content.concat(data[i]);
}
return content;
}
void SuscribeMqtt()
{
uint16_t packetIdSub = mqttClient.subscribe("hello/world", 0);
Serial.print("Subscribing at QoS 2, packetId: ");
Serial.println(packetIdSub);
}
String payload;
void PublisMqtt(unsigned long data)
{
String payload = String(data);
mqttClient.publish("hello/world", 0, true, (char*)payload.c_str());
}
void OnMqttReceived(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)
{
Serial.print("Received on ");
Serial.print(topic);
Serial.print(": ");
String content = GetPayloadContent(payload, len);
Serial.print(content);
Serial.println();
}
Result
We upload all our code to the ESP8266/ESP32 and, as it could not be otherwise, we can see the value of ‘millis()’ in the Serial console. The value that we are sending goes to the broker, and we receive it ourselves.
Something like “I am my own grandfather”, we use the same device for sending and receiving. But in this way, we avoid having to use two ESP8266/ESP32 to verify the sending and receiving. We just verify both things.
If we use a visualizer like MQTT Explorer that we saw in this post, we can also verify that the messages are being sent correctly.
That’s how easy it is! For now, we are only sending the value of ‘millis()’, which is useful to verify that it works but is not particularly interesting.
This will change in the next entry in the series, where we will use AsyncMqttClient to send and receive JSON files.
Download the code
All the code from this post is available for download on Github.
Version for ESP8266: https://github.com/luisllamasbinaburo/ESP8266-Examples
Version for ESP32: https://github.com/luisllamasbinaburo/ESP32-Examples