LE Audio BASS Control Point Explained: Opcodes, Error Codes, and Broadcast Receive Workflows

Technical Specification Snapshot

Parameter Description
Technical Domain Bluetooth LE Audio / BASS
Key Roles Client, BASS Server, Broadcast Source
Transport Protocols GATT / ATT / Periodic Advertising / BIS
Control Characteristic Broadcast Audio Scan Control Point
State Characteristic Broadcast Receive State
Security Requirement Encrypted connection required
Write Methods Write, Write Without Response, Long Write
Core Dependencies Bluetooth Core Spec 5.2+, BAP, PAST
Star Rating Not provided in the source
Primary Languages Protocol specification / embedded implementation, commonly C/C++

The BASS design uses a single control entry point and separates state across multiple instances

This article focuses on the LE Audio BASS control point. It explains why the control point serves as the only command entry for broadcast reception, how the six opcodes and error rules work, and how developers can handle broadcast source synchronization, key delivery, and low-power coordination.

In the LE Audio broadcast receive architecture, BASS defines only two core characteristics: the control point accepts commands, and the state characteristic reports broadcast source status. This split reduces implementation complexity for low-power devices and makes it easier for clients to build a unified control model.

The control point can have only one instance, while Broadcast Receive State can have multiple instances. The former guarantees a single command console and avoids conflicting writes. The latter supports parallel management of multiple broadcast sources, which fits multi-source scenarios such as TVs, phones, and public broadcast systems.

AI Visual Insight: This figure shows the attribute matrix for the two BASS characteristics. It should highlight the control point’s write capabilities, the state characteristic’s read and notify capabilities, and the differences in permissions and instance count, reflecting how the protocol organizes interaction as a “single write entry + multiple state outputs.”

The control point and state characteristics have clearly separated responsibilities

The control point handles only command writes and does not support state queries. The state characteristic carries only broadcast source state and does not accept control commands. This boundary makes firmware layering cleaner and reduces coupling in the synchronization state machine.

// Minimal BASS service abstraction: one control point, multiple state instances
struct bass_service {
    control_point_t cp;          // Single control point: receives all client commands
    recv_state_t states[4];      // Multiple state instances: each instance maps to one broadcast source
};

This code shows that BASS naturally fits a data model built around a single command entry and multiple source state slots.

The control point command format drives all behavior with an 8-bit opcode

All control point commands use a format of a 1-byte opcode followed by variable-length parameters. After the server receives a write, it parses the first byte, then validates length, parameters, and execution conditions based on the opcode.

This format has two clear advantages. First, it keeps parsing costs low for low-power devices. Second, an 8-bit opcode leaves enough room for future extension. The current specification defines only six commands from 0x00 to 0x05. Values from 0x06 to 0xFF are RFU and must not be used.

AI Visual Insight: This figure should show the byte-level structure of the control point payload, with the first byte as the opcode and the remaining bytes as the parameter area. It typically emphasizes the fixed header and variable tail layout to illustrate the server’s parse strategy: dispatch by opcode first, then validate parameters.

Six opcodes cover the full broadcast receive lifecycle

0x00 and 0x01 manage remote scanning state, 0x02 adds a broadcast source, 0x03 modifies a broadcast source, 0x04 delivers the decryption key, and 0x05 removes a broadcast source. Among them, 0x04 Set Broadcast_Code is the only mandatory operation.

0x00 Remote Scan Stopped
0x01 Remote Scan Started
0x02 Add Source
0x03 Modify Source
0x04 Set Broadcast_Code
0x05 Remove Source

This opcode set covers the full path from discovery and synchronization to decryption and cleanup.

Error handling rules directly determine interoperability quality

You must understand BASS error handling together with GATT write semantics. A write with response can explicitly return an ATT Error Response. A write without response usually can only ignore an invalid operation.

The four most common error classes are invalid length, unsupported opcode, invalid Source_ID, and BIS_Sync parameter conflicts. Only the first three can return explicit error feedback in specific scenarios. BIS_Sync conflicts are typically ignored.

Two application-level error codes deserve special attention

0x80 means Opcode Not Supported, and 0x81 means Invalid Source_ID. These are the most useful signals when debugging BASS interactions.

int bass_validate(uint8_t opcode, uint8_t source_id) {
    if (!is_supported_opcode(opcode)) {
        return 0x80; // Opcode not supported
    }
    if (!is_valid_source_id(source_id)) {
        return 0x81; // Invalid Source_ID
    }
    return 0x00;     // Validation passed
}

This code reflects the typical validation order in a control point implementation: validate the opcode first, then validate the resource identifier.

Add Source is the most complex and most critical command

