This tutorial demonstrates how Mongoose Library can be used to implement an MQTT client. We'll create an MQTT client that:

  1. Connects to the public HiveMQ MQTT server mqtt://broker.hivemq.com:1883
  2. When connected, subscribes to the topic mg/+/test
  3. Publishes message hello to the mg/clnt/test
  4. Receives that message back from the subscribed topic and closes the connection
  5. Creates a timer that checks if a connection is closed, and reconnects to repeat the whole process

A full source code for this tutorial is at https://github.com/cesanta/mongoose/tree/master/examples/mqtt-client

Global variables

First we declare global variables to make it easy to change tunable parameters, like MQTT server address, topic names, and QoS level.

Also, we make an MQTT connection pointer, s_conn, globally visible. A reconnection timer function is going to check that pointer for NULL, and if it is NULL, a timer function would create a connection. Then, a connection event handler sets this pointer to NULL when a connection closes.

Main function

The main() function is simple: we initialise an event manager and start the event loop, as usual. Also we create a timer - note that a timer is created on stack but since it is declared in main(), it lives as long as the application is running.

Note that a timer is initialised after the event manager gets initialised. That is important because a timer is created with MG_TIMER_RUN_NOW flag with executes timer function immediately. Timer function uses event manager, so it must be initialised.

Note that the timer function is called with the interval of 3 seconds. Event manager poll timeout is 1 second. It is important to keep polling timeout less than timer interval - because timers are checked at every polling cycle, and if there are no network events, poll function sleeps for the whole duration of the polling interval. If it is larger than a timer timeout, timer invocation could be missed.

The event loop executes until termination signal is received. After that, mg_mgr_free() and mg_timer_free() functions are called which perform cleanup.

Timer function

Timer function implements the reconnection logic and is trivial. It creates a client connection s_conn if it is NULL. This pattern should be used for any type of client connection that must be kept alive.

Event handler

The event handler function checks which event ev has arrived and acts accordingly. The MG_EV_OPEN event is the very first event that is sent to every connection when it is just created and added to the event manager. In our case, we simply log that fact:

On any error - for example, DNS lookup failure, we log that error:

When TCP connection is established with the MQTT server, we initialise TLS if the MQTT server URL is mqtts://:

Note, for that to work, the application must be built with TLS enabled:

$ make MBEDTLS_DIR=/path/to/your/mbedtls/installation

By default, the HiveMQ URL is not TLS, so building with TLS is not required.

Then we catch MG_EV_MQTT_OPEN event which is sent when MQTT server accepted us as a client. There, we subscribe to the topic, and immediately publish to the topic. Note that these functions do not immediately send data to the MQTT server. As any other Mongoose output function, they just append data to the output buffer. Data gets sent when we exit the event handler and Mongoose performs event manager poll, mg_mgr_poll().

When we receive a message - and we should, we print it and signal Mongoose to terminate the connection:

And when we are getting closed, we set s_conn to NULL. Timer function later will kick in the reconnection: