3.4 MQTT TLS – Archivo.ino para mensajes de estado LED

Para añadir un nivel adicional de seguridad a los mensajes MQTT en la red, se incorpora SSL/TLS para un dispositivo ESP8266.

Para simplificar el ejemplo, la explicación supone que tiene operando el dispositico con

Mensajes con ESP8266-Parpadeo LED

y se ha habilitado un broker SSL/TLS con certificado «local» siguiendo el ejemplo de

MQTT – Mosquitto TLS

Librerías Arduino

Para manejar TLS en la conexión, se usa la libreria siguiente:

#include <WiFiClientSecure.h>

Servidor MQTT puerto

El primer valor a actualizar para mensajes TLS es el puerto, revise este valor antes de continuar.

// MQTT: Servidor
char* MQTT_IP = "192.168.10.40";
uint16_t MQTT_puerto = 8883;

Certificados TLS

Los certificados a usar corresponden al certificado de la autoridad y del servidor mqtt.

Para el certificado de la autoridad mqtt-ca.crt se usa el contenido del archivo abierto como texto.

El certificado del servidor mqtt-srv se usa en formato reducido de fingerprint, obtenido con la instruccion descrita anteriormente en la sección mosquitto TLS.

// Certificados TLS: Certificate Authority
const char caCert[] PROGMEM = R"EOF(-----BEGIN CERTIFICATE-----
MIIDRzCCAi+gAwIBAgIUMtlZSXzSZfHpBI0vrnxdLrQuNgwwDQYJKoZIhvcNAQEL
BQAwMzELMAkGA1UEBhMCRUMxDDAKBgNVBAcMA0d5ZTEWMBQGA1UEAwwNMTkyLjE2
.... // ....
OpA6IPNIHmDSSPxgejZq4booviVKro2/M++iOUuGj1jkjR6XSQ6x1JgK0DVyiBtD
4od+M4mYk7gz/CHgl1JEsTVPyUZK4OO/oxC7
-----END CERTIFICATE-----
)EOF";

// MQTT: cert SHA1 fingerprint
const uint8_t mqttCertFingerprint[] = {0x19,0x50,0x25,0x2C,0xD8,0x6A,0x54,0xE6,0xBC,0x8B,0xA4,0x5F,0x76,0x3F,0xD0,0x7D,0xE0,0x5F,0x7C,0x6C};

Conexiones WiFi Seguras

Para usar los componentes de WifiClient y PubsubClient se requiere añadir los detallas de los certificados, por lo que se sustituyen las línea como se muestra:

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

// X.509 parsed CA Cert 
X509List caCertX509(caCert);
// Secure client connection class
WiFiClientSecure wificlient;
// MQTT: Client connection
PubSubClient mqttclient(wificlient);

MQTT – Inicio

En la sección de inicio de MQTT se añaden las partes para que tome en cuenta los certificados al momento de realizar la conexión por Wifi

  // Configura cliente TLS
  // añade CA cert en los sitios de confianza
  wificlient.setTrustAnchors(&caCertX509);
  // Habilita self-signed cert
  wificlient.allowSelfSignedCerts(); 
  // añade fingerprint para validar conexión
  wificlient.setFingerprint(mqttCertFingerprint); 

El resto de las intrucciones se mantienen iguales al proceso anterior.


Ejemplo: Parpadea LED

/* ESP8266 Sensor Parpadea. edelros@espol.edu.ec
 *  con SSL/TLS, Para usar, actualice las secciones de:
 *  - WIFI:Router, MQTT:Servidor, MQTT:Dispositivo
 *  ESP-01 al usar GPIO1 y GPIO3,(Tx,Rx), NO USE Serial.print()
*/
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>

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

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

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

// Certificados TLS: Certificate Authority
const char caCert[] PROGMEM = R"EOF(-----BEGIN CERTIFICATE-----
MIIDRzCCAi+gAwIBAgIUMtlZSXzSZfHpBI0vrnxdLrQuNgwwDQYJKoZIhvcNAQEL
.......// ........
OpA6IPNIHmDSSPxgejZq4booviVKro2/M++iOUuGj1jkjR6XSQ6x1JgK0DVyiBtD
4od+M4mYk7gz/CHgl1JEsTVPyUZK4OO/oxC7
-----END CERTIFICATE-----
)EOF";

// MQTT: cert SHA1 fingerprint
const uint8_t mqttCertFingerprint[] = {0x19,0x50,0x25,0x2C,0xD8,0x6A,0x54,0xE6,0xBC,0x8B,0xA4,0x5F,0x76,0x3F,0xD0,0x7D,0xE0,0x5F,0x7C,0x6C};

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

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

// X.509 parsed CA Cert 
X509List caCertX509(caCert);
// Secure client connection class
WiFiClientSecure wificlient;
// MQTT: Client connection
PubSubClient mqttclient(wificlient);

void setup() {

  if (serial_msg){
    Serial.begin(74880);//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
  }
  if (sensor_estado ==false) {
    digitalWrite(LED_pin, HIGH);
    delay(1000);
  }

  if (WiFi.status()==WL_CONNECTED) {
    publica_estado();
  }
  
  //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();
    }
  }
  yield(); // procesa wifi
} 

sección de inicio MQTT completa:

void inicia_mqtt() {
  int intentos = 5;
  int cuenta = 0;

  // Configura cliente TLS
  // añade CA cert en los sitios de confianza
  wificlient.setTrustAnchors(&caCertX509);
  // Habilita self-signed cert
  wificlient.allowSelfSignedCerts(); 
  // añade fingerprint para validar conexión
  wificlient.setFingerprint(mqttCertFingerprint); 

  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<=intentos)){
    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_estado();
  }
  
  if (serial_msg){
    //Fin de "...."
    Serial.println();
    Serial.print("; MQTT Conectado: ";);
    Serial.println(mqttclient.connected());
    Serial.print("; MQTT Estado: ";);
    Serial.println(mqttclient.state());
  }
}

sección de publicación MQTT es igual que en el ejercicio que le precede

// Publicar el estados del dispositivo
void publica_estado() {
  
  // 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");
    }
  }
}

sección de inicio de Wifi es igual que en el ejercicio que le precede.

void inicia_wifi(){
  int intentos = 10;
  int cuenta = 0;

  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 < intentos)){
    if (serial_msg){
      Serial.print(".");
      }
    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.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.println();
      }
    if (WiFi.status() != WL_CONNECTED){
      WiFi.printDiag(Serial);
      Serial.println();
      }
    }
  }

Referencia: https://blog.thewalr.us/2019/03/27/using-esp8266-as-an-iot-endpoint-with-encrypted-mqtt-transport/