PodBreakout Project

I am linking up the PodBreakout board (DEV-08295) to the Mbed micrcontroller. The iPod Pinout datasheet can be found here.

PodBreakout
After thinking about what it was I wanted to achieve with the breakout board, I removed a large number of the wires I had soldered on, leaving pins 2, 11 and 30 going to ground, pins 3 and 4 as Audio Out, pins 12 and 13 as the serial wires, pin 21 going to ground via a 500k Ohm reistor and pin 20 going to a 12V supply to charge the iPod.

Initially, I soldered connecting wires onto all of the pins on the Breakout board and then I decided to get a working audio output. From wikipedia, I found that the sleeve of the headphone jack should be connected to ground, the ring of the jack to the right channel (pin 3) and the tip to the left channel (pin 4). I soldered the wires onto some molex headers and touched them to the input of my headphones and they were able to play music, although at full volume, very successfully. At this point, I found a proper headphone socket and used that instead.

source:/iPod/AllModes/doc/Pins.jpg

I then began to look at the commands that I could control the iPod with from the Mbed. I found a website on Apple Accessory protocol which was very useful.

I decided to start a program to show the basic method of communicating directly with the iPod. In order for the communication to take place, I connected the Pod Breakout board up. The tx pin(12) was connected to the rx pin of the Mbed, with the rx pin(13) connected to the tx pin of the Mbed. Pins 11 and 29 were then pinned to ground with pin 21 going to ground via a 500k Ohm resistor. Finally, pins 19 and 20 were given a 12V supply for charging the iPod.

source:/iPod/AllModes/doc/BasicWiring.jpg

This is the basic program which I began with:

#include "mbed.h"

Serial pc(USBTX, USBRX);
Serial mySerial(9, 10);

int main() 
{
mySerial.baud(19200);
    while(1) 
    {
		mySerial.putc(0xFF);
		mySerial.putc(0x55);
		mySerial.putc(0x03);
		mySerial.putc(0x04);
		mySerial.putc(0x00);
		mySerial.putc(0x14);
		mySerial.putc(0xE5);
		wait(0.5);
		while(mySerial.readable())
		{
		pc.printf("%x", mySerial.getc());
		}
	}
}

From consultation with the apple accessory protocol link above, I chose to send to the iPod a simple command asking for the iPod's name. This command is written as '0xFF 0x55 0x03 0x04 0x00 0x14 0xE5' in hexadecimal. To make sure this makes sense, I'll quickly run through what it means and described below. The 0xFF 0x55 bytes are headers that must be placed before every request and are returned before every response. Next comes a checking byte, which is the number of bytes that follow minus one (the checksum byte). Then the mode is stated (0x02 for mode 2, 0x04 for mode 4 etc). Then follows the actual command, always taking up two bytes. If there are any parameters, such as you are asking for a count of items on the iPod, then the parameter must be the type of item you wish to count. If parameters are necessary, they are shown in the apple accessory protocol link above. Finally, at the end of every command, comes the checksum byte, which is 0x100 minus the sum of all the other bytes, except for the header, which is excluded from the checksum. I think that about covers it.

Request Response Structure

By using the structure shown above, I created the command to return the name. By sending it to the iPod, a response was returned via the serial port. Reading the response on Hyperterminal, I was able to see the the iPod's reponse, but it was returned in wingding characters. I changed the baud rate in the program to no avail, but once I asked it to display in hexadecimal using the '%x' in the following line it returned a familiar format.

pc.printf("%x", mySerial.getc());

This then returned a hexadecimal value of '0xFF 0x55 0x64 0x01 0x40 0x14 0xDD' which I was able to work with. This appears initially, in accordance with apple accessory protocol(AAP) to say that the iPod is in Voice Memo mode (mode 0) sending a command '0x40 0x14'. From webpages on the AAP, I have yet to find any command type which is similar. The length and checksum bytes don't appear to make sense.


Success! By buffering the bytes with a 0 (as shown below), I was able to read the bytes as '0xFF 0x55 0x06 0x04 0x00 0x01 0x04 0x00 0x14 0xDD'. From the AAP pages, this reads as a response from the iPod in the Advanced Remote (Mode 4) giving feedback with an error, stating that I have exceeded the limit of whatever I was asking for, for the request '0014'. Although, not entirely successful, I have managed to communicate with the iPod.

#include "mbed.h"

Serial pc(USBTX, USBRX);
Serial mySerial(9, 10);

int main() 
{
mySerial.baud(38400);
pc.printf("\nResponse :\n");

		mySerial.putc(0xFF);
		mySerial.putc(0x55);
		mySerial.putc(0x03);
		mySerial.putc(0x04);
		mySerial.putc(0x00);
		mySerial.putc(0x14);
		mySerial.putc(0xE5);
		wait(0.5);
		while(mySerial.readable())
		{
		pc.printf("0x%02x\n\r", mySerial.getc());
		}
}

