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.

  • 1,911 views
  • 3 comments
  • 19 respects

Components and supplies

Apps and online services

About this project

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
      if (trigger == trigTIMEOUT)
      {
        lcd.clear();
        state = MAIN;
      }

      // Now check for button triggers
      if (trigger == btnUP)
      {
        feed_time1_hour++;
        if (feed_time1_hour > 23) feed_time1_hour = 0;
      }
      else if (trigger == btnDOWN)
      {
        feed_time1_hour--;
        if (feed_time1_hour < 0) feed_time1_hour = 23;
      }
      if (trigger == btnSELECT)
      {
        state = EDIT_FEED_TIME1_MINUTE;
      }
      break;
    //---------------------------------------
    case EDIT_FEED_TIME1_MINUTE:
      // 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)
      {
        feed_time1_minute++;
        if (feed_time1_minute > 59) feed_time1_minute = 0;
      }
      else if (trigger == btnDOWN)
      {
        feed_time1_minute--;
        if (feed_time1_minute < 0) feed_time1_minute = 59;
      }
      if (trigger == btnSELECT)
      {
        state = EDIT_FEED_TIME1_ON_OFF;
      }
      if (trigger == btnBACK)
      {
        state = EDIT_FEED_TIME1_HOUR;
      }
      break;
    //---------------------------------------
    case EDIT_FEED_TIME1_ON_OFF:
      // 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)
      {
        feed_time1_active = true;
      }
      else if (trigger == btnDOWN)
      {
        feed_time1_active = false;
      }
      if (trigger == btnBACK)
      {
        state = EDIT_FEED_TIME1_MINUTE;
      }
      if (trigger == btnSELECT)
      {
        // write alarm settings to the RTC chip
        write_feeding_time1();
        // reset feed override variable when timer is set to OFF
        if (feed_time1_active == false)
        {
          manualCancelFeed1 = false;
        }
        lcd.clear();
        lcd.setCursor(0, 0);
        //         0123456789012345 - LCD screen character counter - LCD screen character counter
        lcd.print("*Settings saved*");
        delay(1000);
        lcd.clear();
        state = MAIN;
      }

      break;
    //---------------------------------------
    case EDIT_FEED_TIME2_HOUR:
      // 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)
      {
        feed_time2_hour++;
        if (feed_time2_hour > 23) feed_time2_hour = 0;
      }
      else if (trigger == btnDOWN)
      {
        feed_time2_hour--;
        if (feed_time2_hour < 0) feed_time2_hour = 23;
      }
      if (trigger == btnSELECT)
      {
        state = EDIT_FEED_TIME2_MINUTE;
      }
      break;
    //---------------------------------------
    case EDIT_FEED_TIME2_MINUTE:
      // 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)
      {
        feed_time2_minute++;
        if (feed_time2_minute > 59) feed_time2_minute = 0;
      }
      else if (trigger == btnDOWN)
      {
        feed_time2_minute--;
        if (feed_time2_minute < 0) feed_time2_minute = 59;
      }
      if (trigger == btnSELECT)
      {
        state = EDIT_FEED_TIME2_ON_OFF;
      }
      if (trigger == btnBACK)
      {
        state = EDIT_FEED_TIME2_HOUR;
      }
      break;
    //---------------------------------------
    case EDIT_FEED_TIME2_ON_OFF:
      // 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)
      {
        feed_time2_active = true;
      }
      else if (trigger == btnDOWN)
      {
        feed_time2_active = false;
      }
      if (trigger == btnBACK)
      {
        state = EDIT_FEED_TIME2_MINUTE;
      }
      if (trigger == btnSELECT)
      {
        // write alarm settings to the RTC chip
        write_feeding_time2();
        // reset feed override variable when timer is set to OFF
        if (feed_time2_active == false)
        {
          manualCancelFeed2 = false;
        }
        lcd.clear();
        lcd.setCursor(0, 0);
        //         0123456789012345 - LCD screen character counter - LCD screen character counter
        lcd.print("*Settings saved*");
        delay(1000);
        lcd.clear();
        state = MAIN;
      }
      break;
    //---------------------------------------
    case EDIT_FEED_AMOUNT1:

      // 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)
      {
        feedAmount1++;
        if (feedAmount1 > 9) feedAmount1 = 1;
      }
      else if (trigger == btnDOWN)
      {
        feedAmount1--;
        if (feedAmount1 < 1) feedAmount1 = 9;
      }
      if (trigger == btnSELECT)
      {
        state = EDIT_FEED_AMOUNT2;
      }
      break;
    //---------------------------------------
    case EDIT_FEED_AMOUNT2:

      // 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)
      {
        feedAmount2++;
        if (feedAmount2 > 9) feedAmount2 = 1;
      }
      else if (trigger == btnDOWN)
      {
        feedAmount2--;
        if (feedAmount2 < 1) feedAmount2 = 9;
      }
      if (trigger == btnSELECT)
      {
        write_feedamount();
        lcd.clear();
        lcd.setCursor(0, 0);
        //         0123456789012345 - LCD screen character counter - LCD screen character counter
        lcd.print("*Settings saved*");
        delay(1000);

        lcd.clear();
        state = MAIN;
      }
      break;
    //---------------------------------------
    case EDIT_HOUR:

      // 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)
      {
        Hour++;
        if (Hour > 23) Hour = 0;
      }
      else if (trigger == btnDOWN)
      {
        Hour--;
        if (Hour < 0) Hour = 23;
      }
      if (trigger == btnSELECT)
      {
        state = EDIT_MINUTE;
      }
      break;

    //---------------------------------------
    case EDIT_MINUTE:

      // 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)
      {
        Minute++;
        if (Minute > 59) Minute = 0;
      }
      else if (trigger == btnDOWN)
      {
        Minute--;
        if (Minute < 0) Minute = 59;
      }

      if (trigger == btnBACK)
      {
        state = EDIT_HOUR;
      }
      if (trigger == btnSELECT)
      {
        write_time();
        lcd.clear();
        lcd.setCursor(0, 0);
        //         0123456789012345 - LCD screen character counter - LCD screen character counter
        lcd.print("*Settings saved*");
        delay(1000);

        lcd.clear();
        state = MAIN;
      }
      break;

    //---------------------------------------
    case EDIT_DAY:
      // 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)
      {
        Day++;
        if (Day > 31) Day = 1;
      }
      else if (trigger == btnDOWN)
      {
        Day--;
        if (Day < 1) Day = 31;
      }
      if (trigger == btnSELECT)
      {
        state = EDIT_MONTH;
      }
      break;
    //---------------------------------------
    case EDIT_MONTH:
      // 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)
      {
        Month++;
        if (Month > 12) Month = 1;
      }
      else if (trigger == btnDOWN)
      {
        Month--;
        if (Month < 1) Month = 12;
      }
      if (trigger == btnSELECT)
      {
        // to do: insert check here: day is not > 28
        // when month = 2 and we have no leap year ??
        state = EDIT_YEAR;
      }
      if (trigger == btnBACK)
      {
        state = EDIT_DAY;
      }
      break;
    //---------------------------------------
    case EDIT_YEAR:
      // 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)
      {
        Year++;
        if (Year > 50) Year = 18;
      }
      else if (trigger == btnDOWN)
      {
        Year--;
        if (Year < 18) Year = 50;
      }
      if (trigger == btnBACK)
      {
        state = EDIT_MONTH;
      }
      if (trigger == btnSELECT)
      {
        write_date();
        lcd.clear();
        lcd.setCursor(0, 0);
        //         0123456789012345 - LCD screen character counter - LCD screen character counter
        lcd.print("*Settings saved*");
        delay(1000);
        lcd.clear();
        state = MAIN;
      }
      break;
  }
}


