Project tutorial

Arduino Uno-Based, Easy-to-Build Pet Feeder © CC BY-NC

This is an easy to build pet feeder with two timers and accurate portions delivered each time.

  • 8,966 views
  • 8 comments
  • 38 respects

Components and supplies

Apps and online services

About this project

Update 2018-09-06

My two pet feeders are working flawlessly for the past 7 months. We care for our pets during the day of course but they now get at regular times their feed. Very happy with the system!

Why another Pet Feeder?

My version of a pet feeder is based on an Arduino microcontroller. The materials used are relatively cheap and I think it's easy to build!

Of course there are many version around but I could not find a complete project, ready to build with the features I needed so I started from scratch.

These are the features:

  • *Accurate portions* delivered each time! (by using a Hall sensor)
  • Two feeding times a day
  • *Extremely* accurate Real Time Clock (Only with genuine DS3231 chip)
  • You can cancel an upcoming feeding individually for both timers with display and Led indicator. Cancelling is automatically reset after the set time has passed.
  • Manual feed function (one portion per button press)
  • Adjustable portions for each of the two feeding times (1-9 portions)
  • Overview of all set parameters in the main screen
  • Easy to navigate menu system
  • LED indication if feeding was successful
  • LCD backlight timer (off after 30 sec, on with any button press)
  • Time and other settings are safely stored in memory (EEPROM)

Demonstration:

Overview of the functions

In action! slow motion, LOL

Parts needed:

  • 1x Cereal dispenser (cheap on Ebay)
  • some (scrap) wood to build the cereal dispenser mount
  • 1x Arduino Uno
  • 1x I2C DS3231 (extremely accurate) or DS1307 Real Time Clock
  • 1x LCD display 16 characters / 2 lines
  • 1x I2C backpack for the LCD display
  • 1x Continues rotation Servo (buy a good one!)
  • 1x Honeywell SS443R Unipolar Hall sensor
  • 1x 12Volts/1 Amp external power supply (wall wart)
  • 7x Push Buttons - Momentary
  • 6x LED (2x RED, 3x GREEN, 1 YELLOW)
  • 2x resistor 1KΩ (for the red led’s)
  • 4x resistor 560Ω (for the green and yellow led’s)
  • 1x enclosure for the electronics
  • power plug female and connectors to connect the external servo and hall sensor
  • lots and lots of HOT GLUE.

It is best to use a higher voltage for the servo if possible. More power! My HSR-2645CRH Servo can take 7,4 Volts so if you also want to do this you need:

  • Perf board
  • LM317T Variable voltage regulator
  • 3 resistors to set the output voltage (1KΩ, 150kΩ and 4K7)
  • 2 polarized capacitors (220µF, 10µF)
  • heat sink not needed, the servo will rotate1-2 only seconds each day ;)

Online you can find many LM317 calculators.

The pet feed container

The cheapest and easiest to use are the cereal dispensers you can find in many stores and on Ebay. I bought a dual cereal dispenser on Ebay for 15 euro's including shipment (UK to The Netherlands)

The challenge was to make a mount for the feeder and looking around on the internet did not help me much so I looked around in my shed and found some beautiful pieces of Cedar wood.

The wood holding the plastic cereal dispenser is 3 cm thick so no wobbling! You need to be sure the hole (my feeder base is 86mm) is tight so measure before drilling! Better to drill a smaller hole and shape it a bit than a hole which is too big.

Look at the photo's to get the idea why and how it fits together.

Many pictures are better that a bunch of words:

The Servo and attachment to the dispenser paddle wheel

Be sure to buy a good servo. First I bought a MG996R servo which I modified for continous rotation but that was a waste of money... Then I got quality ones, in my case the HSR-2645CRH Servo

It HAS TO BE a continuous rotation model!! Do not attempt to modify a normal servo as I did. It performed not nearly good enough and when stopping, it was not immediate.

The HSR-2645CRH Servo is very accurate and has a good idle point where it really do stop and stay put:

The Servo connection to the dispenser paddle wheel

So now was the question: how to connect the servo to the feeder paddle wheel? It seemed at first the hardest part of the project but it was in fact very simpel.

I took a piece of 5mm Polycarbonate and with a fretsaw I cut out a round disc of about 6 cm's. In the center I drilled a hole of 8 mm (the thickness of the feeder axis. This plastic axis is removable from the paddle wheel)

At the outside I drilles 6 holes of 3 mm, the thickness of the magnets. These are used to give the Arduino feedback about the position of the feeder peddle wheel.

