This idea stemmed from something I've wanted to try for quite a while. That is to train a pet to do something, maybe even something convoluted or unnecessary, to get food or a treat.
Well I finally have the opportunity to build such a contraption. We recently adopted an orphan cat, and have also recently acquired a lot of spare time. So here's the goal. A small machine that dispenses treats when the cat pushes a button. Some design goals:
- Large container for cat pellets that empties FIFO style.
- Some controlled mechanism of dispensing above-mentioned pellets.
- Sensor feedback to indicate when pellets had left the machine.
- UI feedback to indicate state of machine. (Ready, Dispensing, Done)
- Easy UI for animal to prompt pellet dispensation.
It ended up looking like this..
The cat is meant to press the paddle and then wait for their surprise to pop out the hole. The machine body is made from a cake box and cardboard. Everything was stuck together with cello-tape. Not the best building materials I know but that's all I had at the time.
I thought it was going to be really easy to teach my cat to interface with the machine... Turns out he got impatient quite quickly, and was probably more fascinated with the sound of the spinning motor, than the treats popping out the front.
A breakdown of the electrical components I used:
- Arduino Nano
- ULN2004A Darlington Array
- Some colored LEDS
- Resistors. I think anything between 0.5k to 2k should be fine.
- Photoresistor.
- Stepper motor of your choice
- Power source suited to your motor
The following is what I cooked up around the Arduino. A lot of this was trial and error as I am a programmer and not an electrician. Needless to say this was all a large learning curve (which is what I wanted).
The final Arduino Sketch ended up looking like the below. It wasn't all that I'd hoped it would be; I had gotten fed up several times with trying to monitor time based operations on the Arduino. My sketch always seemed to end up dying after a few hours?.. After a while of hopeless debugging I resigned to just removing the offending bits of code. Sad I know, but actually not too bothered since this is all for the cat. The initial more-feature-complete-sketch can be found here. That still has the photoresistor feedback and some other time based functions in it.
int motorPin1 = 2;
int motorPin2 = 3;
int motorPin3 = 4;
int motorPin4 = 5;
int spinDelay = 25; // the delay between motor steps
int spinAmount = 220; // how many steps should the motor turn?
int motorState = 0;
boolean motorDirection = true; // used to alternate between motor directions
int redLight = 6;
int greenLight = 8;
int buttonPin = 12; // the pin that the pushbutton is attached to
int buttonState = 0; // current state of the button
void setup() {
pinMode(motorPin1, OUTPUT);
pinMode(motorPin2, OUTPUT);
pinMode(motorPin3, OUTPUT);
pinMode(motorPin4, OUTPUT);
pinMode(redLight, OUTPUT);
pinMode(greenLight, OUTPUT);
pinMode(buttonPin, INPUT);
Serial.begin(9600);
digitalWrite(greenLight, HIGH);
digitalWrite(redLight, LOW);
}
void loop() {
buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) {
buttonPushed();
}
}
void buttonPushed() {
digitalWrite(greenLight, LOW);
digitalWrite(redLight, HIGH);
for (int i = 0; i < spinAmount; i++) {
spinMotor();
delay(spinDelay);
}
motorDirection = !motorDirection;
// reset state: motor pins low to conserve battery, green light on, red light off
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, LOW);
digitalWrite(greenLight, HIGH);
digitalWrite(redLight, LOW);
}
void spinMotor() {
if (motorDirection) {
motorState++;
} else {
motorState--;
}
if (motorState <= 1) {
digitalWrite(motorPin1, HIGH);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, LOW);
if (!motorDirection) {
motorState = 9;
}
}
if (motorState == 2) {
digitalWrite(motorPin1, HIGH);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, LOW);
}
if (motorState == 3) {
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, LOW);
}
if (motorState == 4) {
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin4, LOW);
}
if (motorState == 5) {
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin4, LOW);
}
if (motorState == 6) {
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, HIGH);
digitalWrite(motorPin4, HIGH);
}
if (motorState == 7) {
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, HIGH);
}
if (motorState >= 8) {
digitalWrite(motorPin1, HIGH);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, HIGH);
if (motorDirection) {
motorState = 0;
}
}
}
What would I change if I were to start again?
Pushing a button or paddle turned out to be way complicated. I thought a nice big paddle was going to be easy for the cat to just stomp on with his feet. I don't think I could have been more wrong. As intuitive as buttons are to humans, they make no sense to animals. Next time I would place the push button in or on the area where the pellets exit the machine. For a -long- time my cat tried to extract pellets out the machine by pushing his paw in the exit hole. This makes a lot of sense now looking back.
I certainly wouldn't use cardboard and cello-tape to build again. The hardest part to manufacture was a device to let pellets out of the store in a controlled fashion. Moreover, a machine like this needs to be built out of something solid and heavy; More than once we came through to the living room to see the machine knocked over with pellets everywhere.