Simulated the CD changer of a Saab to implement a bluetooth connection to the car stereo. Control of playback device (phone) with steering wheel buttons. Needs a RN52 bluetooth transciever and a CAN transiever. So far only audio playback and control via steering wheel buttons implemented. Hands free calling planned.
RN52.cpp@14:3f4098e94c29, 2016-01-31 (annotated)
- Committer:
- petter
- Date:
- Sun Jan 31 20:58:42 2016 +0000
- Revision:
- 14:3f4098e94c29
- Parent:
- 13:968af0520530
- Child:
- 15:82c3cc87bd02
Power saving features on RN52 & MPC2551; RN52 track info still buggy
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
petter | 0:6cf6e566c0da | 1 | #include "RN52.h" |
petter | 0:6cf6e566c0da | 2 | #include "mbed.h" |
petter | 0:6cf6e566c0da | 3 | |
petter | 0:6cf6e566c0da | 4 | // RN52 action command definitions |
petter | 0:6cf6e566c0da | 5 | #define PLAYPAUSE "AP\r" |
petter | 0:6cf6e566c0da | 6 | #define NEXTTRACK "AT+\r" |
petter | 0:6cf6e566c0da | 7 | #define PREVTRACK "AT-\r" |
petter | 0:6cf6e566c0da | 8 | #define CONNECT "B\r" |
petter | 0:6cf6e566c0da | 9 | #define DISCONNECT "@,1\r" |
petter | 0:6cf6e566c0da | 10 | #define REBOOT "R,1\r" |
petter | 0:6cf6e566c0da | 11 | #define VOLUMEUP "AV+\r" |
petter | 0:6cf6e566c0da | 12 | #define MAXVOLUME "SS,0F\r" |
petter | 0:6cf6e566c0da | 13 | #define GETSTATUS "Q\r" |
petter | 0:6cf6e566c0da | 14 | #define ASSISTANT "P\r" |
petter | 0:6cf6e566c0da | 15 | #define CALLER_ID "T\r" |
petter | 0:6cf6e566c0da | 16 | #define TRACK_METADATA "AD\r" |
petter | 0:6cf6e566c0da | 17 | |
petter | 11:74844f6ca8cf | 18 | #define RESPONSE_TIMEOUT 30 //ms |
petter | 0:6cf6e566c0da | 19 | |
petter | 0:6cf6e566c0da | 20 | Serial serial(p13, p14); // tx, rx |
petter | 0:6cf6e566c0da | 21 | DigitalIn event_pin(p20); |
petter | 13:968af0520530 | 22 | DigitalOut enable_pin(p5); |
petter | 11:74844f6ca8cf | 23 | Timer responsetime; |
petter | 14:3f4098e94c29 | 24 | char temp_response[255]; |
petter | 11:74844f6ca8cf | 25 | |
petter | 0:6cf6e566c0da | 26 | |
petter | 0:6cf6e566c0da | 27 | void RN52::init(){ |
petter | 13:968af0520530 | 28 | enable(); |
petter | 0:6cf6e566c0da | 29 | serial.baud(115200); |
petter | 0:6cf6e566c0da | 30 | printf("Serial baudrate set\r\n"); |
petter | 6:c454f88524d6 | 31 | |
petter | 6:c454f88524d6 | 32 | //make sure event pin is pulled low |
petter | 6:c454f88524d6 | 33 | if(event_pin == 0) { |
petter | 6:c454f88524d6 | 34 | serial.printf(GETSTATUS); |
petter | 14:3f4098e94c29 | 35 | capture_response(temp_response); |
petter | 6:c454f88524d6 | 36 | } |
petter | 0:6cf6e566c0da | 37 | } |
petter | 0:6cf6e566c0da | 38 | |
petter | 13:968af0520530 | 39 | void RN52::enable(){ |
petter | 13:968af0520530 | 40 | enable_pin = 1; |
petter | 13:968af0520530 | 41 | } |
petter | 13:968af0520530 | 42 | |
petter | 13:968af0520530 | 43 | void RN52::disable(){ |
petter | 13:968af0520530 | 44 | enable_pin = 0; |
petter | 14:3f4098e94c29 | 45 | serial.printf(REBOOT); |
petter | 14:3f4098e94c29 | 46 | capture_response(temp_response); |
petter | 13:968af0520530 | 47 | } |
petter | 13:968af0520530 | 48 | |
petter | 5:8e468fef2754 | 49 | bool RN52::check_event(RN52_RESULT * result){ |
petter | 6:c454f88524d6 | 50 | clear_serial(); |
petter | 6:c454f88524d6 | 51 | if(event_pin == 0) { |
petter | 7:2df2c6e8c0df | 52 | clear_result(result); |
petter | 8:beb6c399490a | 53 | get_status(result); |
petter | 5:8e468fef2754 | 54 | switch (result->event) { |
petter | 4:3041a571b7a7 | 55 | case RN52_CALLER_ID_EVENT: |
petter | 8:beb6c399490a | 56 | get_caller_id(result); |
petter | 4:3041a571b7a7 | 57 | break; |
petter | 4:3041a571b7a7 | 58 | case RN52_TRACK_CHANGE_EVENT: |
petter | 8:beb6c399490a | 59 | get_track_metadata(result); |
petter | 4:3041a571b7a7 | 60 | break; |
petter | 4:3041a571b7a7 | 61 | } |
petter | 4:3041a571b7a7 | 62 | return 1; |
petter | 0:6cf6e566c0da | 63 | } |
petter | 0:6cf6e566c0da | 64 | return 0; |
petter | 0:6cf6e566c0da | 65 | } |
petter | 0:6cf6e566c0da | 66 | |
petter | 5:8e468fef2754 | 67 | bool RN52::connect(){ |
petter | 5:8e468fef2754 | 68 | serial.printf(CONNECT); |
petter | 14:3f4098e94c29 | 69 | return capture_response(temp_response); |
petter | 5:8e468fef2754 | 70 | } |
petter | 0:6cf6e566c0da | 71 | |
petter | 5:8e468fef2754 | 72 | bool RN52::disconnect(){ |
petter | 5:8e468fef2754 | 73 | serial.printf(DISCONNECT); |
petter | 14:3f4098e94c29 | 74 | return capture_response(temp_response); |
petter | 5:8e468fef2754 | 75 | } |
petter | 5:8e468fef2754 | 76 | |
petter | 5:8e468fef2754 | 77 | bool RN52::next_track(){ |
petter | 5:8e468fef2754 | 78 | serial.printf(NEXTTRACK); |
petter | 14:3f4098e94c29 | 79 | return capture_response(temp_response); |
petter | 5:8e468fef2754 | 80 | } |
petter | 5:8e468fef2754 | 81 | |
petter | 5:8e468fef2754 | 82 | bool RN52::prev_track(){ |
petter | 5:8e468fef2754 | 83 | serial.printf(PREVTRACK); |
petter | 14:3f4098e94c29 | 84 | return capture_response(temp_response); |
petter | 5:8e468fef2754 | 85 | } |
petter | 5:8e468fef2754 | 86 | |
petter | 5:8e468fef2754 | 87 | bool RN52::toggle_play(){ |
petter | 5:8e468fef2754 | 88 | serial.printf(PLAYPAUSE); |
petter | 14:3f4098e94c29 | 89 | return capture_response(temp_response); |
petter | 5:8e468fef2754 | 90 | } |
petter | 5:8e468fef2754 | 91 | |
petter | 5:8e468fef2754 | 92 | bool RN52::maxvolume(){ |
petter | 6:c454f88524d6 | 93 | serial.printf(MAXVOLUME); |
petter | 14:3f4098e94c29 | 94 | return capture_response(temp_response); |
petter | 0:6cf6e566c0da | 95 | } |
petter | 0:6cf6e566c0da | 96 | |
petter | 8:beb6c399490a | 97 | bool RN52::get_status(RN52_RESULT * result){ |
petter | 8:beb6c399490a | 98 | serial.printf(GETSTATUS); |
petter | 8:beb6c399490a | 99 | capture_response(result->response); |
petter | 8:beb6c399490a | 100 | int response_value = strtoul(result->response, NULL, 16); |
petter | 8:beb6c399490a | 101 | result->media_connected = (response_value & (1 << 10)) >> 10; //byte 0, bit 2 |
petter | 8:beb6c399490a | 102 | result->phone_connected = (response_value & (1 << 11)) >> 11; //byte 0, bit 3 |
petter | 8:beb6c399490a | 103 | result->connection = (RN52_CONNECTION)(response_value & 0x0F); //byte 1, bits 0-3 |
petter | 8:beb6c399490a | 104 | switch (response_value & 0x3000) { |
petter | 8:beb6c399490a | 105 | case 0x1000: //byte 0, bit 4 |
petter | 8:beb6c399490a | 106 | result->event = RN52_CALLER_ID_EVENT; |
petter | 0:6cf6e566c0da | 107 | break; |
petter | 8:beb6c399490a | 108 | case 0x2000: //byte 0, bit 5 |
petter | 8:beb6c399490a | 109 | result->event = RN52_TRACK_CHANGE_EVENT; |
petter | 8:beb6c399490a | 110 | break; |
petter | 8:beb6c399490a | 111 | default: |
petter | 8:beb6c399490a | 112 | result->event = RN52_OTHER_EVENT; |
petter | 0:6cf6e566c0da | 113 | break; |
petter | 8:beb6c399490a | 114 | } |
petter | 8:beb6c399490a | 115 | return 1; |
petter | 8:beb6c399490a | 116 | } |
petter | 8:beb6c399490a | 117 | |
petter | 8:beb6c399490a | 118 | bool RN52::get_caller_id(RN52_RESULT * result){ |
petter | 8:beb6c399490a | 119 | serial.printf(CALLER_ID); |
petter | 8:beb6c399490a | 120 | capture_response(result->response); |
petter | 8:beb6c399490a | 121 | //parse the response |
petter | 8:beb6c399490a | 122 | return 1; |
petter | 8:beb6c399490a | 123 | } |
petter | 8:beb6c399490a | 124 | |
petter | 11:74844f6ca8cf | 125 | |
petter | 8:beb6c399490a | 126 | bool RN52::get_track_metadata(RN52_RESULT * result){ |
petter | 8:beb6c399490a | 127 | serial.printf(TRACK_METADATA); |
petter | 8:beb6c399490a | 128 | if(capture_response(result->response)) {//AOK |
petter | 14:3f4098e94c29 | 129 | while(result->response[3] != 'e') { //time(ms) - always the last one |
petter | 8:beb6c399490a | 130 | capture_response(result->response); |
petter | 8:beb6c399490a | 131 | switch (result->response[3]) { |
petter | 8:beb6c399490a | 132 | case 'l': //Title |
petter | 8:beb6c399490a | 133 | copy_response(result->response, result->title, 6); |
petter | 8:beb6c399490a | 134 | break; |
petter | 8:beb6c399490a | 135 | case 'i': //Artist |
petter | 8:beb6c399490a | 136 | copy_response(result->response, result->artist, 7); |
petter | 8:beb6c399490a | 137 | break; |
petter | 8:beb6c399490a | 138 | case 'u': //Album |
petter | 8:beb6c399490a | 139 | copy_response(result->response, result->album, 6); |
petter | 8:beb6c399490a | 140 | break; |
petter | 8:beb6c399490a | 141 | case 'r': //Genre |
petter | 8:beb6c399490a | 142 | copy_response(result->response, result->genre, 6); |
petter | 8:beb6c399490a | 143 | break; |
petter | 8:beb6c399490a | 144 | case 'c': //TrackNumber or TrackCount |
petter | 8:beb6c399490a | 145 | { |
petter | 8:beb6c399490a | 146 | char number_string[5]; |
petter | 8:beb6c399490a | 147 | if(result->response[5] == 'N') { //TrackNumber |
petter | 8:beb6c399490a | 148 | copy_response(result->response, number_string, 12); |
petter | 8:beb6c399490a | 149 | result->track_number = strtoul(number_string, NULL, 10); |
petter | 8:beb6c399490a | 150 | } |
petter | 8:beb6c399490a | 151 | else { //TrackCount |
petter | 8:beb6c399490a | 152 | copy_response(result->response, number_string, 11); |
petter | 8:beb6c399490a | 153 | result->track_count = strtoul(number_string, NULL, 10); |
petter | 6:c454f88524d6 | 154 | } |
petter | 6:c454f88524d6 | 155 | } |
petter | 8:beb6c399490a | 156 | break; |
petter | 14:3f4098e94c29 | 157 | case 'e': //time(ms) - always the last one |
petter | 8:beb6c399490a | 158 | { |
petter | 8:beb6c399490a | 159 | char duration_string[10]; |
petter | 8:beb6c399490a | 160 | copy_response(result->response, duration_string, 9); |
petter | 8:beb6c399490a | 161 | result->duration = strtoul(duration_string, NULL, 10); |
petter | 8:beb6c399490a | 162 | } |
petter | 8:beb6c399490a | 163 | break; |
petter | 8:beb6c399490a | 164 | } |
petter | 8:beb6c399490a | 165 | } |
petter | 8:beb6c399490a | 166 | clear_serial(); |
petter | 0:6cf6e566c0da | 167 | } |
petter | 0:6cf6e566c0da | 168 | return 1; |
petter | 0:6cf6e566c0da | 169 | } |
petter | 0:6cf6e566c0da | 170 | |
petter | 5:8e468fef2754 | 171 | bool RN52::capture_response(char * str){ |
petter | 11:74844f6ca8cf | 172 | responsetime.reset(); |
petter | 11:74844f6ca8cf | 173 | responsetime.start(); |
petter | 0:6cf6e566c0da | 174 | char c = ' '; |
petter | 0:6cf6e566c0da | 175 | char n = 0; |
petter | 0:6cf6e566c0da | 176 | while(c != '\n') { |
petter | 0:6cf6e566c0da | 177 | if(serial.readable()) { |
petter | 0:6cf6e566c0da | 178 | c = serial.getc(); |
petter | 0:6cf6e566c0da | 179 | str[n] = c; |
petter | 0:6cf6e566c0da | 180 | n++; |
petter | 0:6cf6e566c0da | 181 | } |
petter | 11:74844f6ca8cf | 182 | else if(responsetime.read_ms() > RESPONSE_TIMEOUT) { |
petter | 11:74844f6ca8cf | 183 | clear_serial(); |
petter | 11:74844f6ca8cf | 184 | printf("timeout occured\r\n last response: %s\r\n", str); |
petter | 11:74844f6ca8cf | 185 | return 0; |
petter | 11:74844f6ca8cf | 186 | } |
petter | 0:6cf6e566c0da | 187 | } |
petter | 7:2df2c6e8c0df | 188 | str[n-2] = '\0'; //terminate string before \r\n |
petter | 5:8e468fef2754 | 189 | return (str[0] == 'A'); |
petter | 6:c454f88524d6 | 190 | } |
petter | 6:c454f88524d6 | 191 | |
petter | 6:c454f88524d6 | 192 | void RN52::clear_serial(){ |
petter | 6:c454f88524d6 | 193 | while(serial.readable()) { |
petter | 6:c454f88524d6 | 194 | serial.getc(); |
petter | 6:c454f88524d6 | 195 | } |
petter | 7:2df2c6e8c0df | 196 | } |
petter | 7:2df2c6e8c0df | 197 | |
petter | 7:2df2c6e8c0df | 198 | void RN52::copy_response(char * source, char * destination, char offset){ |
petter | 7:2df2c6e8c0df | 199 | int n = 0; |
petter | 7:2df2c6e8c0df | 200 | while(source[n+offset+1] != '\0') { //remove carraige return in the end |
petter | 7:2df2c6e8c0df | 201 | destination[n] = source[n+offset]; |
petter | 7:2df2c6e8c0df | 202 | n++; |
petter | 7:2df2c6e8c0df | 203 | } |
petter | 7:2df2c6e8c0df | 204 | destination[n] = '\0'; //end string where carriage was |
petter | 7:2df2c6e8c0df | 205 | } |
petter | 7:2df2c6e8c0df | 206 | |
petter | 7:2df2c6e8c0df | 207 | void RN52::clear_result(RN52_RESULT * result) { |
petter | 7:2df2c6e8c0df | 208 | result->event = RN52_NO_EVENT; |
petter | 7:2df2c6e8c0df | 209 | result->media_connected = 0; |
petter | 7:2df2c6e8c0df | 210 | result->phone_connected = 0; |
petter | 8:beb6c399490a | 211 | result->connection = RN52_NOT_SET; |
petter | 7:2df2c6e8c0df | 212 | result->title[0] = '\0'; |
petter | 7:2df2c6e8c0df | 213 | result->artist[0] = '\0'; |
petter | 7:2df2c6e8c0df | 214 | result->album[0] = '\0'; |
petter | 7:2df2c6e8c0df | 215 | result->genre[0] = '\0'; |
petter | 7:2df2c6e8c0df | 216 | result->duration = 0; |
petter | 9:9a4c81493a3d | 217 | result->track_number = 1; |
petter | 9:9a4c81493a3d | 218 | result->track_count = 1; |
petter | 7:2df2c6e8c0df | 219 | result->response[0] = '\0'; |
petter | 0:6cf6e566c0da | 220 | } |