Tutorial MQTT para Arduino, ESP8266 y ESP32

En este artículo aprenderás qué es y cómo funciona MQTT. Profundizamos en el protocolo de mensajes y en la seguridad del mismo.

En este artículo aprenderás qué es MQTT y cómo funciona este protocolo de mensajes.

Este tutorial cubre las siguientes partes:

  • Secuencia de MQTT
  • Protocolo de mensajes
  • Formatos de los mensajes
  • Seguridad del Protocolo MQTT.

MQTT son las siglas de Message Queueing Telemetry Transport y fue inventado por Andy Stanford-Clark de IBM y Arlen Nipper de Cirrus Link en 1999. En los últimos años, “Internet of Thought (IoT) se ha vuelto muy popular. El componente central de IoT es la comunicación y la interacción de diferentes dispositivos físicos directamente o a través de Internet. Por lo tanto, se requiere un protocolo de comunicación de máquina a máquina (M2M). Esto es lo que es MQTT. MQTT es un protocolo de transporte de mensajería ligero basado en mensajería de publicación / suscripción y opera sobre TCP / IP.

Por lo tanto, el protocolo es adecuado para microcontroladores como Arduino, ESP8266, ESP32 o Raspberry Pi. Yo personalmente uso MQTT para enviar datos desde mis estaciones meteorológicas, construidas con un NodeMCU, a mi Raspberry Pi, que es la unidad de control central de mi hogar inteligente. Si está interesado en el ejemplo práctico de conexión MQTT, lea el artículo de comunicación Microcontrolador a Raspberry Pi WiFi MQTT.

También existen aplicaciones industriales basadas en MQTT. Por ejemplo, el mensajero de Facebook se basa en MQTT.

La carga útil que se puede enviar a través del protocolo MQTT está en texto sin formato. Por lo tanto, el suscriptor debe agregar la unidad correspondiente. La longitud mínima del mensaje es de 2 bytes y la longitud máxima del mensaje es de 265 megabytes.


MQTT: cómo funciona

Como se mencionó, MQTT se basa en un modelo de publicación y suscripción. Por lo tanto, se requiere un intermediario de mensajes, a menudo denominado servidor, para administrar la conexión entre el editor y el suscriptor. No hay limitación de que solo un corredor pueda interactuar en la red. El editor y el suscriptor también se denominan clientes.

La información está organizada según una jerarquía de temas. Esto significa que cada dato tiene un

En total, hay 3 partes diferentes que interactúan de manera diferente en una interacción MQTT:

  1. Editor
    El editor envía información al corredor. En un caso de uso doméstico inteligente, una estación meteorológica sería un editor porque envía información de temperatura. Una ventaja del protocolo MQTT es que un editor no necesita información sobre la cantidad y la conexión de suscriptores.
  2. Abonado
    El suscriptor obtiene información del corredor. Una computadora portátil con un tablero que muestre gráficos de temperatura de la estación meteorológica sería un suscriptor. Al igual que el editor, el suscriptor no necesita ninguna información sobre la conexión del editor.
  3. Corredor
    El intermediario de mensajes también puede ser un editor o un suscriptor al mismo tiempo. Estoy usando una Raspberry Pi como servidor, que está suscrito a todos los editores al mismo tiempo para mostrarme un tablero del estado actual de la casa inteligente.
    El corredor siempre registrará la última publicación para cada tema, incluso si no hay un suscriptor para el tema. Por lo tanto, si hay un nuevo cliente que se suscribe a un tema, el suscriptor recibirá el último mensaje en lugar de esperar la próxima vez que un editor envíe datos al corredor.
    Existen diferentes formas de intermediación. Distinguimos entre corredores autohospedados como Mosquitto o HiveMQ y corredores basados ​​en la nube como IBM o Microsoft (Azure).

Secuencia MQTT

Secuencia MQTT
  1. El editor y el suscriptor se conectan al corredor.
  2. Cuando el publicador recibe nuevos datos para su distribución, envía un mensaje que contiene el asunto y los datos al corredor.
  3. El corredor distribuye los datos entrantes a todos los clientes que se suscriben al tema.

Protocolo de mensajes MQTT

Image 3
Image 3

