banner

Ir para o Forum

Automação com App Fernando K e firebase sem precisar de DDNS




Automação sem IP fixo! Hoje mostro como, com meu APP Fernando K, disponível para IOS e Android, você acessa o ESP de duas maneiras: utilizando o socket local e também pela nuvem, via Firebase. O mais legal deste projeto é que, além de fácil, ele é rápido, com delay muito reduzido.





Objetivo

Apresentar o aplicativo Fernando K e suas ferramentas:
·         Conexões:
Socket (Local)
Firebase
·         Componentes:
Botões
Sliders






Modelo ESP32 utilizado

Nas figuras abaixo temos os modelos mais comuns de ESP32 atualmente.
O mesmo projeto foi testado para estes 3 modelos.






Recursos usados

·         ESPWROOM-32 DevKit de 30 pinos
·         Servo Motor
·         Fios
·         Cabo USB para o ESP32
·         Celular Com aplicativo Fernando K






Link para download do Aplicativo








Tela inicial






Menu Options

·         Connection
·         Add Components
·         Screen Components Length
·         Enable/Disable DragButton
·         Save
·         About
·         Getting Buttons
·         Create Buttons
·         Save Buttons





Adicionar nova Conexão

1.       Clique no Botão Menu
2.       Clique em Connection
3.       Clique no botão “+”



Tipos de conexões:

1.       Conexão por Socket.
2.       Conexão por Firebase.





Conexão por Socket

Name: Informe o nome para identificar sua conexão
Host: Informar o Host da conexão
Port: Informar a Porta da conexão
Add: Adiciona uma nova conexão


O valor do Host e da Porta tem que ser o mesmo valores identificados no ESP




Conexão por Firebase

Para ter uma conexão com o Firebase, primeiramente é necessário criar uma conta, depois basta criar um banco de dados dentro do Firebase para obter a “databaseURL” utilizado para conectar ao aplicativo. Esses passos serão mostrados nos slides posteriores.






Criar banco de dados no Firebase

Primeiro acesse o site do Firebase. Clique aqui para acessá-lo.



Clique no botão escrito “Primeiros Passos”.



Para criar um banco de dados no Firebase é necessário ter uma conta do Google.



Faça o Login na sua conta e clique no botão “Seguinte”.



Clique em “Adicionar Projeto”.



Insira um nome para seu projeto. Aceite os termos e clique em “Criar projeto”.



Após criar, clique em “</>” ao lado dos ícones do iOS e do Android.



Copie e salve o conteúdo do “databaseURL” que será utilizado para fazer a conexão entre o aplicativo e o banco.



Clique em “Database”no lado esquerdo da tela.



Clique em “Criar banco de dados”.



Clique em “Criar banco de dados” na seção Realtime Database.



Selecione a opção “Iniciar no modo de teste” e depois clique em “Ativar”.



Pronto! O banco de dados está criado e em funcionamento.





Conectar

Para conectar, clique na conexão.






Comunicação Aplicativo – ESP

Após estar conectado, é possível se comunicar  com o ESP independente se a conexão for por Firebase ou Socket.

O Padrão de comunicação programado no ESP é o nome do comando, seguido por um valor (é possível alterar o padrão mudando o código do ESP).

Exemplos:
·         LED On
·         LED Off

·         Servo 0
·         Servo 180


A comunicação pode ser feita através de 3 maneiras:
1.       Terminal
2.       Botão
3.       Slider





Comunicação via Terminal

Digite um *comando no campo de texto e clique no botão OK.
*Os comandos devem estar devidamente programados no código fonte do ESP32.






Comunicação clique Botão

O botão é um componente que facilita a comunicação com o ESP32, pois os comandos são previamente programados no botão e enviados quando ocorrer um clique.
O botão pode conter mais de um comando, como um script. Os comandos são separados pela quebra de linha e enviados em ordem.




Adicionar novo Botão

1.       Clique no Botão Menu
2.       Clique em Add Components


Title: Informe o nome para identificar o botão
Code: *Informar o script.
Button Color: Cor Botão
Text Color: Cor do nome do Botão
Add: Adiciona o botão


