Wi-fi Remote Control

NodeMCU e Aplicativos de Controle Remoto Wi-Fi para Celulares

Veja neste artigo um passo a passo completo sobre como desenvolver aplicativos de controle remoto wi-fi para celulares em HTML5 e usá-los em aplicações IoT com dispositivos NodeMCU e compatíveis (ESP8266 / ESP32)


Introdução

Atualmente tornou-se prática comum usar o celular como controle remoto em aplicações IoT e, para isso, existem várias alternativas de desenvolvimento tais como:

O que vamos mostrar aqui é uma solução alternativa para desenvolvimento de aplicativos mobile nativos usando HTML5 e tecnologias conhecidas, tais como:

JQuery: Biblioteca Javascript que facilita a manipulação de elementos e eventos em páginas WEB.

Onsen UI: Framework  de componentes CSS responsivos para desenvolvimento de aplicativos mobile com tecnologias tais como HTML5, CSS, Javascript.

PhoneGap: Framework para geração de aplicativos móveis híbridos a partir de aplicações web.

Aplicações móveis híbridas são aplicações que combinam componentes nativos e componentes web.
Do ponto de vista do usuário, uma aplicação híbrida é idêntica a uma aplicação nativa. No entanto, internamente, uma aplicação híbrida utiliza um componente web view que contém a maioria do conteúdo e lógica da aplicação.

O projeto

Para testar as ferramentas acima descritas, vamos desenvolver um pequeno projeto da seguinte forma:

  • Uma placa NodeMCU ligada a três leds;
  • Essa placa vai se conectar à rede wi-fi e rodar um servidor WEB que aguardará as requisições de um cliente;
  • O cliente vai ser nosso app de controle remoto no celular que apresentará uma tela para controlar os três leds. O LED 1 e 2 poderão ser ligados e desligados através de um botão. Já, o LED 3, vamos controlar sua luminosidade através de um slider.

Obs: Para termos uma ideia melhor, veja abaixo imagens e vídeos de nosso projeto já funcionando:

App de controle remoto
Tela do app de controle remoto


Vídeo demonstrativo

Passo 1: O protótipo

Os seguintes componentes serão necessários para montagem do nosso experimento:

  • Protoboard;
  • Placa NodeMCU ou compatível (Wemos, MKR1000, ESP8266 Standalone);
  • Três LEDs coloridos;
  • Três resistores de 220 Ohms;
  • Fios jumpers.

A montagem ficará da seguinte forma:

Protótipo controle remoto
Protótipo controle remoto

Passo 2: Sketch do NodeMCU

A função do nosso sketch será basicamente conectar-se à rede Wi-Fi e criar um web server que ficará aguardando e respondendo as requisições com os comandos de controle dos LEDs.
Vamos usar a própria IDE do Arduino para o desenvolvimento.  Caso ainda não tenha feito, será necessário configurar o ambiente com  o software das placas ESP8266. Para isso, siga os passos desse outro artigo: NodeMCU com a IDE do Arduino

Vejamos agora o código-fonte com os comentários:

/*
NodeMCU Web server for wi-fi remote control
2019, José Augusto Cintra
www.josecintra.com/blog
*/
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WiFiMulti.h> 
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;     // For multiple wi-fi configuratiosn
ESP8266WebServer server(80);    // Create a webserver object that listens for HTTP request on port 80

// function prototypes for HTTP handlers
void handleRoot();              
void handleRequest();
void handleNotFound();

