Logotipe

O protocolo MQTTLeve e simples. Perfeito para IoT e sistemas embarcados.


Motivação


Com a evolução da tecnologia, não somente no que tange a microeletrônica, como também a evolução da computação e as tecnologias de comunicação digitais, atrelado ao desejo de se interconectar os mais variados tipos de objetos e sistemas eletrônicos e computacionais, um termo vem ganhando bastante força nos últimos tempos: a Internet das Coisas.

A popularização dos sistemas embarcados, bem como a profusão de plataformas em nuvem, sensores, hardware diversos e módulos vem aparecendo, gerados pelos mais diversos fabricantes. Intel, Microsoft, Samsung, ARM, Google, IBM, Apple possui hoje áreas dedicadas a criar soluções para IoT(Internet of Things).

Na área de protocolos não é diferente, inúmeras propostas e implementações já existem e um dos protocolos mais conhecidos para troca de mensagens em IoT é o MQTT(Message Queue Telemetry Transport).


Origem do protocolo


Criado pela IBM na década de 90 com foco em sistemas de supervisão e aquisição de dados, o protocolo evoluiu e encontrou seu espaço nesse amplo mercado de Internet das Coisas.

O MQTT não é tão sofisticado quanto o AMQP(Advanced Message Queuing Protocol) que possui mais funcionalidades e cenários de uso, mas é simples o suficiente sem deixar de contemplar características como segurança, qualidade de serviço e facilidade de implementação. Essas características fazem o MQTT um bom candidato para implementações e usos em sistemas embarcados, embora a competição seja acirrada.

O protocolo foi concebido visando utilizar a infraestrutura e realizar integração com os protocolos TCP e IP. Além disso, o MQTT foi projetado para aplicações que utilizam pouca banda de rede, com requisitos de hardware extremamente simples e leve. O MQTT está na mesma camada OSI que o HTTP(Hypertext Transfer Protocol), porém a maior diferença entre eles é o tamanho do payload. No HTTP o payload é maior, o que inviabiliza o uso em conexões de baixa qualidade. Além disso o MQTT possui maior segurança, apresenta mais níveis de serviço, é menos complexo e permite uma comunicação de 1 para N se comparado ao HTTP que também é um protocolo utilizado no universo de Internet das Coisas.


O paradigma Publish-Subscribe


Diferente do paradigma Request-Reponse onde um cliente requisita informações a um servidor e este responde a requisição do cliente. No padrão Publish-Subscribe, quando o cliente ou elemento da rede conhecido também como subscriber deseja receber uma determinada informação, ele dá um subscribe no tópico de interesse através de uma requisição para um outro elemento da rede que é conhecido como Broker que atua como um intermediário no processo de comunicação. Subscribers que desejam publicar algum tipo de informação o fazem através do Broker também, enviando-lhe as informações que possui e este fica responsável por rotear a mensagem até o destinatário. É importante frisar que geralmente o fluxo de comunicação se dá majoritariamente de quem publica informações como sensores por exemplo, para quem assina e deseja receber aquela informação que são os mais variados clientes como por exemplo aplicações rodando na nuvem ou um simples Raspberry.




MQTT na prática


A identificação das mensagens no MQTT se dá através de tópicos (topics). O tópico lembra o conceito de URI, com níveis separados por barras (“/”). Elementos da rede podem enviar diversos tópicos para o broker e subscritores podem escolher os tópicos que desejam subscrever.

Uma sessão é formada com o estabelecimento de conexão entre um cliente e um broker. Quando um cliente MQTT se conecta a um canal de telemetria ele inicia uma nova sessão ou retoma uma sessão antiga. Uma sessão nova não tem mensagens pendentes que não foram reconhecidas, nem assinaturas e nem publicações aguardando entrega. Quando um cliente se conecta, ele especifica se vai começar com uma sessão limpa ou se vai retomar uma sessão existente.


Diferente de uma sessão, uma assinatura estabelece uma conexão ou melhor um vínculo entre um cliente e um tópico. Logicamente tópicos permitem clientes trocarem informações dentro de um campo semântico.Por exemplo, imagine que, em uma rede de sensores, existam vários sensores diferentes de temperatura e umidade, publicando o valor do sensor como o dado útil (payload) e identificando as mensagens com tópicos nos seguintes formatos:


area/ID_da_area/sensor/ID_do_sensor/temperatura

area/ID_da_area/sensor/ID_do_sensor/umidade


Neste esquema, possíveis publicações seriam assim:


area/10/sensor/5000/temperatura

area/10/sensor/5000/umidade

area/10/sensor/5001/temperatura

area/20/sensor/5001/umidade

area/20/sensor/4000/temperatura


Acima, temos informações provenientes de duas áreas diferentes (10 e 20) sendo geradas por três sensores (5000, 5001, 4000), onde cada sensor publica temperatura e umidade. O valor da temperatura ou umidade faz parte dos dados da mensagem, sendo o formato algo dependente da aplicação, uma vez que o MQTT não impõe restrições sobre isso. Seria possível codificar a mensagem em json, ou mesmo enviar um valor binário de 16 bits para cada variável, entre outras formas.

