esp32-net6-mqtt

Cómo conectar un ESP32 con NET6 por MQTT

Finalizamos la serie de entradas destinadas a ver formas de comunicación entre un ESP32 y una aplicación en NET6 escrita en C#. En esta ocasión haremos la comunicación a través de MQTT.

En entradas anteriores de la serie hemos visto otras formas de comunicación entre el ESP32 y la aplicación en NET6, como comunicación por el puerto de serie, a través de peticiones http y a través de WebSockets.

Antes de entrar en detalles, recordamos que NET6 es compatible con una amplia gama de plataformas, incluyendo Windows, Android, Linux, Mac, x86/x64 y ARM7 o superior. Por tanto, podemos usar el mismo código en diferentes dispositivos, como ordenadores de sobremesa con Windows o Linux, móviles Android o Raspberry Pi.

Para aquellos que no estén familiarizados con MQTT, es un protocolo de comunicación bajo patrón PubSub en que los clientes se conectan a un servidor (broker) y envían o reciben mensajes a través de “tópicos”. MQTT es especialmente útil en aplicaciones de IoT por su bajo consumo de energía y ancho de banda. Si queréis más información, revisar está sección.

Código ESP32

Empecemos con la parte relativa al ESP32. Como siempre, organizamos nuestro código en ficheros, agrupando por funcionalidades. Así, tenemos el fichero MqttService.hpp, que contiene la parte de lógica de nuestro proyecto relativa al 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);
  }
};

Aquí simplemente hemos definido las funciones de callback necesarias para el funcionamiento. Nos suscribimos al topic correspondiente y, al recibir un mensaje, simplemente imprimimos el contenido por Serial. Por otro lado, hemos definido dos funciones para enviar ‘A’ y ‘B’.

Como hemos dicho en las entradas anteriores, en un proyecto real tendríais que adaptar la lógica de este fichero a las necesidades de vuestro proyecto. Para lo que nos ocupa en este ejemplo, que es la comunicación, es suficiente.

Ahora, en nuestro sketch principal únicamente tenemos que inicializar el WiFi y nuestro servicio de Mqtt.

void setup() {
  Serial.begin(115200);

  WIFI_Connect();
  delay(2000);

  mqttService.Init();
}

Finalmente, en nuestro ejemplo, en el bucle principal únicamente enviamos ‘A’ y ‘B’ a través de Mqtt de forma alterna, al pulsar el botón.

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();
}

Código NET6

En cuanto a la parte de NET6 usaremos la librería MQTTnet que ya vimos en esta entrada. Al igual que en el caso del ESP32, y en las entradas anteriores, lo primero que hacemos es crearnos un objeto que se encargue de la gestión.

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));

    }
}

Con este fichero, la parte de C# sería tan sencilla como lo siguiente.

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();

En el ejemplo mostrado, hemos enviado la hora actual a través de MQTT cada 2 segundos. Esto es solo un ejemplo y, en un proyecto real, deberíamos integrar esta comunicación en la lógica del programa. Sin embargo, para ilustrar cómo funciona la comunicación a través de MQTT, este ejemplo es suficiente.

En conclusión, en esta serie de entradas hemos visto cómo establecer la comunicación entre un ESP32 y una aplicación en NET6 utilizando diferentes medios, como el puerto de serie, las peticiones HTTP y WebSockets, y finalmente MQTT. ¡Esperamos que esta serie de entradas os sea útil como ayuda o inspiración para vuestros proyectos!

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github. github-full