BlueBus supports two different Bluetooth modules through a unified abstraction layer. The choice of module depends on the hardware version:
- BC127 (Sierra Wireless) - Hardware v1.x
- BM83 (Microchip) - Hardware v2.x
Module abstraction
The bt.c file provides a hardware-agnostic API that automatically routes calls to the appropriate module:
void BTCommandPlay(BT_t *bt)
{
if (bt->type == BT_BTM_TYPE_BC127) {
BC127CommandPlay(bt);
} else {
BM83CommandMusicControl(bt, BM83_CMD_ACTION_PLAY);
}
}
This abstraction allows the rest of the codebase to work identically regardless of which Bluetooth module is installed.
Module detection
The module type is determined by reading the board version pin during initialization:
BT_t BTInit()
{
BT_t bt;
bt.type = UtilsGetBoardVersion();
// Hardware v1 = BC127, Hardware v2 = BM83
}
BC127 implementation
The BC127 is a text-based AT command module from Sierra Wireless.
Communication protocol
The BC127 uses ASCII commands terminated with carriage return (0x0D):
#define BC127_MSG_END_CHAR 0x0D
#define BC127_MSG_DELIMETER 0x20 // Space character
void BC127SendCommand(BT_t *bt, char *command)
{
uint8_t length = strlen(command);
CharQueueWrite(&bt->uart.txQueue, (uint8_t *) command, length);
CharQueueWrite(&bt->uart.txQueue, (uint8_t *) "\r", 1);
}
Common commands
The BC127 implements numerous AT-style commands:
Connection
Audio control
Hands-free
// Open A2DP profile to device
OPEN {mac_address} A2DP
// Close specific connection
CLOSE {link_id}
// List paired devices
LIST
// Set discoverable/connectable state
BT_STATE {connectable} {discoverable}
// Play/pause
MUSIC PLAY {link_id}
MUSIC PAUSE {link_id}
// Track navigation
MUSIC FORWARD {link_id}
MUSIC BACKWARD {link_id}
// Get metadata
METADATA 0x03 {link_id} // Album
METADATA 0x01 {link_id} // Title
METADATA 0x02 {link_id} // Artist
// Call control
CALL {link_id} ANSWER
CALL {link_id} END
CALL {link_id} OUTGOING {number}
// Voice recognition
AT+BVRA=1 // Start VR
AT+BVRA=0 // Stop VR
// AT commands
AT+BLDN // Redial last number
Event handling
The BC127 sends unsolicited event messages:
void BC127Process(BT_t *bt)
{
while (CharQueueGetSize(&bt->uart.rxQueue) > 0) {
char c = CharQueueNext(&bt->uart.rxQueue);
if (c == BC127_MSG_END_CHAR) {
// Parse complete message
if (strncmp(message, "OPEN_OK", 7) == 0) {
BC127ProcessEventOpenOk(bt, tokens);
} else if (strncmp(message, "AVRCP_PLAY", 10) == 0) {
BC127ProcessEventAVRCPPlay(bt, tokens);
}
}
}
}
Key events include:
- OPEN_OK: Profile connection established
- CLOSE_OK: Connection closed
- AVRCP_PLAY: Playback started
- AVRCP_PAUSE: Playback paused
- CALL_ACTIVE: Active call
- METADATA: Track information received
Profile management
The BC127 tracks up to 9 active profiles per device:
typedef struct BTConnection_t {
uint8_t deviceId; // Device index
uint8_t linkId; // Link ID for this profile
uint8_t profiles[BT_PROFILE_COUNT];
} BTConnection_t;
Profiles include:
- A2DP (Audio streaming)
- AVRCP (Remote control)
- HFP (Hands-free)
- PBAP (Phonebook access)
- MAP (Message access)
BM83 implementation
The BM83 uses a binary packet protocol instead of text commands.
Packet structure
BM83 packets have a fixed header structure:
[0xAA] [Length MSB] [Length LSB] [Command] [Data...] [Checksum]
- Start byte: Always 0xAA
- Length: 16-bit big-endian length of data + command
- Command: Single-byte command code
- Data: Variable length payload (max 636 bytes)
- Checksum: Sum of all bytes except start byte and checksum itself
Command examples
Music control
Connection
PBAP
void BM83CommandMusicControl(BT_t *bt, uint8_t action)
{
uint8_t packet[] = {
0x00, // Database index
action // Action code
};
BM83SendCommand(bt, BM83_CMD_MUSIC_CONTROL, packet, 2);
}
// Actions:
// BM83_CMD_ACTION_PLAY 0x05
// BM83_CMD_ACTION_PAUSE 0x06
// BM83_CMD_ACTION_NEXT 0x09
// BM83_CMD_ACTION_PREVIOUS 0x0A
void BM83CommandConnect(BT_t *bt, BTPairedDevice_t *dev, uint8_t profiles)
{
uint8_t packet[] = {
BM83_DATA_LINK_BACK_MAC_ID,
profiles,
dev->macId[0],
dev->macId[1],
dev->macId[2],
dev->macId[3],
dev->macId[4],
dev->macId[5]
};
BM83SendCommand(bt, BM83_CMD_PROFILES_LINK_BACK, packet, 8);
}
void BM83CommandPBAPGetPhonebook(BT_t *bt, uint8_t type,
uint16_t startIndex, uint8_t maxList)
{
uint8_t packet[] = {
BM83_PBAP_SUBCMD_PULL_PHONEBOOK,
BM83_PBAP_REPO_TELECOM,
type,
BM83_PBAP_ORDER_INDEXED,
0x00, // Search value length
0x00, // Search attribute
(startIndex >> 8) & 0xFF,
startIndex & 0xFF,
maxList,
BM83_PBAP_FORMAT_VCARD21
};
BM83SendCommand(bt, BM83_CMD_PBAPC_CMD, packet, 10);
}
Event processing
The BM83 sends binary event packets:
void BM83Process(BT_t *bt)
{
// Parse packet header
if (startByte == BM83_UART_START_WORD) {
uint16_t length = (rxQueue[1] << 8) | rxQueue[2];
uint8_t command = rxQueue[3];
switch (command) {
case BM83_EVT_BTM_STATUS:
BM83ProcessEventBTMStatus(bt, data, length);
break;
case BM83_EVT_CALL_STATUS:
BM83ProcessEventCallStatus(bt, data, length);
break;
case BM83_EVT_AVC_SPECIFIC_RSP:
BM83ProcessEventAVCSpecificRsp(bt, data, length);
break;
}
}
}
The BM83 retrieves metadata through AVRCP vendor-specific commands:
void BM83CommandAVRCPGetElementAttributesAll(BT_t *bt)
{
uint8_t packet[] = {
BM83_AVRCP_PDU_GET_ELEMENT_ATTRIBUTES,
0x00 // Get all attributes
};
BM83SendCommand(bt, BM83_CMD_AVC_VENDOR_DEPENDENT_CMD, packet, 2);
}
Metadata attributes:
- 0x01 - Title
- 0x02 - Artist
- 0x03 - Album
- 0x04 - Track number
- 0x05 - Total tracks
- 0x06 - Genre
- 0x07 - Playing time
Shared structures
Both modules share common data structures defined in bt_common.h:
Bluetooth context
typedef struct BT_t {
UART_t uart;
uint8_t type; // BC127 or BM83
uint8_t status; // Connection status
uint8_t connectable; // Connectable state
uint8_t discoverable; // Discoverable/pairing state
uint8_t callStatus; // Call state
uint8_t playbackStatus; // Playing/paused
char title[BT_METADATA_FIELD_SIZE];
char artist[BT_METADATA_FIELD_SIZE];
char album[BT_METADATA_FIELD_SIZE];
char callerId[BT_CALLER_ID_FIELD_SIZE];
BTPairedDevice_t pairedDevices[BT_MAX_PAIRINGS];
BTActiveDevice_t activeDevice;
BTPBAP_t pbap;
} BT_t;
Paired device
typedef struct BTPairedDevice_t {
uint8_t macId[BT_LEN_MAC_ID];
char deviceName[BT_DEVICE_NAME_LEN];
uint8_t number; // Index in pairing list
} BTPairedDevice_t;
Active device
typedef struct BTActiveDevice_t {
uint8_t deviceIndex; // Index in pairedDevices array
uint8_t aclId; // ACL connection ID
uint8_t a2dpId; // A2DP link ID
uint8_t avrcpId; // AVRCP link ID
uint8_t hfpId; // HFP link ID
uint8_t pbapId; // PBAP link ID
} BTActiveDevice_t;
The active device structure tracks individual profile connection IDs, allowing the system to manage multiple simultaneous profiles with a single device.
PBAP integration
Both modules support Phone Book Access Profile (PBAP) for contact management.
PBAP session
typedef struct BTPBAP_t {
uint8_t active: 1; // Session active
uint8_t status: 2; // Idle/pending/waiting
uint8_t contactCount; // Contacts in buffer
uint8_t contactIdx; // Current contact index
BTPBAPContact_t contacts[BT_PBAP_MAX_CONTACTS];
BTPBAPParserState_t parser; // vCard parser state
} BTPBAP_t;
typedef struct BTPBAPContact_t {
char name[BT_PBAP_CONTACT_NAME_LEN];
BTPBAPContactTelephone_t numbers[BT_PBAP_CONTACT_MAX_NUMBERS];
uint8_t numberCount;
} BTPBAPContact_t;
typedef struct BTPBAPContactTelephone_t {
uint8_t number[8]; // BCD encoded
uint8_t type; // Cell/home/work
} BTPBAPContactTelephone_t;
Phonebook types
BlueBus can access multiple phonebooks:
#define BT_PBAP_OBJ_PHONEBOOK 0x00 // Main contacts
#define BT_PBAP_OBJ_INCOMING 0x01 // Incoming calls
#define BT_PBAP_OBJ_OUTGOING 0x02 // Outgoing calls
#define BT_PBAP_OBJ_MISSED 0x03 // Missed calls
#define BT_PBAP_OBJ_COMBINED 0x04 // All calls
#define BT_PBAP_OBJ_FAVORITES 0x05 // Favorites
#define BT_PBAP_OBJ_SPEEDDIAL 0x06 // Speed dial
vCard parsing
Contacts are received in vCard 2.1 or 3.0 format:
BEGIN:VCARD
VERSION:2.1
N:Doe;John;;;
TEL;CELL:+1234567890
TEL;HOME:+0987654321
END:VCARD
The parser (BTPBAPParserState_t) handles:
- Multi-line vCard entries
- Fragmented packet reassembly
- Multiple phone numbers per contact
- Number type detection (cell/home/work)
Call management
Both modules support hands-free calling:
Call states
#define BT_CALL_INACTIVE 0
#define BT_CALL_ACTIVE 1
#define BT_CALL_VR 2 // Voice recognition
#define BT_CALL_INCOMING 3
#define BT_CALL_OUTGOING 4
SCO audio
SCO (Synchronous Connection-Oriented) links carry voice audio:
#define BT_CALL_SCO_CLOSE 5
#define BT_CALL_SCO_OPEN 6
When SCO opens, BlueBus:
- Switches audio routing to Bluetooth
- Enables microphone input
- Adjusts gain levels
- Displays call information on vehicle displays
Caller ID
Caller ID information is stored and displayed:
char callerId[BT_CALLER_ID_FIELD_SIZE];
// For incoming calls, caller ID from phone
// For outgoing calls, dialed number
// For voice recognition, localized "Voice Assistant" string
Audio configuration
BlueBus configures Bluetooth audio output differently per module:
BC127 audio
The BC127 supports both analog and S/PDIF output:
void BC127CommandSetAudioDigital(BT_t *bt,
char *sampleRate, // "48000"
char *bits, // "16" or "24"
char *mode, // "0" = stereo
char *route // "2" = S/PDIF
);
BM83 audio
The BM83 outputs I2S audio which is converted to S/PDIF:
// Audio flows: BM83 -> I2S -> DIT4096 -> S/PDIF -> PCM5122 DAC
The PCM5122 DAC is configured via I2C in pcm51xx.c.
Event system integration
Bluetooth events trigger callbacks registered by the handler:
// In handler initialization:
EventRegisterCallback(
BT_EVENT_DEVICE_CONNECTED,
&HandlerBTDeviceConnected,
&Context
);
// In BT module:
EventTriggerCallback(BT_EVENT_DEVICE_CONNECTED, &deviceId);
Common Bluetooth events:
BT_EVENT_BOOT - Module initialized
BT_EVENT_DEVICE_CONNECTED - Device connected
BT_EVENT_DEVICE_DISCONNECTED - Device disconnected
BT_EVENT_PLAYBACK_STATUS_CHANGE - Play/pause state changed
BT_EVENT_METADATA_UPDATE - Track info received
BT_EVENT_CALL_STATUS_UPDATE - Call state changed
BT_EVENT_CALLER_ID_UPDATE - Caller ID received
BT_EVENT_PBAP_CONTACT_RECEIVED - Contact downloaded
This event-driven architecture allows clean separation between Bluetooth protocol handling and vehicle integration logic.