//******************************************************************************
//******************************************************************************
//******************************************************************************
// FUNCTIONS

//******************************************************************************
void check_alarm()
{
  // check only every minute
  if (Second == 0)
  {

    ////////////////////////////////////////////////////////////////////////////
    //  condition = true
    if ( (Hour == feed_time1_hour) && (Minute == feed_time1_minute) )
    {
      // set variable so we can reset the manualCancelFeed1 variable later.
      alarm1Activated = true;

      // two 'if' statements before feeding proceeds: 1) feed timer 1 must be ON
      // and 2) feeding time 1 is not cancelled by the user
      if ( (feed_time1_active == true) && (manualCancelFeed1 == false) )
      {
        lcd.clear();
        lcd.setCursor(0, 0);
        //         0123456789012345 - LCD screen character counter - LCD screen character counter
        lcd.print("*** Feed #1 ****");
        // portions counter display, handled by the activateFeeder function
        lcd.setCursor(1, 1);
        lcd.print("/");
        lcd.print(feedAmount1);
        lcd.print(" portions");

        // start the feeder Servo, with the number of turns (portions)
        // dependant of the feedamount1 value
        activateFeeder(feedAmount1);

        // Set the feed success LED on or off depending on the succesfull
        // activation of the Hall sensor.
        if (hallSensorFail == false)
        {
          // no hall sensor faillure so turn Success led 1 on
          digitalWrite(LED_SUCCESS1_PIN, HIGH);
        }
        else
        {
          // hall sensor faillure so turn Success led 1 off
          digitalWrite(LED_SUCCESS1_PIN, LOW);
        }
      }//if (manualCancelFeed1 == false)
    }//if ( (Hour == feed_time1_hour) && (Minute == feed_time1_minute) )


    ////////////////////////////////////////////////////////////////////////////
    // alarm2 condition = true
    if ( (Hour == feed_time2_hour) && (Minute == feed_time2_minute) )
    {
      // set variable so we can reset the manualCancelFeed1 variable later
      alarm2Activated = true;

      // two 'if' statements before feeding proceeds: 1) feed timer 2 must be ON
      // and 2) feeding time 2 is not cancelled by the user
      if ( (feed_time2_active == true) && (manualCancelFeed2 == false) )
      {
        lcd.clear();
        lcd.setCursor(0, 0);
        //         0123456789012345 - LCD screen character counter - LCD screen character counter
        lcd.print("*** Feed #2 ****");
        // portions counter display, handled by the activateFeeder function
        lcd.setCursor(1, 1);
        lcd.print("/");
        lcd.print(feedAmount2);
        lcd.print(" portions");

        // start the feeder Servo, with the number of turns (portions)
        // dependant of the feedamount2 value
        activateFeeder(feedAmount2);

        // Set the feed success LED on or off depending on the succesfull
        // activation of the Hall sensor.
        if (hallSensorFail == false)
        {
          // no hall sensor faillure so turn Success led 1 on
          digitalWrite(LED_SUCCESS2_PIN, HIGH);
        }
        else
        {
          // hall sensor faillure so turn Success led 1 off
          digitalWrite(LED_SUCCESS2_PIN, LOW);
        }

      }//if (manualCancelFeed2 == false)
    }//if ( (Hour == feed_time2_hour) && (Minute == feed_time2_minute) )

  }//if (Second == 0)

  // else, so second value > 0
  else
  {
    // only if alarm1 has been triggered, reset the user cancellation
    // of the feed timer 1, ready for feeding the next day
    if (alarm1Activated == true)
    {
      // of course, we need also to reset the alarm1Activated flag
      alarm1Activated = false;
      manualCancelFeed1 = false;
    }
    // only if alarm2 has been triggered, reset the user cancellation
    // of the feed timer 2, ready for feeding the next day
    if (alarm2Activated == true)
    {
      // of course, we need also to reset the alarm2Activated flag
      alarm2Activated = false;
      manualCancelFeed2 = false;
    }
  }//else
}//void check_alarm()