At this point, I was unsure as to how to proceed with the advanced remote mode (Mode 4), so I decided to focus my work on the simple remote mode (Mode 2). This was very successful as the problem I had encountered was with the responses from the iPod. Requests tended to be successful. I created a class with the different requests to be sent to the iPod and from testing them all, they were all successful with the iPod Video 5G which I was working with.

iPod Program --- ONLY FOR MODE 2 COMMANDS

Software

Right-click the project to import to, select "Import Library..." and use the following details:

  • Import Library URL: http://mbed.co.uk/projects/cookbook/svn/IDG300/mode2/trunk

View the library

Mode 2 Commands!

Pin Schematic
Schematic (Green: Audio Out, Blue: Serial, Red: 12V, Grey: GND)
#include "mbed.h"
#include "iPod.h"

iPod myiPod(9, 10);

int main() 
{
myiPod.iPodOn();
wait(0.1);  
    //input commands (self-explanatory in header)
}
iPodmode2.bin
Mode 2 Source Code Binary

All of the commands seem to work perfectly in Mode 2. Make sure, however that you always put the function 'myiPod.buttonRel();' after any function apart from the myiPod.iPodOn(); and myiPod.iPodOff(); commands. The commands can be found within the iPod.h file, but for ease of use I have displayed them and their respective commands here: Mode 2 commands


Now that the Mode 2 was functioning, I turned back to mode 4 which had been giving me trouble initially. In an effort to understand how it was working I passed it several requests and 'listened' for the response as detailed earlier. Upon requesting the time and the name from the iPod, I received an error message from the iPod, so communication had been established, but I'm unsure how to send more successful requests at the moment.
Success! I've managed to communicate successfully by adding a pause after switching the iPod to Mode 4, giving it time to switch modes.
As communication was working perfectly, I began to add to the program, adding in Mode 0 and Mode 4 commands. For those commands which give back a value, I wrote the code so as to return this value to the function, therefore it would be able to be read on a pc by means of a 'printf' in the main.cpp.

long iPod::requestTrackLength()
{
		_iPod.putc(0xFF);
		_iPod.putc(0x55);
		_iPod.putc(0x03);
		_iPod.putc(0x04);
		_iPod.putc(0x00);
		_iPod.putc(0x1C);
		_iPod.putc(0xDD);
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		long a=(_iPod.getc())<<24;
		long b=(_iPod.getc())<<16;
		long c=(_iPod.getc())<<8;
		long d=(_iPod.getc());
		long length=a+b+c+d;
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		return (length);
}

A technique I had to adopt, shown above is reading all of the bytes, even after I've obtained the information I required from the repsonse. From testing, I found that some checksum bytes ran into other responses I was reading, therefore I needed to read all transmitted bytes, allowing new bytes to be read for the next function which I used.

As I wanted to create the most simple class, to make it easier to use, I decided to leave out some functions. For example, I removed the 'switch to specific shuffle or repeat mode' functions which took up 6 functions. I did this because there was already a couple of functions in the simpler mode 2 which toggled the repeat or shuffle mode on the iPod. By doing this I was able to cut out superfluous functions and 'tidy' up my code.

As another way of compacting the code, I also combined functions into bigger, more useful functions, for example the 'requestSongInfo' function shown below.

char* iPod::requestSongInfo(int type, int offset)	//Type: Title=0x20  Artist=0x22  Album=0x24
{							//Offset is the relative song number to the song playing
		_iPod.putc(0xFF);			//request position in playlist
		_iPod.putc(0x55);
		_iPod.putc(0x03);
		_iPod.putc(0x04);
		_iPod.putc(0x00);
		_iPod.putc(0x1E);
		_iPod.putc(0xDB);
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		long a=(_iPod.getc())<<24;
		long b=(_iPod.getc())<<16;
		long c=(_iPod.getc())<<8;
		long d=(_iPod.getc());
		_iPod.getc();
		long bigbyte=a+b+c+d;			//song number
		bigbyte+=offset;
		_iPod.putc(0xFF);			//request size of playlist
		_iPod.putc(0x55);
		_iPod.putc(0x03);
		_iPod.putc(0x04);
		_iPod.putc(0x00);
		_iPod.putc(0x35);
		_iPod.putc(0xC4);
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		_iPod.getc();
		long w=(_iPod.getc())<<24;
		long x=(_iPod.getc())<<16;
		long y=(_iPod.getc())<<8;
		long z=(_iPod.getc());
		long max=w+x+y+z;
		_iPod.getc();
		if (bigbyte<0 || bigbyte>max)
		{
		return "Song exceeded playlist size!";
		}
		long _bigbyte=(bigbyte & 0x000000FF);
		char _d=(char)_bigbyte;
		_bigbyte=(bigbyte & 0x0000FF00);
		_bigbyte=(_bigbyte>>8);
		char _c=(char)_bigbyte;
		_bigbyte=(bigbyte & 0x00FF0000);
		_bigbyte=(_bigbyte>>16);
		char _b=(char)_bigbyte;
		_bigbyte=(bigbyte & 0xFF000000);
		_bigbyte=(_bigbyte>>24);
		char _a=(char)_bigbyte;
		_iPod.putc(0xFF);
		_iPod.putc(0x55);
		_iPod.putc(0x07);
		_iPod.putc(0x04);
		_iPod.putc(0x00);
		_iPod.putc(type);
		_iPod.putc(_a);
		_iPod.putc(_b);
		_iPod.putc(_c);
		_iPod.putc(_d);
		long checksum=(0x100)-(_a)-(_b)-(_c)-(_d)-(type)-(0x04)-(0x07);
		_iPod.putc(checksum);
		char header1=_iPod.getc();
		char header2=_iPod.getc();
		if((header1==0xFF) && (header2==0x55))
		{
			char length=_iPod.getc();
			_iPod.getc();
			_iPod.getc();
			_iPod.getc();
			char* value = new char[length-2];
			for (int i = 0; i<length-2; i++)
			{
				value[i]=_iPod.getc();
			}
			return value;
		}
		else return NULL;
}

