No post Rede ESP-NOW integrada com Web Server, apresentamos o cenário específico com estações coletoras comunicando com o nó central via o protocolo ESP-NOW e servindo numa rede local como Web Server.
Neste post, apresentaremos um cenário similar utilizando o protocolo LORA (Long Range) para a comunicação entre as estações, mantendo a funcionalidade como Web Server. Utilizaremos o módulo LORA, modelo E32- 433T20D, para transmitir e receber os dados de temperatura, umidade, pressão e altitude coletados através do sensor BME-280.
Utilizaremos o módulo E32-433T20D no seu Modo Transparente, ou seja, com a configuração default que vem de fábrica. Normalmente, na configuração default, o módulo é usado para a comunicação ponto a ponto. Faremos uma prova de conceito como cenário inicial para demonstrar que o Modo Transparente pode ser usado na comunicação multiponto, tornando a implementação mais simples, sem a necessidade de configuração de cada módulo para atender uma rede mais complexa.
Existe um artigo no Blog da Eletrogate, Monitoramento de Temperatura com Heltec ESP32 LoRa, que explora diversos aspectos e cenários da Rede Lora, abordando os conceitos e pré-requisitos para a implementação. Inclusive, o artigo mostra a utilização de uma placa integrada com ESP32 + Lora + Display OLED da empresa Heltec. Portanto, sugerimos fortemente ao leitor dar uma estudada no artigo mencionado.
No nosso exemplo, utilizaremos as placas ESP32 e Arduino NANO bastante conhecidas no mercado com o módulo Lora em separado.
Antes de iniciar a programação, é preciso fazer a instalação das placas da Espressif e das seguintes bibliotecas:
Com exceção da biblioteca ESPAsyncWebServer, você encontra as demais no próprio Gerenciador de Bibliotecas da Arduino IDE, sem contar que algumas, como as relacionadas ao Wi-Fi, são instaladas juntamente com o pacote de placas da Espressif.
Se tem dúvidas na instalação das placas, siga nosso tutorial de instalação.
LoRa é a abreviação para “long range”. Basicamente, essa é uma tecnologia de radiofrequência sem fio de longo alcance e baixa potência, sendo ideal para a adoção de sistemas relacionados à Internet das Coisas (IoT). Ela utiliza uma técnica de modulação de espectro de propagação de chirp (CSS), o que a torna atrativa para aplicações IoT.
Propósito e Aplicações da Rede LoRa:
Resumo:
O E32-433T20D é um módulo de comunicação sem fio da série E32, operando na frequência de 433 MHz, com capacidade de transmissão e recepção, potência de 20 dBm e outras características específicas. Ele é amplamente utilizado em projetos de IoT, automação e redes de sensores. O tamanho do pacote trafegado deve ser até 58 bytes.
Uso da Frequência de 433 Mhz no Brasil:
Embora o Brasil adote oficialmente a frequência de 915 MHz para comunicações sem fio, o uso da faixa de 433 MHz não apresenta problemas significativos para aplicações locais. A Agência Nacional de Telecomunicações (ANATEL) regula o espectro de radiofrequência no país, e embora o 433 MHz não seja uma banda ISM (Industrial, Scientific and Medical), ele é amplamente utilizado em dispositivos de controle remoto industrial e sistemas de automação. Essa popularidade se deve ao fato de que, embora não seja uma faixa licenciada, não há falta de dispositivos operando nessa frequência no Brasil. Portanto, para aplicações locais de baixa potência e alcance limitado, o uso do 433 MHz pode ser uma alternativa viável sem depender de aprovação específica da ANATEL. No entanto, caso você esteja desenvolvendo um produto a ser comercializado, o uso da frequência 915 Mhz é mandatório.
Os pinos M0 e M1 são para a configuração do modo de operação do módulo (4 opções). O Modo Transparente, mencionado anteriormente, considera os pinos M0=M1=aterrados e utiliza somente os pinos RXD, TXD, VCC e GND numa comunicação serial comum e mais simples, usando a biblioteca SoftwareSerial (Arduino NANO) e HardwareSerial (ESP32) sem depender da biblioteca específica do fabricante, que é mais complexa de usar pois foi desenvolvida para atender a todas as funcionalidades do módulo.
O BME-280 é um sensor ambiental integrado desenvolvido especialmente para aplicações móveis. Ele combina sensores de alta linearidade e alta precisão para medição de umidade relativa, pressão barométrica e temperatura ambiente. O tamanho compacto e o baixo consumo de energia tornam o BME-280 ideal para projetos onde esses fatores são essenciais. A tensão de alimentação está na faixa de 1.8 – 5V DC. Normalmente, a versão com comunicação I2C é mais utilizada no mercado.
Propósito e Aplicações do BME-280:
Utilizaremos três estações (1 x ESP32 + 2 x Arduino NANO). Cada estação terá um LED, o módulo E32-433T20D e um PUSH BUTTON. Quando o botão for pressionado, o estado do LED será invertido e a estação transmitirá o comando ON ou OFF (dependendo do novo estado do LED) pela Rede LORA, como se fosse um broadcast, para que as demais estações sincronizem com o mesmo estado do LED da estação emissora.
Componentes usados:
Diagrama do Circuito para o ESP32
Diagrama do circuito para o Arduino NANO
Pontos de máxima atenção sobre o módulo E32-433T20d:
Fizemos um teste de alcance com os circuitos da Prova de Conceito. Distanciamos uma estação da outra por cerca de 2.155 m com visada direta aproveitando um vale que existe na região que moro, conforme pode ser visto na imagem obtida pelo Google Earth a seguir. Infelizmente, não conseguimos avançar até os 3.000 m especificado no datasheet do módulo pois começamos a descer em relação ao topo e com isso a visada passou a não ser direta e a comunicação não foi possível.
Demonstração da Prova de Conceito
A seguir, apresentamos o código usado na Prova de Conceito para o ESP32/NANO:
//------------------------------------------------------------------------------------------- // Função : Este sketch tem como objetivo implementar um nó da Rede Lora para acionar um LED // transmitindo ON ou OFF para os demais participantes da rede seguirem. // O LED é acionado através de um PUSH BUTTON. // // Autor : Dailton Menezes // // Versão : V1.0 Jun/2024 // // Componentes : 1) 1 x módulo Lora E32 433T20D com a configuração default // 2) 1 x LED + 1 x Resistor 330 Ohms // 3) 1 x PUSH BUTTON em PULL-UP // 4) 1 x Protoboard 400 pontos // 5) 1 x Arduino Nano // 6) Jumpers diversos // // Observações : 1) Usaremos um divisor de tensão para reduzir 5V para 3,18V usando um // R1=330 Ohms e R2=470 Ohms => Vsaída = Ventrada*[R2/(R1+R2)] // 2) O TX do NANO será ligado ao RX do Módulo passando pelo divisor pois // pois a tensão da parte lógica do módulo é 3,3V até o limite de 3,6V. // // Referências : BME280 // 1) https://randomnerdtutorials.com/bme280-sensor-arduino-pressure-temperature-humidity/ // 2) https://randomnerdtutorials.com/esp32-bme280-arduino-ide-pressure-temperature-humidity/ // 3) https://microcontrollerslab.com/bme280-esp32-display-values-oled-arduino-ide/ //------------------------------------------------------------------------------------------ #define BAUDRATE 9600 // Baudarate do interface Lora //---------------------------- // Inclusão das bibliotecas //---------------------------- #ifdef ARDUINO_AVR_NANO || ARDUINO_AVR_UNO #include <SoftwareSerial.h> #elif defined(ESP32) #include <HardwareSerial.h> #endif //---------------------------- // Definições dos Pinos Usados //---------------------------- #ifdef ARDUINO_AVR_NANO || ARDUINO_AVR_UNO #define pinRX 2 #define pinTX 3 #define pinBot 4 #define pinLed 5 #elif defined(ESP32) #define pinRX 16 #define pinTX 17 #define pinBot 12 #define pinLed 14 #endif //---------------------------- // Definições de Variáveis //---------------------------- bool estadoLed = false; String comando[2] = {"OFF", "ON"}; //--------------------------- // Objeto para Comunicar //--------------------------- #ifdef ARDUINO_AVR_NANO || ARDUINO_AVR_UNO SoftwareSerial lora(pinRX, pinTX); #endif void acionaLed(bool estado) { estadoLed = estado; digitalWrite(pinLed,estadoLed); } void setup() { // Inicializa da Serial Serial.begin(BAUDRATE); while (!Serial) ; // Inicializa o Módulo Lora #ifdef ARDUINO_AVR_NANO || ARDUINO_AVR_UNO lora.begin(BAUDRATE); #elif ESP32 Serial1.begin(BAUDRATE, SERIAL_8N1, pinRX, pinTX); #endif // Inicializa o Botão pinMode(pinBot, INPUT_PULLUP); // Inicializa o LED pinMode(pinLed, OUTPUT); acionaLed(estadoLed); // Hello na Console Serial.println("Transmissor Lora V1.0 Jun/2024"); Serial.println("Pressione o botão para ligar/desligar o Led e enviar o comando para rede Lora..."); } void loop() { // Verifica se pressionou o botão if ( digitalRead(pinBot) == LOW ) { // Alterna o estado do Led e Aciona o Led acionaLed(!estadoLed); // Envia o comando para a Rede Lora #ifdef ARDUINO_AVR_NANO || ARDUINO_AVR_UNO lora.print(comando[estadoLed]); #elif ESP32 Serial1.print(comando[estadoLed]); #endif Serial.print("Comando transmitido: "); Serial.println(comando[estadoLed]); delay(250); } // Verifica se chegou comando da Rede Lora #ifdef ARDUINO_AVR_NANO || ARDUINO_AVR_UNO if ( lora.available() ) #elif ESP32 if ( Serial1.available() > 0 ) #endif { #ifdef ARDUINO_AVR_NANO || ARDUINO_AVR_UNO String comando = lora.readString(); #elif ESP32 String comando = Serial1.readString(); #endif Serial.print("Comando recebido: "); Serial.println(comando); if (comando.equalsIgnoreCase("OFF")) acionaLed(false); else if (comando.equalsIgnoreCase("ON")) acionaLed(true); else Serial.println("Comando inválido..."); } }
Utilizaremos no nó central/receptor o ESP32 30 pinos e dois Arduinos NANO como satélites/transmissores. Todas as estações serão dotadas de um módulo LORA E32-433T20D e um sensor BME-280. O obtivo é coletar os dados de temperatura, umidade, pressão atmosférica e a altitude aproximada através das três estações. O ESP32 terá a função estendida para desempenhar os papeis de coletor, receptor geral e publicador do conteúdo numa página HTML através da rede local usando o serviço de Web Server na porta 80. Os dois Arduinos NANO apenas coletarão os dados e enviarão pela interface LORA.
Componentes usados:
Teremos dois tipos de comunicação:
Os pacotes a serem trafegados estarão no formato JSON, conforme layout a seguir:
Ex: {“id”:2,”t”:”24″,”u”:”45″,”p”:”923″,”a”:”803″} Onde: id => identificação da estação que enviou |
Observações:
HTML renderizado no Celular
HTML renderizado no Desktop
Circuito do Arduino NANO em Bancada
Diagrama do Circuito do Arduino NANO
Circuito do ESP32 em Bancada
Diagrama do Circuito do ESP32
Demonstração da Rede LORA com Web Server
Código para o Nó Transmissor (Arduino NANO)
//------------------------------------------------------------------------------------------- // Função : Este sketch tem como objetivo implementar um nó da Rede Lora para transmitir // dados de um sensor BME280 (temperatura, umidade, pressão e altitude) para o nó // central ESP32 que publicará os dados recebidos como Web Server na porta 80. O // nó central também coletará dados do mesmo sensor. // // Autor : Dailton Menezes // // Versão : V1.0 Jun/2024 // // Componentes : 1) 1 x Módulo Lora E32 433T20D com a configuração default // 2) 1 x Módulo BME280 // 3) 1 x Arduino Nano ou UNO // 4) 1 x Resistor de 330 Ohms // 5) 1 x Resistor de 470 Ohms // 6) Jumpers diversos // // Observações : 1) Usaremos um divisor de tensão para reduzir 5V para 3,18V usando um // R1=330 Ohms e R2=470 Ohms => Vsaída = Ventrada*[R2/(R1+R2)] // 2) O TX do NANO será ligado ao RX do Módulo passando pelo divisor pois // pois a tensão da parte lógica do módulo é 3,3V até o limite de 3,6V. // 3) O pacote a transmitir será no formato JSON, bem otimizado por causa // restrição do tamanho do pacote do módulo E32-433T20D Lora que permite // 58 bytes. // Ex: {"id":2,"t":"24","u":"51","p":"925","a":"786"} // Onde id => identificação da estação que enviou // t => temperatura em *C // u => umidade em % // p => pressão atmomosférica em hPa // a => altitude em m // 4) O módulo BME280 usado neste projeto usa o endereço 0x76 da interface I2C // // Referências : 1) https://randomnerdtutorials.com/bme280-sensor-arduino-pressure-temperature-humidity/ // 2) https://www.youtube.com/watch?v=fhfKB7Tzm4c //------------------------------------------------------------------------------------------ #define BAUDRATE_LORA 9600 // Baudarate do interface Lora #define BAUDRATE_SERIAL 115200 // Baudarate do interface Lora #define VARREDURA 5000 // Intervalo de varredura em msec #define BME280_ADDR 0x76 // Endereço I2C do BME280 (depende do módulo adquirido) #define ALARME 500 // Tempo para piscar Led como alarme #define TAXA_ERRO_ALT 2.75F // Taxa de erro da Altitude 2,75% #define SEALEVELPRESSURE_HPA (1013.25) // Valor de referência para o nível do mar #define ESTACAO_ID 2 // <<Atenção>> : Informa o N. da Estação de Coleta. // Deve ser diferente para cada nó da rede //------------------------------ // Inclusão das bibliotecas //------------------------------ #include <Wire.h> // Biblioteca de comunicação I2C #include <Adafruit_Sensor.h> // Biblioteca pré-requisito para o BME280 #include <Adafruit_BME280.h> // Biblioteca para tratar o sensor BME280 #include <SoftwareSerial.h> // Biblioteca paara a comunicação serial #include <ArduinoJson.h> // Biblioteca JSON para comunicação e parãmetros //------------------------------ // Definições dos Pinos Usados //------------------------------ #define pinRX 2 // Pino RX para a comunicação serial #define pinTX 3 // Pino TX para a comunicação serial //------------------------------- // Objeto para Comunicação Serial //------------------------------- SoftwareSerial lora(pinRX, pinTX); // Para a comunicação serial //------------------------------- // Objeto para tratar o BME280 //------------------------------- Adafruit_BME280 bme; // I2C //------------------------------- // Variáveis utilizadas //------------------------------- unsigned long ultimaVarredura=0; // ùltima coleta de dados //------------------------------- // Prototipação de Rotinas //------------------------------- void coleta_imprime_envia(); // Rotina para coletar os dados e transmitir String formataValor(float valor, int size, int dec); // Rotina para formatar um número //-------------------------------- // Inicialização Geral do Programa //-------------------------------- void setup() { // Inicializa da Serial Serial.begin(BAUDRATE_SERIAL); while (!Serial) ; // Inicializa o Led Interno pinMode(LED_BUILTIN, OUTPUT); // Inicializa o Módulo Lora lora.begin(BAUDRATE_LORA); // Inicializa o BME280. Atenção: este BMW280 veio configurado na porta x76. // É importante saber qual o endereço do seu módulo para alterar na linha seguinte if (!bme.begin(BME280_ADDR, &Wire)) { Serial.println("Módulo BME280 não encontrado. Verifique o endereço ou a ligação das portas"); while (1) { // Pisca o LED Interno para alertar para o erro digitalWrite(LED_BUILTIN, HIGH); delay(ALARME); digitalWrite(LED_BUILTIN, LOW); delay(ALARME); } } // Hello na Console Serial.print("Transmissor Lora V1.0 Jun/2024 - Estação "); Serial.println(ESTACAO_ID); Serial.println(); } //-------------------------------- // Loop para coletar e enviar //-------------------------------- void loop() { // Verifica se chegou dados da Rede Lora if ( lora.available() ) { // Vamos simplesmente ler e imprimir pois não trataremos os dados // que chegaram pois somos apenas transmissores. String dados = lora.readString(); Serial.print("Dados recebidos: "); Serial.println(dados); Serial.println(); } // Verifica se deve fazer a coleta de dados if (millis()-ultimaVarredura > VARREDURA) { coleta_imprime_envia(); } } //-------------------------------- // Rotina para ler os dados do // BME280 e enviar pela interface // Lora //-------------------------------- void coleta_imprime_envia() { JsonDocument json; // Para conter os dados a enviar float temp; // Temperatura lida float umid; // Umidade lida float pressure; // Pressão atmosférica lida float altitude; // Altitude aproximada inferida através da pressão // Define a estação que enviará os dados json["id"] = ESTACAO_ID; // Trata a Temperatura sem casas decimais temp = bme.readTemperature(); Serial.print("Temperature = "); Serial.print(temp,0); Serial.println(" *C"); json["t"] = formataValor(temp,3,0); // Trata a Umidade sem casas decimais umid = bme.readHumidity(); Serial.print("Humidity = "); Serial.print(umid,0); Serial.println(" %"); json["u"] = formataValor(umid,3,0); // Trata a Pressão sem casas decimais pressure = bme.readPressure()/100.0F; Serial.print("Pressure = "); Serial.print(pressure,0); Serial.println(" hPa"); json["p"] = formataValor(pressure,5,0); // Trata a Pressão sem casas decimais altitude = bme.readAltitude(SEALEVELPRESSURE_HPA)/(1-TAXA_ERRO_ALT/100); Serial.print("Approx. Altitude = "); Serial.print(altitude,0); Serial.println(" m"); json["a"] = formataValor(altitude,5,0); // Dá uma linha de espaço Serial.println(); // Serializando o JSON sem formatação por economia por causa // do limite de 58 bytes da rede LORA String jsonStr; serializeJson(json, jsonStr); // Enviando o JSON para console Serial.print("Transmitindo: "); Serial.println(jsonStr); Serial.print("Tamanho: "); Serial.println(jsonStr.length()); // Dá uma linha de espaço Serial.println(); // Envia pela Interface LORA lora.print(jsonStr); // Atualiza a última varredura ultimaVarredura = millis(); } //-------------------------------- // Rotina para formatar um número //-------------------------------- String formataValor(float valor, int tam, int dec) { char buf[tam+2]; // Buffer par aformatação String result; // Resultado depois da formatação dtostrf(valor, tam, dec, buf); result = String(buf); result.trim(); return result; }
Código para o Nó Receptor (ESP32)
//------------------------------------------------------------------------------------------- // Função : Este sketch tem como objetivo implementar um nó central da Rede Lora para receber // dados enviados pelas estações transmissoras contendo (temperatura, umidade, // pressão e altitude) obtidos através de um sensor BME280. As informações das // estações serão mostradas num html via Web Server escutando na porta 80. // // Autor : Dailton Menezes // // Versão : V1.0 Jun/2024 // // Componentes : 1) 1 x Módulo Lora E32 433T20D com a configuração default // 2) 1 x Módulo BME280 // 3) 1 x ESP32 // 4) Jumpers diversos // // Observações : 1) O pacote a receber virá no formato JSON, bem otimizado por causa // restrição do tamanho do pacote do módulo E32-433T20D Lora que permite // 58 bytes. // Ex: {"id":2,"t":"24","u":"51","p":"925","a":"786"} // Onde id => identificação da estação que enviou // t => temperatura em *C // u => umidade em % // p => pressão atmomosférica em hPa // a => altitude em m // 2) Este nó central também coletará dados do BME280 mas só enviará para // a página html. // // Referências : 1) https://randomnerdtutorials.com/esp32-bme280-arduino-ide-pressure-temperature-humidity/ // 2) https://randomnerdtutorials.com/esp32-web-server-with-bme280-mini-weather-station/ //------------------------------------------------------------------------------------------ #define LED_BUILTIN 2 // Pino para o Led Interno do ESP32/ESP8266 #define BAUDRATE_LORA 9600 // Baudarate do interface Lora #define BAUDRATE_SERIAL 115200 // Baudarate do interface Lora #define VARREDURA 5000 // Intervalo de varredura em msec #define BME280_ADDR 0x76 // Endereço I2C do BME280 (depende do módulo adquirido) #define ALARME 500 // Tempo para piscar Led como alarme #define TAXA_ERRO_ALT 2.75F // Taxa de erro da Altitude 2,75% #define SEALEVELPRESSURE_HPA (1013.25) // Valor de referência para o nível do mar #define ESTACAO_ID 0 // <<Atenção>> : Informa o N. da Estação de Coleta. // O nó cenytral será o de n. zero. #define NOME_ESTACAO "LORAWEB" // Nome desta Estação para efeito de DNS //------------------------------ // Inclusão das bibliotecas //------------------------------ #include <Wire.h> // Biblioteca de comunicação I2C #include <Adafruit_Sensor.h> // Biblioteca pré-requisito para o BME280 #include <Adafruit_BME280.h> // Biblioteca para tratar o sensor BME280 #include <HardwareSerial.h> // Biblioteca paara a comunicação serial #include <ArduinoJson.h> // Biblioteca JSON para comunicação e parãmetros #include <ESPAsyncWebServer.h> // Biblioteca Asynch Web Server #include <ESPmDNS.h> // Biblioteca para inclusão do hostname no mDNS //------------------------------ // Definições dos Pinos Usados //------------------------------ #define pinRX 16 // Pino RX para a comunicação serial #define pinTX 17 // Pino TX para a comunicação serial //------------------------------------------------------ // Ajuste as linhas abaixo para a sua credencial de rede //------------------------------------------------------ const char* ssid = "<não se esqueça de informar o SSID de sua Rede Wi-Fi>"; const char* password = "<não se esqueça de informar s SENHA de sua Rede Wi-Fi>"; //------------------------------- // Objeto para tratar o BME280 //------------------------------- Adafruit_BME280 bme; // I2C //------------------------------- // Variáveis utilizadas //------------------------------- unsigned long ultimaVarredura=0; // ùltima coleta de dados //------------------------------------ // Define o HTML para Página Principal //------------------------------------ const char index_html[] PROGMEM = R"rawliteral( <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>LORA Data</title> <style> header { background-color: #333; color: #fff; text-align: center; padding: 1px; font-size: 16px;} // body { // font-family: Arial, sans-serif; // background-color: #f0f0f0; // margin: 0; // padding: 0; // display: flex; // justify-content: center; // align-items: center; // height: 100vh; // } .container { width: 100%; max-width: 600px; margin: 20px; } .panel { background-color: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); margin-bottom: 10px; padding: 10px; } .panel .header { font-size: 14px; // font-weight: bold; color: #888; margin-bottom: 10px; } .panel .content { font-size: 24px; margin-bottom: 10px; } .panel .timestamp { font-size: 14px; color: #888; } .blue-light { color: lightblue; } .blue-dark { color: darkblue; } .green { color: green; } .red-light { color: lightcoral; } .red-dark { color: darkred; } .orange { color: orange; } .yellow { color: yellow; } </style> </head> <body> <header> <h2>Receptor LORA Web</h2> </header> <div id="transmitter-data"></div> <script> // Função para determinar a cor da Temperatura function getTemperatureClass(temp) { if (temp < 10) return 'blue-light'; if (temp < 20) return 'blue-dark'; if (temp < 25) return 'green'; if (temp < 30) return 'red-light'; return 'red-dark'; } // Função para determinar a cor da Umidade function getHumidityClass(humid) { if (humid <= 15) return 'red-dark'; if (humid <= 30) return 'orange'; if (humid <= 55) return 'blue-light'; return 'green'; } // Função para determinar a cor da Pressão/Altitude function getPressureClass(pressure) { if (pressure > 1013) return 'red-dark'; if (pressure > 800) return 'green'; return 'blue-dark'; } if (!!window.EventSource) { // Lista para manter controle dos elementos por ID var transmitterElements = {}; // Evento para receber os dados var source = new EventSource('/events'); source.addEventListener('open', function(e) { console.log("Events Connected"); }, false); source.addEventListener('error', function(e) { if (e.target.readyState != EventSource.OPEN) { console.log("Events Disconnected"); } }, false); source.addEventListener('message', function(e) { console.log("message", e.data); }, false); source.addEventListener('new_readings', function(e) { console.log("new_readings", e.data); // Obtenha os dados do evento var eventData = JSON.parse(e.data); var idEstacao = eventData.id; var temp = eventData.t; var umid = eventData.u; var pressure = eventData.p; var altitude = eventData.a; var timestamp = new Date().toLocaleString(); // Obtenha as cores para a temperatura e umidade var tempClass = getTemperatureClass(temp); var humidClass = getHumidityClass(umid); var pressureClass = getPressureClass(pressure); // Verifique se o elemento para este ID já existe na lista if (transmitterElements[idEstacao]) { // Atualizar o conteúdo do painel existente var contentElement = transmitterElements[idEstacao].querySelector('.content'); var timestampElement = transmitterElements[idEstacao].querySelector('.timestamp'); contentElement.innerHTML = `Temp: <span class="${tempClass}">${temp}°C</span>, Umid: <span class="${humidClass}">${umid}%</span>, Press: <span class="${pressureClass}">${pressure} hPa</span>, Alt≈ <span class="${pressureClass}">${altitude} m</span>`; timestampElement.innerHTML = `Timestamp: ${timestamp}`; } else { // Caso contrário, crie um novo elemento e adicione-o na lista var newTransmitterDataElement = document.createElement("div"); newTransmitterDataElement.classList.add("panel"); newTransmitterDataElement.id = idEstacao; newTransmitterDataElement.innerHTML = ` <div class="header">ID: ${idEstacao}</div> <div class="content">Temp: <span class="${tempClass}">${temp}°C</span>, Umid: <span class="${humidClass}">${umid}%</span>, Press: <span class="${pressureClass}">${pressure} hPa</span>, Alt≈ <span class="${pressureClass}">${altitude} m</span></div> <div class="timestamp">Timestamp: ${timestamp}</div> `; // Adiciona o novo painel na página document.getElementById("transmitter-data").appendChild(newTransmitterDataElement); // Adiciona a refer~encia do painel na lista transmitterElements[idEstacao] = newTransmitterDataElement; } }, false); } </script> </body> </html> )rawliteral"; //--------------------------------------------- // Variáveis para controle do Server http //--------------------------------------------- AsyncWebServer server(80); AsyncEventSource events("/events"); //------------------------------- // Prototipação de Rotinas //------------------------------- void coleta_imprime_envia(); // Rotina para coletar os dados e transmitir String formataValor(float valor, int size, int dec); // Rotina para formatar um número //-------------------------------- // Inicialização Geral do Programa //-------------------------------- void setup() { // Inicializa da Serial Serial.begin(BAUDRATE_SERIAL); while (!Serial) ; // Inicializa o Led Interno pinMode(LED_BUILTIN, OUTPUT); // Inicializa o Módulo Lora Serial1.begin(BAUDRATE_LORA, SERIAL_8N1, pinRX, pinTX); // Hello na Console Serial.print("Transmissor Lora V1.0 Jun/2024 - Estação "); Serial.println(ESTACAO_ID); Serial.println(); // Inicializa o BME280. Atenção: este BMW280 veio configurado na porta x76. // É importante saber qual o endereço do seu módulo para alterar na linha seguinte if (!bme.begin(BME280_ADDR, &Wire)) { Serial.println("Módulo BME280 não encontrado. Verifique o endereço ou a ligação das portas"); while (1) { // Pisca o LED Interno para alertar para o erro digitalWrite(LED_BUILTIN, HIGH); delay(ALARME); digitalWrite(LED_BUILTIN, LOW); delay(ALARME); } } // Inicializa a estação Wi-Fi WiFi.begin(ssid, password); Serial.print("Configurando o Modo Estação Wi-Fi"); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.print("."); } Serial.println("\n"); // Mostra o IP adquirido da rede Wi-Fi Serial.print("Endereço IP : "); Serial.println(WiFi.localIP()); // Mostra o MAC do Esp32 Serial.print("Endereço MAC: "); Serial.println(WiFi.macAddress()); // Mostra o canal associado pela rede Wi-Fi. Este mesmo canal // deverá ser utilizado pelo Transmissor Serial.print("Canal Wi-Fi : "); Serial.println(WiFi.channel()); Serial.println(); // Define o HostName para o servidor web para facilitar o acesso na rede local // sem conhecer o IP previamente WiFi.setHostname(String(NOME_ESTACAO).c_str()); Serial.print("Adicionando " + String(NOME_ESTACAO) + " no MDNS... "); if (MDNS.begin(String(NOME_ESTACAO).c_str())) { Serial.println("adicionado corretamente no MDNS!"); Serial.println("Use http://" + String(NOME_ESTACAO) + ".local no seu navegador..."); Serial.println("Ou opcionalmente..."); } else { Serial.println("Erro ao adicionar no MDNS!"); } Serial.println("Use http://" + WiFi.localIP().toString() + " no seu navegador...\n"); MDNS.addService("http", "tcp", 80); // Configurar rota para a página HTML server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", index_html); }); // Configura onConnect dos Eventos events.onConnect([](AsyncEventSourceClient *client) { if(client->lastId()) { Serial.printf("Cliente reconectado! Último ID obtido: %u\n", client->lastId()); } }); server.addHandler(&events); // Iniciar o servidor Web server.begin(); } //-------------------------------- // Loop para coletar e enviar //-------------------------------- void loop() { // Verifica se chegou dados da Rede Lora if ( Serial1.available() ) { // Liga o LED Interno na recepção digitalWrite(LED_BUILTIN, HIGH); // Vamos simplesmente ler e imprimir pois não trataremos os dados // que chegaram pois somos apenas transmissores. String dados = Serial1.readString(); Serial.print("Dados recebidos: "); Serial.println(dados); // Envia via Web Socket para o Navegador se for um JSON válido if (dados.startsWith("{\"id\":") && dados.endsWith("}")) { events.send(dados.c_str(), "new_readings", millis()); Serial.println("Enviado para o html..."); } else Serial.println("Ignorado não é o JSON esperado..."); Serial.println(); // Desliga o LED Interno na recepção digitalWrite(LED_BUILTIN, LOW); } // Verifica se deve fazer a coleta de dados if (millis()-ultimaVarredura > VARREDURA) { coleta_imprime_envia(); } } //-------------------------------- // Rotina para ler os dados do // BME280 e enviar pela interface // Lora //-------------------------------- void coleta_imprime_envia() { JsonDocument json; // Para conter os dados a enviar float temp; // Temperatura lida float umid; // Umidade lida float pressure; // Pressão atmosférica lida float altitude; // Altitude aproximada inferida através da pressão // Define a estação que enviará os dados json["id"] = ESTACAO_ID; // Trata a Temperatura sem casas decimais temp = bme.readTemperature(); Serial.print("Temperature = "); Serial.print(temp,0); Serial.println(" *C"); json["t"] = formataValor(temp,3,0); // Trata a Umidade sem casas decimais umid = bme.readHumidity(); Serial.print("Humidity = "); Serial.print(umid,0); Serial.println(" %"); json["u"] = formataValor(umid,3,0); // Trata a Pressão sem casas decimais pressure = bme.readPressure()/100.0F; Serial.print("Pressure = "); Serial.print(pressure,0); Serial.println(" hPa"); json["p"] = formataValor(pressure,5,0); // Trata a Pressão sem casas decimais altitude = bme.readAltitude(SEALEVELPRESSURE_HPA)/(1-TAXA_ERRO_ALT/100); Serial.print("Approx. Altitude = "); Serial.print(altitude,0); Serial.println(" m"); json["a"] = formataValor(altitude,5,0); // Dá uma linha de espaço Serial.println(); // Serializando o JSON sem formatação por economia por causa // do limite de 58 bytes da rede LORA String jsonStr; serializeJson(json, jsonStr); // Enviando o JSON para console Serial.print("Transmitindo: "); Serial.println(jsonStr); Serial.print("Tamanho: "); Serial.println(jsonStr.length()); // Dá uma linha de espaço Serial.println(); // Envia para o HTML events.send(jsonStr.c_str(), "new_readings", millis()); // Atualiza a última varredura ultimaVarredura = millis(); } //-------------------------------- // Rotina para formatar um número //-------------------------------- String formataValor(float valor, int tam, int dec) { char buf[tam+2]; // Buffer par aformatação String result; // Resultado depois da formatação dtostrf(valor, tam, dec, buf); result = String(buf); result.trim(); return result; }
A Rede LORA é um excelente recurso para implementação de diversos cenários de comunicação entre microcontroladores, possuindo um alcance bastante significativo (na ordem de km dependendo do módulo escolhido), independência de Internet ativa e simplicidade na implementação. O máximo tamanho do pacote é 58 bytes, o que pode restringir a comunicação, mas tal limitação pode ser flexibilizada para uma gama variada de aplicações possíveis. Adicionalmente, recursos como Async Web Server pode enriquecer a usabilidade das soluções. Até a próxima!
|
|
A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.
Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!