//******************************************************************************
void check_manual_feed()
{
  if (manualFeed == true)
  {
    // display
    lcd.clear();
    lcd.setCursor(0, 0);
    //         0123456789012345 - LCD screen character counter - LCD screen character counter
    lcd.print(" Manual feeding ");
    lcd.setCursor(2, 1);
    lcd.print("portion");

    // activate the feeder Servo, one portion only
    activateFeeder(1);

    // reset manual feed variable
    // must happen AFTER calling the activateFeeder function
    manualFeed = false;
  }
}

//******************************************************************************
void display_menu_option_set_feedtime1()
{
  // temporarely turn off the cancel1 and cancel2 button LED indicators
  // when entering the menu's. After exiting the LED's will resume their
  // previous status
  digitalWrite(LED_CANCEL1_PIN, LOW);
  digitalWrite(LED_CANCEL2_PIN, LOW);

  // display menu top line
  lcd.setCursor(0, 0);
  // thick dash character
  lcd.write(0);
  lcd.print("MENU");
  // display 9 thick dash characters
  for (int dash = 1; dash <= 9; dash++)
  {
    lcd.write(0);
  }

  // display arrow symbols
  lcd.setCursor(15, 1);
  // down arrow
  lcd.write(7);

  // display menu bottom line
  lcd.setCursor(0, 1);
  //         0123456789012345 - LCD screen character counter
  lcd.print("Feedingtime #1  ");
}

//******************************************************************************
void display_menu_option_set_feedtime2()
{
  // display menu top line
  lcd.setCursor(0, 0);
  // thick dash character
  lcd.write(0);
  lcd.print("MENU");
  // display 9 thick dash characters
  for (int dash = 1; dash <= 9; dash++)
  {
    lcd.write(0);
  }

  // display arrow symbols
  lcd.setCursor(15, 0);
  // up arrow
  lcd.write(6);
  lcd.setCursor(15, 1);
  // down arrow
  lcd.write(7);

  // display menu bottom line
  lcd.setCursor(0, 1);
  lcd.print("Feedingtime #2  ");

}

//******************************************************************************
void display_menu_option_set_feed_amount()
{
  // display menu top line
  lcd.setCursor(0, 0);
  // thick dash character
  lcd.write(0);
  lcd.print("MENU");
  // display 9 thick dash characters
  for (int dash = 1; dash <= 9; dash++)
  {
    lcd.write(0);
  }

  // display arrow symbols
  lcd.setCursor(15, 0);
  // up arrow
  lcd.write(6);
  lcd.setCursor(15, 1);
  // down arrow
  lcd.write(7);

  // display menu bottom line
  lcd.setCursor(0, 1);
  //         0123456789012345 - LCD screen character counter
  lcd.print("Feed portions   ");

}

//******************************************************************************
void display_menu_option_set_time()
{
  // display menu top line
  lcd.setCursor(0, 0);
  // thick dash character
  lcd.write(0);
  lcd.print("MENU");
  // display 9 thick dash characters
  for (int dash = 1; dash <= 9; dash++)
  {
    lcd.write(0);
  }

  // display arrow symbols
  lcd.setCursor(15, 0);
  // up arrow
  lcd.write(6);
  lcd.setCursor(15, 1);
  // down arrow
  lcd.write(7);

  // display menu bottom line
  lcd.setCursor(0, 1);
  //         0123456789012345 - LCD screen character counter
  lcd.print("Set the time    ");
}

//******************************************************************************
void display_menu_option_set_date()
{
  // display menu top line
  lcd.setCursor(0, 0);
  // thick dash character
  lcd.write(0);
  lcd.print("MENU");
  // display 9 thick dash characters
  for (int dash = 1; dash <= 9; dash++)
  {
    lcd.write(0);
  }

  // display arrow symbols
  lcd.setCursor(15, 0);
  // up arrow
  lcd.write(6);
  lcd.setCursor(15, 1);
  // down arrow
  // lcd.write(7);

  lcd.setCursor(0, 1);
  //         0123456789012345 - LCD screen character counter
  lcd.print("Set the date    ");
}


//******************************************************************************
void midnight_reset()
{
  // check for midnight
  if ( (Hour == 0) && (Minute == 0) )
  {
    // reset the Hall sensor fail indicator
    hallSensorFail = false;

    // turn off the feeding success LED's
    digitalWrite(LED_SUCCESS1_PIN, LOW);
    digitalWrite(LED_SUCCESS2_PIN, LOW);

    // reset overriding Feeding time(s)
    //manualCancelFeed1 = false;
    //manualCancelFeed2 = false;
  }
}

//******************************************************************************
void display_time()
{
  // read the time and date from the RTC3231 chip
  get_time();
  get_date();
  // display the values on the LCD
  lcd.setCursor(11, 0);
  leading_zero(Hour);
  lcd.print(":");
  leading_zero(Minute);
  lcd.setCursor(14, 1);
  leading_zero(Second);
}

//******************************************************************************
void displayFeedingAmouts()
{
  // read the values from the Arduino EEPROM
  get_feedamount();
  // display the stored feeding amounts
  lcd.setCursor(8, 0);
  lcd.write(3);
  lcd.print(feedAmount1);
  lcd.setCursor(8, 1);
  lcd.write(3);
  lcd.print(feedAmount2);
}

//******************************************************************************
void displayFeedingTimes()
{
  //----------------------------------
  //-- first feeding time           --
  //----------------------------------
  // display '1' symbol
  lcd.setCursor(0, 0);
  lcd.write(4);

  // display 'bell' symbol (or not)
  lcd.setCursor(6, 0);
  if (feed_time1_active == true)
  {
    if (manualCancelFeed1 == true)
    {
      // blink the bell symbol and led
      if (blink_state == 0)
      {
        // display the Bell symbol
        lcd.write(1);
        // turn on the cancel1 button LED indicator
        digitalWrite(LED_CANCEL1_PIN, HIGH);
      }
      else
      {
        // remove the Bell symbol
        lcd.print(" ");
        // turn off the cancel1 button LED indicator
        digitalWrite(LED_CANCEL1_PIN, LOW);
      }
    }
    else
    {
      // Just display the Bell symbol
      lcd.write(1);
      // turn off the cancel1 button LED indicator
      digitalWrite(LED_CANCEL1_PIN, LOW);
    }
  }

  else
  {
    // alarm active not true so no Bell symbol
    lcd.print(" ");
    // turn off the cancel1 button LED indicator
    digitalWrite(LED_CANCEL1_PIN, LOW);
  }

  // display alarm time 1
  lcd.setCursor(1, 0);
  leading_zero(feed_time1_hour);
  lcd.print(":");
  leading_zero(feed_time1_minute);

  //----------------------------------
  //-- second feeding time          --
  //----------------------------------
  // display '2' symbol
  lcd.setCursor(0, 1);
  lcd.write(5);

  // display 'bell' symbol (or not)
  lcd.setCursor(6, 1);
  if (feed_time2_active == true)
  {
    if (manualCancelFeed2 == true)
    {
      // blink the bell symbol and led
      if (blink_state == 0)
      {
        // display the Bell symbol
        lcd.write(1);
        // turn on the cancel1 button LED indicator
        digitalWrite(LED_CANCEL2_PIN, HIGH);
      }
      else
      {
        // remove the Bell symbol
        lcd.print(" ");
        // turn off the cancel1 button LED indicator
        digitalWrite(LED_CANCEL2_PIN, LOW);
      }
    }
    else
    {
      // Just display the Bell symbol
      lcd.write(1);
      // turn off the cancel1 button LED indicator
      digitalWrite(LED_CANCEL2_PIN, LOW);
    }
  }

  else
  {
    // alarm active not true so no Bell symbol
    lcd.print(" ");
    // turn off the cancel1 button LED indicator
    digitalWrite(LED_CANCEL2_PIN, LOW);
  }

  // display alarm time 2
  lcd.setCursor(1, 1);
  leading_zero(feed_time2_hour);
  lcd.print(":");
  leading_zero(feed_time2_minute);

  // call blinkfunction
  blinkFunction();
}

