loading

This is my automatic pet feeder. Powered by arduino, using a 3D printed auger, and programmable with two feeding times with a user set quantity of food, with a battery backed up internal clock.

My cats drive me nuts in the morning begging for food so I decided it was time to robotize feeding time

I got frustrated with pet feeders available in the UK. They are either the type that rotate and unveil a meal, up to four times only, or the only half decent machine with a hopper is horrific to program, forgets everything if you remove the batteries, and frequently fails to even deliver any food! I program here and there, and the last machine I used made me so mad just trying to set the feeding times I nearly threw it out the window.

America has some good machines but they start at $300 USD, and shipping would be painful to the UK, so I decided to make one!

Step 1: Materials Needed

Electronics:

1 x normal RC servo - regular size

1 x continuous RC servo - off the shelf or hacked.

1 x 3D printed Auger parts (see auger step) (pennies if you have a 3D printer)

1 x 1 1/2" PVC T-Piece pipe (ebay £2.89) http://www.ebay.co.uk/itm/121534086943?_trksid=p20...

1 x Arduino (I used an Uno but any type will do)

OR ... for PCB (1 x ATMega328p, crystal, 2 caps, 5v voltage reg) - google "arduino breadboard"

1 x KY-040 Rotary encoder on board (ebay £3.85) http://www.ebay.co.uk/itm/181595206550?_trksid=p20...

1 x 16x2 LCD HD44780 compatible (ebay £2.53) http://www.ebay.co.uk/itm/281211020326?_trksid=p20...

1 x small push button switch (non-latching)

1 x food hopper - I used one for storing food with a firmly sealable lid - to stop feline criminal activity.

1 x mounting box - I built one from MDF

1 x 12v power supply - around 500Ma+ to be safe - driving the servos. You may need more.

Step 2: The Basics - Making the Auger (food Feed Mechanism)

The 1st hurdle is to make an auger (screw type feeder). Previously this was too much of a hurdle for me, but now with 3D printing, I just downloaded a design from thingiverse and printed it! If you like tinkering, build a 3D printer! - It's fun to make and amazingly useful to own. I built a Prusa i3 for around £350.

(http://www.thingiverse.com/thing:27854) - Credit to Kitlaan for his initial design which my machine is heavily based on.

His physical design is good, but the code is too simplistic for my needs, there is no display, no multiple feedings, and no user variable portion sizes. This is where the Arduino comes in and saves the day. I've previous experience with Arduino, but have never used an LCD or a rotary encoder - but it's no problem - the Arduino tutorials and examples are amazingly well written. I do have experience with battery backed up real time clocks (RTC) so adding one was a no-brainer.

The auger print comes in a few files - one for the auger, one for the servo mount/tube mount, and a servo spacer, which I didn't need. I printed the auger in one go (the author provides a split version too so you can print smaller objects and glue them together) - I have a Prusa i3 which had no issues with this. The auger is 10cm tall, the i3 prints up to 20cm tall. I printed with no support material and it came out fine. I had to trim the base with a knife a bit where the recess for the servo horn is, as it has collapsed a fraction due to the lack of support material. You can tell slic3r to print support material for only the 1st "x" rows, but I didn't think to do that.

I used a Futaba digital servo I had to hand, any servo will do, it doesn't need to be digital. You need it to be continuous ( no end stops) - these can be bought from places like sparkfun, but I just opened the servo , removed the plastic bump on one of the cogs that stops it rotating infinitely, and removed the potentiometer. Voila! - a continuous servo. Just search the web - there are loads of how-to's regarding changing regular servos to continuous servos.

I screwed the servo to the auger base, making sure it was level and rotated without wobble, and then test-fitted it in a piece of 1 1/2 inch plastic T-piece tubing (commonly used for hot tub plumbing) - cost £2.90 off ebay (PVC solvent weld pipe - ponds / hot tubs etc). Once I was happy it would rotate without binding I fixed it in place with hot glue where it inserts into the pipe.

Now I have an auger! (ho ho ho) :-)

Step 3: Making the Box

The box/base/enclosure is a simple MDF box (9mm). I first made a box, then cut out the base with a jigsaw, the holes for the auger with a hole cutting saw you attach to your drill, and the square holes for the control panel and servo with a jigsaw again.

I didn't want the hassle of trying to cut neat enough holes (square holes!) to mount the LCD so istead I 3D printed a home designed facia - this takes the LCD, push button (for manual feed) and the rotary encoder knob. You can then screw this to the wooden box and it looks nice and neat!

The food agitator is again 3D printed and screws directly to a servo horn. The round spout is also 3D printed (credit to Kitlaan again on Thingiverse http://www.thingiverse.com/thing:28483 ) I could have made the auger tube stick out the front of the box more negating the need for the spout but I wanted the top hole a littel further back from the edge so if I decide to replace the current food hopper with a larger one, the hole won't be right at the edge.

The food hopper screws onto the box and is easily replaceable / upgradable.

Time will tell if my cats learn to press the manual feed button!

Step 4: Arduino - the Hardware

This whole project is driven by an Arduino. I highly recommend you build a prototype with a regular arduino and a breadboard. It's the best way to understand what everything does and save mistake later. I've included the Fritzing file so you can load it up and see the breadboard layout in detail (cables are labelled in the program where nescessary). Fritzing software is free/donateware.

I built a breadboard with the LCD on first, then learnt the basics of how to control it. Then I added a rotary encoder and learnt how to use that, then I combined the two in code. Finally I added the real time clock , the servos, and started programming the final code.

You will notice I said "servos". The second servo is a regular servo, used to agitate the pet food before each feed, to stop it clumping (the pet food is known as kibbles to the USofA I believe :-) )

After I was happy with the arduino uno and breadboard test I replaced the Arduino uno with pure components - the ATMEGA328P chip, a few caps, 5v regulator (power supply) and a crystal - basically - an arduino without the circuit board. It's dead simple to knock up (google arduino breadboard) and is a good habit to get into - saves you a fortune on arduino uno's and means you can design one PCB to house everything. The picure of the breadboards with a rats nest of wiring is the "pure version".

This version tested ok so I then drew the circuit in PCB Wizard (a great package that Maplin sells for £40 ish) and converted it into a PCB drawing using the auto routing feature.I've included the file.

I print and etch my PCBs using the laser printer method. I've made an Instructable on it - have a look under my name - it works every time (the PCB, not my name). I've attached the PDFs for printing.

Finally I soldered all the components on and hooked up the LCD, rotary encoder, and manual feed button with ribbon cable.

You don't need to go that far - you can run your feeder with a regular Arduino, and a breadboard, or you could use that PCB board where you cut links with a knife - the choice is yours. The hardware is kind of academic - it's all in the code.

Step 5: Arduino - the Code

The code is not as intimidating as it first looks, and I've tried to annotate it as much as possible.

In summary it:

Reads the time from the RTC chip

Displays the time, and the default feed times and quantity

Checks to see if the rotary encoder has been pressed if it has: it then goes into the programming routine, cycling in order through clock, portion size, feed time 1, feed time 2. When you are done, it continues below....

Checks if it is feeding time 1 or 2 - if so it feeds.

Checks if you have pressed the manual feed button, if so it feeds. And then it starts all over again.

This is the first time I've used an interrupt service routine - for the rotary encoder. This means if you turn it, whatever the program is busy doing , it registers your action. So it's ALWAYS listening for the encoder.

I thought the routine for setting the clock/feed times & qty would be daunting, but I just broke it down into simple steps. 1st I learnt how to make the cursor flash on the screen, easy enough. Then I positioned the cursor on the hour digit of the clock, and told it to increase the hour as the encoder was turned (or decrease), then once the encoder was pressed, write the new "hour" to the real time clock, and then move the cursor to the minute, and start changing that, and so on. Was surprisingly easy.

The feeding routines are simple "if" routines - i.e. "if" the times matched, then do the feeding routine. Simple as that. I had to add a bit so it checks the seconds or else it feeds for a whole minute, as the program loops and checks the time again - and it is still feeding time! so it repeats... adding a check on the seconds stops this as it takes at least a second for the feeding cycle.

The program isn't perfect - sometimes turning the encoder doesn't always increment the number on screen. I think this is down to my inexperience. It doesn't however hinder the operation of the machine!

I've included a screen grab of my arduino IDE for no real reason other than I didn't want this section to be without a picture :-)

Obviously the video on the 1st page shows me pressing the manual feed button, but of course, it auto-feeds at the two set times (you could set both times the same and it would only feed once).

/*

Automatic Auger Audiono pet feeder Copyright Roger Donoghue 28/03/2015 all rights reserved. For personal use only.
Not for commercial use or resale.
Allows you to set 2 feeding times and the quantity as a multiple of the default feed quantity.
Uses a DS1307 real time clock to keep the time, with a rechargable battery built in.
(You can use the arduino RTC example code in the IDE to set the clock , or use the rotary encoder as intended)

*/
// include the library code:
#include <LiquidCrystal.h>
#include <Wire.h>      // needed for the RTC libraty
#include <Time.h>
#include <DS1307RTC.h> // Real Time Clock Library
#include <Servo.h>

// initialize the library with the numbers of the interface pins dor the LCD
LiquidCrystal lcd(12, 11, 5, 8, 7, 6);

#define PIN_SERVO 9

Servo feedServo;
Servo stirServo;
int pos = 0;
volatile boolean TurnDetected;
volatile boolean up;
const int PinCLK=2;                   // Used for generating interrupts using CLK signal
const int PinDT=3;                    // Used for reading DT signal
const int PinSW=4;                    // Used for the push button switch of the Rotary Encoder
const int buttonPin = A3;             // the number of the pushbutton pin for manual feed 13
int buttonState = 0;                  // variable for reading the manual feed pushbutton status
int feed1hour = 07;                   // variables for feeding times and quantity
int feed1minute = 00;
int feed2hour = 17;
int feed2minute = 30;
int feedQty = 4;
int feedRate = 800;   //a pwm rate the triggers forward on the servo 75
int feedReversal = 80; //a pwm rate that triggers reverse on the servo
                       // play with these numbers for your servo. Mine is a Futaba digital servo
                       // that I removed the pot from and the plastic lug, to make it continuous.

void isr ()  {                    // Interrupt service routine is executed when a HIGH to LOW transition is detected on CLK
 if (digitalRead(PinCLK))         // this keeps an eye out for the rotary encoder being turned regardless of where the program is
   up = digitalRead(PinDT);       // currently exectuting - in other words, during the main loop this ISR will always be active
 else
   up = !digitalRead(PinDT);
 TurnDetected = true;
}


void setup ()  {
   // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  // setup the Rotary encoder
 pinMode(PinCLK,INPUT);
 pinMode(PinDT,INPUT);  
 pinMode(PinSW,INPUT);
 pinMode(buttonPin, INPUT);
 attachInterrupt (0,isr,FALLING);   // interrupt 0 is always connected to pin 2 on Arduino UNO
    lcd.setCursor(17,0);            
    lcd.print("Roger Donoghue's");  // A bit of fun :-)
    lcd.setCursor(17,1);
    lcd.print("  Cat-O-Matic");
     for (int positionCounter = 0; positionCounter < 17; positionCounter++) {
     // scroll one position left:
     lcd.scrollDisplayLeft(); 
     // wait a bit:
     delay(150);
          }
      delay(3000);
      for (int positionCounter = 0; positionCounter < 17; positionCounter++) {
     // scroll one position left:
     lcd.scrollDisplayRight(); 
     // wait a bit:
     delay(150);
     
             }  // end of fun
     lcd.setCursor(17,0);
     lcd.print("                ");
     lcd.setCursor(17,1);
     lcd.print("                ");  
        }
  
