iSteg for Mac

Cool and easy to use tool for encrypting/decrypting hidden messages in images. It’s not the most versatile, but it does allow for encrypting and hiding zipped files (so you could hide multiple file types), its great for sending encrypted messages with low fingerprint and it’s easy to use on a mac.

Just download the program here, install it and away you go!

Here’s a demo to make sure its working!

And here’s the image to make sure it’s working (password = test):

If everything was installed right you should get a short message when you inspect it!

If you are looking for a quick read on steganography download, a great pdf can be found here. Also a link to the source here.

Fixing Annoying CSS Opacity Inheritance

The issue of CSS opacity being unwittingly inherited by child elements has come up several times, so below is my workaround.

There are several options:

  1. Create the desired effect with a .png or some other image format with the “alpha” value. This method works well in terms of browser support, but increases loading times and the amount of work a developer would have to do to make changes.
  2. Use the new CSS3 rgba rule, but it’s still relatively new and most of your visitors probably won’t see the changes.
  3. Adjust the page layout to position elements over each other to create the desired layout effect without inheriting the opacity rule (I generally choose this option).

I’m assuming you have some sort of background container you want to make translucent with content inside being opaque. To adjust the page layout, simply move the content elements out of the background container and into another container that you’ll float over top of the transparent/translucent background. See below…

<!DOCTYPE HTML>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>Positioning</title>
		<link href='http://fonts.googleapis.com/css?family=Ultra' rel='stylesheet' type='text/css'>
		<link href="style.css" rel="stylesheet" type="text/css" />
	</head>
	<body>
		<h1>CSS Opacity</h1>
		<!-- style this container to the area and size of the translucent background container -->
		<section>
			<!-- nothing in here...just style this as the background container -->
			<article id="translucent"></article>
			<!-- This article contains the content, we'll position this content over the background above-->
			<article id="opaque-content">
				<h1>Opaque</h1>
				<p>This background has an opacity of 50%, but the content laid on top of it has an opacity of 100%. This is possible by organizing elements on the page to "lay over" on another so they appear to be descendent children but are in fact different levels of elements.</p>
				<p>Feel free to check out the source code. This page is just a demo but I've included the source code in a downloadable package <a href="http://iwearshorts.com/Mike/uploads/2011/06/opacity.zip">here</a>. If you want to know more, read my tutorial and explanation <a href="http://iwearshorts.com/blog/annoying-css-o…ed-by-children/">here</a>.</p>
			</article>
		</section>

	</body>
</html>

And then the CSS here…

body {
	background-color: #3D3D3D;
	background: url(images/bg.gif) scroll repeat;
	font-family: 'Ultra', arial, serif;
}

section {
	margin: 0 auto;
	position: relative;
	width: 500px;
	height: 300px;
	margin-top: 100px;
	left: -10px;
}

article#translucent {
	width: 100%;
	height: 100%;
	background-color: #FF0066;
	/* for IE */
	filter:alpha(opacity=50);
	/* CSS3 standard */
	opacity:0.5;
	-webkit-border-radius: 40px;
	-moz-border-radius: 40px;
	border-radius: 40px;
	border: 20px solid white;
}

article#opaque-content {
	width: 460px;
	position: absolute;
	top: -65px;
	left: 40px;
	color: black;
	text-align: center;
}

h1 {
	text-align: center;
	color: white;
	margin-top: 100px;
	text-shadow: #000 1px 1px 0;
}

p {
	font-family: arial, serif;
	color: white;
}

Basically we create a box to contain our background and content. We position it where we want on the page, and then create the background box as the same size as its container. Then we create the content box and position it over the background box using position: absolute;. Make sure your section element is positioned relatively so the absolutely positioned content container will stay within its boundaries.

Demo.

Download.

Preparing for an Interview

I’m prepping for an interview tomorrow with CP+B and wanted to share a good technique for getting ready.

  • Have goals for the interview, details you want to talk about
  • Prepare some talking points on specific items in your portfolio
  • Write some clear goals for your future in the industry, how do you see yourself changing the industry?
  • Create an evaluation beforehand for user after the interview. Review how the interview went. Did you meet your goals?

Below is the PDF I used to get ready for this interview.

Download Interview Prep

The Yari

Toyota Yaris Project

We did some initial concepting and creative for Toyota in order to engage niche communities to increase brand awareness and loyalty in younger generations.

Team…

Lead/Presentation/Copy: Jon Swihart

Concept: Jesse Weaver, Mike Newell

Creative: Brad Dechter, Max Minor and Megan Newton

Description…

The purpose of this project was to engage a targeted community and introduce Toyota in the conversation.

Store Your passwords with Salts

This isn’t a new practice by any means, if your PHP developer is worth their salt they will recommend relying on a third party to authenticate users like Facebook or Twitter whenever possible. What about the times when you can’t rely on third parties to authenticate your users?

After the Gawker incident there was a large push for better password storage practices. Here’s a fairly safe way to store passwords if you must:

<?php
define('SALT_LENGTH', 9);

function generateHash($plainText, $salt = null)
{
    if ($salt === null)
    {
        $salt = substr(md5(uniqid(rand(), true)), 0, SALT_LENGTH);
    }
    else
    {
        $salt = substr($salt, 0, SALT_LENGTH);
    }

    return $salt . sha1($salt . $plainText);
}
?>

It uses what’s know as a “salt” to add length and randomness to a password string. This “salted string” contains both the users’s password and the salt encrypted together to make rainbow cracking this hash a very laborious task. It’s called a one way function in that the result is hard to invert without knowing some “secret” portion of the equation.

DIY Smart Home – Part 2

In this part of the series on “do it yourself” smart homes we head over to the shower where we’ll be installing an infrared sensor to monitor when we get in and out of the shower. This can provide us with a lot of useful information including an estimation of our water usage and trending to let us know our average shower time.

Getting started…

If you haven’t followed along with part 1, please start there. This tutorial is intended to expand upon the last one and won’t explain as much information. Additionally, we’ll be using the same arduino to run our infrared sensor and the sonar range finder so we’ll need to alter that code. Addtitionally, I’ve made some improvements to the backend and the arduino script.

Materials…

  • Arduino
  • Server/domain to call with a get request
  • IR sensor from radio shack
  • sonar range finder from radio shack
  • New twitter account with API credentials for your shower

Coding the Arduino…

It’s probably good to start off with a simple example to get the new IR sensor working, once we can get that working well port that to the original arduino code that we already have set up to run our sonar range finder. Here’s a simple example from ladyada.net which explains (in great detail) how your IR sensor works.

/*
 * PIR sensor tester
 */

int ledPin = 13;                // choose the pin for the LED
int inputPin = 2;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
int val = 0;                    // variable for reading the pin status

void setup() {
  pinMode(ledPin, OUTPUT);      // declare LED as output
  pinMode(inputPin, INPUT);     // declare sensor as input

  Serial.begin(9600);
}

void loop(){
  val = digitalRead(inputPin);  // read input value
  if (val == HIGH) {            // check if the input is HIGH
    digitalWrite(ledPin, HIGH);  // turn LED ON
    if (pirState == LOW) {
      // we have just turned on
      Serial.println("Motion detected!");
      // We only want to print on the output change, not state
      pirState = HIGH;
    }
  } else {
    digitalWrite(ledPin, LOW); // turn LED OFF
    if (pirState == HIGH){
      // we have just turned of
      Serial.println("Motion ended!");
      // We only want to print on the output change, not state
      pirState = LOW;
    }
  }
}

Once we have our IR sensor connected and we’re able to get a solid serial output. Let’s modify this script to show when we get in and out of the shower instead of just detecting motion. We’ll set up a flag to alternate motion events in order to flag our initial event as the “getting in” event and our second motion event as our “getting out” motion.

/*
 * PIR sensor tester
 */

int ledPin = 13;                // choose the pin for the LED
int inputPin = 2;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
int val = 0;                    // variable for reading the pin status
int flag = 0;

void setup() {
  pinMode(ledPin, OUTPUT);      // declare LED as output
  pinMode(inputPin, INPUT);     // declare sensor as input

  Serial.begin(9600);
}

void loop(){
  val = digitalRead(inputPin);  // read input value
  if (val == HIGH) {            // check if the input is HIGH
    digitalWrite(ledPin, HIGH);  // turn LED ON
    if (pirState == LOW) {
      // we have just turned on
      Serial.println("Motion detected!");

      if(flag == 0) {
        flag = 1;
        Serial.println("in the shower");
      } else if(flag == 1) {
        flag = 0;
        Serial.println("out of the shower");
      }
      // We only want to print on the output change, not state
      pirState = HIGH;
    }
  } else {
    digitalWrite(ledPin, LOW); // turn LED OFF
    if (pirState == HIGH){
      // we have just turned of
      Serial.println("Motion ended!");
      // We only want to print on the output change, not state
      pirState = LOW;
    }
  }
}

Once we get that working it’s time to modify our original code, last time we made a script to run the sonar sensor, we’ll modify this script. The following the newest modified version.

/*

updated as of 4/30/11 - included shower script

  Web client

 This sketch connects to a website (http://www.google.com)
 using an Arduino Wiznet Ethernet shield. 

 Circuit:
 * Ethernet shield attached to pins 10, 11, 12, 13

 created 18 Dec 2009
 by David A. Mellis

 */

#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {  0x90, 0xA2, 0xDA, 0x00, 0x38, 0x04 };
byte ip[] = { 192,168,1,177 };
//byte server[] = { 173,194,33,104 }; // Google
byte server[] = { 67,222,18,93 }; // iwearshorts

// 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;

// shower
int inputPin = 2;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
int val = 0;                    // variable for reading the pin status
int flag = 0;

void setup() {

// 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);

  // shower
  pinMode(inputPin, INPUT);     // declare sensor as input

}

void loop()
{
  val = digitalRead(inputPin);  // read input value
  if (val == HIGH) {            // check if the input is HIGH
//    digitalWrite(ledPin, HIGH);  // turn LED ON
    if (pirState == LOW) {
      // we have just turned on
      Serial.println("Motion detected!");

      if(flag == 0) {
        flag = 1;
        Serial.println("in the shower");
        // we want to notify the server of a shower update
        callServer("1", "infrared");
        client.flush();
        client.stop();
        delay(5000);
      } else if(flag == 1) {
        flag = 0;
        Serial.println("out of the shower");
        // we want to notify the server of a shower update
        callServer("0", "infrared");
        client.flush();
        client.stop();
        delay(5000);
      }
      // We only want to print on the output change, not state
      pirState = HIGH;
    }
  } else {
//    digitalWrite(ledPin, LOW); // turn LED OFF
    if (pirState == HIGH){
      // we have just turned of
      Serial.println("Motion ended!");

      // We only want to print on the output change, not state
      pirState = LOW;
    }
  }

  // if no motion, start monitoring the bathroom
  Serial.println("pinging...");
  // send a sonar ping
  pingSonar();

}

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 < 35 && inches > 0) {
    // then the person is sitting
     callServer(inchesStr, "sonar");
     responseLoop();
  } else {
    // do nothing...
  }
  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()) {

                            char c = client.read();
                            Serial.print(c);

                        }
     delay(1);

   } // end for loop

    // if the server's disconnected, stop the client:
    if (!client.connected()) {
      Serial.println();
      Serial.println("disconnecting.");
      client.stop();

    }
    delay(560000);
      // END IF STATEMENTS FROM LOOP

}

void callServer (String inchesStr, String type) {

  String str = "http://iwearshorts.com/bathroom/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);
  } 

}

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;
}

No that we have our newly modified script running both our sonar range finder and our IR sensor its time to modify the backend to post some pictures of showers on twitter and update our database with our usage data. I have not included the calculations for water usage yet, we’ll do that in part 3 of this tutorial.

The backend…

Now lets open process.php in our backend and modify the script to process IR data as well. We’ve set up our arduino to send a data “type” in the GET string so we’ll just have to decide whether we’re receiving data from a “sonar” event or an “infrared” event. I’ve also set up a new twitter account specifically for my shower so I have an API key and authentication for that account. Based on the data “type” we’ll either upload a picture of a toilet or a shower. Then we’ll post it to twitter and input data to our database. Below is the modified process.php file.

<?php
if(isset($_GET['measurement']) && isset($_GET['type'])) {
	require_once('lib/db.php');
	require_once('lib/TwitPic.php');
	$inches = intval($_GET['measurement']);
	$type = $_GET['type'];

	if($type == "sonar") {
		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_API_KEY"; // your TwitPic API key (http://dev.twitpic.com/apps)
		$consumer_key = "TWITTER_CONSUMER_KEY"; // your Twitter application consumer key
		$consumer_secret = "TWITTER_CONSUMER_SECRET"; // your Twitter application consumer secret
		$oauth_token = "TWITTER_OAUTH_TOKEN"; // the user's OAuth token (retrieved after they log into Twitter and auth there)
		$oauth_secret = "TWITTER_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();
		$result = $db->query("INSERT INTO readings (measurement, sensor_type) VALUES ('$measurement', '$type')");
		if($result) {
			echo "successful.";
		} else {
			echo "unsuccessful input.";
		}

	} elseif ($type == "infrared") {

		if($inches == 1) {

			if ($handle = opendir('./showers')) {
			    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 'showers/'.$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 = "TWITPIC_API_KEY"; // your TwitPic API key (http://dev.twitpic.com/apps)
			$consumer_key = "TWITTER_CONSUMER_KEY"; // your Twitter application consumer key
			$consumer_secret = "TWITTER_CONSUMER_SECRET"; // your Twitter application consumer secret
			$oauth_token = "TWITTER_OAUTH_TOKEN"; // the user's OAuth token (retrieved after they log into Twitter and auth there)
			$oauth_secret = "TWITTER_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.
			  */
	/* 		  if($inches == 1) { */
			  $resp = $twitpic->uploadAndPost(array('media'=>'showers/'.$selectedImageSrc, 'message'=>"Someone is using Mike's shower right now!"));
	/* 		  } */
	/* 		  $resp = $twitpic->uploadAndPost(array('media'=>'showers/'.$selectedImageSrc, 'message'=>"Someone is using Mike's shower right now!")); */
			  print_r($resp);
			} catch(TwitPicAPIException $e) {
				echo $e->getMessage();
			}

		} // end if inches == 1

		//$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();
		if($inches == 1) {
			$measurement = "in";
		} elseif($inches == 0) {
			$measurement = "out";
		} else {
			$measurement = "Unkown shower variable";
		}
		$result = $db->query("INSERT INTO readings (measurement, sensor_type) VALUES ('$measurement', '$type')");
		if($result) {
			echo "successful." . $result;
		} else {
			echo "unsuccessful input." . $result;
		}
	} else {
		echo "type not recognized";

	}

} else {
	echo "get request with measurement not present";
}
?>

Now that we have a PHP script that can handle our data, we just need to test and make sure it works!

Summary…

We updated our arduino script to run both a sonar range finder and IR sensor. We then told it to call our server in the event of an interation with one of these sensors. We send a get request with our data to our server where a script (called process.php) takes our data and decides what to do with it. It then uses the twitpic API to upload a new photo of either a toilet or a shower (depending on the event) and update our database with the new interaction.

Download…

Feel free to download the source and play around with it! You can also follow my shower @MikesShower and my toilet @miketoilet!

Building 16 Sites in 2 Hours

Our program (Boulder Digital Works) recently hosted the Making Digital Work conference in Boulder. Jesse Weaver and I were requested to help with the conference. The idea was to split the conference attendees into teams and develop an idea. They would come up with two variations of that idea and create some copy/keyword combinations to test between. Then Jesse and I code all the variations for each team into web pages that we could test with Google’s web optimizer. Tim Malbon developed the logos and Jesse and I coded. It turned out well and the teams had some great ideas! I have  a few takeaways:

Simple Works….

The template, the process and the testing platform were all immediately understandable and easy to work with.

Start with a Clear Explanation…

Start with a clear explanation of the intended outcome. Tim explained the idea in one short email. The concept wasn’t hard to grasp because it was clearly explained and thus we were able to put most of our energy into building it.

Don’t Agonize…

When you’re rapidly prototyping an idea it doesn’t need to be perfectly polished, the key word here is “rapid.” I noticed several small errors while were presenting the websites, but it didn’t matter because the groups understood that this was an exercise in speed not quality. Most importantly they got the overall gist.

Make Backups…

About 4AM on the day of our presentation, I had the thought to make backups. Like an idiot, I thought “nah, my server hasn’t gone down in 8 months…what are the chances?” Famous last words. About 7:30AM I woke up to a text message telling me my server had gone down. I checked several ports including SSH and found confirmed my server was unavailable. I contacted my hosting provider who informed me their whole network was out due a (d)Dos attack from LA, California. I assumed it would only take an hour to clear up, but three hours later we getting a little worried. I had an encrypted backup, but couldn’t open it because I didn’t have my encryption key. It’s been a while since I’ve used those backups and forgot to get the key when I left my last job. A couple hours before the presentation the network came back online. We got lucky. Had my server been out during that time, we would have been screwed! Always make a backup, test the backup works and have  a backup plan to easily switch to another server just in case.

Designate a Zero Hour…

Decide collectively on a time when all production must be finished and changes cannot be made after that point. There is always an urge to “tweak” things to make you project just a little bit better. This is dangerous territory, in programming there are always unforeseen consequences to changes and they may not be immediately apparent. The reason I was up at 4AM (day of) was because I was “tweaking” things. However, when you’re tired you make mistakes and I failed to realize that some other tweaks I was made obfuscated some of the text on the pages. This made things difficult to read and became pejorative rather than helping.

All in all I think the project was successful, things could have gone smoother but considering we built 16 websites in a day, things went as well as they probably could have. Thank again to Jesse Weaver and Tim Malbon for making this a success.

Make Your Own Instavision

Recently I built the backend for the Instavision TV and wanted to give a quick tutorial on how to build one yourself. Brian Fouhy, George Tong, Jesse Weaver and Pedro Sorrentino actually made this TV a reality.

Materials

  • Retro looking TV
  • VGA to Coaxial adapter
  • Old Laptop
  • Instagram account
  • Instagram API Client ID, Client Secret and an Access Token
  • Node.js
  • Shell access to your server

Getting Started…

The first step is to get the backend working. Register a new client on the Instagram API, just register for the url you’ll be using to setup your feed.

Next you need to store an access token to use later on in our node.js file. Once you have your Client registered with Instagram, simply input the following into your browser’s url (replacing with your newly registered client ID and your specific redirect URI):

https://instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=token

Once you visit this page, simply login with your instagram account credentials and you should be redirected. You don’t necessarily need to have your redirect URI do anything when you land on it, what your interested in is the access token.

Copy this access token and save it for later, we’ll use it to gain access to Instagram and pull information for our TV to display.

Coding…

Lets get our code up and running. The basic idea is to have a client side page calling our server about every 5 seconds to get an update on the most recent photo. We’ll then also need something on our server to go out and get the most recent photos from Instagram, this script will also run about every 5 seconds. It would be hard to do this with PHP, sleep() and a chron job…so we’ll turn to node.js. Copy the following script and put it in the root of your public_html folder (make sure you replace your access token with the access token you got in the previous step also, you’ll need to make sure and replace all paths and urls with your own custom paths and domains).

/*
	INSTRUCTIONS

	shell: cd /home/instavis/public_html/
	shell: node instavision.js
	shell: kill ##### (process number)

	call the url: http://instavision.tv:8080/feed.html

	Needs:
			find out rate limit with instagram = 5000 requests per hour
*/
var sys = require("sys"),
    http = require("http"),
    https = require("https"),
    url = require("url"),
    path = require("path"),
    fs = require("fs"),
    util = require("util"),
    events = require("events");  

function load_static_file(uri, response) {
    var filename = path.join(process.cwd(), uri);
    path.exists(filename, function(exists) {
        if(!exists) {
            response.writeHead(404, {"Content-Type": "text/plain"});
            response.write("404 Not Foundn" + " filename: " + filename);
            response.end();
            return;
        }  

        fs.readFile(filename, "binary", function(err, file) {
            if(err) {
                response.writeHead(500, {"Content-Type": "text/plain"});
                response.write(err + "n");
                response.end();
                return;
            }  

            response.writeHead(200);
            response.write(file, "binary");
            response.end();
        });
    });
}

//var twitter_client = http.createClient(80, "api.instagram.com");  

var tweet_emitter = new events.EventEmitter();  

function get_tweets() {
    //var request = twitter_client.request("GET", "/v1/tags/bdwinstavision/media/recent?access_token=3036112.f59def8.4708bbe9fe9e43639c8d9fe93c5c41df", {"host": "api.instagram.com"}); 

    var options = {
    // public search - unauthenticated
	//host: 'api.instagram.com',
	//path: '/v1/tags/bdwinstavision/media/recent?access_token=3036112.f59def8.4708bbe9fe9e43639c8d9fe93c5c41df',

	// authenticated feed - https://api.instagram.com/v1/users/self/feed?access_token=3036112.c90c34d.411d18988857490c9032a2bac30e6943

	// instavisionTV access token
	// access_token = 3036112.c90c34d.411d18988857490c9032a2bac30e6943

	// instavision access token
	// access_token = 3060511.0b03a1e.fff65e339b694cf0844306131ab82b97
	host: 'api.instagram.com',
	path: '/v1/users/self/feed?access_token=3060511.0b03a1e.fff65e339b694cf0844306131ab82b97',
    } 

    https.get(options, function (res) {
		var raw = "";
		res.on('data', function (chunk) {
		    raw += chunk;
		});
		res.on('end', function () {
		    var response = JSON.parse(raw);

		   //sys.puts(response); 

		    if(raw.length > 0) {
	/*         	sys.puts("tweets:" + response);  */
	            tweet_emitter.emit("tweets", response);
	        }
	        //res.removeListener('connection', callback);
		});
    }).on('error', function(e) {
	  //console.error(e);
	  sys.puts(e);
	});

    /*request.addListener("response", function(response) {
        var body = "";
        response.addListener("data", function(data) {
            body += data;
            sys.puts("tweets:" + data);
        });  

        response.addListener("end", function() {
            var tweets = JSON.parse(body);
            sys.puts(body);
            if(tweets.length > 0) {
            	//sys.puts("tweets:" + tweets);
                tweet_emitter.emit("tweets", tweets);
            }
        });
    }); */ 

    //request.end();
}

setInterval(get_tweets, 5000);

http.createServer(function(request, response) {
    var uri = url.parse(request.url).pathname;
    //var uri = request.url;

    var callback = function(stream) {
	  sys.puts('listener removed!');
	};

    if(uri === "/stream") {  

        //var listener = tweet_emitter.addListener("tweets", function(tweets) {
        var listener = tweet_emitter.on("tweets", function(tweets) {
            response.writeHead(200, { "Content-Type" : "text/plain" });

            response.write(JSON.stringify(tweets));

            //sys.puts("stringify tweets:" + JSON.stringify(tweets));

            response.end();

            //var listenersForTeets = tweet_emitter.listeners(listener);

        	//sys.puts(util.inspect(listener));

        	//tweet_emitter.removeListener(tweets);
        	//tweet_emitter.removeListener(tweet_emitter);
        	//tweet_emitter.removeListener(listener);
        	//tweet_emitter.removeListener("tweets");
  			//tweet_emitter.removeListener("listener");
  			//tweet_emitter.removeListener("tweet_emitter");

  			listener.removeAllListeners("tweets");

            clearTimeout(timeout);  

        });

        //tweet_emitter.removeListener(tweets);

        //sys.puts(util.inspect(tweet_emitter.listeners(listener), true, null));

        var timeout = setTimeout(function() {
            response.writeHead(200, { "Content-Type" : "text/plain" });
            response.write(JSON.stringify([])); 

            response.end();  

            tweet_emitter.removeListener(listener);
        }, 10000); 

    }
    else {
        load_static_file(uri, response);
    }
}).listen(8080);  

sys.puts("Server running at http://instavision.com:8080/");

Note: I am pulling a user feed in this case, you can also do a search for specific photos and other users’ feeds if you call different urls. For more information read the Instagram API.

Once you have this copied and named instavision.js in your root, you need to make an html page with a special script to call instavision.js and get the most recent photos. Copy the following and save this into a file named feed.html, also in the root of your public_html folder:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link href="css/styleDark.css" rel="stylesheet" type="text/css">
<title>Instavision TV</title>
</head>

<body style="">
    <div id="wrap">

    	<div id="tweets"></div>
    </div>

	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
	<script type="text/javascript">
	    var tweet_list = $("#tweets");
	    var tweet_caption = $("#caption"); 

	    function load_tweets() {
	        $.getJSON("/stream", function(tweets) {
	            var imageurl = tweets.data[0].images.standard_resolution.url;
	            var imageUser = tweets.data[0].user.username;
	            var imageCaption = '';
	            if (tweets.data[0].caption != null){
	            var imageCaption = tweets.data[0].caption.text;
	            }

	            console.log(imageUser);
	            console.log(imageCaption);
	            imageurl = "<img id='tweet' src='"+imageurl+"' /><br/>";
	            imageCaption = "<div id='caption'><img id='captionImage' src='/images/ColorBars.png' height='18px' width='29px'/><h1>"+imageUser+"</h1><h3>"+imageCaption+"</h3></div>";
	            console.log(imageurl);
	            $(tweet_list).html(imageurl + imageCaption);
	            load_tweets();
	        });
	    }  

	    setTimeout(load_tweets, 5000); 

	    $(document).ready(function() {
	    	load_tweets();
	    });
	</script>

</body>
</html>

Once you have feed.html saved, you can try it out! You have to run the instavision.js script as a process on your server, simply run these commands to get it running:

cd /home/instavis/public_html
node instavision.js&

Make sure you have node installed. If you find that you want this process to stop, look at its process ID and kill it by:

kill 76234

Once you have the process running, visit the feed.html page at port 8080 (place the url with your custom url):

http://your-custom-domain.com:8080/feed.html

If it worked, then you’re good to go! If it didn’t, leave a comment below and I’ll try to help out!

Once you have it up and running, its simply a matter of plugging the TV in and formatting the image, which is totally a case by case situation and thus beyond the scope of this tutorial.

Bubble Sorting Algorithm

Recently, I had the need to re-order posts in wordpress according to a specifically numbered tag. Unfortunately, WordPress doesn’t let you order post output by tag since that’s usually what categories are used for. So I decided to capture all the output into a multidimensional array. However, I still needed some way to sort the array and asort(); wasn’t the answer. Instead I used something called a Bubble sort algorithm to do the heavy lifting.

The code…

for($x = 0; $x < $array_size; $x++) {
  for($y = 0; $y < $array_size; $y++) {
    if($ran[$x] < $ran[$y]) {
      $hold = $ran[$x];
      $ran[$x] = $ran[$y];
      $ran[$y] = $hold;
    }
  }
}

It works by running through the entire array, comparing each element to every element in the array. If it encounters a situation where the item’s value is less than the next item it stores it in a temporary variable and sets the variable to the larger version. Then replaces the previously larger version with the smaller amount. If you don’t get it, here’s a great video to explain it!

Netbeans, XDebug and Xampp on Mac

I recently had some confusion about x-debug on a mac. I read online when I googled it that it’s not possible to debug with netbeans on a mac using x-debug. Not true, so I wanted to clarify and release some instructions on how to do this.

Prerequisites…

Make sure you have netbeans and xampp installed on your local machine. This environment is great for debugging but not ready for production so please don’t try to install the xampp environment on any production ready server. Once you have everything installed we can alter the installation in to allow xdebug to work.

Installation…

  1. Find out what PHP version your running. If you don’t know, simply put “phpinfo()” in a file, save it and visit the file in a browser (with xampp running of course).
  2. Download the x-debug binaries. You can find there at: http://code.activestate.com/komodo/remotedebugging/
  3. Open the gzipped file and search for a subfolder that corresponds with your php version. In that folder you’ll find a file called “xdebug.so”
  4. Copy this file and navigate to your php extensions folder. I will look something like: /Applications/XAMPP/xamppfiles/lib/php/php-5.3.1/extensions/no-debug-non-zts-20090626. Note the file path, you will use this later.
  5. Paste the “xdebug.so” file into this directory.
  6. Now edit your php.ini file. It’s probably located in /Applications/XAMPP/xamppfiles/etc/php.ini.
  7. Open this file in your favorite text editor and scroll to the bottom. Paste the following in making sure to tweak the file paths to your specific setup:
  8. [xdebug]
    zend_extension=/Applications/XAMPP/xamppfiles/lib/php/php-5.3.1/extensions/no-debug-non-zts-20090626/xdebug.so
    xdebug.file_link_format="txmt://open?url=file://%f&line=%1"
    xdebug.remote_enable = On
    xdebug.remote_autostart = 1
  9. Restart Apache with the xampp controls and your good to go! Open netbeans and happy debugging!

UPDATE:

As of now, the newer XAMPP ships with the latest xdebug pre-installed so no need to worry about the above. Just install a fresh copy of xampp and edit the php.ini file to the following:

; ZEND XDEBUG
zend_extension = /Applications/XAMPP/xamppfiles/lib/php/extensions/no-debug-non-zts-20121212/xdebug.so
xdebug.remote_host=127.0.0.1
xdebug.remote_handler=dbgp
xdebug.remote_enable=on
xdebug.remote_port=9000
xdebug.remote_autostart=1

This should have everything you need to get this up and running immediately and should work with an update copy of netbeans as well!