ESP IoT Solution BLE 对象传输服务(OTS)实战指南:基于 L2CAP 的批量数据管理与传输
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
对象传输服务(Object Transfer Service,OTS)是 BLE 标准服务之一,它通过一个独立的、面向连接的逻辑链路控制与适配协议(L2CAP)信道,为设备间的批量二进制数据传输提供完整的管理与控制能力。本文以 esp-iot-solution 仓库中ble_services组件的 OTS 实现为主线,结合标准服务源码与ble_ots示例工程,系统讲解 OTS 的服务结构、特征定义、对象操作控制点(OACP)、对象列表控制点(OLCP)、过滤与事件机制,并给出可直接编译运行的完整配置与接入流程。读完本文,你将能够在自己的 ESP32 系列项目中使用esp_ble_ots_*API 快速构建一个支持对象管理、列表浏览与批量数据传输的 BLE GATT 服务端。
OTS 服务定位:为批量数据而生
蓝牙 SIG 定义的对象传输服务(Service UUID0x1825)提供了一系列管理和控制功能,用于支持通过独立面向连接的 L2CAP 信道进行的批量数据传输。与传统基于 ATT 特征值小包交互的方式不同,OTS 将"对象"(Object)作为数据传输的基本单位,由 GATT 服务负责对象的元数据管理(名称、类型、大小、创建/修改时间、属性、ID 等),而真正的数据内容则可以交由更高吞吐的 L2CAP CoC 信道承载,从而兼顾了管理灵活性与传输效率。
在 esp-iot-solution 中,该服务由 ble_services 组件提供。该组件为常用的标准/自定义 BLE 服务提供了一套统一、简化的 API 接口,覆盖 ANS、BAS、BCS、CTS、DIS、HRS、HTS、IAS、MIDI、OTA、OTS、TPS、UDS、WSS 等十余种服务。其中 OTS 的实现位于 ots 子目录,对外接口定义在 esp_ots.h,核心逻辑在 esp_ots.c,配套的可运行示例为 ble_ots 示例工程。
服务结构:一个服务、十二个特征
按照 OTS 规范,该服务通过一组特征值来描述单个对象的元数据并提供操作入口。esp-iot-solution 的实现将 OTS 服务(UUID0x1825)注册到 GATT 服务器上,共包含 12 个标准特征(其中 3 个可通过 Kconfig 裁剪),如下表所示:
| 特征名 | 特征 UUID | GATT 属性 | 说明 |
|---|---|---|---|
| OTS Feature(OTS 特性) | 0x2ABD | Read | 声明服务支持的操作能力(OACP/OLCP 功能位) |
| Object Name(对象名称) | 0x2ABE | Read / Write | 对象的名称,最大 20 字节 |
| Object Type(对象类型) | 0x2ABF | Read | 对象类型(16 位 UUID 类型标识) |
| Object Size(对象大小) | 0x2AC0 | Read | 当前大小与已分配大小 |
| Object First Created(对象创建时间) | 0x2AC1 | Read / Write(可裁剪) | 对象首次创建时间(UTC) |
| Object Last Modified(对象最后修改时间) | 0x2AC2 | Read / Write(可裁剪) | 对象最后修改时间(UTC) |
| Object ID(对象 ID) | 0x2AC3 | Read | 6 字节对象标识 |
| Object Properties(对象属性) | 0x2AC4 | Read / Write | 对象的权限位(删除/执行/读/写/追加/截断/补丁/标记) |
| Object Action Control Point(OACP) | 0x2AC5 | Write / Indicate | 对象操作控制点(创建、删除、读写、校验和等) |
| Object List Control Point(OLCP) | 0x2AC6 | Write / Indicate | 对象列表控制点(首/末/上一个/下一个/跳转/排序等) |
| Object List Filter(对象列表过滤) | 0x2AC7 | Read / Write | 对对象列表施加过滤条件 |
| Object Changed(对象变更通知) | 0x2AC8 | Indicate(可裁剪) | 通知客户端对象内容或元数据发生变更 |
这些 UUID 与操作码常量全部定义在 esp_ots.h 中,特征的实际注册逻辑见 esp_ots.c 中的特征查找表:每个特征通过esp_ble_conn_character_t描述其名称、UUID 类型、GATT 属性和对应的属性回调函数,最后由esp_ble_conn_add_svc()一次性注册为完整的服务。
核心数据结构与对象模型
要理解 OTS 的 API,需要先掌握其定义的对象模型。所有数据类型均为紧凑打包(__attribute__((packed)))的结构体,便于直接映射为 ATT 载荷:
OTS Feature(服务能力声明)
esp_ble_ots_feature_t用两组位域声明服务端支持的操作:
- OACP 组:
create_op、delete_op、calculate_op(校验和)、execute_op、read_op、write_op、appending_op、truncation_op、patching_op、abort_op; - OLCP 组:
goto_op、order_op、req_num_op(请求对象数量)、clear_mark_op(清除标记)。
客户端通过读取 OTS Feature 即可获知该服务支持哪些操作,避免发出不支持的命令。
Object Size / UTC 时间 / Object ID
esp_ble_ots_size_t包含current_size(当前大小)与allocated_size(已分配大小)两个 32 位字段;esp_ble_ots_utc_t按公历表示 UTC 时间:year(1582–9999)、month(1–12)、day(1–31)、hours(0–23)、minutes(0–59)、seconds(0–59);esp_ble_ots_id_t为 6 字节对象 ID,规范要求取值范围为0x000000000100至0xffffffffffff,即低 8 位恒为 0 的保留位。
Object Properties(对象权限)
esp_ble_ots_prop_t用位域描述单个对象的访问权限:delete_prop、execute_prop、read_prop、write_prop、append_prop、truncate_prop、patch_prop,以及mark_prop(该对象是否为被标记对象)。这些属性由服务端维护,客户端写入时用于更新对象的权限状态。
控制点与过滤结构
esp_ble_ots_oacp_t:op_code+parameter[20],OACP 命令与参数的载体;esp_ble_ots_olcp_t:op_code+parameter[6],OLCP 命令与参数的载体;esp_ble_ots_olcp_rsp_t:req_code(回显原操作码)+rsp_code(结果码)+rsp_parameter[6];esp_ble_ots_filter_t:filter(过滤类型)+filter_parameter[20];esp_ble_ots_change_t:flag(变更标志位)+object_id[6],用于 Object Changed 特征。
OACP:对象操作控制点协议
对象操作控制点(OACP,UUID0x2AC5)是客户端对对象执行"动作"的入口,属性为 Write + Indicate——客户端写入命令,服务端通过 Indicate 返回响应。esp-iot-solution 中定义的 OACP 操作码如下:
| 操作码 | 名称 | 含义 |
|---|---|---|
0x01 | Create | 创建对象 |
0x02 | Delete | 删除对象 |
0x03 | Calculate Checksum | 计算校验和 |
0x04 | Execute | 执行对象 |
0x05 | Read | 读取对象内容 |
0x06 | Write | 写入对象内容 |
0x07 | Abort | 中止当前操作 |
0x60 | Response | 服务端响应的操作码 |
服务端在处理完命令后,以0x60(Response)操作码携带结果码返回,结果码语义如下:
| 结果码 | 含义 |
|---|---|
0x01 | 操作成功 |
0x02 | 不支持该操作 |
0x03 | 参数无效 |
0x04 | 资源不足 |
0x05 | 对象无效 |
0x06 | 信道不可用 |
0x07 | 不支持的类型 |
0x08 | 操作不被允许 |
0x09 | 对象被锁定 |
0x0A | 操作失败 |
OLCP:对象列表控制点协议
对象列表控制点(OLCP,UUID0x2AC6)用于在服务端维护的对象列表中导航与排序,同样采用 Write + Indicate 交互模式。操作码如下:
| 操作码 | 名称 | 含义 |
|---|---|---|
0x01 | First | 跳转到第一个对象 |
0x02 | Last | 跳转到最后一个对象 |
0x03 | Previous | 跳转到上一个对象 |
0x04 | Next | 跳转到下一个对象 |
0x05 | Go To | 跳转到指定对象 |
0x06 | Order | 指定列表排序方式 |
0x07 | Request Number of Objects | 请求对象数量 |
0x08 | Clear Marking | 清除标记 |
0x70 | Response | 服务端响应 |
其中 Order(0x06)命令需要携带排序参数:按名称、类型、大小、首次创建时间、最后修改时间分别支持升序(0x01–0x05)与降序(0x11–0x15),例如0x01表示"按名称升序",0x15表示"按最后修改时间降序"。OLCP 响应码(针对0x70)包括:成功(0x01)、不支持(0x02)、参数无效(0x03)、操作失败(0x04)、越界(0x05)、对象过多(0x06)、无对象(0x07)、对象 ID 未找到(0x08)。
对象列表过滤
对象列表过滤特征(0x2AC7)允许客户端按条件缩小列表范围,过滤类型(esp_ble_ots_filter_t.filter)包括:按名称起始(0x01)、名称结束(0x02)、名称包含(0x03)、名称精确匹配(0x04)、对象类型(0x05)、创建时间(0x06)、修改时间(0x07)、当前大小(0x08)、已分配大小(0x09)、标记对象(0x0A)。
事件机制:从 GATT 回调到应用层
OTS 组件的事件设计是理解其 API 的关键。以 esp_ots.c 的实现来看:
统一事件基:组件声明了事件基
BLE_OTS_EVENTS,应用通过esp_event_handler_register(BLE_OTS_EVENTS, ESP_EVENT_ANY_ID, ...)订阅所有 OTS 事件;事件 ID 直接使用特征 UUID(如BLE_OTS_CHR_UUID16_OBJECT_NAME),例如 示例中的事件处理函数 分别处理对象名称写入与对象属性写入事件。回调写入与事件广播:特征查找表中每个特征都绑定了属性回调(如
ots_object_name_cb、ots_object_oacp_cb)。当远端客户端写入特征值时,回调先把数据拷贝进组件内部的全局对象(如s_obj_name、s_obj_oacp),再通过esp_event_post(BLE_OTS_EVENTS, ...)异步广播给应用层,最后以ESP_IOT_ATT_SUCCESS作为 ATT 状态返回。对于只读特征(如 OTS Feature、Object Type、Object Size、Object ID),回调会分配缓冲区并返回当前值。并发保护:所有全局对象均通过互斥锁
s_ots_mutex保护(获取超时 2 秒),esp_ble_ots_get_*/esp_ble_ots_set_*系列 API 在临界区内完成拷贝,保证多任务环境下数据一致性。主动下发:
esp_ble_ots_set_oacp()、esp_ble_ots_set_olcp()、esp_ble_ots_set_change()均带need_send参数——置为true时,组件会通过esp_ble_conn_write()将值写入对应特征并主动推送给已连接的 GATT 客户端(如 OACP/OLCP 的 Indicate 响应、Object Changed 通知)。
API 一览:开箱即用的读写接口
esp_ots.h 为每个特征都提供了配套的 get/set 接口,模式统一、易于上手:
- 生命周期:
esp_ble_ots_init()(创建互斥锁并注册服务)、esp_ble_ots_deinit()(注销服务并删除互斥锁); - OTS Feature:
esp_ble_ots_get_feature()/esp_ble_ots_set_feature(); - 对象名称:
esp_ble_ots_get_name(buf, buf_len, &out_len)(支持截断检测,缓冲区不足时返回ESP_ERR_INVALID_SIZE)/esp_ble_ots_set_name(); - 对象类型:
esp_ble_ots_get_type()/esp_ble_ots_set_type(); - 对象大小:
esp_ble_ots_get_size()/esp_ble_ots_set_size(); - 创建/修改时间:
esp_ble_ots_get/set_first_create_time()、esp_ble_ots_get/set_last_modify_time(); - 对象 ID / 属性:
esp_ble_ots_get/set_id()、esp_ble_ots_get/set_prop(); - 控制点与过滤:
esp_ble_ots_get/set_oacp()、esp_ble_ots_get/set_olcp()、esp_ble_ots_get/set_filter()、esp_ble_ots_get/set_change()。
所有接口均在参数非法时返回ESP_ERR_INVALID_ARG,成功返回ESP_OK;init在互斥锁创建失败时返回ESP_ERR_NO_MEM。需要说明的是,本组件提供的 OTS 侧重于标准服务的特征值管理与通信框架,对象内容的实际存取(如创建/删除/读写对象数据、L2CAP 信道的数据搬运)由上层业务实现,这也与示例工程的定位一致。
编译配置:按需裁剪特征
OTS 通过 Kconfig.in 提供编译期配置,菜单项为BLE Standard Services --> GATT Object Transfer Service(即CONFIG_BLE_OTS),启用后还有三个可裁剪的子选项(默认均开启):
BLE_OTS_FIRST_CREATED_CHARACTERISTIC_ENABLE:是否注册 Object First Created 特征(0x2AC1);BLE_OTS_LAST_MODIFIED_CHARACTERISTIC_ENABLE:是否注册 Object Last Modified 特征(0x2AC2);BLE_OTS_OBJECT_CHANGE_CHARACTERISTIC_ENABLE:是否注册 Object Changed 特征(0x2AC8)。
从 esp_ots.c 的特征查找表 可以看到,这三项分别用#ifdef包裹,关闭后可减少服务占用的 ATT 句柄与内存。在 IDF 环境中可通过idf.py menuconfig进入对应菜单调整。
示例工程:快速跑通 BLE OTS
ble_ots 示例 是一个完整的 GATT 服务端工程:创建 OTS 服务后开始广播,等待 GATT 客户端连接。该示例面向 OTS 基础通信能力的验证——不实现完整的对象操作逻辑(如 OACP/OLCP 的创建/删除/读写流程及元数据处理),如需体验完整对象操作,可在其基础上按前文协议实现业务层。支持目标芯片:ESP32、ESP32-C3、ESP32-C2、ESP32-S3、ESP32-H2。
环境与硬件
- 一块上述任一 ESP32 系列开发板 + USB 数据线;
- 安装并配置好 ESP-IDF(示例的 idf_component.yml 要求
idf: ">=4.3")。
配置、编译与烧录
# 1. 设置目标芯片 idf.py set-target <chip_name> # 2. 打开配置菜单 idf.py menuconfig在Example Configuration菜单中可配置:
Advertisement name(CONFIG_EXAMPLE_BLE_ADV_NAME):广播中的设备名称,默认BLE_OTS,最长 29 个字符;Subsequent advertisement data(CONFIG_EXAMPLE_BLE_SUB_ADV):后续广播数据,默认SUB_ADV。
在BLE Standard Services菜单中按需配置 OTS 选项(sdkconfig.defaults 默认已开启蓝牙协议栈与 OTS,并设置CONFIG_BLE_CONN_MGR_ROLE_PERIPHERAL=y,即外设角色)。
# 3. 编译、烧录并打开串口监视器 idf.py -p PORT flash monitor(退出串口监视器请按Ctrl-]。)使用通用 BLE 扫描器 App 即可完成服务发现与连接测试。启动后串口输出类似:
I (330) BLE_INIT: BT controller compile version [9359a4d] I (350) phy_init: phy_version 1150,7c3c08f,Jan 24 2024,17:32:21 I (420) blecm_nimble: BLE Host Task Started I (420) blecm_nimble: No characteristic(0x2a00) found I (430) NimBLE: GAP procedure initiated: stop advertising. I (440) NimBLE: GAP procedure initiated: advertise; I (440) NimBLE: adv_channel_map=0 own_addr_type=0 adv_filter_policy=0 adv_itvl_min=256 adv_itvl_max=256 I (460) main_task: Returned from app_main()示例代码剖析
app_main.c 的初始化流程具有代表性,可直接迁移到自己的工程:
- 初始化 NVS 并创建默认事件循环;
- 注册
BLE_CONN_MGR_EVENTS事件处理器,随后用esp_ble_conn_config_t(设备名、广播数据)调用esp_ble_conn_init()完成连接管理器初始化; - 调用
esp_ble_ots_init()注册 OTS 服务,并注册BLE_OTS_EVENTS事件处理器; - 调用
esp_ble_conn_start()开始广播等待连接;失败时依次执行esp_ble_conn_deinit()、esp_ble_ots_deinit()并注销事件处理器做清理。
值得一提的是连接事件的联动:在ESP_BLE_CONN_EVENT_CONNECTED回调中,示例通过esp_ble_ots_set_size()将对象大小设置为"已分配 256 字节、当前 0 字节"(见 app_main.c),演示了连接后如何初始化对象元数据,客户端随后读取 Object Size 特征即可感知。
将 OTS 集成到自有项目
除了直接使用示例工程,还可以通过组件管理器将服务组件引入项目:
# 添加 ble_services 组件依赖(内含 OTS) idf.py add-dependency "espressif/ble_services=*" # 或直接以示例为模板创建工程 idf.py create-project-from-example "espressif/ble_services=*:ble_ots"注意:ble_services组件依赖 ble_conn_mgr才能正常工作,示例的 idf_component.yml 已同时声明ble_conn_mgr与ble_services两个依赖。集成后,只需在应用代码中完成esp_ble_conn_init()→esp_ble_ots_init()→ 注册事件处理 →esp_ble_conn_start()的流程,即可对外暴露完整的 OTS GATT 服务。
小结
esp-iot-solution 的 OTS 实现将蓝牙 SIG 对象传输服务封装为一组简洁的esp_ble_ots_*API:特征定义完整(12 个标准特征、可裁剪)、控制点协议齐备(OACP 8 种操作码、OLCP 8 种操作码与 10 种排序规则、10 种列表过滤类型)、事件机制统一(基于BLE_OTS_EVENTS事件基与特征 UUID 事件 ID),并借助ble_conn_mgr完成服务注册与连接管理。对于需要"对象元数据管理 + 批量数据传输"场景(如固件升级、文件同步、传感器数据采集导出)的 ESP32 项目,这是一个经过标准定义约束、开箱即用的基础服务组件,值得直接在此基础上扩展业务逻辑。
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考