3.3 MQTT – Archivo.ino – Mensajes de estado LED

Referencia: Librería mqtt https://pubsubclient.knolleary.net/api

Para esta sección se supone que ha establecido la conexión a la red WiFi y dispone de un un servidor/broker MQTT de pruebas,  semejante a lo realizado en las secciones anteriores.


1. Bloque de inicio

Para MQTT se incluye la libreria PubSubClient.h para la activación de un cliente sobre WiFi. como se indica en las intrucciones siguientes:

#include <PubSubClient.h >

// Clientes WiFi y MQTT
WiFiClient wificlient;
PubSubClient mqttclient(wificlient);

2. Configuración de inicio setup()

Para el bloque de configuración setup() se verifica el estado de la conexión WiFi antes del intento de conexión al broker MQTT-Mosquitto.

  // conexión WIFI y MQTT
  inicia_wifi();
  if (WiFi.status() == WL_CONNECTED){
    inicia_mqtt();
  }

El proceso inicia_mqtt() se establecen los párámetros de servidor, identificación de usario, conexión y la primera actualización de estado con pub/sub que corresponda.

3. Procedimientos MQTT

Se usa el procedimiento de inicio de conexión y de publicación de estados con sensores. En el caso de actuador se añade el proceso de recepción de mensajes en el dispositivo (callback)

3.1 Procedimiento inicia_mqtt()

Con los parámetros del broker y dispositivo se establece la conexión con el servidor (setServer(), connect()).

Se tiene un periodo de espera mientras se parpadea el led y se presenta un punto en el puerto serial. En caso de que se verifica conexión se publica el estado inicial del sensor antes de volver al bucle de operación.

void inicia_mqtt() {
  int espera = 5;
  int cuenta = 0;
  
  mqttclient.setServer(MQTT_IP, MQTT_puerto);
  mqttclient.connect(MQTT_ID, MQTT_usuario,
                      MQTT_contrasena);
  //mqttclient.setCallback(callback);

  if (serial_msg){
    Serial.print(" MQTT Conectando a ");
    Serial.println(MQTT_IP);
  }
  
  while (!mqttclient.connected()&&(cuenta<=espera)){
    cuenta = cuenta + 1;
    
    // LED Monitor parpadeo MQTT, enciende LOW
    digitalWrite(LED_pin, LOW);
    delay(600);
    digitalWrite(LED_pin, HIGH);
    delay(400);
    
    if (serial_msg){
      Serial.print(".");
    }
  }
  
  // Si conectado, inicializa estado
  if (mqttclient.connected()){
      publica_mqtt();
  }
  
  if (serial_msg){
    //Fin de "...."
    Serial.println();
    Serial.print(" MQTT Conectado: ");
    Serial.println(mqttclient.connected());
    Serial.print(" MQTT Estado: ");
    Serial.println(mqttclient.state());
  }
}

Se añaden algunos mensajes de estado hacia el computador como parte del proceso de depuración en la etapa de desarrollo y programación.

El resultado de esta subrutina en la pantalla "monitor_serie" es;

inicia setup
 WIFI Conectando a miRouter
.......
 Estado: 3
 MAC: 2C:3A:E8:0A:98:66
 IP: 192.168.10.11
 RSSI: -56

 MQTT Conectando a 192.168.10.50
OFF

 MQTT Conectado: 1
 MQTT Estado: 0
ON
OFF
ON
OFF
ON
OFF

....

OFF
ON
MQTT desconectado
 MQTT Conectando a 192.168.10.50
.....

Como una prueba de funcionamiento, al final se muestra el resultado al desconectar el cable del servidor MQTT.

Estos parámetros se deben actualizar para cada dispositivo específico a desarrolar.

Los valores para mqttclient.state() que se pueden obtener son:

-4 : MQTT_CONNECTION_TIMEOUT - the server didn't respond within the keepalive time
-3 : MQTT_CONNECTION_LOST - the network connection was broken
-2 : MQTT_CONNECT_FAILED - the network connection failed
-1 : MQTT_DISCONNECTED - the client is disconnected cleanly
 0 : MQTT_CONNECTED - the client is connected
 1 : MQTT_CONNECT_BAD_PROTOCOL - the server doesn't support the requested version of MQTT
 2 : MQTT_CONNECT_BAD_CLIENT_ID - the server rejected the client identifier
 3 : MQTT_CONNECT_UNAVAILABLE - the server was unable to accept the connection
 4 : MQTT_CONNECT_BAD_CREDENTIALS - the username/password were rejected
 5 : MQTT_CONNECT_UNAUTHORIZED - the client was not authorized to connect

Referencia: https://pubsubclient.knolleary.net/api

3.2 Procedimiento publica_mqtt()

La publicación de estados se realiza como una subrutina, en el ejemplo es solo encendido y apagado de un LED