//******************************************************************************
void set_feedAmount()
{
  lcd.setCursor(0, 0);
  // thick dash character
  lcd.write(0);
  //         0123456789012345 - LCD screen character counter
  lcd.print("SET PORTIONS    ");
  // display 8 thick dash characters
  for (int dash = 1; dash <= 3; dash++)
  {
    lcd.write(0);
  }

  switch (state)
  {
    //---------------------------------------
    case EDIT_FEED_AMOUNT1:

      lcd.setCursor(0, 1);
      lcd.print("Feed1:");
      if (long_press_button == false)
      {
        if (blink_state == 0)
        {
          lcd.setCursor(6, 1);
          lcd.print(feedAmount1);
        }
        else
        {
          lcd.setCursor(6, 1);
          lcd.print(" ");
        }
      }
      else
      {
        lcd.setCursor(6, 1);
        lcd.print(feedAmount1);
      }

      lcd.setCursor(9, 1);
      lcd.print("Feed2:");
      lcd.print(feedAmount2);
      break;
    //---------------------------------------
    case EDIT_FEED_AMOUNT2:

      lcd.setCursor(0, 1);
      lcd.print("Feed1:");
      lcd.print(feedAmount1);

      if (long_press_button == false)
      {
        if (blink_state == 0)
        {
          lcd.setCursor(15, 1);
          lcd.print(feedAmount2);
        }
        else
        {
          lcd.setCursor(15, 1);
          lcd.print(" ");
        }
      }
      else
      {
        lcd.setCursor(15, 1);
        lcd.print(feedAmount2);
      }
      break;
  }

  // call blink funtion
  blinkFunction();
}
//******************************************************************************
void set_time()
{
  lcd.setCursor(0, 0);
  // thick dash character
  lcd.write(0);
  //         0123456789012345 - LCD screen character counter
  lcd.print("SET THE TIME    ");
  for (int dash = 1; dash <= 3; dash++)
  {
    lcd.write(0);
  }

  switch (state)
  {
    //---------------------------------------
    case EDIT_HOUR:
      if (long_press_button == false)
      {
        if (blink_state == 0)
        {
          lcd.setCursor(4, 1);
          leading_zero(Hour);
        }
        else
        {
          lcd.setCursor(4, 1);
          lcd.print("  ");
        }
      }
      else
      {
        lcd.setCursor(4, 1);
        leading_zero(Hour);
      }
      lcd.print(":");
      leading_zero(Minute);
      break;
    //---------------------------------------
    case EDIT_MINUTE:
      lcd.setCursor(4, 1);
      leading_zero(Hour);
      lcd.print(":");
      if (long_press_button == false)
      {
        if (blink_state == 0) {
          lcd.setCursor(7, 1);
          leading_zero(Minute);
        }
        else
        {
          lcd.setCursor(7, 1);
          lcd.print("  ");
        }
      }
      else
      {
        lcd.setCursor(7, 1);
        leading_zero(Minute);
      }
      break;
  }

  // call blink funtion
  blinkFunction();
}

//******************************************************************************
void set_date()
{
  lcd.setCursor(0, 0);
  // thick dash character
  lcd.write(0);
  //         0123456789012345 - LCD screen character counter
  lcd.print("SET THE TIME    ");
  for (int dash = 1; dash <= 3; dash++)
  {
    lcd.write(0);
  }

  switch (state)
  {
    case EDIT_DAY:
      if (long_press_button == false)
      {
        if (blink_state == 0)
        {
          lcd.setCursor(4, 1);
          leading_zero(Day);
        }
        else
        {
          lcd.setCursor(4, 1);
          lcd.print("  ");
        }
      }
      else
      {
        lcd.setCursor(4, 1);
        leading_zero(Day);
      }
      lcd.print("/");
      leading_zero(Month);
      lcd.print("/");
      leading_zero(Year);
      break;
    //---------------------------------------
    case EDIT_MONTH:
      lcd.setCursor(4, 1);
      leading_zero(Day);
      lcd.print("/");
      if (long_press_button == false)
      {
        if (blink_state == 0)
        {
          lcd.setCursor(7, 1);
          leading_zero(Month);
        }
        else
        {
          lcd.setCursor(7, 1);
          lcd.print("  ");
        }
      }
      else
      {
        lcd.setCursor(7, 1);
        leading_zero(Month);
      }

      lcd.print("/");
      leading_zero(Year);
      break;
    //---------------------------------------
    case EDIT_YEAR:

      lcd.setCursor(4, 1);
      leading_zero(Day);
      lcd.print("/");
      leading_zero(Month);
      lcd.print("/");
      if (long_press_button == false)
      {
        if (blink_state == 0)
        {
          lcd.setCursor(10, 1);
          leading_zero(Year);
        }
        else
        {
          lcd.setCursor(10, 1);
          lcd.print("  ");
        }
      }
      else
      {
        lcd.setCursor(10, 1);
        leading_zero(Year);
      }
      break;
  }

  // call blink funtion
  blinkFunction();
}

//******************************************************************************
void set_feeding1_time()
{
  lcd.setCursor(0, 0);
  // thick dash character
  lcd.write(0);
  //         0123456789012345 - LCD screen character counter
  lcd.print("FEEDING TIME 1");
  // thick dash character
  lcd.write(0);
  /////////////////////////////////////
  if (feed_time1_active == true)
  {
    lcd.setCursor(8, 1);
    lcd.print("ENABLED ");
  } else
  {
    lcd.setCursor(8, 1);
    lcd.print("DISABLED");
  }

  switch (state)
  {
    //---------------------------------------
    case EDIT_FEED_TIME1_HOUR:
      if (long_press_button == false)
      {
        if (blink_state == 0)
        {
          lcd.setCursor(1, 1);
          leading_zero(feed_time1_hour);
        }
        else
        {
          lcd.setCursor(1, 1);
          lcd.print("  ");
        }
      }
      else
      {
        lcd.setCursor(1, 1);
        leading_zero(feed_time1_hour);
      }
      lcd.print(":");
      leading_zero(feed_time1_minute);
      break;
    //---------------------------------------
    case EDIT_FEED_TIME1_MINUTE:
      lcd.setCursor(1, 1);
      leading_zero(feed_time1_hour);
      lcd.print(":");
      if (long_press_button == false)
      {
        if (blink_state == 0)
        {
          lcd.setCursor(4, 1);
          leading_zero(feed_time1_minute);
        }
        else
        {
          lcd.setCursor(4, 1);
          lcd.print("  ");
        }
      }
      else
      {
        lcd.setCursor(4, 1);
        leading_zero(feed_time1_minute);
      }
      break;
    //---------------------------------------
    case EDIT_FEED_TIME1_ON_OFF:
      lcd.setCursor(4, 1);
      leading_zero(feed_time1_minute);

      if (feed_time1_active == true)
      {
        if (blink_state == 0)
        {
          lcd.setCursor(8, 1);
          lcd.print("ENABLED ");
        }
        else
        {
          lcd.setCursor(8, 1);
          lcd.print("        ");
        }
      }
      else
      {
        if (blink_state == 0)
        {
          lcd.setCursor(8, 1);
          lcd.print("DISABLED");
        }
        else
        {
          lcd.setCursor(8, 1);
          lcd.print("        ");
        }
      }
      break;
  }

  // call blink funtion
  blinkFunction();
}

