Fridge Gizmo recap

Behold the mighty FridgeGizmo! Er, not on the fridge. Yet.

Here’s a quick recap on the main steps I took in making a self-contained gizmo which displays London weather, global random tweets and Twitter trends on an LCD display. No soldering was involved. It’s really rather hypnotic watching utterly random tweets scroll by on a cool blue LCD display…

Ingredients:

  • RaspberryPi with normal, default bog-standard Raspbian OS – I used a model B with ethernet, but an A would do as I was also using…
  • Edimax USB wifi dongle.
  • Arduino Uno microcontroller.
  • LCD keypad shield for Arduino – though I’ve not used the buttons yet, and any Arduino-friendly display would do.
  • Kindle power supply for Raspberry Pi.
  • Some random old power supply for the Arduino.
  • A USB lead. A really, really short A-B lead or adapter would be better than the one I have.

Method:

  1. My basis was this project: http://www.midnightcheese.com/2011/10/displaying-twitter-and-weather-on-your-arduino-lcd-screen/
    This uses a Mac to send weather and tweets to an Arduino with a different LCD display.
  2. I tweaked the Arduino code for my display etc.
  3. I got my Raspberry Pi connected to the internet over wifi.
  4. I installed the Apache web server and PHP on the Raspberry Pi.
  5. I tweaked the PHP script to show weather in London in centigrade, rather than Nashville in Fahrenheit.
  6. I added a horrible kludge to the bash script that calls the PHP script in order to keep open the serial communication between the Pi and the Arduino.
  7. I got the RaspberryPi to run the script automatically when I turned it on. This was harder than I expected but I got there.

To do list:

  • get the display to scroll twitter trends or split over two lines – at the moment trends are truncated
  • display UK twitter trends rather than global trends
  • weather forecast, not current weather
  • my display can’t cope with accented characters – strip out, replace with equivalent normal ASCII ones or work out how to get display to do it.
  • display the time of my next train into town – this is going to be difficult as our local train data isn’t freely syndicated – well, why would you want to encourage people to use public transport when you can charge people to breathe the air around them? (Little bit of politics, there).
  • get the buttons to do something – switch between tweets, weather, train info?
  • build it into a box and stick it to the fridge or put it on a bookcase, bedside table
  • add a clock?
  • possible heresy: remove the Arduino and get the Raspberry Pi to address the LCD display directly. This must be eminently do-able, but I like my Arduino and the way the LCD shield just plugs into it. I’m not good at soldering.

Raspberry Pi displaying random tweets on LCD display via an Arduino

Source Code

Here’s the code I uploaded to the Arduino, very slightly tweaked from Midnight Cheese: http://www.midnightcheese.com/2011/10/displaying-twitter-and-weather-on-your-arduino-lcd-screen/

#include <LiquidCrystal.h>
#include <WString.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// these figures copied from sample sketch that came with my LCD shield

void setup() {
//  analogWrite(5, 100);    // Set LCD contrast level (0-255) - removed for my LCD shield
  lcd.begin(16, 2);       // Set the LCD's number of rows and columns
  Serial.begin(9600);     // Initialize communication with serial(USB) port.
  lcd.print("FridgeGizmo v1.1");    // Print welcome message to LCD.
  lcd.setCursor(0, 1);              // move cursor to start of 2nd line of display
  lcd.print(">>>fetching data");    // Print 'please wait' 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;
  }
}

This is the tweetwx.sh bash script on the RaspberryPi that runs the tweetwx.php script with the ‘screen’ command kludge at startup:

#!/bin/bash

screen -d -m /dev/ttyACM0 9600

sudo php /var/www/tweetwx.php

This is the PHP code tweetwx.php that fetches weather & twitter data from the internet and formats it and sends it to the Arduino and display:

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

// Weather
$zipcode = "UKXX0085";
$title = "London UK WX.";
// $unit = "c"; - will add unit switch later

// 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."\"%20and%20u%3D\"c\"&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']."c ".$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 == "London UK 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();
?>
Posted in Arduino, computers, hardware, Raspberry Pi, Raspbian | Tagged , , , , | 1 Comment

Making FridgeGizmo run automatically

…or How to Get a Script to Run Automatically at the Auto-login on default Raspberry Pi Raspbian Linux.

I want to get my FridgeGizmo that displays local weather and global tweets on an LCD display to start itself up automatically when I plug it in, with no user intervention. I tinkered around with .profile files, but I could only get them to run when I manually opened a terminal session (by SSH from my laptop, in my case). This is no good. I wanted the Raspberry Pi to run my script to send text to the LCD display (via the Arduino) when the Pi logs itself in to the normal LXDE graphical environment. I don’t want it to run too soon (at boot, for example) because I need to be sure that it’s connected to the internet and that Apache and PHP are running, as they are needed to make FridgeGizmo work.

Here’s how I did it…

I opened a terminal window (SSH in my case, but a root terminal window on the Pi screen should work too). I navigated to the home folder of the default user – pi – by typing
cd /home/pi
at the command line.

I then created a new file called auto.desktop by typing
sudo nano auto.desktop
at the command line.

Into this file I pasted the following text:

[Desktop Entry]
Encoding=UTF-8
Name= Connect
Comment=Checks internet connectivity
Exec=lxterminal --command "/var/www/tweetwx.sh"
Terminal=true

and saved it using ctrl-O, ctrl-X. /var/www/tweetwx.sh is the path to the shell script which calls the PHP file that pulls weather and tweets from the internet and sends them to the Arduino and its LCD display.

I then had to make a directory inside the /home/pi/.config folder to put this file in by typing in the root command window:
cd /home/pi/.config
mkdir autostart

I then moved my auto.desktop file in there by using this command:
cd /home/pi
mv auto.desktop .config/autostart/

