#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <RTClib.h>
#include <EEPROM.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
RTC_DS3231 rtc;
// Pinos
#define PIN_UP 2
#define PIN_DOWN 3
#define PIN_SET 4
#define PIN_FEED 5
#define RELAY_PIN 6
#define BUZZER_PIN 7 // <-- Novo pino para o buzzer
// Estrutura de horários
struct FeedTime {
int hour;
int minute;
};
FeedTime feedTimes[3];
unsigned long relayTime = 2000;
bool editing = false;
int mode = 0;
int menuIndex = 0;
int menuTop = 0;
bool menuNeedsRedraw = true;
DateTime now;
int lastFeedHour = -1;
int lastFeedMinute = -1;
String lastFeedInfo = "Nada ainda";
const char* menuItems[] = {
"1. Ajustar Data/Hora",
"2. Alimentacao 1",
"3. Alimentacao 2",
"4. Alimentacao 3",
"5. Tempo do Rele",
"6. Alimentar Agora",
"7. Voltar"
};
int totalMenus = sizeof(menuItems) / sizeof(menuItems[0]);
#define EEPROM_ADDR_RELAY 0
#define EEPROM_ADDR_FEED1 10
#define EEPROM_ADDR_FEED2 20
#define EEPROM_ADDR_FEED3 30
// ---------- Funções auxiliares ----------
void beep(int n = 1, int dur = 80) {
for (int i = 0; i < n; i++) {
tone(BUZZER_PIN, 2000);
delay(dur);
noTone(BUZZER_PIN);
delay(60);
}
}
void saveFeedTimeToEEPROM(int index, FeedTime t) {
int addr = EEPROM_ADDR_FEED1 + index * 10;
EEPROM.put(addr, t);
}
FeedTime readFeedTimeFromEEPROM(int index) {
FeedTime t;
int addr = EEPROM_ADDR_FEED1 + index * 10;
EEPROM.get(addr, t);
if (t.hour < 0 || t.hour > 23 || t.minute < 0 || t.minute > 59) {
t.hour = 8 + index * 4;
t.minute = 0;
}
return t;
}
void saveRelayTimeToEEPROM() { EEPROM.put(EEPROM_ADDR_RELAY, relayTime); }
unsigned long readRelayTimeFromEEPROM() {
unsigned long t;
EEPROM.get(EEPROM_ADDR_RELAY, t);
if (t < 500 || t > 10000) t = 2000;
return t;
}
bool buttonPressed(int pin) {
if (digitalRead(pin) == LOW) {
delay(40);
if (digitalRead(pin) == LOW) {
while (digitalRead(pin) == LOW);
delay(40);
return true;
}
}
return false;
}
// ---------- Telas ----------
void drawMainScreen() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("*Alimentador Peixes*");
lcd.setCursor(0, 1);
lcd.print("Data: ");
lcd.print(now.day()); lcd.print("/");
lcd.print(now.month()); lcd.print("/");
lcd.print(now.year());
lcd.setCursor(0, 2);
lcd.print("Hora: ");
if (now.hour() < 10) lcd.print("0");
lcd.print(now.hour());
lcd.print(":");
if (now.minute() < 10) lcd.print("0");
lcd.print(now.minute());
lcd.setCursor(0, 3);
lcd.print("Ultima: ");
lcd.print(lastFeedInfo);
}
void drawMenu() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Menu Principal ");
int visibleItems = 3;
if (menuIndex < menuTop) menuTop = menuIndex;
else if (menuIndex >= menuTop + visibleItems) menuTop = menuIndex - visibleItems + 1;
for (int i = 0; i < visibleItems; i++) {
int itemIndex = menuTop + i;
if (itemIndex >= totalMenus) break;
lcd.setCursor(0, i + 1);
lcd.print((itemIndex == menuIndex) ? ">" : " ");
lcd.print(menuItems[itemIndex]);
}
}
// ---------- Edição ----------
void editDateTime() {
editing = true;
lcd.clear();
int dia = now.day();
int mes = now.month();
int ano = now.year();
int hora = now.hour();
int minuto = now.minute();
int etapa = 0;
while (editing) {
lcd.setCursor(0, 0);
lcd.print(" Ajustar Data/Hora ");
lcd.setCursor(0, 1);
lcd.print("Data: ");
lcd.print(dia < 10 ? "0" : ""); lcd.print(dia);
lcd.print("/"); lcd.print(mes < 10 ? "0" : ""); lcd.print(mes);
lcd.print("/"); lcd.print(ano);
lcd.setCursor(0, 2);
lcd.print("Hora: ");
lcd.print(hora < 10 ? "0" : ""); lcd.print(hora);
lcd.print(":"); lcd.print(minuto < 10 ? "0" : ""); lcd.print(minuto);
lcd.setCursor(0, 3);
const char* campos[] = {"Dia", "Mes", "Ano", "Hora", "Minuto"};
lcd.print("Ajustando: "); lcd.print(campos[etapa]); lcd.print(" ");
if (buttonPressed(PIN_UP)) {
beep();
switch (etapa) {
case 0: dia = (dia < 31) ? dia + 1 : 1; break;
case 1: mes = (mes < 12) ? mes + 1 : 1; break;
case 2: ano++; break;
case 3: hora = (hora < 23) ? hora + 1 : 0; break;
case 4: minuto = (minuto < 59) ? minuto + 1 : 0; break;
}
}
if (buttonPressed(PIN_DOWN)) {
beep();
switch (etapa) {
case 0: dia = (dia > 1) ? dia - 1 : 31; break;
case 1: mes = (mes > 1) ? mes - 1 : 12; break;
case 2: ano--; break;
case 3: hora = (hora > 0) ? hora - 1 : 23; break;
case 4: minuto = (minuto > 0) ? minuto - 1 : 59; break;
}
}
if (buttonPressed(PIN_SET)) {
etapa++;
beep();
if (etapa > 4) {
rtc.adjust(DateTime(ano, mes, dia, hora, minuto, 0));
lcd.clear();
lcd.print(" Data/Hora salva! ");
beep(3, 200);
delay(2000);
editing = false;
}
}
}
}
void editFeedTime(int index) {
editing = true;
int hora = feedTimes[index].hour;
int minuto = feedTimes[index].minute;
int etapa = 0;
while (editing) {
lcd.setCursor(0, 0);
lcd.print("Ajuste Alimentacao ");
lcd.print(index + 1);
lcd.setCursor(0, 1);
lcd.print("Hora: ");
lcd.print(hora < 10 ? "0" : ""); lcd.print(hora);
lcd.print(":");
lcd.print(minuto < 10 ? "0" : ""); lcd.print(minuto);
lcd.setCursor(0, 3);
lcd.print(etapa == 0 ? "Ajustando: Hora " : "Ajustando: Minuto ");
if (buttonPressed(PIN_UP)) {
beep();
if (etapa == 0) hora = (hora < 23) ? hora + 1 : 0;
else minuto = (minuto < 59) ? minuto + 1 : 0;
}
if (buttonPressed(PIN_DOWN)) {
beep();
if (etapa == 0) hora = (hora > 0) ? hora - 1 : 23;
else minuto = (minuto > 0) ? minuto - 1 : 59;
}
if (buttonPressed(PIN_SET)) {
etapa++;
beep();
if (etapa > 1) {
feedTimes[index].hour = hora;
feedTimes[index].minute = minuto;
saveFeedTimeToEEPROM(index, feedTimes[index]);
lcd.clear();
lcd.print("Horario salvo!");
beep(2, 100);
delay(1000);
editing = false;
}
}
}
}
void editRelayTime() {
editing = true;
while (editing) {
lcd.setCursor(0, 0);
lcd.print("Tempo do Rele:");
lcd.setCursor(0, 1);
lcd.print(relayTime);
lcd.print(" ms ");
if (buttonPressed(PIN_UP)) {
relayTime += 500;
beep();
}
if (buttonPressed(PIN_DOWN) && relayTime > 500) {
relayTime -= 500;
beep();
}
if (buttonPressed(PIN_SET)) {
saveRelayTimeToEEPROM();
lcd.clear();
lcd.print("Tempo salvo!");
beep(2, 100);
delay(1000);
editing = false;
}
}
}
// ---------- Alimentação ----------
void feedNowAuto(int index) {
lcd.clear();
lcd.print("Alimentacao ");
lcd.print(index + 1);
lcd.setCursor(0, 1);
lcd.print("Executando...");
beep();
digitalWrite(RELAY_PIN, HIGH);
delay(relayTime);
digitalWrite(RELAY_PIN, LOW);
beep(2);
lcd.setCursor(0, 2);
lcd.print("Concluido!");
lastFeedInfo = String(index + 1) + " (" + String(now.hour()) + ":" + (now.minute() < 10 ? "0" : "") + String(now.minute()) + ")";
delay(1500);
drawMainScreen();
}
void feedNow() {
lcd.clear();
lcd.print("....Alimentando....");
beep();
digitalWrite(RELAY_PIN, HIGH);
delay(relayTime);
digitalWrite(RELAY_PIN, LOW);
beep(2);
lcd.clear();
lcd.print(".....Concluido!.....");
lastFeedInfo = "Manu (" + String(now.hour()) + ":" + (now.minute() < 10 ? "0" : "") + String(now.minute()) + ")";
delay(1000);
}
// ---------- Setup ----------
void setup() {
pinMode(PIN_UP, INPUT_PULLUP);
pinMode(PIN_DOWN, INPUT_PULLUP);
pinMode(PIN_SET, INPUT_PULLUP);
pinMode(PIN_FEED, INPUT_PULLUP);
pinMode(RELAY_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
lcd.init();
lcd.backlight();
rtc.begin();
feedTimes[0] = readFeedTimeFromEEPROM(0);
feedTimes[1] = readFeedTimeFromEEPROM(1);
feedTimes[2] = readFeedTimeFromEEPROM(2);
relayTime = readRelayTimeFromEEPROM();
beep(2);
drawMainScreen();
delay(1000);
}
// ---------- Loop ----------
void loop() {
now = rtc.now();
// Tela principal
if (!editing && mode == 0) {
static unsigned long lastUpdate = 0;
if (millis() - lastUpdate > 1000) {
drawMainScreen();
lastUpdate = millis();
}
// Alimentação automática
for (int i = 0; i < 3; i++) {
if (now.hour() == feedTimes[i].hour &&
now.minute() == feedTimes[i].minute &&
!(lastFeedHour == now.hour() && lastFeedMinute == now.minute())) {
feedNowAuto(i);
lastFeedHour = now.hour();
lastFeedMinute = now.minute();
}
}
if (buttonPressed(PIN_SET)) {
beep();
mode = 1;
menuIndex = 0;
menuTop = 0;
menuNeedsRedraw = true;
lcd.clear();
return;
}
if (buttonPressed(PIN_FEED)) {
feedNow();
drawMainScreen();
return;
}
}
// Menu principal
else if (!editing && mode == 1) {
if (menuNeedsRedraw) {
drawMenu();
menuNeedsRedraw = false;
}
if (buttonPressed(PIN_UP)) {
beep();
menuIndex = (menuIndex == 0) ? totalMenus - 1 : menuIndex - 1;
menuNeedsRedraw = true;
}
if (buttonPressed(PIN_DOWN)) {
beep();
menuIndex = (menuIndex + 1) % totalMenus;
menuNeedsRedraw = true;
}
if (buttonPressed(PIN_SET)) {
beep();
lcd.clear();
switch (menuIndex) {
case 0: editDateTime(); break;
case 1: editFeedTime(0); break;
case 2: editFeedTime(1); break;
case 3: editFeedTime(2); break;
case 4: editRelayTime(); break;
case 5: feedNow(); break;
case 6: mode = 0; break; // Voltar
}
mode = 0;
editing = false;
menuIndex = 0;
menuTop = 0;
lcd.clear();
drawMainScreen();
delay(500);
return;
}
if (buttonPressed(PIN_FEED)) {
beep();
mode = 0;
lcd.clear();
drawMainScreen();
delay(300);
return;
}
}
}