// Sistema de Monitoramento Completo para Vinheria
// Temperatura, Umidade e Luminosidade com Data Logger
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <RTClib.h>
#include <EEPROM.h>
// Opções de configuração
#define LOG_OPTION 1 // Leitura do log ativada
#define SERIAL_OPTION 1 // Comunicação serial ligada
#define UTC_OFFSET -3 // Define fuso horário UTC-3 (Caso deseje utilizar outro fuso, alterar o fuso no código)
// Configuração do DHT22 (DHT11 no arduino físico em sala)
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// Configuração do LCD I2C
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Configuração do RTC
RTC_DS1307 rtc;
// Definindo as variáveis
const int pinoLDR = A0;
const int ledVerde = 8;
const int ledAmarelo = 7;
const int ledVermelho = 6;
const int buzzer = 13;
const int buttonPin = 12;
const int filterButtonPin = 11;
// Variáveis para controlar unidade de temperatura
bool usarFahrenheit = false; // false = Celsius, true = Fahrenheit
bool lastButtonState = LOW; // Último estado do botão de temperatura
bool currentButtonState = LOW; // Estado atual do botão de temperatura
unsigned long lastDebounceTime = 0; // Tempo do último debounce
const unsigned long debounceDelay = 50; // Delay para debounce (50ms)
// Variáveis para controlar filtro do Serial Monitor
bool filtrarSerial = false; // false = mostra tudo, true = mostra apenas ALERTA/CRÍTICO (Armazenados na EEPROM)
bool lastFilterButtonState = LOW; // Estado anterior do botão de filtro
bool currentFilterButtonState = LOW; // Estado atual do botão de filtro
unsigned long lastFilterDebounceTime = 0; // Tempo do último debounce do filtro
// Configurações da EEPROM para data logger
const int maxRecords = 100;
const int recordSize = 10; // 4 (timestamp) + 2 (temp) + 2 (umid) + 2 (lumi)
int startAddress = 0;
int endAddress = maxRecords * recordSize;
int currentAddress = 0;
unsigned long ultimoRegistroEEPROM = 0; // Controla intervalo de 30s
// Triggers - Limites ideais para o armazenamento do vinho
float trigger_t_min = 12.0; // Temperatura mínima ideal (°C)
float trigger_t_max = 18.0; // Temperatura máxima ideal (°C)
float trigger_u_min = 60.0; // Umidade mínima ideal (%)
float trigger_u_max = 80.0; // Umidade máxima ideal (%)
int trigger_l_max = 40; // Luminosidade máxima ideal (%)
// Variáveis de controle do buzzer
unsigned long tempoInicioBuzzer = 0;
bool buzzerAtivo = false;
// Sistema de média móvel para LDR (média de 10 leituras)
const int numLeituras = 10;
int leituras[numLeituras];
int indiceLeitura = 0;
int total = 0;
int media = 0;
// Variáveis de leitura
float temperatura = 0;
float umidade = 0;
int luminosidade = 0;
// Caracteres customizados para o LCD
// Caracteres de inicialização
byte uva[8] = { // Logo - Uva
B01110,
B11111,
B11111,
B01110,
B11111,
B11111,
B11111,
B01110
};
byte folha[8] = { // Logo - Folha
B00100,
B01110,
B11111,
B11111,
B01110,
B00100,
B00100,
B00000
};
// Ícones de temperatura (3 níveis)
byte termoBaixo[8] = { // Temperatura baixa - termômetro vazio
B00100,
B01010,
B01010,
B01010,
B01010,
B10001,
B10001,
B01110
};
byte termoMedio[8] = { // Temperatura média - termômetro meio cheio
B00100,
B01010,
B01010,
B01110,
B01110,
B11111,
B11111,
B01110
};
byte termoAlto[8] = { // Temperatura alta - termômetro cheio
B00100,
B01110,
B01110,
B01110,
B01110,
B11111,
B11111,
B01110
};
// Ícones de umidade (3 níveis)
byte gotaBaixa[8] = { // Umidade baixa - gota vazia
B00100,
B00100,
B01010,
B01010,
B10001,
B10001,
B10001,
B01110
};
byte gotaMedia[8] = { // Umidade média - gota meio cheia
B00100,
B00100,
B01010,
B01010,
B10001,
B11111,
B11111,
B01110
};
byte gotaCheia[8] = { // Umidade alta - gota cheia
B00100,
B00100,
B01110,
B01110,
B11111,
B11111,
B11111,
B01110
};
// Ícones de luminosidade (3 níveis)
byte lampadaApagada[8] = { // Luminosidade baixa - lâmpada apagada
B01110,
B10001,
B10001,
B10001,
B01110,
B01110,
B00100,
B00100
};
byte lampadaMeia[8] = { // Luminosidade média - lâmpada meia
B01110,
B10001,
B10101,
B10101,
B01110,
B01110,
B00100,
B00100
};
byte lampadaAcesa[8] = { // Luminosidade alta - lâmpada acesa
B01110,
B11111,
B11111,
B11111,
B01110,
B01110,
B00100,
B00100
};
// Emojis para os estados
byte emojiFeliz[8] = { // Estado OK
B00000,
B01010,
B01010,
B00000,
B10001,
B10001,
B01110,
B00000
};
byte emojiSerio[8] = { // Estado Alerta
B00000,
B01010,
B01010,
B00000,
B00000,
B11111,
B00000,
B00000
};
byte emojiTriste[8] = { // Estado Crítico
B00000,
B01010,
B01010,
B00000,
B01110,
B10001,
B10001,
B00000
};
// Converte temperatura de Celsius para Fahrenheit
// Usa a fórmula para fazer a transformação: °F = (°C × 9/5) + 32
float celsiusParaFahrenheit(float celsius) {
return (celsius * 9.0 / 5.0) + 32.0;
}
// Apaga todos os LEDs de status
void apagarTodosLEDs() {
digitalWrite(ledVerde, LOW);
digitalWrite(ledAmarelo, LOW);
digitalWrite(ledVermelho, LOW);
}
// Ativa o buzzer para alerta
void ativarBuzzer() {
if (!buzzerAtivo) {
buzzerAtivo = true;
tempoInicioBuzzer = millis();
digitalWrite(buzzer, HIGH);
}
}
// Controla o tempo do buzzer (desliga após 3 segundos)
void controlarBuzzer() {
if (buzzerAtivo && (millis() - tempoInicioBuzzer >= 3000)) {
digitalWrite(buzzer, LOW);
buzzerAtivo = false;
}
}
// Verifica se o ambiente está dentro dos parâmetros ideais
bool isAmbienteOK() {
return (temperatura >= trigger_t_min && temperatura <= trigger_t_max &&
umidade >= trigger_u_min && umidade <= trigger_u_max &&
luminosidade <= trigger_l_max);
}
// Verifica se o ambiente está em estado de alerta
bool isAmbienteAlerta() {
bool tempAlerta = (temperatura < trigger_t_min || temperatura > trigger_t_max) &&
(temperatura >= trigger_t_min - 3 && temperatura <= trigger_t_max + 3);
bool umidAlerta = (umidade < trigger_u_min || umidade > trigger_u_max) &&
(umidade >= trigger_u_min - 10 && umidade <= trigger_u_max + 10);
bool lumAlerta = (luminosidade > trigger_l_max && luminosidade <= 65);
return (tempAlerta || umidAlerta || lumAlerta) && !isAmbienteCritico();
}
// Verifica se o ambiente está em estado crítico
bool isAmbienteCritico() {
bool tempCritica = (temperatura < trigger_t_min - 3 || temperatura > trigger_t_max + 3);
bool umidCritica = (umidade < trigger_u_min - 10 || umidade > trigger_u_max + 10);
bool lumCritica = (luminosidade > 65);
return (tempCritica || umidCritica || lumCritica);
}
// Verifica as condições do ambiente e aciona os alertas apropriados
void verificarCondicoes() {
apagarTodosLEDs();
if (isAmbienteOK()) {
digitalWrite(ledVerde, HIGH);
} else if (isAmbienteAlerta()) {
digitalWrite(ledAmarelo, HIGH);
ativarBuzzer();
} else if (isAmbienteCritico()) {
digitalWrite(ledVermelho, HIGH);
ativarBuzzer();
}
}
// Exibe tela de boas-vindas com animação
void telaBoasVindas() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.write(byte(1));
lcd.print(" Vinharia ");
lcd.write(byte(1));
lcd.setCursor(0, 1);
lcd.write(byte(0));
lcd.print(" Agnello ");
lcd.write(byte(0));
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Inicializando...");
for (int i = 0; i < 16; i++) {
lcd.setCursor(i, 1);
lcd.write(byte(0xFF)); // Tela preenchida
delay(100);
}
delay(500);
lcd.clear();
}
// Lê todos os sensores (temperatura, umidade e luminosidade)
void lerSensores() {
temperatura = dht.readTemperature();
umidade = dht.readHumidity();
if (isnan(temperatura) || isnan(umidade)) {
Serial.println("Erro ao ler DHT22!");
temperatura = 0;
umidade = 0;
}
total = total - leituras[indiceLeitura];
int valorLDR = analogRead(pinoLDR);
leituras[indiceLeitura] = valorLDR;
total = total + leituras[indiceLeitura];
indiceLeitura = (indiceLeitura + 1) % numLeituras;
media = total / numLeituras;
luminosidade = map(media, 0, 1023, 100, 0);
}
// Lê o botão de alternância de unidade de temperatura com debounce
void lerBotao() {
int reading = digitalRead(buttonPin);
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != currentButtonState) {
currentButtonState = reading;
if (currentButtonState == HIGH) {
usarFahrenheit = !usarFahrenheit;
if (SERIAL_OPTION) {
Serial.print("\n>>> Unidade alterada para: ");
Serial.println(usarFahrenheit ? "FAHRENHEIT" : "CELSIUS");
}
}
}
}
lastButtonState = reading;
}
// Lê o botão de filtro do Serial Monitor com debounce
void lerBotaoFiltro() {
int reading = digitalRead(filterButtonPin);
if (reading != lastFilterButtonState) {
lastFilterDebounceTime = millis();
}
if ((millis() - lastFilterDebounceTime) > debounceDelay) {
if (reading != currentFilterButtonState) {
currentFilterButtonState = reading;
if (currentFilterButtonState == HIGH) {
filtrarSerial = !filtrarSerial;
if (SERIAL_OPTION) {
Serial.println("\n========================================");
if (filtrarSerial) {
Serial.println(">>> FILTRO ATIVADO: Exibindo apenas registros ALERTA e CRITICO");
Serial.println(">>> (Dados que serao salvos na EEPROM)");
} else {
Serial.println(">>> FILTRO DESATIVADO: Exibindo todos os registros");
}
Serial.println("========================================\n");
}
}
}
}
lastFilterButtonState = reading;
}
// Exibe os dados no display LCD com ícones
void exibirNoLCD() {
if (temperatura < trigger_t_min) {
lcd.createChar(2, termoBaixo);
} else if (temperatura >= trigger_t_min && temperatura <= trigger_t_max) {
lcd.createChar(2, termoMedio);
} else {
lcd.createChar(2, termoAlto);
}
if (umidade < trigger_u_min) {
lcd.createChar(3, gotaBaixa);
} else if (umidade >= trigger_u_min && umidade <= trigger_u_max) {
lcd.createChar(3, gotaMedia);
} else {
lcd.createChar(3, gotaCheia);
}
if (luminosidade <= trigger_l_max) {
lcd.createChar(4, lampadaApagada);
} else if (luminosidade > trigger_l_max && luminosidade <= 65) {
lcd.createChar(4, lampadaMeia);
} else {
lcd.createChar(4, lampadaAcesa);
}
if (isAmbienteOK()) {
lcd.createChar(5, emojiFeliz);
} else if (isAmbienteAlerta()) {
lcd.createChar(5, emojiSerio);
} else {
lcd.createChar(5, emojiTriste);
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.write(byte(2));
if (usarFahrenheit) {
float tempF = celsiusParaFahrenheit(temperatura);
lcd.print(tempF, 1);
lcd.print("F ");
} else {
lcd.print(temperatura, 1);
lcd.print("C ");
}
lcd.write(byte(3));
lcd.print(umidade, 0);
lcd.print("% ");
lcd.write(byte(4));
lcd.print(luminosidade);
lcd.print("%");
lcd.setCursor(0, 1);
if (isAmbienteOK()) {
lcd.write(byte(5));
lcd.print(" Tudo Certo!");
} else if (isAmbienteAlerta()) {
lcd.write(byte(5));
lcd.print(" Atencao!");
} else {
lcd.write(byte(5));
lcd.print(" Critico!");
}
}
// Exibe os dados no Serial Monitor com filtro opcional para mostrar apenas ALERTA e CRÍTICO
void exibirNoSerial(DateTime ajustado) {
if (filtrarSerial && isAmbienteOK()) {
return;
}
Serial.print(ajustado.year());
Serial.print("/");
if (ajustado.month() < 10) Serial.print("0");
Serial.print(ajustado.month());
Serial.print("/");
if (ajustado.day() < 10) Serial.print("0");
Serial.print(ajustado.day());
Serial.print(" ");
if (ajustado.hour() < 10) Serial.print("0");
Serial.print(ajustado.hour());
Serial.print(":");
if (ajustado.minute() < 10) Serial.print("0");
Serial.print(ajustado.minute());
Serial.print(":");
if (ajustado.second() < 10) Serial.print("0");
Serial.print(ajustado.second());
Serial.print("\t");
if (usarFahrenheit) {
float tempF = celsiusParaFahrenheit(temperatura);
if (tempF < 10) Serial.print(" ");
Serial.print(tempF, 1);
Serial.print("F\t");
} else {
if (temperatura < 10) Serial.print(" ");
Serial.print(temperatura, 1);
Serial.print("C\t");
}
if (umidade < 10) Serial.print(" ");
Serial.print(umidade, 1);
Serial.print("%\t");
if (luminosidade < 10) Serial.print(" ");
Serial.print(luminosidade);
Serial.print("%\t");
if (isAmbienteOK()) {
Serial.println("OK");
} else if (isAmbienteAlerta()) {
Serial.println("ALERTA");
} else {
Serial.println("CRITICO");
}
}
// Calcula o próximo endereço da EEPROM (buffer circular)
void getNextAddress() {
currentAddress += recordSize;
if (currentAddress >= endAddress) {
currentAddress = 0;
}
}
// Salva os dados na EEPROM
void salvarNaEEPROM(DateTime timestamp) {
int tempInt = (int)(temperatura * 100);
int umidInt = (int)(umidade * 100);
int lumInt = luminosidade;
EEPROM.put(currentAddress, timestamp.unixtime());
EEPROM.put(currentAddress + 4, tempInt);
EEPROM.put(currentAddress + 6, umidInt);
EEPROM.put(currentAddress + 8, lumInt);
getNextAddress();
}
// Lê e exibe os logs armazenados na EEPROM
void get_log() {
Serial.println("\n=== LOGS ARMAZENADOS NA EEPROM ===");
Serial.println("Timestamp\t\t\tTemp\tUmid\tLuz");
Serial.println("---------------------------------------------------");
for (int address = startAddress; address < endAddress; address += recordSize) {
unsigned long timeStamp;
int tempInt, umidInt, lumInt;
EEPROM.get(address, timeStamp);
EEPROM.get(address + 4, tempInt);
EEPROM.get(address + 6, umidInt);
EEPROM.get(address + 8, lumInt);
if (timeStamp != 0xFFFFFFFF && timeStamp != 0) {
float temperature = tempInt / 100.0;
float humidity = umidInt / 100.0;
DateTime dt = DateTime(timeStamp);
Serial.print(dt.timestamp(DateTime::TIMESTAMP_FULL));
Serial.print("\t");
Serial.print(temperature, 1);
Serial.print("C\t");
Serial.print(humidity, 1);
Serial.print("%\t");
Serial.print(lumInt);
Serial.println("%");
}
}
Serial.println("===================================\n");
}
void setup() {
pinMode(ledVerde, OUTPUT);
pinMode(ledAmarelo, OUTPUT);
pinMode(ledVermelho, OUTPUT);
pinMode(buzzer, OUTPUT);
pinMode(pinoLDR, INPUT);
pinMode(buttonPin, INPUT);
pinMode(filterButtonPin, INPUT);
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
dht.begin();
rtc.begin();
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
lcd.init();
lcd.backlight();
lcd.createChar(0, uva);
lcd.createChar(1, folha);
telaBoasVindas();
for (int i = 0; i < numLeituras; i++) {
leituras[i] = 0;
}
if (LOG_OPTION) {
get_log();
}
Serial.println("Sistema iniciado!");
Serial.println("Botao pino 12: Alterna Celsius/Fahrenheit");
Serial.println("Botao pino 11: Filtra Serial Monitor (apenas ALERTA/CRITICO)");
Serial.println("Timestamp\t\tTemp\tUmid\tLuz\tStatus");
}
void loop() {
lerBotao();
lerBotaoFiltro();
DateTime now = rtc.now();
int offsetSeconds = UTC_OFFSET * 3600;
DateTime adjustedTime = DateTime(now.unixtime() + offsetSeconds);
lerSensores();
exibirNoLCD();
if (SERIAL_OPTION) {
exibirNoSerial(adjustedTime);
}
verificarCondicoes();
controlarBuzzer();
if (millis() - ultimoRegistroEEPROM >= 30000) {
if (isAmbienteAlerta() || isAmbienteCritico()) {
salvarNaEEPROM(adjustedTime);
Serial.println(" --> REGISTRADO NA EEPROM");
ultimoRegistroEEPROM = millis();
}
}
delay(5000); // Atualiza a cada 5 segundos
}