Midnight Cheese

Exploring Design, Web Development and Art

Old Stones River Road

| Comments

If you look at the Percy Priest Lake area on Google Maps you can often see old roads that lead straight into the water. Recently I noticed such a road parallel to Stones River Road here in La Vergne, and this past fall Merredith and I decided to take a walk to see what we could find.


Old Stones River Road on Google Maps

It turns out there was quite a bit to see of Old Stones River Road. From small drainage bridges to old fencing to the actual roadbed, it looks as though the road was left as-is when Percy Priest Lake was built in the 1960s. The following photos were taken just south of the Hurricane Creek boat ramp area.


Old Stones River Road


One of the drainage bridges along Old Stones River Road


Old Stones River Road showing fencing and drainage bridge


View of Percy Priest Lake from Old Stones River Road

Switching From WordPress to Octopress

| Comments

Today marks yet another milestone for Midnight Cheese by successfully making the migration from WordPress to Octopress. With web trends moving toward fast, light, and responsive, my WordPress setup was becoming slow, bloated, and stagnant. WordPress functions well, but for a single user it was just too large of a feature set. The slow page-load times really made this obvious.

Octopress is the complete opposite of WordPress. Octopress is all static. No scripting. No DB.

Some History
The current design is the default Octopress theme. That means a redesign is coming soon. I started looking back and realized 2007 was the last time I applied a design update, which is a bit embarrassing.

Octopress is the fourth blogging CMS that the site has operated under. Blogger started everything off very early on, followed by Greymatter (also based on static files) in 2001, WordPress in 2004, and now Octopress.

Midnight Cheese in 2002
Midnight Cheese in 2002

Issues with Disqus
The WordPress import instructions on the Octopress site worked well. The most time consuming part for me was getting Disqus properly set up. It turns out you have to enable comments on each post.


  comments: true

This meant a lot of text manipulation across some 470 posts. I used a little perl action to run through the _posts folder and change what was needed in each file. Basically a find and replace action.


  perl -pi -e 's/type: post/type: post\ncomments: true/g' *.html

In addition, to get the comment count to appear on the main index view, I had to implement this Disqus fix from Benjie Gillam.

Image Files and Redirects
Storing my repo on Github, I decided I didn’t want to have all my images and various media files sitting on Github’s servers, or across multiple repos on my various computers. On most posts I often have quite a few images. To solve this I created an assets subdomain and moved everything over, keeping all those large files out of the code base.

Many of my images rank well on Google image search which drives a good bit of traffic to the site. That meant 301 redirects for all my images. And again I used that perl script to run through all the posts to modify image URLs.

To the Future
I love how light and responsive the site has become with the static setup. Next on the list is a fresh design and custom asides. Flickr will probably be top on the aside list.

Displaying Twitter and Weather on Your Arduino LCD Screen

| Comments

Pieced together from many different sources, the following scripts will allow you to display the Twitter public timeline, the top 20 Twitter trending topics and the current weather on your Arduino LCD screen. This is done with a PHP script and an Arduino connected to OS X via USB.

You can download the scripts as a zip file.

The Arduino Program The tricky part of this project was getting the text to scroll up the two line LCD screen. In the Arduino program, we’re looking to see if a line of text has a special character prefixed to it. Text prefixed with ‘!’ will place the text on line 1. Text prefixed with ‘@’ will place the text on line 2. Sending the ‘^’ character clears the screen.


#include <liquidcrystal.h>
#include <wstring.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(2, 3, 4, 9, 10, 11, 12);

void setup() {
  analogWrite(5, 100);    // Set LCD contrast level (0-255)
  lcd.begin(16, 2);       // Set the LCD's number of rows and columns
  Serial.begin(9600);     // Initialize communication with serial(USB) port.
  lcd.print("Hello.");    // Print welcome message to LCD.
}

int bufferArray[250];     // Our array to store characters arriving from serial port.
int output = 0;
int i = 0;