Because the paddle wheel had 6 rubber paddles I used 6 magnets and divide them around the edge of the polycarbonate disc. 360 degrees divided by 6 is 60 degrees. The rod magnets will fit in snugly.

The hall sensor will detect each of the 6 magnets so we can use that information to precisely position and stop the paddle wheel. The servo stop delay time variable is used to add a delay after feeding, before stopping the servo. So with this delay you can tweak the exact position of the paddle wheel. This is important because if the 'stop' position is wrong, the feed will not exit the paddle wheel completely. Also the position of the hall sensor may be different in your case. Tweaking the servo stop delay variable time will solve this.

Now we can glue the paddle wheel axis into the polycarbonate disc.

Notice the comments under the pictures!

The white round servo horn is bolted on the polycarbonate disc with 3 M3 bolts. use some extra nuts as spacers and don't over tighten!

Now the servo is part of the feeder assembly so we can put it in the wooden mount and measure how tall the wood piece has to be under the servo.

The Hall sensor

The hall sensor I used is of the UNIPOLAR type. It will give a LOW output if a magnet is near and DOES NOT need the reverse polarity of the magnet to reset the output to HIGH again. So use a Unipolar hall sensor, much easier.

!!! The hall sensor needs a 10KΩ pull up resistor.

I fitted a 0805 SMD resistor right on top of the hall sensor but you can do it your way. Don't forget this resistor!

Then I took an old ballpoint pen and used the plastic as a holder for the sensor.

The Electronics Housing

I used a Hammond ABS housing with flange. 120 x 65 x 40 mm

for the external connections I used female precision socket connectors, not the cheap chinese ones. I use these a lot in all my projects. Nothing worse than bad connections causing (hard to find) faillures.

On the servo and hall sensor cables I soldered these quality pin headers.
The housing and wiring:

To make programming the Arduino easier, I have an external FTDI connection:

The front panel

I tried a lot of way to make a cheap and good looking front panel...

First I used Mod Podge to glue a paper laserprint on the front of the housing and cover it with more Mod Podge. --> FAIL

Then I tried a clear varnish, based on polyurethan acrylate dispersion. Very durable and strong when dried. I glued the paper very well on the housing and varnishing the upper side of the front panel print looked very good BUT it would not adhere to the laserprinter toner parts of the print. --> FAIL

After that, I took the laser print of the front panel and only laminated the top with laminating pcv. very nice finish, protecting the front and lifting the colors beautifully. Then again, I glued the laminated paper on the housing with the varnish. My idea was that the varnish would permeate the paper making it water resistant. But because I used thick paper that did not work and making it wet to test water resistance, the paper curled off the housing. --> PARTLY FAIL, to be investigated further.

Another attemp: I bought special aluminium foil, made for laser printing. 2,25 euro's per sheet. The result was great but I wanted to protect the toner with a special clear foil. That was no succes because I could not get rid of the imperfections as seen on photo 1:

Then another attempt to protect with the clear varnish, It made the alu foil very high gloss! BUT... again, the varnish would not adhere to the toner so I gave up on the varnish permanently...

FINALLY... I used the foil print without protection... It will show vinger prints when handled often (look under the BACK button of the photo at the top of this page, housing in the chicken feed) so not ideal but still good enough for me.

More photo's are not allowed apparently so...

That's all folks... have fun!

Code

