Tutorial sobre comunicação com WebSockets usando as placas NodeMCU, ESP8266 ou ESP32.
Neste experimento vamos demonstrar como estabelecer uma comunicação usando WebSockets entre um servidor NodeMCU e um cliente WEB que pode ser um PC, um celular ou outra placa ESP*.*
Sobre WebSockets
Websocket é um protocolo de comunicação muito mais rápido que o protocolo HTTP padrão. Websocket permite abrir um canal único de comunicação bidirecional entre dois dispositivos, sem a necessidade de fechar a conexão a cada requisição. Ou seja, se ajusta muito bem no mundo IoT.
Principais vantagens:
- Bidirecional: O protocolo HTTP é unidirecional, ou seja, o cliente envia uma solicitação à qual o servidor responde. O WebSocket é um protocolo bidirecional FULL-DUPLEX no qual o cliente ou servidor podem enviar uma mensagem para a outra parte.
- Conexão TCP única: Diferentemente do protocolo HTTP padrão, com o WebSocket, o cliente e o servidor se comunicam na mesma conexão TCP até que um deles feche a conexão.
- Leve: o Websocket concentra-se no essencial, diferente do HTTP, que carrega muita informação para cada requisição. Em termos de desempenho, o Websocket é muito mais rápido.
A principal desvantagem do protocolo WebSocket é que, por ser muito leve, ele não possui uma camada para tratamento dos pacotes enviados com eficácia. Não existe garantia de integridade entre o dado que foi enviado e o que foi recebido. Portanto, ele não deve ser usado em aplicações ditas críticas, onde necessita-se dessa garantia. Mas é suficiente em um grande número de aplicações.
O Projeto de Teste
Para testar as tecnologias aqui citadas, vamos criar um cliente WEB que vai se comunicar com um servidor WebSocket em uma placa NodeMCU. Nosso objetivo é realizar a comunicação bidirecional:
- A página WEB vai possuir um botão ON/OFF para controlar um LED da placa (desculpem o clichê)
- O servidor vai enviar para a página informações de um sensor de temperatura de 10 em 10 segundos (outro clichê)
Para entender melhor o funcionamento da experiência, veja aqui um vídeo demonstrativo:
O protótipo
Componentes:
- Placa NodeMCU. Na verdade pode ser qualquer placa da Familia ESP*.* compatível com a IDE do Arduino (ESP8266, NodeMCU, Wemos, ESP32, etc.). No vídeo eu utilizei uma NodeMCU Lolin com placa de expansão;
- Sensor de temperatura do tipo termistor NTC 10K. No vídeo eu utilizei um módulo termistor da GBK;
- Fios jumper e, opcionalmente, uma protoboard.
Recursos necessários
Software Cliente (WEB)
Nossa página vai ser criada em HTM5 com Bootstrap 4 e JQuery. Veja abaixo onde obter os pacotes:
- O Bootstrap 4 é Framework CSS responsivo para front-end. Com isso, nossa página vai ser exibida perfeitamente tanto em desktops quanto no mobile. Precisaremos também instalar o pacote de fontes FontAwesome e, opcionalmente, a biblioteca de popups Popper.
- O JQuery é uma biblioteca Javascript que facilita a manipulação dos elementos HTML (DOM).
Software NodeMCU
- Para criamos o servidor websocket vamos precisar instalar a biblioteca arduinoWebSockets que pode ser instalada pela própria IDE do Arduino através de gerenciador de bibliotecas;
- Os dados de temperatura do sensor serão enviados a cada 10 segundos para o cliente WEB. Para descomplicar nosso código, vamos fazer a contagem do tempo com millis fazendo uso da biblioteca NeoTimer, que também está disponível no gerenciador.
- A leitura e cálculo da temperatura será feita através da seguinte rotina disponível na Internet: Thermistor Interfacing with NodeMCU
O código!
Os arquivos com o códigos-fonte e as bibliotecas do lado cliente estão disponíveis aqui no GitHUB.
Lado Cliente
Nossa aplicação WEB é composta de 2 arquivos. O código HTML5 e o script JQuery. Os comentários estão no próprio código fonte:
<!DOCTYPE HTML> <html lang="pt-br"> <head> <title>Teste WebSockets</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/fontawesome/css/all.min.css"> </head> <body class="bg-secondary"> <br/> <div class="container bg-secondary"> <div class="row"> <div class="col-sm-12"> <div class="card bg-dark text-white"> <div class="card-body"> <h2 class="text-center">Teste WebSocket</h2> </div> </div> </div> </div> <br/> <div class="row"> <div class="col-sm-6"> <div class="card bg-dark text-white"> <div class="card-body"> <h5 class="card-title"><strong><i class="fas fa-lightbulb"></i> Situação do LED</strong></h5> <button id="led-status" type="button" class="btn btn-lg btn-secondary">OFF</button> </div> </div> </div> <div class="col-sm-6"> <div class="card bg-dark text-white"> <div class="card-body"> <h5 class="card-title"><strong><i class="fas fa-thermometer-half"></i> Temperatura Atual</strong></h5> <h2><span id="temperature">35</span> °C</h2> </div> </div> </div> </div> <br/> <div class="row"> <div class="col-sm-12"> <div class="card bg-dark text-white"> <div class="card-body"> <h5 class="card-title"><strong><i class="fas fa-history"></i> Log de comunicação</strong></h5> <textarea id="log" class="form-control bg-dark text-white" rows="6" readonly></textarea> </div> </div> </div> </div> </div> <script src="assets/jquery/jquery.min.js" type="text/javascript"></script> <script src="assets/popper/popper.min.js" type="text/javascript"></script> <script src="assets/boostrap/js/bootstrap.min.js" type="text/javascript"></script> <script defer src="assets/fontawesome/js/all.min.js"></script> <script src="assets/js/index.js" type="text/javascript"></script> </body> </html>
/*** Teste de Websockets com ESP8266 - Cliente José Cintra em Outubro de 2019 - www.josecintra.com/blog/comunicacao-websockets-nodemcu-esp8266"> ***/ $(document).ready(function () { let ledStatus = 0; // Status do LED 0 = desligado / 1 = ligado let buttonText = ['OFF', 'ON']; // Texto do botão do LED de acordo com o status let buttonStyle = ['btn btn-lg btn-secondary', 'btn btn-lg btn-success']; // Estilo do botão do LED de acordo com o status // Coloque aqui o IP obtino no ESP8266 let con = new WebSocket('ws://192.168.0.31:81/', ['arduino']); // Evento que ocorre quando a placa envia dados let previous = ""; con.onmessage = function (evt) { if (evt.data != "ON" && evt.data != "OFF"){ $('#temperature').html(evt.data); } previous = $('#log').html(); $('#log').html(previous + '\n' + evt.data); }; //$('#led-status').html(buttonText[ledStatus]); // Clique no botão do LED $('#led-status').click(function () { // Muda a classe e texto do botão de acordo com o status ledStatus = (ledStatus + 1) % 2; // Alterna entre 0 e 1 $('#led-status').removeClass(); $('#led-status').addClass(buttonStyle[ledStatus]); $('#led-status').html(buttonText[ledStatus]); // envia o comando para a placa con.send(buttonText[ledStatus]); }); });
Observações:
Neste exemplo, não usamos um servidor WEB para “hospedar” a página. Mas caso queira usar um banco de dados ou outro recurso de servidor, será necessário configurar um. Recomendo o instalador XAMPP (PHP) para isso.
Lado Servidor
Abaixo o código do ESP8266. Para saber como usar a IDE Arduino com as placas ESP8266, veja o seguinte artigo: Tutorial: Programando a NodeMCU (ESP8266) com a IDE do Arduino
/*** Teste de Websockets com ESP8266 - Servidor José Cintra em Outubro de 2019 - www.josecintra.com/blog/comunicacao-websockets-nodemcu-esp8266"> ***/ #include <ESP8266WiFi.h> #include <WebSocketsServer.h> #include <neotimer.h> //Correção nivel lógico invertido #define ON LOW #define OFF HIGH WebSocketsServer webSocket = WebSocketsServer(81); // Recebe dados do cliente Neotimer mytimer = Neotimer(10000); // Intervalo de 10 segundos para envio dos dados do sensor // Autenticação wi-fi - Coloque aqui a sua configuração const char* ssid = "xxxx"; const char* password = "xxxx"; const int pinLED = D4; String tempString; // Temperatura convertida para String // Tratamento de eventos dos dados que vêm do cliente void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) { switch (type) { case WStype_DISCONNECTED: break; case WStype_CONNECTED: { IPAddress ip = webSocket.remoteIP(num); Serial.println(ip); } break; case WStype_TEXT: { String text = String((char *) &payload[0]); Serial.println(text); Serial.println(num); Serial.println(type); if (text == "ON") { digitalWrite(pinLED, ON); } else { digitalWrite(pinLED, OFF); } } break; } } void setup() { // Inicialização do LED Serial.begin(9600); pinMode(pinLED, OUTPUT); digitalWrite(pinLED, OFF); // Conexões wi-fi e websocket WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { Serial.print(". "); delay(100); } Serial.println(WiFi.localIP()); webSocket.begin(); webSocket.onEvent(webSocketEvent); } void loop() { webSocket.loop(); // Envio periódico dos dados do sensor de temperatura para o cliente if (mytimer.repeat()) { tempString = readTemperature(); webSocket.sendTXT(0, tempString); } } // Leitura e cálculo da temperatura pelo termistor double readTemperature() { // Código extraído do tutorial 'Thermistor Interfacing with NodeMCU' disponível em: // https://www.electronicwings.com/nodemcu/thermistor-interfacing-with-nodemcu const double VCC = 3.3; // NodeMCU on board 3.3v vcc const double R2 = 10000; // 10k ohm series resistor const double adc_resolution = 1023; // 10-bit adc const double A = 0.001129148; // thermistor equation parameters const double B = 0.000234125; const double C = 0.0000000876741; double Vout, Rth, temperature, adc_value; adc_value = analogRead(A0); Vout = (adc_value * VCC) / adc_resolution; Rth = (VCC * R2 / Vout) - R2; temperature = (1 / (A + (B * log(Rth)) + (C * pow((log(Rth)), 3)))); // Temperature in kelvin temperature = temperature - 273.15; // Temperature in degree celsius delay(500); return (temperature); }
Observações:
- As portas do NodeMCU possuem o nível lógico invertido. Cuidado aqui! Isso significa que o LED acende com LOW e apaga com HIGH;
- O LED interno do NODEMCU V3 (Lolin) está em uma porta diferente das demais versões. Então aqui não podemos usar a constante LED_BUILTIN
- Como dissemos acima, para ler e calcular a entrada análogica do sensor NTC, usamos uma rotina específica, já que o sinal lógico dos ESP é de 3.3V, o que pode acarretar inconsistências nos valores da temperatura
Referências
Conclusão
Isso é tudo. Espero ter ajudado…
Ótimo artigo!!Parabens!!
Obrigado Ciro!
Muito bom artigo para estudo ! Abraço