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@16:7bb8b161e00b, 2016-03-18 (annotated)
- Committer:
- petter
- Date:
- Fri Mar 18 20:58:36 2016 +0000
- Revision:
- 16:7bb8b161e00b
- Parent:
- 15:82c3cc87bd02
Added can.monitor in a trial to get the sleep mode working
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 | 15:82c3cc87bd02 | 18 | #define RESPONSE_TIMEOUT 20 //ms |
petter | 16:7bb8b161e00b | 19 | #define METADATA_RESPONSE_TIMEOUT 100 //ms |
petter | 0:6cf6e566c0da | 20 | |
petter | 0:6cf6e566c0da | 21 | Serial serial(p13, p14); // tx, rx |
petter | 0:6cf6e566c0da | 22 | DigitalIn event_pin(p20); |
petter | 13:968af0520530 | 23 | DigitalOut enable_pin(p5); |
petter | 11:74844f6ca8cf | 24 | Timer responsetime; |
petter | 16:7bb8b161e00b | 25 | Timer metadata_responsetime; |
petter | 16:7bb8b161e00b | 26 | |
petter | 14:3f4098e94c29 | 27 | char temp_response[255]; |
petter | 11:74844f6ca8cf | 28 | |
petter | 0:6cf6e566c0da | 29 | |
petter | 0:6cf6e566c0da | 30 | void RN52::init(){ |
petter | 0:6cf6e566c0da | 31 | serial.baud(115200); |
petter | 0:6cf6e566c0da | 32 | printf("Serial baudrate set\r\n"); |
petter | 15:82c3cc87bd02 | 33 | enable(); |
petter | 0:6cf6e566c0da | 34 | } |
petter | 0:6cf6e566c0da | 35 | |
petter | 13:968af0520530 | 36 | void RN52::enable(){ |
petter | 13:968af0520530 | 37 | enable_pin = 1; |
petter | 13:968af0520530 | 38 | } |
petter | 13:968af0520530 | 39 | |
petter | 13:968af0520530 | 40 | void RN52::disable(){ |
petter | 13:968af0520530 | 41 | enable_pin = 0; |
petter | 14:3f4098e94c29 | 42 | serial.printf(REBOOT); |
petter | 14:3f4098e94c29 | 43 | capture_response(temp_response); |
petter | 13:968af0520530 | 44 | } |
petter | 13:968af0520530 | 45 | |
petter | 5:8e468fef2754 | 46 | bool RN52::check_event(RN52_RESULT * result){ |
petter | 6:c454f88524d6 | 47 | clear_serial(); |
petter | 15:82c3cc87bd02 | 48 | if((enable_pin == 1) && (event_pin == 0)) { |
petter | 7:2df2c6e8c0df | 49 | clear_result(result); |
petter | 8:beb6c399490a | 50 | get_status(result); |
petter | 5:8e468fef2754 | 51 | switch (result->event) { |
petter | 4:3041a571b7a7 | 52 | case RN52_CALLER_ID_EVENT: |
petter | 8:beb6c399490a | 53 | get_caller_id(result); |
petter | 4:3041a571b7a7 | 54 | break; |
petter | 4:3041a571b7a7 | 55 | case RN52_TRACK_CHANGE_EVENT: |
petter | 8:beb6c399490a | 56 | get_track_metadata(result); |
petter | 4:3041a571b7a7 | 57 | break; |
petter | 16:7bb8b161e00b | 58 | case RN52_OTHER_EVENT: |
petter | 16:7bb8b161e00b | 59 | if(result->connection == RN52_AUDIO_STREAMING) { |
petter | 16:7bb8b161e00b | 60 | get_track_metadata(result); |
petter | 16:7bb8b161e00b | 61 | } |
petter | 16:7bb8b161e00b | 62 | break; |
petter | 4:3041a571b7a7 | 63 | } |
petter | 4:3041a571b7a7 | 64 | return 1; |
petter | 0:6cf6e566c0da | 65 | } |
petter | 0:6cf6e566c0da | 66 | return 0; |
petter | 0:6cf6e566c0da | 67 | } |
petter | 0:6cf6e566c0da | 68 | |
petter | 5:8e468fef2754 | 69 | bool RN52::connect(){ |
petter | 5:8e468fef2754 | 70 | serial.printf(CONNECT); |
petter | 14:3f4098e94c29 | 71 | return capture_response(temp_response); |
petter | 5:8e468fef2754 | 72 | } |
petter | 0:6cf6e566c0da | 73 | |
petter | 5:8e468fef2754 | 74 | bool RN52::disconnect(){ |
petter | 5:8e468fef2754 | 75 | serial.printf(DISCONNECT); |
petter | 14:3f4098e94c29 | 76 | return capture_response(temp_response); |
petter | 5:8e468fef2754 | 77 | } |
petter | 5:8e468fef2754 | 78 | |
petter | 5:8e468fef2754 | 79 | bool RN52::next_track(){ |
petter | 5:8e468fef2754 | 80 | serial.printf(NEXTTRACK); |
petter | 14:3f4098e94c29 | 81 | return capture_response(temp_response); |
petter | 5:8e468fef2754 | 82 | } |
petter | 5:8e468fef2754 | 83 | |
petter | 5:8e468fef2754 | 84 | bool RN52::prev_track(){ |
petter | 5:8e468fef2754 | 85 | serial.printf(PREVTRACK); |
petter | 14:3f4098e94c29 | 86 | return capture_response(temp_response); |
petter | 5:8e468fef2754 | 87 | } |
petter | 5:8e468fef2754 | 88 | |
petter | 5:8e468fef2754 | 89 | bool RN52::toggle_play(){ |
petter | 5:8e468fef2754 | 90 | serial.printf(PLAYPAUSE); |
petter | 14:3f4098e94c29 | 91 | return capture_response(temp_response); |
petter | 5:8e468fef2754 | 92 | } |
petter | 5:8e468fef2754 | 93 | |
petter | 5:8e468fef2754 | 94 | bool RN52::maxvolume(){ |
petter | 6:c454f88524d6 | 95 | serial.printf(MAXVOLUME); |
petter | 14:3f4098e94c29 | 96 | return capture_response(temp_response); |
petter | 0:6cf6e566c0da | 97 | } |
petter | 0:6cf6e566c0da | 98 | |
petter | 8:beb6c399490a | 99 | bool RN52::get_status(RN52_RESULT * result){ |
petter | 8:beb6c399490a | 100 | serial.printf(GETSTATUS); |
petter | 8:beb6c399490a | 101 | capture_response(result->response); |
petter | 8:beb6c399490a | 102 | int response_value = strtoul(result->response, NULL, 16); |
petter | 8:beb6c399490a | 103 | result->media_connected = (response_value & (1 << 10)) >> 10; //byte 0, bit 2 |
petter | 8:beb6c399490a | 104 | result->phone_connected = (response_value & (1 << 11)) >> 11; //byte 0, bit 3 |
petter | 8:beb6c399490a | 105 | result->connection = (RN52_CONNECTION)(response_value & 0x0F); //byte 1, bits 0-3 |
petter | 8:beb6c399490a | 106 | switch (response_value & 0x3000) { |
petter | 8:beb6c399490a | 107 | case 0x1000: //byte 0, bit 4 |
petter | 8:beb6c399490a | 108 | result->event = RN52_CALLER_ID_EVENT; |
petter | 0:6cf6e566c0da | 109 | break; |
petter | 8:beb6c399490a | 110 | case 0x2000: //byte 0, bit 5 |
petter | 8:beb6c399490a | 111 | result->event = RN52_TRACK_CHANGE_EVENT; |
petter | 8:beb6c399490a | 112 | break; |
petter | 8:beb6c399490a | 113 | default: |
petter | 8:beb6c399490a | 114 | result->event = RN52_OTHER_EVENT; |
petter | 0:6cf6e566c0da | 115 | break; |
petter | 8:beb6c399490a | 116 | } |
petter | 8:beb6c399490a | 117 | return 1; |
petter | 8:beb6c399490a | 118 | } |
petter | 8:beb6c399490a | 119 | |
petter | 8:beb6c399490a | 120 | bool RN52::get_caller_id(RN52_RESULT * result){ |
petter | 8:beb6c399490a | 121 | serial.printf(CALLER_ID); |
petter | 8:beb6c399490a | 122 | capture_response(result->response); |
petter | 8:beb6c399490a | 123 | //parse the response |
petter | 8:beb6c399490a | 124 | return 1; |
petter | 8:beb6c399490a | 125 | } |
petter | 8:beb6c399490a | 126 | bool RN52::get_track_metadata(RN52_RESULT * result){ |
petter | 15:82c3cc87bd02 | 127 | clear_serial(); |
petter | 16:7bb8b161e00b | 128 | metadata_responsetime.start(); |
petter | 16:7bb8b161e00b | 129 | metadata_responsetime.reset(); |
petter | 8:beb6c399490a | 130 | serial.printf(TRACK_METADATA); |
petter | 16:7bb8b161e00b | 131 | capture_response(result->response);//AOK |
petter | 16:7bb8b161e00b | 132 | while(result->response[4] != '(') { //time(ms) - always the last one |
petter | 16:7bb8b161e00b | 133 | capture_response(result->response); |
petter | 16:7bb8b161e00b | 134 | switch (result->response[3]) { |
petter | 16:7bb8b161e00b | 135 | case 'l': //Title |
petter | 16:7bb8b161e00b | 136 | copy_response(result->response, result->title, 6); |
petter | 16:7bb8b161e00b | 137 | break; |
petter | 16:7bb8b161e00b | 138 | case 'i': //Artist |
petter | 16:7bb8b161e00b | 139 | copy_response(result->response, result->artist, 7); |
petter | 16:7bb8b161e00b | 140 | break; |
petter | 16:7bb8b161e00b | 141 | case 'u': //Album |
petter | 16:7bb8b161e00b | 142 | copy_response(result->response, result->album, 6); |
petter | 16:7bb8b161e00b | 143 | break; |
petter | 16:7bb8b161e00b | 144 | case 'r': //Genre |
petter | 16:7bb8b161e00b | 145 | copy_response(result->response, result->genre, 6); |
petter | 16:7bb8b161e00b | 146 | break; |
petter | 16:7bb8b161e00b | 147 | case 'c': //TrackNumber or TrackCount |
petter | 16:7bb8b161e00b | 148 | { |
petter | 16:7bb8b161e00b | 149 | char number_string[5]; |
petter | 16:7bb8b161e00b | 150 | if(result->response[5] == 'N') { //TrackNumber |
petter | 16:7bb8b161e00b | 151 | copy_response(result->response, number_string, 12); |
petter | 16:7bb8b161e00b | 152 | result->track_number = strtoul(number_string, NULL, 10); |
petter | 6:c454f88524d6 | 153 | } |
petter | 16:7bb8b161e00b | 154 | else { //TrackCount |
petter | 16:7bb8b161e00b | 155 | copy_response(result->response, number_string, 11); |
petter | 16:7bb8b161e00b | 156 | result->track_count = strtoul(number_string, NULL, 10); |
petter | 8:beb6c399490a | 157 | } |
petter | 16:7bb8b161e00b | 158 | } |
petter | 16:7bb8b161e00b | 159 | break; |
petter | 16:7bb8b161e00b | 160 | case 'e': |
petter | 16:7bb8b161e00b | 161 | { |
petter | 16:7bb8b161e00b | 162 | char duration_string[10]; |
petter | 16:7bb8b161e00b | 163 | copy_response(result->response, duration_string, 9); |
petter | 16:7bb8b161e00b | 164 | result->duration = strtoul(duration_string, NULL, 10); |
petter | 16:7bb8b161e00b | 165 | } |
petter | 16:7bb8b161e00b | 166 | break; |
petter | 8:beb6c399490a | 167 | } |
petter | 16:7bb8b161e00b | 168 | if(metadata_responsetime.read_ms() > METADATA_RESPONSE_TIMEOUT) { |
petter | 16:7bb8b161e00b | 169 | clear_serial(); |
petter | 16:7bb8b161e00b | 170 | return 0; |
petter | 16:7bb8b161e00b | 171 | } |
petter | 0:6cf6e566c0da | 172 | } |
petter | 16:7bb8b161e00b | 173 | clear_serial(); |
petter | 0:6cf6e566c0da | 174 | return 1; |
petter | 0:6cf6e566c0da | 175 | } |
petter | 0:6cf6e566c0da | 176 | |
petter | 5:8e468fef2754 | 177 | bool RN52::capture_response(char * str){ |
petter | 11:74844f6ca8cf | 178 | responsetime.reset(); |
petter | 11:74844f6ca8cf | 179 | responsetime.start(); |
petter | 0:6cf6e566c0da | 180 | char c = ' '; |
petter | 0:6cf6e566c0da | 181 | char n = 0; |
petter | 0:6cf6e566c0da | 182 | while(c != '\n') { |
petter | 0:6cf6e566c0da | 183 | if(serial.readable()) { |
petter | 0:6cf6e566c0da | 184 | c = serial.getc(); |
petter | 0:6cf6e566c0da | 185 | str[n] = c; |
petter | 0:6cf6e566c0da | 186 | n++; |
petter | 0:6cf6e566c0da | 187 | } |
petter | 11:74844f6ca8cf | 188 | else if(responsetime.read_ms() > RESPONSE_TIMEOUT) { |
petter | 11:74844f6ca8cf | 189 | clear_serial(); |
petter | 11:74844f6ca8cf | 190 | return 0; |
petter | 11:74844f6ca8cf | 191 | } |
petter | 0:6cf6e566c0da | 192 | } |
petter | 7:2df2c6e8c0df | 193 | str[n-2] = '\0'; //terminate string before \r\n |
petter | 15:82c3cc87bd02 | 194 | //return (str[0] == 'A'); |
petter | 15:82c3cc87bd02 | 195 | return 1; |
petter | 6:c454f88524d6 | 196 | } |
petter | 6:c454f88524d6 | 197 | |
petter | 6:c454f88524d6 | 198 | void RN52::clear_serial(){ |
petter | 6:c454f88524d6 | 199 | while(serial.readable()) { |
petter | 6:c454f88524d6 | 200 | serial.getc(); |
petter | 6:c454f88524d6 | 201 | } |
petter | 7:2df2c6e8c0df | 202 | } |
petter | 7:2df2c6e8c0df | 203 | |
petter | 7:2df2c6e8c0df | 204 | void RN52::copy_response(char * source, char * destination, char offset){ |
petter | 7:2df2c6e8c0df | 205 | int n = 0; |
petter | 7:2df2c6e8c0df | 206 | while(source[n+offset+1] != '\0') { //remove carraige return in the end |
petter | 7:2df2c6e8c0df | 207 | destination[n] = source[n+offset]; |
petter | 7:2df2c6e8c0df | 208 | n++; |
petter | 7:2df2c6e8c0df | 209 | } |
petter | 7:2df2c6e8c0df | 210 | destination[n] = '\0'; //end string where carriage was |
petter | 7:2df2c6e8c0df | 211 | } |
petter | 7:2df2c6e8c0df | 212 | |
petter | 7:2df2c6e8c0df | 213 | void RN52::clear_result(RN52_RESULT * result) { |
petter | 7:2df2c6e8c0df | 214 | result->event = RN52_NO_EVENT; |
petter | 7:2df2c6e8c0df | 215 | result->media_connected = 0; |
petter | 7:2df2c6e8c0df | 216 | result->phone_connected = 0; |
petter | 8:beb6c399490a | 217 | result->connection = RN52_NOT_SET; |
petter | 7:2df2c6e8c0df | 218 | result->title[0] = '\0'; |
petter | 7:2df2c6e8c0df | 219 | result->artist[0] = '\0'; |
petter | 7:2df2c6e8c0df | 220 | result->album[0] = '\0'; |
petter | 7:2df2c6e8c0df | 221 | result->genre[0] = '\0'; |
petter | 7:2df2c6e8c0df | 222 | result->duration = 0; |
petter | 9:9a4c81493a3d | 223 | result->track_number = 1; |
petter | 9:9a4c81493a3d | 224 | result->track_count = 1; |
petter | 7:2df2c6e8c0df | 225 | result->response[0] = '\0'; |
petter | 15:82c3cc87bd02 | 226 | } |
petter | 15:82c3cc87bd02 | 227 | |
petter | 15:82c3cc87bd02 | 228 | void RN52::config(char * name) { |
petter | 15:82c3cc87bd02 | 229 | bool en = enable_pin; |
petter | 15:82c3cc87bd02 | 230 | enable_pin = 1; |
petter | 15:82c3cc87bd02 | 231 | wait(0.5); //make sure booted up, not sure if needed |
petter | 15:82c3cc87bd02 | 232 | serial.printf("S%%,18E7\r"); //extended features |
petter | 16:7bb8b161e00b | 233 | serial.printf("SC,200420\r"); //device type |
petter | 15:82c3cc87bd02 | 234 | serial.printf("SN,%s\r", name); //name |
petter | 15:82c3cc87bd02 | 235 | serial.printf(REBOOT); |
petter | 15:82c3cc87bd02 | 236 | clear_serial(); |
petter | 15:82c3cc87bd02 | 237 | enable_pin = en; //restore status |
petter | 0:6cf6e566c0da | 238 | } |