Add Source passes the full broadcast source information to the server and requests synchronization to PA and BIS. It includes fields such as address type, device address, Advertising SID, Broadcast_ID, PA_Sync, PA_Interval, subgroup count, BIS_Sync, and Metadata.

After the server accepts this command, it does not start audio playback immediately. Instead, it allocates a state instance, generates a Source_ID, stores the base information, performs PA synchronization, performs BIS synchronization, and then updates encryption state.

AI Visual Insight: This figure should present the field-level structure of Add Source, including broadcast source identity fields, PA synchronization fields, multiple subgroup BIS bitmaps, and metadata sections. It emphasizes that this command has the longest parameter set in the control point and is the one most likely to require Long Write.

PA synchronization is a prerequisite for BIS synchronization

Without successful PA synchronization, the server cannot obtain the synchronization information required for BIS, so it will not start BIS synchronization. This dependency means you should check PA_Sync_State first during debugging before deciding whether BIS_Sync_State is abnormal.

if (pa_sync_success) {
    start_bis_sync();   // PA must succeed before BIS
} else {
    defer_bis_sync();   // Do not start BIS sync until PA is ready
}

This logic captures the most important state machine constraint after Add Source.

Modify, Set Broadcast_Code, and Remove Source handle updates, decryption, and cleanup

Modify Source uses Source_ID to locate an existing broadcast source. It updates only synchronization settings and metadata and does not create a new instance. It is suitable for adjusting monitored subgroups, stopping synchronization, or switching synchronization strategy.

Set Broadcast_Code delivers a 16-byte key to the server for decrypting encrypted BIS streams. It is defined as the only mandatory operation because encrypted broadcast is a core LE Audio scenario, and devices must support decryption.

AI Visual Insight: This figure should show the minimal parameter structure of Set Broadcast_Code: Opcode, Source_ID, and a 16-byte Broadcast_Code. The key takeaway is that the structure is simple, but its security level is the highest, so it must run over an encrypted GATT connection.

Remove Source releases a state instance, but it has one important restriction: you can remove the target broadcast source only when it is not in PA/BIS synchronization state. Otherwise, the server should reject the operation.

You must stop synchronization before reclaiming a broadcast source

Many implementations fail because they call Remove Source directly. The correct order is to use Modify Source to disable PA/BIS synchronization first, and then call Remove Source to reclaim resources.

// Correct removal sequence
modify_source_stop_sync(source_id); // Stop PA/BIS synchronization first
remove_source(source_id);           // Then remove the state instance

This sequencing avoids state corruption caused by deleting a source while synchronization is still active.

Real interaction flows show the value of phone-to-device coordination

A typical scenario uses a phone as the client to help a hearing aid scan a TV broadcast. The phone first sends Remote Scan Started so the hearing aid can reduce its local scan burden. The phone then parses the broadcast source information and sends it to the hearing aid with Add Source.

If the broadcast is encrypted, the hearing aid enters a key-wait state after synchronizing to the BIG, and the phone then delivers the Broadcast_Code through Set Broadcast_Code. After reception ends, the phone performs three cleanup actions: stop synchronization, remove the broadcast source, and stop remote scanning.

AI Visual Insight: This figure should be a sequence diagram or flowchart showing command flow and state changes among the phone, the BASS server, and the broadcast source. It should emphasize four phases: remote scan delegation, broadcast source addition, key delivery, and resource cleanup.

Four implementation details deserve priority during development

First, all multi-byte fields use little-endian encoding. Second, RFU fields must be written as 0. Third, Add Source and Modify Source should support Long Write. Fourth, the client must maintain the Source_ID mapping in real time to avoid using stale IDs.

These constraints may look basic, but they directly determine whether devices from different vendors can interoperate reliably.

FAQ

Why must the BASS control point remain a single instance?

Because it is the only entry point for client commands. A single instance prevents conflicts caused by concurrent writes to multiple control points and simplifies client discovery and binding logic.

Why is Set Broadcast_Code the only mandatory operation?

Because LE Audio broadcast supports encrypted transport. If a receiving device cannot accept Broadcast_Code, it cannot complete decryption or audio playback, and protocol completeness breaks down.

What should you check first when Add Source fails?

Start by checking the total write length, whether Long Write was used, and whether the PA_Sync parameters are valid. Then confirm that BIS_Sync does not contain a conflict where the same BIS is synchronized more than once across subgroups.

Core summary

This article systematically reconstructs the design of the Broadcast Audio Scan Control Point in LE Audio BASS. It covers the two core characteristics, six opcodes, error handling, Add/Modify Source parameter structures, and the complete interaction flow in which a phone coordinates a hearing aid to receive broadcast audio.