The iPod controller that I submitted for the mbed challenge

Dependencies:   mbed Motordriver PID

ipod.cpp

Committer:
networker
Date:
2011-05-04
Revision:
0:371773dd3dd1

File content as of revision 0:371773dd3dd1:

#include "mbed.h"
#include "ipod.h"

ipod::ipod(PinName tx, PinName rx): led(*new DigitalOut(DEBUGPIN)) {
    com = new Serial(tx, rx);
    com->baud(19200);//should work upto 57600
    rx_ready = false;
    tx_ready = true;
    state = 0;
    tx_buffer[0] = 0xff;
    tx_buffer[1] = 0x55;
    com->attach(this, &ipod::handlerx);
#ifdef TX_IRQ
    com->attach(this, &ipod::handletx, Serial::TxIrq);
#endif
    lastcommand = -1;
    error = err_success;
    replies = 0;
}

void ipod::handlerx() {//Serial port interrupt handler
    led = 1;
    unsigned short c = com->getc();
//   printf("%d: %02X; ", state, chk);
    switch (state) {
        case 0:
            if (c==0xff) state++;
            break;
        case 1:
            if (c==0x55) state++;
            else state = 0;
            break;
        case 2:
            if (rx_ready) {
                state = 0;
                break;//buffer not free, ignore the message
            }
            length = c;
            chk = c;
            rx_buffer = new char[length];
            state++;
            break;
        case 3:
            if (c > 4) {
                state = 0;
                break;//mode not known or not supported
            }
            mode = c;
            chk += c;
            state++;
            break;
        case 4:
            command =  c<<8;
            chk += c;
            state++;
            break;
        case 5:
            command += c;
            chk += c;
            state++;
            break;
        default:
            chk += c;
            if (state < length+3) {
                rx_buffer[state-6] = c;
                state++;
            } else {
                if (chk==0) {
                    rx_ready = true;
                    replies--;
                    if (command-1 == lastcommand) {//it is a reply to lastcommand
                        //printf("reply to %02X\n", lastcommand);
                        lastcommand = -1;
                    }
                    //printf("ready %02X\n", command);
                } else
                    printf("checksum error %02x state=%d\n", chk, state);
                state = 0;
                break;
            }
    }
    led = 0;
}

void ipod::handletx() {
    if (tx_index < tx_size)
        com->putc(tx_buffer[tx_index++]);
    else
        tx_ready = true;
}

bool ipod::waitForReply() {//busy waits for a reply to the last issued command, all other replies are ignored
    for (int i = 0; i < 10000000; i++) {
        if (ready()) {
            if (lastcommand == -1) {//indicates that a reply to the last issued command has been received
                parse();//parse the last reply
                return true;//return to caller for further processing
            }
            release();//release the buffer and hence ignore the message
        }
    }
    printf("timeout: last received %02X(%d, %d, %d, \"%s\")\n", command, arg1, arg2, arg3, string);
    error = err_timeout;
    return false;
}

#define BIGENDIAN
void ipod::copy(char *b, union conv p) {//copy an UINT32 argument bytewise to the buffer
#ifdef BIGENDIAN
    for (int i = 0; i < 4; i++)
        *(b+3-i) = p.asBytes[i];
#else
    for (int i = 0; i < 4; i++)
        *(b+i) = p.asBytes[i];
#endif
}

