Profile 实战

BLE Profile 实战:iBeacon 与 Beacon 广播(室内定位与 proximity 基石)

发布于 2026-07-23 · 来自「嵌入式江湖」技术专栏

导语:Beacon 是 BLE 里一类"只发不收"的设备——它不建立连接,只是周期性广播一段特定格式的数据。最常见的两种标准是 Apple 的 iBeacon 和 Google 的 Eddystone。本文拆解 iBeacon 的完整字节布局、接收端解析方法、与 Eddystone 的差异,以及工程落地时最容易踩的坑。

注意:Beacon 是"广播(Advertising)"层面的东西,不是 GATT Service。它不出现在 GATT 数据库里,不能用 gattc_read 去读——这点常被初学者搞混。

一、Beacon 到底是什么

Beacon(信标)泛指一类"以固定周期广播唯一标识"的 BLE 设备。它的核心特征是:

  • 工作在 发射-only(transmit-only) 模式,不收连接、不进 GATT
  • 广播里携带 1~N 个标识符(UUID / Major / Minor,或 Eddystone 的 10 字节 Namespace+Instance)
  • 接收端(手机)靠 RSSI 估算与信标的距离,结合多信标交叉定位

iBeacon 和 Eddystone 都只是"广播包里那段数据的格式定义",不是新协议、不是新物理层。

二、iBeacon 广播字节布局

iBeacon 把标识塞进 Manufacturer Specific Data(AD Type 0xFF),并打上 Apple 的 Company ID(0x004C)。一个典型完整包(hex):

02 01 06 1A FF 4C 00 02 15
FB 0B 57 A2 82 28 44 CD 91 3A 94 A1 22 BA 12 06 00
01 00 02 D1 00

逐字节拆解(LTV = Length-Type-Value):

偏移 字节 含义
0 02 AD 结构长度(本结构 2 字节:类型+值)
1 01 AD Type:Flags
2 06 LE General Discoverable + BR/EDR Not Supported
3 1A AD 结构长度(26 字节,从下一字节起到结尾)
4 FF AD Type:Manufacturer Specific Data
5–6 4C 00 Company ID:Apple(0x004C),小端序
7 02 iBeacon 子类型(Apple 分配)
8 15 iBeacon 数据长度(0x15 = 21 字节,即后面 UUID+Major+Minor+TxPower 总长)
9–24 16 字节 Proximity UUID(大端序,128-bit)
25–26 2 字节 Major(大端序,uint16)
27–28 2 字节 Minor(大端序,uint16)
29 1 字节 Tx Power(有符号 int8,1 米处的参考 RSSI,单位 dBm)

关键点:

  • 0x4C 00 是 Apple 在 SIG 注册的 Company ID,写成小端(低字节在前)。其它厂商做 iBeacon 兼容信标时必须 用这个值,否则 iOS 不认。
  • 02 15 = 子类型 02 + 数据长度 15,合起来是 Apple iBeacon 的固定前缀。
  • UUID + Major + Minor 三段式,正好对应"场所 / 区域 / 具体点"三级标识。

三、四个字段怎么用

  • Proximity UUID:16 字节,全局唯一的"部署标识"。同一连锁品牌、同一套 App 的所有信标用同一个 UUID。
  • Major(2 字节):次级分组,比如某商场的所有门店、或某栋楼的所有楼层。
  • Minor(2 字节):最细粒度,比如某个具体货架、某个展台、某个停车位。
  • Tx Power:1 字节有符号数,表示在 1 米距离 处测得的 RSSI 理论值(典型 -59 dBm,即 0xC5)。接收端用它和实测 RSSI 做差,估算距离。这个值是设备出厂时校准写死的,不是动态测的。

距离估算不是 iBeacon 协议规定的,而是 App 端基于 RSSI 的路径损耗模型(如 d = 10^((TxPower − RSSI)/(10×n)),n 为环境衰减系数,室内约 2~4)算出来的。iBeacon 只负责把 Tx Power 广播出去。

四、接收端如何解析 iBeacon

扫描拿到原始 advertisement_data,过滤出 0xFF 段,验证 Company ID == 0x004C、子类型 == 0x02,再按偏移拆字段。下面是一段可直接用的 Python 解析函数:

import struct