//******************************************************************************
void set_feeding2_time()
{
  lcd.setCursor(0, 0);
  // thick dash character
  lcd.write(0);
  //         0123456789012345 - LCD screen character counter
  lcd.print("FEEDING TIME 2");
  // thick dash character
  lcd.write(0);

  /////////////////////////////////////
  if (feed_time2_active == true)
  {
    lcd.setCursor(8, 1);
    lcd.print("ENABLED ");
  } else
  {
    lcd.setCursor(8, 1);
    lcd.print("DISABLED");
  }

  switch (state)
  {
    //---------------------------------------
    case EDIT_FEED_TIME2_HOUR:
      if (long_press_button == false)
      {
        if (blink_state == 0)
        {
          lcd.setCursor(1, 1);
          leading_zero(feed_time2_hour);
        }
        else
        {
          lcd.setCursor(1, 1);
          lcd.print("  ");
        }
      }
      else
      {
        lcd.setCursor(1, 1);
        leading_zero(feed_time2_hour);
      }
      lcd.print(":");
      leading_zero(feed_time2_minute);
      break;
    //---------------------------------------
    case EDIT_FEED_TIME2_MINUTE:
      lcd.setCursor(1, 1);
      leading_zero(feed_time2_hour);
      lcd.print(":");
      if (long_press_button == false)
      {
        if (blink_state == 0)
        {
          lcd.setCursor(4, 1);
          leading_zero(feed_time2_minute);
        }
        else
        {
          lcd.setCursor(4, 1);
          lcd.print("  ");
        }
      }
      else
      {
        lcd.setCursor(4, 1);
        leading_zero(feed_time2_minute);
      }
      break;
    //---------------------------------------
    case EDIT_FEED_TIME2_ON_OFF:
      lcd.setCursor(4, 1);
      leading_zero(feed_time2_minute);

      if (feed_time2_active == true)
      {
        if (blink_state == 0)
        {
          lcd.setCursor(8, 1);
          lcd.print("ENABLED ");
        }
        else
        {
          lcd.setCursor(8, 1);
          lcd.print("        ");
        }
      }
      else
      {
        if (blink_state == 0)
        {
          lcd.setCursor(8, 1);
          lcd.print("DISABLED");
        }
        else
        {
          lcd.setCursor(8, 1);
          lcd.print("        ");
        }
      }
      break;
  }

  // call blink funtion
  blinkFunction();
}

//******************************************************************************
void get_time()
{
  Wire.beginTransmission(0x68);
  // set register to zero
  Wire.write(0);
  Wire.endTransmission();
  // 3 bytes (sec, min, hour)
  Wire.requestFrom(0x68, 3);
  Second = bcdToDec(Wire.read() & 0x7f);
  Minute = bcdToDec(Wire.read() );
  Hour   = bcdToDec(Wire.read() & 0x3f);
}

//******************************************************************************
void get_date()
{
  Wire.beginTransmission(0x68);
  // set register to 3 (day)
  Wire.write(4);
  Wire.endTransmission();
  // 3 bytes (day, month, year)
  // DOW, we get from Time.h library
  Wire.requestFrom(0x68, 3);
  Day   = bcdToDec(Wire.read());
  Month = bcdToDec(Wire.read());
  Year  = bcdToDec(Wire.read());
}

//******************************************************************************
void write_time()
{
  Wire.beginTransmission(0x68);
  Wire.write(0x00);
  Second = 0;
  Wire.write(decToBcd(Second));
  Wire.write(decToBcd(Minute));
  Wire.write(decToBcd(Hour));
  Wire.write(0x00);
  Wire.endTransmission();
}

//******************************************************************************
void write_date()
{
  Wire.beginTransmission(0x68);
  Wire.write(4);
  // Wire.write(decToBcd(DoW));
  Wire.write(decToBcd(Day));
  Wire.write(decToBcd(Month));
  Wire.write(decToBcd(Year));
  Wire.endTransmission();
}

//******************************************************************************
void write_feeding_time1()
{
  // save alarm time to EEPROM of the Arduino
  EEPROM.write(0, feed_time1_hour);
  EEPROM.write(1, feed_time1_minute);
  EEPROM.write(2, feed_time1_active);
}

//******************************************************************************
void write_feeding_time2()
{
  // save alarm time to EEPROM of the Arduino
  EEPROM.write(3, feed_time2_hour);
  EEPROM.write(4, feed_time2_minute);
  EEPROM.write(5, feed_time2_active);
}