void setup(void){
  delay(1000);
  pinMode(5, OUTPUT); //Led 1
  pinMode(4, OUTPUT); //Led 2
  digitalWrite(5, 0);
  digitalWrite(4, 0);

  analogWriteRange(100); //Led 3 (PWM) 
  analogWrite(0, 0);
  
  Serial.begin(9600);         // Start the Serial communication to send messages to the computer
  delay(10);
  Serial.println('\n');

  wifiMulti.addAP("ssid1", "password1");   // add Wi-Fi networks you want to connect to
  wifiMulti.addAP("ssid2", "password2");

  Serial.println("Connecting ...");
  int i = 0;
  while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above
    delay(250);
    Serial.print('.');
  }
  Serial.println('\n');
  Serial.print("Connected to ");
  Serial.println(WiFi.SSID());               // Tell us what network we're connected to
  Serial.print("IP address:\t");
  Serial.println(WiFi.localIP());            // Send the IP address of the ESP8266 to the computer

  if (MDNS.begin("esp8266")) {              // Start the mDNS responder for esp8266.local
    Serial.println("mDNS responder started");
  } else {
    Serial.println("Error setting up MDNS responder!");
  }

  server.on("/", HTTP_GET, handleRoot);        // Call the 'handleRoot' function when a client requests URI "/"
  server.on("/command", HTTP_POST, handleRequest); // Call the 'handRequest' function when a POST request is made to URI "/command"
  server.onNotFound(handleNotFound); // When a client requests an unknown URI 

  server.begin(); // Actually start the server
  Serial.println("HTTP server started");
  return;
}

void loop(void){
  server.handleClient(); // Listen for HTTP requests from clients
  return;
}

void handleRoot() {  // When URI / is requested, send a standard web page 
  server.send(200, "text/html", "Wi-fi Remote Control Example");
  return;
}

void handleNotFound(){
  server.send(404, "text/plain", "404: Not found"); // Send HTTP status 404 (Not Found) when there's no handler for the URI in the request
  return;
}

void handleRequest() { // If a POST request is made to URI /command
  // Validate parameters
  if(!server.hasArg("pin") || !server.hasArg("value") || server.arg("pin") == NULL || server.arg("value") == NULL) { 
    server.send(400, "text/plain", "400: Invalid Request");         // The request is invalid, so send HTTP status 400
    return;
  }
  
  // Get the parameters: pin and value
  String temp = "";
  temp = server.arg("pin"); 
  int pin = temp.toInt();
  temp = server.arg("value"); 
  int value = temp.toInt();

  Serial.println(pin);
  Serial.println(value);
  if (pin >= 0 && pin < 17 && value >= 0 && value <= 100) {
    if (pin == 0) {
      analogWrite(pin, value);
    } else {
      digitalWrite(pin, value);
    }
  }
  server.send(200, "text/html", "Wi-fi Remote Control Example");
  return;
}

Pontos de interesse:

  • A lib WiFiMulti permite configurar a autenticação de várias redes wi-fi, facilitando a conexão. Substitua o ssid e password de acordo com a sua rede;
  • A lib mDNS permite nomear um DNS para a rede local do ESP. Nesse caso, não estamos usando esse recurso, pois o Android não oferece suporte;
  • O método handleClient inicia um loop que atua como um listener para as requisições web;
  • Existem três funções que são responsáveis por tratar as requisições vindas do celular: 
    • A função handleNotFound é disparada quando a URI não foi encontrada;
    • A função handleRoot é disparada quando recebe uma requisição GET padrão. No nosso caso, essas requisição é desprezada;
    • A função handleRequest disparado pela condição server.on é responsável por tratar as requisições POST disparadas pelo controle remoto no celular. Nesse ponto iremos validar os parâmetros enviados pelo controle remoto e tomar a ação apropriada.

Passo 3: Aplicativo WEB

Para o desenvolvimento do aplicativo de controle remoto, teremos que fazer o download das bibliotecas JQuery e OnsenUI e descompactá-las nas pastas respectivas, conforme a estrutura descrita abaixo:

  • app
    config.xml ←Arquivo de configuração do Phonegap
    • www
      index.html ←Arquivo HTML de entrada
      • assets
        • img ←Aqui ficam os ícones do app
          icon-128.png

          icon-256.png
        • js
          wifi_rc.js  ←Arquivo JS com a lógica do app
        • lib
          • jquery  ←Descompacte o JQuery aqui
          • onsenui ←Descompacte o OnsenUI aqui

Neste momento vamos nos preocupar com os arquivos index.html e wifi_rc.js responsáveis pela apresentação e programação do nosso controle remoto, conforme abaixo.

<!--
  Wi-fi Remote Control with JQuery and Onsen UI
  Demo by José Cintra
  www.josecintra.com/blog
-->

