#include <Servo.h>
#include <RTClib.h>
#include <HX711.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
int s = 10;
Servo servo;
int led = 11;
int trigger = 12;
int echo = 13;
RTC_DS1307 rtc;
HX711 scale;
LiquidCrystal_I2C lcd(0x27, 20, 4);
const uint8_t ROWS = 4;
const uint8_t COLS = 4;
char keys[ROWS][COLS] =
{
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
};
uint8_t colPins[COLS] = { 5, 4, 3, 2 }; //C1, C2, C3, C4
uint8_t rowPins[ROWS] = { 9, 8, 7, 6 }; //R1, R2, R3, R4
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
bool screenOn = false;
unsigned long LastKeyPress = 0;
int lastFedTime = -1;
struct MealSlot
{
bool active;
int hour;
int minute;
int grams;
};
MealSlot meals[4] =
{
{false, 0, 0, 0},
{false, 0, 0, 0},
{false, 0, 0, 0},
{false, 0, 0, 0}
};
void setup()
{
//Serial.begin(9600);
pinMode(led,OUTPUT);
pinMode(trigger, OUTPUT);
pinMode(echo, INPUT);
servo.attach(s);
servo.write(0);
rtc.begin();
DateTime now = rtc.now();
scale.begin(A3, A2);
scale.set_scale(0.42);
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Pet Feeder");
lcd.setCursor(0, 1);
lcd.print("Current time: ");
if(now.hour() < 10) lcd.print("0");
lcd.print(now.hour());
lcd.print(":");
if(now.minute() < 10) lcd.print("0");
lcd.print(now.minute());
delay(2000);
lcd.clear();
lcd.noBacklight();
scale.tare();
}
void loop()
{
char key = keypad.getKey();
DateTime now = rtc.now();
int weight = scale.get_units(1);
if (key)
{
if (!screenOn)
{
lcd.backlight();
screenOn = true;
LastKeyPress = millis();
key = 0;
HomeScreen();
}
else
{
LastKeyPress = millis();
}
}
if (screenOn && millis() - LastKeyPress > 20000)
{
lcd.clear();
lcd.noBacklight();
screenOn = false;
}
if(screenOn)
{
if(key == 'A')//Calibrate meal time
MealTime();
else if(key == 'B')//Calibrate time
Time();
else if(key == 'C')// Calibrate bowl weight
Bowl();
else if (key == 'D')//Release Food
Button();
}
int currentTime = now.hour() * 60 + now.minute();
for(int i = 0; i < 4; i++)
{
int slotTime = meals[i].hour * 60 + meals[i].minute;
if(meals[i].active &&
currentTime == slotTime &&
lastFedTime != currentTime)
{
lastFedTime = currentTime;
unsigned long feedStart = millis();
while(weight < meals[i].grams && millis() - feedStart < 15000)
{
servo.write(90);
weight = scale.get_units(1);
}
servo.write(0);
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Auto-feeding");
lcd.setCursor(0, 1);
lcd.print("Dispensed :");
lcd.print(weight);
lcd.print("g");
delay(2000);
lcd.noBacklight();
lcd.clear();
break;
}
}
digitalWrite(trigger, HIGH);
delayMicroseconds(10);
digitalWrite(trigger, LOW);
long duration = pulseIn(echo, HIGH,5000);
int distance = duration / 58;
if(distance>50)
digitalWrite(led, HIGH);
else
digitalWrite(led, LOW);
}
void HomeScreen()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Press to Calibrate:");
lcd.setCursor(0, 1);
lcd.print("A-Meal Time, B-Time");
lcd.setCursor(0, 2);
lcd.print("C-Bowl Weight");
lcd.setCursor(0, 3);
lcd.print("D-Dispense Food");
}
void MealTime()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Select Empty to Add");
lcd.setCursor(0, 1);
lcd.print("or Existing to Remove");
lcd.setCursor(0, 3);
lcd.print("# Proceed / * Cancel");
char key = 0;
int slot = -1;
while(1)
{
key = keypad.getKey();
if(key)
LastKeyPress = millis();
if(key == '*')
{
HomeScreen();
return;
}
if(key == '#')
break;
}
ShowSlots();
key = 0;
while(1)
{
key = keypad.getKey();
if(key)
LastKeyPress = millis();
if(key == '*')
{
HomeScreen();
return;
}
if (key == 'A')
slot = 0;
else if(key == 'B')
slot = 1;
else if(key == 'C')
slot = 2;
else if(key == 'D')
slot = 3;
if(slot >= 0)
{
if(!meals[slot].active)//figure out what this does
AddMeal(slot);
else
RemoveMeal(slot);
return;
}
}
}
void Time()
{
DateTime now = rtc.now();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Current time: ");
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, 1);
lcd.print("Enter HH:MM (24H)");
lcd.setCursor(0, 2);
lcd.print("# Confirm / * Cancel");
lcd.setCursor(0, 3);
lcd.print("> ");
int digit[4];
int count = 0;
char key = 0;
while(1)
{
key = keypad.getKey();
if(key)
LastKeyPress = millis();
if(key >= '0' && key <= '9' && count < 4)
{
digit[count] = key - '0';
count++;
lcd.setCursor(2, 3);
for(int i = 0; i < count; i++)
{
lcd.print(digit[i]);
if(i==1)
lcd.print(":");
}
}
else if(key == '*')
{
HomeScreen();
return;
}
else if(key == '#' && count == 4)
break;
}
int hour = digit[0]*10 + digit[1];
int minutes = digit[2]*10 + digit[3];
if(hour < 24 && minutes < 60)
{
rtc.adjust(DateTime(2000, 1, 1, hour, minutes, 0));
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Time set to: ");
if(hour < 10)
lcd.print("0");
lcd.print(hour);
lcd.print(":");
if(minutes < 10)
lcd.print("0");
lcd.print(minutes);
}
else
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Invalid! Try again");
}
delay(500);
HomeScreen();
}
void Bowl()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Place empty bowl");
lcd.setCursor(0, 1);
lcd.print("Press C to Calibrate");
lcd.setCursor(0, 2);
lcd.print("Or * to Cancel");
char key = 0;
while(key!='C')
{
key = keypad.getKey();
if(key)
LastKeyPress = millis();
if(key == '*')
{
HomeScreen();
return;
}
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Calibration Complete");
scale.tare();
delay(500);
HomeScreen();
}
void Button()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Dispensing food");
lcd.setCursor(0, 1);
lcd.print("Press * to stop");
char key = 0;
while(key !='*')
{
key = keypad.getKey();
if(key)
LastKeyPress = millis();
servo.write(90);
int weight = scale.get_units(1);
lcd.setCursor(0, 2);
lcd.print(weight);
lcd.setCursor(4, 2);
lcd.print("g");
}
servo.write(0);
delay(500);
HomeScreen();
}
void ShowSlots()
{
lcd.clear();
char letters[] = {'A', 'B', 'C', 'D'};
for(int i = 0; i < 4; i++)
{
lcd.setCursor(0, i);
lcd.print(letters[i]);
lcd.print(": ");
if(meals[i].active)
{
if(meals[i].hour < 10)
lcd.print("0");
lcd.print(meals[i].hour);
lcd.print(":");
if(meals[i].minute < 10)
lcd.print("0");
lcd.print(meals[i].minute);
lcd.print(" ");
lcd.print(meals[i].grams);
lcd.print("g");
}
else
lcd.print("--:-- ---g");
}
}
void AddMeal(int slot)
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Enter HH:MM (24H)");
lcd.setCursor(0, 2);
lcd.print("# Confirm / * Cancel");
lcd.setCursor(0, 3);
lcd.print("> ");
int digit[4];
int count = 0;
char key = 0;
while(true)
{
key = keypad.getKey();
if(key)
LastKeyPress = millis();
if(key >= '0' && key <= '9' && count < 4)
{
digit[count] = key - '0';//find what this does
count++;
lcd.setCursor(2, 3);
for(int i = 0; i < count; i++)
{
lcd.print(digit[i]);
if(i == 1) lcd.print(":");
}
}
else if(key == '*')
{
HomeScreen();
return;
}
else if(key == '#' && count == 4)
break;
}
int hour = digit[0]*10 + digit[1];
int minutes = digit[2]*10 + digit[3];
if(hour >= 24 || minutes >= 60)
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Invalid time!");
delay(1000);
HomeScreen();
return;
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Enter grams");
lcd.setCursor(0, 2);
lcd.print("# Confirm / * Cancel");
lcd.setCursor(0, 3);
lcd.print("> ");
int gramDigit[3];//maybe 4 but dont want to mess everything up
int gramCount = 0;
key = 0;
while(true) {
key = keypad.getKey();
if(key)
LastKeyPress = millis();
if(key >= '0' && key <= '9' && gramCount < 3)
{
gramDigit[gramCount] = key - '0';
gramCount++;
lcd.setCursor(2, 3);
for(int i = 0; i < gramCount; i++)
lcd.print(gramDigit[i]);
}
else if(key == '*')
{
HomeScreen();
return;
}
else if(key == '#' && gramCount > 0)
break;
}
// Υπολογισμός γραμμαρίων από ψηφία
int grams = 0;
for(int i = 0; i < gramCount; i++)
grams = grams * 10 + gramDigit[i];
meals[slot].active = true;
meals[slot].hour = hour;
meals[slot].minute = minutes;
meals[slot].grams = grams;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Meal added!");
lcd.setCursor(0, 1);
if(hour < 10) lcd.print("0");
lcd.print(hour);
lcd.print(":");
if(minutes < 10) lcd.print("0");
lcd.print(minutes);
lcd.print(" ");
lcd.print(grams);
lcd.print("g");
delay(1500);
HomeScreen();
}
void RemoveMeal(int slot)
{
char letters[] = {'A', 'B', 'C', 'D'};
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Remove slot ");
lcd.print(letters[slot]);
lcd.print("?");
lcd.setCursor(0, 1);
if(meals[slot].hour < 10)
lcd.print("0");
lcd.print(meals[slot].hour);
lcd.print(":");
if(meals[slot].minute < 10)
lcd.print("0");
lcd.print(meals[slot].minute);
lcd.print(" ");
lcd.print(meals[slot].grams);
lcd.print("g");
lcd.setCursor(0, 3);
lcd.print("# Confirm / * Cancel");
char key = 0;
while(1)
{
key = keypad.getKey();
if(key)
LastKeyPress = millis();
if(key == '#')
{
meals[slot].active = false;
meals[slot].hour = 0;
meals[slot].minute = 0;
meals[slot].grams = 0;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Slot ");
lcd.print(letters[slot]);
lcd.print(" removed!");
delay(1000);
HomeScreen();
return;
}
else if(key == '*')
{
HomeScreen();
return;
}
}
}