LuatOS ble(二)
王世豪
Hello ,大家好,我是王世豪。
欢迎大家来到合宙 LuatOS 直播课堂,一起学习 LuatOS 课程。
第一部分:LuatOS 课程背景
因为今天是我们 LuatOS 系列课程的第 007 讲,同时也是 LuatOS BLE 专题课程的第二讲,所以在这里我就不重复讲解整个 LuatOS 课程的背景了;
如果您还不清楚 LuatOS 课程背景,可以访问:LuatOS 课程背景 这个链接,进行了解;
第二部分:LuatOS BLE 课程讲哪些内容
今天是 LuatOS BLE 的第二讲:BLE 的中心设备模式,广播和扫描模式 ;
关于 BLE 的历史以及 BLE 协议栈介绍,请看 BLE 专题的第一讲内容,本次课程不再赘述:https://docs.openluat.com/luatos_lesson/005_luatos_ble/
LuatOS BLE 第二讲课程主要包含以下几个部分:
- 复习 LuatOS BLE 的四种工作模式
- LuatOS 上的中心设备模式应用开发流程;
- LuatOS 上的广播模式应用开发流程;
- LuatOS 上的扫描模式应用开发流程;
- 常见问题
第三部分:复习 LuatOS BLE 的四种工作模式
关于 BLE 的四种模式,以及 LuatOS 的 BLE api,在 BLE 专题的第一讲介绍过,带大家再复习一遍:
https://docs.openluat.com/luatos_lesson/005_luatos_ble/
3.1 外围设备模式(peripheral)
外围设备模式是从广播者模式转化而来的,未被连接的外围设备首先进入广播状态,等待被中心设备搜索,当中心设备扫描到外围设备建立连接后,就可以和中心设备进行数据的收发,其不能主动的建立连接,只能等别人来连接自己。
和广播模式有区别的地方在于,外围设备模式的设备是可以被连接的,定期的和中心设备进行连接和数据传输,在数据传输过程中作外围设备。
代码基本流程:
1、初始化蓝牙框架
bluetooth_device = bluetooth.init()
2、创建BLE对象
local ble_device = bluetooth_device:ble(ble_callback)
3、创建GATT描述
local att_db = {xxx}
4、创建广播信息
ble_device:adv_create(adv_data)
5、开始广播
ble_device:adv_start()
6、等待连接
7、在回调函数中处理连接事件, 如接收数据, 发送数据等
3.2 中心设备模式(central)
中心设备模式是能够搜索别人并主动建立连接的一方,从扫描状态转化而来的。其可以和一个或多个从设备进行连接通信,它会定期的扫描周围的广播状态设备发送的广播信息,可以对周围设备进行搜索并选择所需要连接的从设备进行配对连接,建立通信链路成功后,主从双方就可以发送接收数据。
代码基本流程:
1、初始化蓝牙框架
bluetooth_device = bluetooth.init()
2、创建BLE对象
local ble_device = bluetooth_device:ble(ble_callback)
3、扫描目标BLE设备
ble_device:scan_start()
4、建立与目标设备的连接
ble_device:connect(mac, add_type)
5、处理各类BLE事件(连接、断开连接、扫描报告、GATT操作完成等)
3.3 广播者模式(ibeacon)
处于广播模式的设备,会周期性的广播 beacon 信息, 可以被扫描, 但一般不会被连接,典型应用 ibeacon。
代码基本流程:
1、初始化蓝牙底层框架
bluetooth_device = bluetooth.init()
2、创建BLE对象
local ble_device = bluetooth_device:ble(ble_callback)
3、配置ibeacon广播数据包
包含厂商特定数据格式,ibeacon类型标识符
设置UUID、Major、Minor等关键参数
4、启动BLE广播功能
ble_device:adv_start()
3.4 观察者模式(scan)
观察者模式,该模式下模块为非连接,相对广播者模式的一对多发送广播,观察者可以一对多接收数据。在该模式中,设备可以仅监听和读取空中的广播数据。和中心设备唯一的区别是不能发起连接,只能持续扫描外围设备。
代码基本流程:
1、初始化蓝牙框架
bluetooth_device = bluetooth.init()
2、创建BLE对象
local ble_device = bluetooth_device:ble(ble_callback)
3、开始扫描
ble_device:scan_start()
4、在回调函数中处理扫描事件, 如接收设备信息等
5、按需停止扫描
ble_device:scan_stop()
第四部分:LuatOS 上的中心设备模式应用开发流程
BLE——中心设备模式代码:
https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/demo/ble/central
BLE——中心设备模式文档:
https://docs.openluat.com/air8000/luatos/app/BLE/central/
4.1 前置知识了解
4.1.1 UUID
4.1.1.1 UUID 是什么?
UUID 是蓝牙 GATT 协议的 “数字身份证”,通过标准化的唯一标识机制,实现了跨厂商设备的功能互认(标准 UUID)与厂商个性化功能的扩展(自定义 UUID)
蓝牙协议通过 UUID 实现设备间的标准化通信,使用蓝牙对外提供服务的设备,需要有对应的服务功能,服务是蓝牙设备中功能划分的单元,每个服务都对应着一种特定的功能或数据传输需求。
例如,当一个蓝牙设备(如智能手环)向外界广播服务时,会携带对应的 UUID,其他设备(如手机)通过识别这些 UUID,就能知道该设备提供哪些功能(如心率监测、数据传输等),并建立针对性的连接。
一个蓝牙设备可以包含多个服务(Service),每个服务通过一个 UUID(服务 UUID) 进行标识。
每个服务下包含多个特征(Characteristic),每个特征都拥有一个独立的 UUID(特征 UUID)作为其唯一标识。
特征是最小的数据单元,我们通过读写特征来与设备交互。
1、服务(Service)
是什么:服务是蓝牙设备功能的逻辑分组,类似于一个"功能模块"。
例如:心率监测服务、电池电量服务等。
作用:定义设备能做什么(如测量心率、监控电量)。通过 UUID 唯一标识。
示例:
设备 → 服务 1(心率监测)
→ 服务2(电池电量)
→ 服务3(自定义控制)
2、特征(Characteristic)
是什么:特征是服务的子元素,是服务中的具体数据点,用于实际的数据读写操作。
例如:心率值、电池电量百分比。
作用:存储具体数据(如 71 bpm)。通过 UUID 和 属性(Properties) 定义操作权限(读/写/通知等)。
示例:
服务(心率监测) → 特征 1(心率测量值)
→ 特征2(传感器位置)
3、描述符(Descriptor)
是什么:描述符是特征的子元素,是特征的附加信息,用于细化特征的行为或配置。
例如:启用数据通知、设置数据格式。
作用:配置特征的行为(如开启实时通知)。补充描述特征(如单位、数据范围)。
最常见描述符:。
Client Characteristic Configuration Descriptor (CCCD)
用于启用/禁用特征的 NOTIFY 或 INDICATE 功能。
三者的层级关系:
蓝牙设备(Device)
│
└── 服务(Service) → 功能模块(如心率服务)
│
└── 特征(Characteristic) → 具体的数据点(如心率值)
│
└── 描述符(Descriptor) → 配置或描述特征(如启用通知)
实际示例(手环):
智能手环
├── 电池服务
│ ├── 电池电量特征
│ │ ├── 值:85%
│ │ ├── 属性:可读、可通知
│ │ └── 描述符:通知开关(开)
│ └── 充电状态特征
│ ├── 值:未充电
│ └── 属性:可读
├── 心率服务
│ └── 心率特征
│ ├── 值:72次/分钟
│ ├── 属性:可通知
│ └── 描述符:通知开关(开)
└── 运动服务
├── 步数特征
│ ├── 值:12560步
│ └── 属性:可读、可通知
└── 卡路里特征
├── 值:385千卡
└── 属性:可读
4.1.1.2 UUID 的格式:
蓝牙 UUID 的标准格式为 128 位,通常表示为 32 个十六进制字符,以 8-4-4-4-12 的格式分组,共 36 个字符(包括 4 个连字符):
格式:8-4-4-4-12
示例:0000180D-0000-1000-8000-00805F9B34FB
标准 UUID:
由蓝牙技术联盟(Bluetooth SIG)定义,用于常见服务。
标准的 UUID 为:0000xxxx-0000-1000-8000-00805F9B34FB。
为了节省带宽,标准 UUID 通常使用 16 位或 32 位短格式,实际通信时自动扩展为 128 位。每一个蓝牙技术联盟定义的属性有一个唯一的 16 位 UUID,以代替上面的基本 UUID 的‘x’部分。
若 16 bit UUID 为 xxxx,那么 128 bit UUID 为 0000xxxx-0000-1000-8000-00805F9B34FB。
若 32 bit UUID 为 xxxxxxxx,那么 128 bit UUID 为 xxxxxxxx-0000-1000-8000-00805F9B34FB。
自定义 UUID:
用于私有服务或厂商特定功能,需开发者自行生成,通常使用 UUID 随机生成器生成 128 位 UUID,确保全球唯一性
4.1.1.3 UUID 的分类
蓝牙规定好的 uuid 如下:
- 0x180x 开头 → 只能当 Service UUID
- 0x2Axx 开头 → 只能当 Characteristic UUID
- 0x29xx 开头 → 只能当 Descriptor UUID
0x180x 开头 UUID(Service UUID)
0x180x 区间只能当 Service UUID。
| UUID | Service name | 用途 |
| 0x1800 | Generic Access | 必含,设备名、外观、连接参数 |
| 0x1801 | Generic Attribute | 必含,Service Changed(服务改变) |
| 0x180A | Device Information | 厂商字符串/版本号 |
| 0x180F | Battery Service | 电池电量,手环/键鼠必备 |
| ... | ... | ... |
0x2Axx 开头 UUID(Characteristic UUID)
0x2Axx (0x2A00 – 0x2AFF) 区间只能当 Characteristic UUID。
| UUID | Service name | 用途 |
| 0x2A00 | Device Name | 设备名称,Generic Access 必含 |
| 0x2A01 | Appearance | 外观图标(键盘/鼠标/温度计…) |
| 0x2A04 | Peripheral Preferred Connection Parameters | 外设首选连接参数 |
| 0x2A05 | Service Changed | Generic Attribute 必含,OTA 必备 |
| 0x2A19 | Battery Level | 电池百分比,0-100 |
| 0x2A23 | System ID | 系统ID |
| 0x2A24 | Model Number String | 型号 |
| 0x2A25 | Serial Number String | 序列号 |
| 0x2A29 | Manufacturer Name String | 厂商名 |
| ...... | ...... | ...... |
0x29xx 开头 UUID(Descriptor UUID)
0x29xx 开头只能当 Descriptor UUID,即描述符 UUID。
这里仅介绍一个使用最频繁的 UUID:0x2902
0x2902 - Client Characteristic Configuration(CCC,客户端特征配置描述符)
这是一个可写的描述符,允许客户端设备(如手机、电脑)配置如何从服务器设备(BLE 外设)接收数据更新。主要用于启用或禁用通知(Notifications) 和指示(Indications) 功能。
BLE 实时数据推送(如传感器数据、状态更新)都需要通过正确配置 0x2902 描述符来实现。
UUID 格式:
- 完整 UUID:
00002902-0000-1000-8000-00805f9b34fb - 缩写:
0x2902 - 类型:Descriptor(描述符)
有效值:
0xFFxx 开头 UUID(Vendor Specific)
0xFF00 ~ 0xFFFF 区间为 SIG 的公共预留池(Vendor-Specific 区间) 任何厂商都可以临时借用。
这个区间 SIG 作为公共预留区间,一般用作示例测试参考,属于临时方案,想要作为产品,要么去 SIG 申请正式分配,要么直接用 128-bit 自建 UUID。
注意事项:
- 整个 16-bit 空间都由 Bluetooth SIG 的官方文档 《Assigned Numbers》统一管理,也就是下面的文档链接
- 商用产品不要使用已存在的 UUID,可以用 128-bit 自定义 UUID,或者在 SIG 申请正式分配。
- 同一设备,不同特征 UUID 任何时候都不能一样。
- 自定义的 128-bit UUID,服务 UUID 可以和 特征 UUID 一样,SIG 规定的 16-bit 的服务和特征值的 UUID 不能一样。
更详细的 UUID 介绍,请参考蓝牙联盟的 Assigned_Numbers 文档: https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Assigned_Numbers/out/en/Assigned_Numbers.pdf