void loop ()  {  //Main program loop - most things in here!
    static long virtualPosition=0;    // without STATIC it does not count correctly!!!
    tmElements_t tm;    // This sectionm reads the time from the RTC, sets it in tmElements tm (nice to work with), then displays it.
    RTC.read(tm); 
    lcd.setCursor(0, 0);
    printDigits(tm.Hour); //call to print digit function that adds leading zeros that may be missing
    lcd.print(":");
    printDigits(tm.Minute);
    lcd.print(":");
    printDigits(tm.Second);
    lcd.print("  ");
    lcd.print("Qty ");
    lcd.print(feedQty);
    lcd.print(" ");
    lcd.setCursor(0,1);
    lcd.print("1)");
    printDigits(feed1hour);
    lcd.print(":");
    printDigits(feed1minute);
    lcd.print(" 2)");
    printDigits(feed2hour);
    lcd.print(":");
    printDigits(feed2minute);
    
    
// MAIN BREAKOUT "IF" SECION BELOW THAT MONITORS THE PUSH BUTTON AND ENTERS PROGRAMMING IF IT'S PUSHED 

 if (!(digitalRead(PinSW))) {      // check if pushbutton is pressed
                                   // if YES then enter the programming subroutine
    lcd.blink();   // Turn on the blinking cursor:
    lcd.setCursor(5,0);
    lcd.print(" SET");
    virtualPosition = tm.Hour;  //needed or the hour will be zero each time you change the clock.
       do  {
            lcd.setCursor(0,0);   // put cursor at Time Hour  
            delay(500);   // Delay needed or same button press will exit do-while as while is checking for another button push!
            if (TurnDetected)  {		    // do this only if rotation was detected
            if (up)
              virtualPosition--;
            else
            virtualPosition++;
            TurnDetected = false;          // do NOT repeat IF loop until new rotation detected
                               }
            // Here I change the hour of time -
            tm.Hour = virtualPosition;
            RTC.write(tm);
            lcd.setCursor(0, 0);
            printDigits(tm.Hour);  // then re-print the hour on the LCD
         } while ((digitalRead(PinSW)));  // do this "do" loop while the PinSW button is NOT pressed
         lcd.noBlink();
         delay(1000);
       
       //   SET THE MINS
        
        lcd.blink();   // Turn on the blinking cursor:
       virtualPosition = tm.Minute;  //needed or the minute will be zero each time you change the clock.
       do  {
       lcd.setCursor(3,0);   // put cursor at Time Mins
       delay(500);   // Delay needed or same button press will exit do-while as while is checking for another button push!
           if (TurnDetected)  {		    // do this only if rotation was detected
            if (up)
              virtualPosition--;
            else
            virtualPosition++;
            TurnDetected = false;          // do NOT repeat IF loop until new rotation detected
                               }
            // Here I change the min of time -
            tm.Minute = virtualPosition;
            RTC.write(tm);
            lcd.setCursor(3, 0);
            printDigits(tm.Minute);  // then re-print the min on the LCD
         } while ((digitalRead(PinSW)));
         lcd.noBlink();
         delay(1000);
    
       //   SET THE QTY - Feed quantity
        
       lcd.blink();   // Turn on the blinking cursor:
       virtualPosition = feedQty;  //needed or the qty will be zero.
       do  {
       lcd.setCursor(14,0);   // put cursor at QTY
       delay(500);   // Delay needed or same button press will exit do-while as while is checking for another button push!
           if (TurnDetected)  {		    // do this only if rotation was detected
            if (up)
              virtualPosition--;
            else
            virtualPosition++;
            TurnDetected = false;          // do NOT repeat IF loop until new rotation detected
                               }
            // Here I change the feed qty
            feedQty = virtualPosition;
            lcd.setCursor(14, 0);
            lcd.print(feedQty);
           } while ((digitalRead(PinSW)));
         lcd.noBlink();
         delay(1000);
         
     //   SET THE Feed1 Hour
        
       lcd.blink();   // Turn on the blinking cursor:
       virtualPosition = feed1hour;  //needed or will be zero to start with.
       do  {
       lcd.setCursor(2,1);   // put cursor at feed1hour
       delay(500);   // Delay needed or same button press will exit do-while as while is checking for another button push!
           if (TurnDetected)  {		    // do this only if rotation was detected
            if (up)
              virtualPosition--;
            else
            virtualPosition++;
            TurnDetected = false;          // do NOT repeat IF loop until new rotation detected
                               }
            // Here I change the feed1 hour
            feed1hour = virtualPosition;
            lcd.setCursor(2,1);
            printDigits(feed1hour);
           } while ((digitalRead(PinSW)));
         lcd.noBlink();
         delay(1000);  
  
      //   SET THE Feed1 Mins
        
       lcd.blink();   // Turn on the blinking cursor:
       virtualPosition = feed1minute;  //needed or will be zero to start with.
       do  {
       lcd.setCursor(5,1);   // put cursor at feed1minute
       delay(500);   // Delay needed or same button press will exit do-while as while is checking for another button push!
           if (TurnDetected)  {		    // do this only if rotation was detected
            if (up)
              virtualPosition--;
            else
            virtualPosition++;
            TurnDetected = false;          // do NOT repeat IF loop until new rotation detected
                               }
            // Here I change the feed1 minute
            feed1minute = virtualPosition;
            lcd.setCursor(5,1);
            printDigits(feed1minute);
           } while ((digitalRead(PinSW)));
         lcd.noBlink();
         delay(1000);   

   //   SET THE Feed2 Hour
        
       lcd.blink();   // Turn on the blinking cursor:
       virtualPosition = feed2hour;  //needed or will be zero to start with.
       do  {
       lcd.setCursor(10,1);   // put cursor at feed1hour
       delay(500);   // Delay needed or same button press will exit do-while as while is checking for another button push!
           if (TurnDetected)  {		    // do this only if rotation was detected
            if (up)
              virtualPosition--;
            else
            virtualPosition++;
            TurnDetected = false;          // do NOT repeat IF loop until new rotation detected
                               }
            // Here I change the feed1 hour
            feed2hour = virtualPosition;
            lcd.setCursor(10,1);
            printDigits(feed2hour);
           } while ((digitalRead(PinSW)));
         lcd.noBlink();
         delay(1000);  
  
      //   SET THE Feed2 Mins
        
       lcd.blink();   // Turn on the blinking cursor:
       virtualPosition = feed2minute;  //needed or will be zero to start with.
       do  {
       lcd.setCursor(13,1);   // put cursor at feed1minute
       delay(500);   // Delay needed or same button press will exit do-while as while is checking for another button push!
           if (TurnDetected)  {		    // do this only if rotation was detected
            if (up)
              virtualPosition--;
            else
            virtualPosition++;
            TurnDetected = false;          // do NOT repeat IF loop until new rotation detected
                               }
            // Here I change the feed1 minute
            feed2minute = virtualPosition;
            lcd.setCursor(13,1);
            printDigits(feed2minute);
           } while ((digitalRead(PinSW)));
         lcd.noBlink();
         delay(1000);                         
      
  }  // end of main IF rotary encoder push button checker
 
   // CHECK FOR MANUAL FEED BUTTON
  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH) {
    feed();
  }
  // CHECK FEEDING TIME AND FEED IF MATCHED
  
  if (tm.Hour == feed1hour && tm.Minute == feed1minute && tm.Second == 0)  {  // if I dont' check seconds are zero
    feed();                                                                   // then it'll feed continuously for 1 minute!
      }
  if (tm.Hour == feed2hour && tm.Minute == feed2minute && tm.Second == 0)  {
    feed();
      }  
  
}   // End of main Loop

