BLE 멀티 클라이언트 대응
BLE 4.1부터 peripheral이 여러 central(client)과 동시에 연결할 수 있다. 단일 라디오를 시분할로 공유하므로, 연결 수가 늘면 throughput, 전력, 메모리 모두 영향을 받는다.
플랫폼별 최대 연결 수
| 플랫폼 | 스택 | 최대 연결 | peripheral 역할 제한 |
|---|---|---|---|
| nRF52840 | SoftDevice S140 | 20 | 제한 없음 (역할 자유 배분) |
| STM32WB55 | ST BLE Stack | 8 | central로부터 최대 2개 |
| ESP32 | NimBLE | 9 | 기본값 3, 설정으로 변경 |
| EFR32 | Gecko SDK | 32 | 기본값 4 |
STM32WB55는 총 8개 링크를 지원하지만, peripheral(slave) 역할은 2개로 제한된다.
구현 패턴
연결 테이블
고정 크기 배열로 연결 상태를 관리한다.
#define MAX_CONNECTIONS 8
typedef struct {
uint16_t conn_handle; // BLE_CONN_HANDLE_INVALID if unused
uint8_t peer_addr[6];
uint16_t mtu; // 연결별 개별 협상
uint16_t conn_interval;
bool is_encrypted;
app_state_t app_state; // 애플리케이션 프로토콜 상태
} conn_entry_t;
static conn_entry_t conn_table[MAX_CONNECTIONS];
BLE_GAP_EVT_CONNECTED에서 슬롯 할당, BLE_GAP_EVT_DISCONNECTED에서 해제. 모든 GATT 연산은 conn_handle로 라우팅한다.
CCCD는 연결별 독립
CCCD(Client Characteristic Configuration Descriptor)는 스펙상 연결별 상태다. 클라이언트 A가 notification을 켜도 클라이언트 B에는 영향이 없다.
typedef struct {
uint16_t conn_handle;
bool notifications_enabled;
} cccd_state_t;
cccd_state_t sensor_cccd[MAX_CONNECTIONS];
- bonded 디바이스는 CCCD를 flash에 영속 저장 (Nordic Peer Manager 등)
- 비bonded 디바이스는 연결 해제 시 CCCD 초기화
연결 중 advertising 재개
추가 central을 받으려면 연결 후 advertising을 다시 시작해야 한다.
void on_connected(uint16_t conn_handle) {
conn_table_add(conn_handle);
if (conn_table_count() < MAX_CONNECTIONS) {
ble_advertising_start(BLE_ADV_MODE_CONNECTABLE);
}
}
advertising과 기존 연결 이벤트가 같은 라디오를 공유하므로, 스케줄러가 자동으로 시분할 배치한다.
단일 연결 대비 시스템 단점
1. 스케줄링 충돌
각 central이 독립적으로 connection interval과 anchor point를 설정한다. peripheral은 Connection Parameter Update Request를 보낼 수 있지만, central이 거부할 수 있다.
- 연결 이벤트 하나를 처리하는 데 최소 2.5 ms 필요
- N개 연결, connection interval CI일 때 연결당 가용 시간:
CI / N - guard_band - 각 central의 클럭이 독립적으로 drift하므로, 충돌이 간헐적으로 발생하고 예측 불가
- iOS는 최소 CI 15 ms, Android는 다를 수 있어 서로 다른 OS가 연결되면 파라미터 불일치 발생
2. throughput 저하
단일 라디오를 시분할로 공유하므로, 연결 수에 비례해 throughput이 떨어진다.
| 연결 수 | 연결당 throughput | 비고 |
|---|---|---|
| 1 (BLE 5.0, 2M PHY, DLE) | ~140 kB/s | 최적 단일 링크 |
| 3 (동일 CI) | ~10 kB/s | 약 14배 감소 |
연결당 throughput 공식:
throughput ≈ (packets_per_event × payload_size) / CI
packets_per_event ∝ event_length = CI / N - guard_band
3. 메모리 오버헤드
연결마다 별도의 컨텍스트를 유지해야 한다.
| 리소스 | 설명 | 연결당 크기 |
|---|---|---|
| Link Layer 컨텍스트 | 타이밍, 채널맵, 암호화 상태 | 1~2 KB |
| ATT/GATT 버퍼 | TX/RX 큐, MTU 크기 버퍼 | MTU에 비례 |
| CCCD 상태 | characteristic별 구독 상태 | 2 bytes × characteristic 수 |
| 보안 컨텍스트 | 암호화 키, 본딩 정보 | 80~100 bytes |
nRF52 기준 연결당 1.4~3.8 KB (MTU, 큐 설정에 따라 변동). 256 KB RAM에서 8개 연결 + DLE(251 bytes) 설정 시 BLE 스택만 35~40 KB를 차지한다.
4. 전력 소비 증가
연결 수에 비례해 라디오 wake-up 횟수가 늘어난다.
- 단일 연결 CI=100 ms: 초당 10회 wake-up
- N개 연결 CI=100 ms: 초당 10×N회 wake-up
- 단일 연결 CI=75 ms 기준 평균 전류: ~230 μA (Silicon Labs EFR32 측정값)
- peripheral latency로 이벤트를 건너뛸 수 있지만, 각 central이 독립 제어하므로 멀티 연결 시 효과 제한
5. notification fanout
연결 모드에서 BLE는 multicast를 지원하지 않는다. 같은 데이터를 N개 클라이언트에 보내려면 N번 개별 전송해야 한다.
- 연결 테이블 순회 → CCCD 확인 →
notify()호출을 연결 수만큼 반복 (O(N)) - HVN 큐 깊이가 제한적 (보통 1~4/연결). 큐 풀이면
HVN_TX_COMPLETE이벤트를 기다려야 한다 - 5개 클라이언트, CI=100 ms일 때 첫 번째와 마지막 클라이언트 사이 최대 500 ms 지연
- RF 상태가 나쁜 클라이언트는 재전송으로 큐를 점유해 다른 클라이언트의 notification도 지연
6. WiFi 공존 악화 (단일 라디오 칩)
ESP32처럼 BLE와 WiFi가 하나의 2.4 GHz 라디오를 공유하는 경우:
- WiFi+BLE 시 라디오 시간을 50:50으로 분할 (TDM)
- 단일 BLE 연결이면 BLE 50%를 독점하지만, N개 연결이면
50% / N으로 분산 - WiFi 고부하 시 BLE 연결 이벤트가 누락되어 supervision timeout 발생 가능
- WiFi throughput: BLE 없이 ~20 Mbps → BLE 공존 시 ~8 Mbps (TCP RX)
메모
- STM32WB55에서 멀티 클라이언트 peripheral을 구현할 경우, slave 역할 2개 제한을 반드시 확인해야 한다
- throughput이 중요한 서비스(OTA 등)는 해당 연결 진행 중 다른 연결의 CI를 늘리거나, 연결 수를 제한하는 설계가 필요하다
- notification fanout 지연이 문제되면 BLE 5.0 extended advertising의 periodic advertising (connectionless broadcast)을 대안으로 검토할 수 있다