CPD é um termo em inglês antigo e quase fora de uso, que significa Departamento de Processamento de Dados Central. Na prática, esse termo se refere a uma sala onde os servidores de uma organização ficam instalados e em operação. Geralmente possuem pouco acesso de pessoas e com esquema de segurança no acesso. Possuem um sistema de refrigeração ou condicionamento que às vezes pode falhar, elevando a temperatura e podendo gerar risco para os processadores dos servidores e/ou gerar interrupção nos sistemas críticos da organização. Daí surgiu a ideia para desenvolvimento deste sistema de monitoramento, que é teórico, mas que poderia ser ampliado para virar um produto e ser usado no mundo real. De qualquer forma, ele tem o objetivo de servir como base para projetos mais abrangentes.
1x Módulo WiFi ESP32 Bluetooth 30 pinos
1x Sensor de Umidade e Temperatura DHT22 / Am2302
1x Led RGB Alto Brilho 5mm – Catodo Comum
1x Sensor Fotoresistor LDR de 5mm
2x Protoboard 400 Pontos
1x Display LCD 16×2 com Backlight Azul e I2C
1x Buzzer Ativo 3v
1x Sensor de Chama / Fogo – 3 Pinos
1x Sensor de Gás MQ-2 Inflamável e Fumaça
1x Resistor 10K 1/4W (10 Unidades)
1x Resistor 33K 1/4W (10 Unidades)
1x Resistor 22K 1/4W (10 Unidades)
1x Resistor 4K7 1/4W (10 Unidades)
1x Resistor 220R 1/4W (10 Unidades)
1x Jumpers – Macho/Femea – 40 Unidades de 10cm
1x Jumpers – Macho/Macho – 40 Unidades de 10cm
ESPAsyncWebServer
AsyncTCP
ESP32Ping
WiFiManager
DHT, FS, SPIFFS, Time, ArduinoJson, LiquidCrystal_I2C (Instalados através do Gerenciador de Bibliotecas)
Os objetivos específicos deste projeto são:
//---------------------------------------------------------------------------------------- // Componentes : 1) Placa ESP32 // 2) DHT 22 + 1x Resistor de 4.7k // 3) LED RGB + 3x Resistores 220 Ohms // 4) LDR + 1x resistor de 10k // 5) Jumpers diversos // 6) 2x protoboards 400 adaptadas // 7) 1x Display LCD 16x2 com I2C // 8) 1x Buzzer Ativo 3V // 9) 1x Sensor de Chama Digital // 10) 1x Sensor de Gás MQ-2 + 1x Resistor 33K + 1x Resistor 22K // 11) 1x Sensor de Presença PIR // // Bibliotecas : ESPAsyncWebServer => https://github.com/me-no-dev/ESPAsyncWebServer // AsyncTCP => https://github.com/me-no-dev/AsyncTCP // ESP32Ping => https://github.com/marian-craciunescu/ESP32Ping // WiFiManager => https://github.com/tzapu/WiFiManager // DHT, FS, SPIFFS, // Time, ArduinoJson, // LiquidCrystal_I2C, => Instalado através do Gerenciador de Bibliotecas // // Objetivos : 1) Usar o SPIFFS (sistema de arquivo na FLASH do ESP32) para pegar o SSID/Senha/CPDID/Intervalo do Timer e // persistir. Como HTTP Server, os seguintes modos foram implementados: // // . modo AP usado para configurar o SSID e senha da rede a ser usada, CPDID e o Intervalo na porta 8080. Este modo é // acionado pressionando-se o PUSH BUTTON interno do ESP32 (BOOT) durante a operação (não o botão RESET) // => quando neste modo, o operador deve procurar uma rede com ssid CPD_AP_nnnnn através de um celular ou // computador e deve se conectar usando a senha "password". Depois de conectado, o operador deve acessar // o endereço http://192.168.4.1:8080 através do navegador, uma página será mostrada e clicar o botão Config Wifi, // depois escolher o ssid, informar a senha e definir os parâmetros cpdID (até 50 caracteres) e // intervaloTimer (deve ser maior que 60 seg até 4 dígitos). Por último pressionar o botão SAVE na // interface. Os parâmetros serão salvos na FLASH do ESP32 para não perde quando desligado. A partir daí, // o modo AP é encerrado e o modo normal é acionado. // // . modo Normal é o estado conectado e respondendo aos requests na porta 80 e o monitoramento a cada intervalo // do timer definido e esperando 6 blocos de funcões. A partir deste ponto, o usuário pode acessar via // navegador no IP, mostrado no monitor serial no startup, na porta 81, ou usar programas como FING para // celular que permitem identificar o IP na rede. O ESP32 aparece com a identificação "Espressif" em tais // programas. Outra alternativa, caso o usuário tenha um servidor DNS próprio em sua rede, bastaria criar uma // entrada no DNS usando o CPDID e o IP, que pode ser uma tarefa mais complexa para a maioria dos usuários. // O app FING é uma boa alternativa para descobrir o IP pela rede. A seguir, alguns exemplos já conhecendo // o IP do ESP32: // // Ex1: http://10.0.0.100 // Ex2: http://10.0.0.100/sensores // Ex3: http://10.0.0.100/info // Ex4: http://10.0.0.100/acessos // // 2) Programar o TIMER0 de HW para cada 1 min (ou o valor definido no modo AP) para monitorar a Temperatura, Umidade, // Luminosidade e checar o ping para saber se a internet está disponível e não apenas conectada no AP. // // . o DHT22 informa a Temperatura e umidade // . o LDR informa a luminosidade => como usa porta analógia, deve ser ligado // numa porta ADC1 para uso concomitante com o Wifi no ESP32: 32, 33, 34, 35, // 36, 37, 38 ou 39. // . LED RGB para mostrar a cor correspondente da temperatura como alternativa à visualização do Display. // Isso pode facilitar a visualização de longe, sem a necessidade de entrar na sala do CPD // para ver o Display. Naturalmente, deve existir uma janela ou porta com a possibilidade de se ver // de fora da sala. // . Atualizar o LEDBUILTIN (LED AZUL da placa ligado ou apagado) caso não haja internet disponível. // // 3) Esperar conexão http na porta 80 respondendo as URI's a seguir: // // / => devolve no formato html os valores dos indicadores numa página AUTOREFRESH (a cada intervalo) // /sensores => devolve os indicadores no formato JSON (Local, Temp, Umid, Lumin, TimeStamp) // /info => devolve as informações do server no formato JSON (Local, IP, MAC, StartupTime) // // 4) O LED BUILTIN ficará acesso caso haja a conexão wifi e apagado quando a conexão acabar. // Caso uma desconexão aconteça, uma reconexão será tentada posteriormente. // // 5) Acender uma alerta no LED RGB com a cor relativa ao valor da temperatura // // Temp < 10 => azul claro => gelado // 10 <= Temp < 20 => azul escuro => frio // 20 <= Temp < 25 => verde => normal // 25 <= Temp < 30 => vermelho fraco => quente // Temp >=30 => vermelho forte => muito quente // // 6) Mostrar a Temperatura em °C, (U)midade em % e (L)uminosidade em %, Data/Hora da última varredura num // Display LCD 16x2. // // 7) Programar o TIMER1 de HW para cada 1 min (ou o valor definido no modo AP) para implementação de WatchDog // caso haja travamento no loop principal. Isso causa o reset do ESP32 para evitar paralisação do // monitoramento até que alguma ação corretiva presencial seja feita. // // 8) Monitorar os sensores de Fogo, Gás/Fumaça e de Presença. É considerado emergência quando FOGO ou GÁS é // detectado e um sinal sonoro é emitido enquanto os sensores informam o sinal positvo para o evento. // O sensor de presença é usado para registrar/persistir uma lista com as Top 10 ocorrências de detecção. Um // intervalo de 5 min é considerado para aceitar uma nova detecção de presença. // // Sugestões : A seguir, relacionamos algumas possibilidades de extensão de funcionalidades que os MAKERS podem // implementar a partir das ideias básicas deste projeto, lembrando que o céu é o limite: // // 1) Mandar e-mail para destinatários chave em caso de emergência (temperatura muito alta, fogo ou gás). // 2) Acionar diferentes Relés para atuar em caso de emergência: acionar uma sirene ou um sistema de combate // a incêndio, ou um sistema de refrigeração/condicionamento de emergência, etc. // 3) Como este projeto foi imaginado em essência para acesso numa rede local, sua funcionalidade pode ser estendida // para usar redes mundiais/globais da Arduino, RainMaker ou Blynk para permitir integração com celulares, etc. // 4) Da forma que este projeto foi implementado, como SERVIDOR HTTP, mais funcionalidades podem ser adicionadas // na página principal. Ex: adição de botões para acionar Relés e atuar na situação de emergência. // 5) Outra ideia seria, em caso caso de múltiplos sistemas iguais a este em operação em vários CPD's, // uma aplicação centralizada poderia ser desenvolvida para fazer polling nos diversos sistemas via requisição REST/JSON // e fazer persistência em banco de dados para permitir uma visão histórica dos parâmetros monitorados. // Por isso implementamos as requisições REST/JSON "/sensores", "/info" e "/acessos". // 6) Com uma integração com FIREWALL especialidado (Linux ou outro), este projeto poderia ser usado com NAT // (NetWork Address Translation) para permitir acesso pela Internet em qualquer lugar do mundo sem mudar nada // no código. Neste caso, alguma autenticação deveria ser implementada para aumentar a segurança. // 7) Registrar automaticamente o IP obtido no ESP32 num servidor DNS da rede para o usuário não ter a necessidade de descobrir // o IP usando a biblioteca ESP-NETIF. // 8) Monitorar os sensores de Fogo, Gás/Fumaça e de Presença. É considerado emergência quando FOGO ou GÁS é // detectado e um sinal sonoro é emitido enquanto os sensores informam o sinal positivo para o evento. // O sensor de presença é usado para registrar/persistir uma lista com as Top 10 ocorrências de detecção. // Um intervalo de 5 min é considerado para aceitar uma nova detecção de presença. Isso para não registrar // ocorrências muito próximas, já que apenas 10 detecções são armazenadas. Uma atenção especial deve ser feita // para o sensor de gás/fumaça pois a tensão de saída é de 5V. Foi necessário fazer a redução de tensão para não // comprometer a porta do ESP32 que opera em 3,3V. Dois resistores de 22k e 33k foram usados. Quanto ao sensor de // presença, apesar de ser alimentado com 5V, a tensão de saída é menor do que o limite de 3,3V da porta do ESP32, // sem a necessidade de preocupação. // // Autor : Alberto Menezes // Dailton Menezes // // Referências : 1) https://www.youtube.com/watch?v=VnfX9YJbaU8 // 2) https://www.youtube.com/watch?v=373k6-KwOEE // 3) https://docs.espressif.com/projects/arduino-esp32/en/latest/api/timer.html // 4) https://randomnerdtutorials.com/esp32-async-web-server-espasyncwebserver-library/ // // Versão : 1.0 Ago/2022 //---------------------------------------------------------------------------------------- #include <Arduino.h> // Biblioteca Arduino #include <WiFi.h> // Biblioteca WiFi #include <AsyncTCP.h> // Biblioteca AsyncTCP usado pelo Web #include <ESP32Ping.h> // Biblioteca Ping #include <FS.h> // Biblioteca FileSystem #include <SPIFFS.h> // Biblioteca SPIFFS #include <WiFiManager.h> // Biblioteca WiFi Manager #include <ESPAsyncWebServer.h> // Biblioteca Asynch Web Server #include <DHT.h> // Biblioteca DHT #include <DHT_U.h> // Biblioteca DHT complemenbto #include <time.h> // Biblioteca Time para manipulação de data/hora #include <ArduinoJson.h> // Biblioteca JSON para comunicação e parãmetros #include <LiquidCrystal_I2C.h> // Biblioteca do Display LCD #define MAX_TRILHA 10 // As 10 últimas presenças serão memorizadas #define PIR_MIN_TIME 5*60 // Intervalo em seg. para aceitar nova detecção de presença 5 min = 300seg. #define ESP_DRD_USE_SPIFFS true // Uso com SPIFFS #define JSON_CONFIG_FILE "/cpd_config.json" // Arquivo JSON de configuração #define ESP_getChipId() ((uint32_t)ESP.getEfuseMac() // Simular ID da placa ESP #define alarmeBreve 20 // Define 20 mseg para alarme breve #define alarmeLeve 200 // Define 200 mseg para alarme leve #define alarmeFogo 500 // Define 500 mseg para alarme de fogo #define alarmeGas 1000 // Define 1000 mseg para alarme de Gás #define cleanUpCycle 5000 // 5000 millisec para CleanUp ou 5 sec (sockect's) #define pinLDR 33 // Pino do LDR #define pinBuzzer 27 // Pino do Buzzer #define pinGas 25 // Pino do Sensor de Gás #define pinDHT 23 // Pino do DHT22 #define RedPin 19 // Pino RED do LED RGB #define GreenPin 18 // Pino GREEN do Led RGB #define pinFogo 15 // Pino do Sensor de Fogo #define pinPIR 26 // Pino do Sensor de Presença #define BluePin 5 // Pino BLUE do LED RGB #define LED_BUILTIN 2 // Pino para o Led Interno do ESP32 #define TRIGGER_PIN 0 // Pino do botão para forçar a entrada no modo de configuração do WiFi #define LCDSDAPin 21 // Pino SDA do Display LCD #define LCDSCLPin 22 // Pino SCL do Dispaly LCD #define DHTTYPE DHT22 // Modelo do DHT a ser usado #define PWM_R_ledChannel 0 // Canal do LED RGB Red #define PWM_G_ledChannel 1 // Canal do LED RGB Green #define PWM_B_ledChannel 2 // Canal do LED RGB Blue // Setting LED PWM #define PWM_freq 5000 // Frequência para a trativa do LED RGB na porta PVM #define PWM_resolution 8 // Resolução da porta PVM #define Temp_Gelado 0 // Indicador de Temperatura gelada #define Temp_Frio 1 // Indicador de Temperatura fria #define Temp_Normal 2 // Indicador de Temperatura normal #define Temp_Quente 3 // Indicador de Temperatura quente #define Temp_Fervendo 4 // Indicador de Temperatura elevada #define Time_Slice 60000000 // Default intervalo do timer 1 min #define timeoutWifi 15*1000 // Default tempo de tentativa de reconexão WiFi 15 sec // Definições para sensores de gás e fogo #define GAS LOW // Estado do Sensor quando há gás detectado #define FOGO LOW // Estado do Sensor quando há fogo detectado #define BUZZER_OFF LOW // Nível do Buzzer ativo #define BUZZER_ON HIGH // Nível do Buzzer ativo DHT dht(pinDHT, DHTTYPE); // Tratativa da Temperatura e Umidade // Alertas visuais de cores do LED para a temperatura int Alerta[][3] = { {0, 191, 255}, // Azul Claro T < 10 graus {0, 0, 255}, // Azul entre 10 <= T < 20 {0, 128, 0}, // Verde entre 20 <= T < 25 {250, 128, 114}, // Vermelho Claro entre 25 <= T < 30 {255, 0, 0} // Vermelho T >= 30 }; volatile int interruptCounter = 0; // Variáveis para sincronizar a interrupção de HW para o Timer hw_timer_t * timerSensores = NULL; // Timer para a varredura dos sensores hw_timer_t * timerWatchDog = NULL; // Timer para a implementação de Watchdog para reset do ESP32 em caso de travamento portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; // semáforo para sincronismo com o loop principal AsyncWebServer sv(80); // Servidor http na porta 80 (WifiManager rodará na 8080) AsyncWebSocket ws("/ws"); // Socket para cleanup de conexões antigas perto do limite máximo de conexões simultâneas const char* NTP_SERVER = "a.st1.ntp.br"; // Dados do Servidor NTP do Brasil const char* TZ_INFO = "<-03>3"; // Definição do Fuso time_t acessos[MAX_TRILHA]; // Últimas 10 deteccções de acesso ao CPD int topo=-1; // Topo das lista de detecções time_t startup; // hora do startup time_t varredura; // hora da última varredura int lastTemp = 0; // última Temp lida int lastUmid = 0; // última Umid lida bool primeiraVez = true; // para forçar atualizar o LED antes do timer disparar bool estadoEmergencia = false; // se está no estado de emergência por fogo ou gás IPAddress ip (1, 1, 1, 1); // The remote ip to ping, DNS do Google unsigned long semInternet; // Momento da queda da Internet bool lastInternet; // Última verificação da internet bool atualInternet; // Se tem internet no momento unsigned long lastCleanUp; // Última limpeza de conexões perdidas de navegadores para não estourar o http server bool shouldSaveConfig = false; // Flag se deve persistir os parãmetros char cpdID[50] = "CPD-1"; // Nome default do Nó a ser monitorado (pode vir da parametrição na configuração) int intervaloTimer = 60; // Para receber o Intervalo default do timer (60 seg ou pode vir da parametrição na configuração) bool nivelGas=HIGH; // Se há presença de Gás HIGH=Ausente bool nivelFogo=HIGH; // Se há presença de Fogo HIGH=Ausente String nivelMsg[2] = {"Presente", "Ausente"}; // Estados do Gás e Fogo String emergencia[2] = {"Fogo", "Fumaça"}; // Qual emergência bool nivel = BUZZER_ON; // Nível do Buzzer na situação de emergência unsigned long ultimaTroca = 0; // Última troca do nível na emergência WiFiManager wm; // Define o Objeto WiFiManager LiquidCrystal_I2C lcd(0x27, 16, 2);// Define o objeto LCD // Definição do HTML Principal const char index_html[] PROGMEM = R"rawliteral( <!DOCTYPE html> <html lang="pt-br"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="refresh" content="%ciclo%"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <title>Dados dos sensores</title> <style> html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;} body{margin-top: 50px;} h1 {color: #444444; margin: 50px auto 30px;} p {font-size: 24px; color: #444444; margin-bottom: 10px;} table, th, td {border: 1px solid black;} th, td {padding: 15px; font-size: 24px; color: #444444;} table {margin-left: auto; margin-right: auto;} th {background-color: #4CAF50; color: white;} </style> </head> <body> <div id="webpage"> <h1>Dados dos Sensores - %cpuid%</h1> <p>Temperatura: <b>%temperatura%</b></p> <p>Umidade: <b>%umidade%%</b></p> <p>Luminosidade: <b>%luminosidade%%</b></p> <p>Fogo: <b>%nivelfogo%</b></p> <p>Gás: <b>%nivelgas%</b></p> <p>Data/Hora: <b>%timestamp%</b></p> <p><b>Top %maxtrilha% Acessos</b></p> <table> <tr> <th>Ordem</th> <th>Data/Hora</th> </tr> %acessos% </table> </div> </body> </html> )rawliteral"; // Prototipação de Funções void alarme(int tempo); // Soa o alarme (Buzzer) de forma sincrona (com delay) void disparaSirene(byte pin, int intervalo, int index); // Soa o alarme (Buzzer) de forma assincrona (sem delay) void desligaSirene(byte pin); // Cancela o alarme assincrono (Buzzer) void saveConfigFile(); // Persiste CPUID e Intervalo no SPIFFS do ESP32 bool loadConfigFile(); // Recupera CPUID e Intervalo do SPIFFS do ESP32 void saveConfigCallback(); // Callback para informação do processo de configuração WiFi void configModeCallback(WiFiManager *myWiFiManager); // Callback para WifiManager bool getNTPtime(int sec); // Sincroniza o horário do ESP32 com NTP server brasileiro void WiFiEvent(WiFiEvent_t event); // Evento chamado no processo de conexão do Wifi String getTimeStamp(); // Devolve o localtime dd/mm/aaaa hh:mm:ss int temp2index(int temp); // Classifica o valor da temperatura (faixas) int getTemperatura(); // Devolve a temperatura lendo o DHT 22 int getUmidade(); // Devolve a umidade lendo o DHT 22 int getLuminosidade(); // Devolve a luminosidade lendo o LDR void IRAM_ATTR onTimer(); // Trata a interrupção do Timer void IRAM_ATTR resetModule(); // Trata a interrupção para WatchDog void setColor(int redValue, int greenValue, int blueValue); // Rotina para definir a cor RGB para o LED void Acende_Alerta(int nivel); // Acende o LED RGB de acordo com a temperatura void nao_encontrado(AsyncWebServerRequest *request); // Responde a URL inválida String colorirTemp(int temp); // Dar a cor adequada ao valor da Temperatura para o HTML String colorirNivel(bool nivel); // Dar a cor adequada ao valor do Nível Fogo/Gás para o HTML String html(int temperatura, int umidade, int luminosidade);// Monta o html da resposta para a URI / (home do site) void Check_WiFiManager(bool forceConfig); // Inicialização/Configuração WiFi Manager no ESP32 void registraDeteccao(); // Registra a deteção de presença na lista void displayRequest(AsyncWebServerRequest *request); // Mostra informações da requisição http na Console //------------------------------------------------ // Inicialização da Aplicação no ESP32 //------------------------------------------------ void setup() { // Inicializa a serial Serial.begin(115200); while (!Serial) ; // Inicializa LED_BUILTIN pinMode(LED_BUILTIN, OUTPUT); // Inicializa o Botão interno do ESP32 pinMode(TRIGGER_PIN, INPUT_PULLUP); // Inicializa o Buzzer pinMode(pinBuzzer, OUTPUT); // Inicializa o Sensor Fogo pinMode(pinFogo, INPUT); // Inicializa o Sensor de Gás pinMode(pinGas, INPUT); // Inicializa o PIR pinMode(pinPIR, INPUT); // Inicializa o display LCD lcd.init(); lcd.backlight(); // Inicializa a lista das últimas deteccões de acesso ao CPD memset(acessos, 0, sizeof(acessos)); // Defina a porta do WiFiManager para 8080 no modo AP para não conflitar com a // porta 80 que vamos utilizar para responder as requisições wm.setHttpPort(8080); // Chama Wifi_Manager para conectar no Wifi ou entrar em modo de configuração // caso os parãmetros SSID, Senha, CPIID e Intervalo do TIMER não estejam persistidos Check_WiFiManager(false); // Se chegamos até aqui é porque estamos conectados Serial.println("WiFi conectado..."); Serial.print("IP address: "); Serial.println(WiFi.localIP()); // Imprime o MAC Serial.print("MAC: "); Serial.println(WiFi.macAddress()); // Imprime o Sinal Wifi Serial.print("Sinal: "); Serial.print(WiFi.RSSI()); Serial.println(" db"); // Definições do LED RGB ledcSetup(PWM_R_ledChannel, PWM_freq, PWM_resolution); ledcAttachPin(RedPin, PWM_R_ledChannel); ledcSetup(PWM_G_ledChannel, PWM_freq, PWM_resolution); ledcAttachPin(GreenPin, PWM_G_ledChannel); ledcSetup(PWM_B_ledChannel, PWM_freq, PWM_resolution); ledcAttachPin(BluePin, PWM_B_ledChannel); // Verifica se está navegando pela internet pois às vezes fica conectado no AP porém sem internet lastInternet = Ping.ping(ip,4); if (!lastInternet) { semInternet = millis(); Serial.println("Sem internet no momento..."); } else { Serial.print("Internet ativa com média de "); Serial.print(Ping.averageTime()); Serial.println(" ms"); } // Sincroniza o horário interno com o Servidor NTP nacional Serial.print("Tentando sincronismo com o servidor NTP "); Serial.print(NTP_SERVER); Serial.print(" com TimeZone "); Serial.println(TZ_INFO); configTime(0, 0, NTP_SERVER); setenv("TZ", TZ_INFO, 1); tzset(); if (getNTPtime(10)) { // wait up to 10sec to sync Serial.println("NTP Server sincronizado"); } else { Serial.println("Time not set"); ESP.restart(); } // Pega a hora do startup time(&startup); localtime(&startup); // Inicialização do DHT22 dht.begin(); // Inicializa o Timer 0 de HW para a varredura dos sensores timerSensores = timerBegin(0, 80, true); timerAttachInterrupt(timerSensores, &onTimer, true); timerAlarmWrite(timerSensores, intervaloTimer*1000000, true); timerAlarmEnable(timerSensores); // Inicializa o Timer 1 de HW para implementação de WatchDog timerWatchDog = timerBegin(1, 80, true); timerAttachInterrupt(timerWatchDog, &resetModule, true); timerAlarmWrite(timerWatchDog, intervaloTimer*1000000, true); timerAlarmEnable(timerWatchDog); // Inicializa a resposta para / sv.on("/", HTTP_GET, [](AsyncWebServerRequest * request) { // Responde a Página Principal mostrando os dados dos sensores displayRequest(request); request->send_P(200, "text/html", index_html, processor); }); // Inicializa a resposta para /sensores sv.on("/sensores", HTTP_GET, [](AsyncWebServerRequest * request) { // Devolve JSON com as informações dos sensores displayRequest(request); // Buffer para JSON StaticJsonDocument<250> jsonDocument; char buffer[250]; jsonDocument.clear(); jsonDocument["Local"] = cpdID; sprintf(buffer, "%d ℃", getTemperatura()); jsonDocument["Temperatura"] = buffer; sprintf(buffer, "%d %c", getUmidade(), '%'); jsonDocument["Umidade"] = buffer; sprintf(buffer, "%d %c", getLuminosidade(), '%'); jsonDocument["Luminosidade"] = buffer; sprintf(buffer, "%s", nivelMsg[nivelFogo]); jsonDocument["Fogo"] = buffer; sprintf(buffer, "%s", nivelMsg[nivelGas]); jsonDocument["Gas"] = buffer; jsonDocument["TimeStamp"] = getTimeStamp(); serializeJson(jsonDocument, buffer); request->send(200, "application/json", buffer); jsonDocument.clear(); }); // Inicializa a resposta para /info sv.on("/info", HTTP_GET, [](AsyncWebServerRequest * request) { // Devolve JSON com as informações do servidor displayRequest(request); // JSON data buffer StaticJsonDocument<250> jsonDocument; char buffer[250]; jsonDocument.clear(); jsonDocument["Local"] = cpdID; jsonDocument["IP"] = WiFi.localIP(); jsonDocument["MAC"] = WiFi.macAddress(); jsonDocument["SSID"] = WiFi.SSID(); jsonDocument["IntervaloTimer"] = intervaloTimer; char timestamp[30]; strftime(timestamp, 30, "%d/%m/%Y %T", localtime(&startup)); jsonDocument["Startup"] = timestamp; serializeJson(jsonDocument, buffer); request->send(200, "application/json", buffer); jsonDocument.clear(); }); // Inicializa a resposta para /acessos sv.on("/acessos", HTTP_GET, [](AsyncWebServerRequest * request) { // Devolve JSON com as informações dos Top Acessos displayRequest(request); // JSON data buffer StaticJsonDocument<400> jsonDocument; JsonArray jsonArray = jsonDocument.to<JsonArray>(); char buffer[400]; char timestamp[30]; jsonArray.clear(); if (topo!=-1) { // Adiciona da posição do Topo até a posição 0 for (int ind1=topo;ind1>-1;ind1--) { strftime(timestamp, 30, "%d/%m/%Y %T", localtime(&acessos[ind1])); jsonArray.add(timestamp); } // Adiciona da posição final do array até a posição depois do Topo // Isso para manter em ordem decrescente de data/hora os acessos for (int ind2=MAX_TRILHA-1;ind2>topo;ind2--) { if (acessos[ind2]>0) { strftime(timestamp, 30, "%d/%m/%Y %T", localtime(&acessos[ind2])); jsonArray.add(timestamp); } } } serializeJson(jsonArray, buffer); request->send(200, "application/json", buffer); }); // Inicializa a resposta para endereço inválido (não suportados) sv.onNotFound(nao_encontrado); // Inicia o servidor propriamente dito sv.begin(); // Olá na console Serial.print("Monitoramento do CPD V1.0 Ago/2023 - Online - "); Serial.println(cpdID); // Soa o alarme para avisar que o setup completou alarme(alarmeLeve); // Avisa o início // Seta a condição inicial de varredura do CleanUp de conexões perdidas lastCleanUp = millis(); } //-------------------------------------------------------- // Loop principal para atender 6 blocos de funcionalidades //-------------------------------------------------------- void loop() { //-------------------------------------------------------------------------------------------------- // Bloco 1 : Reseta o Timer 1 do WatchDog //-------------------------------------------------------------------------------------------------- timerWrite(timerWatchDog, 0); //-------------------------------------------------------------------------------------------------- // Bloco 2 : Verifica se o timer acionou para pegar os dados dos sensores e checar se há internet ativa //-------------------------------------------------------------------------------------------------- if (interruptCounter > 0 || primeiraVez) { // Timer tratado if (interruptCounter > 0) { portENTER_CRITICAL(&timerMux); interruptCounter--; portEXIT_CRITICAL(&timerMux); } else primeiraVez = false; // Pega a hora da varredura time(&varredura); // Área de trabalho para formatção de texto char buf[30]; // Lê o sensor de Luminosidade int L = getLuminosidade(); Serial.print("Luminosidade: "); Serial.print(L); Serial.print("%"); // Lê o sensor de temperatura int t = getTemperatura(); sprintf(buf, " - Temperatura: %d℃", t); Serial.print(buf); // Lê o sensor de umidade int u = getUmidade(); sprintf(buf, " - Umidade: %d%c", u, '%'); Serial.println(buf); Serial.print("Detecção Fogo: "); Serial.print(nivelMsg[nivelFogo]); Serial.print("\tGás: "); Serial.println(nivelMsg[nivelGas]); // Atualiza o Display LCD com as informações sprintf(buf, "%d%cC U:%d%c L:%d%c", t, 0xDF, u, '%', L, '%'); lcd.clear(); lcd.setCursor(0, 0); lcd.print(buf); strftime(buf, 30, "%d/%m/%Y %T", localtime(&varredura)); lcd.setCursor(0, 1); lcd.print(buf); // Acende o LED RGB de acordo com a Temperatura Acende_Alerta(temp2index(t)); // Checa se há internet navegando atualInternet = Ping.ping(ip,4); digitalWrite(LED_BUILTIN, atualInternet); if (!lastInternet && atualInternet) { // Voltou a Internet Serial.print("Internet voltou depois de "); Serial.print(millis()-semInternet); Serial.println(" msec"); } else if (lastInternet && !atualInternet) { // Internet caiu semInternet = millis(); Serial.println("Internet caiu"); } else if (!lastInternet && !atualInternet) { // Internet permanece fora Serial.println("Internet continua fora"); } else { // Internet permanece ativa Serial.print("Internet continua ativa com média de "); Serial.print(Ping.averageTime()); Serial.println(" ms"); } //alarme(alarmeBreve); lastInternet = atualInternet; } //-------------------------------------------------------------------------------------------------- // Bloco 3 : Verifica se o botão foi apertado para forçar a entrada no modo de configuração. É útil // quando a senha do wifi mudou ou está se conectando em outra rede wifi. Isso evita ter // o SSID/senha no código. //-------------------------------------------------------------------------------------------------- if ( digitalRead(TRIGGER_PIN) == LOW) { // Força a entrada em modo de configuração Check_WiFiManager(true); } //-------------------------------------------------------------------------------------------------- // Bloco 4 : Verifica os sensores de gás e fogo e independente do timer e aciona o alarme de // forma assíncrona, caso necessário //-------------------------------------------------------------------------------------------------- nivelGas = digitalRead(pinGas); nivelFogo = digitalRead(pinFogo); if ( nivelGas == GAS) disparaSirene(pinBuzzer, alarmeGas, 1); else if ( nivelFogo == FOGO) disparaSirene(pinBuzzer, alarmeFogo, 0); else if (estadoEmergencia) desligaSirene(pinBuzzer); // -------------------------------------------------------------------------------------------------- // Bloco 5 : Verifica o sensor de presença PIR e tenta registrar na lista das 10 últimas ocorrências //--------------------------------------------------------------------------------------------------- if (digitalRead(pinPIR)) { registraDeteccao(); } // ------------------------------------------------------------------------------------------------- // Bloco 6 : Faz o CleanUp de conexões antigas do navegador na situação de limite de usuários // para evitar o crash //-------------------------------------------------------------------------------------------------- if (millis()-lastCleanUp > cleanUpCycle) { ws.cleanupClients(); lastCleanUp = millis(); } } //----------------------------------------------- // Soa o buzzer por um tempo fornecido (com delay) //----------------------------------------------- void alarme(int tempo) { digitalWrite(pinBuzzer,BUZZER_ON); delay(tempo); digitalWrite(pinBuzzer,BUZZER_OFF); } //------------------------------------------------- // Soa o buzzer por um tempo fornecido (sem delay) //------------------------------------------------- void disparaSirene(byte pin, int intervalo, int index) { if (millis() - ultimaTroca > intervalo) { nivel = !nivel; ultimaTroca = millis(); if (!estadoEmergencia) { Serial.print("Emergência detectada: "); Serial.println(emergencia[index]); estadoEmergencia = true; } } digitalWrite(pin, nivel); } //------------------------------------------------- // Cancela o buzzer acionado de forma sem delay //------------------------------------------------- void desligaSirene(byte pin) { digitalWrite(pin, BUZZER_OFF); nivel = BUZZER_OFF; if (estadoEmergencia) { Serial.println("Emergência encerrada..."); estadoEmergencia = false; } } //------------------------------------------------ // Persiste CPUID e Intervalo no SPIFFS //------------------------------------------------ void saveConfigFile() // O arquivo de Config é salvo no formato JSON { Serial.println(F("Persistindo a configuração...")); // Cria um documento JSON StaticJsonDocument<512> json; json["cpdID"] = cpdID; json["intervaloTimer"] = intervaloTimer; json["topo"] = topo; JsonArray lista = json.createNestedArray("acessos"); for (int ind=0;ind<MAX_TRILHA;ind++) { lista.add(acessos[ind]); } // Abre o arquivo de configuração File configFile = SPIFFS.open(JSON_CONFIG_FILE, "w"); if (!configFile) { // Erro, arquino não foi aberto Serial.println("Erro ao persistir a configuração"); } // Serializa os dados do JSON no arquivo serializeJsonPretty(json, Serial); Serial.println(); if (serializeJson(json, configFile) == 0) { // Erro ai gravar o arquivo Serial.println(F("Erro ao gravar o arquivo de configuração")); } // Fecha o Arquivo configFile.close(); } //------------------------------------------------ // Recupera CPUID e Intervalo do SPIFFS //------------------------------------------------ bool loadConfigFile() // Carrega o arquivo de Configuração { // Verifica se o SPIFFS já foi inicializado if (!SPIFFS.begin(true)) { SPIFFS.format(); Serial.println("Sistema de Arquivo no SPIFFS foi formatado"); } // Lê as configurações no formato JSON Serial.println("Montando o FileSystem..."); // Força a entrada na primeira vez if (SPIFFS.begin(true)) { Serial.println("FileSystem montado..."); if (SPIFFS.exists(JSON_CONFIG_FILE)) { // o arquivo existe, vamos ler Serial.println("Lendo o arquivo de configuração"); File configFile = SPIFFS.open(JSON_CONFIG_FILE, "r"); if (configFile) { Serial.println("Arquivo de configuração aberto..."); StaticJsonDocument<512> json; DeserializationError error = deserializeJson(json, configFile); serializeJsonPretty(json, Serial); Serial.println(); if (!error) { Serial.println("Recuperando o JSON..."); strcpy(cpdID, json["cpdID"]); intervaloTimer = json["intervaloTimer"].as<int>(); // Verifica se o array de Acessos existe no JSON recuperado int cont=0; if (json.containsKey("acessos") && json.containsKey("topo")) { topo = json["topo"].as<int>(); JsonArray lista = json["acessos"]; for (int ind=0;ind<lista.size(), ind<MAX_TRILHA;ind++) { cont++; acessos[ind] = lista[ind].as<time_t>(); } Serial.print("Recuperado Topo="); Serial.println(topo); Serial.print("Acessos recuperados="); Serial.println(cont); } else Serial.println("Não encontrada persistência dos Acessos no SPIFFS..."); return true; } else { // Erro ao ler o JSON Serial.println("Erro ao carregar o JSON da configuração..."); } } } } else { // Erro ao montar o FileSystem Serial.println("Erro ao montar o FileSystem"); } return false; } //---------------------------------------------------------- // Callback para informação do processo de configuração WiFi //---------------------------------------------------------- void saveConfigCallback() // Callback para nos lembrar de salvar o arquivo de configuração { Serial.println("Persistência necessária..."); shouldSaveConfig = true; } //---------------------------------------------------------- // Callback para WifiManager //---------------------------------------------------------- void configModeCallback(WiFiManager *myWiFiManager) // É chamado no modo de configuração { Serial.println("Entrando no modo de configuração..."); Serial.print("Config SSID: "); Serial.println(myWiFiManager->getConfigPortalSSID()); Serial.print("Config IP Address: "); Serial.println(WiFi.softAPIP()); } //--------------------------------------------------------- // Sincroniza o horário do ESP32 com NTP server brasileiro //--------------------------------------------------------- bool getNTPtime(int sec) { { uint32_t start = millis(); tm timeinfo; time_t now; int cont=0; do { time(&now); localtime_r(&now, &timeinfo); if (++cont % 80 == 0) Serial.println(); else Serial.print("."); delay(10); } while (((millis() - start) <= (1000 * sec)) && (timeinfo.tm_year < (2016 - 1900))); if (timeinfo.tm_year <= (2016 - 1900)) return false; // the NTP call was not successful Serial.print("\nnow "); Serial.println(now); Serial.print("Time "); Serial.println(getTimeStamp()); } return true; } //------------------------------------------------ // Evento chamado no processo de conexão do Wifi //------------------------------------------------ void WiFiEvent(WiFiEvent_t event) { Serial.printf("[Evento Wi-Fi] evento: %d\n", event); switch (event) { case SYSTEM_EVENT_WIFI_READY: Serial.println("interface WiFi pronta"); break; case SYSTEM_EVENT_SCAN_DONE: Serial.println("Pesquisa por AP completada"); break; case SYSTEM_EVENT_STA_START: Serial.println("Cliente WiFi iniciado"); break; case SYSTEM_EVENT_STA_STOP: Serial.println("Clientes WiFi cancelados"); break; case SYSTEM_EVENT_STA_CONNECTED: Serial.println("Conectado ao AP"); digitalWrite(LED_BUILTIN, HIGH); break; case SYSTEM_EVENT_STA_DISCONNECTED: Serial.println("Desconectado do AP WiFi"); digitalWrite(LED_BUILTIN, LOW); Check_WiFiManager(false); break; case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: Serial.println("Modo de Autenticação do AP mudou"); break; case SYSTEM_EVENT_STA_GOT_IP: Serial.print("Endereço IP obtido: "); Serial.println(WiFi.localIP()); break; case SYSTEM_EVENT_STA_LOST_IP: Serial.println("Endereço IP perdido e foi resetado para 0"); break; case SYSTEM_EVENT_STA_WPS_ER_SUCCESS: Serial.println("WPS: modo enrollee bem sucedido"); break; case SYSTEM_EVENT_STA_WPS_ER_FAILED: Serial.println("WPS: modo enrollee falhou"); break; case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT: Serial.println("WPS: timeout no modo enrollee"); break; case SYSTEM_EVENT_STA_WPS_ER_PIN: Serial.println("WPS: pin code no modo enrollee"); break; case SYSTEM_EVENT_AP_START: Serial.println("AP Wifi Iniciado"); break; case SYSTEM_EVENT_AP_STOP: Serial.println("AP Wifi parado"); break; case SYSTEM_EVENT_AP_STACONNECTED: Serial.println("Cliente conectado"); break; case SYSTEM_EVENT_AP_STADISCONNECTED: Serial.println("Cliente desconectado"); break; case SYSTEM_EVENT_AP_STAIPASSIGNED: Serial.println("IP associado ao Cliente"); break; case SYSTEM_EVENT_AP_PROBEREQRECVED: Serial.println("Requisição de probe recebida"); break; case SYSTEM_EVENT_GOT_IP6: Serial.println("IPv6 é preferencial"); break; case SYSTEM_EVENT_ETH_START: Serial.println("Interface Ethernet iniciada"); break; case SYSTEM_EVENT_ETH_STOP: Serial.println("Interface Ethernet parada"); break; case SYSTEM_EVENT_ETH_CONNECTED: Serial.println("Interface Ethernet conectada"); break; case SYSTEM_EVENT_ETH_DISCONNECTED: Serial.println("Interface Ethernet desconectada"); break; case SYSTEM_EVENT_ETH_GOT_IP: Serial.println("Endereço IP obtido"); break; default: break; } } //------------------------------------------------ // Devolve o localtime dd/mm/aaaa hh:mm:ss //------------------------------------------------ String getTimeStamp() { time_t now; time(&now); char timestamp[30]; strftime(timestamp, 30, "%d/%m/%Y %T", localtime(&now)); return String(timestamp); } //------------------------------------------------ // Classifica o valor da temperatura (faixas) //------------------------------------------------ int temp2index(int temp) { if (temp < 10) return Temp_Gelado; else if (temp < 20) return Temp_Frio; else if (temp < 25) return Temp_Normal; else if (temp < 30) return Temp_Quente; else return Temp_Fervendo; } //------------------------------------------------ // Devolve a temperatura lendo o DHT 22 //------------------------------------------------ int getTemperatura() { // Lê o sensor de temperatura int t = dht.readTemperature(); if (!isnan(t) && t != INT_MAX) { lastTemp = t; return t; } else { Serial.print("\nErro ao ler a Temperatura => última leitura: "); Serial.println(lastTemp); return lastTemp; } } //------------------------------------------------ // Devolve a umidade lendo o DHT 22 //------------------------------------------------ int getUmidade() { // Lê o sensor de umidade int u = dht.readHumidity(); if (!isnan(u) && u != INT_MAX) { lastUmid = u; return u; } else { Serial.print("\nErro ao ler a Umidade => última leitura: "); Serial.println(lastUmid); return lastUmid; } } //------------------------------------------------ // Devolve a luminosidade lendo o LDR //------------------------------------------------ int getLuminosidade() { // Lê o sensor de Luminosidade return map(analogRead(pinLDR), 0, 4095, 0, 100); } //------------------------------------------------ // Trata a interrupção do Timer 0 - Sensores //------------------------------------------------ void IRAM_ATTR onTimer() { portENTER_CRITICAL_ISR(&timerMux); interruptCounter++; portEXIT_CRITICAL_ISR(&timerMux); } //------------------------------------------------ // Trata a interrupção do Timer 1 - WatchDog //------------------------------------------------ void IRAM_ATTR resetModule() { ets_printf("(watchdog) reiniciar\n"); //imprime no log //esp_restart_noos(); //reinicia o chip ESP.restart(); //reinicia o chip } //---------------------------------------------------- // Rotina para definir a cor RGB para o LED //---------------------------------------------------- void setColor(int redValue, int greenValue, int blueValue) { // Acende o LED com as cores fornecidas ledcWrite(PWM_R_ledChannel, redValue); ledcWrite(PWM_G_ledChannel, greenValue); ledcWrite(PWM_B_ledChannel, blueValue); // Atualiza o valor RED na console Serial.print("R="); Serial.print(redValue); Serial.print("\t"); // Atualiza o valor GREEN na console Serial.print("G="); Serial.print(greenValue); Serial.print("\t"); // Atualiza o valor BLUE na console Serial.print("B="); Serial.println(blueValue); } //------------------------------------------------ // Acende o LED RGB de acordo com a temperatura //------------------------------------------------ void Acende_Alerta(int nivel) { setColor(Alerta[nivel][0], Alerta[nivel][1], Alerta[nivel][2]); } //------------------------------------------------ // Responde a URL inválida //------------------------------------------------ void nao_encontrado(AsyncWebServerRequest *request) { // Sub-rotina para caso seja retornado um erro Serial.print("Get NotFound from "); Serial.println(request->client()->remoteIP()); // Retorna a mensagem de erro em caso de um retorno 404 request->send(404, "text/html", "<h1>Erro: Não encontrado</h1>"); } //------------------------------------------------------- // Dar a cor adequada ao valor da Temperatura para o HTML //------------------------------------------------------- String colorirTemp(int temp) { int index = temp2index(temp); String cd = "<span style=\"color:RGB("; cd += Alerta[index][0]; cd += ","; cd += Alerta[index][1]; cd += ","; cd += Alerta[index][2]; cd += ")\">"; cd += temp; cd += "℃</span>"; // retorna o HTML da cor return cd; } //-------------------------------------------------------- // Dar a cor adequada ao valor do Nível Fogo/Gás para Html //-------------------------------------------------------- String colorirNivel(bool nivel) { String result = "<span style=\"color:"; if (nivel==LOW) result += "red"; else result += "green"; result += "\">"; result += nivelMsg[nivel]; result += "</span>"; return result; } //---------------------------------------------------- // Inicialização/Configuração do WiFi Manager no ESP32 //---------------------------------------------------- void Check_WiFiManager(bool forceConfig) { // Tenta carregar os parâmetros do SPIFFS bool spiffsSetup = loadConfigFile(); if (!spiffsSetup) { Serial.println(F("Forçando o modo de configuração...")); forceConfig = true; } // Define o modo AP WiFi.mode(WIFI_STA); // Seta HostName String hostname = "ESP32-"+String(cpdID); WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); WiFi.setHostname(hostname.c_str()); // Define o handle para tratar os eventos do Wifi WiFi.onEvent(WiFiEvent); // Verifica se tem que resetar os parâmetros anteriores if (forceConfig) wm.resetSettings(); // Define o CALLBACK do modo CONFIG com alteração wm.setSaveConfigCallback(saveConfigCallback); // Define o CALLBACK do modo CONFIG wm.setAPCallback(configModeCallback); // Definição de dois campos de parâmteros esperados // cpuID (String) - 50 careteres no máximo WiFiManagerParameter custom_cpdID("CpdID", "Informe o Id do CPD (< 50)", cpdID, 50); // Converte o inteiro em alpha para mostra a interface char convertedValue[6]; sprintf(convertedValue, "%d", intervaloTimer); // intervaloTimer(Number) - 4 caracteres no m[aximo WiFiManagerParameter custom_intervaloTimer("IntervaloTimer", "Informe o intervalo de varredura em seg (< 9999)", convertedValue, 4); // Adiciona os campos de parâmetros no MENU do WifiManager wm.addParameter(&custom_cpdID); wm.addParameter(&custom_intervaloTimer); // Monta o SSID do modo AP para permitir a configuração char ssid[50]; sprintf(ssid, "CPD_AP_%x",(uint32_t)ESP.getEfuseMac()); // Verifica se entra no modo AP/Configuração ou no modo cliente normal if (forceConfig) // Entra no modo de AP de configuração ... com senha fixa { if (!wm.startConfigPortal(ssid, "password")) { Serial.println("Erro na conexão com timeout..."); delay(3000); // Reseta para já tentar entrar no modo Normal ESP.restart(); delay(5000); } } else { // Entra no modo de conexão normal recuperando o SSID/Senha anteriores if (!wm.autoConnect(ssid, "password")) { Serial.println("Erro na conexão com timeout..."); delay(3000); // if we still have not connected restart and try all over again ESP.restart(); delay(5000); } } // Recupera o campo cpuId preenchido na interface strncpy(cpdID, custom_cpdID.getValue(), sizeof(cpdID)); if (strlen(cpdID)==0) strcpy(cpdID,"CPD_NÃO_DEFINIDO"); Serial.print("cpdID: "); Serial.println(cpdID); // Recupera o campo intervaloTimer preenchido na interface convertendo para inteiro intervaloTimer = atoi(custom_intervaloTimer.getValue()); if (intervaloTimer < 60) intervaloTimer = 60; Serial.print("intervaloTimer: "); Serial.println(intervaloTimer); // Salva os parâmetros no FileSystem FLASH -> não perde quando desligado if (shouldSaveConfig) { saveConfigFile(); } } //---------------------------------------------------- // Registra a detecção de presença desde que tenha uma // diferença de PIR_MIN_TIME (5 min.) para a detecção // anterior. O sensor do tipo PIR quando detecta a // presença ele mantém HIGH no pino de 5 seg a 2,5 min. // Estamos partindo do princípio que o PIR está ajustado // para o mínimo. //---------------------------------------------------- void registraDeteccao() { time_t agora; bool alterou=false; // Pega a data/hora do momento time(&agora); // Empilha a data/hora if (topo==-1) { topo++; acessos[topo]=agora; alterou = true; Serial.print("Detecção de presença["); Serial.print(topo); Serial.println("]"); } else if (agora-acessos[topo] > PIR_MIN_TIME) { topo = (topo+1) % MAX_TRILHA; acessos[topo]=agora; alterou = true; Serial.print("Detecção de presença["); Serial.print(topo); Serial.println("]"); } // Verifica se deve persistir no SPIFFS if (alterou) saveConfigFile(); } //------------------------------------------------ // Expande a Página Principal //------------------------------------------------ String processor(const String& var) { if (var == "ciclo") { return String(intervaloTimer); } else if (var.equalsIgnoreCase("cpuid")) { return cpdID; } else if (var.equalsIgnoreCase("temperatura")) { return colorirTemp(getTemperatura()); } else if (var.equalsIgnoreCase("umidade")) { return String(getUmidade()); } else if (var.equalsIgnoreCase("luminosidade")) { return String(getLuminosidade()); } else if (var.equalsIgnoreCase("nivelfogo")) { return colorirNivel(nivelFogo); } else if (var.equalsIgnoreCase("nivelgas")) { return colorirNivel(nivelGas); } else if (var.equalsIgnoreCase("timestamp")) { return getTimeStamp(); } else if (var.equalsIgnoreCase("maxtrilha")) { return String(MAX_TRILHA); } else if (var.equalsIgnoreCase("acessos")) { String cd=""; // Varre da posição do Topo até a posição 0 int cont=0; char timestamp[30]; for (int ind1=topo;ind1>-1;ind1--) { cont++; strftime(timestamp, 30, "%d/%m/%Y %T", localtime(&acessos[ind1])); cd += " <tr>\n"; cd += " <td>"; cd += cont; cd += "</td>\n"; cd += " <td>"; cd += timestamp; cd += "</td>\n"; cd += " </tr>\n"; } // Varre da posição final do array até a posição depois do Topo // Isso para manter em ordem decrescente de data/hora os acessos for (int ind2=MAX_TRILHA-1;ind2>topo;ind2--) { if (acessos[ind2]>0) { cont++; strftime(timestamp, 30, "%d/%m/%Y %T", localtime(&acessos[ind2])); cd += " <tr>\n"; cd += " <td>"; cd += cont; cd += "</td>\n"; cd += " <td>"; cd += timestamp; cd += "</td>\n"; cd += " </tr>\n"; } } return cd; } return String(); } //---------------------------------------------------- // Mostra as informações da requisição http na console //---------------------------------------------------- void displayRequest(AsyncWebServerRequest *request) { Serial.print("Método: "); Serial.print(request->methodToString()); Serial.print("\t| URL: "); Serial.print(request->url()); Serial.print("\t| IP: "); Serial.println(request->client()->remoteIP()); }
A seguir, relacionamos algumas possibilidades de extensão de funcionalidades que os MAKERS podem implementar a partir das ideias básicas deste projeto, lembrando que o céu é o limite:
O ESP32 é um excelente microcontrolador com capacidade para suportar aplicações mais complexas e maiores em código e dados, de baixo custo, inclusive com serviços de Internet de forma assíncrona sem comprometer o fluxo da função “void loop()”. A característica de operar em 3,3V nos exige maior atenção na escolha dos sensores e, quando operam com saída em 5V, a redução de tensão é necessária através do uso de resistores.
WiFiManager with ESP32 – Stop Hard-coding WiFi Credentials!
Os profissionais sabem disso: Interrupt ISR!
Timer — Arduino-ESP32 2.0.6 documentation (espressif.com)
ESP32 Async Web Server – Control Outputs with Arduino IDE (ESPAsyncWebServer library)
|
|
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.
Conheça a Metodologia Eletrogate e Lecione um Curso de Robótica nas Escolas da sua Região!