Bluetooth le auido tutorial – Audio Input Control Service (AICS)

 

 

Bluetooth le auido tutorial – Audio Input Control Service (AICS)
Part 1 of 5  |  Introduction & BLE Stack Context
5 Parts
Complete Series
BLE LE Audio
Modern Profile
BlueZ Ready
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

📖 Part 1  — Introduction (this page)
What is AICS Real-world analogy Where AICS sits in BLE stack GATT sub-procedure requirements Error codes
📖 Part 2  — Service Architecture
Secondary service declaration Included service concept All 6 characteristics at a glance Security requirements
📖 Part 3  — Audio Input State Deep Dive
Gain_Setting field Mute field Gain_Mode field Change_Counter field Gain Setting Properties
📖 Part 4  — Control Point Procedures
5 opcodes explained Packet byte layouts Error handling Change_Counter synchronization
📖 Part 5  — BlueZ Implementation
GATT server in Python + D-Bus Client reads with gatttool Writing to Control Point Notification handling

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.

Real-World Analogy — Phone controlling Hearing Aid mic gain
📱
Smartphone
GATT Client
Sends commands
Reads current state
Write Opcode (Mute)
Notify (new state)
🦻
Hearing Aid
GATT Server
Executes commands
Reports state changes
BLE LE connection — encrypted, GATT over ATT bearer

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.

BLE Protocol Stack — AICS Position
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.

GATT Object Model — How AICS fits
▶ Primary Service  (e.g., Volume Control Service)
► Included Service  =  AICS instance #1 (e.g., “Microphone”)
→ Characteristic: Audio Input State  |  Read, Notify
→ Characteristic: Gain Setting Properties  |  Read
→ Characteristic: Audio Input Control Point  |  Write
→ Characteristic: Audio Input Description  |  Read, Notify
► Included Service  =  AICS instance #2 (e.g., “Line In”)
AICS is always a Secondary Service included inside a Primary Service like Volume Control Service (VCS)

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
⚠ Note on Notification Reliability: When using a plain (unenhanced) ATT bearer over BLE, notifications are fire-and-forget — there is no acknowledgement. If you need guaranteed delivery, you can use an Enhanced ATT (EATT) bearer, which uses L2CAP credit-based flow control. For most AICS use-cases the standard bearer is fine.

Unenhanced ATT vs Enhanced ATT Bearer — Notification Reliability
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.

AICS Application Error Codes — ATT Error Response Layout
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.

Little-Endian Example — 16-bit handle 0x0025 on the wire
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

AICS GATT Server GATT Client Secondary Service Included Service ATT Bearer EATT Notification CCCD Error Code 0x80-0x84 Little Endian Change_Counter

Ready for Part 2?

Next we explore the full service architecture — all 6 characteristics and how they relate to each other.

→ Part 2: Service Architecture

Leave a Reply

Your email address will not be published. Required fields are marked *