//******************************************************************************
void write_feedamount()
{
  // save feed amount values to EEPROM of the Arduino
  EEPROM.write(6, feedAmount1);
  EEPROM.write(7, feedAmount2);
}

//******************************************************************************
void get_feedamount()
{
  // save feed amount values to EEPROM of the Arduino
  feedAmount1 = EEPROM.read(6);
  if (feedAmount1 > 9) feedAmount1 = 0;
  feedAmount2 = EEPROM.read(7);
  if (feedAmount2 > 9) feedAmount2 = 0;
}

//******************************************************************************
void get_feed_time1()
{
  // read feed time 1 from Arduino EEPROM
  feed_time1_hour = EEPROM.read(0);
  if (feed_time1_hour > 23) feed_time1_hour = 0;
  feed_time1_minute = EEPROM.read(1);
  if (feed_time1_minute > 59) feed_time1_minute = 0;
  feed_time1_active = EEPROM.read(2);
}

//******************************************************************************
void get_feed_time2()
{
  // read feed time 2 from Arduino EEPROM
  feed_time2_hour = EEPROM.read(3);
  if (feed_time2_hour > 23) feed_time2_hour = 0;
  feed_time2_minute = EEPROM.read(4);
  if (feed_time2_minute > 59) feed_time2_minute = 0;
  feed_time2_active = EEPROM.read(5);
}

//******************************************************************************
// check communication with RTC
void check_RTC()
{
  if (timeStatus() != timeSet)
    // RTC could not be read
    RTC_error = true;
  else
    // RTC could be read
    RTC_error = false;
}

//******************************************************************************
// convert dec to BCD value
byte decToBcd(byte val)
{
  return ( (val / 10 * 16) + (val % 10) );
}

//******************************************************************************
// convert BCD to dec value
byte bcdToDec(byte val)
{
  return ( (val / 16 * 10) + (val % 16) );
}

//******************************************************************************
void leading_zero(int digits)
{
  // dispay leading zero when only one digit
  if (digits < 10)
  {
    lcd.print("0");
  }
  // display value
  lcd.print(digits);

}

//******************************************************************************
void blinkFunction()
{
  // Blink function
  blink_currentMillis = millis();
  if (blink_currentMillis - blink_previousMillis > blink_interval)
  {
    //reset blink timer
    blink_previousMillis = blink_currentMillis;
    // invert value of blink_state
    blink_state = !blink_state;
  }
}
//******************************************************************************
void displaySpinningWheel()
{

  // display on bottom line, position 15 a 'spinning wheel' symbol
  lcd.setCursor(15, 1);
  switch (spinningWheelSymbol)
  {
    case 1:
      lcd.print("|");
      break;
    case 2:
      lcd.print("/");
      break;
    case 3:
      lcd.print("-");
      break;
    case 4:
      // call backslash character
      // (not available in the LCD display characterset)
      lcd.write(2);
      break;
    case 5:
      // start 'spinning wheel' again with symbol "|"
      spinningWheelSymbol = 1;
      break;
  }

  // check if it is time to change spinning wheel symbol
  spinningWheel_currentMillis = millis();
  if (spinningWheel_currentMillis - spinningWheel_previousMillis
      > spinningWheel_interval)
  {
    //reset busy timer
    spinningWheel_previousMillis = spinningWheel_currentMillis;
    // increase value to select next symbol
    spinningWheelSymbol += 1;
  }
}
//******************************************************************************
void startFeederServo()
{
  digitalWrite(SERVO_OUTPUT_PIN, HIGH);
  // 950µSec = turn full speed  CCW (for the HSR-2645CRH Servo)
  // use 2050µSec to turn the Servo full speed CW
  delayMicroseconds(950);
  digitalWrite(SERVO_OUTPUT_PIN, LOW);
  delay(20);
}

//******************************************************************************
void stopFeederServo()
{
  digitalWrite(SERVO_OUTPUT_PIN, HIGH);
  // ~1490µSec = stop servo (for the HSR-2645CRH Servo)
  delayMicroseconds(1490);
  digitalWrite(SERVO_OUTPUT_PIN, LOW);
  delay(20);
}

