Comunicar ESP8266 y Raspberry con MQTT

En este post veremos cómo usar el protocolo MQTT con la Raspberry Pi y la tarjeta ESP8266. Vamos a transferir simples mensajes, pero a partir de ello podremos, más tarde, enviar datos de sensores.

Lo que haremos será enviar y recibir mensajes en distintos tópicos.

Configuración e instalación de librerías

Necesitamos tener un servidor MQTT. Puedes ver mi tutorial para instalar mosquitto sobre la Raspberry Pi.

Por otro lado recomiendo usar VSCode y PlatformIO para programar la NodeMCU ESP8266. De igual modo es necesario la librería PubSubClient que puedes instalar yendo al apartado librerías, buscando “PubSubClient” e instalando la que aparece en la imagen:

Instalar PubSubClient para MQTT con ESP8266

Finalmente necesitas instalar la librería de WiFi, me parece que viene instalada por defecto al configurar el entorno.

Definir credenciales de acceso a red y a MQTT

Comenzamos definiendo las claves de acceso, nombre de red, ip del servidor, etcétera.

De igual modo creamos unas variables globales, entre ellas el mensaje que será enviado al servidor, el cliente WiFi y el cliente MQTT.

Finalmente definimos un tiempo de espera que será necesario para enviar el mensaje al servidor cada 3 segundos.

#define NOMBRE_RED "NOMBRE_RED"
#define PASSWORD_RED "TU_CONTRASEÑA"
#define DIRECCION_SERVIDOR_MQTT "1.2.3.4"
#define PUERTO_SERVIDOR_MQTT 1883
#define USUARIO_MQTT ""    // En mi caso es vacío
#define PASSWORD_MQTT ""   // En mi caso es vacío
#define TIEMPO_ESPERA 3000 // Cada cuánto enviar el mensaje
int ultimaVez = millis(); // Para enviar cada X segundos pero sin usar delay

WiFiClient clienteWiFi;
PubSubClient clienteMqtt(clienteWiFi);
// El mensaje que se envía al servidor. Debe ser lo suficientemente
// grande
char mensaje[500] = "";

El callback de MQTT

Antes de continuar, debemos definir un callback para cuando se reciba un mensaje de MQTT. Un callback no es más que una función que será invocada al recibir un mensaje del tópico al que estamos suscritos.

La función recibe el tópico como cadena, los datos como un arreglo de bytes (de hecho recibimos un apuntador al inicio del mismo) y finalmente la longitud de datos:

// La función que será llamada cuando se reciba un
// mensaje en el tópico al que nos vamos a suscribir
void callback(char *topico, byte *cargaUtil, unsigned int longitudDeDatos)
{

  Serial.print("Se ha recibido un mensaje en: ");
  Serial.println(topico);

  Serial.print("Contenido: ");
  for (unsigned int i = 0; i < longitudDeDatos; i++)
  {
    Serial.print((char)cargaUtil[i]);
  }
  Serial.println();
}

Lo único que hacemos es imprimir el mensaje, pero podríamos hacer comparaciones. En tutoriales futuros veremos cómo apagar y encender un led dependiendo del mensaje, pero por el momento solo lo imprimimos.

Conectando al WiFi y a servidor MQTT

Antes de conectarnos al servidor MQTT necesitamos tener una conexión Wifi. Para ello vamos a usar el cliente WiFi, es decir, vamos a ser clientes en lugar de servidores. Aquí el código para conectarnos:

// Conexión WiFi
WiFi.begin(NOMBRE_RED, PASSWORD_RED);
Serial.print("\nConectando a WiFi...");
while (WiFi.status() != WL_CONNECTED)
{
  delay(500);
  Serial.print(".");
}

Serial.println("Conectado");

Cuando hemos obtenido la conexión WiFi y ya estamos en la red o en internet, nos intentamos conectar al servidor MQTT.

// Y ahora intentamos conectarnos al servidor MQTT
clienteMqtt.setServer(DIRECCION_SERVIDOR_MQTT, PUERTO_SERVIDOR_MQTT);
// Establecer la función que se invoca al recibir mensajes
clienteMqtt.setCallback(callback);
Serial.print("Conectando a servidor MQTT...");
while (!clienteMqtt.connected())
{
  if (clienteMqtt.connect("ClienteESP8266", USUARIO_MQTT, PASSWORD_MQTT))
  {
    Serial.println("Conectado");
  }
  else
  {
    Serial.print("Error conectando. Estado: ");
    Serial.println(clienteMqtt.state());
    delay(2000);
  }
}
// Después de habernos conectado al servidor MQTT nos suscribimos
clienteMqtt.subscribe("casa/sala/iluminacion");

Una vez que nos conectamos al servidor MQTT nos suscribimos a un tópico y establecemos la función callback mostrada previamente.

Publicación de mensajes a MQTT

Para enviar un mensaje hacemos lo siguiente:

// Formatear una cadena dentro de mensaje
sprintf(mensaje, "Contador: %d", contador);
// Y publicar un simple contador
Serial.print("Publicando mensaje...");
clienteMqtt.publish("jardin/temperatura", mensaje);
Serial.println("OK");

Esto lo haremos dentro del loop.

Recepción de mensajes MQTT

