I’ll be doing a series on smartifying your house. What better way to start this series that with the most important device in man’s modern history, the toilet? Then we’ll take a look generally at the bathroom and see what else we can wire up in order to provide ourselves with some interesting albeit funny data. After conquering the bathroom, we’ll move on to something a little more complex, the kitchen. Finally, we’ll cap the series off with something that everyone with a smartphone should be able to do…check the garage door! We’ll also get into a little iphone development and program an app that will allow us to close the garage door remotely! But first, the toilet…
The idea…
Back when twitter was still learning to fly, I thought it would be funny to make a toilet tweet. I also thought it would be interesting to know how many times a day I use the toilet and how many gallons of water I was using. So I build a simple device to keep track. However, it wasn’t very useful or reliable and with Twitter’s adoption of oAuth Authentication, my feed went the way of the buffalo. Let’s revisit the idea and make some modest improvements in order to get some more information about our bathroom habits and have some fun tweeting about it in the process.
Getting started…
First off, we’ll need to know the basics. We’ll be using an ultrasonic range finder hooked up to an ethernet enabled arduino. We’ll also do some serverside programming in order to monitor out toilet and ultimately tweet every time we use our toilet. The rangefinder will act as a detection sensor and also provide us some useful information about the person’s position when they use the toilet, without the privacy invasion of a camera.
Materials…
- ultrasonic rangefinder from parallax (available at radio shack)
- Arduino
- Ethernet shield
- copper wire
- led
- bread board
- server hosting / LAMP enabled
Wiring…
The first step is to hook everything up and begin running a test program to make sure everything is set up properly. We’ll start by attaching the range finder to our toilet:
Next, we’ll run the wires from the range finder to the arduino/ethernet module hooked up wherever it’s conventient:
Finally, we’ll run power to the arduino and upload the example program for range finders provided with the arduino software package:
Once we’re able to output some serial data that shows we are taking accurate measurements, it’s time to modify code…
Coding…
We need to modify this example program a bit to include the ethernet shield and to notify an address on our server as soon as we’ve tripped our sensor. We’ll start by importing some libraries and getting our setup function working:
#include
#include
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0x99, 0xA9, 0xDA, 0x00, 0x99, 0x99 };
byte ip[] = { 192,168,1,177 };
//byte server[] = { 173,194,33,104 }; // Google
byte server[] = { 11,222,33,44 }; // your server IP address
// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
Client client(server, 80);
long inches = 11;
void setup() {
// sonar pins
//pinMode(13, OUTPUT); // blue
//pinMode(12, OUTPUT); // green
// reset pin
pinMode(9, OUTPUT); // resest
pinMode(7, OUTPUT); // ping pin
// start the Ethernet connection:
Ethernet.begin(mac, ip);
// start the serial library:
Serial.begin(9600);
//digitalWrite(9, LOW);
}
void loop()
{
Serial.println("pinging...");
// send a sonar ping
pingSonar();
}
Next we’ll add some custom functions:
void pingSonar() {
// establish variables for duration of the ping,
// and the distance result in inches and centimeters:
long duration, inches, cm;
const int pingPin = 7;
// The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
// Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
// The same pin is used to read the signal from the PING))): a HIGH
// pulse whose duration is the time (in microseconds) from the sending
// of the ping to the reception of its echo off of an object.
pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH);
// convert the time into a distance
inches = microsecondsToInches(duration);
String inchesStr = inches;
Serial.println("ping returned: "+ inchesStr +"...");
// show LED colors
if(inches < 30 ) {
// then the person is sitting
callServer(inchesStr, "sonar");
responseLoop();
//delay(120000);
} else {
// do nothing...
}
//cm = microsecondsToCentimeters(duration);
//Serial.print(inches);
//Serial.print("in, ");
//Serial.print(cm);
//Serial.print("cm");
Serial.println();
delay(3000);
}
void responseLoop () {
for(int i = 0; i < 1200; i++) {
// delay the timer
// BEGIN IF STATEMENTS FROM LOOP
// if there are incoming bytes available
// from the server, read them and print them:
if (client.available()) {
//while(client.available()) {
char c = client.read();
Serial.print(c);
//}
//delay(120000);
}
delay(1);
} // end for loop
// if the server's disconnected, stop the client:
if (!client.connected()) {
Serial.println();
Serial.println("disconnecting.");
client.stop();
//i = 120000;
//digitalWrite(9, LOW);
//delay(120000);
// do nothing forevermore:
delay(560000);
}
// END IF STATEMENTS FROM LOOP
}
void callServer (String inchesStr, String type) {
// reset pin nine
/*digitalWrite(9, HIGH);
delay(100);
digitalWrite(9, LOW);
delay(100);
digitalWrite(9, HIGH);*/
// define some vars
String str = "http://example.com/path_to/process.php?measurement="+ inchesStr +"&type=" + type;
// give the Ethernet shield a second to initialize:
delay(1000);
Serial.println("connecting...");
// if you get a connection, report back via serial:
if (client.connect()) {
Serial.println("connected");
Serial.println("Calling: "+ str);
// Make a HTTP request:
client.println("GET "+ str +" HTTP/1.0");
client.println();
}
else {
// kf you didn't get a connection to the server:
Serial.println("connection failed:" + str);
//digitalWrite(9, LOW);
//callServer(inchesStr, type);
//delay(120000);
}
}
long microsecondsToInches(long microseconds)
{
// According to Parallax's datasheet for the PING))), there are
// 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
// second). This gives the distance travelled by the ping, outbound
// and return, so we divide by 2 to get the distance of the obstacle.
// See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds)
{
// The speed of sound is 340 m/s or 29 microseconds per centimeter.
// The ping travels out and back, so to find the distance of the
// object we take half of the distance travelled.
return microseconds / 29 / 2;
}
After we're done with our arduino, we'll need to create a script that will collect information about our toilet usages and also notify twitter when we're using it. We'll also need to set up a database where we can store this information. Let's create the database now:
CREATE TABLE IF NOT EXISTS `readings` (
`id` int(11) NOT NULL auto_increment,
`measurement` varchar(255) NOT NULL,
`sensor_type` varchar(255) NOT NULL,
`time` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=96 ;
Now let's create a script that will search for a $_GET request and update the database and twitter when it receives the proper information:
<?php
/*
Written By: Mike Newell
mike@iwearshorts.com
https://iwearshorts.com/
No license - Feel free to copy!
*/
if(isset($_GET['measurement'])) {
require_once('lib/db.php');
require_once('lib/TwitPic.php');
$inches = intval($_GET['measurement']);
if($inches < 16) {
$activity = "sitting";
$searchStr = "sitting+on+toilet";
} else {
$activity = "standing";
$searchStr = "standing+at+urinal";
}
/*
GET CONTENTS OF TOILETS DIRECTORY
*/
if ($handle = opendir('./toilets')) {
echo "Directory handle: $handlen";
echo "Files:n";
$images = array();
/* This is the correct way to loop over the directory. */
while (false !== ($file = readdir($handle))) {
//echo "$filen";
$images[] = $file;
}
closedir($handle);
}
// select a random image
$count = count($images);
for($i = 0; $i < $count; $i++) {
$imageNum = mt_rand(1, $count);
$selectedImageSrc = $images[$imageNum];
}
echo 'toilets/'.$selectedImageSrc;
/*
NEW TWITPIC API
* All of this info is needed only for API calls that
* require authentication. However, if the API call doesn't
* require authentication and you provide the information anyways,
* it won't make a difference.
*/
$api_key = ""; // your TwitPic API key (http://dev.twitpic.com/apps)
$consumer_key = ""; // your Twitter application consumer key
$consumer_secret = ""; // your Twitter application consumer secret
$oauth_token = ""; // the user's OAuth token (retrieved after they log into Twitter and auth there)
$oauth_secret = ""; // the user's OAuth secret (also from Twitter login)
/*
* The TwitPic() constructor can be left blank if only
* doing non-auth'd API calls
*/
$twitpic = new TwitPic($api_key, $consumer_key, $consumer_secret, $oauth_token, $oauth_secret);
try {
/*
* Uploads an image to TwitPic AND posts a tweet
* to Twitter.
*
* NOTE: this still uses v2 of the TwitPic API. This means that the code makes 2 separate
* requests: one to TwitPic for the image, and one to Twitter for the tweet. Because of this,
* understand this call may take a bit longer than simply uploading the image.
*/
$resp = $twitpic->uploadAndPost(array('media'=>'toilets/'.$selectedImageSrc, 'message'=>"Someone is ".$activity." at Mike's toilet right now!"));
print_r($resp);
} catch(TwitPicAPIException $e) {
echo $e->getMessage();
}
//$connection->post('statuses/update', array('status' => "Guess the activity! Someone is using Mike's toilet "));
// set the values to variables
$measurement = $_GET['measurement'];
$type = $_GET['type'];
// store the value in a database
$db = new DBconnect("localhost", "user_name", "password", "database_name");
$result = $db->query("INSERT INTO readings (measurement, sensor_type) VALUES ('$measurement', '$type')");
if($result) {
echo "successful.";
} else {
echo "unsuccessful input.";
}
} else {
echo "get request with measurement not present";
}
?>
We also need a twitter class that will handle pictures. I chose to use twitpic, you can download the library here.
We'll also need a small database class to manage input and output from our database. Download the demo at the bottom of the page to get it.
Once we've properly initialized everything, it's just a matter of getting the proper api keys from twit pic and twitter. Make sure to enter your database connection information into the new class, modify the arduino code with your server's specific IP address and path to the process.php file wherever you decide to store it on your server. You'll also need to make sure and have HTTP_OAuth and HTTP_Request2 installed on your server.
Download the demo!