광고 끝까지: 확장 광고 체인, 주기적 광고와 PAwR
1권 5장의 광고는 세 채널에 31바이트를 외치는 것이었다. 5.0 이후의 광고는 다르다. 주 채널에는 “자세한 건 7번 채널 1.2 ms 뒤에”라는 짧은 안내만 두고, 본문은 데이터 채널로 옮겨 수백 바이트를 체인으로 잇는다. 같은 구조 위에 주기적 광고와 응답 있는 주기적 광고(PAwR)가 올라선다. 이 장은 그 공통 형식을 바이트로 익히고, 사양 샘플 AUX_ADV_IND 하나를 공중 비트까지 다시 만든다.
- ADV_EXT_IND → AUX_ADV_IND → AUX_CHAIN_IND의 역할과, 주 채널·보조 채널의 쓰임을 설명할 수 있다.
- 공통 확장 페이로드(확장 헤더 길이·AdvMode·플래그·AdvA·ADI·AuxPtr·SyncInfo·TxPower)를 바이트로 만들고 읽을 수 있다.
- AuxPtr의 오프셋 단위와 T_MAFS, 1650바이트 체인의 공중 시간을 계산할 수 있다.
- 주기적 광고 동기와 PAST, PAwR의 서브이벤트·응답 슬롯 제약을 안다.
주 채널은 안내판, 본문은 보조 채널로
확장 광고는 광고를 두 층으로 나눈다. 주 채널(37·38·39)에는 ADV_EXT_IND를 보낸다. 데이터 없이 “이 광고 묶음(SID)의 몇 번째 데이터(DID)이고, 본문은 어느 채널에 몇 µs 뒤에 있다(AuxPtr)”만 싣는다. 스캐너는 그 포인터를 따라가 보조 채널(0~36번 데이터 채널 중 하나)의 AUX_ADV_IND를 받는다. 본문이 길면 AUX_ADV_IND에도 AuxPtr을 달아 AUX_CHAIN_IND로 잇는다. 호스트 광고 데이터는 조각내기 전 최대 1650바이트다.
붐비는 역 게시판(주 채널)에는 큰 공지를 다 붙이지 않는다. “○○ 행사 안내는 7번 출구 데스크, 지금부터 1분 뒤”만 붙인다. 관심 있는 사람만 7번 출구로 가서 자세한 안내문(AUX_ADV_IND)을 받고, 안내문이 길면 “다음 장은 옆 데스크에서”(AUX_CHAIN_IND)로 이어진다. 게시판이 덜 붐비고, 긴 안내도 가능하다.
ADI(AdvDataInfo, 2바이트)는 SID 4비트와 DID 12비트다. 한 기기가 광고 묶음 여럿을 동시에 돌릴 때 SID로 구별하고, 스캐너는 DID가 그대로면 같은 데이터라 보고 보조 채널을 다시 따라가지 않을 수 있다. 데이터를 바꾸면 DID를 바꿔야 한다.
확장 광고 PDU와 공통 확장 페이로드(Figure 2.17, AdvMode Table 2.15): [Vol 6] Part B §2.3.4 · ADI: §2.3.4.4 · AuxPtr: §2.3.4.5 · 1650바이트: §2.3.4.9(호스트 광고 데이터)
공통 확장 페이로드를 바이트로
ADV_EXT_IND, AUX_ADV_IND, AUX_CHAIN_IND, AUX_SYNC_IND 등은 모두 같은 페이로드 형식을 쓴다. 첫 바이트는 확장 헤더 길이(아래 6비트, 0~63)와 AdvMode(위 2비트: 00 연결·스캔 불가, 01 연결 가능, 10 스캔 가능). 그 뒤 확장 헤더가 오는데, 첫 바이트 플래그의 비트 0부터 차례로 AdvA(6), TargetA(6), CTEInfo(1), ADI(2), AuxPtr(3), SyncInfo(18), TxPower(1) 칸이 있는지를 표시하고 그 순서대로 이어진다. 남은 자리가 AdvData다(길이 − 확장 헤더 길이 − 1).
AuxPtr 3바이트: 채널 번호 6비트, 시계 정확도(CA) 1비트, 오프셋 단위 1비트(0 = 30 µs, 1 = 300 µs), Aux 오프셋 13비트, Aux PHY 3비트. 오프셋은 이 패킷의 시작에서 보조 패킷 시작까지이고, 적어도 이 패킷 길이 + T_MAFS(300 µs)여야 한다. 245,700 µs 미만이면 30 µs 단위를 쓴다. 보조 패킷은 오프셋보다 이르지 않고, 오프셋 + 한 단위보다 늦지 않게 시작한다.
AUX_ADV_IND 만들기 (사양 4.2.2 샘플)
이 작업대를 만들며 사양 [Vol 6] Part C §4.2.2 샘플의 CRC(00011011 11000100 01110101)가, 같은 절의 PDU 비트에 본문 규칙(광고 채널 PDU는 CRC 레지스터를 0x555555로 시작, [Vol 6] Part B §3.1.1)을 적용한 값과 다르다는 것을 확인했다. 같은 CRC 구현이 레거시 광고(§4.2.1), 데이터 PDU(§4.3.1), 제어 PDU(§4.3.2) 샘플은 모두 그대로 재현하고, 문제의 CRC는 초기값을 0x515555(비트 18 하나 다름)로 두었을 때만 나온다. 샘플을 만들 때의 실수로 보이며, 구현은 본문 규칙(0x555555)을 따라야 한다. 검증 스크립트는 이 불일치를 그대로 기록해 둔다.
확장 헤더 필드 순서와 크기(Figure 2.18): [Vol 6] Part B §2.3.4 · AuxPtr(Table 2.17, T_MAFS 조건, 245,700 µs): §2.3.4.5 · TxPower: §2.3.4.7 · T_MAFS 300 µs: §4.1.2 · 샘플: [Vol 6] Part C §4.2.2(레거시는 §4.2.1)
1650바이트를 보내는 데 걸리는 시간
보조 채널 PDU 하나의 페이로드는 최대 255바이트지만, 확장 헤더가 그 안을 차지한다. 체인이 길수록 PDU마다 AuxPtr과 ADI가 반복되고, 사이마다 T_MAFS 이상을 띄운다. PHY도 크게 영향을 준다. 같은 데이터를 Coded S=8로 보내면 1M보다 약 8배 오래 걸리고, 그동안 다른 일(3장의 스케줄러)이 밀린다.
체인 계획: PDU 수와 공중 시간
스캐너가 AUX_ADV_IND는 받았는데 AUX_CHAIN_IND 하나를 놓쳤다. 호스트에는 무엇이 올라갈까?
체인 중간이 빠진 광고 데이터를 그대로 이어 붙여도 될까?
보조 패킷 시작 시각과 T_MAFS: [Vol 6] Part B §2.3.4.5·§4.1.2 · Extended Advertising Report의 데이터 상태: [Vol 4] Part E §7.7.65.13
주기적 광고, PAST, 그리고 PAwR
AUX_ADV_IND에 SyncInfo(18바이트)를 넣으면 “이 광고 묶음에는 주기적 광고 열차가 있다”는 뜻이다. SyncInfo는 첫 AUX_SYNC_IND까지의 오프셋, 주기, 채널 맵, 접근 주소, CRC 초기값, 이벤트 카운터를 담는다. 스캐너는 이것으로 열차에 동기화하고, 그 뒤에는 광고 채널을 스캔하지 않고 정해진 시각에만 깨어 받는다. Auracast의 BIGInfo도 이 열차에 실려 온다(9장).
동기화하려면 처음 한 번은 스캔해야 하는데, 보청기처럼 전력이 빠듯한 기기에는 부담이다. PAST(Periodic Advertising Sync Transfer)는 이미 동기화한 휴대폰이 연결을 통해 LL_PERIODIC_SYNC_IND(opcode 0x1C)로 SyncInfo를 건네주는 절차다. 보청기는 스캔 없이 열차에 바로 탄다.
PAwR(5.4)은 주기적 광고에 응답 슬롯을 붙인 것이다. 한 주기 안에 서브이벤트를 최대 128개 두고, 서브이벤트마다 AUX_SYNC_SUBEVENT_IND 뒤에 응답 슬롯 여러 개를 연다. 수천 개의 전자 가격표가 각자 정해진 서브이벤트·슬롯에서만 깨어나 받고 답한다(1권 12장). 시간 규칙은 이렇다.
- 주기(Periodic Advertising Interval): 1.25 ms 배수, 7.5 ms ~ 81.91875 s
- 서브이벤트 간격: 1.25 ms 배수, 7.5 ms ~ 318.75 ms, 서브이벤트끼리 겹치지 않음
- 응답 슬롯 지연(서브이벤트 시작 → 첫 슬롯): 1.25 ms 배수, 1.25 ms ~ 317.5 ms
- 응답 슬롯 간격: 0.125 ms 배수, 0.25 ms ~ 31.875 ms, 그리고 ≤ (서브이벤트 간격 − 슬롯 지연) ÷ 슬롯 수
- 응답은 받은 AUX_SYNC_SUBEVENT_IND 시작부터 한 주기 안에 보낸다. 한 기기는 한 서브이벤트에서 슬롯 하나만 쓴다.
PAwR 설계 점검기
SyncInfo: [Vol 6] Part B §2.3.4.6 · 주기적 광고 이벤트: §4.4.2.2.3 · PAwR 이벤트와 시간 규칙: §4.4.2.2.4 · LL_PERIODIC_SYNC_IND(PAST): §2.4.2.27·§5.1.13
핵심 정리
- 주 채널의 ADV_EXT_IND는 안내만(ADI + AuxPtr), 본문은 보조 채널의 AUX_ADV_IND와 AUX_CHAIN_IND. 호스트 데이터 최대 1650바이트.
- 공통 확장 페이로드: [확장 헤더 길이 6비트 · AdvMode 2비트] [플래그] AdvA · TargetA · CTEInfo · ADI · AuxPtr · SyncInfo · TxPower · ACAD · AdvData.
- AuxPtr = 채널 6 · CA 1 · 단위 1(30/300 µs) · 오프셋 13 · PHY 3. 오프셋 ≥ 패킷 길이 + T_MAFS(300 µs).
- ADI의 SID는 광고 묶음, DID는 데이터 판. 데이터를 바꾸면 DID를 바꾼다.
- SyncInfo로 주기적 광고에 동기화하고, PAST로 연결을 통해 넘겨준다. PAwR: 서브이벤트 ≤ 128, 슬롯 간격 ≤ (서브이벤트 간격 − 슬롯 지연) ÷ 슬롯 수.
확인 퀴즈
확장 헤더 플래그가 0x49(0100 1001)이면 있는 칸은?
Aux 오프셋 1,200 µs를 AuxPtr에 넣으면 단위와 값은?
PAST가 해결하는 문제는?
PAwR 한 주기에 둘 수 있는 서브이벤트의 최대 수는?