Objetivos

 

  • Algunas consideraciones previas.
  • Componer el mensaje a publicar.
  • Publicando en broker MQTT.[/three-fourth] [clear/]
  •  

    Material requerido

     

     Vista mayor  Una servidor MQTT
    modelo esp32 con menos pines  Un ESP32

     

    Organizando nuestros tópicos MQTT

     

    En un ejemplo anterior, ya simulamos datos reales con nuestro ESP32 mediante valores aleatorios para subirlos a nuestro servidor Mosquitto. En esta sesión, vamos a mostrar un programa real que envíe datos desde un sensor SCD41 que nos permite medir CO2, temperatura y humedad ambiente relativa.

    Para ello usaremos el montaje que ya usamos en la sesión en que usamos el kit SCD41 y refinaremos el programa del ESP32 para generar datos reales que enviaremos al broker MQTT para que quien quiera pueda suscribirse.

    Para ello repetiremos el montaje que hicimos alli y que os recomiendo revisar antes de seguir con esta sesion, pero la base es un montaje como este:

    Conexion SCD41 con ESP32

    Basta conectar la alimentación y GND, mas los pines I2C Según esta tabla

    ESP32 GND 3.3V 22 21
    SCD41 GND
    (Negro)
    VDD
    (Rojo)
    SCL
    (Amarillo)
    SDA
    (Verde)

    La forma en que enviaremos los datos desde el ESP32 al MQTT nos obligará a tomar algunas decisiones, que pueden no ser sencillas. Me explico; Vamos a enviar una serie de lecturas desde el ESP32 al MQTT para que posteriormente, Telegraf las reciba e inserte los datos en Influx DB, pero tenemos que decidir como queremos enviar esos datos y esto es un problema / compromiso que aparecerá casi siempre a la hora de subir los datos, porque la forma de cómo queremos explotarlos determinará la manera de empaquetar los datos desde el principio.

    Veamos un ejemplo con nuestro sensor de muestra el SCD41. Podemos obtener 3 lecturas de el:

    CO2.
    Temperatura,
    Humedad.  

    Pero también nos interesa añadir algo así como un ID, dado que podemos tener una docena de sensores repartidos por ahí (O imagínate uno en cada clase de un colegio o de una provincia, o …) parece que necesitaremos añadir al menos, un código que identifique al sensor que envía el dato:

    ID = IDXXX

    Este código identificará el origen de las medidas que enviamos y puede tener el formato que queramos, dependiendo del numero de estaciones que tengamos y de la identificación de cada una que necesitemos. Aquí no vamos a comernos el tarro excesivamente porque este es un problema distinto, peri normalmente es buena idea pensar en esto antes de empezar o el infierno de los informáticos aparecerá muy deprisa.

    Tenemos que determinar ahora como nos interesa enviar la medida. Podríamos elegir algo así:

    “ID=ID005, CO2=450, TEMP=22.1, HUME=34.7”

    Con una única línea, englobamos las tres variables y el origen. Parece cómodo y eficiente desde el ESP32… pero, por ejemplo, ya te va a costar usar el MQTT Explorer para ver los datos sobre la marcha, lo cual no es grave, pero cuando Telegraf (O Python o Nodered, ya que estamos) suscriba este dato tendrá que hacer una operación de desempaquetado para meter cada valor en su serie, y como lo de trabaja no nos va mucho el tema parece peliagudo.

    Otra solución sería crear tres series de datos. Es más pesado, pero más fácil de desempaquetar luego:

    “ID=ID005, CO2=450”
    “ID=ID005, TEMP=22.1”
    “ID=ID005, HUME=34.7”

    Vamos a tener que elegir una clave general que diferencie nuestras medidas de otras mil que pueden estar entrando al broker MQTT. En mi caso he elegido que sea Prosensor (Por razones poco relevantes ahora mismo). Por ello podríamos, pensando en la publicación y recepción en el programa que procese la información para insertarla en la BD que podría ser interesante publicar en el MQTT de este otro modo:

    /Prosensor/ID/CO2
    /Prosensor/ID/TEMP
    /Prosensor/ID/HUMED

    Con este sistema podríamos jugar con el ID para organizar nuestros sensores y podríamos usar el MQTT explorer para ver cómo va el COdel sensor 12 ya que podría suscribir

    /Prosensor/ID012/CO2

    Con eso podría organizar bastante bien la publicación fisco, y puedo cambiar sobre la marcha. O, tal vez, prefiramos empaquetar una cadena JSON que incorpore todo de una tacada como hacen los sistemas de información meteorológica:

    {  “ID” : ID005,
       “CO2” : 450,
       “TEMP”: 22.1,
       “HUME”: 34.7
    }

    La virtud de JSON es que si mañana tengo que añadir un sensor de VOCs (Volatile Organic Compunds) como el SGP41 por ejemplo, seria tan sencillo como añadir la 5ª línea sin tocar nada y solo habrá que programar el sistema que procese ese último valor (Por ahora) de la cadena JSON.

    Así que… ¿Qué formato elegimos?

    No hay una respuesta buena y otra mala, si no un enorme depende (¿Que te creías?) Depende de como quieras procesar los datos o archivarlos o buscarlos… AL final todos tienen sus ventajas e inconvenientes y elegiremos uno y ya está

     

    Subiendo lecturas de ESP32 a MQTT

     

    Como vamos a insertar nuestras lecturas en Influx DB, yo voy a elegir un sistema cómodo para que puedan entrar directamente sin complicaciones por lo que, en este ejemplo, generaremos líneas de medidas compatibles con el formato ‘line’ de Influx que, básicamente, es lo que escribiríamos a mano desde el CLI de Influx para insertar los datos. Enseguida veremos como-

    En la sesión ESP32 y Broker MQTT , instalamos la librería PubSub que es la que vamos a utilizar también hoy y empezaremos el programa con los incluidas habituales:

    #include <Arduino.h>
    #include <SensirionI2CScd4x.h>
    #include <Wire.h>
    #include <WiFi.h>
    #include <PubSubClient.h>

    Incluimos las librerías del sensor SCD41, el I2C mas el WIFI para conectar al broker, y ahora algunas definiciones y datos necesarios para conectar:

    const char* ssid = "REDLINEB";
    const char* password = "contrase";
    const char* mqttServer = "192.168.1.36";
    const int mqttPort = 1883;
    const char* mqttUser = "charly";
    const char* mqttPassword = "contrase";
    
    uint16_t co2;
    uint16_t temperature;
    uint16_t humidity;

    Recordad cambiar estos valores por los vuestros propios de SSID y contraseñas, así como los del servidor MQTT. Creamos instancias de todo para poder trabajar:

    ensirionI2CScd4x scd4x;
    WiFiClient espClient;
    PubSubClient client(espClient);

    Tenemos que conectar a la WIFI primero:

    WiFi.begin(ssid, password);
    Serial.println("...................................");
    Serial.print("Connecting to WiFi.");
    while (WiFi.status() != WL_CONNECTED)
    {  delay(500);
       Serial.print(".") ;
    }
    Serial.println("Connected to the WiFi network");

    Y al broker MQTT después:

    client.setServer(mqttServer, mqttPort);
    while (!client.connected())
    {    Serial.print("Connecting to MQTT...");
         if (client.connect("ESP32Client", mqttUser, mqttPassword )) 
             Serial.println("connected");
         else
          {  Serial.print("failed with state ");
             Serial.print(client.state());
             delay(2000);
          }
    }

    Vamos a crear un par de funciones instrumentales para arrancar el ciclo de medidas y otro para imprimir los valores. Empecemos por el inicio de las medidas:

    void StartMeasures()
    {   uint16_t error;
        char errorMessage[256];
    
        scd4x.begin(Wire);  // stop potentially previously started measurement
        error = scd4x.stopPeriodicMeasurement();
        if (error)
         {  Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
            errorToString(error, errorMessage, 256);
            Serial.println(errorMessage);
         }
     }

    El objetivo de esta función, es detener las medidas periódicas, si estuvieran en marcha, comprobando que no haya errores y son simplemente las instrucciones que recomienda el fabricante. Ahora una segunda función que se encarga de mandar a la consola las medidas (Con una serie de cálculos qu son necesarios para convertir las medidas internas a los valores de ºC y humedad relativa en %):

    void PrintMeasures()
     {   Serial.print("Co2:");
         Serial.print(co2);
         Serial.print("\t\t");
         Serial.print("Temperature:");
         Serial.print(temperature * 175.0 / 65536.0 - 45.0);
         Serial.print("\t");
         Serial.print("Humidity:");
         Serial.println(humidity * 100.0 / 65536.0);
     }

    En el setup llamaremos a la función que nos conecta al WIFI y al MQTT y pondremos en marcha las medidas periódicas, controlando posibles errores. También es todo, básicamente lo que recomienda el fabricante en sus ejemplos de uso:

    void setup()
     {   Serial.begin(115200);
         connect_Wifi_MQTT();
         Wire.begin();
    
         uint16_t error;  
         char errorMessage[256];
         scd4x.begin(Wire);
         error = scd4x.stopPeriodicMeasurement();
         if (error) 
          {  Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
             errorToString(error, errorMessage, 256);
             Serial.println(errorMessage);
          }
        error = scd4x.startPeriodicMeasurement();
        if (error) 
         {  Serial.print("Error trying to execute startPeriodicMeasurement(): ");
            errorToString(error, errorMessage, 256);Serial.println(errorMessage); 
         }
    }

    El loop es ya trivial:

    void loop()
    {   uint16_t error;
        char errorMessage[256];
    
        delay(5000);
        client.loop();

    El delay de 5 segundos es necesario para dar tiempo al sensor a inicializar, porque si no, obtendremos con mucha frecuencia errores raros y he visto que es mejor dar este retraso para eliminarlos, dando tiempo al sensor a inicializarse correctamente.

    error = scd4x.readMeasurement(co2, temperature, humidity);

    La función para realizar las lecturas requiere que le pasemos unas variables en las que depositará los resultados, Ahora toca hacer un control de errores:

    if (error)
    {  Serial.print("Error trying to execute readMeasurement(): ");
       errorToString(error, errorMessage, 256);
       Serial.println(errorMessage);
    } 
    else if (co2 == 0)
       Serial.println("Invalid sample detected, skipping.");
    else
    {  //PrintMeasures() ;
        float Temp = temperature * 175.0 / 65536.0 - 45.0;
        float Hum = humidity * 100.0 / 65536.0 ;
        PubMQTT(co2, Temp, Hum);

    y si no aparece ningún error podemos, podemos calcular los valores de temperatura y humedad con las formulas que nos da el fabricante:

    {  //PrintMeasures() ;
        float Temp = temperature * 175.0 / 65536.0 - 45.0;
        float Hum = humidity * 100.0 / 65536.0 ;
        PubMQTT(co2, Temp, Hum);
    }

    De nuevo los cálculos pueden parecer mas o menos extravagantes, pero es el modo en que Sensirion pide que calculemos los valores de temperatura y humedad, así que no vamos a discutírselo. Solo nos queda presentar la función principal que publica los valores al broker MQTT:

    void PubMQTT(int co2, float Temp, float HUM)
    {   sprintf(str, "CO2,Station=%s value=%u", ID,co2);   // Publish CO2
        client.publish("Prosensor/CO2", str);
        Serial.println(str);

    Vamos a emplear la función estándar de C, sprintf, que nos permite formatear un array de char de la forma que nos conviene para que luego podamos insertarla en Influx DB. Sprint formatea un array de caracteres con el texto que le pasamos entre comillas, que son los tópicos a publicar donde reemplaza los % por los valores de Station para indicar el origen y para el valor le pasamos el entero (unsigned) que corresponde al CO2.¡

    Para la temperatura y humedad, usamos pasamos el valor con formato %1.f, es decir, con un solo decimal:

    sprintf(str, "TEMP,Station=%s value=%.1f", ID,Temp);   // Publish Temp
    client.publish("Prosensor/TEMP", str);
    Serial.println(str);
    
    sprintf(str, "HUMED,Station=%s value=%.1f", ID,HUM);
    client.publish("Prosensor/HUMED", str);
    Serial.println(str);
    }

    Aquí os dejo el programa completo:

    CO2_Reading_MQTT_final

     

    Si ponemos en marcha el programa obtendremos esto:

    Resultado del programa

     

    IMAGEN DE MARCA


    Deja una respuesta