Complete Series
Modern Profile
Linux Examples
What You Will Learn in This Series
This five-part tutorial walks you through the Audio Input Control Service (AICS) — a BLE GATT service that lets a remote device read and control the gain (volume) and mute state of an audio input on another device. You will understand the protocol design, each characteristic in detail, and how to build or interact with an AICS server using the BlueZ stack on Linux.
Real-world devices that implement AICS include Bluetooth headsets, hearing aids, wireless microphones, and smart speakers where the phone or tablet acts as a client and adjusts the microphone input level remotely.
Tutorial Series Map
What is AICS?
AICS stands for Audio Input Control Service. Think of it like the equalizer knob on a mixing desk — but accessed remotely over Bluetooth Low Energy. The device that has the microphone or audio input is the server. The phone or tablet that wants to adjust the gain is the client.
Before AICS existed, there was no standard way for a BLE client to ask a device: “Hey, turn your microphone gain up by 3 dB” or “Mute your left microphone input.” AICS standardizes exactly this kind of interaction.
Sends commands
Reads current state
Executes commands
Reports state changes
Where Does AICS Sit in the BLE Stack?
BLE has a layered architecture. AICS lives at the application layer, built on top of GATT, which itself uses ATT, which runs over L2CAP over the BLE radio. Understanding this stack helps you know exactly what happens when your phone writes to the AICS Control Point characteristic.
| AICS (Audio Input Control Service) Application Layer — this tutorial |
| GATT — Generic Attribute Profile Services, Characteristics, Descriptors |
| ATT — Attribute Protocol Read / Write / Notify PDUs |
| L2CAP — Logical Link Control ATT bearer (unenhanced or enhanced) |
| HCI — Host Controller Interface Command / Event / Data interface |
| BLE Radio — PHY + Link Layer 2.4 GHz, 1M / 2M / Coded PHY |
Quick GATT Recap Before We Proceed
GATT organizes data into Services and Characteristics. A Service is a logical grouping of related functionality. A Characteristic is an individual data item inside a service — it has a type (UUID), a value, and properties that say whether you can read it, write to it, or subscribe for notifications.
| ▶ Primary Service (e.g., Volume Control Service) | ||||||||||||||
|
GATT Sub-Procedures Required by AICS
GATT defines a set of sub-procedures — basically, the set of operations the server must support beyond the absolute minimum. AICS requires four specific ones, all of which are Mandatory (M):
| GATT Sub-Procedure | Requirement | Why AICS Needs It |
|---|---|---|
| Write Characteristic Values | M | Client writes opcodes to the Audio Input Control Point to trigger gain/mute changes |
| Notifications | M | Server pushes updated Audio Input State to clients when gain, mute, or mode changes |
| Read Characteristic Descriptors | M | Client reads the CCCD to check notification subscription status |
| Write Characteristic Descriptors | M | Client writes 0x0001 to the CCCD to enable notifications for Audio Input State |
| Feature | Unenhanced ATT Bearer | Enhanced ATT Bearer (EATT) |
|---|---|---|
| L2CAP Channel Mode | Fixed channel (CID 0x0004) | Enhanced Credit-Based Flow Control |
| Notification Reliability | ❌ No ACK — best effort | ✅ Credit-based, reliable |
| Supported Since | BT Core 4.0+ | BT Core 5.2+ (LE Audio) |
| Concurrent Transactions | One at a time | Multiple parallel transactions |
Application Error Codes — The Five AICS Errors
When a client writes to the Audio Input Control Point with an incorrect value, the server responds with an ATT Error Response PDU. AICS defines five custom error codes in the application error code range (0x80–0xFF). Understanding these is critical for debugging BlueZ-based implementations.
| Opcode (1 byte) | Request Opcode (1 byte) | Handle (2 bytes) | Error Code (1 byte) |
|---|---|---|---|
| 0x01 (ATT_ERROR_RSP) | 0x12 (Write Request) | Handle of AICP | 0x80 – 0x84 |
| Code | Name | When It Occurs | How to Fix in Code |
|---|---|---|---|
| 0x80 | Invalid Change Counter | Your Change_Counter operand is stale — the state changed between your Read and your Write | Re-read Audio Input State, get the fresh Change_Counter, retry the write |
| 0x81 | Opcode Not Supported | You wrote an opcode value not in the range 0x01–0x05 | Check your opcode byte — use only defined values |
| 0x82 | Mute Disabled | Tried to mute/unmute when hardware has the Mute field set to Disabled (e.g., privacy switch active) | Only a physical local event on the server can un-disable mute; wait for a notification |
| 0x83 | Value Out of Range | Gain_Setting operand in Set Gain Setting is below Minimum or above Maximum | Read Gain Setting Properties first to know valid min/max, clamp your value |
| 0x84 | Gain Mode Change Not Allowed | Tried to switch gain mode on a device that is Manual Only or Automatic Only | Read Gain_Mode field first to confirm it supports switching (value 2 or 3) |
Byte Transmission Order — Little Endian
All multi-byte values in AICS are transmitted least significant octet (LSO) first, also called little-endian. This is the standard for all BLE GATT characteristics. For AICS specifically, the only multi-byte field you will encounter is the ATT handle in the error response — all AICS characteristic fields are single bytes.
| Byte 0 (first on wire) | Byte 1 (second on wire) |
|---|---|
| 0x25 (LSO — low byte) | 0x00 (MSO — high byte) |
| bits 7..0 of 0x0025 | bits 15..8 of 0x0025 |
Key Terms Introduced in Part 1
Ready for Part 2?
Next we explore the full service architecture — all 6 characteristics and how they relate to each other.
