Recents in Beach


Receba o meu conteúdo GRATUITAMENTE


Automação LoRa e APP Fernando K




Múltiplos assuntos neste vídeo de hoje: ESP32 LoRa, Gateway e Multi Client. Vamos fazer uma automação para modificar o estado de relês utilizando ainda o APP Fernando K. A abordagem é simples de temas que considero mais densos. Confere aí!





RECURSOS USADOS

  • Heltec WiFi LoRa 32
  • Jumpers
  • Relê
  • Fonte





MONTAGEM






BIBLIOTECAS

Heltec ESP32 Dev-Boards




TaskScheduler






SELEÇÃO DE PLACA

Ferramentas->Placa->Heltec WiFi LoRa 32






CÓDIGO GATEWAY APP

Fluxograma




Declarações e variáveis

#include <heltec.h>
#include <TaskScheduler.h>
#include <vector>
#include <WiFi.h>

//Frequência
#define BAND 433E6

//Pino onde o relê está
#define RELAY 13

//SSID e senha do roteador que vc usa em sua casa
#define  SSID     "SSID"
#define  PASSWORD "PASSWORD"

//Objeto que vamos utilizar para guardar o ip recebido
IPAddress myIP;

// Porta do server que vc vai utilizar para conectar pelo aplicativo
const int port = 80; 

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

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

//Tarefas para verificar novos clientes e mensagens enviadas por estes
Scheduler scheduler;
void taskNewClients();
void taskHandleClients();
//Tarefa para verificar se uma nova conexão feita por aplicativo está sendo feita
Task t1(100, TASK_FOREVER, &taskNewClients, &scheduler, true);
//Tarefa para verificar se há novas mensagens vindas de aplicativo
Task t2(100, TASK_FOREVER, &taskHandleClients, &scheduler, true);

//Id e estados deste esp (altere para cada esp)
String ID = "LIGHT1";
String ID_ON = ID + " ON";
String ID_OFF = ID + " OFF";

//Variável para guardar o valor do estado atual do relê 
String currentState = ID_OFF;


setup

void setup() {
  //Coloca tudo em maiúsculo
  ID_ON.toUpperCase();
  ID_OFF.toUpperCase();

  //Coloca o pino onde o relê está como saída
  pinMode(RELAY, OUTPUT);

  Heltec.begin(true /*Ativa o display*/, true /*Ativa lora*/, true /*Ativa informações pela serial*/, true /*Ativa PABOOST*/, BAND /*frequência*/);
 
  //Inicializa o display
  setupDisplay();

  //Ativa o recebimento de pacotes
  LoRa.receive();

  //Se conecta à rede WiFi
  setupWiFi();

  //Inicializa o server ao qual vc vai se conectar utilizando o aplicativo
  server.begin(port);

  //Modifica o estado do relê para desligado
  verifyAndSetRelayState(ID_OFF);

  //Inicializa o agendador de tarefas
  scheduler.startNow();
}


setupDisplay

//Inicializa o display
void setupDisplay() {
  Heltec.display->init();
  //Limpa o display
  Heltec.display->clear();
  //Modifica direcionamento do texto
  Heltec.display->flipScreenVertically();  
  //Alinha o texto à esquerda
  Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT);
  //Altera a fonte
  Heltec.display->setFont(ArialMT_Plain_16);
  //Exibe no display
  Heltec.display->drawString(0, 0, "Display OK");
  Heltec.display->display();
}



setupWiFi

void setupWiFi() {
  Serial.print("Conectando");

  //Faz o ESP se conectar à rede WiFi
  WiFi.begin(SSID, PASSWORD);

  //Enquanto o ESP não se conectar à rede
  while (WiFi.status() != WL_CONNECTED)
  {
    //Esperamos 100 milisegundos
    delay(100);
    Serial.print(".");
  }

  //Se chegou aqui é porque conectou à rede, então mostramos no monitor serial para termos um feedback
  Serial.println("");
  Serial.println("Conectou");

  //Objeto que vamos utilizar para guardar o ip recebido
  myIP = WiFi.localIP();
  //Mostra o ip no monitor serial
  Serial.println(myIP);

  //Atualiza o display para exibir o ip
  refreshDisplay();
}


refreshDisplay

