Controle e Monitoramento IoT com NodeMCU e MQTT
O módulo Wifi ESP8266 NodeMCU é uma das mais interessantes placas / plataformas existentes. A razão disso é simples: em uma pequena placa estão disponíveis I/Os, circuitaria de regulação de tensão, conectividade USB para programação (em Lua ou pela Arduino IDE) e conectividade WI-FI (ESP8266 12-E), caracterizando uma placa auto-suficiente para projetos envolvendo IoT, como faremos neste post envolvendo o NodeMCU e MQTT.
Será mostrada uma das mais interessantes aplicações desta placa: a conectividade com a nuvem através de MQTT para monitoramento e controle de um output, com um diferencial: a interface com o usuário sendo uma página web.
Funcionamento do MQTT
O MQTT (Message Queue Telemetry Transport) consiste em um protocolo de mensagens leve, criado para comunicação M2M (Machine to Machine). Por exigir muito pouco “poder de fogo” em termos de processamento e banda / consumo de Internet, este é um dos protocolos ideais para dispositivos embarcados. Por esta razão, o MQTT é famoso no conceito IoT (Internet of Things).
Uma comunicação MQTT é composta das seguintes partes: há publishers (quem irá disponibilizar informações), subscribers (quem irá receber as informações) e Broker (servidor MQTT, na nuvem / acessível de qualquer lugar do planeta que contenha conexão com a Internet). Teoricamente, não há limite especificado de subscribers e publishers em uma mesma comunicação MQTT, pois o limite nesse aspecto é do servidor em lidar com as conexões.
Em suma: publishers enviam informação para o Broker, subscribers recebem informação do Broker e o Broker gerencia a troca de mensagens. Ou seja, o trabalho pesado fica a cargo do Broker, deixando os sistemas embarcados livre para gerenciar outras coisas.
Sem entrar no mérito da especificação do protocolo MQTT (ou seja, byte a byte do protocolo), grosso modo uma mensagem MQTT publicada / enviada possui duas partes 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. 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 IoT que usa MQTT possui somente um servidor (Broker), sendo todo o restante composto de clients MQTT.
Outra informação importante é que um mesmo client MQTT pode ser subscriber e publisher de diversos tópicos.
Como utilizar o NodeMCU e MQTT
O NodeMCU pode ser programado para interagir com um broker MQTT, ou seja, ele pode ser programado para ser um client MQTT. Antes de ser programado para isso, é necessário:
1 – Preparar a IDE Arduino para programar o NodeMCU. Veja como fazer isso no post Como programar o NodeMCU com IDE Arduino
2 – Baixar e instalar a biblioteca pubsubclient. Para baixar, visite o GitHub do projeto.
Feito isso, devemos escolher um broker MQTT para utilizar. Há inúmeros brokers disponíveis para uso (tanto públicos / sem custo quanto privados / pagos). Dos públicos, eu recomendo fortemente o iot.eclipse.org (broker oficial da equipe que mantém o MQTT), que irá funcionar perfeitamente com o nosso projeto de NodeMCU e MQTT.
Enfim, agora é possível usarmos o NodeMCU como um client MQTT! Para isso, será feito um projeto que permite controlar e monitorar um output do NodeMCU via MQTT. O output em questão é o próprio LED da placa (que, neste caso, está ligado diretamente ao output D0).
Importante:
1) O LED possui acionamento em lógica invertida, ou seja, para acendê-lo é preciso enviar o estado LOW para D0 e para apagá-lo é necessário enviar estado High. Isto ocorre pois o output está ligado ao catodo do LED na placa.
2) O ID MQTT no código serve como identificador para o broker, o permitindo gerenciar as conexões. Se você escolher um ID MQTT que já está sendo utilizado, a conexão do mesmo será interrompida para a sua ser estabelecida. Isto pode gerar grandes transtornos para ambos os dispositivos com mesmo ID MQTT. Desta forma, recomendo que o ID MQTT seja escolhido como algo aleatório (assim garante-se unicidade do mesmo).
3) Como não foi utilizado nenhum componente além da própria NodeMCU, não há circuito esquemático envolvido (ou seja, em termos de hardware é preciso somente do NodeMCU!)
Programa NodeMCU e MQTT
Vamos à programação!
#Programa: NodeMCU e MQTT - Controle e Monitoramento IoT #Autor: Pedro Bertoleti #include <ESP8266WiFi.h> // Importa a Biblioteca ESP8266WiFi #include <PubSubClient.h> // Importa a Biblioteca PubSubClient //defines: //defines de id mqtt e tópicos para publicação e subscribe #define TOPICO_SUBSCRIBE "MQTTFilipeFlopEnvia" //tópico MQTT de escuta #define TOPICO_PUBLISH "MQTTFilipeFlopRecebe" //tópico MQTT de envio de informações para Broker //IMPORTANTE: recomendamos fortemente alterar os nomes // desses tópicos. Caso contrário, há grandes // chances de você controlar e monitorar o NodeMCU // de outra pessoa. #define ID_MQTT "HomeAut" //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). //defines - mapeamento de pinos do NodeMCU #define D0 16 #define D1 5 #define D2 4 #define D3 0 #define D4 2 #define D5 14 #define D6 12 #define D7 13 #define D8 15 #define D9 3 #define D10 1 // WIFI const char* SSID = "SSID"; // SSID / nome da rede WI-FI que deseja se conectar const char* PASSWORD = "SENHA"; // Senha da rede WI-FI que deseja se conectar // MQTT const char* BROKER_MQTT = "iot.eclipse.org"; //URL do broker MQTT que se deseja utilizar int BROKER_PORT = 1883; // Porta do Broker MQTT //Variáveis e objetos globais WiFiClient espClient; // Cria o objeto espClient PubSubClient MQTT(espClient); // Instancia o Cliente MQTT passando o objeto espClient char EstadoSaida = '0'; //variável que armazena o estado atual da saída //Prototypes void initSerial(); void initWiFi(); void initMQTT(); void reconectWiFi(); void mqtt_callback(char* topic, byte* payload, unsigned int length); void VerificaConexoesWiFIEMQTT(void); void InitOutput(void); /* * Implementações das funções */ void setup() { //inicializações: InitOutput(); initSerial(); initWiFi(); initMQTT(); } //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 initSerial() { Serial.begin(115200); } //Função: inicializa e conecta-se na rede WI-FI desejada //Parâmetros: nenhum //Retorno: nenhum void initWiFi() { delay(10); Serial.println("------Conexao WI-FI------"); Serial.print("Conectando-se na rede: "); Serial.println(SSID); Serial.println("Aguarde"); reconectWiFi(); } //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 initMQTT() { MQTT.setServer(BROKER_MQTT, BROKER_PORT); //informa qual broker e porta deve ser conectado MQTT.setCallback(mqtt_callback); //atribui função de callback (função chamada quando qualquer informação de um dos tópicos subescritos chega) } //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; } //toma ação dependendo da string recebida: //verifica se deve colocar nivel alto de tensão na saída D0: //IMPORTANTE: o Led já contido na placa é acionado com lógica invertida (ou seja, //enviar HIGH para o output faz o Led apagar / enviar LOW faz o Led acender) if (msg.equals("L")) { digitalWrite(D0, LOW); EstadoSaida = '1'; } //verifica se deve colocar nivel alto de tensão na saída D0: if (msg.equals("D")) { digitalWrite(D0, HIGH); EstadoSaida = '0'; } } //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 reconnectMQTT() { 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 reconectWiFi() { //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); // Conecta na rede WI-FI 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 VerificaConexoesWiFIEMQTT(void) { if (!MQTT.connected()) reconnectMQTT(); //se não há conexão com o Broker, a conexão é refeita reconectWiFi(); //se não há conexão com o WiFI, a conexão é refeita } //Função: envia ao Broker o estado atual do output //Parâmetros: nenhum //Retorno: nenhum void EnviaEstadoOutputMQTT(void) { if (EstadoSaida == '0') MQTT.publish(TOPICO_PUBLISH, "D"); if (EstadoSaida == '1') MQTT.publish(TOPICO_PUBLISH, "L"); Serial.println("- Estado da saida D0 enviado ao broker!"); delay(1000); } //Função: inicializa o output em nível lógico baixo //Parâmetros: nenhum //Retorno: nenhum void InitOutput(void) { //IMPORTANTE: o Led já contido na placa é acionado com lógica invertida (ou seja, //enviar HIGH para o output faz o Led apagar / enviar LOW faz o Led acender) pinMode(D0, OUTPUT); digitalWrite(D0, HIGH); } //programa principal void loop() { //garante funcionamento das conexões WiFi e ao broker MQTT VerificaConexoesWiFIEMQTT(); //envia o status de todos os outputs para o Broker no protocolo esperado EnviaEstadoOutputMQTT(); //keep-alive da comunicação com broker MQTT MQTT.loop(); }
Desta forma, seu NodeMCU vira um client MQTT acessível de qualquer lugar do planeta!
Interação com o NodeMCU
Vimos até agora que é possível transformar a incrível placa NodeMCU em um client MQTT acessível por todo o planeta. Mas e para interagir com ele (ligar e desligar o output), como faremos?
Para isso, desenvolvi uma interface web que pode ser baixada clicando AQUI. Você pode hospedar essa interface em QUALQUER servidor web ou mesmo rodar no seu próprio computador / rodar localmente (desde que o computador possua conexão com Internet, claro)!
Esta interface web é basicamente um websocket que se comunica diretamente com o broker, por isso pode estar rodando em qualquer lugar com disponibilidade de Internet que funciona.
Observe a figura abaixo:

Figura 1 – screenshot da página de controle do NodeMCU
Para funcionar:
1 – Definir as strings dos tópicos de publish e subscribe e clicar em “Conectar”. Aguardar a mensagem Conectado ao Broker! aparecer na sessão “Debug / respostas do servidor”.
2 – Clicar em Ligar e Desligar para comandar o LED da placa NodeMCU. Observe que na sessão “Status do output” irá constar o estado atual do LED. Este estado é recebido do próprio NodeMCU, via MQTT (o que torna este controle um controle em tempo real).
3 – Se divertir com o NodeMCU e MQTT!
Gostou ? Ajude-nos a melhorar o blog atribuindo uma nota a este tutorial (estrelas no final do artigo), comente e visite nossa loja FILIPEFLOP!
Site: Blog FILIPEFLOP