Conexión con dispositivos

MQTT

MQTT (Message Queuing Telemetry Transport) es un protocolo de mensajería ligero diseñado para dispositivos con recursos limitados y redes de baja calidad. Es ideal para aplicaciones IoT (Internet de las cosas) como sensores, actuadores o microcontroladores.

Ventajas de MQTT:

  • Bajo consumo de ancho de banda

  • Arquitectura publish/subscribe

  • Soporta calidad de servicio (QoS)

  • Uso extendido en la comunidad de microcontroladores (ESP32, ESP8266)

Niveles de QoS

QoS

Garantía de entrega

Uso recomendado

0

No garantiza entrega

Sensores no críticos

1

Entrega al menos una vez

Estados importantes

2

Entrega exacta una vez (costoso)

Transacciones críticas


Instalación

  1. Instalación del broker

El broker MQTT es el servidor encargado de gestionar los mensajes que envían y reciben los dispositivos. Para este caso se usará Mosquitto, un broker ligero y ampliamente utilizado.

sudo apt update
sudo apt upgrade
sudo apt install mosquitto mosquitto-clients

Esto instalará:

  • mosquitto: el servicio que actuará como broker

  • mosquitto_pub y mosquitto_sub: herramientas para publicar/suscribirse a tópicos MQTT desde consola

  1. Arranque de mosquitto

sudo systemctl enable mosquitto
sudo systemctl start mosquitto
sudo systemctl status mosquitto
sudo ss -tulnp | grep 1883
  • enable mosquitto habilita el servicio de mosquitto

  • start mosquitto arranca el servicio de mosquitto

  • status mosquitto verifica el estado del servicio de mosquitto

  • ss -tulnp | grep 1883 comprobación de los puertos habilitados

  1. Instalación del cliente MQTT en Python

sudo ufw status
pip install paho-mqtt

paho-mqtt es una biblioteca mantenida por Eclipse que permite conectarse a brokers MQTT desde Python.

  1. Pruebas del servidor

En la terminal 1

mosquitto_sub -h localhost -t test

En la terminal 3

mosquitto_pub -h localhost -t test -m "Hola mundo"
  1. Configuración del Broker Mosquitto

Para aceptar conexiones externas (por ejemplo, desde un ESP32), debemos configurar Mosquitto.

Editar archivo de configuración:

sudo nano /etc/mosquitto/conf.d/default.conf

Agregar el siguiente contenido:

listener 1883
allow_anonymous true
  • listener 1883: habilita el puerto por defecto de MQTT

  • allow_anonymous true: permite conexiones sin autenticación (ideal para pruebas locales)

Habilitar el puerto en el firewall de ser necesario

sudo ufw allow 1883

Reiniciar Mosquitto sudo systemctl restart mosquitto sudo systemctl status mosquitto

Verificar que está escuchando

sudo netstat -tulnp | grep 1883

Pruebas de funcionamiento

En la terminal 1

mosquitto_sub -h 192.168.100.176 -t test

En la terminal 2

mosquitto_pub -h 192.168.100.176 -t test -m "ss"

Ejercicios

Para revisar el funcionamiento de MQTT, simularemos el intercambio de información entre 2 equipos:

Dentro del paquete mi_pkg_python crear el directorio mqtt

En el archivo equipo1.py

import paho.mqtt.client as mqtt
import json
import time

# Configuración del broker
BROKER            = "192.168.100.176" # Ip del computador o localhost
TOPIC_SUB_SEN     = "rm1/sensores"
TOPIC_SUB_EST     = "rm1/estados"
TOPIC_PUB         = "rm1/acciones"
CLIENT_ID         = "cliente_rm1"

# Callback cuando se conecta al broker
def on_connect(client, userdata, flags, rc, properties=None):
    if rc == 0:
        print("Conectado al broker MQTT")
        client.subscribe(TOPIC_SUB_SEN)
        client.subscribe(TOPIC_SUB_EST)

    else:
        print(f"Error de conexión: código {rc}")

# Callback al recibir un mensaje
def on_message(client, userdata, msg):
    try:
        mensaje = msg.payload.decode("utf-8")
        data = json.loads(mensaje)
        if msg.topic == TOPIC_SUB_SEN:
            print("Mensaje recido del Equipo2 EncoderI:", data["EncoderI"])
        
        if msg.topic == TOPIC_SUB_EST:
            print("Mensaje recido del Equipo2 Estado:", data["Estado"])

    except Exception as e:
        print("Error procesando mensaje:", e)

client = mqtt.Client(client_id=CLIENT_ID, protocol=mqtt.MQTTv311)

# Asociar funciones de callback
client.on_connect = on_connect
client.on_message = on_message

# Conexión al broker
client.connect(BROKER)

# Iniciar loop en segundo plano
client.loop_start()

