Introduction: JimsFridge

About: Hi, I'm a software engineer. I've been involved with FIRST for 15 years and have build several robots.

A bunch of years ago I was on a panel and at one point I found a perfect stop to make a joke, which was 'Yes, I'm going to took my refrigerator up to twitter.' The line went over well and the audience laughed. After this panel I started to think about actually hooking my refrigerator up to twitter. So I did it in 2012 with an Arduino. I replaced it wit ha Raspberry Pi recently and I thought I needed to write up an Instructable about it so other people can hook their refrigerator up to twitter.

The basic idea is to tweet out the temperature and how long the door was open every time I close the door. Every 2 to 8 hours the fridge gets bored and tweet out a 'bored fridge saying.' The third thing is randomly tweet out a 'open door' saying.

Step 1: Creating the Electronics

This this project you will need the following:

  • Raspberry Pi 3
  • Two temperature sensors, I used the SparkFun Digital Temperature, TMP102 (SEN-13314)
  • Two Magnetic Door Switch Set, (Sparkfun COM-13247)
  • Raspberry Pi prototype hat, (Adafruit 2310)
  • Two 10K resisters, (Adafruit 2784)
  • Hookup wire 24 gage
  • 0.1 inch connectors

For the temperature sensor we want to connect two headers up to the +5V, SDA, SCL, and Ground lines, for the I2C communication. For the switches, we and to wire the middle two lines, of a four pin header, to Ground. Then the outside lines connect one to GPIO 17, the other to GPIO 18. Then connect the two resisters to +3V and then to the outside lines as well. This is going to pull the lines up to +3V and then when the reed switches sense a magnet they will connect the GPIO to ground.

Then wire up the two temperature senses to plugs that will go into the two I2C connectors. Then the two reed switches to a plug that goes to the GPIO header.

Step 2: Setup the Raspberry Pi

Install three packages on the Raspberry Pi.

Python

We will need to call a Python Script from the C++ program. This Python Script will be handling the Tweeting.


Twython

Twython is the package for tweeting things.

Wiring Pi

Wireing Pi will handle reading the GPIO pins. We will use the raw I2C file system to handle reading the temerature sensors.

You will also need to have the I2C access connected up. For this, on the menu we will find Preferences/Raspberry Pi Configuration. On this screen enable the I2C port.

Step 3: The Source Code

The source code for this project is on GitHub at, https://github.com/JimWright4089/General/tree/master/jimsfridge

Step 4: Testing the Twython Package.

We will be using C++ for the fridge code, the best Twitter API is Twython, which is a Python based API. So every Tweet we make is going run a System command to pass the string to send to a Python script. Since this is a Tweeting Refigerator, we will not worry too much about the return code or even if this call works. If this was a more important thing we will choose a different mechanism to send a Tweet.

We will need to obtain four keys in order to get Twython to work. They are the Consumer Key, Consumer Secret, Access Token, and Access Token Secret. Follow the Twitter API documentation to obtain these keys: Getting Access Tokens

We will want to keep these keys very secret. Let's put them in their own python script named keys.py. We can also put them in a different place than were we will put the script in order to use Github to keep our source controlled without reveling the keys. For this example we will use /home/pi/keys.py

The key.py file looks like:

C_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
C_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" A_token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" A_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

Let's call the python script to send a tweet, tweetaline.py. It will look like this:

#!/usr/bin/python

import sys from twython import Twython

execfile("/home/pi/keys.py")

if 2 == len(sys.argv): myTweet = Twython(C_key,C_secret,A_token,A_secret) myTweet.update_status(status=sys.argv[1]) else: print "Need a string to tweet"

Basically it's going to import Twython, then call our keys.py to get the keys. Then check to see if there is a line to tweet. We are expecting two paramters, one is the name of the script, the second is the line to tweet out. Then create a Twython Object, and finally tweet out a line.

To test this type the following on a terminal line:

python tweetaline.py "Test, this is a line to test"

We should see this line on our Twitter feed after this.

If it does not work, we will see problems on the terminal window. I Google the problems to find the solution to them, Here is an example of a good tweet and a tweet that does not work (for duplicate tweet):

