MQTT client
Overview
This tutorial demonstrates how Mongoose Library can be used to implement an MQTT client. We'll create an MQTT client that:
- Connects to the public HiveMQ MQTT server
mqtt://broker.hivemq.com:1883
- When connected, subscribes to the topic
mg/+/test
- Publishes a
hello
message to themg/clnt/test
topic - Receives that message back from the subscribed topic and closes the connection
- Creates a timer that checks if a connection is closed, and reconnects to repeat the whole process
The full source code for this tutorial is at https://github.com/cesanta/mongoose/tree/master/examples/mqtt-client
API
The MQTT API is rich, here we'll only use three functions:
struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url,
struct mg_mqtt_opts *opts,
mg_event_handler_t fn, void *fn_data);
void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic,
struct mg_str data, int qos, bool retain);
void mg_mqtt_sub(struct mg_connection *c, struct mg_str topic, int qos);
The function mg_mqtt_connect()
tries to connect to the MQTT broker whose URL is specified in parameter url
, returning a connection handler or NULL on failure. The function specified in parameter fn
will be our callback function, and parameter fn_data
will be its argument. We can indicate session and last will options in a structure pointed to by the opts
parameter.
The function mg_mqtt_pub()
performs the action of publishing the message data
to the topic topic
.
The function mg_mqtt_sub()
performs the action of subscribing to the topic topic
.
For detailed information, see the documentation.
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 will create a new 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 the stack but since it is declared in main()
, it lives
as long as the application is running.
Note that the timer is initialised after the event manager.
This is important because here the timer
is created with the MG_TIMER_RUN_NOW
flag, which executes the timer function
immediately. Our timer function will use the event manager, so it must be initialised beforehand.
The timer function is called with an interval of 3 seconds; and our event manager poll timeout is 1 second.
For more information on timers, check the timers tutorial.
The event loop executes until a termination signal is received. After that, the
mg_mgr_free()
and mg_timer_free()
functions are called to perform
cleanup.
Timer function
The 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:
Connection failures will trigger an MG_EV_ERROR
event, an error reason is passed as a char *
pointer in parameter ev_data
;
check the error handling tutorial for more information.
Here, on any error - for example, DNS lookup failure, we log that error:
When the TCP connection to the MQTT server is established, we initialise TLS
if the MQTT server URL is mqtts://
:
Note that, for that to work, the application must be built with TLS enabled. By default, the HiveMQ URL is not TLS, so building with TLS is not required; more information on how to build can be found below.
Then we catch the MG_EV_MQTT_OPEN
event which is sent when the MQTT server accepted
us as a client. There, we subscribe to the desired topic, and immediately publish
to it. 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 the 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. The timer function
will kick in the reconnection later:
Build and run
- Follow the Build Tools tutorial to setup your development environment.
- Start a terminal in the project directory; if you've not already done so, clone the Mongoose Library repo
git clone https://github.com/cesanta/mongoose
- Build the example, this will also start the MQTT client:
cd mongoose/examples/mqtt-client make clean all
- Observe the log
1814f03947d 2 main.c:30:fn CREATED 1814f03968b 2 main.c:45:fn CONNECTED to mqtt://broker.hivemq.com:1883 1814f03968b 2 main.c:47:fn SUBSCRIBED to mg/+/test 1814f03968b 2 main.c:51:fn PUBLISHED hello -> mg/clnt/test 1814f03978a 2 main.c:56:fn RECEIVED hello <- mg/clnt/test 1814f03978a 2 main.c:59:fn CLOSED 1814f03a344 2 main.c:30:fn CREATED 1814f03a58b 2 main.c:45:fn CONNECTED to mqtt://broker.hivemq.com:1883 1814f03a58b 2 main.c:47:fn SUBSCRIBED to mg/+/test 1814f03a58b 2 main.c:51:fn PUBLISHED hello -> mg/clnt/test 1814f03a681 2 main.c:56:fn RECEIVED hello <- mg/clnt/test 1814f03a681 2 main.c:59:fn CLOSED
Build with TLS support
Check the "How to build" section of the TLS tutorial for specific information on building options for your OS
For more information on developing TLS clients, check the TLS tutorial