Pet Feed-O-Matic v 1.1 - 20-02-2018Arduino
v 1.1: pressing the Cancel1 or 2 buttons will show a explanation
// c++ stuff...
#include <Arduino.h>
#line 1
#line 1 "/Users/Erik/Documents/PlatformIO/Projects/180202-151127-uno/src/Pet_Feeder_1_1_ENG.cpp"
/*
                         _       _
           /\           | |     (_)
          /  \   _ __ __| |_   _ _ _ __   ___
         / /\ \ | '__/ _` | | | | | '_ \ / _ \
        / ____ \| | | (_| | |_| | | | | | (_) |
       /_/    \_\_|  \__,_|\__,_|_|_| |_|\___/
        _____     _     ______            _
       |  __ \   | |   |  ____|          | |
       | |__) |__| |_  | |__ ___  ___  __| | ___ _ __
       |  ___/ _ \ __| |  __/ _ \/ _ \/ _` |/ _ \ '__|
       | |  |  __/ |_  | | |  __/  __/ (_| |  __/ |
       |_|   \___|\__| |_|  \___|\___|\__,_|\___|_|



   Erik de Ruiter
   -----------------------------------------------------------------------------

   To do:
   - remote feeding?
   - feeder stop delay as menu editable item

  last change:
  dinsdag 20 februari 2018 - 17:20:28

   FEATURES:
   - *Accurate portions* each time! (by using a Hall sensor)
   - Two feeding times, once a day
   - *Extremely* accurate Real Time Clock (Only with genuine DS3231 chip)
   - Cancel upcoming feeding individually for both timers with
     display and Led indicator. Automatically reset after time has passed.
   - Manual feed function (one portion per button press)
   - Adjustable portions for each of the two feeding times (1-9 portions)
   - Overview of all set parameters in the main screen
   - Easy to navigate menu system
   - LED indication if feeding was successful
   - LCD backlight timer (off after 30 sec, on with any button press)
   - Backup in the event of a failing Hall sensor
   - Hass sensor LED will blink until midnight if the Hall sensor has failed
   - Time and other settings are safely stored in EEPROM

   WEBSITES:
   LCD HD44780 character generator website to make your own lcd symbols
   https://omerk.github.io/lcdchargen/

   Arduino sketch Large letter comment generator
   http://patorjk.com/software/taag/#p=display&c=c%2B%2B&f=Big&t=Comment


   Revisions:
   zondag 28 januari 2018 - 20:17:10

   -----------------------------------------------------------------------------
   Credits:
   based on the Arduino alarm clock with DS3231 sketch
   of Dejan Rakijasic. Amazing piece of work, using a Finite State
   Machine a you should with such complex coding. Also the
   blinking function when editing values is a great accomplishment.
   kuddos!
   -----------------------------------------------------------------------------
*/

////////////////////////////////////////////////////////////////////////////////
// USER CHANGEABLE VARIABLES
////////////////////////////////////////////////////////////////////////////////

// used for the Hall sensor fail backup function.
// the interval time should be somewhat larger than one 60 degree
// turn of the feeder (one portion)
#define HALL_SENSOR_BACKUP_VALUE 300

// delay before stopping the feeder servo after feeding. This way you can
// let the rotating peddle wheel stop at the right position
#define FEEDER_STOP_DELAY 100

// lcd backlight on time after pressing a button
#define LCD_BACKLIGHT_ON_TIME 30000

// exit menu without save after amanout of time set here in ms
#define MENU_TIMEOUT_VALUE 7000

////////////////////////////////////////////////////////////////////////////////

// https:// github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library
#include <LiquidCrystal_I2C.h>
// https:// github.com/JChristensen/Button
#include <Button.h>
// http:// github.com/JChristensen/DS3232RTC
#include <DS3232RTC.h>
// http:// www.arduino.cc/playground/Code/Time
#include <Time.h>
// http:// arduino.cc/en/Reference/Wire (included with Arduino IDE)
#include <Wire.h>
#include <EEPROM.h>

// CONNECTIONS:
//
// LCD (I2C module):
// SCL - A5
// SDA - A4
// VCC
// GND

// interrupt hal sensor pin
#define HALL_SENSOR_PIN 3

#define BUTTON_BACK_PIN     4
#define BUTTON_UP_PIN       5
#define BUTTON_DOWN_PIN     6
#define BUTTON_SELECT_PIN   7
#define BUTTON_CANCEL1_PIN  8
#define BUTTON_CANCEL2_PIN  9
#define BUTTON_MANUAL_PIN   10

#define LED_CANCEL1_PIN     A0
#define LED_CANCEL2_PIN     A1
#define LED_SUCCESS1_PIN    A2
#define LED_SUCCESS2_PIN    A3

// feeder Servo output Pin
#define SERVO_OUTPUT_PIN    12

// Hall sensor input Pin
#define LED_HALL_SENSOR_PIN 13

// address LCD(16x2) 0x27 or 0x3F
LiquidCrystal_I2C lcd(0x3F, 16, 2);

// define button library settings
// A debounce time of 20 milliseconds usually works well
// for tactile button switches.
#define DEBOUNCE_MS 20
// ms required before repeating on long press
#define REPEAT_FIRST 1000
// repeat interval for long press
#define REPEAT_INCR 200
// To keep things simple, we use the Arduino's internal pullup resistor.
#define PULLUP true
#define INVERT true

// Declare the buttons
Button buttonSelect  (BUTTON_SELECT_PIN,  PULLUP, INVERT, DEBOUNCE_MS);
Button buttonUp      (BUTTON_UP_PIN,      PULLUP, INVERT, DEBOUNCE_MS);
Button buttonDown    (BUTTON_DOWN_PIN,    PULLUP, INVERT, DEBOUNCE_MS);
Button buttonBack    (BUTTON_BACK_PIN,    PULLUP, INVERT, DEBOUNCE_MS);
Button buttonCancel1 (BUTTON_CANCEL1_PIN, PULLUP, INVERT, DEBOUNCE_MS);
Button buttonCancel2 (BUTTON_CANCEL2_PIN, PULLUP, INVERT, DEBOUNCE_MS);
Button buttonManual  (BUTTON_MANUAL_PIN,  PULLUP, INVERT, DEBOUNCE_MS);

// The number that is adjusted
int count;
// Previous value of count (initialized to ensure it's different when
// the sketch starts)
int lastCount = -1;
// A variable time that is used to drive the repeats for long presses
unsigned long rpt = REPEAT_FIRST;

// used for the menu time-out
unsigned long timeoutValue = 0;

// manual cancel feeding times variables
boolean manualCancelFeed1 = false;
boolean manualCancelFeed2 = false;

// Manaual feed option
boolean manualFeed = false;

// Feed amount (in portions)
int feedAmount1 = 1;
int feedAmount2 = 1;
bool feederSuccess = false;

// feeder portions
int portions = 0;
int turns = 0;

// input actions
enum {btnSELECT, btnUP, btnDOWN, btnBACK, btnCANCEL1, btnCANCEL2, btnMANUAL, trigTIMEOUT};

// States of the Finite State Machine (FSM)
enum STATES
{
  MAIN,
  MENU_EDIT_FEEDTIME1,
  MENU_EDIT_FEEDTIME2,
  MENU_EDIT_FEEDAMOUNT,
  MENU_EDIT_TIME,
  MENU_EDIT_DATE,
  MENU_EDIT_SETTINGS,

  EDIT_FEED_TIME1_HOUR,
  EDIT_FEED_TIME1_MINUTE,
  EDIT_FEED_TIME1_ON_OFF,

  EDIT_FEED_TIME2_HOUR,
  EDIT_FEED_TIME2_MINUTE,
  EDIT_FEED_TIME2_ON_OFF,

  EDIT_FEED_AMOUNT1,
  EDIT_FEED_AMOUNT2,

  EDIT_HOUR,
  EDIT_MINUTE,

  EDIT_DAY,
  EDIT_MONTH,
  EDIT_YEAR,

  EDIT_SERVO_STOP_DELAY,
  EDIT_SERVO_BACKUP_DELAY,
};
// Holds the current state of the system
STATES state;

int8_t userInput;
int8_t trigger;

int Second;
int Minute;
int Hour;
int Day;
int Month;
int Year;

int8_t DoW;
String day_of_week;
unsigned char address, data;

int testt = 0;

int feed_time1_hour;
int feed_time1_minute;
bool feed_time1_active = false;
bool alarm1Activated = false;

int feed_time2_hour;
int feed_time2_minute;
bool feed_time2_active = false;
bool alarm2Activated = false;


// used for the blink funtion when editing values
uint32_t blink_interval = 500;
uint32_t blink_previousMillis = 0;
uint32_t blink_currentMillis = 0;
boolean blink_state  = false;

// used for the blink funtion when editing values
uint32_t spinningWheel_interval = 170;
uint32_t spinningWheel_previousMillis = 0;
uint32_t spinningWheel_currentMillis = 0;
int spinningWheelSymbol = 0;

// used for the Hall sensor fail backup function
// the interval time should be somewhat larger than one 60 degree
// turn of the feeder (one portion).
uint32_t hallSensorBackup_interval = HALL_SENSOR_BACKUP_VALUE;
uint32_t hallSensorBackup_currentMillis = 0;
boolean hallSensorFail = false;

// used for the lcd backlight timer
uint32_t lcdBacklight_interval = LCD_BACKLIGHT_ON_TIME;
uint32_t lcdBacklight_currentMillis = 0;

boolean RTC_error = true;
boolean long_press_button = false;

// Define custom symbols for the LCD display
byte bell_symbol_Char[8] = {
  B00100,
  B01110,
  B01110,
  B01110,
  B11111,
  B00100,
  B00000,
  B00000
};

byte inverted_one_Char[8] = {
  0b11111,
  0b11011,
  0b10011,
  0b11011,
  0b11011,
  0b11011,
  0b10001,
  0b11111
};

