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(); }
4 Comentários
if (!LoRa.begin(frequency,PABOOST))
ResponderExcluirprofessor porque da este erro ao mandar programa para esp lora
Fernandão faltou dar um return na função readLoRaPacket() . Logo após o código do Loop.
ResponderExcluirAbraço!
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.
ResponderExcluirOi 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