



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 16x2 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());
}
O vídeo de demonstração é conferido em https://youtu.be/12Ldjl_gXg0.
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)
|
|
Sistema para monitoramento de CPD ou algum tipo sala que exija cuidados com Temperatura, Umidade, Luminosidade, detector de presença, detecção de fogo e fumaça.
Encontre tudo na Loja Eletrogate com frete grátis para compras acima de R$ 200