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

Here is the part list I used to make it work. Depending on your individual needs, you may substitute the scale, amplifier, or water sensor.

  • 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 strain gauge is a metal bar with a series of four resistors on it making a wheatstone bridge. As the bar flexes (and it flexes VERY little) the resistance over the bridge changes a tiny amount. An electronic device can detect that change and, after some math, return the amount of weight on the bar.

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

This particular water level sensor was a perfect choice. It has three wires that connect to each other based on where the float ball is. This provides exactly three states: white+black, white+red, and red+black. In this kit, I only used the "bottom" state and (with a resistor) used the same code as the "example Button" sketch in the Arduino IDE. If the ball falls down to the bottom point, two wires are connected (like a button) and it reports the change to the Arduino.

I used pieces of an Erector set and a zip-tie to hook the sensor to the bowl.

Step 4: The Wiring

There are four main components to this project:
  • 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)
Instead of providing a big schematic that takes a while to read, I'll just give a simple device-by-device wiring.
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

I made this video to explain the code.



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!

Before you hook this thing to a Twitter account, use the Arduino IDE's serial monitor to test all individual parts of your project. To calibrate your sensors, create a simple loop that just grabs the sensor data and reports it over the serial port.

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

Adafruit Make It Tweet Challenge

Finalist in the
Adafruit Make It Tweet Challenge

Microcontroller Contest

Participated in the
Microcontroller Contest