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
- Committer:
- petter
- Date:
- 2016-03-18
- Revision:
- 16:7bb8b161e00b
- Parent:
- 15:82c3cc87bd02
File content as of revision 16:7bb8b161e00b:
#include "RN52.h" #include "mbed.h" // RN52 action command definitions #define PLAYPAUSE "AP\r" #define NEXTTRACK "AT+\r" #define PREVTRACK "AT-\r" #define CONNECT "B\r" #define DISCONNECT "@,1\r" #define REBOOT "R,1\r" #define VOLUMEUP "AV+\r" #define MAXVOLUME "SS,0F\r" #define GETSTATUS "Q\r" #define ASSISTANT "P\r" #define CALLER_ID "T\r" #define TRACK_METADATA "AD\r" #define RESPONSE_TIMEOUT 20 //ms #define METADATA_RESPONSE_TIMEOUT 100 //ms Serial serial(p13, p14); // tx, rx DigitalIn event_pin(p20); DigitalOut enable_pin(p5); Timer responsetime; Timer metadata_responsetime; char temp_response[255]; void RN52::init(){ serial.baud(115200); printf("Serial baudrate set\r\n"); enable(); } void RN52::enable(){ enable_pin = 1; } void RN52::disable(){ enable_pin = 0; serial.printf(REBOOT); capture_response(temp_response); } bool RN52::check_event(RN52_RESULT * result){ clear_serial(); if((enable_pin == 1) && (event_pin == 0)) { clear_result(result); get_status(result); switch (result->event) { case RN52_CALLER_ID_EVENT: get_caller_id(result); break; case RN52_TRACK_CHANGE_EVENT: get_track_metadata(result); break; case RN52_OTHER_EVENT: if(result->connection == RN52_AUDIO_STREAMING) { get_track_metadata(result); } break; } return 1; } return 0; } bool RN52::connect(){ serial.printf(CONNECT); return capture_response(temp_response); } bool RN52::disconnect(){ serial.printf(DISCONNECT); return capture_response(temp_response); } bool RN52::next_track(){ serial.printf(NEXTTRACK); return capture_response(temp_response); } bool RN52::prev_track(){ serial.printf(PREVTRACK); return capture_response(temp_response); } bool RN52::toggle_play(){ serial.printf(PLAYPAUSE); return capture_response(temp_response); } bool RN52::maxvolume(){ serial.printf(MAXVOLUME); return capture_response(temp_response); } bool RN52::get_status(RN52_RESULT * result){ serial.printf(GETSTATUS); capture_response(result->response); int response_value = strtoul(result->response, NULL, 16); result->media_connected = (response_value & (1 << 10)) >> 10; //byte 0, bit 2 result->phone_connected = (response_value & (1 << 11)) >> 11; //byte 0, bit 3 result->connection = (RN52_CONNECTION)(response_value & 0x0F); //byte 1, bits 0-3 switch (response_value & 0x3000) { case 0x1000: //byte 0, bit 4 result->event = RN52_CALLER_ID_EVENT; break; case 0x2000: //byte 0, bit 5 result->event = RN52_TRACK_CHANGE_EVENT; break; default: result->event = RN52_OTHER_EVENT; break; } return 1; } bool RN52::get_caller_id(RN52_RESULT * result){ serial.printf(CALLER_ID); capture_response(result->response); //parse the response return 1; } bool RN52::get_track_metadata(RN52_RESULT * result){ clear_serial(); metadata_responsetime.start(); metadata_responsetime.reset(); serial.printf(TRACK_METADATA); capture_response(result->response);//AOK while(result->response[4] != '(') { //time(ms) - always the last one capture_response(result->response); switch (result->response[3]) { case 'l': //Title copy_response(result->response, result->title, 6); break; case 'i': //Artist copy_response(result->response, result->artist, 7); break; case 'u': //Album copy_response(result->response, result->album, 6); break; case 'r': //Genre copy_response(result->response, result->genre, 6); break; case 'c': //TrackNumber or TrackCount { char number_string[5]; if(result->response[5] == 'N') { //TrackNumber copy_response(result->response, number_string, 12); result->track_number = strtoul(number_string, NULL, 10); } else { //TrackCount copy_response(result->response, number_string, 11); result->track_count = strtoul(number_string, NULL, 10); } } break; case 'e': { char duration_string[10]; copy_response(result->response, duration_string, 9); result->duration = strtoul(duration_string, NULL, 10); } break; } if(metadata_responsetime.read_ms() > METADATA_RESPONSE_TIMEOUT) { clear_serial(); return 0; } } clear_serial(); return 1; } bool RN52::capture_response(char * str){ responsetime.reset(); responsetime.start(); char c = ' '; char n = 0; while(c != '\n') { if(serial.readable()) { c = serial.getc(); str[n] = c; n++; } else if(responsetime.read_ms() > RESPONSE_TIMEOUT) { clear_serial(); return 0; } } str[n-2] = '\0'; //terminate string before \r\n //return (str[0] == 'A'); return 1; } void RN52::clear_serial(){ while(serial.readable()) { serial.getc(); } } void RN52::copy_response(char * source, char * destination, char offset){ int n = 0; while(source[n+offset+1] != '\0') { //remove carraige return in the end destination[n] = source[n+offset]; n++; } destination[n] = '\0'; //end string where carriage was } void RN52::clear_result(RN52_RESULT * result) { result->event = RN52_NO_EVENT; result->media_connected = 0; result->phone_connected = 0; result->connection = RN52_NOT_SET; result->title[0] = '\0'; result->artist[0] = '\0'; result->album[0] = '\0'; result->genre[0] = '\0'; result->duration = 0; result->track_number = 1; result->track_count = 1; result->response[0] = '\0'; } void RN52::config(char * name) { bool en = enable_pin; enable_pin = 1; wait(0.5); //make sure booted up, not sure if needed serial.printf("S%%,18E7\r"); //extended features serial.printf("SC,200420\r"); //device type serial.printf("SN,%s\r", name); //name serial.printf(REBOOT); clear_serial(); enable_pin = en; //restore status }