El protocolo MQTT funciona intercambiando una serie de paquetes de control MQTT. Un paquete de control MQTT consta de hasta tres partes, siempre en el siguiente orden:

  • Encabezado fijo, presente en todos los paquetes de control MQTT
  • Encabezado variable, presente en algunos paquetes de control MQTT
  • Carga útil, presente en algunos paquetes de control MQTT

Encabezado fijo

Image 4
Image 4

El encabezado fijo tiene 1 byte de longitud y se divide en dos partes. Los bits 7 a 4 contienen el tipo de paquete de control MQTT y los indicadores de los bits 3 a 0 específicos para cada tipo de paquete de control MQTT. La siguiente tabla muestra los 14 tipos diferentes de paquetes de control MQTT y sus banderas correspondientes.

  • C = cliente
  • S = servidor
  • DUP = indicador de mensaje duplicado. Notifica al destinatario que es posible que este mensaje ya se haya recibido.
    • = 1: El cliente o servidor emite un mensaje PUBLICAR, PUBRELAR, SUBSCRIBE o UNSUBSCRIBE nuevamente.
  • QoS = PUBLISH Calidad de servicio
    • = 0: entrega como máximo una vez (disparar y olvidar): no hay garantía de que los mensajes se entreguen. MQTT depende de las garantías de entrega de red subyacentes (TCP / IP)
    • = 1: entrega al menos una vez: los mensajes están garantizados, pero puede haber duplicados.
    • = 2: Entrega única: este es el nivel más alto que también resulta en la mayor sobrecarga en términos de mensajes de control y la necesidad de almacenar mensajes localmente.
  • RETAIN = 1: le dice al servidor que mantenga el último mensaje PUBLISH recibido y lo entregue como primer mensaje a las nuevas suscripciones.
NameValueDirection of flowDescriptionFixed header flagsBit 0Bit 1Bit 2Bit 3
Reserved0ForbiddenReserved
CONNECT1C → SClient request to connect to ServerReserved0000
CONNACK2S → CConnect acknowledgment0000
PUBLISH3C → S, S → CPublish messageReservedRETAINQoSQoSDUP
PUBACK4C → S, S → CPublish acknowledgmentReserved0000
PUBREC5C → S, S → CPublish received (assured delivery part 1)Reserved0100
PUBREL6C → S, S → CPublish release (assured delivery part 2)Reserved0100
PUBCOMP7C → S, S → CPublish complete (assured delivery part 3)Reserved0000
SUBSCRIBE8C → SClient subscribe requestReserved0100
SUBACK9S → CSubscribe acknowledgmentReserved0000
UNSUBSCRIBE10C → SUnsubscribe requestReserved0100
UNSUBACK11S → CUnsubscribe acknowledgmentReserved0000
PINGREQ12C → SPING requestReserved0000
PINGRESP13S → CPING responseReserved0000
DISCONNECT14C → SClient is disconnectingReserved0000
Reserved15ForbiddenReserved

El siguiente diagrama de secuencia de mensajes muestra la configuración de CONNECT y SUBSCRIBE

Longitud restante

La longitud restante es el número de bytes que quedan en el paquete actual, incluidos los datos en el encabezado de la variable y la carga útil. La longitud restante no incluye los octetos utilizados para codificar la longitud restante. La longitud restante se codifica mediante un esquema de codificación de longitud variable que utiliza un solo byte para valores de hasta 127. Los valores más grandes se manejan de la siguiente manera.

Los siete bits menos significativos de cada byte codifican los datos y el bit más significativo se utiliza para indicar que hay bytes subsiguientes en la representación. Por tanto, cada byte codifica 128 valores y un “bit de continuación”. El número máximo de bytes en el campo Longitud restante es cuatro.

La ecuación para calcular la longitud restante es: a * 128 ^ 0 + b * 128 ^ 1 + c * 128 ^ 2 + d * 128 ^ 3

Ejemplo de longitud restante = 364

A partir de la ecuación, primero debes pensar: cuál es la potencia más alta de 128 ^ x que es menor que 364. La respuesta es 1 porque 128 ^ 1 es 128 y 128 ^ 2 sería 16,384. Por lo tanto, sabemos que CB1 = 0 para el byte 1 y también necesitaremos el byte 0 para completar la tarea y saber que CB0 = 1.

Ahora necesitamos calcular b: ¿Cuál es el factor máximo b que se puede multiplicar por 128 para acercarse lo más posible a 364. La respuesta es b = 2, porque 2 * 128 = 256 y 3 * 128 = 384. Complete En la siguiente tabla, insertamos el número 2 en binario para la fila del byte 1 que es 0000010 (tenga cuidado porque el bit 0 está en el lado izquierdo).

La longitud restante para el byte 0 es, por tanto, 364 – 2 * 128 ^ 1 = 108.

Ahora hacemos el mismo cálculo para el byte 0 donde tenemos una potencia de 0 a 128. Por lo tanto, sabemos que a = 108 que es igual a 1101100 en formato binario para la siguiente matriz de la longitud restante.

BitCBX0123456DEC
Byte 0 (a*128^0)10011011108
Byte 1 (b*128^1)001000002

Ejemplo de longitud restante = 25897

Ahora hacemos el segundo ejemplo un poco más rápido con la longitud restante de 25897.

  • 25897 -> c * 128 ^ 2 -> c = 1, CB2 = 0, CB1 = 1, CB0 = 1
  • 25897-16384 = 9513 -> b * 128 ^ 1 -> b = 74
  • 9513 – 9472 = 41 -> a * 128 ^ 0 -> a = 41
BitCBX0123456DEC
Byte 0 (a*128^0)1100101041
Byte 1 (b*128^1)1010100174
Byte 2 (c*128^2)010000001

Se reservan un total de 4 bytes para la longitud restante. La siguiente tabla muestra el tamaño del campo Longitud restante.

DigitsFromTo
10 (0x00)127 (0x7F) → 0111|1111
2128 (0x80, 0x01)16 383 (0xFF, 0x7F)
316 384 (0x80, 0x80, 0x01)2 097 151 (0xFF, 0xFF, 0x7F)
42 097 152 (0x80, 0x80, 0x80, 0x01)268 435 455 (0xFF, 0xFF, 0xFF, 0x7F)

Encabezado variable

Algunos tipos de paquetes de control MQTT contienen un componente de encabezado variable. Reside entre el encabezado fijo y la carga útil. El contenido del encabezado variable varía según el tipo de paquete. El campo Identificador de paquete de encabezado variable es común en varios tipos de paquetes.

Bytes de identificador de paquete

Image 5
Image 5

La siguiente tabla muestra qué tipos de paquetes utilizan un identificador de paquete.

Control del campo del identificador del paquete del paquete

Control PacketPacket Identifier field
CONNECTNo
CONNACKNo
PUBLISHYes (if QoS > 0)
PUBACKYes
PUBRECYes
PUBRELYes
PUBCOMPYes
SUBSCRIBEYes
SUBACKYes
UNSUBSCRIBEYes
UNSUBACKYes
PINGREQNo
PINGRESPNo
DISCONNECTNo

SUBSCRIBE, UNSUBSCRIBE y PUBLISH (en los casos en que QoS> 0) Los paquetes de control DEBEN contener un identificador de paquete de 16 bits distinto de cero. Siempre que un cliente envía un nuevo paquete de uno de estos tipos, DEBE asignarle un identificador de paquete no utilizado actualmente.

Si un cliente devuelve un paquete de control en particular, DEBE usar el mismo identificador de paquete en las retransmisiones posteriores de ese paquete.

El identificador de paquete está disponible para su reutilización después de que el cliente procesa el paquete de reconocimiento correspondiente. La siguiente tabla muestra el paquete de reconocimiento correspondiente para los tipos de paquetes.

Paquete de reconocimiento de tipo de paquete

Packet TypeAcknowledgment Packet
PUBLISH (QoS = 1)PUBACK
PUBLISH (QoS = 2)PUBCOMP
SUBSCRIBESUBACK
UNSUBSCRIBEUNSUBACK