void ipod::SendAirCmd(unsigned cmd, unsigned arg1, unsigned arg2, unsigned arg3) {//send an advanced ipod remote command, unused arguments are optional
    union conv par1, par2, par3;
    par1.asInt = arg1;
    par2.asInt = arg2;
    par3.asInt = arg3;
    tx_buffer[3] = 4; //AiR mode
    tx_buffer[4] = 0;
    tx_buffer[5] = cmd & 0xff;
    unsigned expect = 1; //typically expect 1 reply per request
    switch (cmd) {
        case 0x12: //get ipod type
            tx_buffer[2] = 3;
            //expect 2 bytes return
            break;
        case 0x14: //get iPod name
            tx_buffer[2] = 3;
            //expect string return
            break;
        case 0x16: //main lib
            tx_buffer[2] = 3;
            expect = 0;
            break;
        case 0x17: //goto item
            tx_buffer[6] = arg1;
            tx_buffer[2] = 8;
            copy(tx_buffer+7, par2);
            expect = 0;
            break;
        case 0x18: //get count
            tx_buffer[6] = arg1;
            tx_buffer[2] = 4;
            //expect integer return
            break;
        case 0x1A: //get names
            tx_buffer[6] = arg1;
            tx_buffer[2] = 12;
            copy(tx_buffer+7, par2);
            copy(tx_buffer+11, par3);
            //expect many offset,string pairs
            expect = arg3;
            break;
        case 0x1C: //get time/stat
            tx_buffer[2] = 3;
            //expect int,int,byte
            break;
        case 0x1E: //get position
            tx_buffer[2] = 3;
            //expect int
            break;
        case 0x20: //get title
            tx_buffer[2] = 7;
            copy(tx_buffer+6, par1);
            //expect string
            break;
        case 0x22: //get artist
            tx_buffer[2] = 7;
            copy(tx_buffer+6, par1);
            //expect string
            break;
        case 0x24: //get album
            tx_buffer[2] = 7;
            copy(tx_buffer+6, par1);
            //expect string
            break;
        case 0x26: //poll mode
            tx_buffer[6] = arg1;
            tx_buffer[2] = 4;
            //expect many byte,int pairs
            expect = 0; //the number to expect is unknown here, so either stop polling or just insert command anyway
            break;
        case 0x28: //run PL
            tx_buffer[2] = 7;
            copy(tx_buffer+6, par1);
            expect = 0;
            break;
        case 0x29: //command
            tx_buffer[6] = arg1;
            tx_buffer[2] = 4;
            expect = 0;
            break;
        case 0x2C: //get shuffle
            tx_buffer[2] = 3;
            //expect byte
            break;
        case 0x2E: //set shuffle
            tx_buffer[6] = arg1;
            tx_buffer[2] = 4;
            expect = 0;
            break;
        case 0x2F: //get repeat
            tx_buffer[2] = 3;
            //expect byte
            break;
        case 0x31: //set repeat
            tx_buffer[6] = arg1;
            tx_buffer[2] = 4;
            expect = 0;
            break;
        case 0x35: //get nr in PL
            tx_buffer[2] = 3;
            //expect int
            break;
        case 0x37: //goto song
            tx_buffer[2] = 7;
            copy(tx_buffer+6, par1);
            expect = 0;
            break;
        case 0x38: //select
            tx_buffer[6] = arg1;
            tx_buffer[2] = 9;
            copy(tx_buffer+7, par2);
            tx_buffer[11] = 0;//unknown
            break;
        default:
            return;
    }
    tx_size = tx_buffer[2] + 4;
    tx_buffer[tx_size-1] = 0;
    for (int i = 2; i < tx_size-1; i++)
        tx_buffer[tx_size-1] -= tx_buffer[i];//compute checksum
    tx_index = 1;
    replies = expect;
#ifdef TX_IRQ
    while (!tx_ready) /* wait */;
    tx_ready = false;
    com->putc(tx_buffer[0]);//kick-off writing buffer
#else
    write();
#endif
    lastcommand = cmd;
    printf("%02X (%d, %d, %d)\n", cmd, arg1, arg2, arg3);
}

void ipod::guarded_SendAirCmd(unsigned cmd, unsigned arg1, unsigned arg2, unsigned arg3) {//same as SendAirCmd but waits until all previous expected replies have been received
    for (int i = 0; i<1000000; i++)
        if (replies==0) {
            SendAirCmd(cmd, arg1, arg2, arg3);
            return;
        }
    printf("Timeout while waiting for reply to %02X, command %02X cannot be send\n", lastcommand, cmd);
}

