Go Down

Topic: Gobetwino Music Server for iTunes (Read 3704 times) previous topic - next topic


Jul 16, 2011, 11:46 pm Last Edit: Sep 10, 2011, 09:52 pm by Jaaptina Reason: 1
Building a music server the hard way having fun with Arduino.
Using Arduino, LCD screen and SC-card reader to start a selected album in iTunes on a pc.

First step is to control iTunes with Arduino. I tried to use Gobetwino but this doesn't always make the right selections. I ended up using AAC-Keys to translate serial data to key presses and Auto Hot Key (AHK) to control iTunes.
The most difficult part was to select the iTunes search field.

Second step is to make al list of albums. In iTunes export a playlist to Excel. In Excel remove all info except artist and album name. Save as csv-file. Rename to txt-file and copy to SD-card. This creates a list for Arduino to read like:

Giant Sand;Selections Circa 1990 - 2000
Joan Baez;Play Me Backwards
Room Eleven;Mmm... Gumbo?
Nina Simone;The Very Best Of Nina Simone
The Walkabouts;Nighttown
Calexico;The Black Light

Third step is to make Arduino scroll up and down this list. To make Arduino read this list all at once is explained in the SD-card examples.
The scolling function is based on line feeds. During setup al line feed positions are stored in an array. Now we know the starting points of all next lines (albums). The ";" is used to display the album title on the 2nd row.
Setup ends with displaying the first album on the LCD screen. I use a Nuelectronics LCD screen with buttons like this:

Use up and down buttons to display the next or previous album on the LCD screen (see next()). Pushing key 0 sends an AHK command to the pc, adds the title of the selected album and starts the first song in iTunes (see start()).

All works fine on my laptop for now:

Next is to set up a second hand pc as iTunes server with digital out (toslink or coax). This I will connect through a dac to my stereo. I'd like to convert an old AKAI digital tuner into the music server by exchanging the screen with the Arduino LCD screen. But this will take some time.

The code works well and certainly fast enough. Improvements and suggestions on how to use a NAS instead of a pc are welcome.

Code: [Select]
#include <SD.h>
#include <PString.h>
#include <LCD4Bit_mod.h>

LCD4Bit_mod lcd = LCD4Bit_mod(2);
int keyarray[5] = {30, 150, 360, 535, 760 }; //see LCD button shield
int NUM_KEYS = 5;
int keypad;
int key = -1;
int oldkey = -1;

const int chipSelect = 10;
File dataFile;
char x;
int myLines[24];  // array of 24 albums based on line feed positions
int n = 0;        // album counter
int pos = 0;      // album position variable
char album[60];   // "artist name;album name" variable
PString myPstring(album, sizeof(album)); // to built a string out of characters