4.1.2 Properties
特征的关键属性(Properties)
特征通过 “属性” 定义数据的操作方式,常见属性包括:
ble.READ:可读 - 客户端可从特征读取数据(如读取电池电量)
ble.WRITE:有响应写入 - 客户端写入,服务端需确认(如设置设备参数)
ble.WRITE_CMD:无响应写入 - 客户端写入,服务端不确认,快速但可能丢失(如实时控制)
ble.NOTIFY:通知 - 服务端主动推送数据,客户端不确认(如心率实时推送)
ble.INDICATE:指示 - 服务端主动推送数据,客户端需确认(如重要状态更新)
4.2 demo 项目总体设计框图
demo 项目的总体设计框图如下:

4.3 源码分析
4.3.1 文件说明

main.lua:主程序入口文件。ble_client_main.lua:BLE 中心设备主程序,进行 BLE 初始化,处理各类 BLE 事件(连接、断开连接、扫描报告、GATT 操作完成等)。ble_client_receiver.lua:BLE 中心设备接收数据处理。ble_client_sender.lua:BLE 中心设备发送数据处理。ble_timer_app.lua:BLE 中心设备定时器处理逻辑,启动两个循环定时器,一个用于定时读取外围设备特征值 UUID 数据,一个用于定时向外围设备特征值 UUID 发送数据。ble_uart_app.lua:BLE 中心设备接 uart 处理逻辑,将收到的 notify 数据,通过 uart 发送到 pc 端串口工具。check_wifi.lua:Air8000 的蓝牙功能依赖 WiFi 协处理器,需确保 WiFi 固件为最新版本。本脚本文件检查当前 Air8000 模组的 WiFi 固件是否为最新版本,若不是则自动启动升级(需插入可联网的 SIM 卡)
4.3.2 ble_client_main.lua
本文件为 ble client 主应用功能模块,整个应用通过 sysplus.taskInitEx 启动主任务,实现了完整的 BLE 中心设备功能,包括设备发现、连接管理、数据收发和异常恢复机制。核心业务逻辑为:
4.3.2.1 初始化与配置
- 加载依赖模块( ble_client_receiver 用于数据接收处理, ble_client_sender 用于数据发送处理)。
- 定义配置参数(目标设备名称、服务 UUID、特征值 UUID、超时时间等)。
- 调用 ble_init(),初始化蓝牙功能。
4.3.2.2 设备扫描与连接
- 创建并启动 BLE 扫描(默认参数:公共地址模式、扫描间隔 100ms、扫描窗口 100ms)。
- 通过 is_target_device 函数过滤扫描到的设备(匹配设备名称)。
- 发现目标设备后停止扫描并发起连接。
- 连接超时处理和重连机制。
4.3.2.3 事件处理
通过 ble_event_cb 回调函数处理各类 BLE 事件:
- 连接成功(EVENT_CONN)
- 断开连接(EVENT_DISCONN)
- 扫描报告(EVENT_SCAN_REPORT)
- GATT 操作完成(EVENT_GATT_DONE)
- 读取特征值完成(EVENT_READ_VALUE)
4.3.2.4 业务功能
- GATT 服务发现完成后,自动启用目标特征值的通知监听。
- 接收并处理来自其他模块的读取请求(
READ_REQ)。 - 通过消息队列与其他模块通信。
4.3.2.5 异常处理
- 扫描超时或连接失败时触发异常处理。
- 断开连接后自动清理消息队列并尝试重连。
- 异常情况下 5 秒后重新开始扫描连接。
4.3.3 ble_client_receiver.lua
4.3.3.1 主要功能
- 数据分类处理 :接收两类数据(外围设备通知数据和主动读取到的数据)。
- 数据分发 :根据数据类型(通过特征值 UUID 区分)发布到不同的消息队列,供其他模块处理。
4.3.3.2 核心实现
- 提供
proc(service_uuid, char_uuid, data)接口函数,接收服务 UUID、特征值 UUID 和数据。 - 通过判断特征值 UUID 是否匹配配置中的
target_notify_char或target_read_char来区分数据类型。 - 对于外围设备的通知数据,通过
sys.publish("RECV_BLE_NOTIFY_DATA", ...)发布。 - 对于主动读取到的数据,通过
sys.publish("RECV_BLE_READ_DATA", ...)发布。
4.3.3.3 数据流转
- 当
ble_client_main模块接收到EVENT_READ_VALUE事件时,会调用此模块的 proc 函数。 - 本模块将数据分类后发布到对应的消息主题。
- 其他订阅了这些消息主题的模块可以接收并处理数据。
4.3.4 ble_client_sender.lua
其他模块只需发布 "SEND_DATA_REQ" 消息即可请求中心设备向外围设备发送数据。
4.3.4.1 主要功能
- 消息订阅:订阅
"SEND_DATA_REQ"消息,接收其他模块的发送请求。 - 队列管理:维护发送队列 send_queue,存储待发送的数据项(包含服务 UUID、特征值 UUID、数据、回调信息)。
- 任务调度:通过任务处理函数 ble_client_sender_task_func 处理各类 BLE 事件。
- 数据发送:按顺序发送队列中的数据,并通过回调通知发送结果。
4.3.4.2 核心实现
- 其他模块通过
sys.publish("SEND_DATA_REQ", ...)发布 发送请求。 send_data_req_proc_func函数将请求数据加入发送队列,并通知任务。- 任务处理函数根据 BLE 事件状态(连接成功、断开连接等)处理队列数据。
send_item_func函数负责实际发送数据。send_item_cbfunc函数处理发送结果,调用用户回调。
4.3.4.3 事件处理
CONNECT_OK:BLE 连接成功,开始发送队列数据。SEND_REQ:有新数据需要发送,继续处理队列。DISCONNECTED:连接断开,清空队列并通知所有发送请求失败。
4.3.5 ble_uart_app.lua
- UART 初始化:打开 UART1 接口,配置波特率 115200、数据位 8、停止位 1、无奇偶校验。
- 数据接收:订阅 "RECV_BLE_NOTIFY_DATA" 消息,接收来自 BLE 外围设备的通知数据。
- 数据转发:将接收到的 BLE 数据(包含服务 UUID、特征值 UUID 和实际数据)格式化后通过 UART1 发送到 PC 端。
4.3.6 ble_timer_app
4.3.6.1 主要功能
-
创建两个独立的 5 秒循环定时器
一个用于定时 发送 数据到外围设备特定特征值 UUID。
一个用于定时 读取 外围设备特定特征值 UUID 的数据。
4.3.6.2 核心实现
- 定义了目标服务 UUID( FA00 )和特征值 UUID(写: EA02 ,读: EA03 )。
- 实现数据发送结果回调函数 send_data_cbfunc ,用于处理发送成功/失败的通知。
-
实现两个定时器回调函数:
send_data_req_timer_cbfunc:发布SEND_DATA_REQ消息到 ble_client_sender 模块。read_data_req_timer_cbfunc:发送READ_REQ请求到 ble_client_main 模块。 -
启动两个 5 秒循环定时器,分别绑定上述两个回调函数。
4.3.6.3 数据流转
- 发送流程:定时器触发 → 发布
SEND_DATA_REQ消息 →ble_client_sender处理 → 回调通知结果。 - 读取流程:定时器触发 → 发送
READ_REQ请求 →ble_client_main处理 → 结果通过事件返回。
4.4 日志分析
接下来用两个 Air8000 核心板,一个烧录外围设备的 demo,另一个烧录中心设备的 demo,来演示中心设备如何读,写操作。
1、中心设备订阅外围设备特征通知(Notify)