void loop() {
  int count = Serial.available();

  if (Serial.available() > -1) {
    delay(1000);
    for (i=0; i<count ; i++) {
     bufferArray[i] = Serial.read();          // Put into array
     output = 1;                              // Show new data has been recieved
    } 
  }
  
  if (output != 0) {                          // If new bytes have been recieved                
    int position = 0;
    if (bufferArray[0] == '!') {              // Print on first line if message begins with '!'
      lcd.clear();
      lcd.setCursor(0,0);
      position = 1;
    } else if (bufferArray[0] == '@') {       // Print on second line if message begins with '@'
      lcd.setCursor(0,1);
      position = 1;
    } else if (bufferArray[0] == '^') {       // Clear screen if message begins with '^'
      lcd.clear();
      lcd.setCursor(0,0);
      position = 1;
    } else {
      lcd.clear();
      lcd.setCursor(0,0);
    }
    int j;
    for (j = position; j < count; j++) {
      lcd.write(bufferArray[j]);
    }
    output = 0;                               // Don't print on next iteration
    memset(bufferArray, 0, count);
    count = 0;
  }
}

The PHP Script With those rules established, our PHP script can handle the rest. I’m using fun classes like wordwrap and explode to break long strings of text into LCD width chunks that are placed into an array, prefixed with our placement characters, and fed to the Arduino.


<?php
// Include the PHP Serial class.
include "php_serial.class.osx.php";
//Define the serial port to use
define('SERIALPORT','/dev/cu.usbserial-A900adK5');

// Weather
$zipcode = 37211;
$title = "Nashville WX.";

// Setup the serial connection
$serial = new phpSerial; 
$serial->deviceSet(SERIALPORT);
$serial->confBaudRate(9600);

// Time
$lastTime = date('D M j H:i:s Y');
$lastTime = strtotime($lastTime);

// Scroll text up LCD screen 
function scrollChunks($message) {
  global $serial;
  $serial->deviceOpen();
  for ($i=0; $i<count ($message); $i++) {
    if ($i==0) {
      $serial->sendMessage($message[$i]);
    } else if ($i&1 && $i!=0) { // If $i is odd and not zero
      $serial->sendMessage($message[$i]);
      sleep(1);
      $message[$i] = substr_replace($message[$i], "!", 0, 1); // Print on top line !
      $serial->sendMessage($message[$i]);
    } else if (!($i&1) && $i!=0) { // If $i is even and not zero
      $message[$i] = substr_replace($message[$i], "@", 0, 1); // Print on top line @
      $serial->sendMessage($message[$i]);
      sleep(1);
      $message[$i] = substr_replace($message[$i], "!", 0, 1); // Print on top line !
      $serial->sendMessage($message[$i]);
    }
    echo $message[$i]."\n";
    sleep(1);
  }
  sleep(1);
  $serial->sendMessage(" ");
  $serial->deviceClose();
}

// Wordwrap our text to lines no more than 15 characters long.
// The LCD displays 16 characters at a time, minus a space for our
// placement character.
function segmentString($t) {
  $chunks = wordwrap($t, 15, "\n", true);
  $chunks = explode("\n", $chunks);
  // Alternate each line with '!' and '@'
  for ($i=0; $i<count ($chunks); $i++) {
    if ($i&1) {
      $chunks[$i] = "@".$chunks[$i];
    } else {
      $chunks[$i] = "!".$chunks[$i];
    }
  }
  scrollChunks($chunks);
}

function displayRSS() {
  global $serial;
  global $title;
  echo "Display only: ".$title."\n";
  sleep(3);
  $serial->deviceOpen();
  $serial->sendMessage($title);
  sleep(240);
  $serial->sendMessage("^");
  sleep(3);
  $serial->deviceClose();
}

function pullWXRSS() {
  global $zipcode;
  $yql = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20location%3D".$zipcode."&format=json";
  $session = curl_init($yql);
  curl_setopt($session, CURLOPT_RETURNTRANSFER,true);
  $json = curl_exec($session);
  $json_output =  json_decode($json,true);
  global $serial;
  global $title;
  $title = $json_output['query']['results']['channel']['item']['condition']['temp']."F ".$json_output['query']['results']['channel']['item']['condition']['text'];
  echo "Pull and Display: ".$title."\n";
  echo "Waiting a few seconds to display on Arduino...\n\n";
  sleep(5);
  $serial->deviceOpen();
  $serial->sendMessage($title);
  sleep(240);
  $serial->deviceClose();
  sleep(3);
}