void setup(){
 pinMode(10, OUTPUT);    
 dataFile = SD.open("SAVONDS2.TXT");
 if (dataFile) {  
   while (dataFile.available()) {
     x = dataFile.read();         //characters read one by one
     if (x == 10){                //line feed is 10
       myLines[n] = dataFile.position(); //array of line feed positions
   } }
   myLines[23] = 0;               //change last pos into first album position
   n = 23;
 next();                          //print first album to LCD

void loop(){
 keypad = analogRead(0);          //detect key press
 key = getkey(keypad);
 if (key != oldkey){
   keypad = analogRead(0);  
   key = getkey(keypad);  
   if (key != oldkey){
     oldkey = key;
     if (key >=0){buttons();}    // LCD shield with 5 buttons  

void buttons(){              
 if (key == 0){start();}         // itunes starts displayed album
 if (key == 1){n=n-1; next();}   // display next album
 if (key == 2){n=n+1; next();}   // display previous album
 if (key == 3){Serial.println("\033,hold,alt.s");}  // AHK start/pause itunes
 if (key == 4){Serial.println("\033,hold,alt.s");}

void next(){        //read artist and album name (n) from SD-card
 if(n<0){n=23;}    //scroll from first to last album
 if(n>23){n=0;}    //scroll from last to first album
 pos = myLines[n]; //position of line feed preceding ablum (n)
 myPstring = "";
 dataFile = SD.open("SAVONDS2.TXT");
 if (dataFile) {  
   lcd.cursorTo(1, 0);           //artist name on first row
   dataFile.seek(pos);           //position to read
   int q = 0;  
   while (dataFile.available()) {
     x=dataFile.read();          //read next character 'x'
     q++;                        //character counter
     if (x == ';'){
       lcd.cursorTo(2, 0);       //album name on second row
       q=0;                      //reset character counter for next row
       myPstring.print(' ');}    //space between artist and album
       if (x != 10 && x != 13){
         if(q<17){lcd.print(x);} //send 'x' to LCD screen
         myPstring.print(x);    //add 'x' to album
         dataFile.close();       //stop while loop
       }                         //myPstring contains artist and album name

void start(){
 Serial.println("\033,hold,alt.z"); //AHK open itunes search field
 delay(500);                        //wait for blank search field
 Serial.println(album);             //copy album from myPstring
 delay(1500);                       //wait for itunes
 Serial.println("\033,hold,alt.s"); //itunes start album

int getkey(unsigned int input){
 int k;
 for (k=0; k<NUM_KEYS; k++){
   if (input < keyarray[k]){return k;}
 if (k >= NUM_KEYS){k = -1; return k;}

AHK script:
Code: [Select]
; based on code snippets from the iTunes Anywhere topic
; thanks to daorc for activating the search control
; thanks to brohan for ComObjCreate function

#SingleInstance, Force

itunes := ComObjCreate("iTunes.Application")

ControlSend, ahk_parent,{Tab}, iTunes ahk_class iTunes
ControlSend, ahk_parent,{Tab}, iTunes ahk_class iTunes
DetectHiddenWindows, On


IfWinExist,ahk_class iTunes
  controlclick,Edit1,iTunes ahk_class iTunes ;activate the "search" control
  controlfocus,Edit1,iTunes ahk_class iTunes ;activate the "search" control
  ControlSend, ahk_parent,{Esc}, iTunes ahk_class iTunes


Arduino Mega 2560
Arduino Nano
Arduino Duemilanove
MAC OS Montain Lion
Raspberry PI Model B


Aug 24, 2011, 05:17 pm Last Edit: Sep 29, 2011, 10:19 pm by Jaaptina Reason: 1
Using Gobetwino it is possible to read a file with album names from the computer and start and control iTunes. So the SD-card is not needed anymore.

An old AKAI digital tuner is used as housing. Behind the dial is a 10-turns potentiometer. Turning the dial one of a hundred albums can be selected.


Sep 10, 2011, 10:12 pm Last Edit: Sep 17, 2011, 02:57 pm by Jaaptina Reason: 1
The arduino is placed in the old digital tuner. The buttons are wired. The tuning knob is used te scroll through the albums. Gobetwino is used to read the album names from a file on the pc. The button next to the knob is used to start playing the selected album. Gobetwino sends the artist and album name to the iTunes search box and starts the album. Pressing the button again, without changing an album, functions as pause/start for the current song. The button left of the screen shuts down iTunes.

The pc is an Asus Cube with an digital coax out for the highest sound quality.

Future enhancements:

  • remote control (IR)

  • playlists select function (small lists in favor of one big list)

  • relocate the on/off function of the pc to the tuner

  • move the pc system into the tuner housing

  • move the cd's to the attic


Sep 29, 2011, 10:18 pm Last Edit: Dec 14, 2011, 09:26 pm by Jaaptina Reason: 1
With some heavy tuning of the code the Music Server now functions well. I used a lot off sub/sub routines because Gobetwino seems to be responding quicker that way. On the PC-side Gobetwino can activate shutdown.exe now. The main power button (wired to Arduino) now shuts down the PC. I moved the PC to the cellar and used a long USB cable to connect to the music server. The music is send wirelessly to an Apple Airport Express.

Code: [Select]

#include <PString.h>
#include <LCD4Bit_mod.h>

LCD4Bit_mod lcd = LCD4Bit_mod(2);
int iAnVal = A2; int val; int oldval = 1050;
int pot; int oldpot = -1;
int button1;
int button2; int oldbutton2 = LOW;
int buttonPin1 = 13; int buttonPin2 = 12;
int state = 1;
int n;                                      // number of albums
int p;                                      // album position (2 tot n+1)
int y = 0;                                  // playlists no. 0 to 3
int steps = 80;                             // potmeter instances
char* myPlaylists[4]= {"SAVONDS","STEVIG","DIVERSEN","KLASSIEK"};
char x; char buffer[5]; char album[33];     // "artist name;album name" variable
PString myPstring(album, sizeof(album));    // put characters into a string
boolean newalbum = false;
boolean play = false;

void setup(){
  pinMode(buttonPin1, INPUT);
  lcd.init(); lcd.cursorTo(1, 0);

void starttunes(){                           
  while(!Serial.available()){lcd.printIn("Waiting ..."); delay(3000); readline(1);}
  number();            delay(100);
  tunes();             delay(5000);     

void number(){
  myPstring = "";
    x = Serial.read();                   //read character from Gobetwino
    if(x!=10 && x!=13 && x!=48 && x!=59){
      myPstring.print(x);}}              //add character to string(album)
  n = atoi(album);                       //string to int (first line states the number of albums)
  lcd.cursorTo(2, 0);
  lcd.printIn(itoa((n), buffer, 10));    //display number of albums on LCD

void tunes(){
  Serial.println("#S|TUNES|[]#");        //start iTunes (Gobetwino command)

void loop(){                           
  button1 = digitalRead(buttonPin1);       
  button2 = digitalRead(buttonPin2);         
  if(button2 != oldbutton2){                  //select playlist
    if(button2 == HIGH){selectlist();}
    if(button2 == LOW){selectalbum();}
    oldbutton2 = button2; return;}
  if(button2 == LOW && button1 == HIGH){      //button1 pressed
    if(newalbum == true){start(); return;}    //start selected album
      Serial.println("#S|SENDK|[0& ]#");
      play = !play;
      delay(1000); return;}}
  val = analogRead(iAnVal);                   //read potentiometer
  if(val>oldval-5 && val<oldval+5){delay(100); return;}
  val = analogRead(iAnVal);                 //read potentiometer again
  if(val>oldval-5 && val<oldval+5){delay(100); return;}
  oldval = val;
  pot = map(val, 0, 1023, 0, steps);          //map number of steps
  if(pot != oldpot){
    if(button2 == HIGH){selectlist();}
    if(button2 == LOW){selectalbum();}
    oldpot = pot;}

void selectlist(){
  if(y == 3){y = -1;}
  y = y+1;

void selectalbum(){
  if(pot == steps || pot == 0 || oldpot == -1){
    if(pot == steps){p = n+1;}
    if(pot == 0){p = 2;}
    if(oldpot == -1){p = pot;}}
    if(pot>oldpot){                     //rotate right
      int i = pot-oldpot;
      if(i < 3){p = p+i;}
      else{p = p+5*(pot-oldpot);}}      //skip some albums
    if(pot<oldpot){                     //rotate left
      int i = oldpot-pot;
      if(i < 3){p = p-i;}
      else{p = p-5*(oldpot-pot);}}}     //skip some albums         
  while (p > n+1){p = p-n;}           
  if(p<2){p = n+1;}
  newalbum = true;                      //wait for button1 to start album

void displayAlbum(){                    //state 2; read album p from file
  if(!Serial.available()){lcd.printIn("Time out");return;}
  myPstring = "";
  int q = 0; while (Serial.available() && q < 60){
    x = Serial.read(); q++;             //read character from Gobetwino
    if(x == ';'){                       //artist;album seperator
      lcd.cursorTo(2, 0); q = 0;        //album name on second row
      myPstring.print(' ');}            //reset character counter for next row
    if(x != ';' && x !=10 && x != 13 && x != 48 && q < 17){ //discard line feed etc.
      lcd.print(x);                     //send 'x' to LCD screen
      myPstring.print(x);}}             //add 'x' album

void readline(int r){                    //Gobetwino command
  lcd.clear(); lcd.cursorTo(1, 0);
  int q = 0; while (Serial.available() && q < 200){
    x = Serial.read(); q++;}             //empty serial buffer
  Serial.print(itoa((r), buffer, 10));   //read myPlaylist[y] line p
  Serial.println("]#");                  //from gobetwino-defined file
  q = 0; while(!Serial.available() && q < 200){delay(10); q++;} // wait for serial return

void start(){                            //start selected album
  if(play == true){
    Serial.println("#S|SENDK|[0& ]#");
    play = false; delay(50);}
  Serial.println("#S|SENDK|[0&^i]#"); delay(50);
  Serial.println("#S|SENDK|[0&^i]#"); delay(50);
  Serial.println("#S|SENDK|[0&^i]#"); delay(50);
  Serial.print(album);                   //copy album from myPstring
  Serial.println("]#");                  //to iTunes
  delay(1000);                           //wait for itunes 1800
  Serial.println("#S|SENDK|[0&{TAB}]#"); delay(50);
  Serial.println("#S|SENDK|[0& ]#");
  play = true; newalbum = false;


Impressive job.

I would love to have such a Hifi system, but with the audio output, and that would dig the music data onto a local ethernet samba server.

I already found some equivalent equipments, but all of them are based on TV for the man machine interface.
Blog, DIY Electronics & Stuff - http://www.banson.fr


My little heart gets all warm and tender when i see GoBetwino beeing used for cool projects  :)


My little heart gets all warm and tender when i see GoBetwino beeing used for cool projects  :)

Thanks MikMo!
You have written the only program I could work with that can read files on a PC.

Could not have done it without you!  ;)

Go Up