Using MQTT Message Bus in Embedded Systems

1. Inter-Process Communication (IPC)

As an embedded software developer, handling communication between processes is quite common. From the perspective of communication purposes, we can categorize inter-process communication into three types:

  1. For process scheduling: can be achieved through signals;
  2. For resource sharing: can be achieved through mutexes, semaphores, read-write locks, file locks, etc.;
  3. For data transmission: can be achieved through shared memory, named pipes, message queues, and sockets.

Regarding the communication primitives provided by the operating system mentioned above, there is a wealth of information and articles available online, so I won’t elaborate here. How should we choose among these methods? Based on my personal experience, quality over quantity, carefully selecting three or four methods can completely meet daily work needs.

The main topic we want to discuss today is the third one: data transmission, and among the methods mentioned above, my favorite and most commonly used is socket communication.

Some friends might say: Socket communication is just the TCP/IP stuff, and we still need to manage connections, package and unpack data, which is quite troublesome.

Indeed, socket communication does require manual handling of these low-level aspects, but we can dress up the socket with a layer: using MQTT message bus to facilitate data exchange between processes in the system, and we will discuss this in detail below.

2. Advantages of Socket Communication

I won’t elaborate further but will directly quote the views from Professor Chen Shuo’s book “Linux Multi-threaded Server Programming” (Page 65, Section 3.4):

1. Cross-host, scalable

Since it’s multi-process, if one machine’s processing capability is insufficient, we can use multiple hosts to handle it. By distributing processes across multiple machines on the same local area network, modifying the Host:Port configuration allows it to continue working. In contrast, the inter-process communication methods listed at the beginning of the article cannot cross machines, which limits scalability.

2. The operating system automatically reclaims resources

A TCP port is exclusively owned by one process, and when the program unexpectedly exits, the operating system will automatically reclaim resources, leaving no garbage for the system, making it easier to recover after a program restart.

3. Recordable and reproducible

If two processes communicate via TCP and one crashes, the operating system will close the connection, and the other process can almost immediately sense it, allowing for quick failover. Of course, application-layer heartbeat is essential. (Supplement: The operating system has a keep-alive time for TCP connections, which defaults to 2 hours and is global.)

4. Cross-language

The server and client do not need to use the same programming language.

1. Professor Chen Shuo describes general socket communication, so the client and server are typically located on different physical machines. 2. In embedded development, it is usually done in the same programming language, so this cross-language aspect can be somewhat ignored.

3. MQTT Message Bus

1. MQTT is a communication mechanism

For those familiar with the Internet of Things (IoT) field, the MQTT message bus is certainly well-known, as major IoT cloud platforms (Amazon, Alibaba Cloud, Huawei Cloud) all provide access to the MQTT protocol.

Currently, the best document for learning MQTT is IBM’s online manual: https://developer.ibm.com/zh/technologies/messaging/articles/iot-mqtt-why-good-for-iot/.

Here, I will directly list some key information:

  1. MQTT protocol is lightweight, simple, open, and easy to implement;
  2. MQTT is a message protocol based on the publish (Publish)/subscribe (Subscribe) paradigm;
  3. MQTT operates on the TCP/IP protocol family;
  4. There are three levels of message delivery quality;
  5. Small transmission, minimal overhead (the fixed-length header is 2 bytes), and minimized protocol exchange to reduce network traffic;

MQTT message transmission requires middleware called a Broker, which is essentially a server. The communication model is as follows:

Using MQTT Message Bus in Embedded Systems
  1. MQTT Broker needs to start first;
  2. ClientA and ClientB need to connect to the Broker;
  3. ClientA subscribes to topic_1, ClientB subscribes to topic_2;
  4. When ClientA sends a message to topic_2, it will be received by ClientB;
  5. When ClientB sends a message to topic_1, it will be received by ClientA;

The communication method based on topic has a significant advantage: decoupling, a client can subscribe to multiple topics, and any other client connected to the bus can send information to these topics (a client can also send messages to itself).

2. Implementation of MQTT

MQTT is just a protocol, and in IBM’s online documentation, you can see that many languages have implemented the MQTT protocol, including: C/C++, Java, Python, C#, JavaScript, Go, Objective-C, etc. For embedded development, the most commonly used implementations are:

Mosquitto; Paho MQTT; wolfMQTT; MQTTRoute.