byte inverted_two_Char[8] = {
  0b11111,
  0b11011,
  0b10101,
  0b11101,
  0b11011,
  0b10111,
  0b10001,
  0b11111
};

byte arrow_up_Char[8] = {
  0b00100,
  0b01110,
  0b11111,
  0b01110,
  0b01110,
  0b01110,
  0b01110,
  0b00000
};

byte arrow_down_Char[8] = {
  0b00000,
  0b01110,
  0b01110,
  0b01110,
  0b01110,
  0b11111,
  0b01110,
  0b00100
};

byte inverted_p_Char[8] = {
  0b11111,
  0b10001,
  0b10101,
  0b10001,
  0b10111,
  0b10111,
  0b11111,
  0b00000
};

byte backslash_Char[8] = {
  0b00000,
  0b10000,
  0b01000,
  0b00100,
  0b00010,
  0b00001,
  0b00000,
  0b00000
};

byte thickDash_Char[8] = {
  0b00000,
  0b00000,
  0b11111,
  0b11111,
  0b11111,
  0b00000,
  0b00000,
  0b00000
};

volatile boolean hallSensorActivated = false;

// Interrupt 1
void HallSensorIsr()
{
  hallSensorActivated = true;
  // turn on Hall sensor LED
  digitalWrite(LED_HALL_SENSOR_PIN, HIGH);
}
// These lines are needed to make this sketch a C++ file.
// I use it because I edit the code with the Atom
// text editor and the PlatformIO add-on
void HallSensorIsr();
void setup();
void loop();
void change_states();
void check_inputs();
void transition(int trigger);
void check_alarm();
void check_manual_feed();
void display_menu_option_set_feedtime1();
void display_menu_option_set_feedtime2();
void display_menu_option_set_feed_amount();
void display_menu_option_set_time();
void display_menu_option_set_date();
void midnight_reset();
void display_time();
void displayFeedingAmouts();
void displayFeedingTimes();
void set_feedAmount();
void set_time();
void set_date();
void set_feeding1_time();
void set_feeding2_time();
void get_time();
void get_date();
void write_time();
void write_date();
void write_feeding_time1();
void write_feeding_time2();
void write_feedamount();
void get_feedamount();
void get_feed_time1();
void get_feed_time2();
void check_RTC();
byte decToBcd(byte val);
byte bcdToDec(byte val);
void leading_zero(int digits);
void blinkFunction();
void displaySpinningWheel();
void startFeederServo();
void stopFeederServo();
void activateFeeder(int portions);
void check_LcdBacklight();
void lcd_backlight_ON();
void ledsAndLcdDisplayStartup();
void hallSensorCheck();
#line 355
// ******************************************************************************
// SETUP

void setup()
{
  // activate the lcd screen
  lcd.begin();
  // turn lcd backlight on and start backlight off-timer
  lcd_backlight_ON();

  // start I2C
  Wire.begin();

  // hall sensor input to detect rotation of the feeder/feed amount output check
  pinMode(HALL_SENSOR_PIN, INPUT_PULLUP);
  pinMode(LED_HALL_SENSOR_PIN, OUTPUT);
  pinMode(SERVO_OUTPUT_PIN, OUTPUT);
  pinMode(LED_CANCEL1_PIN, OUTPUT);
  pinMode(LED_CANCEL2_PIN, OUTPUT);
  pinMode(LED_SUCCESS1_PIN, OUTPUT);
  pinMode(LED_SUCCESS2_PIN, OUTPUT);

  // set default state of LED's to OFF
  digitalWrite(LED_CANCEL1_PIN, LOW);
  digitalWrite(LED_CANCEL2_PIN, LOW);
  digitalWrite(LED_SUCCESS1_PIN, LOW);
  digitalWrite(LED_SUCCESS2_PIN, LOW);
  digitalWrite(LED_HALL_SENSOR_PIN, LOW);

  lcd.createChar(0, thickDash_Char);
  lcd.createChar(1, bell_symbol_Char);
  lcd.createChar(2, backslash_Char);
  lcd.createChar(3, inverted_p_Char);
  lcd.createChar(4, inverted_one_Char);
  lcd.createChar(5, inverted_two_Char);
  lcd.createChar(6, arrow_up_Char);
  lcd.createChar(7, arrow_down_Char);

  // set feeder Servo to default state OFF
  stopFeederServo();

  Wire.begin();

  // set RTC as the Syncprovider
  setSyncProvider(RTC.get);
  // time in sec of resync with RTC
  setSyncInterval(60);

  // Disable the default square wave of the SQW pin.
  RTC.squareWave(SQWAVE_NONE);

  // Attach an interrupt on the Hall sensor (when unput turns LOW)
  // ever 60 degree turn of the feeder shaft, the hall sensor
  // should generate an interrupt
  attachInterrupt(INT1, HallSensorIsr, FALLING);

  // display test
  ledsAndLcdDisplayStartup();

  // Initial state of the FSM
  state = MAIN;

  // read the stored alarm value from the Arduino memory
  get_feed_time1();
  get_feed_time2();

}// End SETUP



// ******************************************************************************
// LOOP

void loop()
{
  // change states of FSM
  change_states();

  // check inputs (buttons)
  check_inputs();

  // check if alarm was called
  check_alarm();

  // check if manual feed was requsted
  check_manual_feed();

  // at midnight, reset some variables
  midnight_reset();

  // check connection RTC
  check_RTC();

  // Check the Hall sensor function
  hallSensorCheck();

  // check if lcd backlight must be turned off
  check_LcdBacklight();

}// End of LOOP


// ******************************************************************************
// ******************************************************************************
// ******************************************************************************
// ******************************************************************************



//******************************************************************************
// Finite State Machine
void change_states()
{
  // states
  switch (state)
  {
    //---------------------------------------
    case MAIN:
      display_time();
      displayFeedingAmouts();
      displayFeedingTimes();
      break;
    //---------------------------------------
    case MENU_EDIT_FEEDTIME1:
      display_menu_option_set_feedtime1();
      break;
    //---------------------------------------
    case MENU_EDIT_FEEDTIME2:
      display_menu_option_set_feedtime2();
      break;
    //---------------------------------------
    case MENU_EDIT_FEEDAMOUNT:
      display_menu_option_set_feed_amount();
      break;
    //---------------------------------------
    case MENU_EDIT_TIME:
      display_menu_option_set_time();
      break;
    //---------------------------------------
    case MENU_EDIT_DATE:
      display_menu_option_set_date();
      break;

    //---------------------------------------
    case EDIT_FEED_TIME1_HOUR:
      set_feeding1_time();
      break;
    //---------------------------------------
    case EDIT_FEED_TIME1_MINUTE:
      set_feeding1_time();
      break;
    //---------------------------------------
    case EDIT_FEED_TIME1_ON_OFF:
      set_feeding1_time();
      break;

    //---------------------------------------
    case EDIT_FEED_TIME2_HOUR:
      set_feeding2_time();
      break;
    //---------------------------------------
    case EDIT_FEED_TIME2_MINUTE:
      set_feeding2_time();
      break;
    //---------------------------------------
    case EDIT_FEED_TIME2_ON_OFF:
      set_feeding2_time();
      break;

    //---------------------------------------
    case EDIT_FEED_AMOUNT1:
      set_feedAmount();
      break;
    //---------------------------------------
    case EDIT_FEED_AMOUNT2:
      set_feedAmount();
      break;

    //---------------------------------------
    case EDIT_HOUR:
      set_time();
      break;
    //---------------------------------------
    case EDIT_MINUTE:
      set_time();
      break;
    //---------------------------------------
    case EDIT_DAY:
      set_date();
      break;
    //---------------------------------------
    case EDIT_MONTH:
      set_date();
      break;
    //---------------------------------------
    case EDIT_YEAR:
      set_date();
      break;
      //---------------------------------------
  }
}


//******************************************************************************
// Check INPUTS

void check_inputs()
{
  // first check if timeout has occurred
  if ( millis() - timeoutValue > MENU_TIMEOUT_VALUE )
  {
    userInput = trigTIMEOUT;
    transition(userInput);
  }

  // check state of buttons
  buttonSelect.read();
  buttonUp.read();
  buttonDown.read();
  buttonBack.read();
  buttonManual.read();
  buttonCancel1.read();
  buttonCancel2.read();

  // check manual cancel Feed1 button
  switch (buttonCancel1.wasPressed())
  {
    case 1:
      // invert variable value (true to false and vise versa)
      manualCancelFeed1 = !manualCancelFeed1;
      //turn on lcd backlight manually
      lcd_backlight_ON();

      // message when Cancel1 button is pressed
      if (manualCancelFeed1 == 1)
      {
        lcd.clear();
        lcd.setCursor(0, 0);
        //         0123456789012345 - LCD screen character counter
        lcd.print("Upcoming Feed #1");
        lcd.setCursor(0, 1);
        lcd.print("cancelled once  ");
        delay(2000);
        lcd.clear();
      }
      else if (manualCancelFeed1 == 0)
      {
        lcd.clear();
        lcd.setCursor(0, 0);
        //         0123456789012345 - LCD screen character counter
        lcd.print("Upcoming Feed #1");
        lcd.setCursor(0, 1);
        lcd.print("canceling undone");
        delay(2000);
        lcd.clear();
      }
      break;
  }

  // check manual cancel Feed2 button
  switch (buttonCancel2.wasPressed())
  {
    case 1:
      // invert variable value (true to false and vise versa)
      manualCancelFeed2 = !manualCancelFeed2;
      //turn on lcd backlight manually
      lcd_backlight_ON();
      // message when Cancel1 button is pressed
      if (manualCancelFeed2 == 1)
      {
        lcd.clear();
        lcd.setCursor(0, 0);
        //         0123456789012345 - LCD screen character counter
        lcd.print("Upcoming Feed #2");
        lcd.setCursor(0, 1);
        lcd.print("cancelled once  ");
        delay(2000);
        lcd.clear();
      }
      else if (manualCancelFeed2 == 0)
      {
        lcd.clear();
        lcd.setCursor(0, 0);
        //         0123456789012345 - LCD screen character counter
        lcd.print("Upcoming Feed #2");
        lcd.setCursor(0, 1);
        lcd.print("canceling undone");
        delay(2000);
        lcd.clear();
      }
      break;
  }

  // check manual Feed button
  switch (buttonManual.wasPressed())
  {
    case 1:
      manualFeed = true;
      //turn on lcd backlight manually
      lcd_backlight_ON();
      break;
  }

  // check MENU/SELECT button
  switch (buttonSelect.wasPressed())
  {
    case 1:
      userInput = btnSELECT;
      //turn on lcd backlight manually
      lcd_backlight_ON();
      transition(userInput);
      break;
  }
  // check UP button
  switch (buttonUp.wasPressed())
  {
    case 1:
      userInput = btnUP;
      transition(userInput);
      //turn on lcd backlight manually
      lcd_backlight_ON();
      break;
  }
  // check long press UP button
  switch (buttonUp.wasReleased())
  {
    case 1:
      long_press_button = false;
      rpt = REPEAT_FIRST;
      break;
  }
  switch (buttonUp.pressedFor(rpt))
  {
    case 1:
      // increment the long press interval
      rpt += REPEAT_INCR;
      long_press_button = true;
      userInput = btnUP;
      transition(userInput);
      break;
  }
  // check  DOWN button
  switch (buttonDown.wasPressed())
  {
    case 1:
      userInput = btnDOWN;
      transition(userInput);
      //turn on lcd backlight manually
      lcd_backlight_ON();
      break;
  }
  // check long press DOWN button
  switch (buttonDown.wasReleased())
  {
    case 1:
      long_press_button = false;
      rpt = REPEAT_FIRST;
      break;
  }
  switch (buttonDown.pressedFor(rpt))
  {
    case 1:
      // increment the long press interval
      rpt += REPEAT_INCR;
      long_press_button = true;
      userInput = btnDOWN;
      transition(userInput);
      break;
  }
  // check btnBACK button
  switch (buttonBack.wasPressed())
  {
    case 1:
      userInput = btnBACK;
      transition(userInput);
      //turn on lcd backlight manually
      lcd_backlight_ON();
      break;
  }

}


//******************************************************************************
// Check for state transition trigger

