Introduction: Make Your Pet Dishes Tweet!
You give your pets everything: Food, water, a home, toys, and love. Why not give them a twitter account?
This project allows you to monitor your pets' eating habits and receive alerts when their supplies are low. It's also a cute, fun way to learn how to interact with the Arduino, Twitter, sensors, and Ethernet.
Let's get started!
Step 1: The Parts
- An Arduino Uno (available at Adafruit here )
- An Arduino Ethernet Shield (Also at Adafruit here )
- A cheap digital kitchen or postal scale ($20 at Bed Bath and Beyond) for the strain gauge.
- An instrumentation amplifier to boost the strain gauge output. I used a $3 INA126PA from Jameco.
- A water level sensor. For ease of use, I chose the this one from Jameco.
- A prototyping board, prototyping shield, or ability to create circuit boards.
- A 5V USB power supply and USB A-B cable.
- Something to protect your kit from the pets. I used a cheap plastic storage bin.
- A couple resistors, some spare parts from around the house, and some creativity.
There are alternatives to using the Ethernet shield posted by other entries to the Make it Tweet challenge. Check those out and see which method you like more. If you like their projects, vote for them!
Step 2: Science Break: What Is a Strain Gauge and Amplifier?
The change is so miniscule that the Arduino cannot detect it by itself. That is why the INA126PA amplifier was used. The amp can detect the tiny changes and boost them to levels that the Arduino can read.
In my code, it's obvious that I "cheated" at much of the math. Instead of trying to detect the actual number of grams present in the bowl, I simply captured the analog data from the amplifier and watched how it changed between an empty bowl and increments of weight up to a full bowl. At these levels, the readings reported a linear result from 587 to 599. While this is very low resolution, it's enough to report a relative percentage from "empty" to "full" using the map() function in the Arduino IDE.
In this project, I used a metal tray "lid" from an IKEA storage bin and screwed it to the plastic base after removing the glass. This holds the bowls.
Step 3: The Water Sensor
I used pieces of an Erector set and a zip-tie to hook the sensor to the bowl.
Step 4: The Wiring
- Arduino pins (labeled on the Arduino)
- Scale (Red, white, blue, and black are typical from these)
- The INA126PA (There's a notch near Pin 1 and then count up counterclockwise)
- The Water Meter (Red/white/black)
The Amplifier and Water Sensor both have datasheets explaining each pin. See Step 1 for those links.
Strain Gauge:
(1) Red labeled E+ (to +5V)
(2) White labeled V+ (to INA126PA pin 2)
(3) Blue labeled V- (to INA125PA pin 3)
(4) Black labeled E- (to ground)
INA126PA
(1) Resistor to 8 (to set sensitivity - I used 220 ohm)
(2) V+ on INA126PA
(3) V- on INA126PA
(4) to ground
(5) unused
(6) Output (Arduino A0)
(7) +5V from the Arduino
(8) Resistor to 1
Water sensor
Red: to +5V on the Arduino AND to a 10k resistor to ground.
White: To Digital 3 on the Arduino
Black: Unused.
Step 5: Arduino + Twitter + Ethernet Fun
A neat character named Neocat made the Arduino Twitter Library. Instead of storing your user ID and password in Arduino code, it issues a token to use the Twitter API. This token can be revoked at any time which is good if you accidentally post your whole code WITH the token to somewhere. Yes. I did that. Oops.
The difficulty I found is that it only accepts char[] arrays as tweets, so a character array must be declared then populated. Since my code generates Strings, I had to convert the String to a character array before passing it off to my Twitter function.
Here's what I added to the example code to make it a function. The Serial.print lines are there just to help with debugging. I also initialized the variable at the top of the sketch with char msg[125]; // make a nice fat buffer (125 characters) for tweets
If your tweet size exceeds that array size, YOU WILL HAVE PROBLEMS! I spent three hours trying to figure out why my analogRead functions were adding themselves to each other instead of generating new readings.
void postTweet(String tweet){ String termTweet = tweet + "\0" ;// Terminate the tweet with a null Serial.print(termTweet); Serial.println(" - Terminated tweet"); int twtlen = (termTweet.length()+3); // count the characters, add 3 just in case Serial.print(twtlen); Serial.println(" - Tweet length"); termTweet.toCharArray(msg,twtlen); // Convert it to an array called msg Serial.print("Attempted tweet "); Serial.println(msg); Serial.println("connecting ..."); if (twitter.post(msg)) {The rest of the posting code is in the Twitter library.
Step 6: The Whole Program
The entire source code is here, open source, using the Creative Commons Attribution license and other inherited licenses from the Arduino IDE and attached libraries.
/* Tweeting pet dishes ver 0.5b June 25, 2011 This code uses NeoCat's Twitter library and the Ethernet Shield. See the Instructable for more information by Daniel Gentleman for the Adafruit/Instructables Make It Tweet Challenge. */ #if defined(ARDUINO) && ARDUINO > 18 // Arduino 0019 or later #include #endif #include #include #include // The MAC address is on a sticker on the Ethernet shield. byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x3E, 0x28 }; byte ip[] = { 192,168,1, 222 }; // Your Token to Tweet (get it from http://arduino-tweet.appspot.com/) Twitter twitter("YOUR TOKEN HERE"); // Variables variables variables const int foodPin = A2; // Analog 0 for measuring the food weight const int ledPin = 3; // LED const int waterPin = 2; // digital 9 for water sensor int foodWeight; // Variable for storing food weight //const int ledPin = 13; // LED pin for water level int waterState = 0; // Variable for setting water state int lastWaterState = 0; // Variable for last water state const int lowFood = 25; // if the food level is below this, it will tweet int lastFoodWeight; // Last Food State placeholder int foodTweet = 0; // int foodValue = 0; // char msg[125]; // make a nice fat buffer (125 characters) for tweets int waterAlarmState = 0; // Have we reported water yet? int foodAlarmState = 0; // Have we reported an empty bowl? String tweet = "Starting Up" ; void setup() { delay(1000); Ethernet.begin(mac, ip); Serial.begin(9600); pinMode(waterPin, INPUT); pinMode(foodPin, INPUT); pinMode(ledPin, OUTPUT); delay(1000); foodWeight = getFood(); waterState = getWater(); } const int analogInPin = A2; // Analog input pin that the potentiometer is attached to const int analogOutPin = 13; // Analog output pin that the LED is attached to int sensorValue = 0; // value read from the pot int outputValue = 0; // value output to the PWM (analog out) void loop(){ lastFoodWeight = foodWeight; foodWeight = getFood(); if (foodWeight > (lastFoodWeight + 20)){ //If we fed the pets at least 20 String gotfed = "Thanks for feeding me! My bowl is now "; String tweet = gotfed + foodWeight + "% full!"; Serial.println(tweet); postTweet(tweet); foodAlarmState = 0; // Clears food alarm if it was below critical } if (foodWeight < (lastFoodWeight - 10)){ // If the pets ate at least 10 int ateweight = lastFoodWeight - foodWeight; if (ateweight > 100){ ateweight = 0; } String atefood = "I just ate "; String tweet = atefood + ateweight +"% of my food!"; Serial.println(tweet); postTweet(tweet); } if (foodWeight < lowFood){ // If the food is beneath the low threshold if (foodAlarmState != 1){ String foodwarning = "I am almost out of food! I have only "; String tweet = foodwarning + foodWeight + " percent left!"; Serial.println(tweet); postTweet(tweet); foodAlarmState = 1; } } if (getWater() == 0){ if (waterAlarmState != 1){ String waterPre = "My food is at "; String tweet = waterPre + foodWeight + "% but my water is low!"; Serial.println(tweet); postTweet(tweet); waterAlarmState = 1; // Sets water alarm state (so it won't tweet forever) } } if (waterState > lastWaterState){ String tweet = "Thanks for the water!"; Serial.println(tweet); postTweet(tweet); waterAlarmState = 0; // Clears water alarm state } delay(2000); // Regular loop delay } int getFood() { // read the analog in value: int foodValue = analogRead(foodPin); // first sample Serial.print(foodValue); if (foodValue >= 600) { foodValue = 599; } if (foodValue <= 582) { foodValue = 583; } int bowlPercent = map(foodValue, 583, 599, 1, 100); Serial.print("food level = " ); Serial.println(bowlPercent); return bowlPercent; } int getWater(){ lastWaterState = waterState; waterState = digitalRead(waterPin); if (waterState == HIGH) { Serial.println("Water is LOW"); waterState = 0; digitalWrite(ledPin, HIGH); return 0; } else{ Serial.println("Water is good"); waterState = 1; digitalWrite(ledPin, LOW); return 1; } } void postTweet(String tweet){ String termTweet = tweet + "\0" ;// Terminate the tweet with a null Serial.print(termTweet); Serial.println(" - Terminated tweet"); int twtlen = (termTweet.length()+3); // count the characters, add 3 just in case Serial.print(twtlen); Serial.println(" - Tweet length"); termTweet.toCharArray(msg,twtlen); // Convert it to an array called msg Serial.print("Attempted tweet "); Serial.println(msg); // tweet.toCharArray(msg,twtlen); Serial.println("connecting ..."); if (twitter.post(msg)) { // Specify &Serial to output received response to Serial. // If no output is required, you can just omit the argument, e.g. // int status = twitter.wait(); int status = twitter.wait(&Serial); if (status == 200) { Serial.println("OK."); delay (120000); // Wait two minutes before doing anything after a tweet (don't spam!) } else { Serial.print("failed : code "); Serial.println(status); } } else { Serial.println("connection failed."); } }
Step 7: Wrapping It All Up!
For support on THIS project, look for ThoughtFix online everywhere. For general Arduino support, the Arduino.cc and adafruit.com forums are great resources. Finally, I have to give a big shout out to #arduino on irc.freenode.net for their conversations.
Your pets love you and want to tweet, even are if they are messy eaters!
Cat credits:
Lina, the big Tortie
Mr. Rumbles, The little Mau