*O Code pode ser um comando ou uma lista de comandos (Script).


Após clicar em Add um novo botão será criado conforme a figura 2.


*A lista de comandos reservados (como o Wait) está disponível no manual do usuário do aplicativo.
Basta clicar no botão para executar o script colocado no Code.




Comunicação Slider

O slider é um componente que facilita a comunicação com o ESP32 quando o valor a ser enviado está dentro de um intervalo numérico.
Assim, basta arrastar o slider até o valor desejado e será enviado um comando com o nome do slider seguido do valor.
Exemplo: Se o nome do slider for Servo e estiver no valor 180. O comando enviado para o ESP será “Servo 180”.




Adicionar novo Slider

1.       Clique no Botão Menu
2.       Clique em Add Components
3.       Clique em Slider



Title: Informe o nome para identificar o Slider
Min Value: Valor Mínimo.
Max Value: Valor Máximo.
Slider Color: Cor do Slider.
Maker Slider Color: Cor do Marcador
Add: Adiciona o slider.



Após clicar em Add um novo slider será criado conforme a figura 2.


*O nome do Slider será mandando junto com o valor atual para o ESP como um comando quando sofrer alteração.




Montagem

Pinout 30 pinos



Pinout 38 pinos






Montagem







Instalação das Bibliotecas

Instalação da Biblioteca Firebase

É necessário instalar a biblioteca do Firebase para o ESP32. Entre no link clicando aqui.



Clique em “Clone or Download” e depois “Download ZIP” para baixar o ZIP.



Na IDE do Arduino vá em Sketch->Incluir Biblioteca->Adicionar biblioteca .ZIP. Navegue até a pasta que baixou a biblioteca e adicione ela.






 Instalação da Biblioteca ArduinoJSON

Na IDE do Arduino vá em Sketch->Incluir Biblioteca->Gerenciar bibliotecas



Pesquise a biblioteca ArduinoJSON, selecione a primeira feita por Benoit Blanchon, selecione a versão 5.13.3 e clique em Instalar.





Código

Declarações e variáveis

#include <Arduino.h> //Lib Arduino
#include <WiFi.h> // Lib WiFi
#include <Wire.h>  // Necessário apenas para o Arduino 1.6.5 e posterior
#include <SPI.h> // Lib de comunicação SPI
#include <vector> // Lib com as funções de vetor (vector)
#include <IOXhop_FirebaseESP32.h> //Lib Firebase
#define FIREBASE_HOST "https://databaseURL.firebaseio.com" //databaseURL fornecido pelo Firebase para conexão
#define FIREBASE_AUTH ""
#include "esp_task_wdt.h"
const char* ssid = "NomeWiFi"; // Coloque o nome da sua rede wifi aqui
const char* password = "SenhaWiFi"; // Coloque a sua senha wifi aqui

const IPAddress IP = IPAddress(192,168,3,141); // IP fixo que o ESP utilizará
const IPAddress GATEWAY = IPAddress(192,168,3,1); // Gateway
const IPAddress SUBNET = IPAddress(255,255,255,0); // Máscara
// Google Public DNS represents two IP addresses for IPv4 – 8.8.8.8 and 8.8.4.4
const IPAddress PRIMARY_DNS(8, 8, 8, 8);   
const IPAddress SECONDARY_DNS(8, 8, 4, 4); 

const int port = 80; // Porta

// Objeto WiFi Server, o ESP será o servidor
WiFiServer server(port); 

// Vetor com os clientes que se conectarão no ESP
std::vector clients; 

// Task que insere novos clientes (recém conectados) no vector
void taskNewClients(void *);
// Task que recebe executa comandos dos clientes
void handleClients(void *);

int LED_BUILTIN = 2;
const int freq = 50; 
const int canal = 0; 
const int resolucao = 12; 

const int pinoAtuacao = 13; 
int ciclo = 200; 


Setup

