#include <EEPROM.h>
#include <DHT.h>
#include <Servo.h>
#include "AlarmTone.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define I2C_ADDR 0x27
#define LCD_COLUMNS 20
#define LCD_LINES 4
#define DHTPIN 2
#define DHTTYPE DHT22
#define RELAY_HEAT 3
#define FAN_PIN 4
#define UMIDIFICADOR_PIN 6 // Novo pino para o relé do umidificador
#define SPEAKER_PIN A3
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
DHT dht(DHTPIN, DHTTYPE);
AlarmTone alarmTone;
int pos = 0;
float tempMin = 37.0;
float tempMax = 38.0;
float umidadeMin = 45.0;
float umidadeMax = 55.0;
unsigned long media = 0;
unsigned long umedia = 0;
unsigned long ultimoUpdateLCD = 0;
const long intervalo = 1UL * 60UL * 1000UL; // 1 minutos (ajuste para TEMPO real de viragem)
const unsigned long tempMedia = 1UL * 60UL * 1000UL; // 1 minuto
const unsigned long umiMedia = 1UL * 60UL * 1000UL; // 1 minuto
const int TEMPO_INCUBACAO_DIAS = 21; // 21 dias para eclosão de ovos de galinha
const int DIAS_ALARME_ECLOSAO = 3; // Alerta 3 dias antes da eclosão
const unsigned long updateLCDInterval = 2000UL; // Atualiza LCD a cada 2 segundos
Servo meuServo;
unsigned long tempoAnterior = 0; // Armazena o último tempo que o servo se moveu
int estadoServo = 0; // 0 para 0 graus, 1 para 180 graus
// Contagem de dias sem RTC
unsigned long dataInicioMillis = 0;
const int ENDERECO_DATA_INICIO = 0;
void setup() {
pinMode(13, OUTPUT);
// EEPROM limpa apenas se necessário
for (int i = 0 ; i < EEPROM.length() ; i++) {
EEPROM.write(i, 0);
}
// Init
lcd.init();
lcd.backlight();
Serial.begin(9600);
lcd.begin(16, 2);
dht.begin();
pinMode(RELAY_HEAT, OUTPUT);
pinMode(FAN_PIN, OUTPUT);
pinMode(UMIDIFICADOR_PIN, OUTPUT); // Novo pino do umidificador
pinMode(13, OUTPUT);
meuServo.attach(5);
meuServo.write(0);
tempoAnterior = millis(); // Inicializa o tempo anterior
alarmTone.begin(SPEAKER_PIN);
lcd.setCursor(0, 0);
lcd.print("Incubadora");
delay(2000);
lcd.clear();
// Carrega a data de início da EEPROM
EEPROM.get(ENDERECO_DATA_INICIO, dataInicioMillis);
// Se o valor for 0, é a primeira inicialização. Salva o millis() atual.
if (dataInicioMillis == 0) {
dataInicioMillis = millis();
EEPROM.put(ENDERECO_DATA_INICIO, dataInicioMillis);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Incubacao ");
lcd.setCursor(0, 1);
lcd.print("iniciada!");
delay(2000);
lcd.clear();
}
}
void loop() {
unsigned long tempoAtual = millis();
// --- Leitura do sensor e atualização do LCD (não bloqueante) ---
if (tempoAtual - ultimoUpdateLCD >= updateLCDInterval) {
float temperatura = dht.readTemperature();
float umidade = dht.readHumidity();
if (isnan(temperatura) || isnan(umidade)) {
Serial.println("Falha na leitura do sensor DHT!");
lcd.setCursor(0, 0);
lcd.print("Erro no sensor");
delay(2000);
return;
}
// --- Lógica de Contagem de Dias e Alarme de Eclosão ---
long diasPassados = (tempoAtual - dataInicioMillis) / 86400000UL; // 86400000UL = milissegundos em um dia
long diasRestantes = TEMPO_INCUBACAO_DIAS - diasPassados;
lcd.setCursor(0, 0);
lcd.print("T:");
lcd.print(temperatura, 1); // Exibe com uma casa decimal
lcd.print((char)223); // Símbolo de grau
lcd.print("C");
lcd.setCursor(0, 1);
lcd.print("U:");
lcd.print(umidade, 1); // Exibe com uma casa decimal
lcd.print("%");
lcd.setCursor(9, 1);
lcd.print("D:");
if (diasRestantes < 10)lcd.print("-");
lcd.print(diasRestantes);
// Controle de aquecimento
if (temperatura < tempMin) {
digitalWrite(RELAY_HEAT, HIGH);
lcd.setCursor(12, 0);
lcd.print(" Aqc");
} else if (temperatura > tempMax) {
digitalWrite(RELAY_HEAT, LOW);
lcd.setCursor(12, 0);
lcd.print(" ");
}
// Controle de ventilação
if (temperatura > tempMax || umidade > umidadeMax) {
digitalWrite(FAN_PIN, HIGH);
digitalWrite(UMIDIFICADOR_PIN, HIGH);
lcd.setCursor(13, 1);
lcd.print(" Vt");
} else {
digitalWrite(FAN_PIN, LOW);
digitalWrite(UMIDIFICADOR_PIN, HIGH);
lcd.setCursor(13, 1);
lcd.print(" ");
}
// Controle de umidade
if (umidade > umidadeMin - 5 && umidade < umidadeMax) {
analogWrite(UMIDIFICADOR_PIN, 1);
delayMicroseconds(random(15000));
analogWrite(UMIDIFICADOR_PIN, 10);
delayMicroseconds(random(15000));
analogWrite(UMIDIFICADOR_PIN, 100);
delayMicroseconds(random(15000));
analogWrite(UMIDIFICADOR_PIN, 255); // 100% duty cycle
delayMicroseconds(random(15000));
lcd.setCursor(8, 0);
lcd.print(" Umd");
} else {
analogWrite(UMIDIFICADOR_PIN, 0);
lcd.setCursor(8, 0);
lcd.print(" ");
}
// Alarme de temperatura fora da média
if (temperatura < tempMin || temperatura > tempMax) {
if (millis() - media > tempMedia) {
alarmTone.play();
analogWrite(13, 255);
}
} else {
media = millis();
alarmTone.stop();
analogWrite(13, 0);
}
// --- Alerta de eclosão (opcional, pode ser removido) ---
if (diasRestantes <= 3) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Eclodindo: pinto");
lcd.setCursor(0, 1);
lcd.print("Aguarde: Verifica");
// Não atualiza eclode para manter a mensagem
}
// Alarme de umidade fora da média
if (umidade < umidadeMin) {
if (millis() - umedia > umiMedia) {
alarmTone.play();
}
} else{
if (umidade >= umidadeMax){
umedia = millis();
alarmTone.stop();
}
}
ultimoUpdateLCD = tempoAtual;
}
// Viragem dos ovos
unsigned long tempAtual = millis();
// Se o tempo atual menos o tempo anterior for maior que o intervalo,
// é hora de mover o servo
if (tempAtual - tempoAnterior >= intervalo) {
tempoAnterior = tempAtual; // Atualiza o tempo anterior
if (estadoServo == 0) {
meuServo.write(180);
estadoServo = 1;
} else {
meuServo.write(0);
estadoServo = 0;
}
}
delay(1000); // 1 segundo
}