# Envío continuo de mensajes cada segundo
try:
    while True:
        payload = {
            "vel": {
                "u_meta": 0.34,
                "w_meta": 0.10,

            } ,
            "nombre" :"robot1",
            "avanzar":0,
            "soltar_obj":0,
            "parar":1,
            }
        mensaje = json.dumps(payload)
        client.publish(TOPIC_PUB, mensaje)
        #print("Publicado en datos_1:", payload)
        time.sleep(1)

except KeyboardInterrupt:
    print("\n Finalizando conexión MQTT...")

finally:
    client.loop_stop()
    client.disconnect()
    print(" Desconectado correctamente.")

En el archivo equipo2.py


import paho.mqtt.client as mqtt
import json
import time

# Configuración del broker
BROKER            = "192.168.100.176" #Ip del computador o Localhost
TOPIC_PUB_SEN     = "rm1/sensores"
TOPIC_PUB_EST     = "rm1/estados"
TOPIC_SUB         = "rm1/acciones"
CLIENT_ID         = "cliente_Equipo2"

# Callback cuando se conecta al broker
def on_connect(client, userdata, flags, rc, properties=None):
    if rc == 0:
        print("Conectado al broker MQTT")
        client.subscribe(TOPIC_SUB)

    else:
        print(f"Error de conexión: código {rc}")

# Callback al recibir un mensaje
def on_message(client, userdata, msg):
    try:
        mensaje = msg.payload.decode("utf-8")
        data = json.loads(mensaje)
        if msg.topic == TOPIC_SUB:
            print("Mensaje recido del Equipo1:", data["vel"]["u_meta"])

    except Exception as e:
        print("Error procesando mensaje:", e)

client = mqtt.Client(client_id=CLIENT_ID, protocol=mqtt.MQTTv311)

# Asociar funciones de callback
client.on_connect = on_connect
client.on_message = on_message

# Conexión al broker
client.connect(BROKER)

# Iniciar loop en segundo plano
client.loop_start()

# Envío continuo de mensajes cada segundo
try:
    while True:
        payload = {
            "Encoderi": 20,
            "Encoderd": 30,
            }
        mensaje = json.dumps(payload)
        client.publish(TOPIC_PUB_SEN, mensaje)
        print("Publicado:", payload)

        payload2 = {
            "Estado": "Encedido"
            }
        mensaje2 = json.dumps(payload2)
        client.publish(TOPIC_PUB_EST, mensaje2)
        #print("Publicado:", payload)
        time.sleep(1)

except KeyboardInterrupt:
    print("\n Finalizando conexión MQTT...")

finally:
    client.loop_stop()
    client.disconnect()
    print(" Desconectado correctamente.")

Arduino IDE 2.x

Una vez configurado mosquitto, es necesario descargar arduino IDE, el programa que se utilazará para programar la ESP32.

Instalación

  1. Descargar Arduino IDE

Enlace de descargar, AppImage arduino

En la carpeta donde se descargó el AppImage, cambiar el nombre al archivo descargado por arduino-ide

sudo apt update
sudo apt upgrade
sudo install libfuse2
sudo chmod +x arduino-ide.AppImage
./arduino-ide.AppImage
  1. Instalar soporte para ESP32

    1. Ir a Archivo Preferencias

    2. En el campo URLs adicionales para tarjetas, agregar:

    https://espressif.github.io/arduino-esp32/package_esp32_index.json
    
    1. Ir a Herramientas Placa Gestor de tarjetas

    2. Buscar esp32 e instalar versión 3.x

Esta versión es compatible con las últimas bibliotecas MQTT y WiFi.


  1. Acceso a puertos USB***

El ESP32 se conecta como un dispositivo serie (/dev/ttyUSB* o /dev/ttyACM*) generalmente la conexión se realiza en el puerto ttyUSB. Para acceder sin sudo:

Añadir usuario a grupo dialout**

sudo usermod -aG dialout $USER

Reiniciar sesión para aplicar cambios.

Ver dispositivos conectados

ls /dev/ttyUSB*

Ver cambios al conectar o desconectar

watch -n 1 ls /dev/ttyUSB*

  1. Instalación de bibliotecas recomendadas en Arduino IDE

Tabla de librerías recomendadas para tu proyecto ESP32 con WiFiManager y MQTT

Librería

Autor

Versión recomendada

Notas importantes

WiFiManager

tzapu

2.0.17

Compatible con ESP32 core 3.x. Asegura el uso del namespace correcto para ESP32. Soporta parámetros personalizados.

WiFi

Espressif

Incluida en el core

No requiere instalación manual. Ya viene con el core de ESP32.

EEPROM

Espressif

Incluida en el core

Opcional migrar a Preferences si deseas mayor robustez.

PubSubClient

Nick O’Leary

2.8.0

Para conexión MQTT (broker IP, topic, publish/subscribe). Ligera y muy estable.

ArduinoJson

Benoit Blanchon

7.x.x

Usa DynamicJsonDocument y StaticJsonDocument


Ejemplos de Uso.

  1. Uso de MQTT desde Arduino

Crear 2 pestañas: Esp32_mqtt y Conf_mqtt

