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:
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í:
Y de igual modo, si enviamos un mensaje, la ESP8266 lo recibirá:
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.
Cuando unes estos datos a Node Red, es posible acceder a ellos desde cualquier parte? Saludos Crack!
Depende, si el servidor está expuesto en internet entonces sí