How to use FreeRTOS to publish messages over MQTT
In this tutorial, we will explore how to use an STM32 microcontroller as an MQTT client to publish messages to different topics on an MQTT broker. Instead of running everything inside the main loop, we will take advantage of FreeRTOS to organize our code into separate tasks.
We will create two independent FreeRTOS tasks, where each task is responsible for publishing data to its own MQTT topic. This approach not only makes the system more modular and easier to manage, but also demonstrates the power of multitasking with FreeRTOS in real-world IoT applications.
Prerequisites:
This tutorial is Part 3 of the STM32 IoT series with ESP8266. In the earlier parts of this series, we learned how to connect and configure the ESP8266 with STM32, and how to register the STM32 client with an MQTT broker to publish messages to a topic.
If you haven’t followed those tutorials yet, I recommend going through them first before continuing with this one:
By the end of this guide, you will understand how to use STM32 and ESP8266 together for MQTT communication and build a solid base for more advanced IoT projects like sending sensor data, subscribing to topics, or using RTOS for message handling.
STM32 FreeRTOS MQTT Video Tutorial
This guide explains how to use FreeRTOS with STM32 to publish messages to multiple MQTT topics. Along with the step-by-step written tutorial, I’ve also recorded a practical video walkthrough. Watch the video and follow the code here to clearly understand how two FreeRTOS tasks can publish to different topics in real time.
Watch the FreeRTOS MQTT TutorialSTM32 MQTT Project Requirements
Before we begin building our STM32 ESP8266 MQTT project, let’s take a quick look at the tools and components you’ll need. This includes both hardware and software required for the project.
- Hardware:
- Software:
- STM32CubeIDE
- MQTT Broker (I am going to use the HiveMQ Public broker for testing)
- MQTT Client (Optional us it for testing)
- Internet access
Connecting STM32 and ESP8266 for MQTT
To connect the ESP8266 WiFi module with STM32, we use the UART pins for communicating with the module. Along with the basic connections (power, TX, RX, and GND), adding a USB to TTL converter is very handy for debugging and logging, so you can easily see what’s happening during the STM32 WiFi setup.
Hardware Connections and Pin Configuration
I’m using an ESP-01 Adapter with the ESP8266-01 module. This adapter makes things easier by reducing the connections from 8 pins to just 4. The image below shows how the module is connected using the adapter.
Always make UART connections in a cross pattern: connect TX to RX and RX to TX.
- Connect the TX pin from the Adapter to PA0 (UART4_RX) and the RX pin to PA1 (UART4_TX).
- Power the Adapter with 5V from the MCU. The ST-Link provides only 3.3V, so connect the USB cable to the dev board to supply 5V.
The Adapter’s onboard voltage regulator converts 5V to 3.3V for the ESP8266-01 module. If you use an external power supply for the module, ensure the supply and STM32 dev board share a common ground.
FTDI (FT232) Module
We use it to view user interaction logs on the serial console. Since we only send data from STM32 to the FTDI, connect PA10 (UART1_TX) to the RX pin of the module.
STM32 ESP8266 CubeMX Configuration
We will start configuring the UART4 to connect the ESP8266-01 module.
UART4 (ESP8266) Configuration
The image below shows the UART4 configuration.