I then rebooted the Raspberry Pi, connected to the screenless and keyboardless Arduino, and soon weather and tweets started appearing on the LCD with no human intervention. Yay!

Posted in Arduino, computers, hardware, Raspberry Pi, Raspbian | Tagged , , , , | Leave a comment

Pi shall tweet unto Arduino

DSCF4545

More work to do, but big breakthroughs with FridgeGizmo today. I got the RaspberryPi pulling London weather and random worldwide tweets and trends and displaying them on my LCD display via an Arduino Uno.

I installed Apache 2, the Apache curl module and PHP on the Raspberry Pi using the excellent instructions here: http://fusionstrike.com/2012/installing-apache2-raspberry-pi-debian

I added the PHP Serial class to my Apache directory /var/www/ and used similar files I used on my OS X test version – changing the device name to ttyACM0, which is how an Arduino Uno appears on the Raspberry Pi running Raspbian.

I had to power the Arduino & LCD display shield with another power supply – the screen’s contrast was dim otherwise, suggesting it wasn’t getting enough current from the Raspberry Pi’s USB port alone.

When I ran my bash shell script to fire it off.. not much happened. It pulled the weather ok, then just hung with no error messages. It looked like it might be a similar problem I had in OS X, with serial connections over USB getting dropped. I found that typing

stty -F /dev/ttyACM0 -hupcl

at the RaspberryPi command line before running the script, made it work. Lots more tidying up and tweaking to do, but I have a self-contained RaspPi/Arduino combo that gets weather and tweets over wifi and displays them on an LCD screen. Yay!

UPDATE: having powered it all off and on, I’ve struggled to get this working again without typing screen /dev/ttyACM0 9600 and sending some characters to the Arduino before quitting Screen and running the script. More work needed… :)

UPDATE TO THE UPDATE!

Well, seems like stty -F /dev/ttyACM0 -hupcl was a red herring. According to this article, there are big problems with serial communication between the Raspberry Pi and Arduino. So how did I get it to work? I think the screen command (which I used in my Raspberry Pi/Arduino Morse code project) must do something to force open a connection. I tried getting my bash launching script to run screen but it would just hang and go no further until you manually quit the screen session. I then found you can run screen without starting a new console by using the command screen -d -m. So my bash script that launches the PHP code to pull tweets & weather down from the internet, now looks like this:

#!/bin/bash
screen -d -m /dev/ttyACM0 9600
php tweetwx.php

I know this is a horrible kludge, and I have no idea how or why it works, but it does seem to do the trick. As I recall screen doesn’t come included with Raspbian, I had to install it using sudo apt-get install screen at the Raspberry Pi terminal command line.

Now I just need to get my bash script to run automatically when the Pi boots up and it really will be self-contained, requiring no keyboard, TV or mouse.

Posted in Arduino, computers, hardware, internet, Raspberry Pi, Raspbian | Tagged , , , , , , | Leave a comment

Adding wifi to Raspberry Pi with Edimax EW-7811UN dongle

Tiny USB wifi adaptor for Raspberry Pi
After a lot of huffing and puffing and making things over-complicated, I got my Raspberry Pi on the internet by wifi with the miniscule £8.27 EdimaxEW-7811UN WiFi dongle. Here’s how I set it up. NB: I’m using a Kindle power supply, which seems to provide enough current to run this tiny wifi adaptor plugged straight into the Pi’s USB socket (which will be important for FridgeGizmo). I am also using Raspbian wheezy 2012-08-16 as my OS.

1) I updated my firmware by installing and running rpi-update: https://github.com/Hexxeh/rpi-update – new firmware includes drivers needed by this wifi dongle.

2) I followed these instructions: http://raspberrypi.stackexchange.com/questions/1244/wifi-on-raspberry-pi-raspbmc
specifically the bit where you go to the root terminal and type:

nano /etc/network/interfaces

and add the below text to the end of the file:

auto wlan0
iface wlan0 inet dhcp
wpa-conf /etc/wpa.conf

Then in the terminal type:

nano /etc/wpa.conf

and add the below text replacing NETWORK_SSID with my wifi network name and NETWORK_PASSWORD with my wifi password:

network={
ssid="NETWORK_SSID"
proto=RSN
key_mgmt=WPA-PSK
pairwise=CCMP TKIP
group=CCMP TKIP
psk="NETWORK_PASSWORD"
}

Then at the command line I typed

sudo ifup wlan0

and my wifi dongle lit up blue, and I got on t’internet. Phew!

Posted in computers, Debian, Linux, Raspberry Pi, Raspbian | Tagged , , , , , | 8 Comments

Displaying Yahoo London weather in Celsius on Arduino LCD

London weather! In Celsius!

I’ve cracked it! I have got my proto-fridge-gizmo to display the weather for London in centigrade.

The key is to tweak the YQL query to read:

select * from weather.forecast where
location="UKXX0085" and u="c" &format=json

In PHP you’ll need to escape those spaces, equals sign and quote marks, so you’ll end up with a line of PHP that reads something like:

$yql = "http://query.yahooapis.com/v1/public
/yql?q=select%20*%20from%20weather.forecast%20where
%20location%3D\"".$zipcode."\"%20and
%20u%3D\"c\"&format=json";

$zipcode is a variable that contains the location code, in our case “UKXX0085″ for London. I’m trying to add another variable so you can choose if you want temperatures in centigrade or Fahrenheit.

Next things to do:

  • get this working driven from a Raspberry Pi, rather than a laptop running OS X. This might involve installing the Apache web server and maybe PHP as well. 
  • Make the buttons do something.
  • Source some train info – this is going to be tricky as SE Train info is not publicly syndicated. I probably could show Dublin DART times more easily…
Posted in Arduino | Tagged , , | Leave a comment