void printDigits(int digits){   // utility function for digital clock display: prints leading 0
   if(digits < 10)
    lcd.print('0');
   lcd.print(digits);
 }
 
 void feed() {
   
   lcd.setCursor(17,0);
   lcd.print("   Meowwwww!");
    for (int positionCounter = 0; positionCounter < 16; positionCounter++) {
     // scroll one position left:
     lcd.scrollDisplayLeft(); 
     // wait a bit:
     delay(150);
         }
  
   // Stir servo section     If you don't need a stir servo simply comment out all fo this until the Auger rotate section  
   stirServo.attach(10);      // I don't know if I need one either but I'm adding it now as it's easiest before I build it!
    for(pos = 0; pos <= 180; pos += 1) {                                  
    stirServo.write(pos);              
    delay(5);   } 
   for(pos = 180; pos>=0; pos-=1)  {                                
    stirServo.write(pos);          
    delay(10);   } 
    delay(200);
   for(pos = 0; pos <= 180; pos += 1) {                                  
    stirServo.write(pos);              
    delay(10);   } 
   for(pos = 180; pos>=0; pos-=1)  {                                
    stirServo.write(pos);          
    delay(5);   }  
   stirServo.detach();  
   
   // rotate the Auger   
   feedServo.attach(PIN_SERVO);
    for (int cnt = 0; cnt < feedQty; cnt++)
    {
      feedServo.write(feedRate);  //the feedrate is really the feed direction and rate.
      delay(600);   //this delay sets how long the servo stays running from the previous command
      feedServo.write(feedReversal);  //...until this command sets the servo a new task!
      delay(200);
      feedServo.write(feedRate);  
      delay(600);   
      feedServo.write(feedReversal);  // if you want to increase the overall feedrate increase the forward delays (1000 at the moment)
      delay(200);                     // or better still just copy and past the forward & backwards code underneath to repeat
          }                           // that way the little reverse wiggle is always there to prevent jams
     feedServo.detach();
     for (int positionCounter = 0; positionCounter < 16; positionCounter++) {
     // scroll one position left:
     lcd.scrollDisplayRight(); 
     // wait a bit:
     delay(150);
         }
             }

Step 6: Update: I've Replaced the Feed Servo With a NEMA 17 Style Stepper Motor

My feed servo started to refuse to go backwards recently. I'm not sure why, and I've never been keen on the PWM method of controlling it's direction. All seemed a bit like guess work, so, I replaced it with a Nema 17 style stepper motor, driven by an Easydriver stepper driver.

The changes are simple. Use a Nema 17 size stepper of your choice. I had one spare. Mine was a 6 wire - I idnetified which wires were the pair for coil "A" and which for coil "B" and ignored the remaining wires. With four wire steppers you won't need to worry about this!.

