#include <EEPROM.h>
#include <DHT.h>
#include <Servo.h>
#include "pitches.h"
#include <Wire.h> // Necessário para o RTC
#include "RTClib.h" // Biblioteca para o RTC
#include <LiquidCrystal.h>
#define DHTPIN 2
#define DHTTYPE DHT22
#define RELAY_HEAT 3
#define FAN_PIN 4
#define SERVO_PIN 5
#define SPEAKER_PIN 6
// Pinos do LCD
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
DHT dht(DHTPIN, DHTTYPE);
Servo virador;
RTC_DS1307 rtc; // Objeto para o RTC
// Variáveis para as leituras e controle
float tempMin = 37.0;
float tempMax = 38.0;
float umidadeMin = 45.0;
float umidadeMax = 55.0;
// Variáveis de tempo
unsigned long ultimaVirada = 0;
unsigned long ultimoAlarmeTemp = 0;
unsigned long ultimoUpdateLCD = 0;
unsigned long eclode = 0;
unsigned long media = 0;
unsigned long dataInicioMillis;
bool alarmeDiaAtivado = false; // Flag para controlar se o alarme já tocou
// Constantes de tempo (em milissegundos)
const unsigned long intervaloVirada = 60UL * 1000UL; // 4 horas
const unsigned long alarmeTempDelay = 1UL * 60UL * 1000UL; // 5 minutos para alarme
const unsigned long updateLCDInterval = 2000UL; // Atualiza LCD a cada 2 segundos
const int tempoIncubacao = 20; // 21 dias para eclosão de ovos de galinha
const int DIAS_ALARME_ECLOSAO = 3; // Alerta de eclosão 3 dias antes
const int alarmeTones[] = { NOTE_G3, NOTE_C4, NOTE_E4, NOTE_G5};
const byte ledPins[] = {13};
// Endereços da EEPROM
const int ENDERECO_DATA_INICIO = 0; // Endereço para armazenar a data de início
void setup() {
Serial.begin(9600);
// Inicialização dos componentes
dht.begin();
pinMode(RELAY_HEAT, OUTPUT);
pinMode(FAN_PIN, OUTPUT);
pinMode(SPEAKER_PIN, OUTPUT);
virador.attach(SERVO_PIN);
pinMode(13, OUTPUT);
// Inicialização do LCD
lcd.begin(16, 2);
lcd.print("Iniciando...");
delay(2000);
// Inicialização do RTC
if (!rtc.begin()) {
lcd.clear();
lcd.print("Erro no RTC!");
while (1);
}
// Verifica se a data de início já foi salva na EEPROM
EEPROM.get(ENDERECO_DATA_INICIO, dataInicioMillis);
// Se o valor na EEPROM for 0, significa que é a primeira vez
// que o programa roda, então salva a data atual como a data de início
if (dataInicioMillis != 0) {
dataInicioMillis = rtc.now().unixtime();
EEPROM.put(ENDERECO_DATA_INICIO, dataInicioMillis);
lcd.clear();
lcd.print("Incubacao iniciada!");
delay(2000);
}
// Inicialização de estados
digitalWrite(RELAY_HEAT, LOW);
digitalWrite(FAN_PIN, LOW);
lcd.clear();
// Sincroniza o tempo de eclosão com a data de início
eclode = dataInicioMillis + (unsigned long)tempoIncubacao * 24 * 60 * 60;
}
/**
Lights the given LED and plays a suitable tone
*/
void lightLedAndPlayTone(byte ledIndex) {
digitalWrite(ledPins[ledIndex], HIGH);
tone(SPEAKER_PIN, alarmeTones[ledIndex]);
delay(300);
digitalWrite(ledPins[ledIndex], LOW);
noTone(SPEAKER_PIN);
}
void loop() {
DateTime now = rtc.now();
unsigned long tempoAtual = millis();
// --- Leitura do sensor e atualização do LCD (não bloqueante) ---
if (tempoAtual - ultimoUpdateLCD >= updateLCDInterval) {
// Código para leitura e controle de temperatura e umidade (sem alterações)
float temperatura = dht.readTemperature();
float umidade = dht.readHumidity();
if (isnan(temperatura) || isnan(umidade)) {
lcd.setCursor(0, 0);
lcd.print("Erro no sensor");
return;
}
lcd.setCursor(0, 0);
lcd.print("T:");
lcd.print(temperatura, 1);
lcd.print((char)223);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("U:");
lcd.print(umidade, 1);
lcd.print("%");
lcd.setCursor(8, 0);
lcd.print(now.hour(), DEC);
lcd.print(':');
lcd.print(now.minute(), DEC);
lcd.print(':');
lcd.print(now.second(), DEC);
lcd.print(" ");
// Controle de aquecimento
if (temperatura < tempMin) {
digitalWrite(RELAY_HEAT, HIGH);
//lcd.setCursor(12, 0);
//lcd.print("Aq");
} else if (temperatura > tempMax) {
digitalWrite(RELAY_HEAT, LOW);
//lcd.setCursor(12, 0);
//lcd.print(" ");
}
// Alarme de temperatura fora da média
if (dht.readTemperature() < tempMin || dht.readTemperature() > tempMax) {
if (millis() - ultimoAlarmeTemp > alarmeTempDelay) {
lcd.clear();
lcd.print("Ver: resistencia");
// Play a Wah-Wah-Wah-Wah sound
tone(SPEAKER_PIN, NOTE_DS5);
delay(300);
tone(SPEAKER_PIN, NOTE_D5);
delay(300);
tone(SPEAKER_PIN, NOTE_CS5);
delay(300);
for (byte i = 0; i < 10; i++) {
for (int pitch = -10; pitch <= 10; pitch++) {
tone(SPEAKER_PIN, NOTE_C5 + pitch);
delay(5);
}
lightLedAndPlayTone(SPEAKER_PIN);
}
noTone(SPEAKER_PIN);
delay(500);
}
} else {
ultimoAlarmeTemp = millis();
noTone(SPEAKER_PIN);
digitalWrite(13, LOW);
}
// Controle de ventilação
if (temperatura > tempMax || umidade > umidadeMax) {
digitalWrite(FAN_PIN, HIGH);
} else {
digitalWrite(FAN_PIN, LOW);
}
ultimoUpdateLCD = tempoAtual;
}
// --- Lógica de Alarme de Dia e Contagem de Dias ---
unsigned long tempoAtualUnix = rtc.now().unixtime();
long cronometro = (tempoAtualUnix - dataInicioMillis) / 86400;
long diasRestantes = (long)tempoIncubacao - cronometro;
// Alerta de alarme quando faltarem DIAS_ALARME_ECLOSAO dias
if (diasRestantes <= DIAS_ALARME_ECLOSAO && diasRestantes <= 0) {
// Só toca o alarme uma vez
if (!alarmeDiaAtivado) {
tone(SPEAKER_PIN, alarmeTones);
alarmeDiaAtivado = true;
Serial.println("Alarme de eclosao acionado!");
}
lcd.setCursor(8, 1);
lcd.print("ALARME");
} else if (diasRestantes <= 0) {
// Eclosão
lcd.setCursor(8, 1);
lcd.print("Eclode!");
} else {
// Contagem normal
lcd.setCursor(8, 1);
lcd.print("D:");
if (diasRestantes < 10) lcd.print("-");
lcd.print(diasRestantes);
alarmeDiaAtivado = false; // Reseta a flag caso a data mude
Serial.println(cronometro);
}
// --- Viragem dos ovos (não bloqueante) ---
if (tempoAtual - ultimaVirada >= intervaloVirada) {
virador.write(90); // Gira para 90 graus
delay(500);
virador.write(0); // Volta à posição inicial
ultimaVirada = tempoAtual;
Serial.println("Ovos virados!");
}
}