Un paquete PUBLISH no debe contener un ID de paquete si su valor de QoS se establece en 0.
Un paquete PUBACK, PUBREC o PUBREL DEBE contener el mismo ID de paquete que el paquete PUBLISH que se envió originalmente. Asimismo, SUBACK y UNSUBACK DEBEN contener el identificador de paquete que se utilizó en el paquete SUBSCRIBE y UNSUBSCRIBE correspondiente.

Carga útil

Algunos paquetes de control MQTT contienen una carga útil como última parte del paquete. En el caso del paquete PUBLISH, este es el mensaje de la aplicación. La siguiente tabla muestra los paquetes de control que contienen la carga útil.

Control de carga útil de paquetes

Control PacketPayload
CONNECTRequired
CONNACKNone
PUBLISHOptional
PUBACKNone
PUBRECNone
PUBRELNone
PUBCOMPNone
SUBSCRIBERequired
SUBACKRequired
UNSUBSCRIBERequired
UNSUBACKNone
PINGREQNone
PINGRESPNone
DISCONNECTNone

Formatos de mensaje MQTT

CONNECT

El mensaje CONNECT contiene mucha información sobre la sesión en forma de campos de encabezado opcionales.

Image 6
Image 6
  • Protocol Name: cadena de nombre de protocolo codificada en UTF-8. Ejemplo “Light_Protocol”.
  • Protocol Version: valor 3 para MQTT V3.
  • Username Flag: si se establece en 1, esto indica que la carga útil contiene un nombre de usuario.
  • Password Flag: si se establece en 1, indica que la carga útil contiene una contraseña. Si se establece el indicador de nombre de usuario, también se deben establecer el indicador de contraseña y la contraseña.
  • Will Retain: si se establece en 1, le dice al servidor que mantenga un mensaje Will para el cliente que se publica en caso de que el cliente se desconecte inesperadamente.
  • Will QoS: especifica el nivel de QoS para un mensaje Will.
  • Will Flag: indica que el mensaje contiene un mensaje Will en la carga útil con los indicadores Will Keep y Will QoS.
  • Clean Session: si se establece en 1, el servidor borra toda la información anterior sobre la (reconexión) del cliente (nueva sesión de limpieza). Si se establece en 0, el servidor retiene las suscripciones de un cliente desconectado, incluido el almacenamiento de los mensajes de nivel 1 y 2 de QoS para ese cliente. Cuando el cliente se vuelve a conectar, el servidor publica los mensajes almacenados en el cliente.
  • Keep Alive Timer: utilizado por el servidor para detectar conexiones rotas con el cliente.
  • Client Identifier: ID de cliente (entre 1 y 23 caracteres) identifica de forma única al cliente en el servidor. El identificador de cliente debe ser único en todos los clientes que se conectan a un servidor.
  • Will Topic:  Asunto sobre el que se publica un mensaje de testamento si se establece el indicador de testamento.
  • Will Message: ¿Se publicará el mensaje si se establece la bandera?
  • Username and Password: nombre de usuario y contraseña si se establecen las banderas correspondientes.

CONNACK

Image
Image
  • Reservado: campo reservado para uso futuro.
  • Conecte el código de retorno:
    • 0: conexión aceptada
    • 1: Conexión rechazada, motivo = versión de protocolo inaceptable
    • 2: Conexión rechazada, motivo = nombre de usuario rechazado
    • 3: Conexión rechazada, motivo = servidor no disponible
    • 4: Conexión rechazada, motivo = nombre de usuario o contraseña incorrectos
    • 5: Conexión rechazada, motivo = no autorizado
    • 6-255: reservado para uso futuro

PUBLISH

Image 1
Image 1
  • Nombre del elemento con el nombre del elemento Longitud de la cadena: nombre del elemento en el que se publica el mensaje. Los primeros 2 bytes del campo del nombre del tema indican la longitud de la cadena del nombre del tema.
  • ID de mensaje: un ID de mensaje representa si QoS es 1 (al menos una vez entregado, confirmado) o 2 (exactamente una vez entregado).
  • Publicar el mensaje: Mensaje en forma de tabla ob bytes. La estructura del mensaje publicado es específica de la aplicación.

SUBSCRIBE

