ESP IoT Solution 设备信息服务(BLE DIS)开发指南:GATT 服务构建与特征值读写实战
2026/9/19 16:10:18 网站建设 项目流程

ESP IoT Solution 设备信息服务(BLE DIS)开发指南:GATT 服务构建与特征值读写实战

【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution

设备信息服务(Device Information Service,DIS)是蓝牙低功耗(BLE)协议栈中用于对外公开设备制造商、供应商与产品版本信息的标准 GATT 服务。本文基于 esp-iot-solution 仓库中的 BLE 标准服务实现(docs/en/bluetooth/ble_dis.rst)与配套示例工程,讲解如何基于esp_ble_conn_mgr连接管理框架快速注册 DIS 服务、配置各特征值与 PnP ID,并通过esp_ble_dis_*API 在连接事件中读取设备信息,帮助读者掌握在 ESP32 系列芯片上集成标准 BLE 服务与连接管理的完整实战路径。

一、DIS 服务是什么

DIS(Device Information Service)是 Bluetooth SIG 定义的标准 GATT 服务(Service UUID 为 0x180A),其核心职责是公开关于设备的制造商和/或供应商信息。在 esp-iot-solution 的文档中,这一服务被描述为 "exposes manufacturer and/or vendor information about a device",即向 BLE 对端(如手机 App、网关)暴露设备型号、序列号、固件/硬件/软件版本、厂商名、System ID 以及 PnP ID 等元数据。

该服务的典型应用场景包括:

  • 设备身份识别:客户端扫描到广播后,通过读取 DIS 特征值即可获知设备的型号与厂商,用于在 UI 中展示设备类型;
  • 固件版本校验:OTA 升级前读取 Firmware Revision,判断是否需要推送新版本;
  • 产测与溯源:通过 Serial Number 唯一标识每一台设备实例,配合 System ID 与 PnP ID 完成设备追踪。

在 esp-iot-solution 中,DIS 属于 components/bluetooth/ble_services/dis 下的标准 BLE 服务组件,与 ANS、BAS、HRS 等服务并列,统一基于 components/bluetooth/ble_conn_mgr 连接管理框架实现,而非直接操作 NimBLE/Bluedroid 底层 API。

二、仓库结构与示例总览

DIS 相关资源在仓库中分布在两个位置:

位置内容作用
components/bluetooth/ble_services/dis/include/esp_dis.h服务 UUID、特征值 UUID、数据结构与全部 API 声明组件头文件,供应用层调用
components/bluetooth/ble_services/dis/src/esp_dis.c服务注册、特征值回调与读写实现组件源码
components/bluetooth/ble_services/dis/Kconfig.in组件配置菜单(Kconfig)定义各特征值的开关与默认值
examples/bluetooth/ble_services/ble_dis/main/app_main.c示例主程序演示初始化、配置与事件回调
examples/bluetooth/ble_services/ble_dis/README.md示例说明编译、烧录与运行指引

示例工程 examples/bluetooth/ble_services/ble_dis 创建一个 GATT Server 并开始广播,等待 GATT Client 连接;它基于 BLE 连接管理(esp_ble_conn_mgr)构建,官方支持 ESP32、ESP32-C3、ESP32-C2、ESP32-S3、ESP32-H2 等芯片目标。文档(ble_dis.rst)中给出的核心入口为:

:example:`bluetooth/ble_services/ble_dis`.

即直接引用该示例工程作为实战起点。

三、源码级剖析:DIS 组件如何工作

3.1 服务与特征值 UUID 定义

在 esp_dis.h 中定义了服务与特征值的 16 位 UUID:

UUID含义
BLE_DIS_UUID160x180ADIS 服务
BLE_DIS_CHR_UUID16_SYSTEM_ID0x2A23System ID
BLE_DIS_CHR_UUID16_MODEL_NUMBER0x2A24Model Number(型号)
BLE_DIS_CHR_UUID16_SERIAL_NUMBER0x2A25Serial Number(序列号)
BLE_DIS_CHR_UUID16_FIRMWARE_REVISION0x2A26Firmware Revision(固件版本)
BLE_DIS_CHR_UUID16_HARDWARE_REVISION0x2A27Hardware Revision(硬件版本)
BLE_DIS_CHR_UUID16_SOFTWARE_REVISION0x2A28Software Revision(软件版本)
BLE_DIS_CHR_UUID16_MANUFACTURER_NAME0x2A29Manufacturer Name(厂商名)
BLE_DIS_CHR_UUID16_REG_CERT0x2A2ARegulatory Certification Data(法规认证数据)
BLE_DIS_CHR_UUID16_PNP_ID0x2A50PnP ID

