0:09
Hello and welcome to ControllersTech. In today’s session, we will continue
0:14
working with the STM32 IoT series using the ESP8266 Wi-Fi transceiver module.
0:22
In the earlier parts of this series, we learned how to connect the STM32
0:26
with the ESP8266 module, how to link the module and the board to a Wi-Fi network,
0:33
and finally how to send data to the Thingspeak cloud using a TCP connection.
0:39
Now, we are starting a new mini-series that focuses on the MQTT protocol.
0:43
In this mini-series, the STM32 will act as an MQTT client. This means it will have the
0:51
ability to both publish messages to topics and subscribe to topics in order to receive messages.
0:58
I am calling it a “mini-series” because the MQTT protocol will be covered in about 3 to 4 parts.
1:04
While writing the code, I will also explain the protocol details step by step. Since the
1:10
protocol has several elements, it will take a little time to cover everything properly.
1:16
In today’s video, we will specifically focus on two important parts: the connect packet
1:21
and the publish packet sent by the client. We will go through the packet structure,
1:26
understand how these packets are created, how to send them to the broker,
1:30
and finally how to handle the acknowledgement messages that come back from the broker.
1:34
To make things easier, I have prepared an MQTT document that explains these concepts.
1:40
This document only contains the most important details that we will actually use in this MQTT
1:47
series, while skipping the extra parts that are not relevant right now. If you would like
1:51
the complete version of the document, you can find the link in the description of this video.
1:56
We will use this document later in the video when I explain the MQTT functions in more detail.
2:02
Before we move forward, please make sure you have already watched the first part of this series,
2:07
because from this point on, I will assume that the STM32 is already connected to the internet.
2:14
Now, before diving into the coding, let’s set up a few things that we will need.
2:19
First, we require another MQTT client to monitor the messages that will be published
2:25
by STM32. For this purpose, I will use MQTT Explorer, which is a simple and useful tool.
2:33
You can download MQTT Explorer directly from their official website. I already have it installed
2:39
on my computer, so we will use that here. The next requirement is a broker. You can
2:44
use any MQTT broker of your choice, but in this video, I will be using the HiveMQ public broker.
2:51
To access this broker, simply visit hivemq.com. Under the MQTT section,
2:57
click on “MQTT Public Broker.” Here you will see the HiveMQ public
3:02
broker page. Click on the Connect button, and it will display the broker details.
3:08
From this, we only need two things: the host address and the TCP port. I will be using the TCP
3:15
port because it allows for an easy connection to the broker without needing certificates.
3:21
Although this method is not secure, it is simpler to use when starting out. Don’t worry,
3:27
we will cover secure MQTT communication using SSL certificates in one of the future videos.
3:34
Once you have the host and port, go back to your MQTT client and enter
3:39
those details in the respective fields. After that, click on the “Advanced”
3:43
section to add topics. I have already added two topics for this client so
3:48
that it can publish and subscribe to them. Adding a topic is very simple. Just type the
3:54
topic name and click Add. Once you do this, the client will automatically subscribe to that topic.
4:01
Now, any messages that are published to these topics will appear right
4:04
here in the MQTT Explorer client. Finally, after adding the topics,
4:10
click on Connect to establish a connection between the client and the broker.
4:14
At this point, one of our clients—the MQTT Explorer—is ready. Now, the next step is to
4:21
prepare the other client, which is our STM32. I am going to continue with the project that we
4:28
created in Part 1 of this IoT series. As you can see here, the project
4:32
from Part 1 is already loaded into the IDE. To keep things organized and avoid any confusion
4:39
in future videos, let’s rename this project from Part 1 to Part 3. While renaming, make sure to
4:45
also check the option to update references, so that all linked files are updated automatically.
4:51
Alright, the renaming is complete. All the references
4:54
that mentioned Part 1 are now updated to Part 3. The only exception is the debug configuration.
5:02
Let’s delete it, because it will be automatically regenerated the next time
5:06
we launch the debugger for this project. Now, we also need to delete the ESP8266
5:13
library files from the project. That’s because we are going to copy new files
5:17
that contain the updated MQTT-related functions. I’m copying those new files here now. You can
5:24
download these files yourself from the link provided in the description of this video.
5:29
Let’s open the ESP8266 source file and see what has changed.
5:34
This file still contains the Thingspeak function from the previous video. Because of that, you will
5:40
notice a printf error for floating-point values. We don’t actually need the Thingspeak function
5:46
anymore, but instead of deleting it, I have decided to leave it in the file. To fix the
5:51
printf error, we just need to enable float printf support in the project settings.
5:57
Once that is done, let’s review the source file again.
6:00
Here we have several functions already explained in the earlier parts of this series:
6:05
ESP_Init – used to initialize the ESP8266 module.
6:12
ESP_ConnectWiFi – connects the module to a known Wi-Fi network.
6:17
ESP_GetConnectionState – checks the current connection status of the module.
6:23
ESP_SendToThingSpeak – sends data to the Thingspeak cloud.
6:28
Now, for the MQTT implementation, I have added some new functions:
6:33
ESP_MQTT_Connect – to connect the module to an MQTT broker.
6:39
ESP_MQTT_Publish – to publish a message to a topic.
6:44
ESP_MQTT_Ping – to send a keep-alive ping to the broker.
6:49
Apart from these global functions, there are also some static functions
6:54
specifically for MQTT communication. For example, we now have ESP_SendBinary,
7:01
which will be used to send MQTT commands. Normally, we use ESP_SendCommand for sending
7:08
ASCII-based AT commands, but since MQTT requires binary data, a new function was necessary.
7:15
You’ll see why this is important very soon. Another new function is ESP_MQTTBuildConnect,
7:22
which builds the MQTT connect command that allows the client to register itself with the broker.
7:29
Let’s now go through these functions in detail, starting with ESP_MQTT_Connect.
7:35
This function is used by the STM32 client to connect to the broker. Its parameters include:
7:45
The client ID, which identifies this client to the broker
7:49
A username and password, if required by the broker
7:53
And finally, the keep-alive time in seconds
7:57
At the beginning of this function, we send the AT+CIPSTART command.
8:02
This command opens a TCP connection with the broker at the given address and port.
8:07
If the broker accepts the connection, it responds with a CONNECT acknowledgement.
8:12
We already saw something similar in the previous video when we connected to the Thingspeak cloud.
8:17
Once the connection is established, the next step is to send the connect packet
8:22
to the broker. This packet officially registers the STM32 as an MQTT client.
8:29
Before sending it, though, we first need to build the connect packet
8:33
using the function parameters we just discussed.
8:36
Let’s understand how this connect packet is structured with the help of the MQTT document.
8:42
Every MQTT control packet is made up of three parts:
8:46
A Fixed Header, which is common to all MQTT packets
8:50
A Variable Header, which is present in some packets
8:54
A Payload, which is also present in some packets Let’s now take a closer look at the MQTT
8:59
control packet for the Connect command. The fixed header in this packet is 2 bytes long.
9:05
The first byte represents the packet type—in this case, the Connect packet—which has a value
9:11
of 0x10. The second byte represents the remaining length of the command.
9:17
The remaining length indicates the total size of the variable header plus the payload.
9:23
Inside the buildConnect function, we start by storing the value 0x10
9:29
to represent the Connect packet type. The remaining length is skipped at this stage,
9:34
because we don’t know it yet. This position will be filled later, after the entire control
9:39
packet is built and we know its exact length. With that, the fixed header is complete. Next,
9:46
we move on to preparing the variable header. The variable header begins with the protocol
9:51
name. To store this, we first write two bytes—0x00 and 0x04—which indicate the
10:00
length of the protocol name. After that, we store the actual name itself, which is "MQTT".
10:06
In the buildConnect function, you’ll see this being done: first 0x00, then 0x04,
10:15
followed by the four characters of "MQTT". Next, we store the protocol level. Since
10:20
we are using MQTT version 3.1.1, the protocol level value will be 0x04. This
10:29
is also handled in the buildConnect function. After that, we set the Connect Flags. These flags
10:36
configure different options for the behavior of the MQTT connection. To enable a feature,
10:41
we set the corresponding bit in this byte. For example, if the broker requires a username and
10:48
password, their respective flags will be set here. Other flags, such as Retain, Quality of Service,
10:55
or Will parameters, are not covered in this series, so we will skip them for now.
11:00
However, one important flag is the Clean Session bit, which must always be set. This ensures
11:06
that the broker does not store any previous session information and starts fresh each time.
11:12
If the username and password are included in the function parameters,
11:16
their flags will also be enabled here. Once the connect flags are set,
11:21
we define the Keep Alive Interval. The keep alive is a time interval, defined by
11:26
the client, that ensures the TCP connection with the MQTT broker remains active. It’s 2 bytes long,
11:34
so we must split the 16-bit parameter into two bytes, sending the higher byte first,
11:40
followed by the lower byte. That completes the variable
11:43
header section of the Connect packet. Now we move on to the final part, the payload.
11:48
The payload may contain several items, such as the Client ID, Will topic, Will message,
11:55
Username, and Password. But in every case, it must at least include the Client ID.
12:01
The Client ID comes from the function parameter. To store it, we first calculate its length.
12:07
Then, we send two bytes to indicate that length, followed by the actual ID data.
12:13
If the function parameters also include a username and password,
12:18
these are added in the same way—first the 2-byte length field, and then the data itself.
12:24
Once the entire Connect packet is built, we now have the correct total length
12:29
of the packet stored in a variable. We then go back and place this length
12:34
value into the fixed header, but with one adjustment: we subtract 2 from it.
12:39
This is because the two bytes of the fixed header itself are not included
12:43
in the “remaining length” field. Instead, that field should only represent the combined
12:48
length of the variable header and the payload. Finally, the buildConnect function returns the
12:54
total length of the entire packet. This includes the fixed header as well, so we know the complete
13:00
size of the Connect packet at the end. After building the Connect packet,
13:05
the next step is to inform the ESP module about how many bytes we are going to send.
13:10
We do this using the AT+CIPSEND command. When this command is sent, the ESP responds
13:17
with a greater-than sign (>), which indicates that it is now ready to receive the data.
13:22
At this point, we send the entire Connect packet using the ESP_SendBinary function.
13:29
This function takes the following parameters: The control packet we want to send
13:34
The length of the control packet
13:36
The expected response from the ESP
13:39
And the timeout value in milliseconds
13:42
Once the expected response is received, it means that the STM32
13:47
client has successfully connected to the broker. Now let’s look at a quick example to understand
13:52
how these commands appear in practice, and also why we can’t use our regular
13:57
ESP_SendCommand function to send MQTT packets. Here is an example of an MQTT Connect packet
14:04
without a username and password. The fixed header comes first,
14:09
containing two values: the packet type 0x10 and the remaining length, which in this case is 23
14:16
bytes. These 23 bytes represent the total size of the variable header and payload combined.
14:23
The variable header begins with 0x00 and 0x04, which indicate the length of the
14:31
protocol name. This is followed by the actual protocol name “MQTT”, the protocol level 0x04,
14:39
the clean session flag (0x02), and the keep-alive time of 60 seconds.
14:45
Next comes the payload. Since we are not using a username and password in this example, the payload
14:51
only includes the Client ID. The packet first stores the two-byte length of the Client ID,
14:57
followed by the actual Client ID itself. This gives us the final Connect
15:02
packet in string format. Now notice something important:
15:06
this packet contains a 0x00 byte. Our regular ESP_SendCommand function
15:13
relies on strlen() to calculate the length of the command string. However,
15:20
strlen() treats 0x00 as a terminating character. Because of that, it would assume the packet
15:28
length is only 2 bytes, which is incorrect. This is exactly why we created the ESP_SendBinary
15:36
function. Instead of calculating the length inside the function, we pass the packet length
15:42
directly as a parameter, ensuring the entire packet—including the 0x00 bytes—is sent correctly.
15:51
Let’s now look at another example where we connect to the broker using both a username and password.
15:57
Most parts of the Connect packet remain the same, but there are a few key changes.
16:02
The fixed header now shows a larger remaining length,
16:06
since it must also include the size of the username and password fields. In this case,
16:12
the remaining length becomes 38 bytes, assuming the username is user1 and the password is pass1.
16:20
The variable header is almost identical to the earlier example.
16:24
The only difference is in the Connect Flags. Here, we need to enable the Username and
16:30
Password flags along with the Clean Session flag. This makes the Connect Flags byte equal to 0xC2.
16:37
Finally, the payload includes three items: the Client ID, the Username, and the Password.
16:45
For each of these fields, we first send the 2-byte length, followed by the actual data.
16:51
The result is our complete MQTT Connect packet with authentication.
16:56
To summarize, this packet consists of: 2 bytes of fixed header
17:00
10 bytes of variable header
17:03
And 26 bytes of payload
17:05
Together, they form the full Connect packet containing the Client ID, Username, and Password.
17:12
When we send these Connect packets to the broker, the broker replies with a Connect Acknowledgement.
17:17
The fixed header of this acknowledgement packet contains:
17:21
The acknowledgement packet type, which is 0x20
17:26
And the remaining length, which is always 2 bytes
17:29
The variable header of the acknowledgement is also 2 bytes long.
17:34
The first byte contains only the Session Present flag, while the second byte is 0x00.
17:41
If the Session Present flag is set to 0, that means the connection request
17:46
has been accepted by the broker. So this is what the final Connect
17:50
Acknowledgement packet looks like in the case of a successful connection.
17:54
And with that, our MQTT_Connect function is complete.
17:59
Now that the client is connected to the broker, it can start publishing messages to any topic.
18:05
For this, we use the MQTT_Publish function, which takes the following parameters:
18:14
And the Quality of Service level
18:16
To keep things simple, I will assume that the Quality of Service is always
18:20
0 throughout this series. Just like the Connect packet,
18:24
the Publish packet is also made up of a fixed header, a variable header,
18:29
and the payload. Let’s quickly go through this with the help of the document.
18:33
The first byte of the fixed header contains the publish packet type,
18:37
which is 0x30. Along with it, there are flags such as Duplicate, Quality of Service, and Retain.
18:45
Since we are assuming all these flags are 0, the fixed header becomes 0x30. This is followed by
18:53
the remaining length, which specifies the size of the variable header and payload.
18:58
Next is the variable header, which consists of the topic name.
19:02
Here, we first send the 2-byte length of the topic name, followed by the topic name itself.
19:09
Finally, we have the payload, which is simply the message the client wants to send. Unlike
19:14
the topic name, the payload does not include a length field—it only contains the message itself.
19:22
Once the total packet is ready, we calculate its length and store it in the “remaining
19:27
length” field of the fixed header. Here is an example of a Publish
19:31
packet with Quality of Service set to 0. As you can see, we first have the fixed header,
19:37
then the variable header with the topic name, and finally the message in the payload.
19:42
This completes the Publish packet. Since the Quality of Service is 0,
19:47
the broker will not send back an acknowledgement for this Publish packet.
19:51
After preparing the Publish packet, we again inform the ESP module about how
19:56
many bytes we are going to send by using the AT+CIPSEND command.
20:01
The module will respond with the greater-than sign (>), showing that it is ready to receive the data.
20:07
Next, we send the Publish packet, and then wait for
20:10
the SEND OK acknowledgement from the ESP module. Keep in mind that there will be no acknowledgement
20:15
from the broker in this case, so we must rely on the acknowledgement coming from the module itself.
20:21
We will talk about the Ping Request in the next video, but before that, let’s test the Connect
20:26
and Publish packets we just created. I have also added a function that
20:31
allows us to check the current connection status with the broker.
20:34
The command used is AT+CIPSTATUS, which tells us if the module is still connected.
20:41
If the connection is active, the module responds with STATUS:3.
20:46
This check can be performed before publishing data to a topic, just to ensure that the
20:51
connection with the broker is still alive. Now let’s move on to writing the main code.
20:56
After the ESP module is successfully connected to the Wi-Fi network,
21:00
we will call the ESP_MQTT_Connect function to establish a connection with the broker.
21:06
In this function, we need to provide the following details:
21:13
The client ID that we want to assign to our STM32
21:17
Username and password, if the broker requires authentication
21:21
And finally, the keep-alive interval in seconds
21:25
If the connection is successful, the module will respond with an OK.
21:30
Next, I will publish messages continuously inside the while loop.
21:34
Before publishing, we first check if the module is still connected to the broker. If the connection
21:40
is valid, we then call the ESP_MQTT_Publish function to send data to the topic.
21:47
For this example, I will publish to the topic controllerstech/test1,
21:52
and the message will remain the same each time. The Quality of Service will be 0.
21:58
Let’s build the project now. The build is successful,
22:01
which means we are ready to flash it to the board.
22:04
The hardware connections remain the same as in the first part of this series:
22:08
The RX pin from the adapter is connected to PA0 (UART4 TX).
22:14
The TX pin from the adapter is connected to PA1 (UART4 RX).
22:19
The ESP adapter is powered by 5 volts from the STM32 board itself,
22:25
but make sure that the USB cable is connected to the development board.
22:29
The ST-Link V2 only provides a 3.3V supply to the board. So if you want,
22:36
you can use an external 5V supply for the adapter, but in that case, remember to
22:41
connect the ground of the external supply with the STM32 development board’s ground as well.
22:47
The PA9 (UART1 TX) pin is connected to the RX pin of the FT232 adapter. Since we
22:56
only need to send logs to the console, connecting just the TX pin is enough.
23:01
Now, let’s flash the project to the board.
23:04
Once done, open the serial port on the console at a baud rate of 115200 to view the logs.
23:11
The initialization process begins. At the same time, I have MQTT Explorer
23:17
running as another client connected to the same broker, subscribed to the same topic.
23:23
This allows us to verify the messages being published by the STM32 client.
23:28
You can see from the logs that the broker has accepted the connection,
23:32
and data publishing has started. The message appears on the MQTT Explorer
23:37
client under the topic controllerstech/test1. The STM32 has published another message,
23:44
and it has also appeared in MQTT Explorer. Since the message content is the same each time,
23:51
it looks identical. But if you check under the main topic controllerstech, you can
23:55
see that there are now 3 messages in total. This confirms that the STM32, as an MQTT client,
24:03
is able to connect to the broker and publish messages to a topic successfully.
24:08
Now let’s modify the main function so that the STM32 sends different messages each time.
24:16
First, define a count variable that will increment with every loop.
24:20
We also need a count array to store the value of this variable in character form.
24:25
Inside the while loop, use sprintf to convert the count value into a string
24:30
and store it inside the count array. Then increment the count variable,
24:34
so each iteration produces a new value. Finally, pass this count array as the
24:40
message in the ESP_MQTT_Publish function to publish it to the topic.
24:46
Let’s build and flash the project again. I’ll clear the old data from MQTT Explorer
24:51
for a fresh start. Now, the STM32 client
24:55
has successfully connected to the broker and started transmitting updated values.
25:00
You can clearly see the new values being published to the topic in MQTT Explorer,
25:06
proving that the setup is working perfectly. So, the STM32, acting as an MQTT client,
25:14
is able to connect, publish fixed messages, and also publish dynamic messages to the broker.
25:20
In the next video, we will cover why and how to send a Ping Request to the broker.
25:25
After that, I’ll show you how to publish messages using RTOS, making the process
25:31
more practical and efficient. And in the video following that,
25:35
we will learn how to subscribe to a topic and make use of the data received from it.
25:40
That’s it for today’s video. I hope the explanation was clear. You can download
25:44
the project from the link in the description. If you have any doubts,
25:48
feel free to leave them in the comments. Keep watching, and have a great day ahead.