Auto-dine 2009

I spent some time during Thanksgiving to build an automatic cat feeder but I’m just now getting around to writing something about it. Our cats want to be fed at 6am but I don’t want to get up to do so. This aims to get them fed and back to bed without waking us up! All the good stuff after the break.

The Parts:

I managed to scavenge everything for this project except for a couple of transistors. This included left over wood, acrylic, angle brackets, and fasteners used to make the body of the Auto-dine.

  • Atmel AVR ATmega8 microcontroller (from previous project)
  • Servo without a control board (from a BEAM build)
  • Radio Shack prototyping board (depopulated from old project)
  • 2 tactile momentary push buttons (mine are already on a board form an old stereo)
  • LCD display (I used a Parallax serial character LCD that’s been sitting around for years)
  • DC H-bridge:
    • 2x – 2n3904 NPN Transistors (purchased from Radio Shack
    • 2x – 2n3906 PNP Transistors (purchased from Radio Shack
    • 4x – 1N4001 Diodes (on hand)
    • 4x – 1k Resistors (on hand)
  • 5v regulated power supply (Reused a Blackberry charger)
  • Misc Components:
    • 8 Mhz crystal oscillator (salvaged from broken Xbox controller)
    • 2x 22pf filtering capacitors
    • 1x 0.1uf decoupling capacitor
    • 1x 4k7 resistor (pull-up for reset pin)
    • Various pin headers, cables, and connectors

The Concept:

Working with parts on hand, I looked for the easiest way to dispense the right about of food. Knowing that it would be difficult to have the device measure accurately I settled on a single dose dispenser that was gravity fed. After weighing the concepts in my mind, I felt that a false-bottom with a center pivot would be the easiest physical gate to build.

I first built a box to act as a chute with plexi-glass on one side. I attached a servo rather creatively since I had removed the mounting brackets from it on a previous project. I put a long bolt through two holes that I had drilled in the body and secured them to the acrylic using zip ties. This servo has no control circuitry so it’s acting like a geared motor. I’ve used another piece of acrylic and two bits of aluminum angle-bracket to act as the base of the food chamber.

The food chamber is capped off with another piece of acrylic that pivots on a screw with a nylon stand-off around it. I use a nail as a peg to make sure our cats can’t swivel this to the side and get at the food stored within. This is a single serving feeder so we measure the food each night when we fill this up.

When the servo tilts the false-bottom the food comes screaming out of this bad boy. To corral this I’ve made a half-funnel out of a large yogurt container and stapled it in place. This does a fairly nice job of getting the food into the bowl. We lose around five pieces on each drop but the cat’s always clean it up.

The control hardware is housed in a box on the side of the chute. The tinted acrylic is from an old computer case (you can see the opening for a case fan to the left). I’ve mounted the LCD display and the two buttons to this. The protoboard with the ATmega8 is mounted to the wood on the back, and you can see the black power cord, a cell phone charger, exiting between the wood and acrylic. I tied an overhand knot for strain relief.

The controls are pretty simple. When in idle mode, the lower button will cycle the servo (serve the food). The upper button scrolls through the different settings modes for clock hours and minutes, and feed timer hours and minutes with the second button incrementing the selected digits.

I didn’t get a picture of the control board before I buttoned up the enclosure. In the closeup taken through the acrylic you can see the H-bridge that controls the servo. I’m a fan of David Cook’s h-bridges and used the “poor” option because when I jammed this servo it was still using less than 300mA so Radio Shack transistors should be able to stand up to it. To the right in the middle you can see the ATmega8 with a filtering cap to its left and a crystal and two load caps below that. The rest of this is just connectors; the cell phone charge I’m using puts out regulated 5V so I didn’t include a regulator in the build.

That’s about it. Below you can see a video and check out the schematic. I’ve copied the source code but it’s nothing special. You’ll need to include an LCD display library if you want to get it to work.

The Auto-dine 2009 in action.

#define F_CPU 8000000

#include
#include
#include
#include "serial_lcd.h"

typedef unsigned char   u8;
typedef signed short   s16;

//#define   XTAL      1e6      // 1MHz

#define KEY_PORT     PORTD
#define KEY_PIN      PIND
#define KEY0      6 //Left
#define KEY1      4 //Up
#define Increment 3 //Down
#define Set      2 //Right
#define KEY4      5 //Enter

#define ServPort    PORTC
#define ServDDR        DDRC
#define ServPos0    (1<
#define ServPos1    (1<
#define ServNeg0    (1<
#define ServNeg1    (1<

#define ServMask (ServPos0 | ServPos1 | ServNeg0 | ServNeg1)
#define ServPair0 (ServPos0 | ServNeg0)
#define ServPair1 (ServPos1 | ServNeg1)

volatile unsigned char six_sec_flag = 0;
unsigned char hours = 0;
unsigned char minutes = 0;
unsigned char seconds = 0;
unsigned char feed_hours = 5;
unsigned char feed_minutes = 45;
unsigned char feed_seconds = 0;

unsigned char settings_mode_flag = 0;
unsigned char settings_timer = 0;

u8 key_state;            // debounced and inverted key state:
 // bit = 1: key pressed
u8 key_press;            // key press detect

u8 get_key_press( u8 key_mask )
{
 cli();               // read and clear atomic !
 key_mask &= key_press;                        // read key(s)
 key_press ^= key_mask;                        // clear key(s)
 sei();
 return key_mask;
}

void init_timers(void)
{
 TCCR0 |= 1<<
 TIMSK |= 1<

 TCCR1B |= (1<<<
 OCR1A = 46875;                // Compare match at 6 seconds
 TIMSK |= 1< 23) hours = 0;
 Display_Time(hours,minutes,0);
 break;
 case 2:    //time minutes
 if (++minutes > 59) minutes = 0;
 Display_Time(hours,minutes,0);
 break;
 case 3:    //feed hours
 if (++feed_hours > 23) feed_hours = 0;
 Display_Time(feed_hours,feed_minutes,1);
 break;
 case 4:    //feed minutes
 if (++feed_minutes > 59) feed_minutes = 0;
 Display_Time(feed_hours,feed_minutes,1);
 break;
 }
}

int main(void)
{
 //use internal pullups:
 KEY_PORT |= ((1<<Increment) | (1<<Set));

 delay_ms(100);

 lcd_init(LCD_DISP_ON);  //Initialize LCD with the cursor off

 lcd_clrscr();           //Clear the LCD screen
 init_timers();
 Standard_Display();

 for (;;)
 {
 if( get_key_press(1<<Increment))
 {
 Inc_Settings();
 }
 if( get_key_press(1< 4) {
 settings_mode_flag = 0;
 settings_timer = 0;
 }
 else settings_timer = 5;
 Settings_Modes();
 }

 if (six_sec_flag > 0) {
 six_sec_flag = 0;

 if (settings_timer) {
 if (--settings_timer == 0) {
 settings_mode_flag = 0;
 Standard_Display();
 }
 }
 seconds += 6;
 if (seconds > 59) {
 seconds = 0;
 if (++minutes > 59) {
 minutes = 0;
 if (++hours > 23) hours = 0;
 }
 if (settings_mode_flag == 0) Display_Time(hours,minutes,0);
 }
 if ((hours == feed_hours) && (minutes == feed_minutes) && (seconds == 0)) Cycle_Servo();
 }
 }
}

ISR(TIMER0_OVF_vect)         // every 10ms
{
 static u8 ct0, ct1;
 u8 i;

 TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5);   // preload for 10ms

 i = key_state ^ ~KEY_PIN;      // key changed ?
 ct0 = ~( ct0 & i );         // reset or count ct0
 ct1 = ct0 ^ (ct1 & i);      // reset or count ct1
 i &= ct0 & ct1;         // count until roll over ?
 key_state ^= i;         // then toggle debounced state
 key_press |= key_state & i;      // 0->1: key press detect
}

ISR(TIMER1_COMPA_vect)
{
 ++six_sec_flag;
}
essential