Below, we will focus on the compilation and usage of Mosquitto, which is the one I use the most in projects.

3. Designing your own communication protocol on top of MQTT

From the description above, it can be seen that the MQTT message bus is just a communication mechanism, providing a channel for the communication subjects to transmit data.

On this channel, we can send any format or encoding of data according to the actual project needs. In projects, we most commonly use pure text in JSON format, which is also the recommended method by various IoT cloud platforms. If binary data needs to be included in the text data, it should be converted to BASE64 encoding before sending.

4. How to Utilize MQTT Message Bus in Embedded Systems

From the above description, as long as a MQTT Broker is running on the server side, each client connected to the bus can flexibly send and receive data from each other.

We can apply this mechanism in the design of embedded applications: the MQTT Broker runs as an independent service on the embedded system locally, and other processes that need interaction can connect to this local Broker to send data to each other. The operating model is as follows:

Using MQTT Message Bus in Embedded Systems

Each process only needs to subscribe to a fixed topic (for example: its own client ID), so whenever other processes want to send data to it, they just send it to that topic.

1. A Communication Framework for an Embedded System

I previously developed an environmental monitoring system to collect parameters such as PM2.5 and PM10 in the atmosphere, developed on the Contex A8 platform, and needed to implement data recording (database), UI monitoring interface, and other functions.

The pollutant data sampling hardware module was provided by a third-party company, and we only needed to control the sampling device and receive sampling data through the serial protocol provided by that module. The final designed communication model is as follows:

Using MQTT Message Bus in Embedded Systems
  1. The UI process sends control commands to the sampling control process through the message bus, and the sampling control process sends control commands to the sampling module through the serial port after receiving them;
  2. The sampling control process receives data such as PM2.5 from the sampling module via the serial port and sends all data to the specified topic on the message bus;
  3. The UI process subscribes to that topic and displays the data on the screen after receiving it;
  4. The database process also subscribes to that topic and stores the data in the SQLite database after receiving it;

In this product, the core process is the sampling control process, responsible for interacting with the sampling module. By designing the UI processing and database processing as independent processes, the complexity of the system is reduced, and even if these two processes crash, it will not affect the core sampling control process.

For example: if the UI process encounters an error and crashes, it will restart immediately, and upon startup, it will know through cached information that it is currently executing the sampling work, so the UI process will connect to the message bus, enter the sampling data display interface, and continue to receive and display the PM2.5 data sent by the sampling control process.

This communication model has another advantage: scalability.

In the later stages of project development, the client requested to integrate a third-party gas module to collect parameters such as NO and SO2 in the atmosphere, with a communication method of RS485.

At this point, expanding this functional module becomes exceptionally simple: just write an independent gas parameter process, connecting it to the message bus. This process will directly send NO and SO2 gas parameters received from the third-party gas module to a certain topic on the message bus, and the UI process and database process subscribing to that topic will be able to receive the gas-related data immediately.

Additionally, this design model has other advantages:

  1. Parallel development: each process can be developed in parallel by different personnel as long as the communication protocol is well defined;
  2. Convenient debugging: since the data sent is manually readable, during the development phase, a dedicated monitoring program can be written on a PC, connecting to the MQTT Broker in the embedded system, allowing it to receive all messages sent by the processes;
  3. Communication security: after the product release, to prevent others from eavesdropping on the data (such as the debugging process mentioned in point 2), a configuration file can be specified for the MQTT Broker, allowing only local processes (127.0.0.1) to connect to the message bus.
Using MQTT Message Bus in Embedded Systems

2. A Slightly More Complex Communication Model

In the previously described embedded system framework design, each process runs locally, and all messages are sent and received within the system. So, how should we design it if we need to transfer data to the cloud or receive some control commands from the cloud?

By adding an MQTT Bridge module! This involves adding another process that connects simultaneously to the cloud MQTT Broker and the local MQTT Broker, and the communication model is as follows:

Using MQTT Message Bus in Embedded Systems
  1. When the MQTT Bridge receives instructions from the cloud, it forwards them to the local message bus;
  2. When the MQTT Bridge receives messages from the local message bus, it forwards them to the cloud message bus.

5. Mosquitto: A Simple Test Code

The above content mainly discusses design ideas, and on the code level, I generally use Mosquitto, this open-source implementation.