<!DOCTYPE html>
<html lang="pt-br">

  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="Author" content="José Cintra"
    <link rel="stylesheet" href="assets/lib/OnsenUI/css/onsenui.min.css">
    <link rel="stylesheet" href="assets/lib/OnsenUI/css/onsen-css-components.min.css">
  </head>

  <body>

  <ons-page modifier="material">

    <ons-toolbar modifier="material">
      <div class="center"><strong>Wi-Fi Remote Control</strong></div>
    </ons-toolbar>
    <br/>
    <ons-card modifier="material">

      <div class="content">
        <ons-list modifier="material">

          <ons-list-item modifier="material">
            <div class="center" >
              <strong>LED 1 &nbsp;</strong> <label id="led1v">OFF</label>
            </div>
            <div class="right">
              <ons-switch id="led1" class="sw" modifier="material"></ons-switch>&nbsp;
            </div>
          </ons-list-item >
          <ons-list-item modifier="material">
            <div class="center" >
              <strong>LED 2 &nbsp;</strong> <label id="led2v">OFF</label>
            </div>
            <div class="right">
              <ons-switch id="led2" class="sw" modifier="material"></ons-switch>
            </div>
          </ons-list-item>
          <ons-list-item modifier="material">
            <div class="center">
              <strong>LED 3 &nbsp;</strong> <label id="led3v">0</label>% 
            </div>
            <div class="right">
              <ons-range id="led3" class="rg" modifier="material" style="width: 100%;" value="0" max = "100" ></ons-range>
            </div>
          </ons-list-item>
        </ons-list>
      </div>
    </ons-card>

  </ons-page>

  <!-- Javascript -->
  <script src="assets/lib/OnsenUI/js/onsenui.min.js"></script>
  <script src="assets/lib/jquery/jquery-3.3.1.min.js"></script>
  <script src="assets/js/wifi_rc.js"></script>

</body>

</html>
/* 
  Wi-fi Remote Control with JQuery and Onsen UI
  Demo by José Cintra
  www.josecintra.com/blog
*/

$(function () {

  // Server address and pin numbers of the board (ESP8266/32 and compatibles)
  let addr = "http://192.168.0.33/command"; 
  let pins = new Map([
    [ '#led1', '05' ],
    [ '#led2', '04' ],
    [ '#led3', '00' ], 
  ]);
   
  // Click Event on switch Class
  $('.sw').on('click', function (e) { 
    let onoff = ['OFF','ON'];
    let id = "#" + $(this).attr("id"); // Get the id of the control
    let pin = pins.get(id); // Pin number
    let value = String(+$(id).prop('checked')); // On or Off
    $(id + 'v').html(onoff[value]); 
    sendAjax(addr, pin, value);
  });

  // Input event on range class
  $('.rg').on('input', function (e) {
    let id = "#" + $(this).attr("id"); // Get the id of the control
    let value = String($(id).val()); // Input range
    $(id + 'v').html(value); // Notification
  });
  $('.rg').on('change', function (e) {
    let id = "#" + $(this).attr("id"); // Get the id of the control
    let pin = pins.get(id); // Pin number
    let value = String($(id).val()); // Input range
    sendAjax(addr, pin, value);
  });


});

function sendAjax(addr, p, v) {
  $.ajax({
    method: "POST",
    url: addr,
    data: {pin: p, value: v}
  });
}

Pontos de interesse:

  1. No arquivo index.html fica toda a parte de apresentação do aplicativo com os elementos CSS disponibilizados pelo framework OnsenUI.
  2. Os LEDs 1 e 2 serão controlados por um botão tipo switch e o LED 3 por um controle range. É importante observar os atributos id e class que vão ser usados no script JS.
  3. O código Javascript do arquivo wifi_rc.js adota o padrão ES6.
  4. No arquivo wifi_rc.js destaca-se o evento function() que ocorre uma única vez após a carga da página. É ali que programamos as ações que respondem aos eventos dos controles.
  5. Para representar os números dos pinos no NodeMCU usamos uma estrutura MAP. Essas informações serão passadas, juntamente com os valores dos controles, para o método sendAjax e enviadas ao web server da placa.

Passo 4: Aplicativo Mobile