// Display current weather from Yahoo! YQL
function displayWX() {
  global $lastTime;
  global $title;
  
  $currentTime = date('D M j H:i:s Y');
  $currentTime = strtotime($currentTime);
  
  echo "\n\n\n\nCurrent Time: ".$currentTime."\n";
  echo "Last Time: ".$lastTime."\n";
  
  $timeDiff = $currentTime - $lastTime;
  echo "Time Difference: ".$timeDiff."\n";
  
  // 1 hour = 3600
  if ($timeDiff>=3605 || $title == "Nashville WX.") {
    echo "It's been an hour. Pulling WX.\n";
    $lastTime = $currentTime;
    pullWXRSS();
  } else {
    echo "An hour has not passed. Displaying old WX.\n";
    displayRSS();
  }
  displayPubTimeline();
}

function displayPubTimeline() {
  $jsonurl = "http://api.twitter.com/1/statuses/public_timeline.json";
  $json = file_get_contents($jsonurl,0,null,null);
  $json_output = json_decode($json,true);
  foreach ($json_output as $tweet) {
    $name = $tweet['user']['screen_name'];
    $text = $tweet['text'];
    echo "\n\n\n".$name."\n";
    echo $text."\n\n";
    global $serial;
    $serial->deviceOpen();
    $serial->sendMessage("Twitter User:");
    sleep(1.5);
    $serial->sendMessage("@".$name);
    echo "@".$name."\n";
    sleep(2);
    $serial->deviceClose();
    if (strlen($text) > 16) {
      segmentString($text);
    } else {
      $serial->deviceOpen();
      $serial->sendMessage($text);
      $serial->deviceClose();
    }
    sleep(5);
  }
  sleep(5);
  displayTopTrends();
}

function displayTopTrends () {
  $jsonurl = "http://api.twitter.com/1/trends/daily.json";
  $json = file_get_contents($jsonurl,0,null,null);
  $json_output = json_decode($json,true);
  $json_output = current($json_output['trends']);
  
  global $serial;
  
  $serial->deviceOpen();
  $serial->sendMessage("The top 20");
  sleep(1);
  $serial->sendMessage("@Twitter trends:");
  $serial->deviceClose();
  sleep(3);
  echo "\n\nTwitter Trends\n\n";
  
  foreach ($json_output as $trend) {
    $trendname = $trend['name'];
    echo $trendname."\n";
    $serial->deviceOpen();
    $serial->sendMessage("^".$trendname);
    $serial->deviceClose();
    sleep(15);
  }
  $serial->deviceOpen();
  $serial->sendMessage("^");
  sleep(1);
  $serial->deviceClose();
  sleep(3);
  displayWX();
}

displayWX();
?>

PHP Serial Class The PHP script requires an OS X specific serial class to talk with the Arduino board. I’m using this serial class by Rémy Sanchez, modified by Andrew Hutchings. You’ll need to define your serial device (which you can find in /dev). It should look something like /dev/cu.usbserial-A900adK5.

Bash Start-up File In addition, due to the way OS X handles communication over the USB connection (you can read more about it here) we need to run a sleep command to prevent the Arduino from resetting itself each time you send data to it.


#!/bin/bash

nohup sleep 36000 < /dev/cu.usbserial-A900adK5 &

php tweet-arduino.php

That’s it. Pass the Arduino program to your Arduino, change the name of your serial device in the PHP script and these scripts should run right out of the box.

10K Apart Weather App

| Comments

10KWX is a responsive weather app that I submitted to this year’s 10K Apart exercise.

10KWX weather app during the day

The app uses a web browser’s geo location feature to grab the user’s location and then pass that info off to Yahoo!’s weather API for current conditions and forecast.

10KWX Mobile screen shotsThe application is responsive!

To fit within the 10K file size limit, use of images was out of the question. But, what’s a weather forecast without a nice graphical indicator of the weather? Using the HTML5 canvas element and CSS3 animation I was able to render the graphics and the subtle animation of each weather illustration. (Be sure to check in during a thunderstorm.)

Having done quite a bit of animation in Flash and After Effects, CSS3 animation techniques are actually a welcomed way of doing things. I love being able to specify keyframes as percentage points.


@-moz-keyframes rain1 {  
  0% {  
    margin-top: 0px;
    margin-left: 0px;
    opacity: 0;
  }
  20% {
    margin-top: 6px;
    margin-left: -4px;
    opacity: .55;
  }
  80% {
    margin-top: 54px;
    margin-left: -36px;
    opacity: .55;
  }
  100% {  
    margin-top: 60px;
    margin-left: -40px;
    opacity: 0;
  }  
}

One bug(?) I found with CSS3 animation was not being able to span values across multiple percentage breaks. For example, looking at the animation above, I originally didn’t have the top and left margin values written out on the 20% and 80% marks, just the 0% and 100% marks. But, this caused some funky animation to occur, especially on the iOS version of Webkit. Adding top and left margin to the 20% and 80% points solved this problem. The only downside: It requires calculating those margin values at that specific percentage point.

10KWX weather app at night

This project was a lot of fun. Surprisingly time consuming, but that happened to be a good thing.

You can view my 10K Apart project page, the actual 10KWX app, and this enhanced version of 10KWX with a nice font from Google and the ability to enter a zip code as a URL variable. (?z=33186)

Also, Ai->Canvas is a great plugin for converting Adobe Illustrator shapes to a canvas object.

Podcastin’ With Jawgrind

| Comments

I had the opportunity to geek out with Trey and Stephen on the lastest episode of Jawgrind. We had a lot of fun talking about all things web related: VimConf, Skeleton, 10K Apart (I’ll be posting my project soon), bookmarking sites, HTML5 and JavaScript Weekly, Rails 3.1 and much more. Alcohol may have been involved.

Stephen and Trey are much more talented in the world of web dev than I, so it was great to sit-in and learn a thing or two. Give Jawgrind a listen here.

Seedling: A Garden Tracking App

| Comments

Since it’s inception about a year ago, I’ve been working on a new project that has really been a lot of fun. The project is called Seedling and it’s a new web app that will let people track the progress of their backyard gardens.

The app lets you log basic milestones like plant yield, height, photos, etc. With that data, the app automatically creates graphs so you can visualize the progress and performance of your fruit and vegetables (or any other type of plant.)

Seedling

There will also be a social aspect to Seedling. In addition to posting and sharing photos, people can comment on milestones and follow other users.

We’re aiming for a soft launch/preview in time for folks to start tracking some of their fall gardens, which means invite emails will be going out soon.

To sign-up for the first launch or to receive more info as we progress, you can get on our email list at http://seedlinglog.com/ We’ll also be posting more info on the Seedling blog in the coming weeks.

Broadcast iTunes AirPlay Tracks to Campfire Chat With TrackFire

| Comments

Update: TrackFire is now on GitHub for your forking pleasure.

TrackFire is an AppleScript “app” that posts iTunes track titles played over an AirPlay device to a Campfire chat room.

In the office we often play music from iTunes over an Airport Express device for the entire floor to hear. Inevitably, someone asks, “What band is this?” Being the avid users of Campfire that we are, we thought it would be perfect if iTunes AirPlay tracks could automatically have their name and album info posted to a Campfire chatroom. And so TrackFire took form.

The AppleScript runs every few seconds checking for a running version of iTunes, then if iTunes is in play mode and broadcasting to a specific AirPlay device, the track information is posted to Campfire.

For the most part, the script runs without issue, but an error is thrown every once in a while. “Can’t make «class cFIT» id 10219 of «class cUsP» id 10192 of «class cSrc» id 65 of application ‘iTunes’ into the expected type.” (Still tracking down the cause.) Thanks to the folks over at the Apple Discussion Boards for all their help.

Installation
  1. Paste the script into AppleScript Editor replacing the Campfire variables with your own information.
  2. Save as an Application with “Stay Open” checked. Double-click your new script and it will run in the background.
  3. Use iTunes as you normally would and the script does the rest.

(* Begin user defined settings ************)

property campfire_token : "1234567" (* Your Campfire API authentication token *)
property airplay_device : "Apple TV" (* The name of your AirPlay device *)
property campfire_room : "https://yourname.campfirenow.com/room/123456/speak.xml" (* The Campfire room you'd like to post to *)

(* End user defined settings *************)

global current_track, last_track, current_device

on run
	(* init at runtime*)
	set current_track to ""
	set current_device to ""
	set last_track to ""
end run

on idle
	if application "iTunes" is not running then return 10
	tell application "iTunes"
		if (player state is not playing) or (current track is equal to last_track) then return 5
		
		set last_track to current track
		
		set minimized of front browser window to false
		set visible of front browser window to true
		set current_device to my getDevice()
		if current_device as string is not equal to airplay_device & " AirPlay" then return 5
		
		set track_info to my mungeText({name, artist, album} of last_track, "", " :: ")
		set track_info to track_info as string
		set track_info to my mungeText(track_info, "&", "&") -- Replace ampersands
		set track_info to my mungeText(track_info, "\"", """) -- Replace quotation marks
		set track_info to my mungeText(track_info, "'", "'") -- Replace apostrophes
		
		set shellCommand to ("curl -u " & campfire_token & ":X -H 'Content-Type: application/xml' -d 'TextMessage" & track_info & "' " & campfire_room)
		set shellCommand to shellCommand as string
		do shell script shellCommand
		(*display dialog shellCommand*)
		(*log "Posting to Campfire:" & shellCommand*)
		return 5
	end tell
end idle

on getDevice()
	tell application "System Events"
		tell process "iTunes"
			return description of button 8 of window "iTunes"
		end tell
	end tell
end getDevice

on mungeText(itxt, stxt, rtxt)
	set tid to AppleScript's text item delimiters
	if class of itxt is text then
		set AppleScript's text item delimiters to stxt
		set itxt to text items of itxt
	end if
	set AppleScript's text item delimiters to rtxt
	set otxt to itxt as text
	set AppleScript's text item delimiters to tid
	return otxt
end mungeText

Bbbrowser: Fun With the Dribbble API

| Comments

I love thinking up ways to display interesting data that continuously updates. Browsing through the Dribbble API I thought it would be a lot of fun to see everyone’s shots pass through a display as they’re uploaded.

bbbrowser — Fun with Dribbble

bbbrowser hits the Dribbble API every 24 seconds and displays the newest shots in your browser window. I like to leave this window open throughout the day and watch all the new artwork roll in. It’s a great source of inspiration. If your browser supports fullscreen mode, even better.

The layout is tailored to widescreen monitors. The images do stretch as your browser is resized, however, so make sure you find that sweet spot between size and aspect ratio. (I’d hate to see someone’s beautiful typography get all squishy.)

Jump in and start exploring!

Arduino, LCD and Plexiglass

| Comments

I’ve been wanting to build some type of container for my Ardiuno/LCD combination for some time. A couple weekends ago I finally bought some plexiglass and machine screws and made it happen.

The plexiglass is more fragile than I expected. It’s extremely scratch prone and cracks easily with just a slight bit of pressure. Scoring and snapping was actually the easiest part of dealing with the plastic. Drilling holes produced small cracks on more than one occasion.

Eventually I got it right and now the Arduino and its components have a solid base to function on.

Arduino, Plexiglass and LCD screen

Arduino and LCD screen

My iPhoneTracker Map Is Mostly Useless

| Comments

A former Apple employee has created an app called iPhoneTracker that pulls data from your iPhone backups in iTunes and then displays pretty much everywhere you’ve been lately. There’s a huge storm of privacy concern going on as a result.

You can certainly tell what cities someone has been to, but most of the location data is way off. Hundreds of miles off in my case. I can’t imagine anyone finding much use in this as local data is grossly unreliable.

Here’s my map. Larger dots are correct. I have been to Birmingham and Jacksonville, but the smaller dots in places like Northwestern Alabama and the central panhandle of Florida are completely incorrect.

iPhoneTracker Map