// Publicar el estados del dispositivo
void publica_mqtt() {
  
  // formato para envio (Texto)
  if (sensor_estado == true ){
    snprintf(MQTT_SensorEstado,10, "ON");
  }else{
    snprintf(MQTT_SensorEstado,10, "OFF");
  }

  // publicar estados
  if (mqttclient.connected()==true) {
    mqttclient.publish(MQTT_TOPIC,
                       MQTT_SensorEstado,
                       true);
  }

  if (serial_msg){
    if (mqttclient.connected()==true) {
      Serial.println(MQTT_SensorEstado);
    }
    if (mqttclient.connected()==false) {
      Serial.println("MQTT desconectado");
    }
  }
}

4. Instrucciones Arduino

Las instrucciones usadas para que el estado del parpadeo de un LED se transmita al broker MQTT se incluyen en para la parte principal en la pestaña principal.

/* ESP8266/ESP32 Sensor LED Parpadea. 
 * edelros@espol.edu.ec
 * Actualice: WIFI:Router, MQTT:Servidor, MQTT:Dispositivo
 * ESP-01, NO USE Serial.print()
*/
// detectar placa al compilar
# ifdef defined(ESP8266)
  #include <ESP8266WiFi.h>
# elif defined(ESP32)
  #include <WiFi.h>
# endif
#include <PubSubClient.h>
// Clientes WiFi y MQTT
WiFiClient wificlient;
PubSubClient mqttclient(wificlient);

// WIFI: conexión a Router
char* ssid = "miRouter";
char* password = "miRouterClave";

// MQTT: Servidor
char* MQTT_IP = "192.168.10.50";
uint16_t MQTT_puerto = 1883;
char* MQTT_usuario = "usuarioprueba";
char* MQTT_contrasena = "usuarioclave";

// MQTT: Dispositivo
char* MQTT_ID = "Dispositivo";
char* MQTT_TOPIC = "oficina/mensaje";
char MQTT_SensorEstado[10] = "OFF";
boolean mqtt_desconectado = true;

// Sensor 
const uint8_t sensor_pin = 1;
volatile boolean sensor_estado  = false;

// LED monitor interno: ESP01-pin=1, ESP07-pin=2
const uint8_t LED_pin = 2;

// Mensajes por Puerto Serial
volatile boolean serial_msg = true;

void setup() {

  if (serial_msg){
    Serial.begin(115200);//74880, 115200
    while (!Serial){delay(50);}
    Serial.println("inicia setup");
  }
  
  // LED monitor, Enciende en LOW
  pinMode(LED_pin, OUTPUT);

  // conexión WIFI y MQTT
  inicia_wifi();
  if (WiFi.status() == WL_CONNECTED){
    inicia_mqtt();
  }
}

void loop() {
  // Parpadea estado de sensor
  if (sensor_estado==true){
    sensor_estado = false;
  }else{
    sensor_estado = true;
  }
  
  // LED Monitor Parpadea, enciende en LOW
  if (sensor_estado ==true) {
    digitalWrite(LED_pin, LOW);
    delay(1000); // un segundo
  }
  
  #ifdef defined(ESP8266)
    yield(); // procesa wifi
  #endif
  
  if (sensor_estado ==false) {
    digitalWrite(LED_pin, HIGH);
    delay(1000);
  }

  if (mqttclient.connected()==true){
    publica_mqtt();
  }
  
  //Revisa estado de Wifi o reintenta conexión  
  if (WiFi.status() != WL_CONNECTED){
    inicia_wifi();
  }else{
    if (mqttclient.connected()==false){
        inicia_mqtt(); // reintento
    }
    if (mqttclient.connected()==true){
      mqttclient.loop();
    }
  }
  #ifdef defined(ESP8266)
      yield(); // procesa wifi
  #endif
} 

Si no se ha implementado el broker MQTT solo obtendrá mensajes de «MQTT desconectado».

Al final, recuerde añadir los otros procedimientos inicia_wifi(), inicia_mqtt(), publica_mqtt().

void inicia_wifi(){
  int espera = 10; // >=10 para conectar
  int cuenta = 0;

  // mensajes por serial activado
  if (serial_msg){
    Serial.print(" WIFI Conectando a ");
    Serial.println(ssid);
    }
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  while ((WiFi.status()!=WL_CONNECTED)&&(cuenta<espera)){
    cuenta = cuenta+1;
    
    // Parpadeo de Monitor enciende en LOW
    digitalWrite(LED_pin, LOW);
    delay(300);
    digitalWrite(LED_pin, HIGH);
    delay(200);
    
    if (serial_msg){
      Serial.print(".");
      }
    }

  //activado mensajes por serial
  if (serial_msg){
    Serial.println();  //Fin de "..."
    if (WiFi.status() == WL_CONNECTED){
      Serial.print(" Estado: ");
      Serial.println(WiFi.status());
      Serial.print(" MAC: ");
      Serial.println(WiFi.macAddress());
      Serial.print(" IP: ");
      Serial.println(WiFi.localIP());
      Serial.print(" RSSI: ");
      Serial.println(WiFi.RSSI());
      Serial.print(" Canal: ");
      Serial.println(WiFi.channel());
      Serial.println();
      }
    if (WiFi.status() != WL_CONNECTED){
      WiFi.printDiag(Serial);
      Serial.println();
      }
    }
  }