/*
Automatic Pet Feeder with Load Cell, RTC, and A4988 Stepper Motor Driver
---------------------------------------------------------
🛠️ Designed by: Technical Shubham
🤖 Code generated with the help of ChatGPT (OpenAI)
Features:
- HX711 load cell for weight detection
- RTC DS1307 for real-time clock scheduling
- Manual and scheduled feeding
- EEPROM memory for saving settings
- A4988 driver-based stepper motor control
- 16x2 I2C LCD Display
- Mode navigation with 5 push buttons
*/
#include "HX711.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <RTClib.h>
#include <AccelStepper.h>
#include <EEPROM.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
// HX711
#define DOUT 11
#define CLK 10
// Buttons
#define BT1 A0
#define BT2 A1
#define BT3 A2
#define BT4 A3
#define BT5 A4
// Stepper motor with A4988
#define STEP_PIN 6
#define DIR_PIN 7
AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);
HX711 scale;
RTC_DS1307 rtc;
// EEPROM addresses
#define ADDR_HOUR 0
#define ADDR_MINUTE 1
#define ADDR_GRAMS 2
int mode = 0;
int setHour = 0;
int setMinute = 0;
int setgrams = 0;
bool alarmTriggered = false;
bool manualFeedTriggered = false;
bool isManualFeeding = false;
unsigned long manualFeedStartTime = 0;
const unsigned long manualFeedDisplayDuration = 2000;
unsigned long lastDebounceTime1 = 0;
unsigned long lastDebounceTime2 = 0;
unsigned long lastDebounceTime3 = 0;
unsigned long lastDebounceTime4 = 0;
unsigned long lastDebounceTime5 = 0;
const unsigned long debounceDelay = 150;
unsigned long lastLCDUpdate = 0;
const unsigned long lcdUpdateInterval = 500;
bool lcdOn = false;
DateTime now;
// -------------------- EEPROM Handling ------------------
void saveSettingsToEEPROM() {
EEPROM.write(ADDR_HOUR, setHour);
EEPROM.write(ADDR_MINUTE, setMinute);
EEPROM.write(ADDR_GRAMS, setgrams);
}
void loadSettingsFromEEPROM() {
setHour = EEPROM.read(ADDR_HOUR);
setMinute = EEPROM.read(ADDR_MINUTE);
setgrams = EEPROM.read(ADDR_GRAMS);
if (setHour > 23) setHour = 0;
if (setMinute > 59) setMinute = 0;
if (setgrams > 100) setgrams = 0;
}
// -------------------- Motor Feed ------------------
void feedMotor(int grams) {
int stepsPerGram = 20; // Adjust for calibration
int totalSteps = grams * stepsPerGram;
stepper.setMaxSpeed(1000);
stepper.setAcceleration(500);
stepper.moveTo(totalSteps);
while (stepper.distanceToGo() != 0) {
stepper.run();
}
stepper.setCurrentPosition(0);
delay(200);
}
// -------------------- LCD Clock Display ------------------
void print_hour() {
lcd.setCursor(0, 3);
lcd.print(" ");
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.print(":");
if (now.second() < 10) lcd.print("0");
lcd.print(now.second());
lcd.print(" ");
}
// -------------------- Setup ------------------
void setup() {
scale.begin(DOUT, CLK);
pinMode(BT1, INPUT_PULLUP);
pinMode(BT2, INPUT_PULLUP);
pinMode(BT3, INPUT_PULLUP);
pinMode(BT4, INPUT_PULLUP);
pinMode(BT5, INPUT_PULLUP);
stepper.setMaxSpeed(1000);
stepper.setAcceleration(500);
stepper.setCurrentPosition(0);
lcd.init();
lcd.backlight();
loadSettingsFromEEPROM();
if (!rtc.begin()) {
lcd.setCursor(0, 0);
lcd.print("RTC Error");
while (1);
}
}
// -------------------- Loop ------------------
void loop() {
now = rtc.now();
// Toggle LCD ON/OFF
if (digitalRead(BT4) == LOW && millis() - lastDebounceTime4 > debounceDelay) {
lcdOn = !lcdOn;
if (lcdOn) {
lcd.init();
lcd.backlight();
lcd.clear();
} else {
lcd.noBacklight();
lcd.clear();
}
lastDebounceTime4 = millis();
}
if (!lcdOn) return;
// Mode Change
if (digitalRead(BT1) == LOW && millis() - lastDebounceTime1 > debounceDelay) {
mode = (mode + 1) % 4;
lcd.clear();
lastDebounceTime1 = millis();
}
// Increase
if (digitalRead(BT2) == LOW && millis() - lastDebounceTime2 > debounceDelay) {
lastDebounceTime2 = millis();
if (mode == 1) {
if (setHour < 23) setHour++; else setHour = 0;
} else if (mode == 2) {
if (setMinute < 59) setMinute++; else setMinute = 0;
} else if (mode == 3 && setgrams < 100) setgrams++;
saveSettingsToEEPROM();
}
// Decrease
if (digitalRead(BT3) == LOW && millis() - lastDebounceTime3 > debounceDelay) {
lastDebounceTime3 = millis();
if (mode == 1) {
if (setHour > 0) setHour--; else setHour = 23;
} else if (mode == 2) {
if (setMinute > 0) setMinute--; else setMinute = 59;
} else if (mode == 3 && setgrams > 0) setgrams--;
saveSettingsToEEPROM();
}
// Manual Feed
if (digitalRead(BT5) == LOW && millis() - lastDebounceTime5 > debounceDelay) {
manualFeedTriggered = true;
isManualFeeding = true;
manualFeedStartTime = millis();
lastDebounceTime5 = millis();
}
// LCD Update Section
if (millis() - lastLCDUpdate > lcdUpdateInterval) {
lastLCDUpdate = millis();
if (isManualFeeding && (millis() - manualFeedStartTime < manualFeedDisplayDuration)) {
lcd.setCursor(0, 0);
lcd.print("Manual Feed Running");
lcd.setCursor(0, 1);
lcd.print("Dispensing ");
lcd.print(setgrams);
lcd.print("g ");
lcd.setCursor(0, 2);
lcd.print("Please wait... ");
print_hour();
return;
} else if (isManualFeeding) {
isManualFeeding = false;
}
if (scale.is_ready()) {
float weight = scale.get_units() * 2.380952380952381;
int intWeight = int(weight);
int percent = (intWeight * 2.0) / 100;
if (mode == 0) {
lcd.setCursor(0, 0);
if (intWeight == 0) lcd.print("Please refill! ");
else if (intWeight < 500) lcd.print("Below 500g ");
else {
lcd.print("Weight: ");
lcd.print(weight / 1000.0, 2);
lcd.print("kg ");
}
lcd.setCursor(0, 1);
lcd.print("Grams: ");
lcd.print(intWeight);
lcd.print(" g ");
lcd.setCursor(0, 2);
lcd.print("Level: ");
lcd.print(percent);
lcd.print("% ");
print_hour();
if ((now.hour() == setHour && now.minute() == setMinute && !alarmTriggered) || manualFeedTriggered) {
feedMotor(setgrams);
alarmTriggered = true;
manualFeedTriggered = false;
} else if (now.hour() != setHour || now.minute() != setMinute) {
alarmTriggered = false;
}
} else if (mode == 1) {
lcd.setCursor(0, 0);
lcd.print("Set Feed Hour: ");
lcd.setCursor(0, 1);
lcd.print("Hour: ");
if (setHour < 10) lcd.print("0");
lcd.print(setHour);
lcd.setCursor(0, 2);
lcd.print("Press -> confirm");
print_hour();
} else if (mode == 2) {
lcd.setCursor(0, 0);
lcd.print("Set Feed Minute: ");
lcd.setCursor(0, 1);
lcd.print("Minute: ");
if (setMinute < 10) lcd.print("0");
lcd.print(setMinute);
lcd.setCursor(0, 2);
lcd.print("Press -> confirm");
print_hour();
} else if (mode == 3) {
lcd.setCursor(0, 0);
lcd.print("Set Food Amount: ");
lcd.setCursor(0, 1);
lcd.print(setgrams);
lcd.print(" g ");
lcd.setCursor(0, 2);
lcd.print("Press -> confirm");
print_hour();
}
} else {
lcd.setCursor(0, 0);
lcd.print("HX711 Error ");
lcd.setCursor(0, 1);
lcd.print("Check wiring ");
}
}
}
LOAD CELL
16X4 LCD DISPLAY
STEPPER MOTOR
MOTOR DRIVER
MENU
UP
DOWN
LCD ON/OFF
MANUAL CONTROL
RTC