Image 2
Image 2
  • ID de mensaje: el campo ID de mensaje se utiliza para el acuse de recibo del mensaje SUBSCRIBE porque tienen un nivel de calidad de servicio de 1.
  • Nombre del artículo con el nombre del artículo Longitud de la cadena: nombre del artículo al que se suscribe el cliente. Los primeros 2 bytes del campo del nombre del tema indican la longitud de la cadena del nombre del tema. Las cadenas de nombres de temas pueden contener caracteres comodín. Varios nombres de elementos y su nivel de QoS solicitado pueden aparecer en un mensaje SUBSCRIBE.
  • Nivel de QoS: nivel de QoS en el que los clientes desean recibir mensajes de temas determinados.

Si está interesado en la descripción completa del formato del mensaje, encontrará la especificación completa aquí.

Seguridad MQTT (ejemplo de Mosquitto como corredor)

Al igual que con cualquier conexión entre diferentes dispositivos, el nivel de seguridad que necesita depende de su caso de uso. Por mi parte, recomendaría un nivel básico de seguridad porque el esfuerzo que tienes que poner es casi nulo. Nuestro objetivo es proteger los datos que se transfieren entre el editor, el corredor y el suscriptor.

El protocolo MQTT define que los mecanismos de seguridad son iniciados por el intermediario y aplicados por los clientes. En total, hay 3 formas de verificar la identidad de un cliente por parte del corredor.

Identifique a un cliente mediante el ID de cliente

La calificación de identificación a través del ID de cliente es que cada cliente MQTT debe proporcionar un ID de cliente. Cuando un cliente se suscribe a un tema oa diferentes temas, el ID de cliente está vinculado al tema y a la conexión TCP. Debido a una conexión persistente, el corredor recuerda la identificación del cliente y el sujeto suscrito correspondiente.

Nombre de usuario y contraseña

La seguridad de nombre de usuario y contraseña se usa más en una conexión MQTT porque es fácil de implementar. El intermediario solicita al cliente un nombre de usuario y una contraseña válidos antes de permitir la conexión. El cliente transmite el nombre de usuario y la contraseña como texto sin formato. Si el nombre de usuario y la contraseña son válidos, se establece la conexión entre el cliente y el servidor. Sin embargo, si el nombre de usuario y la contraseña no son válidos, el servidor finaliza la conexión.

La desventaja es que la transmisión del nombre de usuario y la contraseña no es segura sin cifrado de transporte adicional como SSL, por ejemplo. Una ventaja adicional de estos mecanismos de seguridad es que el nombre de usuario también se puede utilizar como autenticación de temas de acceso.

También existe la opción de acceder al servidor como cliente anónimo. Por lo tanto, hay varias opciones si el corredor restringe el acceso según la opción de acceso anónimo y el archivo de nombre de usuario y contraseña. Las siguientes tablas muestran todas las combinaciones y si el acceso está restringido o no.

Acceso anónimoArchivo de contraseña especificadoAcceso restringido
VerdaderoNoNo
VerdaderoSi el cliente envía un nombre de usuario/contraseña, entonces debe ser válido, de lo contrario se devuelve un error de autenticación. Si no envía ninguno, entonces no se requiere ninguno y se produce una conexión normal.
FalsoNoEl cliente debe enviar un nombre de usuario y una contraseña, pero no se comprueba. Si el cliente no envía un nombre de usuario/contraseña, se genera un código de error de autenticación.
Falso

Certificados

También existe la posibilidad de asegurar mediante certificados. Este es el método más seguro de autenticación de clientes, pero también el más difícil debido a la gestión de certificados.

Hay dos protocolos criptográficos principales que puede utilizar para asegurar su conexión MQTT:

  • Seguridad de la capa de transporte (TLS)
  • Capa de sockets seguros (SSL)

Ambos proporcionan un canal de comunicación seguro entre un cliente y un servidor. Por lo tanto, se utiliza un mecanismo de protocolo de enlace para negociar varios parámetros para crear una conexión segura. Una vez que se completa la negociación, se establece una comunicación encriptada entre el cliente y el servidor y ningún atacante puede espiar ninguna parte de la comunicación. Hay una desventaja de usar MQTT sobre TLS: la seguridad tiene un costo en términos de uso de CPU y sobrecarga de comunicación.


Deja un comentario