Um subscritor pode assinar estas mensagens, especificando exatamente o tópico que deseja. No entanto, é muito mais interessante receber informações agrupadas. Por exemplo, para assinar todas as informações de sensores de temperatura da área 10, o seguinte tópico poderia ser usado: area/10/sensor/+/temperatura.

O “+” tem função de curinga, aceitando qualquer valor naquele nível do tópico. Existe também uma outra notação utilizando o símbolo “#”, que significa “qualquer coisa abaixo de determinado nível do tópico”. Assim, para ter acesso a qualquer valor de sensoriamento dentro da área 20, o tópico seria: area/20/sensor/# independente do setor e da variável sensoriada, tudo seria recebido.


Cliente MQTT


Clientes podem ser tanto publicadores como assinantes, ou seja, clientes assinam tópicos para enviar e receber mensagens.


O cliente é dito um cliente MQTT quando um dispositivo é capaz de rodar as bibliotecas e módulos MQTT disponíveis para os mais diversos ambientes e linguagens de programação. Um exemplo de cliente MQTT seria um Raspberry rodando um código em python utilizando as bibliotecas Eclipse Paho MQTT Python client ou um ESP32 utilizando um arduino client para MQTT. Aqui temos uma lista de clientes possíveis: github



Broker


É o coração de qualquer protocolo pub/sub. O broker tem como principal função receber, filtrar e encaminhar as mensagens aos clientes. Além disso, o broker é responsável por comandar os tópicos e receber as assinaturas dos clientes. Outro função que o broker desempenha é de armazenamento de dados, tanto informções de registro dos assinantes, como as informações publicadas. Outra responsabilidade é o gerenciamento de segurança como autenticação e autorização dos clientes. Há 2 maneiras de implementar recursos que aumentam a segurança na rede MQTT: o uso de TSL/SSL que faz a encriptação dos dados que trafegam e a autenticação e autorização como mencionado anteriormente, utilizando nome de usuário e senha e/ou utilizando certificados X.509. O broker é altamente escalável e resistente a falhas e pode ser integrado a outros sistemas. Existem diferentes Brokers disponíveis, tanto soluções em nuvem, quanto em hardware, pagos ou de graça, com a grande maioria Open-Source. Um dos mais conhecidos é o mosquitto por ser free e ser suportado pela eclipse.org.


Implementações e exemplos de uso.


Existem diversas implementações para clientes e brokers MQTT, open source ou não, e para diversas linguagens como Java, C, C#, Javascript e Python. Como exemplo, será usado o broker open source Mosquitto, que pode ser facilmente baixado e instalado para diversos sistemas operacionais e plataformas (Windows, MAC, Linux, Raspberry Pi, OpenWRT, etc). Adicionalmente, para a parte de criptografia do Mosquitto, existe a necessidade de instalação do pacote openssl.

Utilizando os tópicos de temperatura e umidade mencionado antes, podemos criar um pequeno script capaz de se comportar como um sensor dentro da área 10, enviando valores de temperatura e umidade a cada 5 segundos e com um QoS de nível 0.


#Para implementar os códigos abaixo, é preciso ter o python instalado e o módulo paho-mqtt.

# -*- coding: cp1252 -*-

import paho.mqtt.client as mqtt

from struct import pack

from random import randint

from time import sleep

AREA_ID = 10

SENSOR_ID = 5000

# topicos providos por este sensor

tt = "area/%d/sensor/%s/temperatura" % (AREA_ID,SENSOR_ID)

ut = "area/%d/sensor/%s/umidade" % (AREA_ID,SENSOR_ID)

# cria um identificador baseado no id do sensor

client = mqtt.Client(client_id = 'NODE:%d-%d' % (AREA_ID,SENSOR_ID),

protocol = mqtt.MQTTv31)

# conecta no broker

client.connect("127.0.0.1", 1883)

while True:

# gera um valor de temperartura aleatório

t = randint(0,50)

# codificando o payload como big endian, 2 bytes

payload = pack(">H",t)

# envia a publicação

client.publish(tt,payload,qos=0)

print tt + "/" + str(t)

# gera um valor de umidade aleatório

u = randint(0,100)

# codificando o payload como big endian, 2 bytes

payload = pack(">H",u)

# envia a publicação

client.publish(ut,payload,qos=0)

print ut + "/" + str(u)

sleep(5)


Neste publicador, preferiu-se o QoS 0 e os possíveis erros de conexão não foram tratados para que o exemplo não ficasse mais complexo.

Nos outros níveis de qualidade de serviço é necessário a chamada de outras funções que avaliam o recebimento de confirmações e gerenciam retransmissões.

Note que os dados foram organizados como inteiros de 16 bits no formato big endian, ou seja, o valor mais significativo vem no payload.

A função pack e seu argumento “>H” faz esta codificação (“>” para big endian e “H” para inteiro sem sinal de 16 bits).

Posteriormente, este valor será decodificado com a função unpack. Para maiores informações, consultar o módulo struct da linguagem.

Criar um subscritor capaz de receber todas as mensagens da área 10, por sua vez, pode ser implementado como a seguir:


# -*- coding: cp1252 -*-

import paho.mqtt.client as mqtt

from struct import unpack

from time import sleep

# assinando todas as publicações dentro da area 10

TOPIC = "area/10/sensor/#"

# função chamada quando a conexão for realizada, sendo

# então realizada a subscrição

def on_connect(client, data, rc):

client.subscribe([(TOPIC,0)])

# função chamada quando uma nova mensagem do tópico é gerada

def on_message(client, userdata, msg):

# decodificando o valor recebido

v = unpack(">H",msg.payload)[0]

print msg.topic + "/" + str(v)

# clia um cliente para supervisã0

client = mqtt.Client(client_id = 'SCADA',

protocol = mqtt.MQTTv31)

# estabelece as funçõe de conexão e mensagens

client.on_connect = on_connect

client.on_message = on_message

# conecta no broker

client.connect("127.0.0.1", 1883)

# permace em loop, recebendo mensagens

client.loop_forever()


Todas as mensagens que chegam e casam com o tópico assinado acabam sendo tratadas pelo método on_message. É possível criar funções diferentes de recepção, separando a recepção dos tópicos.

A subscrição dos tópicos deve ser feita após a conexão, algo tratado pela função on_connect. Por fim, via uma chamada loop_forever() do módulo MQTT do paho, o subscritor fica indefinidamente

recebendo as mensagens do tópico assinado. Os dois scripts devem rodar sem modificações tanto num PC quanto numa Beaglebone ou Raspberry Pi. Recomenda-se um estudo no módulo MQTT do paho,

explorando as demais funcionalidades providas por ele.


Conexão, Segurança e qualidade de serviço.


A conexão do cliente ao broker, seja ele subscritor ou publicador, é originalmente feita via TCP, com opções de login (usuário e senha) e uso de criptografia (SSL/TLS). É possível encontrar também outros meios físicos, com MQTT rodando em links seriais, por exemplo. Todo processo de conexão estabelece também um nível de qualidade de serviço (QoS, de Quality of Service) desejado, indicando como deve ser a relação entre os elementos comunicantes. São previstos três níveis de qualidade de serviço:


QoS 1(at most once):Assemelha-se ao protocolo UDP, onde não há confirmação de entrega da mensagemn e quem envia não tem obrigação de manter a mensagem armazenada para futuras retransmissões.

QoS 2 (at least once): Aqui temos a confirmação de entrega de uma mensagem. Quem envia também mantém a cópia da mensagem em caso de time out. Quando a mensagem chegar ao destino, o destinatário envia uma mensagem ao emissor confirmando o recebimento.

QoS 3 (exactly once): Por último, garante que a mensagem seja entregue exatamente 1 vez, com o envio da confirmação de recebimento e a confirmação da confirmação do recebimento. Resumindo, existe confirmação nos 2 sentidos para tudo que é trafegado. Enquanto uma mensagem não é confirmada, ela é mantida.

Não existe uma QoS melhor ou pior, vai depender do tipo de aplicação. Tudo é gerenciado pelo broker, não entre publicador e subscritor. Logo é possível ter uma publicação em QoS 0 e uma subscrição em QoS 2 para um mesmo tópico.


Um cliente pode fornecer um LWT(Last Will and Testament) da primeira vez que ele se conectar com um broker. Se tal cliente se desconectar da rede, o broker envia uma mensagem de broadcast aos outros clientes. Usado geralmente para saber quando um dispositivo fica offline em uma rede. Sessões(conversa) sobrevivem a problemas de conexão. Se um cliente ficar offline por alguma razão, a sessão é preservada. Broker armazena as mensagens em cache e quando a conexão é reestabelecida, as mensagens são entregues.


O MQTT especifica que é responsabilidade do cliente garantir que o intervalo de tempo em que as mensagens demoram a ser enviadas não exceda um valor de Keep Alive. Caso o valor seja excedido, o cliente envia uma mensagem de controle PINGREQ. PINGREQ avisa ao Broker que ele ainda está vivo(online). Se o Broker não receber um PINGREQ ou qualquer mensagem de um cliente específico, o Broker fecha a sessão e envia um LWT(se o cliente especificou algum). O máximo intervalo de Keep Alive é 18h 12 min e 15 seg, se o valor for setado para 0, o recurso é desativado. Se o problema de conexão ainda persistir, a biblioteca iniciará o recurso chamado “Client Take Over”, fazendo com que o broker feche a sessão e envie uma requisição de uma nova conexão com o cliente.


Fontes bibliográficas


HiveMQ - Enterprise MQTT Broker. HiveMQ. Disponível em: HiveMQ Site. Acesso em: 15 abr. 2018.

Oasis - Advancing Open Standards For the Information Society. Oasis. Disponível em: Oasis Site. Acesso em: 15 abr. 2018.

MQ Telemetry transport. Peter R. Egli. Disponível em: http://www.indigoo.com/dox/wsmw/1_Middleware/MQTT.pdf. Acesso em: 07 abr. 2018.

HCC Documentation. HCC Embedded. Disponível em: https://doc.hcc-embedded.com/. Acesso em: 18 abr. 2018.