In this function, it is possible to request the title, artist or album name of any song in relation to the one playing (giving offset the value of -6 would provide the information for the 6th song prior to the one playing). I then re-used two functions I had created earlier to find the position of the current song in the current playlist and also to ask for the size of the current playlist. By knowing that information, I could ensure that any request for information would not ask for a song number which didn't exist (was too high or too low). I then asked for the song information and, by adding the offset to the current song number, the iPod would respond with that song's information as a text string, which was then returned to the function. Although a lot of effort went into that function, it enabled me to remove at least 6 other functions and enabled any song's information to be requested in the entire iPod.

All the other more basic functions I created by using the basic apple protocol linked above and simply passing each request byte to the iPod one at a time using the serial '.putc' function, listening for a reply with '.getc' if a response was anticipated.

Every response from a function I sent back to the pc via the USB lead on the Mbed serially. To look at the returned information, I was using a program called Tera Term.

I had successfully created the class for the iPod, allowing the Mbed to communicate with the iPod.

iPod Program (Hello World)

Software

Right-click the project to import to, select "Import Library..." and use the following details:

  • Import Library URL: http://mbed.co.uk/projects/cookbook/svn/iPod/AllModes/trunk

View the library

Hello World!

Overview of the wiring
Wiring
#include "mbed.h"
#include "iPod.h"

Serial pc(USBTX, USBRX);
iPod myiPod(13, 14);        //tx for iPod, rx for iPod

int main() 
    {
    myiPod.airMode();
    pc.printf("Hello, my name is ");
    pc.printf(myiPod.requestName());
    pc.printf("\n\r");
    }
iPod2noremote.bin
Source Code Binary

After testing all the mode 4 functions I could perceive the class needing, I found all the vital ones to be working. I then began to look at the remote control aspect of the project. I was given a IR Receiver Breakout board from Sparkfun.

IR Receiver Breakout

I tried to link a Panasonic remote up to the Mbed, using this, but the protocol was not widely discussed online and therefore I purchased a universal remote with the hope of linking the receiver and remote via the RC5 remote protocol. This was a lot more successful. By using a Vivanco Sky compatible universal remote I bought from Tesco's, I input several of the Phillips codes into the remote, looking for code changes using theRemote Decoder program in the cookbook. Once this was working using the Phillips 216 code on the remote, I adapted the remote decoder program to create a simpler function for use in the iPod class. From pressing each button, I was able to record the code received for every button press, therefore I created a 'usecode' function to give commands to the iPod based on what code has been recorded.

if (code==3383)		//play/pause button
{
playPause();
buttonRel();
}

In this application of the iPod class, I also added a Mobile LCD display as shown in the cookbook, the product of which is shown below.

Overview of applied class

iPod Infrared Program

Software

Right-click the project to import to, select "Import Library..." and use the following details:

  • Import Library URL: http://mbed.co.uk/projects/cookbook/svn/iPod/Final/trunk

View the library

Infrared and LCD display

Wiring including the LCD display and IR receiver
Wiring
#include "mbed.h"
#include "iPod.h"
#include "MobileLCD.h"

Serial pc(USBTX, USBRX);
iPod myiPod(13, 14, 18);        //tx for iPod, rx for iPod, input from IR receiver
MobileLCD lcd(5, 6, 7, 8, 9);

int main() {
    lcd.background(0x0000FF);
    lcd.cls();
    myiPod.simpleRemoteMode();
    while(1) {
    pc.printf("%d\n\r", myiPod.seekCode());
    myiPod.useCode();
    }
    }
iPod3lcd.bin
Source Code Binary

For those of you who are interested, here is a short video of the remote in action: Final video!

Attachments