Installing and testing on Linux systems is very convenient, and I will briefly explain it below.

1. Install and Test Directly via apt

You can refer to this document (https://www.vultr.com/docs/how-to-install-mosquitto-mqtt-broker-server-on-ubuntu-16-04) for installation and testing.

(1) Installation

sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppasudo apt-get updatesudo apt-get install mosquittosudo apt-get install mosquitto-clients

(2) Testing

After installation, the mosquitto broker will automatically start, and you can use netstat to check port 1883 to confirm.

Receiver: After connecting to the broker, subscribe to the “test” topic.

mosquitto_sub -t "test"

Sender: After connecting to the broker, send the string “hello” to the “test” topic.

mosquitto_pub -m "hello" -t "test"

When the sender executes mosquitto_pub, the receiver’s terminal window will display the received string “hello”.

2. Manually Compile and Test via Source Code

Using apt for installation is mainly for simple learning and testing. If you want to use Mosquitto in project development, you will definitely need to compile it manually to obtain header files and library files, and then copy them for use in your application.

(1) Manually Compile and Install Mosquitto

My development environment is:

  1. Compiler: gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
  2. Mosquitto Version: mosquitto-1.4.9

You can download mosquitto-1.4.9 from the official website, or from the download link provided at the end of this article, or you can try a higher version.

Compilation and installation commands:

makemake install prefix=$PWD/install

After successful installation, you can find the output files in the install folder of the current directory:

Using MQTT Message Bus in Embedded Systems
  1. bin: mqtt client program;
  2. include: header files needed for the application;
  3. lib: library files needed for linking the application;
  4. sbin: mqtt broker service program.

If you encounter errors during compilation, such as missing dependencies like ares.h or uuid.h, you just need to install the corresponding development packages via apt.

(2) The Simplest Mosquitto Client Code

The Mosquitto source code provides rich sample examples. If you don’t want to explore, you can directly download the demo program from the download link at the end of this article, which connects to the message bus and subscribes to the “topic_01” topic. Of course, you can modify the code to send messages (by calling the mosquitto_publish function).

After entering the c_mqtt example code directory, you will see that it already contains the bin, include, and lib directories, which are copied from the install directory mentioned above.

After executing make, it will compile successfully, yielding an executable file: mqtt_client.

The testing process is as follows:

Step 1: Start the MQTT Broker

In the first terminal window, start the sbin/mosquitto broker program. If you have already started a broker in the above tests, you need to kill that previous broker, as they both use port 1883 by default and cannot coexist.

Step 2: Start the Receiver Program mqtt_client

In the second terminal window, start mqtt_client, which is the executable program compiled from our example code, and it subscribes to the topic “topic_01”.

./mqtt_client 127.0.0.1 1883

Parameter 1: The IP address of the Broker service, which is 127.0.0.1 since it’s all on the local system; Parameter 2: The port number, typically defaulting to 1883.

Step 3: Start the Sender Program bin/mosquitto_pub

In the third terminal window, start bin/mosquitto_pub with the following command:

./mosquitto_pub -h 127.0.0.1 -p 1883 -m "hello123" -t "topic_01"

Parameter -h: The IP address of the Broker service, which is 127.0.0.1 since it’s all on the local system; Parameter -p: Port number 1883; Parameter -m: The message content to send; Parameter -t: The topic to send to.

At this point, you should see the received message printed in the second terminal window (mqtt_client).

6. Conclusion

This article mainly introduces a design pattern in embedded systems: using a message bus to achieve communication between processes, and discusses the open-source implementation Mosquitto.

In actual projects, more stringent permission controls are needed, such as providing usernames, passwords, and device certificates when connecting to the message bus, ensuring the client names meet specified formats, and the subscribed topics conforming to certain formats, etc.

In the next article, we will continue discussing this topic, providing a more specific and practical demo example.

7. Resource Downloads

1. mosquitto-1.4.9.tgz

Link: https://pan.baidu.com/s/1izQ3dAlGbHiHwDvKnOSfyg Password: dozt

2. Mosquitto Demo Example Code

Link: https://pan.baidu.com/s/1M-dU3xapNbKyk2w07MtDyw Password: aup3

Previous Recommendations

Embedded Mixed Bag Fans Benefits | Free Development Board!

Reply1024 in the public account chat interface to obtain embedded resources.

Leave a Comment

×