The wiring of the Easydriver (https://www.sparkfun.com/products/12779) goes like this:

Motor plus goes to your power source of 12v+

Motor Gnd goes to ground -

Motor "A" pins go to the motor A pair of wires of your stepper.

Motor "B" pins go to the motor B pair of wired of your stepper.

The "Step" pin goes to digital pin 13 on your arduino.

The "Dir" pin goes to analogue pin A0 on your arduino (although we are using this as a digital pin)

Finally, the "Enable" pin on the Easydriver goes to the arduino A1 pin. This is used to turn the Easydriver power on and off for power and heat saving..

I just soldered the wires directly onto my existing PCB so if you use my PCB design you can do the same. I wasn't going to redesign the PCB just for this change and it took less than 10 mins to add on the easydriver and it's connections. I just disconnected the old feed servo.

The code is not annoteded as well as I'd like as it was done in a hurry but the changes are obvious. I've used the accellstepper library to drive the stepper, and I've used the enable pin on the Easydriver to power down the motor coils when not in use, or it gets really warm being held in place for hours upon end. Using the enable pin to power it down makes good sense - it doesn't need to hold the auger still when not in use!

<p>What is the value of the capacitors?</p>
<p>I made it with some changes and works perfect, thanks a lot for the information.</p><p>I use Serial LCD with SDL/SCL connection to save connection pins, instead of second servo, i made a ramp in the food box.</p>
<p>Wow, going serial frees a lot of wires. Would love to see the wiring schematic and code that follows.</p>
<p>Here you are, hope it helps</p>
<p>That's awesome! - Nice looking feeder :-)</p><p>Thanks for the pics</p>
<p>&quot;stop feline criminal activity.&quot;</p><p>I just pictured your cats hacking for food.</p><p>LOL ;-)</p>
<p>Dear dodgey, this is indeed a great home project! I'm a newbie, <br>trying to repeat the same construction at home to feed my guinea-pig =)</p><p>Could<br> you please kindly advise, which capacitors and resistors did you use to<br> connect rotary encoder (and how to connect them properly)? My device is<br> constantly switching the cursor forward and doesn't react on encoder <br>pushes...</p><p>Many thanks for support!</p>
<p>i think i found the problem because i could compile the code...the problem is with libraries , i added the last llibraries by github and now the code is like something like that :</p><p>#include &lt;Time.h&gt;</p><p>#include &lt;TimeLib.h&gt;</p><p>#include &lt;LiquidCrystal.h&gt;</p><p>#include &lt;Wire.h&gt; // needed for the RTC libraty</p><p>#include &lt;DS1307RTC.h&gt; // Real Time Clock Library</p><p>#include &lt;Servo.h&gt;</p><p>// initialize the library with the numbers of the interface pins dor the LCD</p><p>LiquidCrystal lcd(12, 11, 5, 8, 7, 6);</p><p>#define PIN_SERVO 9</p><p>Servo feedServo;</p><p>Servo stirServo;</p><p>int pos = 0;</p><p>volatile boolean TurnDetected;</p><p>volatile boolean up;</p><p>const int PinCLK=2; // Used for generating interrupts using CLK signal</p><p>const int PinDT=3; // Used for reading DT signal</p><p>const int PinSW=4; // Used for the push button switch of the Rotary Encoder</p><p>const int buttonPin = A3; // the number of the pushbutton pin for manual feed 13</p><p>int buttonState = 0; // variable for reading the manual feed pushbutton status</p><p>int feed1hour = 07; // variables for feeding times and quantity</p><p>int feed1minute = 00;</p><p>int feed2hour = 17;</p><p>int feed2minute = 30;</p><p>int feedQty = 4;</p><p>int feedRate = 800; //a pwm rate the triggers forward on the servo 75</p><p>int feedReversal = 80; //a pwm rate that triggers reverse on the servo</p><p> // play with these numbers for your servo. Mine is a Futaba digital servo</p><p> // that I removed the pot from and the plastic lug, to make it continuous.</p><p>void isr () { // Interrupt service routine is executed when a HIGH to LOW transition is detected on CLK</p><p> if (digitalRead(PinCLK)) // this keeps an eye out for the rotary encoder being turned regardless of where the program is</p><p> up = digitalRead(PinDT); // currently exectuting - in other words, during the main loop this ISR will always be active</p><p> else</p><p> up = !digitalRead(PinDT);</p><p> TurnDetected = true;</p><p>}</p><p>void setup () {</p><p> // set up the LCD's number of columns and rows:</p><p> lcd.begin(16, 2);</p><p> // setup the Rotary encoder</p><p> pinMode(PinCLK,INPUT);</p><p> pinMode(PinDT,INPUT);</p><p> pinMode(PinSW,INPUT);</p><p> pinMode(buttonPin, INPUT);</p><p> attachInterrupt (0,isr,FALLING); // interrupt 0 is always connected to pin 2 on Arduino UNO</p><p> lcd.setCursor(17,0);</p><p> lcd.print(&quot;Roger Donoghue's&quot;); // A bit of fun :-)</p><p> lcd.setCursor(17,1);</p><p> lcd.print(&quot; Cat-O-Matic&quot;);</p><p> for (int positionCounter = 0; positionCounter &lt; 17; positionCounter++) {</p><p> // scroll one position left:</p><p> lcd.scrollDisplayLeft();</p><p> // wait a bit:</p><p> delay(150);</p><p> }</p><p> delay(3000);</p><p> for (int positionCounter = 0; positionCounter &lt; 17; positionCounter++) {</p><p> // scroll one position left:</p><p> lcd.scrollDisplayRight();</p><p> // wait a bit:</p><p> delay(150);</p><p> } // end of fun</p><p> lcd.setCursor(17,0);</p><p> lcd.print(&quot; &quot;);</p><p> lcd.setCursor(17,1);</p><p> lcd.print(&quot; &quot;);</p><p> }</p><p>void loop () { //Main program loop - most things in here!</p><p> static long virtualPosition=0; // without STATIC it does not count correctly!!!</p><p> tmElements_t tm; // This sectionm reads the time from the RTC, sets it in tmElements tm (nice to work with), then displays it.</p><p> RTC.read(tm);</p><p> lcd.setCursor(0, 0);</p><p> printDigits(tm.Hour); //call to print digit function that adds leading zeros that may be missing</p><p> lcd.print(&quot;:&quot;);</p><p> printDigits(tm.Minute);</p><p> lcd.print(&quot;:&quot;);</p><p> printDigits(tm.Second);</p><p> lcd.print(&quot; &quot;);</p><p> lcd.print(&quot;Qty &quot;);</p><p> lcd.print(feedQty);</p><p> lcd.print(&quot; &quot;);</p><p> lcd.setCursor(0,1);</p><p> lcd.print(&quot;1)&quot;);</p><p> printDigits(feed1hour);</p><p> lcd.print(&quot;:&quot;);</p><p> printDigits(feed1minute);</p><p> lcd.print(&quot; 2)&quot;);</p><p> printDigits(feed2hour);</p><p> lcd.print(&quot;:&quot;);</p><p> printDigits(feed2minute);</p><p>// MAIN BREAKOUT &quot;IF&quot; SECION BELOW THAT MONITORS THE PUSH BUTTON AND ENTERS PROGRAMMING IF IT'S PUSHED</p><p> if (!(digitalRead(PinSW))) { // check if pushbutton is pressed</p><p> // if YES then enter the programming subroutine</p><p> lcd.blink(); // Turn on the blinking cursor:</p><p> lcd.setCursor(5,0);</p><p> lcd.print(&quot; SET&quot;);</p><p> virtualPosition = tm.Hour; //needed or the hour will be zero each time you change the clock.</p><p> do {</p><p> lcd.setCursor(0,0); // put cursor at Time Hour</p><p> delay(500); // Delay needed or same button press will exit do-while as while is checking for another button push!</p><p> if (TurnDetected) { // do this only if rotation was detected</p><p> if (up)</p><p> virtualPosition--;</p><p> else</p><p> virtualPosition++;</p><p> TurnDetected = false; // do NOT repeat IF loop until new rotation detected</p><p> }</p><p> // Here I change the hour of time -</p><p> tm.Hour = virtualPosition;</p><p> RTC.write(tm);</p><p> lcd.setCursor(0, 0);</p><p> printDigits(tm.Hour); // then re-print the hour on the LCD</p><p> } while ((digitalRead(PinSW))); // do this &quot;do&quot; loop while the PinSW button is NOT pressed</p><p> lcd.noBlink();</p><p> delay(1000);</p><p> // SET THE MINS</p><p> lcd.blink(); // Turn on the blinking cursor:</p><p> virtualPosition = tm.Minute; //needed or the minute will be zero each time you change the clock.</p><p> do {</p><p> lcd.setCursor(3,0); // put cursor at Time Mins</p><p> delay(500); // Delay needed or same button press will exit do-while as while is checking for another button push!</p><p> if (TurnDetected) { // do this only if rotation was detected</p><p> if (up)</p><p> virtualPosition--;</p><p> else</p><p> virtualPosition++;</p><p> TurnDetected = false; // do NOT repeat IF loop until new rotation detected</p><p> }</p><p> // Here I change the min of time -</p><p> tm.Minute = virtualPosition;</p><p> RTC.write(tm);</p><p> lcd.setCursor(3, 0);</p><p> printDigits(tm.Minute); // then re-print the min on the LCD</p><p> } while ((digitalRead(PinSW)));</p><p> lcd.noBlink();</p><p> delay(1000);</p><p> // SET THE QTY - Feed quantity</p><p> lcd.blink(); // Turn on the blinking cursor:</p><p> virtualPosition = feedQty; //needed or the qty will be zero.</p><p> do {</p><p> lcd.setCursor(14,0); // put cursor at QTY</p><p> delay(500); // Delay needed or same button press will exit do-while as while is checking for another button push!</p><p> if (TurnDetected) { // do this only if rotation was detected</p><p> if (up)</p><p> virtualPosition--;</p><p> else</p><p> virtualPosition++;</p><p> TurnDetected = false; // do NOT repeat IF loop until new rotation detected</p><p> }</p><p> // Here I change the feed qty</p><p> feedQty = virtualPosition;</p><p> lcd.setCursor(14, 0);</p><p> lcd.print(feedQty);</p><p> } while ((digitalRead(PinSW)));</p><p> lcd.noBlink();</p><p> delay(1000);</p><p> // SET THE Feed1 Hour</p><p> lcd.blink(); // Turn on the blinking cursor:</p><p> virtualPosition = feed1hour; //needed or will be zero to start with.</p><p> do {</p><p> lcd.setCursor(2,1); // put cursor at feed1hour</p><p> delay(500); // Delay needed or same button press will exit do-while as while is checking for another button push!</p><p> if (TurnDetected) { // do this only if rotation was detected</p><p> if (up)</p><p> virtualPosition--;</p><p> else</p><p> virtualPosition++;</p><p> TurnDetected = false; // do NOT repeat IF loop until new rotation detected</p><p> }</p><p> // Here I change the feed1 hour</p><p> feed1hour = virtualPosition;</p><p> lcd.setCursor(2,1);</p><p> printDigits(feed1hour);</p><p> } while ((digitalRead(PinSW)));</p><p> lcd.noBlink();</p><p> delay(1000);</p><p> // SET THE Feed1 Mins</p><p> lcd.blink(); // Turn on the blinking cursor:</p><p> virtualPosition = feed1minute; //needed or will be zero to start with.</p><p> do {</p><p> lcd.setCursor(5,1); // put cursor at feed1minute</p><p> delay(500); // Delay needed or same button press will exit do-while as while is checking for another button push!</p><p> if (TurnDetected) { // do this only if rotation was detected</p><p> if (up)</p><p> virtualPosition--;</p><p> else</p><p> virtualPosition++;</p><p> TurnDetected = false; // do NOT repeat IF loop until new rotation detected</p><p> }</p><p> // Here I change the feed1 minute</p><p> feed1minute = virtualPosition;</p><p> lcd.setCursor(5,1);</p><p> printDigits(feed1minute);</p><p> } while ((digitalRead(PinSW)));</p><p> lcd.noBlink();</p><p> delay(1000);</p><p> // SET THE Feed2 Hour</p><p> lcd.blink(); // Turn on the blinking cursor:</p><p> virtualPosition = feed2hour; //needed or will be zero to start with.</p><p> do {</p><p> lcd.setCursor(10,1); // put cursor at feed1hour</p><p> delay(500); // Delay needed or same button press will exit do-while as while is checking for another button push!</p><p> if (TurnDetected) { // do this only if rotation was detected</p><p> if (up)</p><p> virtualPosition--;</p><p> else</p><p> virtualPosition++;</p><p> TurnDetected = false; // do NOT repeat IF loop until new rotation detected</p><p> }</p><p> // Here I change the feed1 hour</p><p> feed2hour = virtualPosition;</p><p> lcd.setCursor(10,1);</p><p> printDigits(feed2hour);</p><p> } while ((digitalRead(PinSW)));</p><p> lcd.noBlink();</p><p> delay(1000);</p><p> // SET THE Feed2 Mins</p><p> lcd.blink(); // Turn on the blinking cursor:</p><p> virtualPosition = feed2minute; //needed or will be zero to start with.</p><p> do {</p><p> lcd.setCursor(13,1); // put cursor at feed1minute</p><p> delay(500); // Delay needed or same button press will exit do-while as while is checking for another button push!</p><p> if (TurnDetected) { // do this only if rotation was detected</p><p> if (up)</p><p> virtualPosition--;</p><p> else</p><p> virtualPosition++;</p><p> TurnDetected = false; // do NOT repeat IF loop until new rotation detected</p><p> }</p><p> // Here I change the feed1 minute</p><p> feed2minute = virtualPosition;</p><p> lcd.setCursor(13,1);</p><p> printDigits(feed2minute);</p><p> } while ((digitalRead(PinSW)));</p><p> lcd.noBlink();</p><p> delay(1000);</p><p> } // end of main IF rotary encoder push button checker</p><p> // CHECK FOR MANUAL FEED BUTTON</p><p> buttonState = digitalRead(buttonPin);</p><p> if (buttonState == HIGH) {</p><p> feed();</p><p> }</p><p> // CHECK FEEDING TIME AND FEED IF MATCHED</p><p> if (tm.Hour == feed1hour &amp;&amp; tm.Minute == feed1minute &amp;&amp; tm.Second == 0) { // if I dont' check seconds are zero</p><p> feed(); // then it'll feed continuously for 1 minute!</p><p> }</p><p> if (tm.Hour == feed2hour &amp;&amp; tm.Minute == feed2minute &amp;&amp; tm.Second == 0) {</p><p> feed();</p><p> }</p><p>} // End of main Loop</p><p>void printDigits(int digits){ // utility function for digital clock display: prints leading 0</p><p> if(digits &lt; 10)</p><p> lcd.print('0');</p><p> lcd.print(digits);</p><p> }</p><p> void feed() {</p><p> lcd.setCursor(17,0);</p><p> lcd.print(&quot; Meowwwww!&quot;);</p><p> for (int positionCounter = 0; positionCounter &lt; 16; positionCounter++) {</p><p> // scroll one position left:</p><p> lcd.scrollDisplayLeft();</p><p> // wait a bit:</p><p> delay(150);</p><p> }</p><p> // Stir servo section If you don't need a stir servo simply comment out all fo this until the Auger rotate section</p><p> stirServo.attach(10); // I don't know if I need one either but I'm adding it now as it's easiest before I build it!</p><p> for(pos = 0; pos &lt;= 180; pos += 1) {</p><p> stirServo.write(pos);</p><p> delay(5); }</p><p> for(pos = 180; pos&gt;=0; pos-=1) {</p><p> stirServo.write(pos);</p><p> delay(10); }</p><p> delay(200);</p><p> for(pos = 0; pos &lt;= 180; pos += 1) {</p><p> stirServo.write(pos);</p><p> delay(10); }</p><p> for(pos = 180; pos&gt;=0; pos-=1) {</p><p> stirServo.write(pos);</p><p> delay(5); }</p><p> stirServo.detach();</p><p> // rotate the Auger</p><p> feedServo.attach(PIN_SERVO);</p><p> for (int cnt = 0; cnt &lt; feedQty; cnt++)</p><p> {</p><p> feedServo.write(feedRate); //the feedrate is really the feed direction and rate.</p><p> delay(600); //this delay sets how long the servo stays running from the previous command</p><p> feedServo.write(feedReversal); //...until this command sets the servo a new task!</p><p> delay(200);</p><p> feedServo.write(feedRate);</p><p> delay(600);</p><p> feedServo.write(feedReversal); // if you want to increase the overall feedrate increase the forward delays (1000 at the moment)</p><p> delay(200); // or better still just copy and past the forward &amp; backwards code underneath to repeat</p><p> } // that way the little reverse wiggle is always there to prevent jams</p><p> feedServo.detach();</p><p> for (int positionCounter = 0; positionCounter &lt; 16; positionCounter++) {</p><p> // scroll one position left:</p><p> lcd.scrollDisplayRight();</p><p> // wait a bit:</p><p> delay(150);</p><p> }</p><p> }</p>
<p>Can send email to me the completed of coding for this project ? i very very need </p><p>Email : Dol3t80@gmail.com</p>
<p>Hi,</p><p>when I compile your code into arduino, I got many error from the DS1302RTC.h. Which library file are you using ?</p><p>Thanks !<br></p>
<p>I had to import the library manually, download the .zip from here: https://github.com/PaulStoffregen/DS1307RTC/releases then import it using Sketch, Include libuary, Add zip libuary and upload it.</p>
<p>Still got many error from the DS1302RTC.h. You delete or add some code ? </p>
<p>Thanks Paul.</p><p>I used the same DS1307RTC lib as you but still got the same tmElements error and many like this one:<br>Cat_feeder_code:87: error: expected primary-expression before ')' token</p><p> RTC.read(tm);</p><p>Do you have an idea ?</p><p>Thanks!<br>Mathieu </p>
<p>I have found alternative libraries, which allow me to compile to code error free.</p><p><a href="https://github.com/PaulStoffregen/DS1307RTC" rel="nofollow">https://github.com/PaulStoffregen/DS1307RTC</a><br><br><a href="https://github.com/PaulStoffregen/Time" rel="nofollow">https://github.com/PaulStoffregen/Time</a></p>
<p>Hello, I've built he project out on breadboard and am verifying the code in the Arduino app. I get an error:<br><br>exit status 1</p><p>'tmElements_t' was not declared in this scope</p><p>I'm very new to Arduino and C, but i've done some searching and can't find a obvious solution to this problem. Has anyone else come across this and have a fix?</p>
<p>In the code, I want to add a second auger and servo instead of the mixing servo, how to I add that to the code?</p>
<p>You forgot to add the RTC module in the components list, but all in all an enjoyable project! I will be making in at the end on June 2016 (After my internship and exams) lol</p>
<p>Hi, this is an awesome project! I'm going to be printing the NEMA 17 stepper mount and I was wondering if this is in inches. </p>
<p>I love this idea! I've been trying to figure out how to create a feeding device. Do you have any recommendation where someone could get an auger similar to the one you made, without access to a 3D printer?</p>
<p>tq bro for your articles and this posting really help me alot..i want to make your projects for my final year projects..please i need your help if something goes wrong..can you give me anything that can make me easier to cantact you if in urgent..email me on muhamadluqmanizahamdual@gmail.com ..pleaseeeeeeee :) </p>
<p>Thanks for your design and ideas. I'm new to Arduino but this is definitely something I want to build. I plan to go a step further and read the cat's microchip too so each cat gets the right quantity of food when they come in. I don't yet appreciate the different types of motor enough so I was wondering why you chose a servo over, say, a stepper to drive the auger? Thanks for you feedback!</p>
<p>I like the RFID reader idea :-) The motors all have different benefits: A servo motor is easy to wire, doesn't need a specific driver board, and had a massive amount of torque. The downside is servos are not designed to rotate more than 360 degrees unless modified (there ARE some specialist free spinning servos out there) and driving servos needs PWM pulses to tell them speed and direction, which can be a little erratic. </p><p>Stepper motors dont have as much torque, but are very easy to control precisely with a simple stepper driver board (like an Easy Driver). You can be very precise with steppers - even saying, &quot;turn this direction and go to specific point &quot;x&quot; </p><p>I'm finding my version of the machine with a stepper motor is jamming (the auger) far more than the servo motor.</p>
<p>Interesting! Thank you for the information. I guess I'll give the modified servo a go first. I would expect that the jamming may be a combination of speed and how much space there is between the fins of the auger, altering how much food is in motion at any one time. Really must get myself a 3D printer so I can try out more than one design when I do this :)</p>
<p>Do it! make a Prusa i3 or similar. having a 3D printer if you like making things is truly amazing. Draw it in Sketchup, then a little while later you have it! Still blows my mind :-)</p><p>The jams are the food getting trapped as the auger pushes the food into the tube and the odd kibble gets stuck between the edge of the auger fin and the edge of the tube where it goes from the feeding &quot;T&quot; section into the sealed round remainder of the tube, if you see what I mean. Having the servo do a partial reverse jiggle deals with this and lets the jammed bit drop.</p>
<p>Yes I do see what you mean. I wonder if having the inlet from the T-piece more rounded in the direction of travel for the food will help to mitigate the problem. Something like this: <a href="http://image.made-in-china.com/2f0j00ZKdaVDNPCtkQ/PVC-Pipe-Fitting-Mould-PVC-Belling-50mm-Y-Shape.jpg" rel="nofollow"> http://image.made-in-china.com/2f0j00ZKdaVDNPCtkQ...</a></p><p>Do you have any tips when buying a kit for the i3 printer? I see they're as low as &pound;159 on eBay, and sometimes as expensive as &pound;500. I'm no fool to believe they are as good as each other, but which parts shouldn't be skimped on?</p><p>Thanks again</p>
<p>Perhaps that might help...</p><p>The printer is all about the quality of parts - mostly the print head, and the parts used in the extruder. I bought the parts from various sources. The frame kit from one, and the electrics from another (Ebay). That way I got the features I wanted (like a RAMPS board, and LCD control for computerless printing)</p>
<p>Do you run the auger always forward? On the Kitlaan project he runs it alternatively forwards and backwards to avoid any food getting stuck.</p><p>Thanks!</p>
I run it both ways for the same reason.
<p>Awesome!<br>Could you post an stp model for the red 3d printed screw also?</p>
<p>I don't have an STP model but I'll see if I can add my sketchup models as an update to this project. I've just made a new tube mount and screw to fit a NEMA 17 style stepper motor as my feed servo started playing up recently.</p>
The parts are linked in the text (http://www.thingiverse.com/thing:27854)
<p>Thanks :)<br>I checked the link before I posted but got sent to &quot;this is the end of the universe&quot; page for some reason. Now the link seems to work and everything is up for grabs as you say. Great work, and thanks for sharing!</p>
it's all included in my existing attachments. I'll double check
<p>thank you bro </p>
<p>Thanks, Amazing build...!!</p>
<p>Thanks, Amazing build...!!</p>
<p>Nice one... Never underestimate feline criminal activity.. !!</p>
<p>In the video - that's Jeffry - talk about great timing! :-)</p>
Amazing build!
<p>Hahaha awesome! I see things like this and think that my beagle will gnaw his way in. I loved the meow from the cat at the end of the video! </p>

About This Instructable

50,372views

359favorites

License:

More by dodgey99:Automatic Arduino Powered Pet Feeder Arduino Etch-A-Sketch Clock Easy, Consistent & Cheap Toner Transfer Method for Single & Double Sided PCBs 
Add instructable to: