WebSocket ESP8266

Comunicação via WebSockets com NodeMCU – ESP8266

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.
WebSocket Diagram
WebSocket Diagrama

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

Protótipo WebSocket NodeMCU
Protótipo WebSocket NodeMCU

Componentes:

  1. 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;
  2. Sensor de temperatura do tipo termistor NTC 10K. No vídeo eu utilizei um módulo termistor da GBK;
  3. 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:

  1. 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;
  2. 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
  3. 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…

3 comentários em “Comunicação via WebSockets com NodeMCU – ESP8266”

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *