An updated version of this project can be found here - https://www.hackster.io/darian-johnson/automated-cat-feeder-with-alexa-and-amazon-dash-ae2602
IntroductionMy family owns three cats; for the most part, they are well behaved - unless they are hungry. When it's time for them to eat, they get a little crazy - constantly meowing and running under/bewteen our legs, or waking us up at night.
We used to keep extra food in their dishes, but they would just overeat - resulting in cat throw-up (which, without fail, I seemed to step in every morning on my way to the kitchen).
We've been living in this "claw-ful" situation for a few years, and never really considered resolving the problem, until I saw the re:Invent Alexa Skill contest sponsored by Capital One and Amazon Alexa. My oldest daughter suggested that we (and by we, she really meant me) build an automated cat feeder. I told her that I didn't have the time to build one... but then, I figured, why not give it a try.
OverviewThe Cat Feeder is really two things - a Alexa skill that controls the device and a dry food dispenser powered by a Raspberry Pi.
Cat Feeder Alexa Skill:
The Cat Feeder skill allows the user to:
- Feed the cat (an amount of 1, 2, 3 or 4 ounces)
- Ask when Alexa last fed the cat
- Ask if the cats have food
- Ask if the cat feeder needs to be refilled
- Tell me a cat joke (an easter egg, of sorts. Really for my kids)
Commands interact with the physical Cat Feeder using AWS IoT by sending messages to the device via MQTT or getting status of the feeder via device shadow (more on this in the technical sections).
Note - The above video does not show the cat joke intent (or passing the amount slot with the feed cat intent). See video below for demo.
Raspberry Pi Cat Feeder
The RPI feeder (inspired by David Bryan's Automated Cat Feeder) executes the commands from the skill in the following manner:
- A servo is used to rotate a paddle-wheel (which dispenses the food)
- A usb camera is used to send a photo of the cat food bowl back to user (to determine if the cat's food bowl is empty)
- A light sensor is used to determine if more food needs to be added to the hopper.
skillamzn1.ask.skill.bd90e946-6a6e-4f9c-b6f0-2698b4c5f37b
Design Inspiration/PrinciplesNote - For those that are looking into building Alexa skills, please read and consider. Otherwise, skip this section to get to the "how to build/replicate" the skill.
I've published 4 or 5 Alexa skills - each slightly better than the previous one. I wanted to take my learnings from those activities and apply them to this skill. Here were my design principles:
Reuse-ability - I wanted to limit barriers to use this skill. For this skill, that meant:
- No account linking: My skills typically need to save user preferences. In the past, I've used Google Auth to enable account linking - retrieving the user id to save preferences to in a DyamoDB table. Google recently changed how they allow authentication in web apps, which "broke" a few of my skills. I didn't want to live through that again, so I decided to use the Alexa user id to persist user preferences.
- Limited code configuration by the end user: I wanted to make configuration of the physical device as simple as possible, so I (a) build the configuration instructions into the skill and (b) limited the code changes to one variable change in two scripts.
Maintain-ability - My previous skills were too difficult to maintain. The code was overly complex. For this skill, I decided to RTFM (read the freaking manual) and made better use of the functions provided by the Alexa Skills Kits SDK and Alexa IoT SDK. I also made heavy use of local development (as outlined in this tutorial by Nathan Grice. This saved me HOURS of testing time).
Security - My last IoT skill was not built with security in mind. I didn't restrict policies or roles, which became a problem when others wanted to replicate my project using my Alexa skill. This time, I thought through my security before I started coding. This allowed me to restrict roles and policies so that I could share access keys and certificates without fear of allowing too much access.
A Hacking/Prototype PoV - As I stated in the introduction, I wasn't originally going to build this device - as I didn't think I could get a working prototype in place by competition deadline. But, I decide to give it a go, because the time for me to build the skill (basically, over Thanksgiving weekend) was about 30-40 working hours. I wanted to prove that a working prototype could be built in this time - hopefully inspiring companies to allow their employees to invest 1-2 work weeks developing an Alexa skill for their companies.
Solution Build: Cat Feeder Alexa CodeNote: this section details how I built the Alexa skill.
This Alexa skill is somewhat advanced and uses multiple AWS "services" (in addition to Lambda). For those new to Alexa development, I suggest you start first with the Alexa getting started guide. If you've developed Alexa skills before, please continue.
Upfront Configuration
The skill (written in node js) is build on the Alexa Skills Kits SDK for Node.js and requires the following code dependencies
- AWS IOT SDK
- UUID
- Request
One DynamoDB table is needed:
- Table Name: Cat_Feeder_Config
- Primary partition key: UserId (String)
One S3 bucket is needed:
- I called mine CatFeeder
API key from APIUX to get day/night information based on zipcode (https://www.apixu.com/)
The role used will need to have permissions to IoT and DynamoDB
First Time Use
When the skill is engaged, the user id (session.user.userId) is validated in the 'Cat_Feeder_Config' table. If there is no entry, then a UUID is created and provided to the user. The end user uses this UUID (referred to as a Topic ID) to configure his/her code on the physical Raspberry Pi device. See the example when the "FeedCat" intent is called:
"FeedCat": function (intent, session, response) {
getTopic(session.user.userId, function(value){
topic_id = value;
if (topic_id === 'notset'){
firstTimeConfig(intent, session, response);
}else{
feedCat(intent, session, response);
}
});
}
After Configuration is complete, all commands can be used. Details on all commands (and their interaction) can be found in the VUI diagrams.
Interacting with the Raspberry Pi
Interaction with the raspberry pi is through two channels.
MQTT - the Alexa skill sens a command to the RPI via an MQTT topic. Each user has a unique topic-id (this is the code provided during configuration)
- Topic-id/feed - tells the servo to feed the cat (2 sec for 1 oz, 4 sec for 2 oz, etc)
- Topic-id/picture - tells the RPI to take a photo and upload the photo to S3 (for use in the Alexa App)
Device Shadow- a mechanism to get the state of a physical device. I use the device shadow to let me know the state of the light resistor.
function getCatFeederStatusAPI(userID, eventCallback){
var params = {
thingName: topic_id /* required, set as a global varuable*/
};
iotdata.getThingShadow(params, function(err, body) {
if (err) {
eventCallback(-1); //error occured
}else{
console.log("Zip Code: " + zipcode);
console.log(body);
payload = JSON.parse(body.payload);
var property_value = payload.state.desired.property;
//check to see if light (0 = which means empty)
//or dark (1 = which means full)
eventCallback(property_value);
}
});
}
Code/Skill Availability
The code is available in my Github library. I hope to have the skill certified and available for general use in the next few weeks.
Solution Build: Raspberry Pi Cat FeederThe CatFeeder was build using a Zervo Dry Food Dispenser. Any dispenser can be user - you just need to be sure that it is controlled by a paddle wheel. Your servo needs to be a continuous rotation servo.
Connecting the Servo
This was the hardest part of the physical build. I originally built my own shaft to connect with the servo and paddle wheel.
Unfortunately, the shaft broke during testing, so I ended up attaching the servo to the original handle.
Stabilizing the Servo
My second challenge was stabilizing the servo. My original plan called for the the servo to be mounted in the back of the device - which would provide the support needed to turn the paddle wheel. When my first design failed, I had to move the servo to the front - where there was no support. I resolved this by making a makeshift stand to keep the servo in place.
Code to control the Servo
The Servo is controlled using the CatFeeder.py script (see script in GitHib library). The Alexa skill passes in (via the MQTT topic) the amount of time for the servo to run. The command 'gpio -g pwm 18 200' starts the servo; 'gpio -g pwm 18 150' stops the servo.
runtime = parsed_json["amount"]
t_end = time.time() + runtime
while time.time() < t_end:
os_string = "gpio -g pwm 18 200"
os.system(os_string)
print("done spinning")
os_string = "gpio -g pwm 18 150"
os.system(os_string)
Code to control the photos
A standard usb webcam is used to take pictures of the cat food bowl.
The Alexa skill sends a message to the MQTT topic, which triggers taking the photo and uploading it to S3.
elif msg.topic == topic + "/photo": #logic to take the photo
try:
photo_name = parsed_json["photo"]
#delete old photos
os.system("rm Photos/*.jpg")
#take a photo using the name passed to us from mqtt message
photo = "Photos/" + photo_name + ".jpg"
os_string = "fswebcam --no-banner " + photo
os.system(os_string)
#use tinyS3 to upload the photo to AWS S3
S3_SECRET_KEY = 'secret key'
S3_ACCESS_KEY = 'access key'
conn = tinys3.Connection(S3_ACCESS_KEY,S3_SECRET_KEY,tls=True)
f = open(photo,'rb')
conn.upload(photo,f,'catfeeder')
Using the Light Sensor to indicate food level
I attached a light sensor at the base of the plastic hopper. When the food level in hopper is above the "base", then the sensor does not detect light (meaning that the hopper does not need food). When light is detected, then food needs to be added to the hopper. The light state (0 = light, 1 = dark) is saved to the device shadow and updated every 30 seconds. The Alexa skill queries the state to let the user know if the hopper needs to be refilled.
This code is in the Update_Shadow.py file.py
The Final ProductThe final product, once assembled:
Call-outs/EnhancementsSince I use a light sensor to detect food level, there is a chance I would get a "false positive" depending on the lighting the room - e.g., the hopper is empty, but the feeder is in a dark room. To deal with some of that ambiguity, I ask the user to provide his/her zip code. I use a weather API from APIXU (https://www.apixu.com/ ) to determine if it is day or night. If it is night time, I caveat the "empty" statement (see the live example in the long demo above).
For those thinking of building your own, I'm calling out the following enhancements:
- Replace the light sensor with an ultrasonic distance sensor (I believe it will be more accurate, but I worry about getting good readings.
- Build a sturdier base for the servo.
- Build schedule functionality (to feed the cats 'x' times a day, either at a certain time, or an interval).
- Build Notification (via email/text) or Amazon Replenishment capabilities into the feeder when the food level is low.
- Add a water spigot to provide fresh water
- Use Amazon Rekognition to determine if the pet food bowl is empty
I've build the skill and code to be used with minimal changes
Build the Raspberry Pi Feeder
The most difficult part of replicating this project will be building your feeder. I suggest you follow my rough guide above, or check out the following posts:
- David Bryan's blog - http://drstrangelove.net/2013/12/raspberry-pi-power-cat-feeder-updates
- Dog feeder build by Brandon Krahn and Hunter Soper -https://www.hackster.io/31542/automated-dog-food-dispenser-514136
Install the code
Use git to download the code. Your code should be in the home/pi/CatFeeder location. You should also have certificates downloaded to the home/pi/certs folder.
You will also need to install the following modules:
- AWSIoTPythonSDK
- gpiozero
- paho.mqtt.client
- tinys3
- uuid
Enable the skill and retrieve a code
- Enable the Cat Feeder skill in the Alexa App.
- Ask Alexa "tell cat feeder to configure".
- This will trigger Alexa to send you a code in the Alexa app.
- Update the Update_Shadow.py and CatFeeder.py python scripts with this code (update the topic variable at the top of the script).
At this point, your the skill should communicate with the Raspberry Pi.
Thanks/Shout-Outs/AttributionA few people need to be thanked:
- David Bryan, for the initial idea
- Hackster, Capital One, and Amazon Alexa
- My family (I basically coded 85% of this over Thanksgiving - so I wasn't around a lot)
- The Amazon Alexa solution architects
Comments