def parse_ibeacon(mfr_data: bytes):
    """mfr_data 是 AD Type 0xFF 的 value(不含前面的 length/type 字节)。"""
    if len(mfr_data) < 25:
        return None
    company = mfr_data[0] | (mfr_data[1] << 8)   # 小端
    if company != 0x004C:
        return None
    subtype = mfr_data[2]
    if subtype != 0x02:
        return None
    # 0x15 = 21 字节数据长度;校验总长
    data = mfr_data[4:4 + 21]
    if len(data) < 21:
        return None
    raw = data[0:16].hex()
    uuid = f"{raw[0:8]}-{raw[8:12]}-{raw[12:16]}-{raw[16:20]}-{raw[20:32]}"
    major, minor = struct.unpack(">HH", data[16:20])
    tx_power = struct.unpack(">b", data[20:21])[0]
    return {"uuid": uuid, "major": major, "minor": minor, "tx_power": tx_power}

# 解析前面示例包(注意 raw 已含 Company ID 0x4C00 + 子类型 02 15)
raw = bytes.fromhex("4C00" + "0215" +
        "FB0B57A2822844CD913A94A122BA1206" + "0100" + "02D1" + "00")
print(parse_ibeacon(raw))
# -> {'uuid': 'fb0b57a2-8228-44cd-913a-94a122ba1206', 'major': 256, 'minor': 721, 'tx_power': 0}

注意:示例里的 Tx Power = 0x00 是占位,真实部署必须填校准值(如 0xC5 = -59)。

五、Eddystone:Google 的竞品

Eddystone 不用 Company ID,而是把 0xFEAA 放进 Service Data(AD Type 0x16) 或 "Complete List of 16-bit Service UUIDs(0x03 / 0x07)"。它定义了多种帧(frame):

帧类型 用途
UID 10 字节(Namespace 10 + Instance 6)标识,类似 iBeacon 但更紧凑
URL 直接广播一个压缩 URL,手机点开即跳网页,无需 App
TLM 遥测帧:电池电压、温度、上电秒数、广播计数,给运维用
EID 短暂可变的加密 ID,用于隐私 / 防追踪场景

Eddystone-URL 是最大的差异点:它让"扫码"变成"靠近即推送网页",对没有 App 的用户也友好。

六、iBeacon vs Eddystone 速查表

维度 iBeacon Eddystone
提出方 Apple Google
标识载体 Manufacturer Data 0xFF + Company 0x004C Service UUID 0xFEAA + Service Data
标识结构 UUID + Major + Minor UID / URL / TLM / EID 多帧
是否需 App 一般需要(iOS 原生 CoreLocation 支持) URL 帧可免 App 直接打开网页
遥测 有 TLM
隐私 固定 UUID 易被追踪 有 EID 临时 ID

七、实现要点(发射端)

  • Flags 必须有:至少 02 01 06。iOS 26(含 iPhone 17)开始对缺失 Flags 的广播直接忽略,很多老模组因此"看不见"。
  • Manufacturer Data 紧跟 Flags:iOS 解析顺序敏感,Flags 缺失或顺序错会导致不被识别。
  • 广播间隔:iBeacon 典型 100ms~1s。越小发现越快但越费电;资产追踪常用 300~1000ms。
  • 发射功率与 Tx Power 校准:必须实测温距点(1 米)写入 Tx Power,否则距离估算全偏。
  • 广播类型:用 ADV_NONCONN_IND(不可连接、可扫描)或 ADV_SCAN_IND,别用可连接类型。

八、典型应用与坑

  1. 室内定位:商场 / 医院 / 停车场导航,靠多信标 RSSI 三角定位,单点精度 3~5 米,遮挡严重处更差。
  2. Proximity 推送:进店触发优惠券,靠 Major/Minor 识别具体区域。
  3. 资产追踪:仓库货架、展品定位。
  4. 容易被伪造(spoofing):iBeacon 包明文广播,谁都能抓包重放。涉及身份 / 支付的场景必须 App 端二次校验,别只信广播内容。
  5. RSSI 抖动大:人体遮挡、多径反射让 RSSI 剧烈波动,距离估算要加滤波(滑动平均 / 卡尔曼)。
  6. UUID 规划混乱:同一 App 下不同场所 UUID 要统一管理,否则 App 分不清。
  7. Android 无原生 iBeacon API:Android 不认 iBeacon 专有格式,要么用库(如 AltBeacon)要么自己解析 Manufacturer Data。
  8. iOS 后台限制:App 在后台时 iBeacon 区域监听(region monitoring)可用,但 ranging(持续测距)会被系统限制,别指望后台高精度定位。
  9. 电量:电池供电信标要权衡广播频率与寿命,典型纽扣电池 1~2 年。
  10. iOS 26 兼容性:老固件务必补上 Flags、规范字节序,否则新手机识别不了(见第六节)。
更多 BLE / 鸿蒙星闪 / 芯片选型 / MCU 实战,持续更新