void transition(int trigger)
{
  switch (state)
  {
    //---------------------------------------
    case MAIN:

      // set time-out timr
      timeoutValue = millis();

      if (trigger == btnSELECT)
      {
        lcd.clear();
        state = MENU_EDIT_FEEDTIME1;
      }
      else if (trigger == btnBACK)
      {
        //lcd.clear();
        //state = ALARM1_AND_2_TIME;
      }
      break;
    //---------------------------------------
    case MENU_EDIT_FEEDTIME1:

      // set time-out timer
      timeoutValue = millis();
      // check for time-out 'button' trigger
      if (trigger == trigTIMEOUT)
      {
        lcd.clear();
        state = MAIN;
      }

      // Now check for button triggers
      if (trigger == btnUP)
      {
        //no action, this is the first menu
      }
      else if (trigger == btnDOWN)
      {
        lcd.clear();
        state = MENU_EDIT_FEEDTIME2;
      }
      if (trigger == btnSELECT)
      {
        lcd.clear();
        state = EDIT_FEED_TIME1_HOUR;
      }
      if (trigger == btnBACK)
      {
        lcd.clear();
        state = MAIN;
      }
      break;
    //---------------------------------------
    case MENU_EDIT_FEEDTIME2:

      // set time-out timer
      timeoutValue = millis();
      // check for time-out 'button' trigger
      if (trigger == trigTIMEOUT)
      {
        lcd.clear();
        state = MAIN;
      }

      // Now check for button triggers
      if (trigger == btnUP)
      {
        lcd.clear();
        state = MENU_EDIT_FEEDTIME1;
      }
      else if (trigger == btnDOWN)
      {
        lcd.clear();
        state = MENU_EDIT_FEEDAMOUNT;
      }
      if (trigger == btnSELECT)
      {
        lcd.clear();
        state = EDIT_FEED_TIME2_HOUR;
      }
      if (trigger == btnBACK)
      {
        lcd.clear();
        state = MAIN;
      }
      break;
    //---------------------------------------
    case MENU_EDIT_FEEDAMOUNT:

      // set time-out timer
      timeoutValue = millis();
      // check for time-out 'button' trigger
      if (trigger == trigTIMEOUT)
      {
        lcd.clear();
        state = MAIN;
      }

      // Now check for button triggers
      if (trigger == btnUP)
      {
        lcd.clear();
        state = MENU_EDIT_FEEDTIME2;
      }
      else if (trigger == btnDOWN)
      {
        lcd.clear();
        state = MENU_EDIT_TIME;
      }
      if (trigger == btnSELECT)
      {
        lcd.clear();
        state = EDIT_FEED_AMOUNT1;
      }
      if (trigger == btnBACK)
      {
        lcd.clear();
        state = MAIN;
      }
      break;
    //---------------------------------------
    case MENU_EDIT_TIME:

      // set time-out timer
      timeoutValue = millis();
      // check for time-out 'button' trigger
      if (trigger == trigTIMEOUT)
      {
        lcd.clear();
        state = MAIN;
      }

      // Now check for button triggers
      if (trigger == btnUP)
      {
        lcd.clear();
        state = MENU_EDIT_FEEDTIME2;
      }
      if (trigger == btnDOWN)
      {
        lcd.clear();
        state = MENU_EDIT_DATE;
      }
      if (trigger == btnSELECT)
      {
        lcd.clear();
        state = EDIT_HOUR;
      }
      if (trigger == btnBACK)
      {
        lcd.clear();
        state = MAIN;
      }
      break;
    //---------------------------------------
    case MENU_EDIT_DATE:

      // set time-out timer
      timeoutValue = millis();
      // check for time-out 'button' trigger
      if (trigger == trigTIMEOUT)
      {
        lcd.clear();
        state = MAIN;
      }

      // Now check for button triggers
      if (trigger == btnUP)
      {
        lcd.clear();
        state = MENU_EDIT_TIME;
      }
      else if (trigger == btnDOWN)
      {
        //no action, end of menu items!
      }
      if (trigger == btnSELECT)
      {
        lcd.clear();
        state = EDIT_DAY;
      }
      if (trigger == btnBACK)
      {
        lcd.clear();
        state = MAIN;
      }
      break;

    //---------------------------------------
    case EDIT_FEED_TIME1_HOUR:
      // set time-out timer
      timeoutValue = millis();
      // check for time-out 'button' trigger
...

This file has been truncated, please download it to see its full contents.

Schematics

Arduino Pet Feed-O-Matic
Arduino based Pet Feeder
Pet feed o matic schematic v wun9ik8mk7

Comments

Similar projects you might like

IoT Pet Feeder

Project tutorial by circuito.io team

  • 38,861 views
  • 31 comments
  • 128 respects

Pet Center - Feed and Entertain Your Pet

Project tutorial by Vítor Barbosa

  • 1,600 views
  • 0 comments
  • 5 respects

Pet Feeder

Project tutorial by MOHRAN JURDI and Joshua Hypes

  • 1,894 views
  • 1 comment
  • 15 respects

Pet Feeder

Project tutorial by Namsulee

  • 4,209 views
  • 0 comments
  • 11 respects

Voice Activated DIY Pet Feeder Using Bolt

Project tutorial by Team Bolt

  • 864 views
  • 1 comment
  • 7 respects

Pet Feeder

Project showcase by kathleen kuypers

  • 8,489 views
  • 0 comments
  • 198 respects
Add projectSign up / Login