void ipod::SetMode(int m) { //char buf[] = {0xff, 0x55, 0x03, 0x00, 0x01, 0x00, 0x00};
    tx_buffer[2] = 3; //length
    tx_buffer[3] = 0; //mode 0, mode switching
    tx_buffer[4] = 1; //cmd high byte
    tx_buffer[5] = m; //cmd low byte
    tx_buffer[6] = 0x100 - 3 - 1 - m;//checksum
    tx_index = 1;
    tx_size = 7;
#ifdef TX_IRQ
    while (!tx_ready) /* wait */;
    tx_ready = false;
    com->putc(tx_buffer[0]);
#else
    write();
#endif
}

unsigned ipod::copy(char* s) {//return a bytewise argument from the buffer as a UINT32
    union conv p;
#ifdef BIGENDIAN
    for (int i = 0; i < 4; i++)
        p.asBytes[3-i] = s[i];
#else
    for (int i = 0; i < 4; i++)
        p.asBytes[i] = s[i];
#endif
    return p.asInt;
}

void ipod::parse() {
    error = err_success;
    switch (mode) {
        case 0: //mode 0 reply
            if (command & 0xFF == 4) {//reply to mode status request
                printf("mode 0 reply = %04X\n", command);
            } else
                printf( "unexpected mode 0 reply\n");
            break;  //assume s.c_str()[4] == 0
        case 4://reply to AiR command
            switch (command) {
                case 0:
                    if (rx_buffer[0]==0x04) {
                        error = err_unknown;
                        printf("command %04X is not understood\n", *(unsigned short*)&rx_buffer[1]);
                    }
                    break;
                case 1:
                    error = (errors)rx_buffer[0];
                    switch (error) {
                        case err_success:
                            //printf("command %04X succeeded\n",*(unsigned short*)&rx_buffer[1]);
                            break;
                        case err_failure:
                            printf("command %04X failed\n",*(unsigned short*)&rx_buffer[1]);
                            break;
                        case err_limit:
                            printf("command %04X had wrong parameter(s)\n",*(unsigned short*)&rx_buffer[1]);
                            break;
                        case err_answer:
                            printf("command %04X is an answer\n",*(unsigned short*)&rx_buffer[1]);
                            break;
                        default:
                            printf("unknown error\n");
                            break;
                    }
                    break;
                    //2 bytes
                case get_ipod_size+1: //ipod type
                    break;
                    //single string
                case get_ipod_name+1:
                case get_title+1:
                case get_artist+1:
                case get_album+1:
                    string = rx_buffer;
                    printf("%04X: %s\n", command, string);
                    break;
                    //number+string
                case get_names+1:
                    string = rx_buffer + 4;
                    arg1 = copy(rx_buffer);
                    printf("%04X: %d %s\n", command, arg1, string);
                    break;
                    //number+number+byte
                case get_time_status+1:
                    arg1 = copy(rx_buffer);
                    arg2 = copy(rx_buffer+4);
                    arg3 = rx_buffer[8];
                    printf("%04X: %d %d %02X\n", command, arg1, arg2, arg3);
                    break;
                    //number
                case get_count+1:
                case get_position+1:
                case get_nr_in_playlist+1:
                    arg1 = copy(rx_buffer);
                    printf("%04X: %d\n", command, arg1);
                    break;
                    //byte + number
                case polling+1:
                    arg1 = rx_buffer[0];
                    arg2 = copy(rx_buffer+1);
                    break;
                    //byte
                case get_shuffle+1:
                case get_repeat+1:
                    arg1 = rx_buffer[0];
                    printf("%04X: %02X\n", command, arg1);
                    break;
                    //10 bytes
                case select+1: //select
                    break;
                default:
                    printf("unsupported reply");
                    break;
            }
            break;
        default:
            printf("unsupported mode\n");
            break;
    }
}