O ESP32, SoC mais recente da Espressif Systems, se consolidou no mercado maker como uma das principais escolhas para aqueles que querem fazer protótipos ou projetos com conectividade, bons recursos computacionais (processamento e memórias) e baixo preço. Ainda, o ESP32 pode ser programado com a Arduino IDE, o que facilita a vida das pessoas já habituadas ao Arduino, tornando seu aprendizado e uso muito mais simples e rápido.
Por possuir conectividade à Internet, o ESP32 se torna apto a utilizar os mais diversos protocolos de comunicação utilizados em Internet das Coisas, dentre eles o MQTT. O MQTT é, em sua essência, um protocolo de comunicação M2M (Machine-to-Machine) de simples concepção, leve em termos de poder computacional exigido e uso de Internet exigido e facilmente utilizável nas linguagens de programação mais requeridas no mercado (dentre elas: C, C++, Java, Python e JavaScript). Sua versatilidade, baixa exigência de recursos computacionais e baixos requisitos de banda de Internet o fizeram um dos principais protocolos de comunicação com as maiores plataformas IoT do mercado.
Dada a importância do MQTT e a popularidade do ESP32, este artigo juntará estes dois mundos: aqui, você aprenderá como utilizar comunicação MQTT (envio e recepção de informações) no ESP32.
Material necessário
Para realizar a comunicação MQTT aqui proposta, você precisará apenas de um ESP32 e um cabo micro-USB para programação e alimentação. Pode-se utilizar qualquer placa que possua um ESP32, dentre elas a ESP32 Devkit 1 (uma das mais acessíveis do mercado neste quesito), mostrada na figura 1.
Ainda, como utilizaremos a Arduino IDE para programar o ESP32, é necessário a ter instalada em seu computador. Recomendo a instalação da versão mais recente da Arduino IDE para seu sistema operacional (Windows, Linux ou Mac OS X). Para fazer o download, acesse o link https://www.arduino.cc/en/Main/Software .
MQTT - informações gerais
O MQTT (Message Queue Telemetry Transport) é em um protocolo de mensagens leve / light-weight criado em 1999. Seu objetivo original era ser um protocolo adequado para comunicação entre máquinas / dispositivos em rede (tanto local como Internet), ou seja, M2M (acrônimo para Machine-to-Machine). Pelo fato de não utilizar alto poder computacional (processamento e uso de memória RAM), assim como não utilizar alta banda de Internet, o MQTT é um dos protocolos mais adequados atualmente para comunicação M2M e telemetria em dispositivos embarcados de forma geral. Justamente por isso, o MQTT é popular em se tratando de Internet das Coisas.
Simplificadamente, uma comunicação MQTT é composta das seguintes partes:
Publishers: o(s) dispositivo(s) que irá (irão) disponibilizar informações.
Subscribers: o(s) dispositivo(s) que irá (irão) receber as informações.
Broker: este é o servidor da comunicação MQTT. Ele fica “na nuvem” (em um servidor na Internet, acessível de qualquer lugar do mundo desde que haja conexão com à Internet). Teoricamente, não há limites máximo e mínimo especificados de subscribers e publishers em uma mesma comunicação MQTT, portanto a escalabilidade de uma comunicação MQTT pode ser altíssima.
Há brokers pagos e grátis sendo os grátis referidos como brokers públicos. O seguinte site fornece uma lista de brokers públicos para uso geral: https://github.com/mqtt/mqtt.github.io/wiki/public_brokers
Dizendo em outras palavras: os dispositivos publishers enviam informações para o Broker. Por sua vez, os dispositivos subscribers recebem tais informações do Broker e ele gerencia a troca de mensagens. Ou seja, o “trabalho pesado” de sincronizar o envio e recebimento fica sob inteira responsabilidade do Broker, o que faz com que os sistemas embarcados (publishers e subscribers) fiquem livre para gerenciar outras coisas, inclusive entrar em sleep ou deep sleep mode. Então, além de utilizar poucos recursos computacionais, pode-se afirmar que o MQTT leva a uma redução de consumo energético (se comparado a alguns outros protocolos de comunicação, como o HTTP, por exemplo).
Observe o diagrama da comunicação MQTT mostrado na figura 2.
Sem entrar no mérito da especificação oficial do protocolo MQTT, uma mensagem MQTT publicada / enviada possui duas partes muito importantes:
Tópico: “chave” / identificação da informação publicada. É usado para direcionar a informação publicada / enviada a quem assina (quem “dá subscribe”) no tópico, permitindo ao broker MQTT gerenciar esta entrega de mensagens. O tópico consiste de uma string (por exemplo: MQTTTesteTopico)
Payload: informação que deseja enviar (propriamente dita).
Um publisher, conectado ao Broker (servidor MQTT) , envia / publica as informações em um dado momento. Os subscribers, assim como os publishers, também estão conectados aos brokers e “escutando” mensagens trafegadas com o tópico-alvo. Quando uma mensagem com o tópico alvo é publicada, automaticamente são direcionadas aos subscribers.
Em outras palavras: uma solução em Internet das Coisas que usa MQTT possui somente um servidor (Broker), sendo todo o restante composto de clients MQTT. Isto é uma característica muito positiva, uma vez que dessa forma o servidor não precisa conhecer (e nem ter acesso direto) às redes em que os clientes MQTT estão conectados. Dessa forma, não há necessidade da infraestrutura de rede dos clientes MQTT ser reconfigurada para redirecionar portas e nem liberar acessos aos IPs dos clientes, por exemplo.
Em termos de portas de comunicação MQTT, tipicamente há duas opções:
Porta 1883: conexão com broker MQTT sem encriptação. Por razões de segurança, é recomendado o uso de MQTT com conexão não encriptada somente para testes rápidos, protótipos e afins, nunca em produtos finais.
Porta 8883: conexão com broker MQTT com encriptação (usando TLS/SSL).
O uso de encriptação na conexão MQTT é algo não tão simples de se fazer e foge do escopo deste livro. Portanto, para fins didáticos, os exemplos do livro utilizarão conexão com broker sem encriptação (porta 1883) somente.
Instalação da biblioteca para comunicação MQTT
Agora chegou a hora de instalarmos a biblioteca PubSubClient, biblioteca na qual permitirá com que o módulo WiFi LoRa 32(V2) se comunique com a Internet utilizando o protocolo MQTT. Tal biblioteca é open-source, podendo ser obtida gratuitamente em: https://github.com/knolleary/pubsubclient
Para instalar esta biblioteca, vamos seguir o procedimento abaixo:
1. Acesse o repositório da biblioteca ( https://github.com/knolleary/pubsubclient ) e baixe-a clicando em “Clone or Download” e depois em “Download ZIP” , conforme mostra a figura 3.
Guarde o arquivo ZIP baixado em uma pasta conhecida e de fácil acesso para você
2. Agora, na Arduino IDE, vamos instalar a biblioteca que acabamos de baixar. Para isso, vamos clicar no menu “Sketch”, depois em “Incluir Biblioteca” e, por fim, clicar em “Adicionar biblioteca .ZIP”, conforme a figura 4.
3. Na janela que abrir, procure pelo arquivo da biblioteca que você baixou (conforme passo 1 deste procedimento) e clique em “Abrir”.
Desta forma, concluímos a instalação da biblioteca PubSubClient!
Tópicos MQTT para este exemplo
No exemplo deste artigo, usaremos dois tópicos MQTT: um para enviar informações do ESP32 para o broker MQTT e outro para receber informações do broker MQTT no ESP32. Veja estes tópicos na tabela 1.
Tópico |
Fluxo de informação |
INCB_ESP32_envia_informacao |
ESP32 -> broker MQTT |
INCB_ESP32_recebe_informacao |
Broker MQTT -> ESP32 |
Tabela 1 - tópicos MQTT utilizados no exemplo
Código-fonte
O código-fonte deste exemplo está a seguir. IMPORTANTE: para total compreensão, leia atentamente aos comentários contidos no código-fonte.
//Programa: comunicação MQTT com ESP32 //Autor: Pedro Bertoleti /* Headers */ #include <WiFi.h> /* Header para uso das funcionalidades de wi-fi do ESP32 */ #include <PubSubClient.h> /* Header para uso da biblioteca PubSubClient */ /* Defines do MQTT */ /* IMPORTANTE: recomendamos fortemente alterar os nomes desses tópicos. Caso contrário, há grandes chances de você enviar e receber mensagens de um ESP32 de outra pessoa. */ /* Tópico MQTT para recepção de informações do broker MQTT para ESP32 */ #define TOPICO_SUBSCRIBE "INCB_ESP32_recebe_informacao" /* Tópico MQTT para envio de informações do ESP32 para broker MQTT */ #define TOPICO_PUBLISH "INCB_ESP32_envia_informacao" /* id mqtt (para identificação de sessão) */ /* IMPORTANTE: este deve ser único no broker (ou seja, se um client MQTT tentar entrar com o mesmo id de outro já conectado ao broker, o broker irá fechar a conexão de um deles). */ #define ID_MQTT "INCB_Cliente_MQTT" /* Variáveis e constantes globais */ /* SSID / nome da rede WI-FI que deseja se conectar */ const char* SSID = "SSID"; /* Senha da rede WI-FI que deseja se conectar */ const char* PASSWORD = "SENHA"; /* URL do broker MQTT que deseja utilizar */ const char* BROKER_MQTT = "broker.hivemq.com"; /* Porta do Broker MQTT */ int BROKER_PORT = 1883; /* Variáveis e objetos globais */ WiFiClient espClient; PubSubClient MQTT(espClient); //Prototypes void init_serial(void); void init_wifi(void); void init_mqtt(void); void reconnect_wifi(void); void mqtt_callback(char* topic, byte* payload, unsigned int length); void verifica_conexoes_wifi_mqtt(void); /* * Implementações das funções */ void setup() { init_serial(); init_wifi(); init_mqtt(); } /* Função: inicializa comunicação serial com baudrate 115200 (para fins de monitorar no terminal serial * o que está acontecendo. * Parâmetros: nenhum * Retorno: nenhum */ void init_serial() { Serial.begin(115200); } /* Função: inicializa e conecta-se na rede WI-FI desejada * Parâmetros: nenhum * Retorno: nenhum */ void init_wifi(void) { delay(10); Serial.println("------Conexao WI-FI------"); Serial.print("Conectando-se na rede: "); Serial.println(SSID); Serial.println("Aguarde"); reconnect_wifi(); } /* Função: inicializa parâmetros de conexão MQTT(endereço do * broker, porta e seta função de callback) * Parâmetros: nenhum * Retorno: nenhum */ void init_mqtt(void) { /* informa a qual broker e porta deve ser conectado */ MQTT.setServer(BROKER_MQTT, BROKER_PORT); /* atribui função de callback (função chamada quando qualquer informação do tópico subescrito chega) */ MQTT.setCallback(mqtt_callback); } /* Função: função de callback * esta função é chamada toda vez que uma informação de * um dos tópicos subescritos chega) * Parâmetros: nenhum * Retorno: nenhum * */ void mqtt_callback(char* topic, byte* payload, unsigned int length) { String msg; //obtem a string do payload recebido for(int i = 0; i < length; i++) { char c = (char)payload[i]; msg += c; } Serial.print("[MQTT] Mensagem recebida: "); Serial.println(msg); } /* Função: reconecta-se ao broker MQTT (caso ainda não esteja conectado ou em caso de a conexão cair) * em caso de sucesso na conexão ou reconexão, o subscribe dos tópicos é refeito. * Parâmetros: nenhum * Retorno: nenhum */ void reconnect_mqtt(void) { while (!MQTT.connected()) { Serial.print("* Tentando se conectar ao Broker MQTT: "); Serial.println(BROKER_MQTT); if (MQTT.connect(ID_MQTT)) { Serial.println("Conectado com sucesso ao broker MQTT!"); MQTT.subscribe(TOPICO_SUBSCRIBE); } else { Serial.println("Falha ao reconectar no broker."); Serial.println("Havera nova tentatica de conexao em 2s"); delay(2000); } } } /* Função: reconecta-se ao WiFi * Parâmetros: nenhum * Retorno: nenhum */ void reconnect_wifi() { /* se já está conectado a rede WI-FI, nada é feito. Caso contrário, são efetuadas tentativas de conexão */ if (WiFi.status() == WL_CONNECTED) return; WiFi.begin(SSID, PASSWORD); while (WiFi.status() != WL_CONNECTED) { delay(100); Serial.print("."); } Serial.println(); Serial.print("Conectado com sucesso na rede "); Serial.print(SSID); Serial.println("IP obtido: "); Serial.println(WiFi.localIP()); } /* Função: verifica o estado das conexões WiFI e ao broker MQTT. * Em caso de desconexão (qualquer uma das duas), a conexão * é refeita. * Parâmetros: nenhum * Retorno: nenhum */ void verifica_conexoes_wifi_mqtt(void) { /* se não há conexão com o WiFI, a conexão é refeita */ reconnect_wifi(); /* se não há conexão com o Broker, a conexão é refeita */ if (!MQTT.connected()) reconnect_mqtt(); } /* programa principal */ void loop() { /* garante funcionamento das conexões WiFi e ao broker MQTT */ verifica_conexoes_wifi_mqtt(); /* Envia frase ao broker MQTT */ MQTT.publish(TOPICO_PUBLISH, "ESP32 se comunicando com MQTT"); /* keep-alive da comunicação com broker MQTT */ MQTT.loop(); /* Agurda 1 segundo para próximo envio */ delay(1000); }
Para compilar e enviar o software compilado ao módulo, basta fazer da mesma forma como você faria no Arduino convencional. Ou seja, clique sobre o ícone em forma de “V” (em laranja na figura 5) para compilar e, para gravar o software no módulo, basta clicar no ícone em forma de seta para a direita (em amarelo na figura 5).
Testes e resultados
Para testar, utilize qualquer cliente MQTT disponível, como por exemplo o MQTTLens ( https://chrome.google.com/webstore/detail/mqttlens/hemojaaeigabkbcookmlgmdigohjobjm?hl=pt-BR ).
No cliente MQTT de sua escolha, publique qualquer mensagem no tópico que o ESP32 faz subscribe (no caso, utilizei o tópico INCB_ESP32_recebe_informacao) e observe a confirmação da recepção da mensagem no exemplo (via Serial Monitor, na Arduino IDE), conforme mostra a figura 6.
Ainda no cliente MQTT de sua escolha, se subscreva ao tópico que o ESP32 está publicando informações (no caso, utilizei o tópico INCB_ESP32_envia_informacao). Como resultado, você verá, segundo a segundo, a frase "ESP32 se comunicando com MQTT" chegar, conforme mostra a figura 7.
Desta forma, está validada a comunicação MQTT usando ESP32.
Conclusão
Neste artigo, vimos em linhas gerais o que é o protocolo de comunicação MQTT, do que é composta uma mensagem MQTT e, além disso, utilizamos um ESP32 para comunicação (envio e recepção) de mensagens MQTT para um broker.
Se aprofundando um pouco mais no conteúdo deste artigo, você conseguirá se comunicar com plataformas IoT diversas, podendo inserir seus projetos no mundo da Internet das Coisas de forma profissional.