其中 0x2A2A 仅在头文件中声明了 UUID 宏,当前实现并未为它注册特征值回调;其余特征值均可在 Kconfig 中按需启用。

3.2 数据结构

组件用两个核心结构体承载设备信息(见 esp_dis.h):

esp_ble_dis_pnp_t(PnP ID,__attribute__((packed))紧凑排列):

typedef struct esp_ble_dis_pnp { uint8_t src; /*!< The vendor ID source */ uint16_t vid; /*!< The product vendor from the namespace in the vendor ID source*/ uint16_t pid; /*!< Manufacturer managed identifier for this product*/ uint16_t ver; /*!< Manufacturer managed version for this product*/ } __attribute__((packed)) esp_ble_dis_pnp_t;

esp_ble_dis_data_t(保存全部特征值字符串与 PnP ID):

typedef struct esp_ble_dis_data { char model_number[CONFIG_BLE_DIS_STR_MAX]; char serial_number[CONFIG_BLE_DIS_STR_MAX]; char firmware_revision[CONFIG_BLE_DIS_STR_MAX]; char hardware_revision[CONFIG_BLE_DIS_STR_MAX]; char software_revision[CONFIG_BLE_DIS_STR_MAX]; char manufacturer_name[CONFIG_BLE_DIS_STR_MAX]; char system_id[CONFIG_BLE_DIS_STR_MAX]; esp_ble_dis_pnp_t pnp_id; } esp_ble_dis_data_t;

字符串存储长度由CONFIG_BLE_DIS_STR_MAX决定(默认 32,范围 2~64),超出长度的写入会被截断(见源码中MIN(CONFIG_BLE_DIS_STR_MAX, strlen(value))的处理)。

3.3 特征值回调与只读语义

DIS 的特征值均为只读(BLE_CONN_GATT_CHR_READ)。在 esp_dis.c 中通过nu_lookup_table将每个特征值与回调函数绑定:

static const esp_ble_conn_character_t nu_lookup_table[] = { #ifdef CONFIG_BLE_DIS_SYSTEM_ID {"system_id", BLE_CONN_UUID_TYPE_16, BLE_CONN_GATT_CHR_READ, { BLE_DIS_CHR_UUID16_SYSTEM_ID }, esp_dis_system_id_cb}, #endif #ifdef CONFIG_BLE_DIS_MODEL {"model_number", BLE_CONN_UUID_TYPE_16, BLE_CONN_GATT_CHR_READ, { BLE_DIS_CHR_UUID16_MODEL_NUMBER }, esp_dis_model_number_cb}, #endif /* ... */ #ifdef CONFIG_BLE_DIS_PNP {"pnp_id", BLE_CONN_UUID_TYPE_16, BLE_CONN_GATT_CHR_READ, { BLE_DIS_CHR_UUID16_PNP_ID }, esp_dis_pnp_id_chr_cb}, #endif }; static const esp_ble_conn_svc_t svc = { .type = BLE_CONN_UUID_TYPE_16, .uuid = { .uuid16 = BLE_DIS_UUID16, }, .nu_lookup_count = sizeof(nu_lookup_table) / sizeof(nu_lookup_table[0]), .nu_lookup = (esp_ble_conn_character_t *)nu_lookup_table };

每个字符串回调(如esp_dis_model_number_cb)遵循统一模式:对读请求用strndup复制对应字符串到输出缓冲区,并设置*att_status = ESP_IOT_ATT_SUCCESS;当资源不足时返回ESP_IOT_ATT_INSUF_RESOURCE,参数非法时返回ESP_IOT_ATT_INTERNAL_ERROR。PnP ID 回调则直接calloc一块sizeof(esp_ble_dis_pnp_t)内存并memcpy结构体内容,保证以紧凑的二进制格式(5 字节)上报。

3.4 服务的注册与注销

服务注册通过连接管理框架完成:

esp_err_t esp_ble_dis_init(void) { return esp_ble_conn_add_svc(&svc); } esp_err_t esp_ble_dis_deinit(void) { return esp_ble_conn_remove_svc(&svc); }

也就是说,DIS 组件本身不直接接触 GATT 栈 API,而是把"服务表"交给esp_ble_conn_mgr统一注册,从源码结构看,esp_ble_conn_mgr负责在 Host 启动后批量添加服务并处理连接/MTU 更新等事件。

四、Kconfig 配置详解

DIS 的全部可配置项位于 Kconfig.in,可通过idf.py menuconfigBLE Standard Services菜单下进入GATT Device Information service配置。各配置项说明如下:

4.1 基础配置

配置项类型默认值说明
BLE_DISbool-DIS 服务总开关(menuconfig)
BLE_DIS_STR_MAXint32字符串存储长度,范围 2~64
BLE_DIS_MODELstringESP设备型号
BLE_DIS_MANUFstringManufacturer厂商名
BLE_DIS_SYSTEM_IDstringSystemSystem ID(注意:此处为字符串类型,按文本上报)

4.2 PnP ID 相关配置

配置项类型默认值说明
BLE_DIS_PNPbooly是否启用 PnP ID 特征值
BLE_DIS_PNP_VID_SRCint1Vendor ID Source:1 = Bluetooth SIG 分配,2 = USB IF 分配(范围 1~2)
BLE_DIS_PNP_VIDhex0x0Vendor ID,与 VID Source 配合标识厂商(范围 0x0~0xFFFF)
BLE_DIS_PNP_PIDhex0x0Product ID,厂商自管的产品标识(范围 0x0~0xFFFF)
BLE_DIS_PNP_VERhex0x1Product Version,BCD 编码的版本号(范围 0x0~0xFFFF)

其中BLE_DIS_PNP_VER采用 Binary-Coded Decimal 编码:值为0xJJMN表示版本JJ.M.N。例如版本 2.1.3 编码为0x0213,版本 2.0.0 编码为0x0200。规范建议:向上兼容的变更递增 minor 版本号,不兼容变更递增 major 版本号,bug 修复递增 sub-minor 版本号。

4.3 版本类特征值开关

配置项类型说明
BLE_DIS_SERIAL_NUMBERbool启用 Serial Number 特征值
BLE_DIS_SERIAL_NUMBER_STRstringSerial Number 内容(依赖前者)
BLE_DIS_FW_REVbool启用 Firmware Revision 特征值
BLE_DIS_FW_REV_STRstringFirmware Revision 内容
BLE_DIS_HW_REVbool启用 Hardware Revision 特征值
BLE_DIS_HW_REV_STRstringHardware Revision 内容
BLE_DIS_SW_REVbool启用 Software Revision 特征值
BLE_DIS_SW_REV_STRstringSoftware Revision 内容

在示例工程中,sdkconfig.defaults 通过以下配置同时打开 NimBLE 协议栈、连接管理外设角色与 DIS 各特征值:

CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y CONFIG_BLE_CONN_MGR_ROLE_PERIPHERAL=y CONFIG_BLE_DIS=y CONFIG_BLE_DIS_SERIAL_NUMBER=y CONFIG_BLE_DIS_FW_REV=y CONFIG_BLE_DIS_HW_REV=y CONFIG_BLE_DIS_SW_REV=y CONFIG_BLE_DIS_PNP=y

注意BLE_DIS_MODELBLE_DIS_MANUFBLE_DIS_SYSTEM_ID等字符串项在 Kconfig 中直接给出默认值即视为启用;而版本类特征值需要同时打开 bool 开关并填写字符串内容。示例同时提供了 sdkconfig.ci.nimble 用于 CI 环境的 NimBLE 配置。

五、示例工程实战

5.1 环境准备与硬件要求

示例官方支持 ESP32、ESP32-C3、ESP32-C2、ESP32-S3、ESP32-H2 芯片。硬件上需要一块对应的开发板、一根 USB 线(供电与烧录)。开始前先设置目标芯片:

idf.py set-target <chip_name>

例如idf.py set-target esp32c3

5.2 工程配置

运行idf.py menuconfig打开配置界面:

  • Example Configuration菜单中,通过Advertisement name修改广播设备名(默认BLE_DIS);Subsequent advertisement data可设置后续广播数据(默认SUB_ADV);Settings Device Information开关用于决定是否在运行期覆写设备信息(默认开启);
  • BLE Standard Services菜单中,通过GATT Device Information service选择需要启用的可选特征值(默认关闭,示例通过 sdkconfig.defaults 显式开启)。

5.3 编译、烧录与监控

idf.py -p PORT flash monitor

PORT替换为实际串口(如/dev/ttyUSB0)。退出串口监控按Ctrl-]