Finalmente, nesse ponto vamos gerar nossa aplicação mobile para ser instalada no celular. Para que o nosso aplicativo HTML5 possa ser instalado como um app nativo,  será necessário usar as ferramentas de compilação do  Phonegap.
A maneira mais simples de fazer isso é usar os serviços de compilação em nuvem do Phonegap Build.

Os passos são os seguintes:

  1. Criar uma conta no serviço Phonegap Build, escolhendo um plano pago ou gratuito;
  2. Criar os ícones do aplicativo que serão exibidos no celular
  3. Criar um arquivo config.xml com as configurações necessárias;
  4. Compactar todos os arquivos e enviar para compilação;
  5. Solicitar a compilação do aplicativo;
  6. Escolher a plataforma desejada (Android, iPhone ou Microsoft) e baixar o app compilado para essa plataforma.

Obs: Os arquivos dos ícones devem ser no formato png, nos tamanhos 128×128 e 256×256.

Segue abaixo um exemplo de arquivo config:

<?xml version="1.0" encoding="UTF-8" ?>
<widget id="com.josecintra.wifi_rc" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
	<name>Wi-fi Remote Control</name>
	<description>Example of building a wi-fi remote control with HTML5/JQuery/Onsen UI and Phonegap. It will be used to control IoT devices, such as NodeMCU and compatibles</description>
	<author href="https://www.josecintra.com/blog" email="josecintra@josecintra.com">Jose Cintra</author>

	<icon src="www/assets/img/icon-256.png" width="256" height="256" density="xxxhdpi" />
	<icon src="www/assets/img/icon-128.png" width="128" height="128" density="xhdpi" />

	<preference name="android-targetSdkVersion" value="26" />

	<preference name="orientation" value="portrait" />
	<preference name="fullscreen" value="true" />
	<preference name="DisallowOverscroll" value="true" />
	
	<config-file parent="UIStatusBarHidden" platform="ios" target="*-Info.plist"><true/></config-file>
	<config-file parent="UIViewControllerBasedStatusBarAppearance" platform="ios" target="*-Info.plist"><false/></config-file>

	<preference name="deployment-target" value="10.0" />
	<preference name="android-minSdkVersion" value="21" />
	<access origin="*" />

	<plugin name="cordova-custom-config" />
	<plugin name="cordova-plugin-file" />
	<plugin name="cordova-plugin-media" />
	<plugin name="cordova-plugin-statusbar" />
	<plugin name="cordova-plugin-whitelist" />

	<engine name="ios" />
	<engine name="android" />
</widget>

Pontos de interesse

O arquivo config.xml contém todas as informações necessária para a geração do app. Entre elas, destacamos:

  • name: Nome do app
  • description: Descrição do app
  • author: Informações sobre o autor do app
  • icon: Nome dos ícones que irão identificar o app no celular
  • orientation: O app pode ser exibido no modo portrait, landscape. Caso não seja informado. O app irá se adaptar à inclinação do celular.
  • access origin: Define quais urls a aplicação pode acessar

Além dessas, existem dezenas de outras configurações. Um ponto importante é a escolha dos plugins que permitem adicionar funcionalidades adicionais ao aplicativo como o acesso à câmera, gps, e outros recursos nativos do celular. Para mais detalhes sobre o processo de compilação, consulte esse link.

Passo 5: Instalação do App

No passo anterior, geramos um app que pode ser instalado no celular ou disponibilizado para download nas app stores.  No nosso caso, geramos um app Android com a extensão apk.

Para testar esse aplicativo no celular sem passar pela app store, precisamos configurar o Android para permitir a instalação de apps de “Fontes desconhecidas”. Normalmente essa opção está na seção de segurança.
Depois disso, é só copiar o app para o celular. Isso pode ser feito de várias maneiras como, por exemplo, vias USB, Kies, Wi-Fi ou através de um link na Internet. O próprio Android cuidará da instalação.

E agora?

Apresentamos aqui um exemplo didático simples de um controle remoto wi-fi para dispositivos IoT. Esse exemplo pode ser aprimorado de diversas formas:

Últimas Palavras

Obrigado por visitar meu blog. Os arquivos desse tutorial estão todos disponíveis também no GitHub. Ah! Não esqueça de configurar o ssid e password do seu wi-fi  no sketch do NodeMCU…

Deixe um comentário

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