Pestaña 1 ESP32_mqtt

#include <PubSubClient.h>
#include <WiFi.h>
#include <ArduinoJson.h>

// Variable de Control Alarmar
int activar  = 0;
int estado   = 0;
int encoderi = 0;
int encoderd = 0;

// GPIO de salidad Digital
int pin_led   = 2;

//  Credenciales Wifi 
const char* ssid = "CARMEN GONZALEZ_";
const char* password = "123wa321vg";

// Credenciales MQTT
const char* mqtt_broker = "192.168.100.76";
const int mqtt_port = 1883;
const char* cliente = "rm1_esp32";

// Temas MQTT Publicar
const char* tema_sub = "rm1/acciones";
const char* tema_pub_est = "rm1/estados";
const char* tema_pub_sen = "rm1/sensores";

// Creacion del objeto cliente
WiFiClient espClient;
PubSubClient client(espClient);

// Tamaño de mensaje JSON
const size_t capacidad_json = JSON_OBJECT_SIZE(30);


void setup() {
  // Setup Serial
  Serial.begin(115200);
  // Setup Wifi
  setup_wifi();
  // Setup MQTT
  conexion();
  // Manejo del rele
  pinMode(pin_led, OUTPUT);     
  
}

void loop() {
  Loop_MQTT();
  if (activar == 1){
    digitalWrite(pin_led, HIGH);  // Enciende el LED
    estado = 1;
    encoderi = 35;
    envioDatos(tema_pub_est, estado, encoderi, encoderd);
    envioDatos(tema_pub_sen, estado, encoderi, encoderd);
    }
  else {
    digitalWrite(pin_led, LOW);  // Enciende el LED
    estado = 0;
    encoderi = 30;
    envioDatos(tema_pub_est, estado, encoderi, encoderd);
    envioDatos(tema_pub_sen, estado, encoderi, encoderd);
  }
  
}

Pestaña 2 Conf_Mqtt

void setup_wifi() {
  // Conexión Wifi
  delay(10);
  Serial.println();
  Serial.print("Conectando a ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("WiFi conectado - Dirección IP del ESP: ");
  Serial.println(WiFi.localIP());
}

void reconnect() {
  // Control de conexión MQTT
  while (!client.connected()) {
    Serial.print("Intentando conexión MQTT...");
    if (client.connect(cliente)) {
      Serial.println("Conectado");
      // Suscripción a TEMAS
      client.subscribe(tema_sub, 1);
    } else {
      Serial.print("Falló, rc=");
      Serial.print(client.state());
      Serial.println("Intentando de nuevo en 2 segundos");
      delay(2000);
    }
  }
}

void conexion() {
  // Conexión MQTT
  client.setServer(mqtt_broker, mqtt_port);
  client.setCallback(callback);
}

void Loop_MQTT() {
  // Manejo MQTT 
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

void envioDatos(const char* mqtt_topic_publicar, int estado, int encoderi, int encoderd) {
  DynamicJsonDocument mensaje(256);

  if (mqtt_topic_publicar == tema_pub_est) {
    mensaje["estado"]   = estado;
    String mensaje_json;
    serializeJson(mensaje, mensaje_json);
    client.publish(mqtt_topic_publicar, mensaje_json.c_str(), 1);
  }

  if (mqtt_topic_publicar == tema_pub_sen) {
     mensaje["encoderi"]   = encoderi;
     mensaje["encoderd"]   = encoderd;
     String mensaje_json;
    serializeJson(mensaje, mensaje_json);
    client.publish(mqtt_topic_publicar, mensaje_json.c_str(), 1);
  }
  
  
}

void callback(char* topic, byte* payload, unsigned int length) {
  StaticJsonDocument<capacidad_json> doc;
  char buffer[length + 1];
  memcpy(buffer, payload, length);
  buffer[length] = '\0';  // Asegura que el buffer tenga fin de cadena

  DeserializationError error = deserializeJson(doc, buffer);

  if (error) {
    Serial.print("Error al deserializar JSON: ");
    Serial.println(error.c_str());
    return;
  }

  String topico(topic);
  if (topico == "rm1/acciones") {
      activar  = doc["avanzar"];
  }
}

Notas adicionales: Publicar mensaje:

client.publish("topic", mensaje_json.c_str(), 1);  // QoS 1

Suscribirse a un tópico:

client.subscribe("Carrito_1/Acciones", 0);  // QoS 0

Puente MQTT - ROS2

Una vez obtenida una base de MQTT para su uso en microcontroladores y equipos. Se presenta un nodo que funcionará como puente para la comunicación entre la ESP32 y ROS2.


Nota

Existe la posibilidad de utilizar ros2 en microcontroladores, sin embargo se necesitan versiones de nucleo de tarjetas específicas.

Para revisar mas contenido relacionado se puede revisar los siguientes links:

  • micro-ROS oficial: https://micro.ros.org/

  • Repositorio Arduino: https://github.com/micro-ROS/micro_ros_arduino