The UART must be configured in the Asynchronous mode. Leave the Parameter configuration to default, with 8 bits Word Length, no Parity and 1 Stop bit. The pins PA0 and PA1 are configured as the UART TX and RX pins.
The Baud Rate configuration here should match the Baud Rate of the ESP8266 Module. The default baud rate of ESP8266-01 is 115200, but you should still test it prior connecting to the MCU.
You check the video to see how to test the module to find the correct baud rate.
UART1 (FT232) Configuration
As I mentioned earlier, the user interactive logs will be sent through UART1 to the FT232, which will then be displayed on the serial console. The image below shows the UART1 configuration.
The UART1 is configured in the Asynchronous mode. The Baud rate is set to 115200 Bits/s with 8 Bits of word length, No parity and 1 Stop bit. We can configure this however we want, but this is an optimal configuration.
The pins PA9 and PA10 are configured as the UART1_TX and UART1_RX pins respectively. Since we only need to transmit data to the FTDI, PA9 (UART1_TX) is connected to FTDI RX as mentioned in the wiring diagram.
STM32 MQTT FreeRTOS Configuration
After configuring the UART peripherals to connect the hardwares, we will now configure the FreeRTOS. The image below shows the parameter configuration for the FreeRTOS.
I have enable the CMSIS_V2 for the RTOS. The parameters are configured in their default state, I have not modified anything at all.
Create MQTT Tasks
Next, go to “Tasks and Queues” section to add Tasks and Queues. We will start by adding the Tasks first. The images below shows the configuration for the Three tasks we will use in this project.
The MQTT_Task
will handle the publishing part. It will receive the data from the queue and publish the message to the respective topic. You should use the stack size of at least 512 words and priority higher than the other tasks. This is to make sure that the task is not preempted, specially when it is publishing data.
The PublishTask1
and PublishTask2
will send the message to be published and topic names to the queue. This data will then pass to the MQTT_Task, which will actually call the publish function.
You should keep the priorities of these tasks lower than the MQTT_Task.
Create MQTT Publish Queue
We will create a Queue to pass the data from PublishTask
to MQTT_Task
. The image below shows the queue.
The PublishQueue has space for 4 elements. This is enough since we have only 2 publish tasks sending data via this queue. Note that the Item Size is 16 bits, but we will change this later in the code after we create a structure for the data. You can read more about using Queue in FreeRTOS in another tutorial → Queue in STM32 FreeRTOS.
STM32 MQTT HAL Example Code
I have already explained the MQTT publish function and how to use it in the previous tutorial → STM32 MQTT connect and Publish, therefore I will skip the explanation here. We will only focus on the new function, that is, FreeRTOS Tasks and queue.
Create a structured queue
The Queue we defined in the CubeMX is a simple queue with 16 bit element size. We want the PublishTasks to send topic name and message to the queue, hence a 16 bit element won’t be enough to store this information. Therefore we need to create a structured queue.
We will start with creating a structure first.
typedef struct {
char topic[64];
char message[64];
} publishQueue_t;
This structure contains two arrays, one for the topic name and another for the message. Next we will edit the Queue definition to change it to structured queue.
/* creation of publishQueue */
publishQueueHandle = osMessageQueueNew (4, sizeof(publishQueue_t), &publishQueue_attributes); // sizeof(uint16_t) -> sizeof(publishQueue_t)
Note that I have changed the pre-generated size element (uint16_t) to publishQueue_t. This changes our simple queue to structured queue and we can pass the structure to this queue instead of single data values.
Write the Publish Tasks
Now our queue is ready, so we will pass the data to the queue. We will do this in the Publish_Task
we defined in the FreeRTOS configuration.
The code below shows the Publish_Task1 send the topic name and message to the queue.
void StartPublishTask1(void *argument)
{
publishQueue_t pub;
int count = 0;
for(;;)
{
snprintf(pub.topic, sizeof(pub.topic), "controllerstech/test1");
snprintf(pub.message, sizeof(pub.message), "PUB1 : %d", count++);
osMessageQueuePut(publishQueueHandle, &pub, 0, 1000);
osDelay(10000);
}
}
Here we will first define a structure, pub
, which will store the topic name and message.
Inside the infinite loop, store the topic name in the topic
array and message to be sent in the message
array. The message will contain the count value, which increments at each call to this task, so we will get an updated value each time.
Next, call the function osMessageQueuePut
to send the prepared structure to the publishQueue. Finally we will put the task to sleep for 10 seconds.
You can read more about using Queue in FreeRTOS in another tutorial → Queue in STM32 FreeRTOS.
The PublishTask2
is similar to task 1. I have only make a minor change, instead of sending the increasing count value, it sends the decreasing value. This is shown below.
void StartPublishTask2(void *argument)
{
publishQueue_t pub;
int count = 1000;
for(;;)
{
snprintf(pub.topic, sizeof(pub.topic), "controllerstech/test2");
snprintf(pub.message, sizeof(pub.message), "PUB2 : %d", count--);
osMessageQueuePut(publishQueueHandle, &pub, 0, 1000);
osDelay(10000);
}
}
The count value starts with 1000 and with every call to this task, the value decreases. This way the PublishTask2 send message as follows: PUB2 : 1000, PUB2: 999, PUB2: 998 and so on, while the PublishTask1 sends PUB1 : 0, PUB1: 1, PUB1: 2 and so on.
Write the MQTT_Task
The Publish Tasks and now sending the data over the queue. We need to receive that data and publish the message to the respective topic. The MQTT_Task
will do exactly this.
void StartMQTT_Task(void *argument)
{
publishQueue_t msg;
uint32_t lastActivity = osKernelGetTickCount();
for(;;)
{
if (osMessageQueueGetCount(publishQueueHandle) > 0){
osMessageQueueGet(publishQueueHandle, &msg, NULL, 1000);
if (ESP_CheckTCPConnection() == ESP8266_OK){
ESP_MQTT_Publish(msg.topic, msg.message, 0);
lastActivity = osKernelGetTickCount();
}
}
if ((osKernelGetTickCount() - lastActivity) > 20000){
if (ESP_CheckTCPConnection() == ESP8266_OK){
ESP_MQTT_Ping();
}
else {
ESP_MQTT_Connect("broker.hivemq.com", 1883, "STM32Client", NULL, NULL, 30);
}
}
osDelay(100);
}
}
Here we will first define a structure, msg
, to store the data received from the queue. The variable lastActivity
will keep track of “when was the last time the client sent the message or ping to the broker”. We will use this data to send the ping to the broker, so it does not disconnect the client due to inactivity.
- Inside the infinite loop first check if the publish queue has received some data in it. This is done using the function
osMessageQueueGetCount
, which returns the number of messages pending the queue. - Next, use the function
osMessageQueueGet
to read the data from the queue and store it in themsg
structure we defined earlier. - Then call the function
ESP_MQTT_Publish
to publish the message to the respective topic. - Also update the
lastActivity
so that this transaction can count for Active time.
As I mentioned earlier, we will also send a ping request to the broker if the client is inactive for a longer period. This will prevent the broker from disconnecting the client. To do so, we will first check if the time for the lastActivity
is more than 20 seconds. If it is, then send the ping request by calling the function ESP_MQTT_Ping()
.
Why send the Ping Request ?
In the ESP_MQTT_Connect
function, I have configured the keepAlive time to 30 seconds. The broker will wait for some kind of activity from the client between 1 to 1.5x time period (between 30 to 45 seconds). If the client does not send anything, the broker will disconnect the client.
The ping request is sent by the client to perform an activity, so to inform the broker that it is alive and there is no need to close the connection.
Some public brokers might close the connection even if the client is actively sending the messages. We can not prevent that from happening but if it happens, we can send a re-connect request to the broker. This is demonstrated below.
if (ESP_CheckTCPConnection() == ESP8266_OK){
ESP_MQTT_Ping();
}
else {
ESP_MQTT_Connect("broker.hivemq.com", 1883, "STM32Client", NULL, NULL, 30);
}
This is the code from MQTT_Task
. Here we check if the ESP8266 is still connected to the broker by calling the function ESP_CheckTCPConnection()
. If the module is not connected, we will send the connect request again.
STM32 MQTT Publish Result
The image below shows the data sent by the STM32 client using FreeRTOS. I am using another client (MQTT Explorer) to view the messages published to the topics. It is connected to the same broker and is subscribed to the same topics.
You can see that the MQTT Explorer is receiving the messages published to the topics by STM32 client. The value of the count variable from Task1 is increasing from 0 while the count variable from Task2 is reducing from 1000.
This indicates that the STM32 MQTT Client is publishing messages to two different topic successfully.
Conclusion
In this tutorial, we successfully implemented MQTT publishing on STM32 with the ESP8266 by leveraging FreeRTOS tasks and structured queues. By separating message generation and transmission into different tasks, the design becomes cleaner, scalable, and easier to maintain. The queue-based approach also ensures that multiple publishers can work simultaneously without conflicts, while the MQTT task takes care of connectivity and keep-alive management.
The next tutorial in this MQTT series will cover how the STM32 Client can subscribe to the topics and receive the messages. You can check the other STM32 IoT tutorials listed below.
Browse More STM32 Iot Tutorials
STM32 IoT with ESP8266 (Part 2): – Send Sensor Data to ThingSpeakCloud
STM32 IoT with ESP8266 (Part 3): MQTT Connect and Publish messages
STM32 ESP8266 MQTT Project Download
Info
You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.
STM32 ESP8266 MQTT FreeRTOS FAQs
You can either increase the queue size in osMessageQueueNew
, implement logic to overwrite the oldest message when full, or use backpressure (blocking publishers until space is available). The right choice depends on whether losing old messages is acceptable for your application.
Yes. One approach is to create multiple queues with different priorities, and have the MQTT task check them in order of importance. Alternatively, you can use FreeRTOS message priorities by adjusting the msg_prio
parameter in osMessageQueuePut
.
The MQTT task will detect publish failures and attempt reconnection. However, it’s good practice to add a watchdog or status flag that notifies your application when Wi-Fi is disconnected, so you can buffer or log messages until the link is restored.
Yes. You can add a dedicated "subscriber task" that listens for incoming MQTT messages (via the ESP8266 UART) and posts them into a queue for processing by other tasks. This keeps publishing and subscribing independent but coordinated.
Instead of using a public broker without encryption, enable MQTT over TLS (if supported by the ESP8266 AT firmware) or use a more advanced Wi-Fi module that supports SSL/TLS directly. You should also implement authentication (username/password or certificates) to protect your data.
Search This Site
Subscribe
