We conclude the series of posts aimed at exploring ways of communication between an ESP32 and a NET6 application written in C#. This time we will communicate via MQTT.
In previous posts of the series, we have seen other ways of communication between the ESP32 and the NET6 application, such as communication via the serial port, through HTTP requests, and through WebSockets.
Before getting into details, we remind you that NET6 is compatible with a wide range of platforms, including Windows, Android, Linux, Mac, x86/x64, and ARM7 or higher. Therefore, we can use the same code on different devices, such as desktop computers with Windows or Linux, Android mobile phones, or Raspberry Pi.
For those unfamiliar with MQTT, it is a communication protocol under the PubSub pattern where clients connect to a server (broker) and send or receive messages through “topics”. MQTT is especially useful in IoT applications due to its low power and bandwidth consumption. If you want more information, check out this section.
ESP32 Code
Let’s start with the part related to the ESP32. As always, we organize our code into files, grouping by functionalities. Thus, we have the MqttService.hpp file, which contains the logic part of our project related to MQTT.
class MqttService
{
public:
void Init()
{
mqttClient.setServer(MQTT_BROKER_ADRESS, MQTT_PORT);
SuscribeMqtt();
mqttClient.setCallback([this](char* topic, uint8_t* payload, unsigned int length) -> void
{
this->OnMqttReceived(topic, payload, length);
});
}
void ConnectMqtt()
{
while (!mqttClient.connected())
{
Serial.print("Starting MQTT connection...");
if (mqttClient.connect(MQTT_CLIENT_NAME))
{
SuscribeMqtt();
}
else
{
Serial.print("Failed MQTT connection, rc=");
Serial.print(mqttClient.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void HandleMqtt()
{
if (!mqttClient.connected())
{
ConnectMqtt();
}
mqttClient.loop();
}
void SuscribeMqtt()
{
mqttClient.subscribe("hello/world/net");
}
String payload;
void SendB()
{
String topic = "hello/world/esp32";
payload = "A";
mqttClient.publish(topic.c_str(), payload.c_str());
}
void SendA()
{
String topic = "hello/world/esp32";
payload = "B";
mqttClient.publish(topic.c_str(), payload.c_str());
}
String content = "";
void OnMqttReceived(char* topic, uint8_t* payload, unsigned int length)
{
content = "";
for (size_t i = 0; i < length; i++)
{
content.concat((char)payload[i]);
}
Serial.println(content);
}
};
Here we have simply defined the necessary callback functions for operation. We subscribe to the corresponding topic and, upon receiving a message, we simply print the content via Serial. On the other hand, we have defined two functions to send ‘A’ and ‘B’.
As mentioned in previous posts, in a real project you would need to adapt the logic of this file to the needs of your project. For what concerns us in this example, which is communication, it is sufficient.
Now, in our main sketch we only need to initialize WiFi and our MQTT service.
void setup() {
Serial.begin(115200);
WIFI_Connect();
delay(2000);
mqttService.Init();
}
Finally, in our example, in the main loop we only send ‘A’ and ‘B’ via MQTT alternately when the button is pressed.
bool isOn = false;
void Update()
{
if (M5.BtnA.wasPressed())
{
if (isOn == false)
{
isOn = true;
Serial.println("A");
mqttService.SendA();
}
else if (isOn == true)
{
isOn = false;
Serial.println("B");
mqttService.SendB();
}
}
}
void loop()
{
mqttService.HandleMqtt();
delay(10);
Update();
}
NET6 Code
Regarding the NET6 part, we will use the MQTTnet library that we already saw in this post. As in the case of the ESP32 and previous posts, the first thing we do is create an object to handle the management.
internal class MqttService
{
private IMqttClient mqttClient;
public async Task Initialize(string brokerIpAddress)
{
var factory = new MqttFactory();
var options = new MqttClientOptionsBuilder()
.WithClientId("Client1")
.WithTcpServer(brokerIpAddress, 1883)
.Build();
mqttClient = factory.CreateMqttClient();
mqttClient.ConnectedAsync += (async e =>
{
await mqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("hello/world/esp32").Build());
});
mqttClient.ApplicationMessageReceivedAsync += (async e =>
{
Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}");
});
await mqttClient.ConnectAsync(options, CancellationToken.None);
}
public async Task SendMqtt(object payload)
{
var json = JsonSerializer.Serialize(payload);
var applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic("hello/world/net")
.WithPayload(json)
.Build();
await Task.Run(() => mqttClient.PublishAsync(applicationMessage));
}
}
With this file, the C# part would be as simple as the following.
var brokerIpAddress = "192.168.1.xxx";
Console.Write(" - Initializing MQTT:");
var mqttService = new MqttService();
await mqttService.Initialize(brokerIpAddress);
Console.WriteLine(" OK");
var timer = Observable.Interval(TimeSpan.FromSeconds(2))
.Subscribe(async _ =>
{
try
{
await mqttService.SendMqtt(new { data = System.DateTime.Now });
}
catch (Exception ex)
{
Console.WriteLine($"{DateTime.Now} Error sending: {ex.Message}");
}
});
Console.ReadLine();
In the example shown, we have sent the current time via MQTT every 2 seconds. This is just an example and, in a real project, we should integrate this communication into the program’s logic. However, to illustrate how communication via MQTT works, this example is sufficient.
In conclusion, in this series of posts we have seen how to establish communication between an ESP32 and a NET6 application using different means, such as the serial port, HTTP requests and WebSockets, and finally MQTT. We hope this series of posts is useful as help or inspiration for your projects!
Download the Code
All the code from this post is available for download on Github.