void refreshDisplay() {
  //Limpa o display
  Heltec.display->clear();
  //Exibe o estado atual do relê
  Heltec.display->drawString(0, 0, currentState);
  //Exibe o ip deste esp para ser utilizado no aplicativo
  Heltec.display->drawString(0, 15, myIP.toString());
  Heltec.display->display();
}


taskNewClients

// Task que insere novos clientes conectados no vector
void taskNewClients(){
  // Se existir um novo client atribuimos para a variável
  WiFiClient 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()));
  }
}


taskHandleClients

// Função que verifica se o app enviou um comando
void taskHandleClients()
{
  // String que receberá o comando vindo do aplicativo
  String cmd;

  // 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');
      // Verificamos o comando, enviando por parâmetro a String cmd
      handleCommand(cmd);        
    }          
  }
}


refreshConnections

// 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;
}


handleCommand

// Função que verifica o comando vindo do app
void handleCommand(String cmd)
{
  // Se a String estiver vazia não precisamos fazer nada
  if (cmd.equals(""))
    return;

  //Coloca todos os caracteres em maiúsculo
  cmd.toUpperCase();

  // Exibimos o comando recebido no monitor serial
  Serial.println("Received from app: " + cmd);

  //Verifica se a mensagem é para este esp e modifica o estado do relê de acordo com o que foi enviado
  bool forMe = verifyAndSetRelayState(cmd);

  //Se a mensagem é para este esp
  if(forMe) {
    //Envia mensagem de confirmaçao de volta para os aplicativos conectados
    String confirmationMessage = currentState + " OK";
    sendToClients(confirmationMessage);
    Serial.println("Changed Relay status: " + confirmationMessage);
  }
  //Se não é para este esp
  else {
    //Envia o comando para os outros esp através de um pacote LoRa
    sendLoRaPacket(cmd);
  }
}


verifyAndSetRelayState

//Verifica se estado é valido para este esp e modifica o estado do relê de acordo
//Retorna true se a mensagem for para este esp e false caso contrário
bool verifyAndSetRelayState(String state) {
  //Se a mudança de estado pertence ao id vinculado a este esp
  if (state == ID_ON || state == ID_OFF) {
    //Guarda o estado atual
    currentState = state;

    //Modificamos o estado do relê de acordo com o estado enviado
    digitalWrite(RELAY, currentState == ID_ON ? LOW : HIGH);
    
    //Atualizamos o display com o estado atualizado
    refreshDisplay();
    return true;
  }
  return false;
}


sendToClients e sendLoRaPacket

//Função que envia mensagem para todos os apps conectados
void sendToClients(String msg) {
  for(int i=0; i<clients.size(); i++){
    clients[i].print(msg);
  }
}

//Envia um pacote LoRa
void sendLoRaPacket(String str) {
  //Inicializa o pacote
  LoRa.beginPacket();
  //Coloca a string no pacote
  LoRa.print(str);
  //Finaliza e envia o pacote
  LoRa.endPacket();
}


loop

void loop() {
  //Faz a leitura do pacote
  String packet = readLoRaPacket();  

  //Se uma mensagem chegou
  if(!packet.equals("")) {
    //As mensagens enviadas dos outros ESPs são apenas de feedback
    //Então enviamos a mensagem como confirmação para os aplicativos conectados
    sendToClients(packet);
  }

  //Executa as tarefas que foram adicionadas ao scheduler
  scheduler.execute();
}


readLoRaPacket