2、中心设备写入特征值(Write)

3、中心设备主动读取特征值数据(Read)

第五部分:LuatOS 上的广播模式应用开发流程
BLE——广播模式代码:
https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/demo/ble/ibeacon
BLE——广播模式文档:
https://docs.openluat.com/air8000/luatos/app/BLE/ibeacon/
接下来通过典型应用 ibeacon 来了解广播模式:
5.1 前置知识了解
5.1.1 ibeacon 介绍
1、ibeacon 技术是 Apple 公司在 2013 年 9 月发布的一种基于 BLE 蓝牙的通信协议,主要用于短距离传送少量数据。
它通过周期性广播包含唯一标识符(UUID、Major、Minor)的数据包,使智能设备在接收信号后,结合信号强度(RSSI)估算距离,实现室内定位、场景触发等功能。
2、ibeacon 规定了一个 30 个字节的广播包。其中需要重点解析的是后 21 个字节(即从 UUID 开始),此前字节重在标识是否为 ibeacon 协议。
ibeacon 广播数据包的完整格式如下:
| 字段 | 长度 | 值 |
| 标志位 | 3字节 | 0x02,0x01,0x06 |
| 长度 | 1字节 | 0x1A |
| 类型 | 1字节 | 0xFF |
| 公司标识符 | 2字节 | 自定义 |
| ibeacon类型标识符 | 1字节 | 0x02 |
| ibeacon数据长度 | 1字节 | 0x15 |
| Proximity UUID | 16字节 | 自定义 |
| Major | 2字节 | 自定义 |
| Minor | 2字节 | 自定义 |
| Tx Power | 1字节 | 自定义 |
一个 30 字节的完整的 iBeacon 可做如下拆解:
-
AD Structure1(Advertising Data Structure)
-
长度字段(1 字节):此处一般为 0x02(即十进制 2),表示该 AD Structure 后续数据的总字节数
- 类型字段(1 字节):0x01(Flags,广播标志位)
- 数据(1 字节):0x06(Flags 的值,表示可被发现且支持 BLE 通用模式,Flags 见 7.2 章节)
-
AD Structure2
-
长度字段(1 字节):此处一般为 0x1A(即十进制 26)表示后续数据的总字节数
- 类型字段(1 字节):固定为 0xFF,表示厂商特定数据。
- 数据(此处为公司标识符)(2 字节):苹果的公司 ID 为 0x004C(小端存储为 0x4C 0x00)。
-
iBeacon 有效载荷(仍属于 AD Structure2)
-
iBeacon 类型标识符(1 字节):固定为 0x02,表示子类型为 iBeacon
- iBeacon 数据长度(1 字节):固定为 0x15,表示 ibeacon 后续数据长度为 21 字节 后续数据长度为需要重点解析的 21 字节
- Proximity UUID(16 字节):设备的唯一标识符(如 UUID)。
- Major(2 字节):用于区分区域(如建筑楼层)。
- Minor(2 字节):用于更细粒度的定位(如具体房间)。
- Tx Power(1 字节):校准信号强度的参考值(RSSI at 1m)。
AD Structures 是什么?
在 BLE 协议中,设备通过广播包(Advertising Packet)向外发送数据。
一个广播包可能包含多个 AD Structure(Advertising Data Structure),每个 AD Structure 用于描述不同的信息(例如设备名称、服务 UUID、厂商数据等)。
每个 AD Structure 的格式固定为:
[长度(1字节)] + [类型(1字节)] + [数据(N字节)]
- 长度字段(1 字节):表示 类型 + 数据 的总字节数(即
类型1字节 + 数据N字节)。 - 类型字段(1 字节):定义数据的用途(例如
0xFF表示厂商数据,0x09表示设备名称)。 - 数据字段(N 字节):具体内容,长度由长度字段-1 决定(因为类型占 1 字节)。
AD Structure 的解析规则:
- 顺序无关:AD Structure 的顺序不固定。
- 长度限制:总长度不超过 31 字节(若为扩展广播,可更长,但 iBeacon 不支持)。
- 类型唯一性:同一类型可能重复出现(例如多个厂商数据块)。
在解析时,可以通过以下步骤遍历所有 AD Structure:
- 从广播包首字节开始。
- 读取长度字段(1 字节),确定当前 AD Structure 的总长度(包括类型和数据)。
- 读取类型字段(1 字节),判断数据类型。
- 根据类型,处理后续数据。
常见 AD Structure 类型对照表:
详见:https://www.bluetooth.com/specifications/assigned-numbers/ 2.3Common Data Types 章节
其中标志位,长度,类型我们不需要处理,我们只需要关注 ibeacon 的有效载荷部分,接下来详细介绍下这部分字段内容:
厂商标识符(2 字节):是蓝牙技术中用于唯一标识设备制造商或品牌方的 2 字节(16 位)编码,范围是 0x0000 ~ 0xFFFF(即 0~65535),它的核心作用是区分不同厂商的蓝牙设备,确保数据解析和兼容性。
ibeacon 类型标识符(1 字节):固定为 0x02, 表示子类型为 ibeacon。
ibeacon 数据长度(1 字节):固定为 0x15,表示 ibeacon 数据长度 21 字节。
Proximity UUID(16 字节):通用唯一标识符。
这是一个 128 位(16 字节)的唯一标识符,用于区分你所在的 iBeacon 网络。 例如,一个商店的所有 iBeacon 可以使用同一个 UUID,这样你的应用就可以知道用户进入了该商店区域。 通常,一个组织或一个应用使用同一个 UUID,然后通过 Major 和 Minor 来进一步细分区域和设备。
Major(2 字节):主标识,用于区分同一组织(UUID 相同)下的不同区域或组别。
Major 是一个 16 位的无符号整数,用于将一组相关的设备进行分组。 例如,一个连锁商店的每个分店可以使用相同的 Major 值,这样应用就知道用户进入了哪个分店。
Minor(2 字节):次标识,用于在同一个 Major 组内进行更细粒度的区分。
Minor 也是一个 16 位的无符号整数,用于标识特定的 iBeacon。 例如,在一个分店内,每个货架或区域可以有一个唯一的 Minor 值。这样,应用就可以知道用户接近哪个具体的货架。
Signal Power(1 字节):校准信号强度的参考值,单位 dBm。(该位为 8 位有符号数据,范围为-128 到 127,最高位是符号位)
这个字段是 8 位有符号整数,表示在距离 iBeacon 设备 1 米处测量到的信号强度(RSSI)。这个值用于校准,帮助估算与设备之间的实际距离。设备接收到信标的信号强度(RSSI)后,与这个校准值进行比较,通过信号衰减模型来估算距离。 例如:0xC0 对应-64dBm
- 十六进制 0xC0 的二进制表示为 11000000
- 作为有符号整数,最高位 1 表示负数
- 取反加 1 计算补码值:01000000 = 64
- 因此,0xC0 对应十进制-64
注:
1、如何获取厂商标识符?
(1)向蓝牙技术联盟(SIG)直接申请。
(2)使用已授权厂商的 ID,申请得到其授权。
2、Signal Power 是 8 位有符号整数,例如:0xC0 的二进制表示是 1100 0000,最高位 1 代表负数,对应十进制是-64。
3、厂商标识符是按照小端序存储的,例如:Apple 的公司 ID 是 0x004C(大端序),但在蓝牙数据包中按规范存储为 4C 00(小端序)。
注意:Apple 的 ID 是 0x004C,但是 demo 中需按照 0x4C,0x00 的方式写入存储。
原因:在 BLE 协议中,公司 ID 按照小端序存储。
大端序:高位字节存储在低地址,低位字节存储在高地址,0x004C 在大端序中存储为00 4C
低地址 高地址
+---------+---------+
| 0x00 | 0x4C |
+---------+---------+
小端序:高位字节存储在高地址,低位字节存储在低地址,0x004C 在小端序中存储为4C 00
低地址 高地址
+---------+---------+
| 0x4C | 0x00 |
+---------+---------+
5.1.2 ibeacon 如何利用 Signal Power 和 RSSI 进行距离估算?
ibeacon 通过 Signal Power(出厂校准信号强度) 和 RSSI(接收信号强度) 估算设备与 ibeacon 之间的距离,其核心原理基于蓝牙信号衰减模型。简单来讲就是设备出厂前先根据实际情况,测算在 1 米距离时的信号强度是多少,作为基准值,然后基于蓝牙信号衰减模型,根据实际信号值 RSSI 推算出实际距离。以下是具体方法和实现步骤:
注意:不同的蓝牙设备或相同设备不同的工况甚至不同的场地环境,都会影响 Signal Power 值,因此这个值虽然可以测量,但一定程度上是个经验值,很难测准。故 ibeacon 的距离估算功能只是估算大概范围,并不能用于精准定位。
1、关键概念
2、距离估算公式
iBeacon 使用 对数路径损耗模型(Log-distance Path Loss Model)计算距离:

- d:估算距离(米)。
- n:环境衰减因子(通常 2~4,空旷环境=2,普通室内=2.5-3,复杂环境多障碍物=3.5-4),环境越复杂信号衰减越快。
- Tx Power:Beacon 的校准信号强度(通过实际环境测量拟合,需预先标定)。
- RSSI:手机实际测得的信号强度。
3、示例计算
Tx Power = -60 dBm
RSSI = -80 dBm
n = 2.5(室内环境)

5.2 源码分析
5.2.1 文件说明
main.lua:主程序入口文件。ble_ibeacon.lua:iBeacon 功能的具体实现,负责蓝牙初始化、广播配置和异常处理。check_wifi.lua:Air8000 的蓝牙功能依赖 WiFi 协处理器,需确保 WiFi 固件为最新版本。本脚本文件检查当前 Air8000 模组的 WiFi 固件是否为最新版本,若不是则自动启动升级(需插入可联网的 SIM 卡)
5.2.2 ble_ibeacon.lua
5.2.2.1 全局变量定义
device_name :广播设备名称,
adv_state :广播状态标志,用于跟踪 iBeacon 广播的开启/关闭状态,
ibeacon_data :iBeacon 广播数据包,包含以下结构:
- 厂商标识符(2 字节):0x004C,本例演示的是 ibeacon,所以采用 Apple 的 ID:0x004C(iBeacon 是 Apple 的专有技术,采用 Apple 的 ID 才能显示成 ibeacon)
- ibeacon 类型标识符(2 字节):固定为 0x02,0x15, 表示子类型为 ibeacon。
- Proximity UUID(16 字节):0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
- Major(2 字节):0x00,0x01
- Minor(2 字节):0x00,0x02
- Signal Power(1 字节):0xC0
-- 广播状态
local adv_state = false
-- 配置ibeacon广播数据包
local ibeacon_data = string.char(0x4C, 0x00, -- Manufacturer ID(2字节)
0x02, -- ibeacon数据类型(1字节)
0x15, -- ibeacon数据长度(1字节)
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, -- UUID(16字节)
0x00, 0x01, -- Major(2字节)
0x00, 0x02, -- Minor(2字节)
0xC0) -- Signal Power(1字节)
5.2.2.2 事件回调函数 (ble_callback)
处理 BLE 设备的各种事件:
- ble.EVENT_ADV_START :广播成功启动时设置 adv_state 为 true
- ble.EVENT_ADV_STOP :广播停止时设置 adv_state 为 false
-- 事件回调函数
function ble_callback(ble_device, ble_event)
if ble_event == ble.EVENT_ADV_START then
log.info("iBeacon", "广播已成功启动")
adv_state = true
elseif ble_event == ble.EVENT_ADV_STOP then
log.info("iBeacon", "广播已停止,等待重新启动")
adv_state = false
end
end
5.2.2.3 核心任务函数 (ble_ibeacon_task_func)
这是模块的主要功能实现,采用无限循环结构确保广播稳定运行:
- 初始化蓝牙核心 :创建 bluetooth_device 实例
- 初始化 BLE 功能 :创建 ble_device 实例并注册回调
- 配置广播参数 :通过 adv_create 方法设置:
- 广播地址模式 (ble.PUBLIC)
- 广播通道 (ble.CHNLS_ALL)
- 广播间隔 (intv_min/max = 120)
- 广播数据 (包含标志位、iBeacon 数据和设备名称)
- 启动广播 :调用 adv_start()方法
- 状态监控 :通过 while adv_state 循环监控广播状态
- 异常处理 :使用 goto EXCEPTION_PROC 标签统一处理各种初始化失败情况
- 资源清理与重试 :在异常情况下停止广播并重新初始化,间隔 5 秒后重试
function ble_ibeacon_task_func()
while true do
-- 初始化蓝牙核心
bluetooth_device = bluetooth_device or bluetooth.init()
if not bluetooth_device then
log.error("BLE", "蓝牙初始化失败")
goto EXCEPTION_PROC
end
-- 初始化BLE功能
ble_device = ble_device or bluetooth_device:ble(ble_callback)
if not ble_device then
log.error("BLE", "当前固件不支持完整的BLE")
goto EXCEPTION_PROC
end
-- 设置广播内容
-- 由于没有 "COMPLETE_LOCAL_NAME" ,故仅安卓可用
adv_create = adv_create or ble_device:adv_create({
addr_mode = ble.PUBLIC, -- 广播地址模式, 仅支持: ble.PUBLIC
channel_map = ble.CHNLS_ALL, -- 广播的通道, 可选值: ble.CHNL_37, ble.CHNL_38, ble.CHNL_39, ble.CHNLS_ALL
intv_min = 120, -- 广播间隔最小值, 单位为0.625ms, 最小值为20, 最大值为10240
intv_max = 120, -- 广播间隔最大值, 单位为0.625ms, 最小值为20, 最大值为10240
adv_data = { -- 支持表格形式, 也支持字符串形式(255字节以内)
{ble.FLAGS, string.char(0x06)}, -- 广播标志位
{ble.MANUFACTURER_SPECIFIC_DATA, ibeacon_data}, -- 厂商特定数据, 包含ibeacon数据
}
})
if not adv_create then
log.error("BLE", "BLE创建广播失败")
goto EXCEPTION_PROC
end
log.info("开始广播")
if not ble_device:adv_start() then
log.error("BLE", "BLE广播启动失败")
goto EXCEPTION_PROC
end
adv_state = true
-- 等待直到广播停止
while adv_state do
sys.wait(1000)
end
::EXCEPTION_PROC::
log.info("iBeacon", "检测到广播停止,准备重新初始化")
-- 停止广播
if ble_device then
ble_device:adv_stop()
ble_device = nil
end
-- 5秒后跳转到循环体开始位置,重新广播
sys.wait(5000)
end
end
5.3 日志分析
luatools 日志比较简单,只有开启广播的 log 打印

