Saturday, April 14, 2012

Arduino based Pandora Display

picture of device in front of pandora screen
I recently acquired my first Arduino in addition to an LCD shield kit from Adafruit. After I soldered together the kit and went through all the fun demos with the LCD shield, I decided I wanted to try and make something somewhat useful. I thought it would be fun to use it as a current song and artist display for Pandora.

I spent a lot of time online looking for code snippets and ideas. I ended up modifying a Pandora notifier extension for Chrome using a javascript snippet to pass the current song and artist  to an apache server on my computer using the GET method and jquery. From there I used a PHP script to pass all the data via a serial connection. My next goals are to use the buttons that came on the LCD shield to thumbs up and thumbs down songs. All the juicy details are featured below. Feel free to leave feedback and tips or to ask questions in the comments section.


Demonstration:




Arduino Shield:


The display is an RGB LCD shield kit from Adafruit. There's a chip that communicates with the Arduino via I2C. They have a nice library and set of examples that are perfect for a beginner like me. I have very little experience soldering, but I was able to solder this together on my first try.

Arduino Code:


It's not pretty, but it works. I basically just tell the Arduino to sit and listen to the serial port for another song. As the text comes in it looks for slashes to know when to switch to the next line for the artist or to prepare to receive the next color. I thought I might as well take advantage of the RGB aspect of the LCD screen by changing colors after each song. I keep track of the last color in the PHP section. 

The libraries I used

My code:

/*********************

Arduino Pandora Display Project

Waits for song, artist, and color to be sent to it from a serial connection
then scrolls in across the screen

Based on example code for the Adafruit RGB Character LCD Shield and Library

**********************/

// include the library code:
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>

// The shield uses the I2C SCL and SDA pins. On classic Arduinos
// this is Analog 4 and 5 so you can't use those for analogRead() anymore
// However, you can connect other I2C sensors to the I2C bus and share
// the I2C bus.
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();

// These #defines make it easy to set the backlight color
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#define WHITE 0x7

int time2;
int scrollspeed=300;
int nextscroll=0;
uint8_t i=0;

char current[2];
int color = 1;
int input_mode=1;


void setup() {
  // Debugging output
  Serial.begin(9600);
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);
  lcd.print("Waiting for first song...");
  lcd.setBacklight(7);
}


void loop() {
  
  // when characters arrive over the serial port...
  if (Serial.available()) {
    // wait a bit for the entire message to arrive
    delay(100);
    // clear the screen
    lcd.clear();
    // Reset the input mode (only necessary if it didn't resest each time)
    input_mode = 0;
    lcd.setCursor(0,0);
    // read all the available characters
    while (Serial.available() > 0) {
      //Read in the digit
      current[1]=Serial.read();
      //This directs the incoming stuff
      //Listens for a / to know it's for the next input
      if (current[1]=='/') {
        //increment the input type
        input_mode=input_mode+1;
        if (input_mode==2){
          //Moves to the next line for the artist
          lcd.setCursor(0,1);
        }
        if (input_mode==3){
          //Lastly the new color is read in
          color=(Serial.read());
          lcd.setBacklight(color);
        }
      }
      //Writes the character to the display if not a /
      else { 
      lcd.write(current[1]);
      }
    }
  }
    
    //This chunk just scrolls the text
    time2=millis();
    if (time2>=nextscroll) {
    // scroll one position left:
    lcd.scrollDisplayLeft(); 
    // set the next scroll time
    nextscroll=time2+scrollspeed;
        }
  
}

PHP Portion:

Since I didn't know how to make a Chrome extension that could establish a serial connection, I decided to setup an Apache server on my computer and use a PHP script. This works because permission to call other sites can be added to Chrome extensions. I used an example of how to connect to the Arduino via serial connection from PHP to pass all the info over a serial connection. Unfortunately the Arduino resets each time a serial connection is established. Although methods to work around this exist, I decided to just roll with it and not depend on the Arduino storing any variable in between songs. I also have the PHP script store the last color in a text file. You'll need to create a text file named color_storage.txt with a six digit color name inside. I just used a six digit color name (spaces added for short ones like red) for each so that I could read in an exact number of characters each time. Storing the color as text instead of number is silly in hindsight, but it helped me figure it all out.
Here's the code for that section


<?php
ini_set('display_errors', 'On');
//Retrieves the variables from the GET method
$song = $_GET['song'];
$artist = $_GET['artist'];
//Pulls up which color was last sent to the Arduino
//and replaces the value with the next color
$myFile = "color_storage.txt";
$fh = fopen($myFile, 'r') or die("can't open file");
$oldcolor = fread($fh, 6);
fclose($fh);
$fh = fopen($myFile, 'w') or die("can't open file"); //close and reopen file to put the cursor back at the beginning
echo("oldcolor: " . $oldcolor);
switch ($oldcolor){
case "RED ":
$newcolor = "YELLOW";
$colornumb = "3";
break;
case "YELLOW":
$newcolor = "GREEN ";
$colornumb = "2";
break;
case "GREEN ":
$newcolor = "TEAL ";
$colornumb = "6";
break;
case "TEAL ":
$newcolor = "VIOLET";
$colornumb = "5";
break;
case "VIOLET":
$newcolor = "BLUE ";
$colornumb = "4";
break;
case "BLUE ":
$newcolor = "RED ";
$colornumb = "1";
break;
}
fwrite($fh, $newcolor);
fclose($fh);
//Puts together the string to send to the arduino
$arg = "/ " . $song . " / by: " . $artist . " /" . $colornumb;
// Sets up the serial connection
exec("mode com6: BAUD=9600 PARITY=N data=8 stop=1 xon=off");
$fp = fopen("COM6", "w");
//waiting on Arduino to reset
sleep(2);
//Sends the Song to the Arduino via the open serial connection
print "writing " . $arg . "\n";
fwrite($fp, $arg);
//Closes up the serial connection and prints out some text encase you manually call it
print "closing\n";
fclose($fp);
?>

Chrome Extension


I'm not very familiar with javascript so I decided I should just modify an existing extension. I downloaded this Pandora notification extension that already takes care of a lot of the dirty work of getting the info I need out of Pandora. I just added

<SCRIPT src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></SCRIPT>
<SCRIPT>
// This sends the artist and song to the php script to send over serial
$.get("http://localhost/send2arduino.php", { song: unescape(getParam('song')) , artist: unescape(getParam('artist')) })
</SCRIPT>
to the end of the notifications HTML file. I know I could have done this without jquery, but it was easier for a javascript amateur like me to take advantage of how simple it makes it. Once you've downloaded and modified the extension, you can turn on developer mode in the extension menu in Chrome to load an unpacked extension. You could easily modify this extension to pull data from other websites and send it to the Arduino display if you wanted to put your own spin on this project.

Conclusion:

I've only had my Arduino for a week, but I'm already having a great time. It's also forcing me to learn more about stuff like javascript, PHP, and C. I highly recommend it as a hobby. I really love all the information and the large community Arduino has online. I hope this post can give a little bit back to the community and help someone else make something cool.