Mongoose-OS has a built-in MQTT client and a simple mJS (a reduced JavaScript) interpreter. These tools allow us to quickly and easily connect to an MQTT broker and perform our proof-of-concept tests for more complex applications.
Configuration
We’ll configure the ESP32 running Mongoose-OS to use the MQTT client and connect to a WiFi network. Since we are going to connect to a server (the MQTT broker), this is perhaps the simplest and fastest way to run our tests. This action can be done manually using RPCs (Remote Procedural Calls), writing a JSON config file, or it can be defined in the YAML file that describes how the project will be built. We’ve chosen this last option for our tests.
libs: - origin: https://github.com/mongoose-os-libs/mqtt # Include the MQTT client config_schema: - ["mqtt.enable", true] # Enable the MQTT client - ["mqtt.server", "address:port"] # Broker IP address (and port)
The most common port for MQTT is 1883. The broker can also ask us to send a username and password, full configuration details are available at the corresponding Mongoose-OS doc page.
Operation
Before we build our project and execute our code, it is perhaps convenient to have an MQTT client connected to the broker, so we can see the message the device will send at connect time. We’ve used a broker we installed in a server in our lab:
$ mosquitto_sub -h mqtt.lab -p 1883 -t "#" -v
In addition (if daily traffic allows), it is also possible to use Eclipse’s open MQTT broker, in that case we should be a bit more specific in the topic we subscribe to, in order to try to receive only our messages
$ mosquitto_sub -h mqtt.eclipseprojects.io -p 1883 -t "/this/#" -v
After the code is compiled and linked (mos build) and the microcontroller’s flash is programmed (mos flash) using mos tool, we’ll watch the log and check if everything is going on as it should, or catch any possible errors. Remember we need to configure the proper access credentials to connect to our WiFi network of choice (SSID and password), and the address and port for the MQTT broker we are going to use.
[Feb 11 15:19:42.353] mgos_mqtt_conn.c:435 MQTT0 connecting to mqtt.sensors.lab:1883 [Feb 11 15:19:42.370] mgos_mqtt_conn.c:188 MQTT0 TCP connect ok (0) [Feb 11 15:19:42.380] mgos_mqtt_conn.c:235 MQTT0 CONNACK 0 [Feb 11 15:19:42.385] mgos_mqtt_conn.c:168 MQTT0 sub /this/sub @ 1 [Feb 11 15:19:42.393] init.js:38 MQTT connected [Feb 11 15:19:42.407] init.js:26 Published:OK topic:/this/pub/esp32_807A98 msg:CONNECTED!
At this time we’ll see that message in our MQTT client window:
$ mosquitto_sub -h mqtt.lab -p 1883 -t "#" -v /this/pub/esp32_807A98 CONNECTED!
Then, we can send a message to our device by publishing to the topic to which it is subscribed:
$ mosquitto_pub -t /this/sub -h mqtt.lab -m "This message"
At this time we’ll see that message in our MQTT client window:
$ mosquitto_sub -h mqtt.lab -p 1883 -t "#" -v /this/pub/esp32_807A98 CONNECTED! /this/sub This message
and in our device’s log
[Feb 11 15:20:02.647] init.js:32 Got a msg: This message
Finally, we’ll power off our ESP32 and after a while (minutes) the broker will detect the disconnection; at that time we’ll see the last-will message in our MQTT client window
$ mosquitto_sub -h mqtt.lab -p 1883 -t "#" -v /this/pub/esp32_807A98 CONNECTED! /this/sub This message /this/test lost
The mJS code
Whenever we want to publish a message, we’ll call MQTT.pub(). There’s no need to wait nor perform any handshakes, just invoke that method and see the results. Internally, the message is queued and will be sent as soon as possible; the value returned by the publishing method is an indicator of the result of the queuing action, not the actual transmission of the message.
This article has companion code, there we’ve written a simple function as a usage example:
let publish = function (topic, msg) { let ok = MQTT.pub(topic, msg, 1, false); // QoS = 1, do not retain Log.print(Log.INFO, 'Published:' + (ok ? 'OK' : 'FAIL') + ' topic:' + topic + ' msg:' + msg); return ok; }; let device_id = Cfg.get('device.id'); publish('/this/pub/'+device_id,'CONNECTED!');
Should we want our message to be retained, the fourth parameter for the MQTT.pub() method call must be true.
To receive messages, we subscribe to the desired topic invoking the MQTT.sub() method. Its second parameter is a handler for a callback function that will be run when we get a message. The following example can also be found in the companion code:
MQTT.sub('/this/sub', function(conn, topic, msg) { Log.print(Log.INFO, 'Got a msg: ' + msg); }, null);
Last-will messages
If we want to configure a “last-will message”, that is, a message that will be published by the broker in our behalf when it detects we are no longer connected, we can do it this way:
- ["mqtt.will_topic", "/this/test"] - ["mqtt.will_message", "lost"]
For some applications it is required that this message is also retained by the broker, so if other clients connect after we disconnected and before we reconnect, they can receive that message, which will in turn inform them that we are not available at that time.
- ["mqtt.will_retain", true]
Retain, persistence
Message retention (be them last-will or common messages with the retain flag set) requires an MQTT broker with persistence capability, that is, with a database in which to store the latest messages that have been marked as persistent in every topic and send them to whoever client subscribes to that topic when it does so.
In our case, we’ve used Eclipse Mosquitto as our broker, but there are other available options.
When using Mosquitto, to configure persistence we need to add
persistence true
to the config file (if we search for it, we’ll find it). It is also possible that we have to add the name and path to the persistence database (paths in GNU/Linux environment form):
persistence_file mosquitto.db persistence_location /var/lib/mosquitto/
but that will depend on our distro, it may already be configured as default. The user running the Mosquitto process has to have write permission on this database.
Example
The companion example code is available at Github.