重点看 nrf connect 扫描出来的 ibeacon 信息:

第六部分:LuatOS 上的扫描模式应用开发流程
BLE——扫描模式代码:
https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/demo/ble/scan
BLE——扫描模式文档:
https://docs.openluat.com/air8000/luatos/app/BLE/scan/
接下来结合 ibeacon 应用,通过分析扫描到的 ibeacon 广播包数据,来了解扫描模式:
6.1 前置知识了解
6.1.1 扫描窗口和扫描间隔
扫描窗口(scan_window):
是指 BLE 设备在扫描过程中,打开接收器去监听广播设备的时间段。这个时间段是设备实际进行扫描操作的时间,也称为扫描事件的持续时间。扫描窗口的单位通常是 0.625ms,并且它的值必须小于或等于扫描间隔。
扫描间隔(scan_interval):
表示两次扫描事件之间的间隔时间。扫描间隔的单位与扫描窗口相同,单位也是 0.625ms。
注:
- 如果扫描窗口与扫描间隔一样长,表明主机一直在扫描。
- 扫描窗口和扫描间隔是在 ble_device:scan_create 创建扫描需要填写的参数。
默认参数, addr_mode=0, scan_interval=100, scan_window=100
ble_device:scan_create(addr_mode, scan_interval, scan_window)
-- addr_mode:地址模式,在BLE规范中,0表示公共地址模式(public address mode)
-- scan_interval:扫描间隔,单位为0.625ms,最小值为20,最大值为10240
-- scan_window:扫描窗口,单位为0.625ms,最小值为20,最大值为10240
6.1.2 扫描到的广播数据如何解码?
ble.EVENT_SCAN_REPORT 事件的 ble.param 包含如下内容:
- addr_type :整数类型,表示蓝牙设备的地址类型
- adv_addr :设备的广播地址,可以通过 :toHex() 方法转换为十六进制字符串形式的 MAC 地址
- rssi :整数类型,表示接收信号强度,单位为 dBm
- data :二进制数据类型,表示接收到的原始广播数据,可以通过 :toHex() 方法转换为十六进制字符串
其中的广播数据 data 可以通过 ble_device:adv_decode(data) 来解码。
ble_device:adv_decode(data)
功能
解码广播数据
注意事项
广播数据通常来自于 ble.EVENT_SCAN_REPORT 事件的 ble_param.data
参数
data
含义说明:原始广播数据;
数据类型:string;
取值范围:广播数据的字符串表示;
是否必选:是;
注意事项:注意参数根据实际情况填写;
返回值
local adv_data = ble_device:adv_decode(data)
adv_data
含义说明:广播数据的解码结果,为table类型,表结构如下:
索引型表(索引从1开始);
每个索引对应一个子表,代表一个广告数据单元(AD Structure);
每个子表包含以下三个字段:
len :整数类型,表示数据长度
tp :整数类型,表示广播数据类型(AD Type)
data :字符串类型(二进制数据),表示广播数据内容
数据类型:table/nil;
取值范围:解码成功返回table对象,失败返回nil。
注意事项:返回的数据需要自行解析提取
返回示例:成功返回:table: 0C7F6570
失败返回:nil
local result = ble_device:adv_decode(data)
log.info("result", result)
示例
-- 解码广播数据
local adv_data = ble_device:adv_decode(ble_param.data)
-- 解析广播数据
if adv_data then
for k, v in pairs(adv_data) do
log.info("ble", "adv data", v.len, v.tp, v.data:toHex())
end
end
6.2 源码分析
6.2.1 文件说明
main.lua:主程序入口文件。ble_scan.lua:ble_scan.lua 是 Air8000 的蓝牙扫描功能实现模块,主要负责初始化蓝牙框架、配置并执行设备的扫描操作,并通过回调函数处理扫描到的设备信息。check_wifi.lua:Air8000 的蓝牙功能依赖 WiFi 协处理器,需确保 WiFi 固件为最新版本。本脚本文件检查当前 Air8000 模组的 WiFi 固件是否为最新版本,若不是则自动启动升级(需插入可联网的 SIM 卡)
6.2.2 ble_scan.lua
6.2.2.1 全局变量定义
scan_state :扫描状态标志,用于跟踪 BLE 扫描的开启/关闭状态,初始值为 false 表示未扫描
-- 扫描状态
local scan_state = false
6.2.2.2 处理扫描报告事件 (handle_scan_report)
- 功能:当扫描到 BLE 设备时,处理扫描报告事件。
- 处理内容:记录并输出发现设备的信息,包括 RSSI 值、设备地址和广播数据等,可以根据需求筛选数据。
- 示例演示了如何筛选 ibeacon 广播数据。
-- 处理扫描报告事件
local function handle_scan_report(ble_device, ble_param)
-- 1. 打印基础设备信息
log.info("ble_scan", "发现设备",
"RSSI:", ble_param.rssi,
"地址:", ble_param.adv_addr:toHex(),
"数据:", ble_param.data:toHex())
-- 2. 打印解析的广播数据
-- local adv_data = ble_device:adv_decode(ble_param.data)
-- if adv_data then
-- for k, v in pairs(adv_data) do
-- -- log.info("ble_scan", "广播数据", "长度:", v.len, "类型:", v.tp, "数据:", v.data:toHex())
-- -- 以下是演示如何筛选ibeacon广播数据
-- -- 检查Manufacturer Specific Data (类型0xFF)
-- if v.tp == 0xFF then
-- local mfg_data = v.data
-- -- iBeacon格式检查
-- if mfg_data:len() >= 25 then
-- local company_id = mfg_data:byte(1) + mfg_data:byte(2) * 256
-- local beacon_type = mfg_data:byte(3) -- 0x02
-- local data_length = mfg_data:byte(4) -- 0x15 (21字节)
-- if beacon_type == 0x02 and data_length == 0x15 then
-- log.info("ble_scan", "发现iBeacon设备")
-- -- 解析iBeacon数据
-- local uuid = mfg_data:sub(5, 20):toHex()
-- local major = mfg_data:byte(21) * 256 + mfg_data:byte(22)
-- local minor = mfg_data:byte(23) * 256 + mfg_data:byte(24)
-- local tx_power_byte = mfg_data:byte(25)
-- local tx_power
-- if tx_power_byte > 127 then
-- tx_power = tx_power_byte - 256
-- else
-- tx_power = tx_power_byte
-- end
-- log.info("ble_scan", "iBeacon详情",
-- "UUID:", uuid,
-- "Major:", major,
-- "Minor:", minor,
-- "TxPower:", tx_power.. " dBm",
-- "RSSI:", ble_param.rssi .. " dBm")
-- end
-- end
-- end
-- end
-- end
end
6.2.2.3 事件回调函数 (ble_callback)
处理 BLE 设备的各种扫描事件:
- ble.EVENT_SCAN_INIT :扫描初始化成功时,记录日志并设置 scan_state 为 true
- ble.EVENT_SCAN_REPORT :接收到扫描报告时,调用 handle_scan_report 函数处理扫描数据
- ble.EVENT_SCAN_STOP :扫描停止时,记录日志并设置 scan_state 为 false
-- 事件回调函数
local function ble_callback(ble_device, ble_event, ble_param)
-- 扫描初始化事件
if ble_event == ble.EVENT_SCAN_INIT then
log.info("ble_scan", "scan init")
scan_state = true
-- 扫描报告事件
elseif ble_event == ble.EVENT_SCAN_REPORT then
handle_scan_report(ble_device, ble_param)
-- 停止扫描事件
elseif ble_event == ble.EVENT_SCAN_STOP then
log.info("ble_scan", "scan stop")
scan_state = false
-- 在这里可以添加自己的扫描停止后的处理逻辑
end
end
6.2.2.4 核心任务函数 (ble_scan_task_func)
这是模块的主要功能实现,采用无限循环结构确保扫描稳定运行:
- 初始化蓝牙核心 :创建 bluetooth_device 实例
- 初始化 BLE 功能 :创建 ble_device 实例并注册回调
- 配置扫描参数 :通过 scan_create()方法设置扫描参数(地址模式、扫描间隔、扫描窗口等)
- 启动扫描 :调用 scan_start()方法开始扫描周围 BLE 设备
- 状态监控 :通过 while scan_state 循环监控扫描状态
- 异常处理 :使用 goto EXCEPTION_PROC 标签统一处理各种初始化失败情况
- 资源清理与重试 :在异常情况下停止扫描并重新初始化,间隔 5 秒后重试
function ble_scan_task_func()
while true do
-- 初始化蓝牙核心
bluetooth_device = bluetooth_device or bluetooth.init()
if not bluetooth_device then
log.error("BLE", "蓝牙初始化失败")
goto EXCEPTION_PROC
end
-- 初始化BLE功能
ble_device = ble_device or bluetooth_device:ble(ble_callback)
if not ble_device then
log.error("BLE", "当前固件不支持完整的BLE")
goto EXCEPTION_PROC
end
-- 创建扫描
if not ble_device:scan_create() then
log.error("BLE", "BLE创建扫描失败")
goto EXCEPTION_PROC
end
log.info("ble_scan", "开始扫描")
if not ble_device:scan_start() then
log.error("ble_scan", "扫描启动失败")
goto EXCEPTION_PROC
end
scan_state = true
-- 等待直到扫描停止
while scan_state do
sys.wait(1000)
end
::EXCEPTION_PROC::
log.info("ble_scan", "检测到扫描异常,准备重新初始化")
-- 停止扫描
if ble_device then
ble_device:scan_stop()
ble_device = nil
end
-- 5秒后跳转到循环体开始位置,重新扫描
sys.wait(5000)
end
end
-- 启动扫描任务
sys.taskInit(ble_scan_task_func)
6.3 日志分析
luatools 日志,可以查看扫描到的 ibeacon 的广播包数据信息

最后再结合 ibeacon 应用,进行简单的距离估算:
代码:
-- 计算大致距离 (米)
-- 使用公式: d = 10^((Tx Power-RSSI)/(10*n))
-- n为环境因子,通常在2-4之间,根据实际环境调整
local function calculate_distance(rssi, tx_power)
log.info("ble_scan", "计算距离", "RSSI:", rssi, "TxPower:", tx_power)
-- 避免无效值
if rssi == 0 or tx_power == 0 then
return -1 -- 无法计算
end
-- 设置环境因子n(可根据实际环境调整)
local n = 2.7 -- 环境因子,通常在2-4之间
-- 计算距离: d = 10^((Tx Power-RSSI)/(10*n))
local exponent = (tx_power - rssi) / (10 * n)
local distance = math.pow(10, exponent)
return distance
end
local distance_str = string.format("%.2f 米", calculate_distance(ble_param.rssi, tx_power))
第七部分:常见问题
7.1 ibeacon 应用,为什么无法显示名称?
答:

一个完整的 BLE 广播数据包最长可以有 37 个字节,其中前 6 个字节固定用于设备 MAC 地址,剩下的 31 个字节才是我们可以自由配置的广播数据区域。这 31 个字节会被划分为若干个广播数据结构体(AD Structure)。
也就是 BLE 广播包 广播数据区域不超过 31 字节,那么 iBeacon 包含的数据和包含 flag 的 AD Structure 已经占了 30 个字节,如果要设置设备命名,会超出 31 字节。
可能有人会问,不是还有 1 个字节的空间吗,设备名称用一个字节不可以吗?
答:
- 名称:
"1"(1 字符)。 - AD Structure 长度:1(数据) + 1(类型) + 1(长度) = 3 字节。
- 总长度:27(iBeacon) + 3(Flags) + 3(名称) = 33 字节 → 仍超出。
7.2 什么是 Flags?
ble.FLAGS 说明
在 BLE 中,标志(flags)通常出现在广播数据中,这些标志用于指示设备的能力和可发现性等。
根据蓝牙核心规范(Bluetooth Core Specification),广播数据中的 Flags 字段是一个 8 位的位图(bitmask),每个位代表特定的含义,以下是各个位的定义(从 LSB 到 MSB):
- Bit 0: LE Limited Discoverable Mode(有限可发现模式)
- Bit 1: LE General Discoverable Mode(通用可发现模式,设备持续可被发现)
- Bit 2: BR/EDR Not Supported(不支持经典蓝牙)
- Bit 3: Simultaneous LE and BR/EDR to Same Device Capable(同一设备同时支持 BLE 和经典蓝牙)
- Bit 4: Simultaneous LE and BR/EDR to Different Devices Capable(支持同时连接不同设备的 BLE 和经典蓝牙)
- Bit 5-7: 保留(Reserved)
注意:Bit 0 和 Bit 1 不能同时被设置。如果同时设置,则视为无效。
Bit1 和 Bit2 被设置时,Flags 字段的值为 0x06(二进制 0000 0110),
Bit2 被设置时,Flags 字段的值为 0x04(二进制 0000 0100)。
7.3 常见的 BLE 断开连接 reason
| 值 | 原因 |
| 0x08 (8) | Connection Timeout(数据传输过程中超时断开连接) |
| 0x09 (9) | Maximum Number Of Connections(已达到最大连接数限制) |
| 0x0C (12) | Command Disallowed(当前状态下不允许执行此命令) |
| 0x13 (19) | Remote User Terminated Connection(远程用户终止连接,表示对方设备主动断开连接) |
| 0x16 (22) | Connection Terminated By Local Host(本地主机终止连接,表示本设备主动断开连接) |
| 0x18 (24) | Unacceptable Connection Parameters(不可接受的连接参数,如连接间隔、超时时间等参数不符合要求) |
| 0x3E (62) | Connection Timeout("连接超时"或"未在规定时间内建立连接") |
示例:
1、中心设备和外围设备在数据传输过程中,把外围设备断电
中心设备会提示 8 的错误码,即数据传输过程中超时断开连接。

2、中心设备和外围设备连接时,中心设备主动发起 disconnect
中心设备会提示 22 的错误码,即本地主机终止连接。

外围设备会提示 19 的错误码,即远程用户终止连接。
