The iPod controller that I submitted for the mbed challenge

Dependencies:   mbed Motordriver PID

Committer:
networker
Date:
Wed May 04 15:41:13 2011 +0000
Revision:
0:371773dd3dd1
first publication

Who changed what in which revision?

UserRevisionLine numberNew contents of line
networker 0:371773dd3dd1 1 #include "mbed.h"
networker 0:371773dd3dd1 2 #include "ipodcontrol.h"
networker 0:371773dd3dd1 3
networker 0:371773dd3dd1 4 char* strdup(const char *s) {
networker 0:371773dd3dd1 5 char *r = new char[strlen(s)+1];
networker 0:371773dd3dd1 6 strcpy(r, s);
networker 0:371773dd3dd1 7 return r;
networker 0:371773dd3dd1 8 }
networker 0:371773dd3dd1 9
networker 0:371773dd3dd1 10 /* There are 6 different paths through the ipod menu structure, they all start at the toplevel and end at the song level.
networker 0:371773dd3dd1 11 The longest path has 5 levels and each type has a variable number of entries, except the toplevel which has always 6 entries (paths).
networker 0:371773dd3dd1 12 When going down the hierarchy, each type starts at entry 0 (the top of the item list).
networker 0:371773dd3dd1 13 When going up the hierarchy, each type starts at the entry it was at when going down, this is remembered in array 'last'.
networker 0:371773dd3dd1 14 */
networker 0:371773dd3dd1 15 //enum types { top, playlist, artist, album, genre, song, composer, podcast};
networker 0:371773dd3dd1 16 const char *ipodControl::toplevel[] = {"Playlist","Artist","Album","Genre","Song","Composer"};//do not change order!
networker 0:371773dd3dd1 17 const types ipodControl::paths[][maxdepth] = {
networker 0:371773dd3dd1 18 {top,playlist,song},
networker 0:371773dd3dd1 19 {top,artist,album,song},
networker 0:371773dd3dd1 20 {top,album,song},
networker 0:371773dd3dd1 21 {top,genre,artist,album,song},//path with the maximum depth
networker 0:371773dd3dd1 22 {top,song},
networker 0:371773dd3dd1 23 {top,composer,album,song}
networker 0:371773dd3dd1 24 };
networker 0:371773dd3dd1 25
networker 0:371773dd3dd1 26 ipodControl::ipodControl(ipod& p): pod(p) {
networker 0:371773dd3dd1 27 mode = nav;
networker 0:371773dd3dd1 28 path = 0;
networker 0:371773dd3dd1 29 level = 0;
networker 0:371773dd3dd1 30 items = sizeof(toplevel)/sizeof(char*);
networker 0:371773dd3dd1 31 current = 0;
networker 0:371773dd3dd1 32 OnGetNames = 0;
networker 0:371773dd3dd1 33 OnTrackChange = 0;
networker 0:371773dd3dd1 34 OnTime = 0;
networker 0:371773dd3dd1 35 OnTitle = 0;
networker 0:371773dd3dd1 36 OnAlbum = 0;
networker 0:371773dd3dd1 37 OnArtist = 0;
networker 0:371773dd3dd1 38 OnStatus = 0;
networker 0:371773dd3dd1 39 OnError = 0;
networker 0:371773dd3dd1 40 wrap = false;
networker 0:371773dd3dd1 41 pod.SetMode(4);
networker 0:371773dd3dd1 42 }
networker 0:371773dd3dd1 43
networker 0:371773dd3dd1 44 bool ipodControl::readName() {
networker 0:371773dd3dd1 45 name = 0;
networker 0:371773dd3dd1 46 pod.SendAirCmd(get_ipod_name);
networker 0:371773dd3dd1 47 if (pod.waitForReply())
networker 0:371773dd3dd1 48 pod.release();
networker 0:371773dd3dd1 49 if (pod.getError()) {
networker 0:371773dd3dd1 50 if (OnError)
networker 0:371773dd3dd1 51 OnError(pod.getError(), get_ipod_name);
networker 0:371773dd3dd1 52 return false;
networker 0:371773dd3dd1 53 }
networker 0:371773dd3dd1 54 return true;
networker 0:371773dd3dd1 55 }
networker 0:371773dd3dd1 56
networker 0:371773dd3dd1 57 //the menu structure currently has no <All> items
networker 0:371773dd3dd1 58 void ipodControl::OK(unsigned item) {//when the user presses 'OK' (rotary encoder), we move down the menu hierarchy until we reach the 'song' level, we then switch to playback mode
networker 0:371773dd3dd1 59 if (mode == nav) {
networker 0:371773dd3dd1 60 if (paths[path][level] != song) { //present level is not yet 'song'
networker 0:371773dd3dd1 61 last[level] = item; //push the current position
networker 0:371773dd3dd1 62 if (level>0) //not at the toplevel
networker 0:371773dd3dd1 63 pod.SendAirCmd(select, paths[path][level], item); //'select' the current item
networker 0:371773dd3dd1 64 level++;
networker 0:371773dd3dd1 65 current = 0; //start at beginning (no forward history)
networker 0:371773dd3dd1 66 pod.SendAirCmd(get_count, paths[path][level]); //get the number of subitems
networker 0:371773dd3dd1 67 pod.waitForReply();
networker 0:371773dd3dd1 68 items = pod.Arg1();
networker 0:371773dd3dd1 69 pod.release();
networker 0:371773dd3dd1 70 if (items>0)
networker 0:371773dd3dd1 71 pod.SendAirCmd(get_names, paths[path][level],0U,1U); //display the first subitem
networker 0:371773dd3dd1 72 printf("new level: %s\n", toplevel[paths[path][level]-1]); //diag. display the new level category
networker 0:371773dd3dd1 73 } else { //pressed OK at song level
networker 0:371773dd3dd1 74 mode = playback;
networker 0:371773dd3dd1 75 pod.SendAirCmd(select, paths[path][level], item); //'select' the current item
networker 0:371773dd3dd1 76 pod.SendAirCmd(play_list, item); //execute the current item, start playing
networker 0:371773dd3dd1 77 Update();
networker 0:371773dd3dd1 78 }
networker 0:371773dd3dd1 79 } else //mode>=play
networker 0:371773dd3dd1 80 { //real ipod will cycle through volume, position(elapsed), cover_art and stars
networker 0:371773dd3dd1 81 }
networker 0:371773dd3dd1 82 }
networker 0:371773dd3dd1 83
networker 0:371773dd3dd1 84 void ipodControl::Menu() {//when the user presses 'menu' we move up the menu structure, we enter navigation mode but playback(if active) continues
networker 0:371773dd3dd1 85 if (mode != nav) {
networker 0:371773dd3dd1 86 mode = nav;
networker 0:371773dd3dd1 87 return;
networker 0:371773dd3dd1 88 }
networker 0:371773dd3dd1 89 if (level > 0) {
networker 0:371773dd3dd1 90 level--;
networker 0:371773dd3dd1 91 if (level > 0) { //still not at top level
networker 0:371773dd3dd1 92 pod.SendAirCmd(get_count, paths[path][level]); //get number of items at new level
networker 0:371773dd3dd1 93 pod.waitForReply();
networker 0:371773dd3dd1 94 items = pod.Arg1();
networker 0:371773dd3dd1 95 pod.release();
networker 0:371773dd3dd1 96 if (items>0) {
networker 0:371773dd3dd1 97 pod.SendAirCmd(get_names, paths[path][level],last[level],1U); //display the item at the pushed position, or...
networker 0:371773dd3dd1 98 current = last[level];//pop the last position
networker 0:371773dd3dd1 99 } else {
networker 0:371773dd3dd1 100 current = 0;
networker 0:371773dd3dd1 101 }
networker 0:371773dd3dd1 102 printf("new level: %s\n", toplevel[paths[path][level]-1]); //diag. display the new level category
networker 0:371773dd3dd1 103 } else { //reached toplevel
networker 0:371773dd3dd1 104 items = sizeof(toplevel)/sizeof(char*);
networker 0:371773dd3dd1 105 current = path;
networker 0:371773dd3dd1 106 printf("new level: Music\n"); //diag. display the new level category
networker 0:371773dd3dd1 107 pod.SendAirCmd(select, 1U); //'select' all (entire iPod)
networker 0:371773dd3dd1 108 //pod.SendAirCmd(switch_to_main); //switch to main library
networker 0:371773dd3dd1 109 }
networker 0:371773dd3dd1 110 }
networker 0:371773dd3dd1 111 //else was already at top => do nothing
networker 0:371773dd3dd1 112 }
networker 0:371773dd3dd1 113
networker 0:371773dd3dd1 114 void ipodControl::Right() {
networker 0:371773dd3dd1 115 if (mode == nav) {
networker 0:371773dd3dd1 116 Next();
networker 0:371773dd3dd1 117 if (paths[path][level] == top) //top level
networker 0:371773dd3dd1 118 path = current;
networker 0:371773dd3dd1 119 else //get name of item at new position
networker 0:371773dd3dd1 120 pod.SendAirCmd(get_names, paths[path][level],current,1U);
networker 0:371773dd3dd1 121 } else
networker 0:371773dd3dd1 122 pod.SendAirCmd(command, next);//skip fwd
networker 0:371773dd3dd1 123 }
networker 0:371773dd3dd1 124
networker 0:371773dd3dd1 125 void ipodControl::Left() {
networker 0:371773dd3dd1 126 if (mode == nav) {
networker 0:371773dd3dd1 127 Prev();
networker 0:371773dd3dd1 128 if (paths[path][level] == top) { //top level
networker 0:371773dd3dd1 129 path = current;
networker 0:371773dd3dd1 130 } else {//get name of item at new position
networker 0:371773dd3dd1 131 pod.SendAirCmd(get_names, paths[path][level],current,1U);
networker 0:371773dd3dd1 132 }
networker 0:371773dd3dd1 133 } else
networker 0:371773dd3dd1 134 pod.SendAirCmd(command, prev);//skip back
networker 0:371773dd3dd1 135 }
networker 0:371773dd3dd1 136
networker 0:371773dd3dd1 137 void ipodControl::MoveTo(float pos) {
networker 0:371773dd3dd1 138 newpos = pos;
networker 0:371773dd3dd1 139 switch (mode) {
networker 0:371773dd3dd1 140 case playback:
networker 0:371773dd3dd1 141 if (pos < elapsed) { //reverse
networker 0:371773dd3dd1 142 //newpos = pos;
networker 0:371773dd3dd1 143 mode = fastr;
networker 0:371773dd3dd1 144 printf("moving back to %f sec\n", 0.001*pos);
networker 0:371773dd3dd1 145 pod.SendAirCmd(command, frwd);//assume that polling is active and that the command is accepted
networker 0:371773dd3dd1 146 } else if (pos > elapsed) { //forward
networker 0:371773dd3dd1 147 //newpos = pos;
networker 0:371773dd3dd1 148 mode = fastf;
networker 0:371773dd3dd1 149 printf("moving fwd to %f sec\n", 0.001*pos);
networker 0:371773dd3dd1 150 pod.SendAirCmd(command, ffwd);
networker 0:371773dd3dd1 151 }
networker 0:371773dd3dd1 152 break;
networker 0:371773dd3dd1 153 case fastf:
networker 0:371773dd3dd1 154 //newpos = pos;
networker 0:371773dd3dd1 155 printf("> moving fwd to %f sec\n", 0.001*pos);
networker 0:371773dd3dd1 156 break;
networker 0:371773dd3dd1 157 case fastr:
networker 0:371773dd3dd1 158 //newpos = pos;
networker 0:371773dd3dd1 159 printf("< moving back to %f sec\n", 0.001*pos);
networker 0:371773dd3dd1 160 break;
networker 0:371773dd3dd1 161 case nav:
networker 0:371773dd3dd1 162 printf("N");
networker 0:371773dd3dd1 163 break;
networker 0:371773dd3dd1 164 }
networker 0:371773dd3dd1 165 }
networker 0:371773dd3dd1 166
networker 0:371773dd3dd1 167 void ipodControl::poll() {//this is called as part of the main event loop
networker 0:371773dd3dd1 168 if (pod.ready()) {
networker 0:371773dd3dd1 169 pod.parse();
networker 0:371773dd3dd1 170 switch (pod.cmd()-1) {
networker 0:371773dd3dd1 171 case get_count:
networker 0:371773dd3dd1 172 items = pod.Arg1();
networker 0:371773dd3dd1 173 break;
networker 0:371773dd3dd1 174 case get_names:
networker 0:371773dd3dd1 175 if (OnGetNames)
networker 0:371773dd3dd1 176 OnGetNames(pod.Arg1(), pod.text());
networker 0:371773dd3dd1 177 break;
networker 0:371773dd3dd1 178 case get_position:
networker 0:371773dd3dd1 179 current = pod.Arg1();
networker 0:371773dd3dd1 180 break;
networker 0:371773dd3dd1 181 case polling:
networker 0:371773dd3dd1 182 if (pod.Arg1()==1) {//track change
networker 0:371773dd3dd1 183 current = pod.Arg2();
networker 0:371773dd3dd1 184 mode = playback;
networker 0:371773dd3dd1 185 if (OnTrackChange)
networker 0:371773dd3dd1 186 OnTrackChange(current);
networker 0:371773dd3dd1 187 } else {//elapsed time
networker 0:371773dd3dd1 188 elapsed = pod.Arg2();
networker 0:371773dd3dd1 189 switch (mode) {
networker 0:371773dd3dd1 190 case playback:
networker 0:371773dd3dd1 191 if (OnTime)
networker 0:371773dd3dd1 192 OnTime(elapsed);
networker 0:371773dd3dd1 193 break;
networker 0:371773dd3dd1 194 case fastf:
networker 0:371773dd3dd1 195 printf(" F ");
networker 0:371773dd3dd1 196 if (elapsed >= newpos)
networker 0:371773dd3dd1 197 mode = stopfast;
networker 0:371773dd3dd1 198 break;
networker 0:371773dd3dd1 199 case fastr:
networker 0:371773dd3dd1 200 printf(" R ");
networker 0:371773dd3dd1 201 if (elapsed <= newpos)
networker 0:371773dd3dd1 202 mode = stopfast;
networker 0:371773dd3dd1 203 break;
networker 0:371773dd3dd1 204 case stopfast:
networker 0:371773dd3dd1 205 printf(" S ");
networker 0:371773dd3dd1 206 if (elapsed >= newpos)
networker 0:371773dd3dd1 207 mode = playback;
networker 0:371773dd3dd1 208 break;
networker 0:371773dd3dd1 209 }
networker 0:371773dd3dd1 210 if (mode == stopfast) {
networker 0:371773dd3dd1 211 printf("reached position %f sec\n", 0.001*elapsed);
networker 0:371773dd3dd1 212 pod.SendAirCmd(command, stopf);
networker 0:371773dd3dd1 213 }
networker 0:371773dd3dd1 214 }
networker 0:371773dd3dd1 215 break;
networker 0:371773dd3dd1 216 case get_ipod_name:
networker 0:371773dd3dd1 217 if (name) delete[] name;
networker 0:371773dd3dd1 218 name = strdup(pod.text());
networker 0:371773dd3dd1 219 break;
networker 0:371773dd3dd1 220 case get_title:
networker 0:371773dd3dd1 221 if (OnTitle)
networker 0:371773dd3dd1 222 OnTitle(pod.text());
networker 0:371773dd3dd1 223 break;
networker 0:371773dd3dd1 224 case get_artist:
networker 0:371773dd3dd1 225 if (OnArtist)
networker 0:371773dd3dd1 226 OnArtist(pod.text());
networker 0:371773dd3dd1 227 break;
networker 0:371773dd3dd1 228 case get_album:
networker 0:371773dd3dd1 229 if (OnAlbum)
networker 0:371773dd3dd1 230 OnAlbum(pod.text());
networker 0:371773dd3dd1 231 break;
networker 0:371773dd3dd1 232 case get_time_status: //arg1=tracklength in ms, arg2=elapsed time, arg3=status (0=stop, 1=play, 2=pause)
networker 0:371773dd3dd1 233 tracklength = pod.Arg1();
networker 0:371773dd3dd1 234 elapsed = pod.Arg2();
networker 0:371773dd3dd1 235 status = pod.Arg3();
networker 0:371773dd3dd1 236 if (OnStatus)
networker 0:371773dd3dd1 237 OnStatus(tracklength, elapsed, pod.Arg3());
networker 0:371773dd3dd1 238 break;
networker 0:371773dd3dd1 239 case 0: //ack/error
networker 0:371773dd3dd1 240 if (pod.Arg1()>0 && OnError)
networker 0:371773dd3dd1 241 OnError(pod.Arg1(), pod.Arg2());
networker 0:371773dd3dd1 242 break;
networker 0:371773dd3dd1 243 default:
networker 0:371773dd3dd1 244 printf("Unknown reply from iPod %04X\n", pod.cmd());
networker 0:371773dd3dd1 245 break;
networker 0:371773dd3dd1 246 }
networker 0:371773dd3dd1 247 unsigned reply = pod.cmd();
networker 0:371773dd3dd1 248 pod.release();//reset ready flag and delete the receive buffer, meaning that all returned results should be saved or processed
networker 0:371773dd3dd1 249 updater(reply);
networker 0:371773dd3dd1 250 }
networker 0:371773dd3dd1 251 }
networker 0:371773dd3dd1 252
networker 0:371773dd3dd1 253 void ipodControl::updater(unsigned reply) {
networker 0:371773dd3dd1 254 if (update_state != usIdle)
networker 0:371773dd3dd1 255 printf("\t\tstate=%d, reply = %02X\n", update_state, reply);
networker 0:371773dd3dd1 256 switch (update_state) {
networker 0:371773dd3dd1 257 case usGet_time_status:
networker 0:371773dd3dd1 258 if (reply==get_time_status+1) {
networker 0:371773dd3dd1 259 update_state = usGet_title;
networker 0:371773dd3dd1 260 pod.SendAirCmd(get_title, current);
networker 0:371773dd3dd1 261 printf("updater: getting title\n");
networker 0:371773dd3dd1 262 } else if (reply==polling+1) {//only do this when polling is off
networker 0:371773dd3dd1 263 pod.SendAirCmd(get_time_status); //reissue the same command and stay in the same state
networker 0:371773dd3dd1 264 printf("updater: getting status (again)\n");
networker 0:371773dd3dd1 265 }
networker 0:371773dd3dd1 266 break;
networker 0:371773dd3dd1 267 case usGet_title:
networker 0:371773dd3dd1 268 if (reply==get_title+1) {
networker 0:371773dd3dd1 269 update_state = usGet_artist;
networker 0:371773dd3dd1 270 pod.SendAirCmd(get_artist, current);
networker 0:371773dd3dd1 271 printf("updater: getting artist\n");
networker 0:371773dd3dd1 272 }
networker 0:371773dd3dd1 273 break;
networker 0:371773dd3dd1 274 case usGet_artist:
networker 0:371773dd3dd1 275 if (reply==get_artist+1) {
networker 0:371773dd3dd1 276 update_state = usIdle;
networker 0:371773dd3dd1 277 pod.SendAirCmd(polling, 1);
networker 0:371773dd3dd1 278 printf("updater: going idle\n");
networker 0:371773dd3dd1 279 }
networker 0:371773dd3dd1 280 break;
networker 0:371773dd3dd1 281 }
networker 0:371773dd3dd1 282 }