| Supported Targets | ESP32-H4 | ESP32-S31 |
|---|
BLE CIS Peripheral Example
(See the README.md file in the upper level examples directory for more information about examples.)
Overview
This is a raw BLE Connected Isochronous Stream (CIS) example operating directly at the ISO transport layer over either the NimBLE or Bluedroid host (selected at build time via Kconfig). It is not a BAP/CAP (BLE Audio profile) example — it does not implement Unicast Server, ASCS, PACS, or any LC3 codec; it only exercises the underlying CIS accept/receive plumbing.
The peripheral starts connectable extended advertising under the name CIS Peripheral (1M primary / 2M secondary PHY, 200 ms interval), registers an ISO server with security level ESP_BLE_ISO_SECURITY_NO_MITM and an accept callback that hands out the single CIS channel slot, sets up the output data path to the HCI in transparent format when the CIS connects, and tallies received SDUs through the shared RX-metrics helper.
There is no audio decoding — incoming SDUs are simply counted (valid / error / lost / null), so any 120-byte dummy payload from the central is consumed as opaque data via the esp_ble_iso_* APIs.
Requirements
- A board with BLE 5.2 and ISO support (e.g. ESP32-H4, ESP32-S31)
- Peer device running the paired example
Configuration
idf.py menuconfig
No build-time options — runtime defaults are baked into source.
Security & Pairing
Just-Works pairing (LE Secure Connections, no MITM, IO capability = None) with bonding enabled. The configuration is set up in the shared host init ../common_components/example_init/ble_iso_example_init.c for both NimBLE (ble_hs_cfg.sm_*) and Bluedroid (esp_ble_gap_set_security_param()). ISO examples do not register any custom GATT services.
Build & Flash
The base sdkconfig.defaults defaults to the Bluedroid host; idf.py automatically merges the per-target overlay (sdkconfig.defaults.$IDF_TARGET). To build with NimBLE host instead, layer sdkconfig.defaults.nimble on top via -DSDKCONFIG_DEFAULTS.
Bluedroid host (default)
idf.py set-target esp32h4
idf.py -p PORT flash monitor
NimBLE host
idf.py set-target esp32h4
idf.py -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.defaults.esp32h4;sdkconfig.defaults.nimble" -p PORT flash monitor
For esp32s31, replace the chip overlay accordingly.
(Exit serial monitor with Ctrl-].)
Example Flow
- Initialize NVS, the selected BLE host, and the ISO common layer with a GAP callback.
- Register an ISO server (
esp_ble_iso_server_register) whose accept callback returns the single static CIS channel. - Build flags + complete-local-name AD payload and start connectable extended advertising on handle 0 at a 200 ms interval.
- On ACL connect, log the peer; the central drives subsequent pairing and CIG/CIS creation.
- When the CIS request arrives, the accept callback supplies the channel; on CIS connect, set up the output data path (HCI / transparent) and reset RX metrics.
- Each received SDU updates valid/error/lost counters; periodic RX summaries are logged by the shared utility.
- On ACL disconnect, restart extended advertising.
Expected Log
TAG: CIS_PER
Advertising and connection phase:
I CIS_PER: Advertising started (handle 0)
I CIS_PER: Connected: handle <h> role <r> peer XX:XX:XX:XX:XX:XX
I CIS_PER: Security: handle <h> level <l> bonded <b>
CIS accept and streaming phase (RX log emitted every LOG_INTERVAL_PACKETS SDUs by the shared utility):
I CIS_PER: Incoming CIS request from handle <h>
I CIS_PER: [CIS #0] Connected
I CIS_PER: [CIS #0] RX: <count> packets
Disconnect path:
I CIS_PER: [CIS #0] Disconnected, reason 0x<rr>
I CIS_PER: Disconnected: handle <h> reason 0x<rr>
Peer Pairing
Run cis_central on a second board.
- Flash and run
cis_peripheralfirst; it advertises asCIS Peripheralon extended-advertising handle 0. - Flash and run
cis_centralon the second board; it scans and matches the name. - The central opens the ACL connection and initiates pairing; this peripheral handles the security change passively.
- The central creates the CIG and CIS; the peripheral's accept callback returns the local channel.
- The peripheral configures its output data path on CIS connection.
- Incoming 120-byte SDUs are counted and
RX: <count> packetsis logged periodically.