//Faz a leitura de um pacote (se chegou algum)
String readLoRaPacket() {
    String packet = "";

    //Verifica o tamanho do pacote
    int packetSize = LoRa.parsePacket();

    //Lê cada caractere e concatena na string 
    for (int i = 0; i < packetSize; i++) { 
      packet += (char) LoRa.read(); 
    }




CÓDIGO RECEIVERS

Fluxograma



Imports e Variáveis

#include "heltec.h"

//Frequência
#define BAND 433E6

//Pino onde o relê está
#define RELAY 13

//Id e estados deste esp (altere para cada esp)
String ID = "LIGHT2";
String ID_ON = ID + " ON";
String ID_OFF = ID + " OFF";

//Variável para guardar o valor do estado atual do relê 
String currentState = ID_OFF;



setup

void setup() { 
    //Coloca tudo em maiúsculo
    ID_ON.toUpperCase();
    ID_OFF.toUpperCase();

    //Coloca o pino onde o relê está como saída
    pinMode(RELAY, OUTPUT);
    
    Heltec.begin(true /*Ativa display*/, true /*Ativa LoRa*/, true /*Ativa informações pela serial*/, true /*Ativa PABOOST*/, BAND /*frequência*/);

    //Inicializa o display
    setupDisplay();

    //Ativa o recebimento de pacotes
    LoRa.receive();

    //Coloca o estado do relê como desligado
    verifyAndSetRelayState(ID_OFF);
}


setupDisplay

//Inicializa o display
void setupDisplay() {
    Heltec.display->init();
    //Limpa o display
    Heltec.display->clear();
    //Modifica direcionamento do texto
    Heltec.display->flipScreenVertically();  
    //Alinha o texto à esquerda
    Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT);
    //Altera a fonte
    Heltec.display->setFont(ArialMT_Plain_16);
    //Exibe no display
    Heltec.display->drawString(0, 0, "Display OK");
    Heltec.display->display();
}


verifyAndSetRelayState

//Verifica se estado é valido para este esp e modifica o estado do relê de acordo
//Retorna true se a mensagem for para este esp e false caso contrário
bool verifyAndSetRelayState(String state) {
  //Se a mudança de estado pertence ao id vinculado a este esp
  if (state == ID_ON || state == ID_OFF) {
    //Guarda o estado atual
    currentState = state;

    //Modificamos o estado do relê de acordo com o estado enviado
    digitalWrite(RELAY, currentState == ID_ON ? LOW : HIGH);
    
    //Atualizamos o display com o estado atualizado
    refreshDisplay();
    return true;
  }
  return false;
}


refreshDisplay

//Atualiza dados que aparecem no display
void refreshDisplay() {
    //Limpa o display
    Heltec.display->clear();
    //Mostra o estado atual do relê
    Heltec.display->drawString(0 , 0 , currentState);
    //Mostra os dados
    Heltec.display->display();
}


loop

void loop() {
    //Faz a leitura do pacote
    String packet = readLoRaPacket();  

    //Se uma mensagem chegou
    if(!packet.equals("")) {
        //Verifica se a mensagem é para este esp e modifica o estado do relê de acordo com o que foi enviado
        bool forMe = verifyAndSetRelayState(packet);

        //Se a mensagem é para este esp
        if(forMe) {
            //Envia de volta um pacote de confirmação
            sendLoRaPacket(currentState + " OK");
        } 
    }
}


readLoRaPacket

//Faz a leitura de um pacote (se chegou algum)
String readLoRaPacket() {
    String packet = "";

    //Verifica o tamanho do pacote
    int packetSize = LoRa.parsePacket();

    //Lê cada caractere e concatena na string 
    for (int i = 0; i < packetSize; i++) { 
      packet += (char) LoRa.read(); 
    }

    return packet;
}


sendLoRaPacket

//Envia um pacote LoRa
void sendLoRaPacket(String str) {
    //Inicializa o pacote
    LoRa.beginPacket();
    //Coloca a string no pacote
    LoRa.print(str);
    //Finaliza e envia o pacote
    LoRa.endPacket();
}




APP FERNANDO K
















FAÇA O DOWNLOAD DOS ARQUIVOS


Postar um comentário

4 Comentários

  1. if (!LoRa.begin(frequency,PABOOST))

    professor porque da este erro ao mandar programa para esp lora

    ResponderExcluir
  2. Fernandão faltou dar um return na função readLoRaPacket() . Logo após o código do Loop.
    Abraço!

    ResponderExcluir
  3. For instance, as per the report of AlphaBeta Australia, Australia witnessed a 67% improve in on-line betting in April 2020. Similarly, according to a report by Concordia 점보카지노 University, Great Britain observed a 17.5% rise in internet-based playing. Thus, the shutdown of non-essential services through the pandemic-induced lockdown boosted the adoption of cell playing among users. Thus, through the pandemic crisis, the market witnessed outstanding development price. Moreover, the market is predicted to grow with a major CAGR within the post-pandemic interval.

    ResponderExcluir
  4. Oi Fernado, ao compilar o código na função 'void sendLoRaPacket(String str) {' da erro na linha: LoRa.beginPacket() , que LoRa não foi declarad neste scope. Pode me ajudar??

    ResponderExcluir