├── library.json ├── examples ├── SimpleRemote_with_Bounce_and_wake │ └── SimpleRemote_with_Bounce_and_wake.pde ├── SimpleRemote_nunchuck │ ├── SimpleRemote_nunchuck.pde │ └── nunchuck_funcs.h ├── AdvancedRemote_dump_playlists │ └── AdvancedRemote_dump_playlists.pde ├── SimpleRemote_ethernet │ └── SimpleRemote_ethernet.pde ├── AdvancedRemote_polling │ └── AdvancedRemote_polling.pde └── AdvancedRemote_ethernet │ └── AdvancedRemote_ethernet.pde ├── README ├── SimpleRemote.h ├── keywords.txt ├── iPodSerial.h ├── SimpleRemote.cpp ├── iPodSerial.cpp ├── AdvancedRemote.h └── AdvancedRemote.cpp /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AAP", 3 | "keywords": "aap, apple, iphone, ipod, protocol", 4 | "description": "An Arduino library for controlling iPods (and some features for the iPod app on iPhones and iPod Touches) via the Apple Accessory Protocol (AAP)", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/finsprings/arduinaap.git" 9 | }, 10 | "frameworks": "arduino", 11 | "platforms": "atmelavr" 12 | } 13 | -------------------------------------------------------------------------------- /examples/SimpleRemote_with_Bounce_and_wake/SimpleRemote_with_Bounce_and_wake.pde: -------------------------------------------------------------------------------- 1 | // Simple example of using iPodSerial library to send 2 | // Simple Remote Play command on the press of a button. 3 | // This will alternate between Play and Pause on the iPod. 4 | // 5 | // Uses the Arduino Bounce library for debouncing. 6 | // You can get this from http://arduino.cc/playground/code/Bounce 7 | #include 8 | #include 9 | 10 | const byte BUTTON_PIN = 5; 11 | const unsigned long DEBOUNCE_MS = 20; 12 | 13 | Bounce button(BUTTON_PIN, DEBOUNCE_MS); 14 | SimpleRemote simpleRemote; 15 | 16 | void setup() 17 | { 18 | pinMode(BUTTON_PIN, INPUT); 19 | 20 | // enable pull-up 21 | digitalWrite(BUTTON_PIN, HIGH); 22 | 23 | Serial.begin(iPodSerial::IPOD_SERIAL_RATE); 24 | } 25 | 26 | void loop() 27 | { 28 | simpleRemote.loop(); 29 | 30 | if (button.update()) 31 | { 32 | if (button.read() == LOW) 33 | { 34 | // prod the iPod in case it went to sleep since we last talked to it 35 | // (after which older iPods will stop responding to us otherwise) 36 | simpleRemote.sendiPodOn(); 37 | delay(50); 38 | simpleRemote.sendButtonReleased(); 39 | 40 | simpleRemote.sendPlay(); 41 | } 42 | else 43 | { 44 | simpleRemote.sendButtonReleased(); 45 | } 46 | } 47 | } 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /examples/SimpleRemote_nunchuck/SimpleRemote_nunchuck.pde: -------------------------------------------------------------------------------- 1 | // Example of Simple Remote using a Wii Nunchuck for control 2 | // 3 | // The Nunchuck should be connected to Analog 2,3,4,5 4 | // via a WiiChuck or equivalent - see 5 | // http://todbot.com/blog/2008/02/18/wiichuck-wii-nunchuck-adapter-available/ 6 | // 7 | #include 8 | #include 9 | #include "nunchuck_funcs.h" 10 | 11 | bool is_pressing = false; 12 | 13 | // joy x y readings from nunchuck_print_data: 14 | // center: 124,126 15 | // x range: 22-224 16 | // y range: 31-223 17 | bool joy_is_left() 18 | { 19 | return nunchuck_joyx() < 80; 20 | } 21 | 22 | bool joy_is_right() 23 | { 24 | return nunchuck_joyx() > 180; 25 | } 26 | 27 | bool joy_is_up() 28 | { 29 | return nunchuck_joyy() > 160; 30 | } 31 | 32 | bool joy_is_down() 33 | { 34 | return nunchuck_joyy() < 80; 35 | } 36 | 37 | // accel x y: 38 | // center 132 139 39 | // fwd 141 171 40 | // bwd 133 95 41 | // left 89 130 42 | // right 162 134 43 | bool accel_is_tilted_back() 44 | { 45 | return nunchuck_accely() < 100; 46 | } 47 | 48 | SimpleRemote sr; 49 | 50 | void setup() 51 | { 52 | nunchuck_setpowerpins(); 53 | nunchuck_init(); 54 | Serial.begin(iPodSerial::IPOD_SERIAL_RATE); 55 | } 56 | 57 | void loop() 58 | { 59 | sr.loop(); 60 | nunchuck_get_data(); 61 | 62 | if (nunchuck_zbutton()) 63 | { 64 | sr.sendPlay(); 65 | is_pressing = true; 66 | } 67 | else if (nunchuck_cbutton()) 68 | { 69 | sr.sendOkSelectButton(); 70 | is_pressing = true; 71 | } 72 | else if (joy_is_left()) 73 | { 74 | sr.sendSkipBackward(); 75 | is_pressing = true; 76 | } 77 | else if (joy_is_right()) 78 | { 79 | sr.sendSkipForward(); 80 | is_pressing = true; 81 | } 82 | else if (joy_is_up()) 83 | { 84 | sr.sendScrollUp(); 85 | is_pressing = true; 86 | } 87 | else if (joy_is_down()) 88 | { 89 | sr.sendScrollDown(); 90 | is_pressing = true; 91 | } 92 | else if (accel_is_tilted_back()) 93 | { 94 | sr.sendMenuButton(); 95 | is_pressing = true; 96 | } 97 | else if (is_pressing) 98 | { 99 | sr.sendButtonReleased(); 100 | is_pressing = false; 101 | } 102 | 103 | // we need a little delay to avoid 104 | // swamping the iPod with commands 105 | delay(50); 106 | } 107 | 108 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | iPodSerial Apple Accessory Protocol (AAP) Arduino library. 2 | 3 | This library lets an Arduino interact with an iPod, or some features of the iPod application on the iPhone and iPod Touch*, via the serial AAP protocol through the iPod's dock connector. 4 | 5 | See http://ipodlinux.org/wiki/Apple_Accessory_Protocol for more details of AAP. I used http://nuxx.net/wiki/Apple_Accessory_Protocol during development but it seems to be stale now. The ipodlinux.org one has the correct format of the Advanced Remote polling response, for example, and the nuxx.net version does not. 6 | 7 | The library sends commands via serial to the iPod and listens for responses. If and when responses come back over serial from the iPod, the library parses them and passes the data to callback functions provided by the user of the library. Responses are received asynchronously, and so the calling code is not blocked waiting for the iPod to respond; therefore it can continue to blink lights, scroll a display, poll buttons, or whatever. 8 | 9 | If you have an Arduino Mega you can take advantage of its multiple serial ports to have debugging messages out one serial port and communication with the iPod on another. The library provides setup functions to let you do this. You could probably also use SoftwareSerial for this. 10 | 11 | The library consists of three classes: SimpleRemote, AdvancedRemote and iPodSerial. iPodSerial is a common base class for the other two; it does the low-level protocol stuff to talk to the iPod. 12 | 13 | The SimpleRemote class implements AAP Mode 2, aka iPod Remote, aka Simple Remote. This lets you send commands like play/pause, change the volume, etc, but also still control the iPod via its own interace. This is the mode I used for my in-car remote, the write up for which is at http://davidfindlay.org/weblog/files/2009_09_07_ipod_remote.php. 14 | 15 | The AdvancedRemote class implements AAP Mode 4, aka Advanced Remote. Be aware that in Advanced Remote mode the iPod will display a large checkmark and the message "OK to disconnect"; in this mode you cannot control the iPod via its own interface so you need to do everything from your Arduino sketch. Advanced Remote has more options though, like being able to put the iPod in polling mode, where it will send you back the currently-playing track's elapsed time every 500ms; you could use this to update a display controlled by your Arduino (I'm thinking nixie tubes with the arduinix shield would be cool!). 16 | 17 | NOTE: When connecting your iPod to your Arduino, please double-check your wiring. iPods are expensive and you don't want to break yours by sending it too high a voltage or whatever. You use this library at your own risk etc. 18 | 19 | * On my iPhone 3GS and my wife's iPhone 3G I get the "This accessory is not made to work with iPhone" popup and occasionally the longer error message that asks if you want to put it into Airplane mode. Advanced Mode commands don't work. Simple Remote commands do seem to work fine though. -------------------------------------------------------------------------------- /SimpleRemote.h: -------------------------------------------------------------------------------- 1 | #ifndef SIMPLE_REMOTE 2 | #define SIMPLE_REMOTE 3 | /******************************************************************************* 4 | * Copyright (c) 2009 David Findlay 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * - Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * - Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | ******************************************************************************/ 27 | 28 | #include "iPodSerial.h" 29 | 30 | /** 31 | * Issue Simple Remote (AAP Mode 2) commands. 32 | */ 33 | class SimpleRemote : public iPodSerial 34 | { 35 | public: 36 | /** 37 | * Send this command when the user lets go of a button. 38 | * So, for example, if you want to simulate the user pressing 39 | * Play, you should call sendPlay() when the button becomes 40 | * depressed, then call sendButtonReleasd() when the button 41 | * is let go. The same goes for all the other buttons. 42 | * The time between send() and sendButtonReleased() 43 | * is significant - it can trigger press-and-hold behaviour 44 | * and so on. 45 | */ 46 | void sendButtonReleased(); 47 | 48 | void sendPlay(); 49 | void sendVolPlus(); 50 | void sendVolMinus(); 51 | void sendSkipForward(); 52 | void sendSkipBackward(); 53 | void sendNextAlbum(); 54 | void sendPreviousAlbum(); 55 | void sendStop(); 56 | void sendJustPlay(); 57 | void sendJustPause(); 58 | void sendToggleMute(); 59 | void sendNextPlaylist(); 60 | void sendPreviousPlaylist(); 61 | void sendToggleShuffle(); 62 | void sendToggleRepeat(); 63 | void sendiPodOff(); 64 | void sendiPodOn(); 65 | void sendMenuButton(); 66 | void sendOkSelectButton(); 67 | void sendScrollUp(); 68 | void sendScrollDown(); 69 | }; 70 | 71 | #endif // SIMPLE_REMOTE 72 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For iPodSerial 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | SimpleRemote KEYWORD1 10 | AdvancedRemote KEYWORD1 11 | 12 | ####################################### 13 | # Methods and Functions (KEYWORD2) 14 | ####################################### 15 | 16 | sendButtonReleased KEYWORD2 17 | sendPlay KEYWORD2 18 | sendVolPlus KEYWORD2 19 | sendVolMinus KEYWORD2 20 | sendSkipForward KEYWORD2 21 | sendSkipBackward KEYWORD2 22 | sendNextAlbum KEYWORD2 23 | sendPreviousAlbum KEYWORD2 24 | sendStop KEYWORD2 25 | sendJustPlay KEYWORD2 26 | sendJustPause KEYWORD2 27 | sendToggleMute KEYWORD2 28 | sendNextPlaylist KEYWORD2 29 | sendPreviousPlaylist KEYWORD2 30 | sendToggleShuffle KEYWORD2 31 | sendToggleRepeat KEYWORD2 32 | sendiPodOff KEYWORD2 33 | sendiPodOn KEYWORD2 34 | sendMenuButton KEYWORD2 35 | sendOkSelectButton KEYWORD2 36 | sendScrollUp KEYWORD2 37 | sendScrollDown KEYWORD2 38 | 39 | setFeedbackHandler KEYWORD2 40 | setiPodNameHandler KEYWORD2 41 | setItemCountHandler KEYWORD2 42 | setItemNameHandler KEYWORD2 43 | setTimeAndStatusHandler KEYWORD2 44 | setPlaylistPositionHandler KEYWORD2 45 | setTitleHandler KEYWORD2 46 | setArtistHandler KEYWORD2 47 | setAlbumHandler KEYWORD2 48 | setPollingHandler KEYWORD2 49 | setShuffleModeHandler KEYWORD2 50 | setRepeatModeHandler KEYWORD2 51 | setCurrentPlaylistSongCountHandler KEYWORD2 52 | enable KEYWORD2 53 | disable KEYWORD2 54 | getiPodName KEYWORD2 55 | switchToMainLibraryPlaylist KEYWORD2 56 | switchToItem KEYWORD2 57 | getItemCount KEYWORD2 58 | getItemNames KEYWORD2 59 | getTimeAndStatusInfo KEYWORD2 60 | getPlaylistPosition KEYWORD2 61 | getTitle KEYWORD2 62 | getArtist KEYWORD2 63 | getAlbum KEYWORD2 64 | setPollingMode KEYWORD2 65 | controlPlayback KEYWORD2 66 | getShuffleMode KEYWORD2 67 | setShuffleMode KEYWORD2 68 | getRepeatMode KEYWORD2 69 | setRepeatMode KEYWORD2 70 | getSongCountInCurrentPlaylist KEYWORD2 71 | jumpToSongInCurrentPlaylist KEYWORD2 72 | setDebugPrint KEYWORD2 73 | setLogPrint KEYWORD2 74 | loop KEYWORD2 75 | setSerial KEYWORD2 76 | ####################################### 77 | # Instances (KEYWORD2) 78 | ####################################### 79 | 80 | ####################################### 81 | # Constants (LITERAL1) 82 | ####################################### 83 | 84 | ITEM_PLAYLIST LITERAL1 85 | ITEM_ARTIST LITERAL1 86 | ITEM_ALBUM LITERAL1 87 | ITEM_GENRE LITERAL1 88 | ITEM_SONG LITERAL1 89 | ITEM_COMPOSER LITERAL1 90 | STATUS_STOPPED LITERAL1 91 | STATUS_PLAYING LITERAL1 92 | STATUS_PAUSED LITERAL1 93 | POLLING_START LITERAL1 94 | POLLING_STOP LITERAL1 95 | PLAYBACK_CONTROL_PLAY_PAUSE LITERAL1 96 | PLAYBACK_CONTROL_STOP LITERAL1 97 | PLAYBACK_CONTROL_SKIP_FORWARD LITERAL1 98 | PLAYBACK_CONTROL_SKIP_BACKWARD LITERAL1 99 | PLAYBACK_CONTROL_FAST_FORWARD LITERAL1 100 | PLAYBACK_CONTROL_REVERSE LITERAL1 101 | PLAYBACK_CONTROL_STOP_FF_OR_REV LITERAL1 102 | SHUFFLE_MODE_OFF LITERAL1 103 | SHUFFLE_MODE_SONGS LITERAL1 104 | SHUFFLE_MODE_ALBUMS LITERAL1 105 | REPEAT_MODE_OFF LITERAL1 106 | REPEAT_MODE_ONE_SONG LITERAL1 107 | REPEAT_MODE_ALL_SONGS LITERAL1 108 | FEEDBACK_SUCCESS LITERAL1 109 | FEEDBACK_FAILURE LITERAL1 110 | FEEDBACK_INVALID_PARAM LITERAL1 111 | FEEDBACK_SENT_RESPONSE LITERAL1 112 | CMD_GET_IPOD_NAME LITERAL1 113 | CMD_SWITCH_TO_MAIN_LIBRARY_PLAYLIST LITERAL1 114 | CMD_SWITCH_TO_ITEM LITERAL1 115 | CMD_GET_ITEM_COUNT LITERAL1 116 | CMD_GET_ITEM_NAMES LITERAL1 117 | CMD_GET_TIME_AND_STATUS_INFO LITERAL1 118 | CMD_GET_PLAYLIST_POSITION LITERAL1 119 | CMD_GET_TITLE LITERAL1 120 | CMD_GET_ARTIST LITERAL1 121 | CMD_GET_ALBUM LITERAL1 122 | CMD_POLLING_MODE LITERAL1 123 | CMD_PLAYBACK_CONTROL LITERAL1 124 | CMD_GET_SHUFFLE_MODE LITERAL1 125 | CMD_SET_SHUFFLE_MODE LITERAL1 126 | CMD_GET_REPEAT_MODE LITERAL1 127 | CMD_SET_REPEAT_MODE LITERAL1 128 | CMD_GET_SONG_COUNT_IN_CURRENT_PLAYLIST LITERAL1 129 | CMD_JUMP_TO_SONG_IN_CURRENT_PLAYLIST LITERAL1 130 | -------------------------------------------------------------------------------- /examples/AdvancedRemote_dump_playlists/AdvancedRemote_dump_playlists.pde: -------------------------------------------------------------------------------- 1 | // Example of Advanced Remote (Mode 4) dumping all the playlists on an iPod 2 | // 3 | // If your iPod ends up stuck with the "OK to disconnect" message on its display, 4 | // reset the Arduino. There's a called to AdvancedRemote::disable() in the setup() 5 | // function which should put the iPod back to its normal mode. If that doesn't 6 | // work, or you are unable to reset your Arduino for some reason, resetting the 7 | // iPod will put it back to its normal mode. 8 | 9 | #include 10 | #include 11 | 12 | // This sketch needs to be adapted (change serial port config in setup()) 13 | // to be used on a non-Mega, so check the board here so people notice. 14 | #if !defined(__AVR_ATmega1280__) 15 | #error "This example is for the Mega, because it uses Serial3 for the iPod and Serial for debug messages" 16 | #endif 17 | 18 | const byte BUTTON_PIN = 22; 19 | const unsigned long DEBOUNCE_MS = 50; 20 | 21 | Bounce button(BUTTON_PIN, DEBOUNCE_MS); 22 | AdvancedRemote advancedRemote; 23 | 24 | // we'll ask the iPod for this 25 | unsigned long playlistCount = 0; 26 | 27 | // 28 | // our handler (aka callback) implementations; 29 | // these are what get sent data back from the iPod 30 | // when we make requests of it. 31 | // 32 | 33 | void feedbackHandler(AdvancedRemote::Feedback feedback, byte cmd) 34 | { 35 | Serial.print("got feedback of 0x"); 36 | Serial.print(feedback, HEX); 37 | Serial.print(" for cmd 0x"); 38 | Serial.println(cmd, HEX); 39 | 40 | if (feedback != AdvancedRemote::FEEDBACK_SUCCESS) 41 | { 42 | Serial.println("Giving up as feedback wasn't SUCCESS"); 43 | return; 44 | } 45 | } 46 | 47 | void itemCountHandler(unsigned long count) 48 | { 49 | Serial.print("Playlist Count: "); 50 | Serial.println(count); 51 | 52 | playlistCount = count; 53 | 54 | // that ought to be the count of playlists, since that's what we asked for. 55 | // so, let's start printing out their names 56 | advancedRemote.getItemNames(AdvancedRemote::ITEM_PLAYLIST, 0, playlistCount); 57 | } 58 | 59 | void itemNameHandler(unsigned long offset, const char *name) 60 | { 61 | Serial.print("Playlist "); 62 | Serial.print(offset, DEC); 63 | Serial.print(" is named '"); 64 | Serial.print(name); 65 | Serial.println("'"); 66 | 67 | if (offset == playlistCount - 1) 68 | { 69 | Serial.println("Got last playlist name, coming out of advanced mode"); 70 | advancedRemote.disable(); 71 | } 72 | } 73 | 74 | void setup() 75 | { 76 | pinMode(BUTTON_PIN, INPUT); 77 | 78 | // enable pull-up resistor 79 | digitalWrite(BUTTON_PIN, HIGH); 80 | 81 | Serial.begin(9600); 82 | 83 | // enable debugging 84 | // #define IPOD_SERIAL_DEBUG 85 | 86 | // direct library's log msgs to the default serial port 87 | //advancedRemote.setLogPrint(Serial); 88 | 89 | // direct library's debug msgs to the default serial port 90 | //advancedRemote.setDebugPrint(Serial); 91 | 92 | // use Serial3 (Mega-only) to talk to the iPod 93 | Serial3.begin(iPodSerial::IPOD_SERIAL_RATE); 94 | advancedRemote.setSerial(Serial3); 95 | 96 | // register callback functions for the things we're going to read 97 | advancedRemote.setFeedbackHandler(feedbackHandler); 98 | advancedRemote.setItemCountHandler(itemCountHandler); 99 | advancedRemote.setItemNameHandler(itemNameHandler); 100 | 101 | // start disabled, i.e. in good old Simple Remote mode 102 | advancedRemote.disable(); 103 | } 104 | 105 | void loop() 106 | { 107 | // service iPod serial. this is who will end up 108 | // calling our handler functions when responses 109 | // come back from the iPod 110 | advancedRemote.loop(); 111 | 112 | if (button.update() && (button.read() == LOW)) 113 | { 114 | if (advancedRemote.isCurrentlyEnabled()) 115 | { 116 | advancedRemote.disable(); 117 | } 118 | else 119 | { 120 | advancedRemote.enable(); 121 | 122 | // start our commands - we'll do them in sequence 123 | // as our handlers get called. for example, this 124 | // one will call our Feedback handler when it's done 125 | advancedRemote.getItemCount(AdvancedRemote::ITEM_PLAYLIST); 126 | } 127 | } 128 | } 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /examples/SimpleRemote_ethernet/SimpleRemote_ethernet.pde: -------------------------------------------------------------------------------- 1 | // Example of Simple Remote that exposes an ethernet command interface. 2 | // Look at the clientLoop() function to see what it understands. 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | SimpleRemote sr; 9 | 10 | byte MAC_ADDRESS[] = { 11 | 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 12 | byte IP_ADDRESS[] = { 13 | 192, 168, 13, 2 }; 14 | 15 | // Don't use 23 or telnet client will tend to do telnet negotiation (WILL/WONT/DO/DONT etc) 16 | byte PORT = 80; 17 | EthernetServer server = EthernetServer(PORT); 18 | EthernetClient c(255); 19 | 20 | char buffer[32]; 21 | int len = 0; 22 | 23 | const char *tryAndReadALine() 24 | { 25 | int b = -1; 26 | if (c.available()) 27 | { 28 | b = c.read(); 29 | if ((b != -1) && (b != '\r') && (b != '\n')) 30 | { 31 | buffer[len++] = b; 32 | } 33 | }; 34 | 35 | if (b == '\n') 36 | { 37 | buffer[len] = '\0'; 38 | len = 0; 39 | return buffer; 40 | } 41 | else 42 | { 43 | return NULL; 44 | } 45 | } 46 | 47 | // see if we got a full command from the client, and if so process it 48 | void clientLoop() 49 | { 50 | const char *cmd = tryAndReadALine(); 51 | if (cmd == NULL) 52 | { 53 | // not a complete command yet 54 | return; 55 | } 56 | 57 | long index; 58 | 59 | if (strcmp(cmd, "play") == 0) 60 | { 61 | sr.sendPlay(); 62 | c.println("sent play"); 63 | } 64 | else if (strcmp(cmd, "vol-plus") == 0) 65 | { 66 | sr.sendVolPlus(); 67 | c.println("sent vol plus"); 68 | } 69 | else if (strcmp(cmd, "vol-minus") == 0) 70 | { 71 | sr.sendVolMinus(); 72 | c.println("sent vol minus"); 73 | } 74 | else if (strcmp(cmd, "skip-forward") == 0) 75 | { 76 | sr.sendSkipForward(); 77 | c.println("sent skip forward"); 78 | } 79 | else if (strcmp(cmd, "skip-backward") == 0) 80 | { 81 | sr.sendSkipBackward(); 82 | c.println("sent skip backward"); 83 | } 84 | else if (strcmp(cmd, "next-album") == 0) 85 | { 86 | sr.sendNextAlbum(); 87 | c.println("sent next album"); 88 | } 89 | else if (strcmp(cmd, "previous-album") == 0) 90 | { 91 | sr.sendPreviousAlbum(); 92 | c.println("sent previous album"); 93 | } 94 | else if (strcmp(cmd, "stop") == 0) 95 | { 96 | sr.sendStop(); 97 | c.println("sent stop"); 98 | } 99 | else if (strcmp(cmd, "just-play") == 0) 100 | { 101 | sr.sendJustPlay(); 102 | c.println("sent just play"); 103 | } 104 | else if (strcmp(cmd, "just-pause") == 0) 105 | { 106 | sr.sendJustPause(); 107 | c.println("sent just pause"); 108 | } 109 | else if (strcmp(cmd, "toggle-mute") == 0) 110 | { 111 | sr.sendToggleMute(); 112 | c.println("sent toggle mute"); 113 | } 114 | else if (strcmp(cmd, "next-playlist") == 0) 115 | { 116 | sr.sendNextPlaylist(); 117 | c.println("sent next playlist"); 118 | } 119 | else if (strcmp(cmd, "previous-playlist") == 0) 120 | { 121 | sr.sendPreviousPlaylist(); 122 | c.println("sent previous playlist"); 123 | } 124 | else if (strcmp(cmd, "toggle-shuffle") == 0) 125 | { 126 | sr.sendToggleShuffle(); 127 | c.println("sent toggle shuffle"); 128 | } 129 | else if (strcmp(cmd, "toggle-repeat") == 0) 130 | { 131 | sr.sendToggleRepeat(); 132 | c.println("sent toggle repeat"); 133 | } 134 | else if (strcmp(cmd, "ipod-off") == 0) 135 | { 136 | sr.sendiPodOff(); 137 | c.println("sent ipod off"); 138 | } 139 | else if (strcmp(cmd, "ipod-on") == 0) 140 | { 141 | sr.sendiPodOn(); 142 | c.println("sent ipod on"); 143 | } 144 | else if (strcmp(cmd, "menu-button") == 0) 145 | { 146 | sr.sendMenuButton(); 147 | c.println("sent menu button"); 148 | } 149 | else if (strcmp(cmd, "ok-select-button") == 0) 150 | { 151 | sr.sendOkSelectButton(); 152 | c.println("sent ok select button"); 153 | } 154 | else if (strcmp(cmd, "scroll-up") == 0) 155 | { 156 | sr.sendScrollUp(); 157 | c.println("sent scroll up"); 158 | } 159 | else if (strcmp(cmd, "scroll-down") == 0) 160 | { 161 | sr.sendScrollDown(); 162 | c.println("sent scroll down"); 163 | } 164 | else 165 | { 166 | c.println("?"); 167 | } 168 | 169 | // for now just do one press of each button 170 | sr.sendButtonReleased(); 171 | } 172 | 173 | void setup() 174 | { 175 | Ethernet.begin(MAC_ADDRESS, IP_ADDRESS); 176 | server.begin(); 177 | 178 | Serial.begin(iPodSerial::IPOD_SERIAL_RATE); 179 | } 180 | 181 | void loop() 182 | { 183 | // service iPod 184 | sr.loop(); 185 | 186 | if (c && c.connected()) 187 | { 188 | // service network client 189 | clientLoop(); 190 | } 191 | else 192 | { 193 | // look for a new network client 194 | EthernetClient newClient = server.available(); 195 | if (newClient) 196 | { 197 | if (c) 198 | { 199 | c.stop(); 200 | } 201 | 202 | c = newClient; 203 | len = 0; 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /examples/SimpleRemote_nunchuck/nunchuck_funcs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Nunchuck functions -- Talk to a Wii Nunchuck 3 | * 4 | * This library is from the Bionic Arduino course : 5 | * http://todbot.com/blog/bionicarduino/ 6 | * 7 | * 2007 Tod E. Kurt, http://todbot.com/blog/ 8 | * 9 | * The Wii Nunchuck reading code originally from Windmeadow Labs 10 | * http://www.windmeadow.com/node/42 11 | */ 12 | 13 | #include 14 | 15 | static uint8_t nunchuck_buf[6]; // array to store nunchuck data, 16 | 17 | // Uses port C (analog in) pins as power & ground for Nunchuck 18 | static void nunchuck_setpowerpins() 19 | { 20 | #define pwrpin PORTC3 21 | #define gndpin PORTC2 22 | DDRC |= _BV(pwrpin) | _BV(gndpin); 23 | PORTC &=~ _BV(gndpin); 24 | PORTC |= _BV(pwrpin); 25 | delay(100); // wait for things to stabilize 26 | } 27 | 28 | // initialize the I2C system, join the I2C bus, 29 | // and tell the nunchuck we're talking to it 30 | static void nunchuck_init() 31 | { 32 | Wire.begin(); // join i2c bus as master 33 | Wire.beginTransmission(0x52);// transmit to device 0x52 34 | Wire.write(0x40);// sends memory address 35 | Wire.write(0x00);// sends sent a zero. 36 | Wire.endTransmission();// stop transmitting 37 | } 38 | 39 | // Send a request for data to the nunchuck 40 | // was "send_zero()" 41 | static void nunchuck_send_request() 42 | { 43 | Wire.beginTransmission(0x52);// transmit to device 0x52 44 | Wire.write(0x00);// sends one byte 45 | Wire.endTransmission();// stop transmitting 46 | } 47 | 48 | // Encode data to format that most wiimote drivers except 49 | // only needed if you use one of the regular wiimote drivers 50 | static char nunchuk_decode_byte (char x) 51 | { 52 | x = (x ^ 0x17) + 0x17; 53 | return x; 54 | } 55 | 56 | // Receive data back from the nunchuck, 57 | // returns 1 on successful read. returns 0 on failure 58 | static int nunchuck_get_data() 59 | { 60 | int cnt=0; 61 | Wire.requestFrom (0x52, 6);// request data from nunchuck 62 | while (Wire.available ()) { 63 | // receive byte as an integer 64 | nunchuck_buf[cnt] = nunchuk_decode_byte(Wire.read()); 65 | cnt++; 66 | } 67 | nunchuck_send_request(); // send request for next data payload 68 | // If we recieved the 6 bytes, then go print them 69 | if (cnt >= 5) { 70 | return 1; // success 71 | } 72 | return 0; //failure 73 | } 74 | 75 | // Print the input data we have recieved 76 | // accel data is 10 bits long 77 | // so we read 8 bits, then we have to add 78 | // on the last 2 bits. That is why I 79 | // multiply them by 2 * 2 80 | static void nunchuck_print_data() 81 | { 82 | static int i=0; 83 | int joy_x_axis = nunchuck_buf[0]; 84 | int joy_y_axis = nunchuck_buf[1]; 85 | int accel_x_axis = nunchuck_buf[2]; // * 2 * 2; 86 | int accel_y_axis = nunchuck_buf[3]; // * 2 * 2; 87 | int accel_z_axis = nunchuck_buf[4]; // * 2 * 2; 88 | 89 | int z_button = 0; 90 | int c_button = 0; 91 | 92 | // byte nunchuck_buf[5] contains bits for z and c buttons 93 | // it also contains the least significant bits for the accelerometer data 94 | // so we have to check each bit of byte outbuf[5] 95 | if ((nunchuck_buf[5] >> 0) & 1) 96 | z_button = 1; 97 | if ((nunchuck_buf[5] >> 1) & 1) 98 | c_button = 1; 99 | 100 | if ((nunchuck_buf[5] >> 2) & 1) 101 | accel_x_axis += 2; 102 | if ((nunchuck_buf[5] >> 3) & 1) 103 | accel_x_axis += 1; 104 | 105 | if ((nunchuck_buf[5] >> 4) & 1) 106 | accel_y_axis += 2; 107 | if ((nunchuck_buf[5] >> 5) & 1) 108 | accel_y_axis += 1; 109 | 110 | if ((nunchuck_buf[5] >> 6) & 1) 111 | accel_z_axis += 2; 112 | if ((nunchuck_buf[5] >> 7) & 1) 113 | accel_z_axis += 1; 114 | 115 | Serial.print(i,DEC); 116 | Serial.print("\t"); 117 | 118 | Serial.print("joy:"); 119 | Serial.print(joy_x_axis,DEC); 120 | Serial.print(","); 121 | Serial.print(joy_y_axis, DEC); 122 | Serial.print(" \t"); 123 | 124 | Serial.print("acc:"); 125 | Serial.print(accel_x_axis, DEC); 126 | Serial.print(","); 127 | Serial.print(accel_y_axis, DEC); 128 | Serial.print(","); 129 | Serial.print(accel_z_axis, DEC); 130 | Serial.print("\t"); 131 | 132 | Serial.print("but:"); 133 | Serial.print(z_button, DEC); 134 | Serial.print(","); 135 | Serial.print(c_button, DEC); 136 | 137 | Serial.print("\r\n"); // newline 138 | i++; 139 | } 140 | 141 | // returns zbutton state: 1=pressed, 0=notpressed 142 | static int nunchuck_zbutton() 143 | { 144 | return ((nunchuck_buf[5] >> 0) & 1) ? 0 : 1; // voodoo 145 | } 146 | 147 | // returns zbutton state: 1=pressed, 0=notpressed 148 | static int nunchuck_cbutton() 149 | { 150 | return ((nunchuck_buf[5] >> 1) & 1) ? 0 : 1; // voodoo 151 | } 152 | 153 | // returns value of x-axis joystick 154 | static int nunchuck_joyx() 155 | { 156 | return nunchuck_buf[0]; 157 | } 158 | 159 | // returns value of y-axis joystick 160 | static int nunchuck_joyy() 161 | { 162 | return nunchuck_buf[1]; 163 | } 164 | 165 | // returns value of x-axis accelerometer 166 | static int nunchuck_accelx() 167 | { 168 | return nunchuck_buf[2]; // FIXME: this leaves out 2-bits of the data 169 | } 170 | 171 | // returns value of y-axis accelerometer 172 | static int nunchuck_accely() 173 | { 174 | return nunchuck_buf[3]; // FIXME: this leaves out 2-bits of the data 175 | } 176 | 177 | // returns value of z-axis accelerometer 178 | static int nunchuck_accelz() 179 | { 180 | return nunchuck_buf[4]; // FIXME: this leaves out 2-bits of the data 181 | } 182 | -------------------------------------------------------------------------------- /iPodSerial.h: -------------------------------------------------------------------------------- 1 | #ifndef IPOD_SERIAL 2 | #define IPOD_SERIAL 3 | /******************************************************************************* 4 | * Copyright (c) 2009 David Findlay 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * - Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * - Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | ******************************************************************************/ 27 | 28 | #if defined(ARDUINO) && ARDUINO >= 100 29 | #include "Arduino.h" 30 | #else 31 | #include "WProgram.h" 32 | #endif 33 | 34 | /** 35 | * Helper macro for figuring out the length of command byte arrays. 36 | */ 37 | #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0])) 38 | 39 | // Uncomment the line below if you want to be able to use 40 | // setLogPrint and setDebugPrint 41 | //#define IPOD_SERIAL_DEBUG 42 | 43 | class iPodSerial 44 | { 45 | public: // attributes 46 | static const int IPOD_SERIAL_RATE = 19200; 47 | 48 | public: 49 | iPodSerial(); 50 | 51 | /** 52 | * Checks for data coming in from the iPod and processes it if there is any. 53 | * The library handles partial messages coming in from the iPod so it 54 | * will buffer those chunks until it gets a complete message. Once a complete 55 | * message (that the library understands) has been received it will process 56 | * that message in here and call any callbacks that are applicable to that 57 | * message and that have been configured. 58 | */ 59 | void loop(); 60 | 61 | /** 62 | * Sets the serial port that the library will use to communicate with the iPod. 63 | * This defaults to "Serial", i.e. the normal serial port. 64 | */ 65 | void setSerial(Stream &newiPodSerial); 66 | 67 | #if defined(IPOD_SERIAL_DEBUG) 68 | /** 69 | * Sets the Print object to which debug messages will be directed. 70 | * By default there isn't one and so debug messages are off. 71 | */ 72 | void setDebugPrint(Print &newDebugSerial); 73 | 74 | /** 75 | * Sets the Print object to which log messages will be directed. 76 | * By default there isn't one and so log messages are off. 77 | */ 78 | void setLogPrint(Print &newLogSerial); 79 | #endif 80 | 81 | protected: // attributes 82 | static const byte MODE_SWITCHING_MODE = 0x00; 83 | static const byte SIMPLE_REMOTE_MODE = 0x02; 84 | static const byte ADVANCED_REMOTE_MODE = 0x04; 85 | 86 | byte dataSize; 87 | byte dataBuffer[128]; // TODO: Why did I pick 128? 88 | #if defined(IPOD_SERIAL_DEBUG) 89 | Print *pDebugPrint; 90 | Print *pLogPrint; 91 | #endif 92 | 93 | protected: // methods 94 | #if defined(IPOD_SERIAL_DEBUG) 95 | /** 96 | * convenience function to print out a debug message 97 | */ 98 | void log(const char *); 99 | 100 | void dumpReceive(); 101 | #endif 102 | 103 | /* 104 | * Different flavours of command-sending method to keep 105 | * it simple for the other classes. They're a bit silly, 106 | * and you wouldn't want many more than there are, but they 107 | * suffice for the simple and advanced remotes. 108 | */ 109 | void sendCommandWithLength(size_t length, const byte *pData); 110 | void sendCommand( 111 | byte mode, 112 | byte cmdByte1, 113 | byte cmdByte2); 114 | void sendCommandWithOneByteParam( 115 | byte mode, 116 | byte cmdByte1, 117 | byte cmdByte2, 118 | byte param); 119 | void sendCommandWithOneNumberParam( 120 | byte mode, 121 | byte cmdByte1, 122 | byte cmdByte2, 123 | unsigned long param); 124 | void sendCommandWithOneByteAndOneNumberParam( 125 | byte mode, 126 | byte cmdByte1, 127 | byte cmdByte2, 128 | byte param1, 129 | unsigned long param2); 130 | void sendCommandWithOneByteAndTwoNumberParams( 131 | byte mode, 132 | byte cmdByte1, 133 | byte cmdByte2, 134 | byte param1, 135 | unsigned long param2, 136 | unsigned long param3); 137 | 138 | private: // attributes 139 | static const byte HEADER1 = 0xFF; 140 | static const byte HEADER2 = 0x55; 141 | 142 | enum ReceiveState 143 | { 144 | WAITING_FOR_HEADER1 = 0, 145 | WAITING_FOR_HEADER2, 146 | WAITING_FOR_LENGTH, 147 | WAITING_FOR_DATA, 148 | WAITING_FOR_CHECKSUM 149 | }; 150 | ReceiveState receiveState; 151 | byte *pData; 152 | byte checksum; 153 | 154 | Stream *pSerial; 155 | 156 | private: // methods 157 | void sendHeader(); 158 | void sendLength(size_t length); 159 | void sendBytes(size_t length, const byte *pData); 160 | void sendByte(byte b); 161 | void sendNumber(unsigned long n); 162 | void sendChecksum(); 163 | bool validChecksum(const byte actual); 164 | void processResponse(); 165 | 166 | virtual void processData(); 167 | }; 168 | 169 | #endif // IPOD_SERIAL 170 | -------------------------------------------------------------------------------- /examples/AdvancedRemote_polling/AdvancedRemote_polling.pde: -------------------------------------------------------------------------------- 1 | // Example of Advanced Remote (Mode 4) that picks a track, starts it playing 2 | // then goes into polling mode, where the iPod sends back elapsed time 3 | // information every 500ms. 4 | // 5 | // If your iPod ends up stuck with the "OK to disconnect" message on its display, 6 | // reset the Arduino. There's a called to AdvancedRemote::disable() in the setup() 7 | // function which should put the iPod back to its normal mode. If that doesn't 8 | // work, or you are unable to reset your Arduino for some reason, resetting the 9 | // iPod will put it back to its normal mode. 10 | 11 | #include 12 | #include 13 | 14 | // This sketch needs to be adapted (change serial port config in setup()) 15 | // to be used on a non-Mega, so check the board here so people notice. 16 | #if !defined(__AVR_ATmega1280__) 17 | #error "This example is for the Mega, because it uses Serial3 for the iPod and Serial for debug messages" 18 | #endif 19 | 20 | const byte BUTTON_PIN = 22; 21 | const unsigned long DEBOUNCE_MS = 50; 22 | 23 | Bounce button(BUTTON_PIN, DEBOUNCE_MS); 24 | AdvancedRemote advancedRemote; 25 | 26 | unsigned long chosenSongIndexIntoPlaylist; 27 | 28 | // 29 | // our handler (aka callback) implementations; 30 | // these are what get sent data back from the iPod 31 | // when we make requests of it. 32 | // 33 | 34 | void feedbackHandler(AdvancedRemote::Feedback feedback, byte cmd) 35 | { 36 | Serial.print("got feedback of 0x"); 37 | Serial.print(feedback, HEX); 38 | Serial.print(" for cmd 0x"); 39 | Serial.println(cmd, HEX); 40 | 41 | if (feedback != AdvancedRemote::FEEDBACK_SUCCESS) 42 | { 43 | Serial.println("Giving up as feedback wasn't SUCCESS"); 44 | return; 45 | } 46 | 47 | switch (cmd) 48 | { 49 | case AdvancedRemote::CMD_SWITCH_TO_ITEM: 50 | Serial.println("(Presumably) now at the playlist zero (the main one)"); 51 | Serial.println("Asking for song count"); 52 | advancedRemote.getSongCountInCurrentPlaylist(); 53 | // wait for our song count handler to get called now 54 | break; 55 | 56 | case AdvancedRemote::CMD_JUMP_TO_SONG_IN_CURRENT_PLAYLIST: 57 | Serial.println("Jumped to our song"); 58 | Serial.println("Asking for title"); 59 | advancedRemote.getTitle(chosenSongIndexIntoPlaylist); 60 | // wait for title handler to get called now 61 | break; 62 | 63 | case AdvancedRemote::CMD_PLAYBACK_CONTROL: 64 | Serial.println("playback control complete"); 65 | break; 66 | 67 | case AdvancedRemote::CMD_POLLING_MODE: 68 | Serial.println("polling mode change complete"); 69 | // from experimentation it starts playing automatically 70 | break; 71 | 72 | default: 73 | Serial.println("got feedback we didn't expect"); 74 | break; 75 | } 76 | } 77 | 78 | void titleHandler(const char *name) 79 | { 80 | Serial.print("Title: "); 81 | Serial.println(name); 82 | 83 | Serial.println("Asking for artist"); 84 | advancedRemote.getArtist(chosenSongIndexIntoPlaylist); 85 | // wait for artist handler to get called 86 | } 87 | 88 | void artistHandler(const char *name) 89 | { 90 | Serial.print("Artist: "); 91 | Serial.println(name); 92 | 93 | Serial.println("Asking for album"); 94 | advancedRemote.getAlbum(chosenSongIndexIntoPlaylist); 95 | // wait for album handler to get called 96 | } 97 | 98 | void albumHandler(const char *name) 99 | { 100 | Serial.print("Album: "); 101 | Serial.println(name); 102 | 103 | Serial.println("Turning on polling (which also seems to make it start playing"); 104 | advancedRemote.setPollingMode(AdvancedRemote::POLLING_START); 105 | Serial.println("The button will let you do play/pause now. Reset Arduino to go back to normal mode"); 106 | } 107 | 108 | void currentPlaylistSongCountHandler(unsigned long count) 109 | { 110 | Serial.print("Current playlist song count is "); 111 | Serial.println(count, DEC); 112 | 113 | // pick any old song for test porpoises 114 | chosenSongIndexIntoPlaylist = count / 2; 115 | 116 | Serial.print("Jumping to song at index "); 117 | Serial.println(chosenSongIndexIntoPlaylist, DEC); 118 | advancedRemote.jumpToSongInCurrentPlaylist(chosenSongIndexIntoPlaylist); 119 | } 120 | 121 | void pollingHandler(AdvancedRemote::PollingCommand command, 122 | unsigned long playlistPositionOrelapsedTimeMs) 123 | { 124 | Serial.print("Poll: "); 125 | 126 | switch (command) 127 | { 128 | case AdvancedRemote::POLLING_TRACK_CHANGE: 129 | Serial.print("track change to: "); 130 | Serial.println(playlistPositionOrelapsedTimeMs, DEC); 131 | break; 132 | 133 | case AdvancedRemote::POLLING_ELAPSED_TIME: 134 | Serial.print("elapsed time: "); 135 | printTime(playlistPositionOrelapsedTimeMs); 136 | break; 137 | 138 | default: 139 | Serial.print("unknown: "); 140 | Serial.println(playlistPositionOrelapsedTimeMs, DEC); 141 | break; 142 | } 143 | } 144 | 145 | void printTime(const unsigned long ms) 146 | { 147 | const unsigned long totalSecs = ms / 1000; 148 | const unsigned int mins = totalSecs / 60; 149 | const unsigned int partialSecs = totalSecs % 60; 150 | 151 | Serial.print(mins, DEC); 152 | Serial.print("m "); 153 | Serial.print(partialSecs, DEC); 154 | Serial.println("s"); 155 | } 156 | 157 | void setup() 158 | { 159 | pinMode(BUTTON_PIN, INPUT); 160 | 161 | // enable pull-up resistor 162 | digitalWrite(BUTTON_PIN, HIGH); 163 | 164 | Serial.begin(9600); 165 | 166 | // enable debugging 167 | // #define IPOD_SERIAL_DEBUG 168 | // direct library's log msgs to the default serial port 169 | //advancedRemote.setLogPrint(Serial); 170 | // direct library's debug msgs to the default serial port 171 | //advancedRemote.setDebugPrint(Serial); 172 | 173 | // use Serial3 (Mega-only) to talk to the iPod 174 | Serial3.begin(iPodSerial::IPOD_SERIAL_RATE); 175 | advancedRemote.setSerial(Serial3); 176 | 177 | // register callback functions for the things we're going to read 178 | advancedRemote.setFeedbackHandler(feedbackHandler); 179 | advancedRemote.setTitleHandler(titleHandler); 180 | advancedRemote.setArtistHandler(artistHandler); 181 | advancedRemote.setAlbumHandler(albumHandler); 182 | advancedRemote.setPollingHandler(pollingHandler); 183 | advancedRemote.setCurrentPlaylistSongCountHandler(currentPlaylistSongCountHandler); 184 | 185 | // start in simple remote mode 186 | advancedRemote.disable(); 187 | } 188 | 189 | void loop() 190 | { 191 | // service iPod serial. this is who will end up 192 | // calling our handler functions when responses 193 | // come back from the iPod 194 | advancedRemote.loop(); 195 | 196 | // use the button as a toggle 197 | if (button.update() && (button.read() == LOW)) 198 | { 199 | if (advancedRemote.isCurrentlyEnabled()) 200 | { 201 | //advancedRemote.disable(); 202 | advancedRemote.controlPlayback(AdvancedRemote::PLAYBACK_CONTROL_PLAY_PAUSE); 203 | } 204 | else 205 | { 206 | advancedRemote.enable(); 207 | 208 | // start our commands - we'll do them in sequence 209 | // as our handlers get called. for example, this 210 | // one will call our Feedback handler when it's done 211 | advancedRemote.switchToItem(AdvancedRemote::ITEM_PLAYLIST, 0); 212 | } 213 | } 214 | } 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /SimpleRemote.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009 David Findlay 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | ******************************************************************************/ 25 | #include "SimpleRemote.h" 26 | 27 | void SimpleRemote::sendButtonReleased() 28 | { 29 | static const byte button_released[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00}; 30 | 31 | #if defined(IPOD_SERIAL_DEBUG) 32 | log("Sending button_released"); 33 | #endif 34 | sendCommandWithLength(ARRAY_LEN(button_released), button_released); 35 | } 36 | 37 | void SimpleRemote::sendPlay() 38 | { 39 | static const byte play[] = {SIMPLE_REMOTE_MODE, 0x00, 0x01}; 40 | 41 | #if defined(IPOD_SERIAL_DEBUG) 42 | log("Sending play"); 43 | #endif 44 | sendCommandWithLength(ARRAY_LEN(play), play); 45 | } 46 | 47 | void SimpleRemote::sendVolPlus() 48 | { 49 | static const byte vol_plus[] = {SIMPLE_REMOTE_MODE, 0x00, 0x02}; 50 | 51 | #if defined(IPOD_SERIAL_DEBUG) 52 | log("Sending vol_plus"); 53 | #endif 54 | sendCommandWithLength(ARRAY_LEN(vol_plus), vol_plus); 55 | } 56 | 57 | void SimpleRemote::sendVolMinus() 58 | { 59 | static const byte vol_minus[] = {SIMPLE_REMOTE_MODE, 0x00, 0x04}; 60 | 61 | #if defined(IPOD_SERIAL_DEBUG) 62 | log("Sending vol_minus"); 63 | #endif 64 | sendCommandWithLength(ARRAY_LEN(vol_minus), vol_minus); 65 | } 66 | 67 | void SimpleRemote::sendSkipForward() 68 | { 69 | static const byte skip_forward[] = {SIMPLE_REMOTE_MODE, 0x00, 0x08}; 70 | 71 | #if defined(IPOD_SERIAL_DEBUG) 72 | log("Sending skip_forward"); 73 | #endif 74 | sendCommandWithLength(ARRAY_LEN(skip_forward), skip_forward); 75 | } 76 | 77 | void SimpleRemote::sendSkipBackward() 78 | { 79 | static const byte skip_backward[] = {SIMPLE_REMOTE_MODE, 0x00, 0x10}; 80 | 81 | #if defined(IPOD_SERIAL_DEBUG) 82 | log("Sending skip_backward"); 83 | #endif 84 | sendCommandWithLength(ARRAY_LEN(skip_backward), skip_backward); 85 | } 86 | 87 | void SimpleRemote::sendNextAlbum() 88 | { 89 | static const byte next_album[] = {SIMPLE_REMOTE_MODE, 0x00, 0x20}; 90 | 91 | #if defined(IPOD_SERIAL_DEBUG) 92 | log("Sending next_album"); 93 | #endif 94 | sendCommandWithLength(ARRAY_LEN(next_album), next_album); 95 | } 96 | 97 | void SimpleRemote::sendPreviousAlbum() 98 | { 99 | static const byte previous_album[] = {SIMPLE_REMOTE_MODE, 0x00, 0x40}; 100 | 101 | #if defined(IPOD_SERIAL_DEBUG) 102 | log("Sending previous_album"); 103 | #endif 104 | sendCommandWithLength(ARRAY_LEN(previous_album), previous_album); 105 | } 106 | 107 | void SimpleRemote::sendStop() 108 | { 109 | static const byte stop[] = {SIMPLE_REMOTE_MODE, 0x00, 0x80}; 110 | 111 | #if defined(IPOD_SERIAL_DEBUG) 112 | log("Sending stop"); 113 | #endif 114 | sendCommandWithLength(ARRAY_LEN(stop), stop); 115 | } 116 | 117 | void SimpleRemote::sendJustPlay() 118 | { 119 | static const byte just_play[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x01}; 120 | 121 | #if defined(IPOD_SERIAL_DEBUG) 122 | log("Sending just_play"); 123 | #endif 124 | sendCommandWithLength(ARRAY_LEN(just_play), just_play); 125 | } 126 | 127 | void SimpleRemote::sendJustPause() 128 | { 129 | static const byte just_pause[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x02}; 130 | 131 | #if defined(IPOD_SERIAL_DEBUG) 132 | log("Sending just_pause"); 133 | #endif 134 | sendCommandWithLength(ARRAY_LEN(just_pause), just_pause); 135 | } 136 | 137 | void SimpleRemote::sendToggleMute() 138 | { 139 | static const byte toggle_mute[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x04}; 140 | 141 | #if defined(IPOD_SERIAL_DEBUG) 142 | log("Sending toggle_mute"); 143 | #endif 144 | sendCommandWithLength(ARRAY_LEN(toggle_mute), toggle_mute); 145 | } 146 | 147 | void SimpleRemote::sendNextPlaylist() 148 | { 149 | static const byte next_playlist[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x20}; 150 | 151 | #if defined(IPOD_SERIAL_DEBUG) 152 | log("Sending next_playlist"); 153 | #endif 154 | sendCommandWithLength(ARRAY_LEN(next_playlist), next_playlist); 155 | } 156 | 157 | void SimpleRemote::sendPreviousPlaylist() 158 | { 159 | static const byte previous_playlist[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x40}; 160 | 161 | #if defined(IPOD_SERIAL_DEBUG) 162 | log("Sending previous_playlist"); 163 | #endif 164 | sendCommandWithLength(ARRAY_LEN(previous_playlist), previous_playlist); 165 | } 166 | 167 | void SimpleRemote::sendToggleShuffle() 168 | { 169 | static const byte toggle_shuffle[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x80}; 170 | 171 | #if defined(IPOD_SERIAL_DEBUG) 172 | log("Sending toggle_shuffle"); 173 | #endif 174 | sendCommandWithLength(ARRAY_LEN(toggle_shuffle), toggle_shuffle); 175 | } 176 | 177 | void SimpleRemote::sendToggleRepeat() 178 | { 179 | static const byte toggle_repeat[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x00, 0x01}; 180 | 181 | #if defined(IPOD_SERIAL_DEBUG) 182 | log("Sending toggle_repeat"); 183 | #endif 184 | sendCommandWithLength(ARRAY_LEN(toggle_repeat), toggle_repeat); 185 | } 186 | 187 | void SimpleRemote::sendiPodOff() 188 | { 189 | static const byte ipod_off[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x00, 0x04}; 190 | 191 | #if defined(IPOD_SERIAL_DEBUG) 192 | log("Sending ipod_off"); 193 | #endif 194 | sendCommandWithLength(ARRAY_LEN(ipod_off), ipod_off); 195 | } 196 | 197 | void SimpleRemote::sendiPodOn() 198 | { 199 | static const byte ipod_on[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x00, 0x08}; 200 | 201 | #if defined(IPOD_SERIAL_DEBUG) 202 | log("Sending ipod_on"); 203 | #endif 204 | sendCommandWithLength(ARRAY_LEN(ipod_on), ipod_on); 205 | } 206 | 207 | void SimpleRemote::sendMenuButton() 208 | { 209 | static const byte menu_button[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x00, 0x40}; 210 | 211 | #if defined(IPOD_SERIAL_DEBUG) 212 | log("Sending menu_button"); 213 | #endif 214 | sendCommandWithLength(ARRAY_LEN(menu_button), menu_button); 215 | } 216 | 217 | void SimpleRemote::sendOkSelectButton() 218 | { 219 | static const byte ok_select_button[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x00, 0x80}; 220 | 221 | #if defined(IPOD_SERIAL_DEBUG) 222 | log("Sending ok_select_button"); 223 | #endif 224 | sendCommandWithLength(ARRAY_LEN(ok_select_button), ok_select_button); 225 | } 226 | 227 | void SimpleRemote::sendScrollUp() 228 | { 229 | static const byte scroll_up[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x00, 0x00, 0x01}; 230 | 231 | #if defined(IPOD_SERIAL_DEBUG) 232 | log("Sending scroll_up"); 233 | #endif 234 | sendCommandWithLength(ARRAY_LEN(scroll_up), scroll_up); 235 | } 236 | 237 | void SimpleRemote::sendScrollDown() 238 | { 239 | static const byte scroll_down[] = {SIMPLE_REMOTE_MODE, 0x00, 0x00, 0x00, 0x00, 0x02}; 240 | 241 | #if defined(IPOD_SERIAL_DEBUG) 242 | log("Sending scroll_down"); 243 | #endif 244 | sendCommandWithLength(ARRAY_LEN(scroll_down), scroll_down); 245 | } 246 | -------------------------------------------------------------------------------- /iPodSerial.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009 David Findlay 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | ******************************************************************************/ 25 | #include "iPodSerial.h" 26 | #include 27 | 28 | static const char *STATE_NAME[] = 29 | { 30 | "Waiting for Header 1", 31 | "Waiting for Header 2", 32 | "Waiting for length", 33 | "Waiting for data", 34 | "Waiting for checksum" 35 | }; 36 | 37 | iPodSerial::iPodSerial() 38 | : receiveState(WAITING_FOR_HEADER1), 39 | dataSize(0), 40 | pData(0), 41 | checksum(0), 42 | pSerial(&Serial) // default to regular serial port as that's all most Arduinos have 43 | #if defined(IPOD_SERIAL_DEBUG) 44 | , 45 | pDebugPrint(0), // default to no debug, since most Arduinos don't have a spare serial to use for debug 46 | pLogPrint(0) // default to no log, since most Arduinos don't have a spare serial to use for debug 47 | #endif 48 | { 49 | } 50 | 51 | void iPodSerial::setSerial(Stream &newiPodSerial) 52 | { 53 | pSerial = &newiPodSerial; 54 | } 55 | 56 | #if defined(IPOD_SERIAL_DEBUG) 57 | void iPodSerial::setDebugPrint(Print &newPrint) 58 | { 59 | pDebugPrint = &newPrint; 60 | pDebugPrint->println("Debug Print now set"); 61 | } 62 | 63 | void iPodSerial::setLogPrint(Print &newPrint) 64 | { 65 | pLogPrint = &newPrint; 66 | 67 | if (pDebugPrint) 68 | { 69 | pDebugPrint->println("Log Print now set"); 70 | } 71 | } 72 | #endif 73 | 74 | bool iPodSerial::validChecksum(byte actual) 75 | { 76 | int expected = dataSize; 77 | for (int i = 0; i < dataSize; ++i) 78 | { 79 | expected += dataBuffer[i]; 80 | } 81 | 82 | expected = (0x100 - expected) & 0xFF; 83 | 84 | if (expected == actual) 85 | { 86 | return true; 87 | } 88 | else 89 | { 90 | #if defined(IPOD_SERIAL_DEBUG) 91 | if (pDebugPrint) 92 | { 93 | pDebugPrint->print("checksum mismatch: expected "); 94 | pDebugPrint->print(expected, HEX); 95 | pDebugPrint->print(" but got "); 96 | pDebugPrint->println(actual, HEX); 97 | } 98 | #endif 99 | 100 | return false; 101 | } 102 | } 103 | 104 | #if defined(IPOD_SERIAL_DEBUG) 105 | void iPodSerial::dumpReceive() 106 | { 107 | if (!pDebugPrint) 108 | { 109 | return; 110 | } 111 | 112 | pDebugPrint->print("data size = "); 113 | pDebugPrint->println(dataSize, DEC); 114 | 115 | for (size_t i = 0; i < sizeof(dataBuffer); ++i) 116 | { 117 | pDebugPrint->print("dataBuffer["); 118 | pDebugPrint->print(i, DEC); 119 | pDebugPrint->print("] = "); 120 | pDebugPrint->println(dataBuffer[i], HEX); 121 | } 122 | } 123 | #endif 124 | 125 | void iPodSerial::processResponse() 126 | { 127 | const int avail = pSerial->available(); 128 | if (avail <= 0) 129 | { 130 | return; 131 | } 132 | 133 | // read a single byte from the iPod 134 | const int b = pSerial->read(); 135 | 136 | #if defined(IPOD_SERIAL_DEBUG) 137 | if (pDebugPrint) 138 | { 139 | pDebugPrint->print("Receive Status: "); 140 | pDebugPrint->println(STATE_NAME[receiveState]); 141 | 142 | pDebugPrint->print(b, HEX); 143 | pDebugPrint->print(" "); 144 | 145 | if (isprint(b)) 146 | { 147 | #if defined(ARDUINO) && ARDUINO >= 100 148 | pDebugPrint->write(b); 149 | #else 150 | pDebugPrint->print(b, BYTE); 151 | #endif 152 | } 153 | pDebugPrint->println(); 154 | } 155 | #endif 156 | 157 | switch (receiveState) 158 | { 159 | case WAITING_FOR_HEADER1: 160 | if (b == HEADER1) 161 | { 162 | receiveState = WAITING_FOR_HEADER2; 163 | } 164 | break; 165 | 166 | case WAITING_FOR_HEADER2: 167 | if (b == HEADER2) 168 | { 169 | receiveState = WAITING_FOR_LENGTH; 170 | } 171 | break; 172 | 173 | case WAITING_FOR_LENGTH: 174 | dataSize = b; 175 | pData = dataBuffer; 176 | receiveState = WAITING_FOR_DATA; 177 | #if defined(IPOD_SERIAL_DEBUG) 178 | if (pDebugPrint) 179 | { 180 | pDebugPrint->print("Data length is "); 181 | pDebugPrint->println(dataSize, DEC); 182 | } 183 | #endif 184 | break; 185 | 186 | case WAITING_FOR_DATA: 187 | *pData++ = b; 188 | 189 | if ((pData - dataBuffer) == dataSize) 190 | { 191 | receiveState = WAITING_FOR_CHECKSUM; 192 | } 193 | #if defined(IPOD_SERIAL_DEBUG) 194 | else 195 | { 196 | if (pDebugPrint) 197 | { 198 | pDebugPrint->print("Waiting for "); 199 | pDebugPrint->println(dataSize - (pData - dataBuffer)); 200 | } 201 | } 202 | #endif 203 | break; 204 | 205 | case WAITING_FOR_CHECKSUM: 206 | if (validChecksum(b)) 207 | { 208 | processData(); 209 | } 210 | receiveState = WAITING_FOR_HEADER1; 211 | memset(dataBuffer, 0, sizeof(dataBuffer)); 212 | break; 213 | } 214 | } 215 | 216 | void iPodSerial::sendCommandWithLength( 217 | size_t length, 218 | const byte *pData) 219 | { 220 | #if defined(IPOD_SERIAL_DEBUG) 221 | if (pDebugPrint) 222 | { 223 | pDebugPrint->print("Sending command of length: "); 224 | pDebugPrint->println(length, DEC); 225 | } 226 | #endif 227 | 228 | sendHeader(); 229 | sendLength(length); 230 | sendBytes(length, pData); 231 | sendChecksum(); 232 | } 233 | 234 | void iPodSerial::sendCommand( 235 | byte mode, 236 | byte cmdByte1, 237 | byte cmdByte2) 238 | { 239 | sendHeader(); 240 | sendLength(1 + 1 + 1); 241 | sendByte(mode); 242 | sendByte(cmdByte1); 243 | sendByte(cmdByte2); 244 | sendChecksum(); 245 | } 246 | 247 | void iPodSerial::sendCommandWithOneByteParam( 248 | byte mode, 249 | byte cmdByte1, 250 | byte cmdByte2, 251 | byte byteParam) 252 | { 253 | sendHeader(); 254 | sendLength(1 + 1 + 1 + 1); 255 | sendByte(mode); 256 | sendByte(cmdByte1); 257 | sendByte(cmdByte2); 258 | sendByte(byteParam); 259 | sendChecksum(); 260 | } 261 | 262 | void iPodSerial::sendCommandWithOneNumberParam( 263 | byte mode, 264 | byte cmdByte1, 265 | byte cmdByte2, 266 | unsigned long numberParam) 267 | { 268 | sendHeader(); 269 | sendLength(1 + 1 + 1 + 4); 270 | sendByte(mode); 271 | sendByte(cmdByte1); 272 | sendByte(cmdByte2); 273 | sendNumber(numberParam); 274 | sendChecksum(); 275 | } 276 | 277 | void iPodSerial::sendCommandWithOneByteAndOneNumberParam( 278 | byte mode, 279 | byte cmdByte1, 280 | byte cmdByte2, 281 | byte byteParam1, 282 | unsigned long numberParam2) 283 | { 284 | sendHeader(); 285 | sendLength(1 + 1 + 1 + 1 + (1 * 4)); 286 | sendByte(mode); 287 | sendByte(cmdByte1); 288 | sendByte(cmdByte2); 289 | sendByte(byteParam1); 290 | sendNumber(numberParam2); 291 | sendChecksum(); 292 | } 293 | 294 | void iPodSerial::sendCommandWithOneByteAndTwoNumberParams( 295 | byte mode, 296 | byte cmdByte1, 297 | byte cmdByte2, 298 | byte byteParam1, 299 | unsigned long numberParam2, 300 | unsigned long numberParam3) 301 | { 302 | sendHeader(); 303 | sendLength(1 + 1 + 1 + 1 + (2 * 4)); 304 | sendByte(mode); 305 | sendByte(cmdByte1); 306 | sendByte(cmdByte2); 307 | sendByte(byteParam1); 308 | sendNumber(numberParam2); 309 | sendNumber(numberParam3); 310 | sendChecksum(); 311 | } 312 | 313 | void iPodSerial::sendHeader() 314 | { 315 | sendByte(HEADER1); 316 | sendByte(HEADER2); 317 | } 318 | 319 | void iPodSerial::sendLength(size_t length) // length is mode+command+parameters in bytes 320 | { 321 | sendByte(length); 322 | checksum = length; 323 | } 324 | 325 | void iPodSerial::sendBytes(size_t length, const byte *pData) 326 | { 327 | for (size_t i = 0; i < length; i++) 328 | { 329 | sendByte(pData[i]); 330 | } 331 | } 332 | 333 | void iPodSerial::sendByte(byte b) 334 | { 335 | pSerial->write(b); 336 | checksum += b; 337 | 338 | #if defined(IPOD_SERIAL_DEBUG) 339 | // likely to slow stuff down! 340 | if (pDebugPrint) 341 | { 342 | pDebugPrint->print("sent byte "); 343 | pDebugPrint->println(b, HEX); 344 | } 345 | #endif 346 | } 347 | 348 | void iPodSerial::sendNumber(unsigned long n) 349 | { 350 | // parameter (4-byte int sent big-endian) 351 | sendByte((n & 0xFF000000) >> 24); 352 | sendByte((n & 0x00FF0000) >> 16); 353 | sendByte((n & 0x0000FF00) >> 8); 354 | sendByte((n & 0x000000FF) >> 0); 355 | } 356 | 357 | void iPodSerial::sendChecksum() 358 | { 359 | sendByte((0x100 - checksum) & 0xFF); 360 | } 361 | 362 | void iPodSerial::loop() 363 | { 364 | if (pSerial->available() > 0) 365 | { 366 | processResponse(); 367 | } 368 | } 369 | 370 | void iPodSerial::processData() 371 | { 372 | #if defined(IPOD_SERIAL_DEBUG) 373 | if (pDebugPrint) 374 | { 375 | pDebugPrint->println("Ignoring data from iPod:"); 376 | dumpReceive(); 377 | } 378 | #endif 379 | } 380 | 381 | #if defined(IPOD_SERIAL_DEBUG) 382 | void iPodSerial::log(const char *message) 383 | { 384 | if (pLogPrint) 385 | { 386 | pLogPrint->println(message); 387 | } 388 | } 389 | #endif 390 | -------------------------------------------------------------------------------- /examples/AdvancedRemote_ethernet/AdvancedRemote_ethernet.pde: -------------------------------------------------------------------------------- 1 | // NOTE: This sketch is still a work-in-progress. 2 | 3 | // Example of Advanced Remote that exposes an ethernet command interface. 4 | // Look at the clientLoop() function to see what it understands. 5 | // 6 | // If your iPod ends up stuck with the "OK to disconnect" message on its display, 7 | // reset the Arduino. There's a called to AdvancedRemote::disable() in the setup() 8 | // function which should put the iPod back to its normal mode. If that doesn't 9 | // work, or you are unable to reset your Arduino for some reason, resetting the 10 | // iPod will put it back to its normal mode. 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | AdvancedRemote ar; 17 | 18 | byte MAC_ADDRESS[] = { 19 | 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 20 | byte IP_ADDRESS[] = { 21 | 192, 168, 13, 2 }; 22 | 23 | // Don't use 23 or telnet client will tend to do telnet negotiation (WILL/WONT/DO/DONT etc) 24 | byte PORT = 80; 25 | EthernetServer server = EthernetServer(PORT); 26 | EthernetClient c(255); 27 | 28 | char buffer[32]; 29 | int len = 0; 30 | 31 | /* 32 | // for debug only, but right now using it causes the connection to reset for some reason 33 | class ClientPrint : 34 | public Print 35 | { 36 | public: 37 | virtual void write(uint8_t b) 38 | { 39 | if (c) 40 | { 41 | c.write(b); 42 | } 43 | } 44 | }; 45 | ClientPrint clientPrint; 46 | */ 47 | 48 | // 49 | // our handler (aka callback) implementations; 50 | // these are what get sent data back from the iPod 51 | // when we make requests of it. 52 | // 53 | 54 | void feedbackHandler(AdvancedRemote::Feedback feedback, byte cmd) 55 | { 56 | c.print("{\"feedback\":{\"cmd\": "); 57 | c.print(cmd, DEC); 58 | c.print(", \"feedback\": "); 59 | c.print(feedback, DEC); 60 | c.println("}}"); 61 | } 62 | 63 | void iPodNameHandler(const char *name) 64 | { 65 | c.print("{\"ipod-name\": \""); 66 | c.print(name); 67 | c.println("\"}"); 68 | } 69 | 70 | void titleHandler(const char *name) 71 | { 72 | c.print("{\"title\": \""); 73 | c.print(name); 74 | c.println("\"}"); 75 | } 76 | 77 | void artistHandler(const char *name) 78 | { 79 | c.print("{\"artist\": \""); 80 | c.print(name); 81 | c.println("\"}"); 82 | } 83 | 84 | void albumHandler(const char *name) 85 | { 86 | c.print("{\"album\": \""); 87 | c.print(name); 88 | c.println("\"}"); 89 | } 90 | 91 | void currentPlaylistSongCountHandler(unsigned long count) 92 | { 93 | c.print("{\"pl-song-count\": "); 94 | c.print(count, DEC); 95 | c.println("}"); 96 | } 97 | 98 | void pollingHandler(AdvancedRemote::PollingCommand command, 99 | unsigned long playlistPositionOrelapsedTimeMs) 100 | { 101 | c.print("{\"poll\": "); 102 | 103 | switch (command) 104 | { 105 | case AdvancedRemote::POLLING_TRACK_CHANGE: 106 | c.print("{\"track-change-to\": "); 107 | break; 108 | 109 | case AdvancedRemote::POLLING_ELAPSED_TIME: 110 | c.print("{\"elapsed-time\": "); 111 | break; 112 | 113 | default: 114 | c.print("{\"unknown\": "); 115 | break; 116 | } 117 | 118 | c.print(playlistPositionOrelapsedTimeMs, DEC); 119 | c.println("}}"); 120 | } 121 | 122 | void playlistPositionHandler(unsigned long index) 123 | { 124 | c.print("{\"pl-position\": "); 125 | c.print(index, DEC); 126 | c.println("}"); 127 | } 128 | 129 | void itemCountHandler(unsigned long count) 130 | { 131 | c.print("{\"item-count\": "); 132 | c.print(count, DEC); 133 | c.println("}"); 134 | } 135 | 136 | void itemNameHandler(unsigned long offset, const char *name) 137 | { 138 | c.print("{\"item\": {\"offset\": "); 139 | c.print(offset, DEC); 140 | c.print(", \"name\": \""); 141 | c.print(name); 142 | c.println("\"}}"); 143 | } 144 | 145 | void timeAndStatusHandler(unsigned long trackLenMs, unsigned long elapsedTimeMs, AdvancedRemote::PlaybackStatus status) 146 | { 147 | c.print("{\"status\": {\"track-length\" "); 148 | c.print(trackLenMs, DEC); 149 | c.print(", \"elapsed-time\": "); 150 | c.print(elapsedTimeMs); 151 | c.println("}}"); 152 | } 153 | 154 | const char *tryAndReadALine() 155 | { 156 | int b = -1; 157 | if (c.available()) 158 | { 159 | b = c.read(); 160 | if ((b != -1) && (b != '\r') && (b != '\n')) 161 | { 162 | buffer[len++] = b; 163 | } 164 | }; 165 | 166 | if (b == '\n') 167 | { 168 | buffer[len] = '\0'; 169 | len = 0; 170 | return buffer; 171 | } 172 | else 173 | { 174 | return NULL; 175 | } 176 | } 177 | 178 | // extract the number at the end of a string, e.g. the 1 in "foo1" 179 | long getIndex(const char *cmd, const char *prefix) 180 | { 181 | const size_t prefixLen = strlen(prefix); 182 | 183 | if (strncmp(cmd, prefix, prefixLen) == 0) 184 | { 185 | return atol(cmd + prefixLen); 186 | } 187 | else 188 | { 189 | return -1; 190 | } 191 | } 192 | 193 | // see if we got a full command from the client, and if so process it 194 | void clientLoop() 195 | { 196 | const char *cmd = tryAndReadALine(); 197 | if (cmd == NULL) 198 | { 199 | // not a complete command yet 200 | return; 201 | } 202 | 203 | long index; 204 | 205 | if (strcmp(cmd, "enable") == 0) 206 | { 207 | ar.enable(); 208 | // iPod has no response for this, so fake one 209 | c.println("{\"enabled\": 1}"); 210 | } 211 | else if (strcmp(cmd, "disable") == 0) 212 | { 213 | ar.disable(); 214 | // iPod has no response for this, so fake one 215 | c.println("{\"enabled\": 0}"); 216 | } 217 | else if (strcmp(cmd, "ipod-name") == 0) 218 | { 219 | ar.getiPodName(); 220 | } 221 | /* 222 | else if (strcmp(cmd, "s-main-pl") == 0) 223 | { 224 | ar.switchToMainLibraryPlaylist(); 225 | } 226 | */ 227 | // 228 | // item switchers 229 | // 230 | else if ((index = getIndex(cmd, "s-pl-")) != -1) 231 | { 232 | ar.switchToItem(AdvancedRemote::ITEM_PLAYLIST, index); 233 | } 234 | else if ((index = getIndex(cmd, "s-artist-")) != -1) 235 | { 236 | ar.switchToItem(AdvancedRemote::ITEM_ARTIST, index); 237 | } 238 | else if ((index = getIndex(cmd, "s-album-")) != -1) 239 | { 240 | ar.switchToItem(AdvancedRemote::ITEM_ALBUM, index); 241 | } 242 | else if ((index = getIndex(cmd, "s-genre-")) != -1) 243 | { 244 | ar.switchToItem(AdvancedRemote::ITEM_GENRE, index); 245 | } 246 | else if ((index = getIndex(cmd, "s-song-")) != -1) 247 | { 248 | ar.switchToItem(AdvancedRemote::ITEM_SONG, index); 249 | } 250 | else if ((index = getIndex(cmd, "s-composer-")) != -1) 251 | { 252 | ar.switchToItem(AdvancedRemote::ITEM_COMPOSER, index); 253 | } 254 | // 255 | // item counts 256 | // 257 | else if (strcmp(cmd, "g-pl-count") == 0) 258 | { 259 | ar.getItemCount(AdvancedRemote::ITEM_PLAYLIST); 260 | } 261 | else if (strcmp(cmd, "g-artist-count") == 0) 262 | { 263 | ar.getItemCount(AdvancedRemote::ITEM_ARTIST); 264 | } 265 | else if (strcmp(cmd, "g-album-count") == 0) 266 | { 267 | ar.getItemCount(AdvancedRemote::ITEM_ALBUM); 268 | } 269 | else if (strcmp(cmd, "g-genre-count") == 0) 270 | { 271 | ar.getItemCount(AdvancedRemote::ITEM_GENRE); 272 | } 273 | else if (strcmp(cmd, "g-song-count") == 0) 274 | { 275 | ar.getItemCount(AdvancedRemote::ITEM_SONG); 276 | } 277 | else if (strcmp(cmd, "g-composer-count") == 0) 278 | { 279 | ar.getItemCount(AdvancedRemote::ITEM_COMPOSER); 280 | } 281 | // 282 | // item names 283 | // 284 | else if ((index = getIndex(cmd, "g-pl-name-")) != -1) 285 | { 286 | ar.getItemNames(AdvancedRemote::ITEM_PLAYLIST, index, 1); 287 | } 288 | else if ((index = getIndex(cmd, "g-artist-name-")) != -1) 289 | { 290 | ar.getItemNames(AdvancedRemote::ITEM_ARTIST, index, 1); 291 | } 292 | else if ((index = getIndex(cmd, "g-album-name-")) != -1) 293 | { 294 | ar.getItemNames(AdvancedRemote::ITEM_ALBUM, index, 1); 295 | } 296 | else if ((index = getIndex(cmd, "g-genre-name-")) != -1) 297 | { 298 | ar.getItemNames(AdvancedRemote::ITEM_GENRE, index, 1); 299 | } 300 | else if ((index = getIndex(cmd, "g-song-name-")) != -1) 301 | { 302 | ar.getItemNames(AdvancedRemote::ITEM_SONG, index, 1); 303 | } 304 | else if ((index = getIndex(cmd, "g-composer-name-")) != -1) 305 | { 306 | ar.getItemNames(AdvancedRemote::ITEM_COMPOSER, index, 1); 307 | } 308 | // 309 | // Misc 310 | // 311 | else if (strcmp(cmd, "g-status") == 0) 312 | { 313 | ar.getTimeAndStatusInfo(); 314 | } 315 | else if (strcmp(cmd, "g-pl-song-count") == 0) 316 | { 317 | ar.getSongCountInCurrentPlaylist(); 318 | } 319 | else if (strcmp(cmd, "g-pl-position") == 0) 320 | { 321 | ar.getPlaylistPosition(); 322 | } 323 | else if ((index = getIndex(cmd, "jump-to-song-")) != -1) 324 | { 325 | ar.jumpToSongInCurrentPlaylist(index); 326 | } 327 | else if (strcmp(cmd, "play") == 0) 328 | { 329 | ar.controlPlayback(AdvancedRemote::PLAYBACK_CONTROL_PLAY_PAUSE); 330 | } 331 | else if (strcmp(cmd, "stop") == 0) 332 | { 333 | ar.controlPlayback(AdvancedRemote::PLAYBACK_CONTROL_STOP); 334 | } 335 | else if (strcmp(cmd, "poll-start") == 0) 336 | { 337 | ar.setPollingMode(AdvancedRemote::POLLING_START); 338 | } 339 | else if (strcmp(cmd, "poll-stop") == 0) 340 | { 341 | ar.setPollingMode(AdvancedRemote::POLLING_STOP); 342 | } 343 | else if ((index = getIndex(cmd, "switch-")) != -1) 344 | { 345 | ar.executeSwitch(index); 346 | } 347 | else if (strcmp(cmd, "switch") == 0) 348 | { 349 | // send the index that means 'first track no matter the shuffle order' 350 | ar.executeSwitch(0xFFFFFFFF); 351 | } 352 | else if ((index = getIndex(cmd, "pl-info-")) != -1) 353 | { 354 | ar.getItemNames(AdvancedRemote::ITEM_PLAYLIST, index, 1); 355 | ar.switchToItem(AdvancedRemote::ITEM_PLAYLIST, index); 356 | ar.getSongCountInCurrentPlaylist(); 357 | ar.getPlaylistPosition(); 358 | ar.getItemCount(AdvancedRemote::ITEM_ARTIST); 359 | ar.getItemCount(AdvancedRemote::ITEM_ALBUM); 360 | ar.getItemCount(AdvancedRemote::ITEM_SONG); 361 | ar.getItemCount(AdvancedRemote::ITEM_GENRE); 362 | ar.getItemCount(AdvancedRemote::ITEM_COMPOSER); 363 | } 364 | else 365 | { 366 | c.println("{\"huh\"}"); 367 | } 368 | 369 | } 370 | 371 | void setup() 372 | { 373 | Ethernet.begin(MAC_ADDRESS, IP_ADDRESS); 374 | server.begin(); 375 | 376 | // register callback functions for the things we're going to read 377 | ar.setFeedbackHandler(feedbackHandler); 378 | ar.setiPodNameHandler(iPodNameHandler); 379 | ar.setTitleHandler(titleHandler); 380 | ar.setArtistHandler(artistHandler); 381 | ar.setAlbumHandler(albumHandler); 382 | ar.setPollingHandler(pollingHandler); 383 | ar.setCurrentPlaylistSongCountHandler(currentPlaylistSongCountHandler); 384 | ar.setItemCountHandler(itemCountHandler); 385 | ar.setItemNameHandler(itemNameHandler); 386 | ar.setTimeAndStatusHandler(timeAndStatusHandler); 387 | ar.setPlaylistPositionHandler(playlistPositionHandler); 388 | 389 | /* 390 | // non-working debug stuff 391 | ar.setLogPrint(clientPrint); 392 | ar.setDebugPrint(clientPrint); 393 | */ 394 | 395 | Serial.begin(iPodSerial::IPOD_SERIAL_RATE); 396 | 397 | // start in simple remote mode 398 | ar.disable(); 399 | } 400 | 401 | void loop() 402 | { 403 | 404 | // service iPod 405 | ar.loop(); 406 | 407 | if (c && c.connected()) 408 | { 409 | // service network client 410 | clientLoop(); 411 | } 412 | else 413 | { 414 | // look for a new network client 415 | EthernetClient newClient = server.available(); 416 | if (newClient) 417 | { 418 | if (c) 419 | { 420 | c.stop(); 421 | } 422 | 423 | c = newClient; 424 | len = 0; 425 | } 426 | } 427 | } 428 | -------------------------------------------------------------------------------- /AdvancedRemote.h: -------------------------------------------------------------------------------- 1 | #ifndef ADVANCED_REMOTE 2 | #define ADVANCED_REMOTE 3 | /******************************************************************************* 4 | * Copyright (c) 2009 David Findlay 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * - Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * - Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | ******************************************************************************/ 27 | 28 | #include "iPodSerial.h" 29 | 30 | class AdvancedRemote : public iPodSerial 31 | { 32 | public: // enums 33 | enum ItemType 34 | { 35 | ITEM_PLAYLIST = 0x01, 36 | ITEM_ARTIST = 0x02, 37 | ITEM_ALBUM = 0x03, 38 | ITEM_GENRE = 0x04, 39 | ITEM_SONG = 0x05, 40 | ITEM_COMPOSER = 0x06 41 | }; 42 | 43 | enum PlaybackStatus 44 | { 45 | STATUS_STOPPED = 0, 46 | STATUS_PLAYING = 1, 47 | STATUS_PAUSED = 2 48 | }; 49 | 50 | enum PollingMode 51 | { 52 | POLLING_START = 0x01, 53 | POLLING_STOP = 0x00 54 | }; 55 | 56 | enum PollingCommand 57 | { 58 | POLLING_TRACK_CHANGE = 0x01, 59 | POLLING_ELAPSED_TIME = 0x04 60 | }; 61 | 62 | enum PlaybackControl 63 | { 64 | PLAYBACK_CONTROL_PLAY_PAUSE = 0x01, 65 | PLAYBACK_CONTROL_STOP = 0x02, 66 | PLAYBACK_CONTROL_SKIP_FORWARD = 0x03, 67 | PLAYBACK_CONTROL_SKIP_BACKWARD = 0x04, 68 | PLAYBACK_CONTROL_FAST_FORWARD = 0x05, 69 | PLAYBACK_CONTROL_REVERSE = 0x06, 70 | PLAYBACK_CONTROL_STOP_FF_OR_REV = 0x07 71 | }; 72 | 73 | enum ShuffleMode 74 | { 75 | SHUFFLE_MODE_OFF = 0x00, 76 | SHUFFLE_MODE_SONGS = 0x01, 77 | SHUFFLE_MODE_ALBUMS = 0x02 78 | }; 79 | 80 | enum RepeatMode 81 | { 82 | REPEAT_MODE_OFF = 0x00, 83 | REPEAT_MODE_ONE_SONG = 0x01, 84 | REPEAT_MODE_ALL_SONGS = 0x02 85 | }; 86 | 87 | enum Feedback 88 | { 89 | FEEDBACK_SUCCESS = 0x00, 90 | FEEDBACK_FAILURE = 0x02, 91 | FEEDBACK_INVALID_PARAM = 0x04, 92 | FEEDBACK_SENT_RESPONSE = 0x05 93 | }; 94 | 95 | // the internal command types; only useful if you're implementing the Feedback handler 96 | static const byte CMD_GET_IPOD_NAME = 0x14; 97 | static const byte CMD_SWITCH_TO_MAIN_LIBRARY_PLAYLIST = 0x16; 98 | static const byte CMD_SWITCH_TO_ITEM = 0x17; 99 | static const byte CMD_GET_ITEM_COUNT = 0x18; 100 | static const byte CMD_GET_ITEM_NAMES = 0x1A; 101 | static const byte CMD_GET_TIME_AND_STATUS_INFO = 0x1C; 102 | static const byte CMD_GET_PLAYLIST_POSITION = 0x1E; 103 | static const byte CMD_GET_TITLE = 0x20; 104 | static const byte CMD_GET_ARTIST = 0x22; 105 | static const byte CMD_GET_ALBUM = 0x24; 106 | static const byte CMD_POLLING_MODE = 0x26; 107 | static const byte CMD_EXECUTE_SWITCH = 0x28; 108 | static const byte CMD_PLAYBACK_CONTROL = 0x29; 109 | static const byte CMD_GET_SHUFFLE_MODE = 0x2C; 110 | static const byte CMD_SET_SHUFFLE_MODE = 0x2E; 111 | static const byte CMD_GET_REPEAT_MODE = 0x2F; 112 | static const byte CMD_SET_REPEAT_MODE = 0x31; 113 | static const byte CMD_GET_SONG_COUNT_IN_CURRENT_PLAYLIST = 0x35; 114 | static const byte CMD_JUMP_TO_SONG_IN_CURRENT_PLAYLIST = 0x37; 115 | 116 | public: // handler definitions 117 | typedef void FeedbackHandler_t(Feedback feedback, byte cmd); 118 | typedef void iPodNameHandler_t(const char *ipodName); 119 | typedef void ItemCountHandler_t(unsigned long count); 120 | typedef void ItemNameHandler_t(unsigned long offet, const char *itemName); 121 | typedef void TimeAndStatusHandler_t(unsigned long trackLengthInMilliseconds, 122 | unsigned long elapsedTimeInMilliseconds, 123 | PlaybackStatus status); 124 | typedef void PlaylistPositionHandler_t(unsigned long playlistPosition); 125 | typedef void TitleHandler_t(const char *title); 126 | typedef void ArtistHandler_t(const char *artist); 127 | typedef void AlbumHandler_t(const char *album); 128 | typedef void PollingHandler_t(PollingCommand command, 129 | unsigned long playlistPositionOrelapsedTimeMs); 130 | typedef void ShuffleModeHandler_t(ShuffleMode mode); 131 | typedef void RepeatModeHandler_t(RepeatMode mode); 132 | typedef void CurrentPlaylistSongCountHandler_t(unsigned long count); 133 | 134 | 135 | public: // handler setting methods; you probably want to call these from init() 136 | void setFeedbackHandler(FeedbackHandler_t newHandler); 137 | void setiPodNameHandler(iPodNameHandler_t newHandler); 138 | void setItemCountHandler(ItemCountHandler_t newHandler); 139 | void setItemNameHandler(ItemNameHandler_t newHandler); 140 | void setTimeAndStatusHandler(TimeAndStatusHandler_t newHandler); 141 | void setPlaylistPositionHandler(PlaylistPositionHandler_t newHandler); 142 | void setTitleHandler(TitleHandler_t newHandler); 143 | void setArtistHandler(ArtistHandler_t newHandler); 144 | void setAlbumHandler(AlbumHandler_t newHandler); 145 | void setPollingHandler(PollingHandler_t newHandler); 146 | void setShuffleModeHandler(ShuffleModeHandler_t newHandler); 147 | void setRepeatModeHandler(RepeatModeHandler_t newHandler); 148 | void setCurrentPlaylistSongCountHandler(CurrentPlaylistSongCountHandler_t newHandler); 149 | 150 | 151 | public: // methods 152 | AdvancedRemote(); 153 | 154 | /** 155 | * Turn on Advanced Remote mode. This causes the iPod to 156 | * show a check mark and "OK to disconnect" on its display. 157 | * The iPod can only be controlled by the remote in this mode. 158 | * 159 | * You can just reset your iPod to get it out of this mode 160 | * if you end up with it stuck in this mode for some reason, 161 | * such as your sketch not calling disable(). 162 | * 163 | * You must call this before calling any of the getXXX methods. 164 | */ 165 | void enable(); 166 | 167 | /** 168 | * disabled Advanced Remote mode. 169 | * 170 | * You should call this to get your iPod to stop saying 171 | * "OK to disconnect", and let you control it through its 172 | * own interface again. 173 | */ 174 | void disable(); 175 | 176 | /** 177 | * Get the name of the iPod. 178 | * The response will be sent to the iPodNameHandler, if one 179 | * has been registered (otherwise it will be ignored). 180 | */ 181 | void getiPodName(); 182 | 183 | /** 184 | * Switch to the Main Library playlist (playlist 0). 185 | */ 186 | void switchToMainLibraryPlaylist(); 187 | 188 | /** 189 | * Switch to the item identified by the type and index given. 190 | * This is the equivalent of drilling down from the Music 191 | * menu to the item specified. If you want to select the item 192 | * for playing you then need to call executeSwitch() 193 | */ 194 | void switchToItem(ItemType itemType, long index); 195 | 196 | /** 197 | * Get the total number of items of the specified type. 198 | * The response will be sent to the ItemCountHandler, if one 199 | * has been registered (otherwise it will be ignored). 200 | * The count returned is in the context of the most-recently 201 | * selected playlist (via switchToItem). The playlist doesn't 202 | * have to also have been executeSwitch()'d to however. 203 | */ 204 | void getItemCount(ItemType itemType); 205 | 206 | /** 207 | * Get the names of a range of items. 208 | * offset is the starting item (starts at 0, not 1) 209 | * The offset is in the context of the most-recently 210 | * selected playlist (via switchToItem). The playlist doesn't 211 | * have to also have been executeSwitch()'d to however. 212 | */ 213 | void getItemNames(ItemType itemType, unsigned long offset, unsigned long count); 214 | 215 | /** 216 | * Ask the iPod for time and playback status information. 217 | * The response will be sent to the TimeAndStatusHandler, if one 218 | * has been registered (otherwise it will be ignored). 219 | */ 220 | void getTimeAndStatusInfo(); 221 | 222 | /** 223 | * Ask the iPod for the current position in the current playlist. 224 | * The response will be sent to the PlaylistPositionHandler, if one 225 | * has been registered (otherwise it will be ignored). 226 | * The current playlist is the one last selected via executeSwitch 227 | * or via the iPod's controls - calling switchToItem for a playlist is 228 | * not enough. 229 | */ 230 | void getPlaylistPosition(); 231 | 232 | /** 233 | * ask the iPod for the title of a track. 234 | * The response will be sent to the TitleHandler, if one 235 | * has been registered (otherwise it will be ignored). 236 | * The index is in the context of the most-recently 237 | * selected playlist (via switchToItem). The playlist doesn't 238 | * have to also have been executeSwitch()'d to however. 239 | */ 240 | void getTitle(unsigned long index); 241 | 242 | /** 243 | * ask the iPod for the artist for a track. 244 | * The response will be sent to the ArtistHandler, if one 245 | * has been registered (otherwise it will be ignored). 246 | * The index is in the context of the most-recently 247 | * selected playlist (via switchToItem). The playlist doesn't 248 | * have to also have been executeSwitch()'d to however. 249 | */ 250 | void getArtist(unsigned long index); 251 | 252 | /** 253 | * ask the iPod for the album for a track. 254 | * The response will be sent to the AlbumHandler, if one 255 | * has been registered (otherwise it will be ignored). 256 | * The index is in the context of the most-recently 257 | * selected playlist (via switchToItem). The playlist doesn't 258 | * have to also have been executeSwitch()'d to however. 259 | */ 260 | void getAlbum(unsigned long index); 261 | 262 | /** 263 | * Start or stop polling mode. 264 | * Polling mode causes the iPod to return the track elapsed time every 500 milliseconds. 265 | * A polling notification will also be sent when a track begins. 266 | * These responses will be sent to the PollingHandler, if one 267 | * has been registered (otherwise they will be ignored). 268 | */ 269 | void setPollingMode(PollingMode newMode); 270 | 271 | /** 272 | * Execute playlist-switch specified in last setItem call, and jump to specified number 273 | * in the playlist (0xFFFFFFFF means start at the beginning of the playlist, even when 274 | * shuffled). After this call getSongCountInCurrentPlaylist(), getPlaylistPosition() 275 | * and jumpToSongInCurrentPlaylist() will be in the context of this newly-switched-to 276 | * playlist. 277 | */ 278 | void executeSwitch(unsigned long index); 279 | 280 | /** 281 | * Control playback (play/pause, etc) 282 | */ 283 | void controlPlayback(PlaybackControl command); 284 | 285 | /** 286 | * Ask the iPod for the current shuffle mode. 287 | * The response will be sent to the ShuffleModeHandler, if one 288 | * has been registered (otherwise it will be ignored). 289 | */ 290 | void getShuffleMode(); 291 | 292 | /** 293 | * Set the shuffle mode. 294 | */ 295 | void setShuffleMode(ShuffleMode newMode); 296 | 297 | /** 298 | * Ask the iPod for the current repeat mode. 299 | * The response will be sent to the RepeatModeHandler, if one 300 | * has been registered (otherwise it will be ignored). 301 | */ 302 | void getRepeatMode(); 303 | 304 | /** 305 | * Set the repeat mode. 306 | */ 307 | void setRepeatMode(RepeatMode newMode); 308 | 309 | /** 310 | * Get the count of songs in the current playlist. 311 | * The response will be sent to the CurrentPlaylistSongCountHandler, if one 312 | * has been registered (otherwise it will be ignored). 313 | * The current playlist is the one last selected via executeSwitch 314 | * or via the iPod's controls - calling switchToItem for a playlist is 315 | * not enough. 316 | */ 317 | void getSongCountInCurrentPlaylist(); 318 | 319 | /** 320 | * Jump to the specified song in the current playlist. 321 | * The current playlist is the one last selected via executeSwitch 322 | * or via the iPod's controls - calling switchToItem for a playlist is 323 | * not enough. 324 | */ 325 | void jumpToSongInCurrentPlaylist(unsigned long index); 326 | 327 | /** 328 | * returns true if advanced mode is enabled, false otherwise. 329 | * Will be fooled if you put the iPod in advanced mode without calling enable() 330 | */ 331 | bool isCurrentlyEnabled(); 332 | 333 | private: // attributes 334 | static const byte RESPONSE_BAD = 0x00; 335 | static const byte RESPONSE_FEEDBACK = 0x01; 336 | 337 | FeedbackHandler_t *pFeedbackHandler; 338 | iPodNameHandler_t *piPodNameHandler; 339 | ItemCountHandler_t *pItemCountHandler; 340 | ItemNameHandler_t *pItemNameHandler; 341 | TimeAndStatusHandler_t *pTimeAndStatusHandler; 342 | PlaylistPositionHandler_t *pPlaylistPositionHandler; 343 | TitleHandler_t *pTitleHandler; 344 | ArtistHandler_t *pArtistHandler; 345 | AlbumHandler_t *pAlbumHandler; 346 | PollingHandler_t *pPollingHandler; 347 | ShuffleModeHandler_t *pShuffleModeHandler; 348 | RepeatModeHandler_t *pRepeatModeHandler; 349 | CurrentPlaylistSongCountHandler_t *pCurrentPlaylistSongCountHandler; 350 | 351 | bool currentlyEnabled; 352 | 353 | private: // methods 354 | virtual void processData(); 355 | static unsigned long endianConvert(const byte *p); 356 | }; 357 | 358 | #endif // ADVANCED_REMOTE 359 | -------------------------------------------------------------------------------- /AdvancedRemote.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009 David Findlay 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * - Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * - Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 17 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | * POSSIBILITY OF SUCH DAMAGE. 24 | ******************************************************************************/ 25 | #include "AdvancedRemote.h" 26 | 27 | AdvancedRemote::AdvancedRemote() 28 | : pFeedbackHandler(0), 29 | piPodNameHandler(0), 30 | pItemCountHandler(0), 31 | pItemNameHandler(0), 32 | pTimeAndStatusHandler(0), 33 | pPlaylistPositionHandler(0), 34 | pTitleHandler(0), 35 | pArtistHandler(0), 36 | pAlbumHandler(0), 37 | pPollingHandler(0), 38 | pShuffleModeHandler(0), 39 | pRepeatModeHandler(0), 40 | pCurrentPlaylistSongCountHandler(0), 41 | currentlyEnabled(false) 42 | { 43 | } 44 | 45 | void AdvancedRemote::setFeedbackHandler(FeedbackHandler_t newHandler) 46 | { 47 | pFeedbackHandler = newHandler; 48 | } 49 | 50 | void AdvancedRemote::setiPodNameHandler(iPodNameHandler_t newHandler) 51 | { 52 | piPodNameHandler = newHandler; 53 | } 54 | 55 | void AdvancedRemote::setItemCountHandler(ItemCountHandler_t newHandler) 56 | { 57 | pItemCountHandler = newHandler; 58 | } 59 | 60 | void AdvancedRemote::setItemNameHandler(ItemNameHandler_t newHandler) 61 | { 62 | pItemNameHandler = newHandler; 63 | } 64 | 65 | void AdvancedRemote::setTimeAndStatusHandler(TimeAndStatusHandler_t newHandler) 66 | { 67 | pTimeAndStatusHandler = newHandler; 68 | } 69 | 70 | void AdvancedRemote::setPlaylistPositionHandler(PlaylistPositionHandler_t newHandler) 71 | { 72 | pPlaylistPositionHandler = newHandler; 73 | } 74 | 75 | void AdvancedRemote::setTitleHandler(TitleHandler_t newHandler) 76 | { 77 | pTitleHandler = newHandler; 78 | } 79 | 80 | void AdvancedRemote::setArtistHandler(ArtistHandler_t newHandler) 81 | { 82 | pArtistHandler = newHandler; 83 | } 84 | 85 | void AdvancedRemote::setAlbumHandler(AlbumHandler_t newHandler) 86 | { 87 | pAlbumHandler = newHandler; 88 | } 89 | 90 | void AdvancedRemote::setPollingHandler(PollingHandler_t newHandler) 91 | { 92 | pPollingHandler = newHandler; 93 | } 94 | 95 | void AdvancedRemote::setShuffleModeHandler(ShuffleModeHandler_t newHandler) 96 | { 97 | pShuffleModeHandler = newHandler; 98 | } 99 | 100 | void AdvancedRemote::setRepeatModeHandler(RepeatModeHandler_t newHandler) 101 | { 102 | pRepeatModeHandler = newHandler; 103 | } 104 | 105 | void AdvancedRemote::setCurrentPlaylistSongCountHandler(CurrentPlaylistSongCountHandler_t newHandler) 106 | { 107 | pCurrentPlaylistSongCountHandler = newHandler; 108 | } 109 | 110 | void AdvancedRemote::enable() 111 | { 112 | #if defined(IPOD_SERIAL_DEBUG) 113 | log("enabling advanced remote mode"); 114 | #endif 115 | sendCommand(MODE_SWITCHING_MODE, 0x01, ADVANCED_REMOTE_MODE); 116 | currentlyEnabled = true; // strictly it's not until we get feedback of success 117 | } 118 | 119 | void AdvancedRemote::disable() 120 | { 121 | #if defined(IPOD_SERIAL_DEBUG) 122 | log("disabling advanced remote mode"); 123 | #endif 124 | sendCommand(MODE_SWITCHING_MODE, 0x01, SIMPLE_REMOTE_MODE); 125 | currentlyEnabled = false; // strictly it's not until we get feedback of success 126 | } 127 | 128 | void AdvancedRemote::getiPodName() 129 | { 130 | #if defined(IPOD_SERIAL_DEBUG) 131 | log("getiPodName"); 132 | #endif 133 | sendCommand(ADVANCED_REMOTE_MODE, 0x00, CMD_GET_IPOD_NAME); 134 | } 135 | 136 | void AdvancedRemote::switchToMainLibraryPlaylist() 137 | { 138 | #if defined(IPOD_SERIAL_DEBUG) 139 | log("switchToMainLibraryPlaylist"); 140 | #endif 141 | sendCommand(ADVANCED_REMOTE_MODE, 0x00, CMD_SWITCH_TO_MAIN_LIBRARY_PLAYLIST); 142 | } 143 | 144 | void AdvancedRemote::switchToItem(AdvancedRemote::ItemType itemType, long index) 145 | { 146 | #if defined(IPOD_SERIAL_DEBUG) 147 | log("switchToItem"); 148 | #endif 149 | sendCommandWithOneByteAndOneNumberParam(ADVANCED_REMOTE_MODE, 0x00, CMD_SWITCH_TO_ITEM, itemType, index); 150 | } 151 | 152 | void AdvancedRemote::getItemCount(AdvancedRemote::ItemType itemType) 153 | { 154 | #if defined(IPOD_SERIAL_DEBUG) 155 | log("getItemCount"); 156 | #endif 157 | sendCommandWithOneByteParam(ADVANCED_REMOTE_MODE, 0x00, CMD_GET_ITEM_COUNT, itemType); 158 | } 159 | 160 | void AdvancedRemote::getItemNames(AdvancedRemote::ItemType itemType, unsigned long offset, unsigned long count) 161 | { 162 | #if defined(IPOD_SERIAL_DEBUG) 163 | log("getItemNames"); 164 | #endif 165 | sendCommandWithOneByteAndTwoNumberParams(ADVANCED_REMOTE_MODE, 0x00, CMD_GET_ITEM_NAMES, itemType, offset, count); 166 | } 167 | 168 | void AdvancedRemote::getTimeAndStatusInfo() 169 | { 170 | #if defined(IPOD_SERIAL_DEBUG) 171 | log("getTimeAndStatusInfo"); 172 | #endif 173 | sendCommand(ADVANCED_REMOTE_MODE, 0x00, CMD_GET_TIME_AND_STATUS_INFO); 174 | } 175 | 176 | void AdvancedRemote::getPlaylistPosition() 177 | { 178 | #if defined(IPOD_SERIAL_DEBUG) 179 | log("getPlaylistPosition"); 180 | #endif 181 | sendCommand(ADVANCED_REMOTE_MODE, 0x00, CMD_GET_PLAYLIST_POSITION); 182 | } 183 | 184 | void AdvancedRemote::getTitle(unsigned long index) 185 | { 186 | #if defined(IPOD_SERIAL_DEBUG) 187 | log("getTitle"); 188 | #endif 189 | sendCommandWithOneNumberParam(ADVANCED_REMOTE_MODE, 0x00, CMD_GET_TITLE, index); 190 | } 191 | 192 | void AdvancedRemote::getArtist(unsigned long index) 193 | { 194 | #if defined(IPOD_SERIAL_DEBUG) 195 | log("getArtist"); 196 | #endif 197 | sendCommandWithOneNumberParam(ADVANCED_REMOTE_MODE, 0x00, CMD_GET_ARTIST, index); 198 | } 199 | 200 | void AdvancedRemote::getAlbum(unsigned long index) 201 | { 202 | #if defined(IPOD_SERIAL_DEBUG) 203 | log("getAlbum"); 204 | #endif 205 | sendCommandWithOneNumberParam(ADVANCED_REMOTE_MODE, 0x00, CMD_GET_ALBUM, index); 206 | } 207 | 208 | void AdvancedRemote::setPollingMode(AdvancedRemote::PollingMode newMode) 209 | { 210 | #if defined(IPOD_SERIAL_DEBUG) 211 | log("setPollingMode"); 212 | #endif 213 | sendCommandWithOneByteParam(ADVANCED_REMOTE_MODE, 0x00, CMD_POLLING_MODE, newMode); 214 | } 215 | 216 | void AdvancedRemote::executeSwitch(unsigned long index) 217 | { 218 | #if defined(IPOD_SERIAL_DEBUG) 219 | log("executeSwitch"); 220 | #endif 221 | sendCommandWithOneNumberParam(ADVANCED_REMOTE_MODE, 0x00, CMD_EXECUTE_SWITCH, index); 222 | } 223 | 224 | void AdvancedRemote::controlPlayback(AdvancedRemote::PlaybackControl command) 225 | { 226 | #if defined(IPOD_SERIAL_DEBUG) 227 | log("controlPlayback"); 228 | #endif 229 | sendCommandWithOneByteParam(ADVANCED_REMOTE_MODE, 0x00, CMD_PLAYBACK_CONTROL, command); 230 | } 231 | 232 | void AdvancedRemote::getShuffleMode() 233 | { 234 | #if defined(IPOD_SERIAL_DEBUG) 235 | log("getShuffleMode"); 236 | #endif 237 | sendCommand(ADVANCED_REMOTE_MODE, 0x00, CMD_GET_SHUFFLE_MODE); 238 | } 239 | 240 | void AdvancedRemote::setShuffleMode(AdvancedRemote::ShuffleMode newMode) 241 | { 242 | #if defined(IPOD_SERIAL_DEBUG) 243 | log("setShuffleMode"); 244 | #endif 245 | sendCommandWithOneByteParam(ADVANCED_REMOTE_MODE, 0x00, CMD_SET_SHUFFLE_MODE, newMode); 246 | } 247 | 248 | void AdvancedRemote::getRepeatMode() 249 | { 250 | #if defined(IPOD_SERIAL_DEBUG) 251 | log("getRepeatMode"); 252 | #endif 253 | sendCommand(ADVANCED_REMOTE_MODE, 0x00, CMD_GET_REPEAT_MODE); 254 | } 255 | 256 | void AdvancedRemote::setRepeatMode(AdvancedRemote::RepeatMode newMode) 257 | { 258 | #if defined(IPOD_SERIAL_DEBUG) 259 | log("setRepeatMode"); 260 | #endif 261 | sendCommandWithOneByteParam(ADVANCED_REMOTE_MODE, 0x00, CMD_SET_REPEAT_MODE, newMode); 262 | } 263 | 264 | void AdvancedRemote::getSongCountInCurrentPlaylist() 265 | { 266 | #if defined(IPOD_SERIAL_DEBUG) 267 | log("getSongCountInCurrentPlaylist"); 268 | #endif 269 | sendCommand(ADVANCED_REMOTE_MODE, 0x00, CMD_GET_SONG_COUNT_IN_CURRENT_PLAYLIST); 270 | } 271 | 272 | void AdvancedRemote::jumpToSongInCurrentPlaylist(unsigned long index) 273 | { 274 | #if defined(IPOD_SERIAL_DEBUG) 275 | log("jumpToSongInCurrentPlaylist"); 276 | #endif 277 | sendCommandWithOneNumberParam(ADVANCED_REMOTE_MODE, 0x00, CMD_JUMP_TO_SONG_IN_CURRENT_PLAYLIST, index); 278 | } 279 | 280 | void AdvancedRemote::processData() 281 | { 282 | const byte mode = dataBuffer[0]; 283 | if (mode != ADVANCED_REMOTE_MODE) 284 | { 285 | #if defined(IPOD_SERIAL_DEBUG) 286 | if (pDebugPrint) 287 | { 288 | pDebugPrint->println("response not for adv mode so ignoring"); 289 | dumpReceive(); 290 | } 291 | #endif 292 | return; 293 | } 294 | 295 | const byte firstCommandByte = dataBuffer[1]; 296 | if (firstCommandByte != 0x00) 297 | { 298 | #if defined(IPOD_SERIAL_DEBUG) 299 | if (pDebugPrint) 300 | { 301 | pDebugPrint->println("1st cmd byte in response not 0x00 so ignoring"); 302 | dumpReceive(); 303 | } 304 | #endif 305 | return; 306 | } 307 | 308 | const byte secondCommandByte = dataBuffer[2]; 309 | switch (secondCommandByte) 310 | { 311 | case RESPONSE_BAD: 312 | #if defined(IPOD_SERIAL_DEBUG) 313 | if (pDebugPrint) 314 | { 315 | pDebugPrint->print("BAD Response: Result=0x"); 316 | pDebugPrint->print(dataBuffer[3], HEX); 317 | pDebugPrint->print(", Command=0x"); 318 | pDebugPrint->print(dataBuffer[4], HEX); 319 | pDebugPrint->print(", 0x"); 320 | pDebugPrint->println(dataBuffer[5], HEX); 321 | } 322 | #endif 323 | return; 324 | 325 | case RESPONSE_FEEDBACK: 326 | /* 327 | * Result: 328 | * 0=success, 329 | * 2=failure, 330 | * 4=you exceeded the limit of whatever you were requesting/wrong parameter-count, 331 | * 5=sent an iPod Response instead of a command 332 | */ 333 | #if defined(IPOD_SERIAL_DEBUG) 334 | if (pDebugPrint) 335 | { 336 | pDebugPrint->print("Feedback Response: Result=0x"); 337 | pDebugPrint->print(dataBuffer[3], HEX); 338 | pDebugPrint->print(", Command=0x"); 339 | pDebugPrint->print(dataBuffer[4], HEX); 340 | pDebugPrint->print(", 0x"); 341 | pDebugPrint->println(dataBuffer[5], HEX); 342 | } 343 | #endif 344 | if (pFeedbackHandler) 345 | { 346 | const Feedback feedback = (Feedback) dataBuffer[3]; 347 | pFeedbackHandler(feedback, dataBuffer[5]); 348 | } 349 | return; 350 | } 351 | // if we made it past that, hopefully this is a response 352 | // to a command that we sent 353 | 354 | const byte commandThisIsAResponseFor = secondCommandByte - 1; 355 | // -1 because response number is always cmd number + 1 356 | 357 | const byte *pData = &dataBuffer[3]; 358 | switch (commandThisIsAResponseFor) 359 | { 360 | case CMD_GET_IPOD_NAME: 361 | if (piPodNameHandler) 362 | { 363 | piPodNameHandler((const char *) pData); 364 | } 365 | break; 366 | 367 | case CMD_GET_ITEM_COUNT: 368 | if (pItemCountHandler) 369 | { 370 | pItemCountHandler(endianConvert(pData)); 371 | } 372 | break; 373 | 374 | case CMD_GET_ITEM_NAMES: 375 | if (pItemNameHandler) 376 | { 377 | const unsigned long itemOffset = endianConvert(pData); 378 | const char *itemName = (const char *) (pData + 4); 379 | pItemNameHandler(itemOffset, itemName); 380 | } 381 | break; 382 | 383 | case CMD_GET_TIME_AND_STATUS_INFO: 384 | if (pTimeAndStatusHandler) 385 | { 386 | const unsigned long trackLength = endianConvert(pData); 387 | const unsigned long elapsedTime = endianConvert(pData + 4); 388 | PlaybackStatus playbackStatus = (PlaybackStatus) *(pData + 8); 389 | 390 | pTimeAndStatusHandler(trackLength, elapsedTime, playbackStatus); 391 | 392 | } 393 | break; 394 | 395 | case CMD_GET_PLAYLIST_POSITION: 396 | if (pPlaylistPositionHandler) 397 | { 398 | pPlaylistPositionHandler(endianConvert(pData)); 399 | } 400 | break; 401 | 402 | case CMD_GET_TITLE: 403 | if (pTitleHandler) 404 | { 405 | pTitleHandler((const char *) pData); 406 | } 407 | break; 408 | 409 | 410 | case CMD_GET_ARTIST: 411 | if (pArtistHandler) 412 | { 413 | pArtistHandler((const char *) pData); 414 | } 415 | break; 416 | 417 | case CMD_GET_ALBUM: 418 | if (pAlbumHandler) 419 | { 420 | pAlbumHandler((const char *) pData); 421 | } 422 | break; 423 | 424 | case CMD_POLLING_MODE: 425 | if (pPollingHandler) 426 | { 427 | const PollingCommand command = (PollingCommand) pData[0]; 428 | const unsigned long number = endianConvert(pData + 1); 429 | pPollingHandler(command, number); 430 | } 431 | break; 432 | 433 | case CMD_GET_SHUFFLE_MODE: 434 | if (pShuffleModeHandler) 435 | { 436 | pShuffleModeHandler((ShuffleMode) *pData); 437 | } 438 | break; 439 | 440 | case CMD_GET_REPEAT_MODE: 441 | if (pRepeatModeHandler) 442 | { 443 | pRepeatModeHandler((RepeatMode) *pData); 444 | } 445 | break; 446 | 447 | case CMD_GET_SONG_COUNT_IN_CURRENT_PLAYLIST: 448 | if (pCurrentPlaylistSongCountHandler) 449 | { 450 | pCurrentPlaylistSongCountHandler(endianConvert(pData)); 451 | } 452 | break; 453 | 454 | #if defined(IPOD_SERIAL_DEBUG) 455 | default: 456 | if (pDebugPrint) 457 | { 458 | pDebugPrint->print("unsupported response: "); 459 | dumpReceive(); 460 | } 461 | break; 462 | #endif 463 | } 464 | } 465 | 466 | /* 467 | * iPod is big endian and arduino is little endian, 468 | * so we must byte swap the iPod's 4-byte integers 469 | * before we can use them on the arduino. 470 | * note that on arduino int is 16-bit and long is 32-bit. 471 | * TODO: Is that true of all the micrcontrollers on 472 | * arduino boards? 473 | */ 474 | unsigned long AdvancedRemote::endianConvert(const byte *p) 475 | { 476 | return 477 | (((unsigned long) p[3]) << 0) | 478 | (((unsigned long) p[2]) << 8) | 479 | (((unsigned long) p[1]) << 16) | 480 | (((unsigned long) p[0]) << 24); 481 | } 482 | 483 | bool AdvancedRemote::isCurrentlyEnabled() 484 | { 485 | return currentlyEnabled; 486 | } 487 | --------------------------------------------------------------------------------