5.4 初始化流程解析

app_main.c 的启动流程分四步:

  1. 初始化 NVS:调用nvs_flash_init(),若返回ESP_ERR_NVS_NO_FREE_PAGESESP_ERR_NVS_NEW_VERSION_FOUND则先nvs_flash_erase()再重试;
  2. 创建并注册事件循环esp_event_loop_create_default()后,用esp_event_handler_register(BLE_CONN_MGR_EVENTS, ESP_EVENT_ANY_ID, app_ble_conn_event_handler, NULL)订阅连接管理事件;
  3. 初始化连接管理esp_ble_conn_init(&config),配置项包括广播名device_name与后续广播数据broadcast_data
  4. 初始化 DIS 并启动app_ble_dis_init()调用esp_ble_dis_init()注册服务,随后esp_ble_conn_start()开始广播;启动失败则依次esp_ble_conn_stop()esp_ble_conn_deinit()并注销事件回调。

5.5 在运行期覆写设备信息

CONFIG_EXAMPLE_BLE_DIS_INFO开启时,app_main.c 演示了如何在运行期用代码覆盖 Kconfig 默认值:

static void app_ble_dis_init(void) { esp_ble_dis_init(); #ifdef CONFIG_EXAMPLE_BLE_DIS_INFO esp_ble_dis_pnp_t pnp_id = { .src = CONFIG_BLE_DIS_PNP_VID_SRC, .vid = CONFIG_BLE_DIS_PNP_VID, .pid = CONFIG_BLE_DIS_PNP_PID, .ver = CONFIG_BLE_DIS_PNP_VER }; static char mac_str[19] = {0}; uint8_t mac[6]; ESP_ERROR_CHECK(esp_read_mac((uint8_t *)mac, ESP_MAC_BT)); sprintf(mac_str, MACSTR, MAC2STR(mac)); esp_ble_dis_set_model_number(CONFIG_BLE_DIS_MODEL); esp_ble_dis_set_serial_number(mac_str); esp_ble_dis_set_firmware_revision(esp_get_idf_version()); esp_ble_dis_set_hardware_revision(esp_get_idf_version()); esp_ble_dis_set_software_revision(esp_get_idf_version()); esp_ble_dis_set_manufacturer_name(CONFIG_BLE_DIS_MANUF); esp_ble_dis_set_system_id(CONFIG_BLE_DIS_SYSTEM_ID); esp_ble_dis_set_pnp_id(&pnp_id); #endif }

这段代码展示了两个实用技巧:

  • 用蓝牙 MAC 地址作为序列号:通过esp_read_mac(mac, ESP_MAC_BT)读取蓝牙 MAC,格式化为xx:xx:xx:xx:xx:xx字符串后作为 Serial Number,实现每台设备唯一;
  • 版本号动态获取esp_get_idf_version()直接取当前 IDF 版本作为固件/硬件/软件版本,避免硬编码。

5.6 连接事件中读取设备信息

app_main.c 的事件回调在收到ESP_BLE_CONN_EVENT_CONNECTED时,通过 Get 系列 API 读取全部设备信息并打印:

case ESP_BLE_CONN_EVENT_CONNECTED: ESP_LOGI(TAG, "ESP_BLE_CONN_EVENT_CONNECTED"); esp_ble_dis_get_pnp_id(&pnp_id); esp_ble_dis_get_model_number(&model_number); esp_ble_dis_get_serial_number(&serial_number); esp_ble_dis_get_firmware_revision(&firmware_revision); esp_ble_dis_get_hardware_revision(&hardware_revision); esp_ble_dis_get_software_revision(&software_revision); esp_ble_dis_get_manufacturer_name(&manufacturer); esp_ble_dis_get_system_id(&system_id); /* ... ESP_LOGI 打印各字段,PnP ID 打印 src/vid/pid/ver */ break;

字符串类 Get 接口返回const char **指针(指向组件内部静态存储,勿释放),PnP ID 通过esp_ble_dis_get_pnp_id(&pnp_id)以结构体方式拷贝返回。

六、API 速查表

以下 API 全部声明于 esp_dis.h,返回esp_err_t(成功为ESP_OK,参数非法为ESP_ERR_INVALID_ARG):

API功能
esp_ble_dis_init()注册 DIS 服务到连接管理框架(失败返回ESP_FAIL
esp_ble_dis_deinit()从连接管理框架注销服务(失败返回esp_ble_conn_remove_svc()的错误码)
esp_ble_dis_get_model_number(const char **value)获取型号
esp_ble_dis_set_model_number(const char *value)设置型号
esp_ble_dis_get_serial_number(const char **value)获取序列号
esp_ble_dis_set_serial_number(const char *value)设置序列号
esp_ble_dis_get_firmware_revision(const char **value)获取固件版本
esp_ble_dis_set_firmware_revision(const char *value)设置固件版本
esp_ble_dis_get_hardware_revision(const char **value)获取硬件版本
esp_ble_dis_set_hardware_revision(const char *value)设置硬件版本
esp_ble_dis_get_software_revision(const char **value)获取软件版本
esp_ble_dis_set_software_revision(const char *value)设置软件版本
esp_ble_dis_get_manufacturer_name(const char **value)获取厂商名
esp_ble_dis_set_manufacturer_name(const char *value)设置厂商名
esp_ble_dis_get_system_id(const char **value)获取 System ID
esp_ble_dis_set_system_id(const char *value)设置 System ID
esp_ble_dis_get_pnp_id(esp_ble_dis_pnp_t *pnp_id)获取 PnP ID(结构体拷贝)
esp_ble_dis_set_pnp_id(esp_ble_dis_pnp_t *pnp_id)设置 PnP ID

Set 系列接口内部通过memset清零后按MIN(CONFIG_BLE_DIS_STR_MAX, strlen(value))截断拷贝,因此传入超长字符串会被安全截断。

七、运行效果与验证

示例 README(README.md)给出了连接并读取特征值后的典型串口输出。启动阶段可见 NimBLE 广播启动日志与连接管理日志:

I (200) blecm_nimble: BLE Host Task Started I (200) blecm_nimble: getting characteristic(0x2a00) I (200) blecm_nimble: getting characteristic(0x2a01) I (200) blecm_nimble: getting characteristic(0x2a05) I (220) NimBLE: GAP procedure initiated: advertise;

对端连接并读取特征值后,app_main 打印设备信息:

I (27280) app_main: ESP_BLE_CONN_EVENT_CONNECTED I (27280) app_main: Model name ESP I (27280) app_main: Serial Number 84:f7:03:08:21:1a I (27280) app_main: Firmware revision v4.3.5-dirty I (27280) app_main: Hardware revision v4.3.5-dirty I (27280) app_main: Software revision v4.3.5-dirty I (27280) app_main: Manufacturer name Manufacturer I (27280) app_main: System ID System I (27280) app_main: PnP ID Vendor ID Source 0x1, Vendor ID 0x00, Product ID 0x00, Product Version 0x01

同时 blecm_nimble 会打印客户端对各个特征值 UUID 的读请求记录(如0x2a23System ID、0x2a24Model Number、0x2a25Serial Number、0x2a26Firmware Revision、0x2a27Hardware Revision、0x2a28Software Revision、0x2a29Manufacturer Name、0x2a50PnP ID),可直接用任意 BLE 扫描/调试 App(如 nRF Connect)验证。

八、常见问题与注意事项

  • 服务未出现在对端设备中:检查CONFIG_BLE_DIS是否已使能(示例中通过 sdkconfig.defaults 显式开启),并确认esp_ble_dis_init()esp_ble_conn_start()之前被调用;
  • 特征值读取为空:确认对应特征值的 Kconfig bool 开关(如CONFIG_BLE_DIS_SERIAL_NUMBER)已打开,否则特征值不会被注册到nu_lookup_table
  • 字符串被截断:所有字符串特征值长度受CONFIG_BLE_DIS_STR_MAX限制(默认 32),如需更长内容请调整该配置(范围 2~64);
  • 版本号建议动态化:示例中通过esp_get_idf_version()填充版本特征值,避免固件升级后版本信息失实;
  • DIS 为只读服务:所有特征值均为BLE_CONN_GATT_CHR_READ权限,客户端无法写入修改。

九、进一步探索

  • 该组件属于 components/bluetooth/ble_services 标准服务家族,可参照其结构了解 ANS、BAS、CTS、HRS 等其他服务的实现方式;
  • 连接管理框架 components/bluetooth/ble_conn_mgr 是理解服务注册与事件分发机制的关键;
  • 更多 BLE 服务示例见 examples/bluetooth/ble_services,更多 BLE 主题文档见 docs/en/bluetooth。

【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询