void setup() 
{  
  // Iniciamos a serial com 115200 de velocidade
  Serial.begin(115200);  
  pinMode (LED_BUILTIN, OUTPUT);
  pinMode(pinoAtuacao, OUTPUT); 
  ledcSetup(canal, freq, resolucao); 
  ledcAttachPin(pinoAtuacao, canal); 
  //Iniciamos o valor do servo com 150, que se equivale a 0.
  ledcWrite(canal, 150);   

  // Iniciamos o servidor (WiFi Server)
  serverBegin();
  // Criamos 3 tasks (mais detalhes no escopo da função)
  createTasks();  
  //Inicia a conexão com o Firebase0
  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
  
  //Muda o valor do Servo no firebase para 0.
  Firebase.setInt("Servo", 0);
  //Se está o comando é On, ligar o LED.
  //Callback quando tem alguma alteração no firebase
  Firebase.stream("/", [](FirebaseStream stream) {
    //Entra quando o evento que vem do callback é de alteração "put"
    if (stream.getEvent() == "put") 
    {
      
      //Verifica se o nome é o mesmo do Servo. O nome tem que ser igual com o nome no Firebase e no Aplicativo.
      if (stream.getPath() == "/Servo") {
        //Muda o valor do Servo de acordo com oque foi recebido e é feito o mapeamento do valor.
        ledcWrite(canal, map(stream.getDataInt(),0,180,150,450));   
      }
      else{
        String cmd;
        cmd=stream.getPath();
        cmd.toUpperCase();
        Serial.println("Recebido: ["+stream.getPath()+"]");
        if(cmd == "/LED"){
          cmd= stream.getDataString();
          cmd.toUpperCase();
          Serial.println("Recebido2: ["+cmd+"]");
          if(cmd=="ON"){
            
            digitalWrite(LED_BUILTIN, HIGH);
          }
          else{
            if(cmd=="OFF"){
              digitalWrite(LED_BUILTIN, LOW);
            }
          } 
        }
      }  
    }
  });
}


Loop

void loop() 
{
  // Exibimos o total de clientes conectados
  showClients();
  delay(5000);   
}



ServerBegin

// Conectamos no WiFi e iniciamos o servidor
void serverBegin()
{ 
  // Iniciamos o WiFi  
  WiFi.begin(ssid, password);  
  Serial.println("Connecting to WiFi");
  
  // Enquanto não estiver conectado exibimos um ponto
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    delay(1000);
  }
  // Exibimos na serial
  Serial.println("OK");

  //Configuramos o WiFi com o IP definido anteriormente 
  if (!WiFi.config(IP, GATEWAY, SUBNET, PRIMARY_DNS, SECONDARY_DNS)) 
  {
    Serial.println("STA Failed to configure");
    while(1);
  }

  // Printamos o IP (debug)
  Serial.println(WiFi.localIP());
    
  // Iniciamos o servidor
  server.begin(port);
  delay(1000);
}


Create Tasks

// Criamos as 2 Tasks
void createTasks()
{    
  //Criamos a task que insere os novos clientes no vector
  xTaskCreatePinnedToCore(taskNewClients, "taskNewClients", 10000, NULL, 2, NULL, 1);

  //Criamos a task que recebe e executa os comandos dos clients conectados
  xTaskCreatePinnedToCore(handleClients, "handleClients", 10000, NULL, 2, NULL, 1);  
}


Task 2: Core 0 - taskNewClients


// Task que insere novos clientes conectados no vector
void taskNewClients(void *p)
{
  // Objeto WiFiClient que receberá o novo cliente conectado
  WiFiClient newClient;
  // Tempo esperado no delay da task (1 ms)
  TickType_t taskDelay = 1 / portTICK_PERIOD_MS;

  while(true)
  {    
    // Se existir um novo client atribuimos para a variável
    newClient = server.available(); 
    
    // Se o client for diferente de nulo
    if(newClient)    
    {      
      // Inserimos no vector
      clients.push_back(newClient);
      // Exibimos na serial indicando novo client e a quantidade atual de clients
      Serial.println("New client! size:"+String(clients.size()));
    }    
    // Aguardamos 1ms
    vTaskDelay(taskDelay);
  }
}


Task 3: Core 0 – handleClients


// Função que verifica se um cliente enviou um comando
void handleClients(void *p)
{
  // String que receberá o comando
  String cmd;
  // Tempo aguardado pela task (1 ms)
  TickType_t taskDelay = 1 / portTICK_PERIOD_MS;

  // Loop infinito
  while(true)
  {    
    // Atualizamos o vector deixando somente os clientes conectados
    refreshConnections();

    // Percorremos o vector
    for(int i=0; i<clients.size(); i++)
    {      
      // Se existir dados a serem lidos
      if(clients[i].available())
      {
        // Recebemos a String até o '\n'
        cmd = clients[i].readStringUntil('\n');
        // Executamos um comando, enviando por parametro a String cmd, e o client que nos enviou o comando
        executeCommand(cmd, clients[i]);        
      }          
      // Delay de 1 ms
      esp_task_wdt_reset();
    }
    // Delay de 1 ms
    vTaskDelay(taskDelay); 
  }
}



refreshConnections (função chamada pela task handleClients)

// Função que verifica se um ou mais clients se desconectaram do server e, se sim, estes clients serão retirados do vector
void refreshConnections()
{
  // Flag que indica se pelo menos um client ser desconectou
  bool flag = false;
  // Objeto que receberá apenas os clients conectados
  std::vector<WiFiClient> newVector;

  // Percorremos o vector
  for(int i=0; i<clients.size(); i++)
  {
    // Verificamos se o client está desconectado
    if(!clients[i].connected())
    {
      // Exibimos na serial que um cliente se desconectou e a posição em que ele está no vector (debug)
      Serial.println("Client disconnected! ["+String(i)+"]");
      // Desconectamos o client
      clients[i].stop();      
      // Setamos a flag como true indicando que o vector foi alterado
      flag = true;          
    }
    else
      newVector.push_back(clients[i]); // Se o client está conectado, adicionamos no newVector
  }  
  // Se pelo menos um client se desconectou, atribuimos ao vector "clients" os clients de "newVector"
  if(flag)
    clients = newVector;
}


executeCommand (função chamada pela task handleClients)

// Função que executa um comando de acordo com a String "cmd"
void executeCommand(String cmd, WiFiClient client)
{
  // String que guarda a resposta que será enviada para o client
  String response = "";

  // Se a String estiver vazia, abortamos a função
  if (cmd.equals("")) 
    return;

  // Exibimos o comando recebido na serial e no 
  Serial.println("Recebido: ["+cmd+"]");
  // Deixamos a string toda em maiúsculo
  cmd.toUpperCase();  
  //Verifica se o comando é destinado ao Servo
  if(cmd.indexOf("SERVO")==0)
  {
    //Recebe o grau de acordo com o valor após o espaço " "
    int grau = atoi((cmd.substring(cmd.indexOf(" ")+1).c_str()));

    //Mapeia o valor recebido para o valor do Servo
    ciclo = map(grau,0,180,150,450);
    ledcWrite(canal, ciclo); 
    
    response = "ok";
  }
  else{
    if(cmd.indexOf("LED ON")==0){
      digitalWrite(LED_BUILTIN, HIGH);
       response = "ok";
    }
    else{
      if(cmd.indexOf("LED OFF")==0){
        digitalWrite(LED_BUILTIN, LOW);
         response = "ok";
      }
      else{
        response = "Invalid Command";
      }
    }
    
  }
  // Enviamos para o client passado por parâmetro e exibimos sucesso ou falha na serial
  if(client.print(response)>0)
    Serial.println("Resposta enviada");
  else
    Serial.println("Erro ao enviar resposta"); 
}




Faça o download dos arquivos:





4 comentários:

  1. Bom dia. Essa biblioteca do firebase funciona com Arduino uno R3, no caso eu teria que modifica-lo para usar o módulo ethernet?

    ResponderExcluir
    Respostas
    1. Olá, você pode entrar no blog http://forum.fernandok.com e colocar sua dúvida lá!

      Abs

      Excluir
  2. Boa tarde! O app funciona com placas NodeMCU ESP8266?

    ResponderExcluir
    Respostas
    1. Olá, você pode entrar no blog http://forum.fernandok.com e colocar sua dúvida lá!

      Abs

      Excluir

Tecnologia do Blogger.