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.

Dependencies:   mbed

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
}