<p>pi@JimsFridge01:~/samba/jimsfridge $ python tweetaline.py "Testing a line for Instructables Article"<br>pi@JimsFridge01:~/samba/jimsfridge $ python tweetaline.py "Testing a line for Instructables Article"
Traceback (most recent call last):
  File "tweetaline.py", line 10, in <module>
    myTweet.update_status(status=sys.argv[1])
  File "/usr/lib/python2.7/dist-packages/twython/endpoints.py", line 112, in update_status
    return self.post('statuses/update', params=params)
  File "/usr/lib/python2.7/dist-packages/twython/api.py", line 268, in post
    return self.request(endpoint, 'POST', params=params, version=version)
  File "/usr/lib/python2.7/dist-packages/twython/api.py", line 258, in request
    api_call=url)
  File "/usr/lib/python2.7/dist-packages/twython/api.py", line 194, in _request
    retry_after=response.headers.get('X-Rate-Limit-Reset'))
twython.exceptions.TwythonError: Twitter API returned a 403 (Forbidden), Status is a duplicate.</module></p>

Step 5: Building the Main Program for Fridge Tweeter

We will use C++ for the main part of the code. We won't bother with makefile, we will put the build line as a comment in the source file. For this example we will use the file name JimsFridge.cpp and produce the JimsFridge object. The build line will then be:

g++ JimsFridge.cpp -o JimsFridge -lwiringPi -lstdc++

We need to line in the wiringPi library, as well as, the standard template library.

Step 6: The Tweet Function.

The function to tweet stuff is simple. The text gets put into a line that adds "python / tweetaline.py "" to the front and then """ to the end. Then calls the system function to send it to a command line.

void Tweet(string text)
{ string comandToSend = "python /home/pi/samba/jimsfridge/tweetaline.py \"" + text + "\"";

cout << comandToSend << endl; system(comandToSend.c_str()); }

Step 7: Debouncing the GPIO Pins

Both of the GPIO pins need to be debounced. Debouncing is the process to ensure that the switch is fully closed before reacting to it. When a switch closes sometimes it is noisy and this filters that noise out.