Para procesar lo recibido simplemente invocamos al método loop del cliente:

// Procesar los mensajes entrantes en caso de que existan
clienteMqtt.loop();

En caso de que haya un mensaje recibido se invocará al callback.

Código completo

Ahora veamos el código completo, y más abajo lo vamos a probar.

/*

  ____          _____               _ _           _       
 |  _ \        |  __ \             (_) |         | |      
 | |_) |_   _  | |__) |_ _ _ __ _____| |__  _   _| |_ ___ 
 |  _ <| | | | |  ___/ _` | '__|_  / | '_ \| | | | __/ _ \
 | |_) | |_| | | |  | (_| | |   / /| | |_) | |_| | ||  __/
 |____/ \__, | |_|   \__,_|_|  /___|_|_.__/ \__, |\__\___|
         __/ |                               __/ |        
        |___/                               |___/         
    
    Blog:       https://parzibyte.me/blog
    Ayuda:      https://parzibyte.me/blog/contrataciones-ayuda/
    Contacto:   https://parzibyte.me/blog/contacto/
    
    Copyright (c) 2020 Luis Cabrera Benito
    Licenciado bajo la licencia MIT
    
    El texto de arriba debe ser incluido en cualquier redistribucion
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

#define NOMBRE_RED "tu_red"
#define PASSWORD_RED "tu_conraseña"
#define DIRECCION_SERVIDOR_MQTT "192.168.1.79"
#define PUERTO_SERVIDOR_MQTT 1883
#define USUARIO_MQTT ""    // En mi caso es vacío
#define PASSWORD_MQTT ""   // En mi caso es vacío
#define TIEMPO_ESPERA 3000 // Cada cuánto enviar el mensaje

int ultimaVez = millis(); // Para enviar cada X segundos pero sin usar delay

WiFiClient clienteWiFi;
PubSubClient clienteMqtt(clienteWiFi);
// El mensaje que se envía al servidor. Debe ser lo suficientemente
// grande
char mensaje[500] = "";

// La función que será llamada cuando se reciba un
// mensaje en el tópico al que nos vamos a suscribir
void callback(char *topico, byte *cargaUtil, unsigned int longitudDeDatos)
{

  Serial.print("Se ha recibido un mensaje en: ");
  Serial.println(topico);

  Serial.print("Contenido: ");
  for (unsigned int i = 0; i < longitudDeDatos; i++)
  {
    Serial.print((char)cargaUtil[i]);
  }
  Serial.println();
}

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

  // Conexión WiFi
  WiFi.begin(NOMBRE_RED, PASSWORD_RED);
  Serial.print("\nConectando a WiFi...");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }

  Serial.println("Conectado");
  // Y ahora intentamos conectarnos al servidor MQTT
  clienteMqtt.setServer(DIRECCION_SERVIDOR_MQTT, PUERTO_SERVIDOR_MQTT);
  // Establecer la función que se invoca al recibir mensajes
  clienteMqtt.setCallback(callback);
  Serial.print("Conectando a servidor MQTT...");
  while (!clienteMqtt.connected())
  {
    if (clienteMqtt.connect("ClienteESP8266", USUARIO_MQTT, PASSWORD_MQTT))
    {
      Serial.println("Conectado");
    }
    else
    {
      Serial.print("Error conectando. Estado: ");
      Serial.println(clienteMqtt.state());
      delay(2000);
    }
  }
  // Después de habernos conectado al servidor MQTT nos suscribimos
  clienteMqtt.subscribe("casa/sala/iluminacion");
}

// Ahora viene el loop, al cual se entra en caso de que en el setup
// todo haya ido bien
int contador = 0;

void loop()
{
  // Procesar los mensajes entrantes en caso de que existan
  clienteMqtt.loop();

  // Si han pasado más de 3 segundos desde la última vez, enviar un mensaje
  if (millis() - ultimaVez > TIEMPO_ESPERA)
  {

    // Formatear una cadena dentro de mensaje
    sprintf(mensaje, "Contador: %d", contador);
    // Y publicar un simple contador
    Serial.print("Publicando mensaje...");
    clienteMqtt.publish("jardin/temperatura", mensaje);
    Serial.println("OK");
    contador++;
    ultimaVez = millis();
  }
  delay(10);
}

Es importante notar que vamos a publicar en el tópico jardin/temperatura, y que vamos a recibir mensajes del tópico casa/sala/iluminacion.

Enviando mensajes

Los mensajes serán enviados cada 3 segundos, con un simple contador. Si nos suscribimos veremos algo así:

Suscribir desde Raspberry a mensajes enviados por ESP8266

Y de igual modo, si enviamos un mensaje, la ESP8266 lo recibirá:

Enviando mensajes a ESP8266 con MQTT

Conclusión

Recuerda que en este caso he usado una Raspberry como servidor, y un ESP8266 como cliente. Sin embargo, podemos instalar mosquitto en distintos dispositivos.

De igual modo puedes usar como cliente un Arduino o un montón de tarjetas compatibles con MQTT. Y puede haber más de dos dispositivos.

En próximos tutoriales veremos cómo enviar datos de un sensor.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

2 comentarios en “Comunicar ESP8266 y Raspberry con MQTT”

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *