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:
Diagrama do Circuito
Circuito em Bancada
App Fing para descobrir o IP obtido na Rede
Página Principal – /
Dados dos Sensores – /sensores
Informações do Servidor – /info
Top 10 Acessos
Seleção do equipamento no Modo AP
Conectado ao equipamento no Modo AP
Tela Principal da Configuração
Tela para Definição das Configurações
Tipos de Sensores de Gás Existentes no Mercado
//---------------------------------------------------------------------------------------- // 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!