//******************************************************************************
void activateFeeder(int portions)
{
  // reset the feeder Servo turn counter
  // every turn of 60 degrees = 1 portion
  turns = 1;
  int rotationTime = 0;

  // reset spinning wheel indicator
  spinningWheelSymbol = 1;

  // set the time-out variable as backup for a possibly failing hall sensor
  hallSensorBackup_currentMillis = millis();

  // activate the feeder Servo until the number of turns exceeds the
  // desired number of feed 'portions' requested, by using the
  // 'portions' variable which is given when calling this function
  while (turns <= portions)
  {
    // display the current portion being delivered, in the
    // lower left corner of the LCD display
    lcd.setCursor(0, 1);
    lcd.print(turns);

    // display 'spinning wheel'
    displaySpinningWheel();

    // activate feeder Servo
    startFeederServo();

    // check if the Hall sensor has been activated by
    // the magnets on the feeder
    if ( hallSensorActivated == true
        ||
        millis() - hallSensorBackup_currentMillis > hallSensorBackup_interval )
    {
      // store the time for one rotation of the feeder
      rotationTime = millis() - hallSensorBackup_currentMillis;

      // Hall sensor FAIL check:
      // if we got here by the timing instead of the Hall sensor being
      // activated (and variable set via the interrupt routine),
      // set the hallSensorFail variable to blink the the Hall sensor FAIL LED.
      if (hallSensorActivated == false)
      {
        hallSensorFail = true;
      }

      // reset variable and wait for next hall sensor interrupt
      hallSensorActivated = false;
      hallSensorBackup_currentMillis = millis();

      // check if user wants to cancel the feeding
      buttonBack.read();
      if (buttonBack.wasPressed())
      {
        // reset variables
        hallSensorActivated = false;
        // break While loop
        break;
      }

      // one turn completed, increment the counter
      turns += 1;

      // turn off Hall sensor LED, ready for the next activation
      // by the Hall sensor interrupt function
      digitalWrite(LED_HALL_SENSOR_PIN, LOW);

    }//if (hallSensorActivated == true)
  }//while(1)

  // number of requested turns (portions) done or user has cancelled the
  // feeding process.
  // Remove the 'spinning wheel' symbol
  lcd.setCursor(15, 1);
  lcd.print(" ");
  // delay to stop the feeder at the right position
  delay(FEEDER_STOP_DELAY);
  // stop the feeder servo
  stopFeederServo();

  // clear screen and display message
  lcd.clear();
  lcd.setCursor(0, 0);
  //        "0123456789012345 - LCD screen character counter"
  lcd.print("* Feeding done *");
  // when manual feed button was pressed, display after one turn
  // the timing between one turn, just for fun but also to determine
  // the value of the hallSensorBackup_interval! Set this value 100ms
  // larger than value of one turn of the feeder.
  if (manualFeed == true)
  {
    lcd.setCursor(0, 1);
    lcd.print("1 rotation=");
    lcd.print(rotationTime);
    lcd.print("ms");
  }
  delay(2000);
  lcd.clear();
}

//******************************************************************************
void check_LcdBacklight()
{
  if ( millis() - lcdBacklight_currentMillis > LCD_BACKLIGHT_ON_TIME)
  {
    lcd.noBacklight();
  }
}

//******************************************************************************
void lcd_backlight_ON()
{
  // reset lcd backlight timer
  lcdBacklight_currentMillis = millis();
  // turn lcd backlight ON
  lcd.backlight();
}
//******************************************************************************
void ledsAndLcdDisplayStartup()
{
  //LED test
  digitalWrite(LED_CANCEL1_PIN, HIGH);
  digitalWrite(LED_CANCEL2_PIN, HIGH);
  digitalWrite(LED_SUCCESS1_PIN, HIGH);
  digitalWrite(LED_SUCCESS2_PIN, HIGH);
  digitalWrite(LED_HALL_SENSOR_PIN, HIGH);

  //LCD test, just for the fun of it ;)
  lcd.clear();
  for (int p = 0; p <= 15; p++)
  {
    // block character fill from left to right on line 0
    lcd.setCursor(p, 0);
    lcd.write(255);
    // block character fill from right to left on line 1
    lcd.setCursor(15 - p, 1);
    lcd.write(255);
    delay(50);
  }
  lcd.clear();
  // now do it in reverse :)
  for (int p = 0; p <= 15; p++)
  {
    // block character fill from right to left on line 0
    lcd.setCursor(15 - p, 0);
    lcd.write(255);
    // block character fill from left to right on line 1
    lcd.setCursor(p, 1);
    lcd.write(255);
    delay(50);
  }

  // display startup screen
  lcd.clear();
  lcd.setCursor(0, 0);
  // LCD pos:0123456789012345 - LCD screen character counter
  lcd.print("Pet Feed-O-Matic");
  lcd.setCursor(0, 1);
  lcd.print("v1.1_UK 20-02-18");
  delay(5000);
  lcd.clear();

  // clear lcd display and turn led's off
  lcd.clear();
  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);

}
//******************************************************************************
void hallSensorCheck()
{
  if (hallSensorFail == true)
  {
    // blink the Hall sensor LED
    if (blink_state == 0)
    {
      // turn on the Hall sensor LED
      digitalWrite(LED_HALL_SENSOR_PIN, HIGH);
    }
    else
    {
      // turn on the Hall sensor LED
      digitalWrite(LED_HALL_SENSOR_PIN, LOW);
    }
    // call the blink function
    blinkFunction();
  }
}

Schematics

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

Comments

Similar projects you might like

HOW PIR SENSOR WORK

Project tutorial by DIY Partners

  • 2,340 views
  • 6 comments
  • 21 respects

4-Stroke Digital Clock With Arduino

Project tutorial by LAGSILVA

  • 7,358 views
  • 8 comments
  • 30 respects

RGB Camera Backlight Control with Android Mobile

Project tutorial by DIY Partners

  • 116 views
  • 0 comments
  • 2 respects

"Living Art" Arduino-Controlled Desk Lamp

Project tutorial by Modustrial Maker

  • 1,424 views
  • 0 comments
  • 5 respects

Dead Bug Valentine

Project tutorial by wrightmac

  • 1,089 views
  • 0 comments
  • 7 respects
Add projectSign up / Login