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