banner

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:





9 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
  3. Olá Fernando, o app não está salvando as configurações, saiu do app, tem que configurar tudo, ip, firebase, botão e etc. Grato.

    ResponderExcluir
  4. Não está disponível na App Store da Apple

    ResponderExcluir
  5. Boa noite,
    Fiz tudo conforme explicado, porém não esta me dando nenhum retorno dos valores modificados.

    ResponderExcluir
  6. Bom dia, está funcionando corretamente por scocket, pelo firebase funciona na inicialização apos alguns minutos para de funcionar e so volta apos reset.

    ResponderExcluir

Tecnologia do Blogger.