bool Debounce(uint8 pin, uint8* curState, uint8* lastState, uint32* lastTime)
{ // read the state of the switch into a local variable: uint8 reading = digitalRead(pin); bool stateChanged = false;

// check to see if you just pressed the button // (i.e. the input went from LOW to HIGH), and you've waited // long enough since the last press to ignore any noise:

// If the switch changed, due to noise or pressing: if (reading != *lastState) { // reset the debouncing timer *lastTime = GetTime(); }

if ((GetTime() - *lastTime) > BUTTON_DEBOUNCE) { // whatever the reading is at, it's been there for longer // than the debounce delay, so take it as the actual current state:

// if the button state has changed: if (reading != *curState) { *curState = reading; stateChanged = true; } }

*lastState = reading; return(stateChanged); }

Basically we read the digital pin (this is a wireingPi function). If the state is different that the last one start a timers. If the timer has expired then the switch has been changed. If the state has been changed then return a true from the function.

To call this function use:

//----------------------------------------------------------------------------
// File constants //----------------------------------------------------------------------------

const uint8 FRIDGE_DOOR = 18;
const uint8 FREEZER_DOOR = 17; const uint8 DOOR_CLOSED = 0; const uint8 DOOR_OPEN = 1;

//----------------------------------------------------------------------------
// File constants //---------------------------------------------------------------------------- // Door debounce vars uint8 mFridgeDoorState = DOOR_CLOSED; uint8 mFreezerDoorState = DOOR_CLOSED; uint8 mLastFridgeDoorState = DOOR_CLOSED; uint8 mLastFreezerDoorState = DOOR_CLOSED; uint32 mLastFridgeDoorTime = 0; uint32 mLastFreezerDoorTime = 0;

wiringPiSetup();
wiringPiSetupGpio(); pinMode (FRIDGE_DOOR, INPUT) ; pinMode (FREEZER_DOOR, INPUT) ;

bool fridgeChange = Debounce(FRIDGE_DOOR, &mFridgeDoorState, &mLastFridgeDoorState, &mLastFridgeDoorTime);
bool freezerChange = Debounce(FREEZER_DOOR, &mFreezerDoorState, &mLastFreezerDoorState, &mLastFreezerDoorTime);

Step 8: The I2C Code

The addresses of the two I2C temperature sensors are 0x48 and 0x49. We will use these as constants in the code:

<p>const uint8  I2C_WAIT_TIME = 100;<br>const int mFridgeAddress = 0x49;  // i2C addresses
const int mFreezerAddress = 0x48;</p>

The variables we will use are:

<p>double mFridgeTemp = 0.0;         // Temperatures<br>double mFreezerTemp = 0.0;</p>

The initialization code, open up the I2C device than then open the Fridge Temperature sensor file, and then the Freeze file .

<p>void InitI2C()<br>{
  char *filename = (char*)"/dev/i2c-1";</p><p>  //----- OPEN THE I2C BUS -----
  if ((mFileFridge = open(filename, O_RDWR)) < 0)
  {
    printf("Failed to open the fridge i2c bus");
    return;
  }</p><p>  if (ioctl(mFileFridge, I2C_SLAVE, mFridgeAddress) < 0)
  {
    printf("Failed to acquire bus access and/or talk to fridge temp sensor.\n");
    return;
  }</p><p>  if ((mFileFreezer = open(filename, O_RDWR)) < 0)
  {
    printf("Failed to open the freezer i2c bus");
    return;
  }</p><p>  if (ioctl(mFileFreezer, I2C_SLAVE, mFreezerAddress) < 0)
  {
    printf("Failed to acquire bus access and/or talk to freezer temp sensor.\n");
    return;
  }
}</p>

To read the sensors the code looks like:

<p>double ReadTempSensor(int filePointer)<br>{
  uint8 data[2];
  uint8 length;</p><p>//  usleep(25000);</p><p>  // a 0x00 means ge tthe temperature
  data[0] = 0x00;</p><p>  //----- WRITE BYTES -----
  length = 1;
  if (write(filePointer, data, length) != length)
  {
    printf("Failed to write to the i2c bus.\n");
  }</p><p>  usleep(I2C_WAIT_TIME);</p><p>  //----- READ BYTES -----
  length = 2;
  if (read(filePointer, data, length) != length)
  {
    printf("Failed to read from the i2c bus.\n");
  }
  else
  {
    // The temperature is in a 12 bit int.
    short intTemp = data[0]<<8;
    intTemp += data[1];
    intTemp /=32;
    // move the decimal
    return ((double)intTemp)*0.0625;
  }
  return 0.0;
}</p>

The temperature comes in as two bytes, we will pack them into a two byte short to preserve the sign, then divide the number by 32 to move it 5 bits to the right. Finally return it as a double multiplied by .0625, which is the constant found in the data sheet for the device.

To call the temperature reads use the code:

InitI2C();


mFridgeTemp = ReadTempSensor(mFileFridge); mFreezerTemp = ReadTempSensor(mFileFreezer);

Step 9: Reading in the Saying File.

The three files are read in to Vectors of Strings. Each file has three lines at the end. A line named 'fin, which tells the code to stop reading the file. Then two ruler lines to ensure no line is longer then 140 characters for Twitter.

void InitSayings(const char* fileName, vector& list)
{ ifstream sayingsFile(fileName);

if(!sayingsFile) { cout << "Cannot open input file.\n"; return; }

// The saysing file all end with "fin'" and then the character count lines, // we want to load everything up to fin. string saying; while(getline( sayingsFile, saying )) { if("fin'" == saying.substr(0,4)) { break; } list.push_back(saying); }

sayingsFile.close(); }

Step 10: Finally

Attach the PI to a board and then attach the board to the top of your refrigerator. Mount the temperature sensors into the Freezer and Refrigerator. Then position the reed switches above the door, with the magnets on the door, so that they detect when the door has opened.

I hope you will drop me a line if you get your tweeting device working. I will make sure JimsFridge follows it.