ESP32(ESP-IDF)设置128位广播UUID服务问题求助
在ESP32(ESP-IDF)中配置128位UUID广播服务的解决方案
问题背景
需要在ESP32上使用ESP-IDF配置128位自定义广播UUID服务,目标UUID为BB930001-3CE1-4720-A753-28C0159DC777。现有代码修改后,在NRF CONNECT中无法搜索到设备,仅能看到1800等默认服务。
核心问题分析
- UUID字节序错误:BLE规范要求128位UUID在初始化时使用小端字节序,原代码中的UUID字节排列不符合要求。
- 未将自定义UUID加入广播数据:BLE设备的发现依赖广播包,默认广播仅包含设备名称、连接参数等基础信息,不会自动添加自定义服务UUID,必须手动配置广播数据。
修正步骤
1. 正确初始化128位UUID
将目标UUID BB930001-3CE1-4720-A753-28C0159DC777 按小端字节序拆分:
- 原UUID分段:
BB9300013CE14720A75328C0159DC777 - 小端字节序转换:
01 00 93 BBE1 3C20 4753 A777 C7 9D 15 C0 28
替换原代码中的UUID初始化:
// 正确的128位UUID初始化(小端序) static const ble_uuid128_t gatt_svr_svc_uuid = BLE_UUID128_INIT(0x01, 0x00, 0x93, 0xBB, 0xE1, 0x3C, 0x20, 0x47, 0x53, 0xA7, 0x77, 0xC7, 0x9D, 0x15, 0xC0, 0x28);
2. 配置广播数据,添加自定义UUID
在启动广播的代码中,添加自定义服务UUID到广播数据或扫描响应数据中。示例广播配置代码如下:
static void ble_adv_start(void) { struct ble_gap_adv_params adv_params; struct ble_hs_adv_fields fields; int rc; // 初始化广播字段 memset(&fields, 0, sizeof(fields)); // 设置设备名称(可选) fields.name = (uint8_t *)"ESP32-Custom-Service"; fields.name_len = strlen((char *)fields.name); fields.name_is_complete = 1; // 添加完整的128位服务UUID到广播字段 fields.uuids128 = &gatt_svr_svc_uuid.u; fields.num_uuids128 = 1; fields.uuids128_is_complete = 1; // 设置广播参数:非定向广播,可连接 memset(&adv_params, 0, sizeof(adv_params)); adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; // 应用广播字段配置 rc = ble_gap_adv_set_fields(&fields); if (rc != 0) { MODLOG_DFLT(ERROR, "ble_gap_adv_set_fields failed; rc=%d\n", rc); return; } // 启动广播 rc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, &adv_params, NULL, NULL); if (rc != 0) { MODLOG_DFLT(ERROR, "ble_gap_adv_start failed; rc=%d\n", rc); return; } }
3. 确保广播启动时机
在BLE初始化完成后(如ble_hs_sync_cb回调中)调用广播启动函数:
static void ble_on_sync(void) { int rc; // 设置设备地址(可选,使用公共地址) rc = ble_hs_id_set_rnd(0); if (rc != 0) { MODLOG_DFLT(ERROR, "ble_hs_id_set_rnd failed; rc=%d\n", rc); return; } // 启动广播 ble_adv_start(); } // 注册BLE同步回调 void app_main(void) { // 其他初始化代码... ble_hs_cfg.sync_cb = ble_on_sync; // ... }
完整修正后的核心代码片段
#include "ble_hs.h" #include "ble_uuid.h" #include "ble_gatt.h" #include "ble_svc_gap.h" #include "ble_svc_gatt.h" #include "modlog/modlog.h" // 目标UUID BB930001-3CE1-4720-A753-28C0159DC777 的小端序初始化 static const ble_uuid128_t gatt_svr_svc_uuid = BLE_UUID128_INIT(0x01, 0x00, 0x93, 0xBB, 0xE1, 0x3C, 0x20, 0x47, 0x53, 0xA7, 0x77, 0xC7, 0x9D, 0x15, 0xC0, 0x28); static uint8_t gatt_svr_chr_val; static uint16_t gatt_svr_chr_val_handle; static const ble_uuid128_t gatt_svr_chr_uuid = BLE_UUID128_INIT(0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33); static uint8_t gatt_svr_dsc_val; static const ble_uuid128_t gatt_svr_dsc_uuid = BLE_UUID128_INIT(0x01, 0x01, 0x01, 0x01, 0x12, 0x12, 0x12, 0x12, 0x23, 0x23, 0x23, 0x23, 0x34, 0x34, 0x34, 0x34); static int gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); static const struct ble_gatt_svc_def gatt_svr_svcs[] = { { .type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &gatt_svr_svc_uuid.u, .characteristics = (struct ble_gatt_chr_def[]) { { .uuid = &gatt_svr_chr_uuid.u, .access_cb = gatt_svc_access, #if CONFIG_EXAMPLE_ENCRYPTION .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE, #else .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE, #endif .val_handle = &gatt_svr_chr_val_handle, .descriptors = (struct ble_gatt_dsc_def[]) { { .uuid = &gatt_svr_dsc_uuid.u, #if CONFIG_EXAMPLE_ENCRYPTION .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC, #else .att_flags = BLE_ATT_F_READ, #endif .access_cb = gatt_svc_access, }, { 0, } }, }, { 0, } }, }, { 0, }, }; static int gatt_svr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len, void *dst, uint16_t *len) { uint16_t om_len; int rc; om_len = OS_MBUF_PKTLEN(om); if (om_len < min_len || om_len > max_len) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } rc = ble_hs_mbuf_to_flat(om, dst, max_len, len); if (rc != 0) { return BLE_ATT_ERR_UNLIKELY; } return 0; } static int gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { const ble_uuid_t *uuid; int rc; switch (ctxt->op) { case BLE_GATT_ACCESS_OP_READ_CHR: if (conn_handle != BLE_HS_CONN_HANDLE_NONE) { MODLOG_DFLT(INFO, "Characteristic read; conn_handle=%d attr_handle=%d\n", conn_handle, attr_handle); } else { MODLOG_DFLT(INFO, "Characteristic read by NimBLE stack; attr_handle=%d\n", attr_handle); } uuid = ctxt->chr->uuid; if (attr_handle == gatt_svr_chr_val_handle) { rc = os_mbuf_append(ctxt->om, &gatt_svr_chr_val, sizeof(gatt_svr_chr_val)); return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; } goto unknown; case BLE_GATT_ACCESS_OP_WRITE_CHR: if (conn_handle != BLE_HS_CONN_HANDLE_NONE) { MODLOG_DFLT(INFO, "Characteristic write; conn_handle=%d attr_handle=%d\n", conn_handle, attr_handle); } else { MODLOG_DFLT(INFO, "Characteristic write by NimBLE stack; attr_handle=%d\n", attr_handle); } uuid = ctxt->chr->uuid; if (attr_handle == gatt_svr_chr_val_handle) { rc = gatt_svr_write(ctxt->om, sizeof(gatt_svr_chr_val), sizeof(gatt_svr_chr_val), &gatt_svr_chr_val, NULL); ble_gatts_chr_updated(attr_handle); MODLOG_DFLT(INFO, "Notification/Indication scheduled for all subscribed peers.\n"); return rc; } goto unknown; case BLE_GATT_ACCESS_OP_READ_DSC: if (conn_handle != BLE_HS_CONN_HANDLE_NONE) { MODLOG_DFLT(INFO, "Descriptor read; conn_handle=%d attr_handle=%d\n", conn_handle, attr_handle); } else { MODLOG_DFLT(INFO, "Descriptor read by NimBLE stack; attr_handle=%d\n", attr_handle); } uuid = ctxt->dsc->uuid; if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) { rc = os_mbuf_append(ctxt->om, &gatt_svr_dsc_val, sizeof(gatt_svr_chr_val)); return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; } goto unknown; case BLE_GATT_ACCESS_OP_WRITE_DSC: goto unknown; default: goto unknown; } unknown: assert(0); return BLE_ATT_ERR_UNLIKELY; } void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) { char buf[BLE_UUID_STR_LEN]; switch (ctxt->op) { case BLE_GATT_REGISTER_OP_SVC: MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), ctxt->svc.handle); break; case BLE_GATT_REGISTER_OP_CHR: MODLOG_DFLT(DEBUG, "registering characteristic %s with def_handle=%d val_handle=%d\n", ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), ctxt->chr.def_handle, ctxt->chr.val_handle); break; case BLE_GATT_REGISTER_OP_DSC: MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), ctxt->dsc.handle); break; default: assert(0); break; } } int gatt_svr_init(void) { int rc; ble_svc_gap_init(); ble_svc_gatt_init(); ble_svc_ans_init(); rc = ble_gatts_count_cfg(gatt_svr_svcs); if (rc != 0) { return rc; } rc = ble_gatts_add_svcs(gatt_svr_svcs); if (rc != 0) { return rc; } gatt_svr_dsc_val = 0x99; return 0; } // 广播启动函数 static void ble_adv_start(void) { struct ble_gap_adv_params adv_params; struct ble_hs_adv_fields fields; int rc; memset(&fields, 0, sizeof(fields)); // 设置设备名称 fields.name = (uint8_t *)"ESP32-Custom-Svc"; fields.name_len = strlen((char *)fields.name); fields.name_is_complete = 1; // 添加自定义128位服务UUID到广播 fields.uuids128 = &gatt_svr_svc_uuid.u; fields.num_uuids128 = 1; fields.uuids128_is_complete = 1; memset(&adv_params, 0, sizeof(adv_params)); adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; rc = ble_gap_adv_set_fields(&fields); if (rc != 0) { MODLOG_DFLT(ERROR, "ble_gap_adv_set_fields failed; rc=%d\n", rc); return; } rc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, &adv_params, NULL, NULL); if (rc != 0) { MODLOG_DFLT(ERROR, "ble_gap_adv_start failed; rc=%d\n", rc); return; } } // BLE同步完成回调 static void ble_on_sync(void) { int rc; rc = ble_hs_id_set_rnd(0); if (rc != 0) { MODLOG_DFLT(ERROR, "ble_hs_id_set_rnd failed; rc=%d\n", rc); return; } ble_adv_start(); } // 主函数示例 void app_main(void) { // 初始化NimBLE主机/控制器 int rc = ble_hs_init(); if (rc != 0) { MODLOG_DFLT(ERROR, "ble_hs_init failed; rc=%d\n", rc); return; } // 注册GATT服务 rc = gatt_svr_init(); if (rc != 0) { MODLOG_DFLT(ERROR, "gatt_svr_init failed; rc=%d\n", rc); return; } // 设置同步回调 ble_hs_cfg.sync_cb = ble_on_sync; // 启动BLE任务(根据ESP-IDF NimBLE示例的任务启动方式) nimble_port_run(); }
验证方法
- 编译烧录修正后的代码到ESP32。
- 打开NRF CONNECT应用,扫描BLE设备。
- 可看到名称为
ESP32-Custom-Svc的设备,其广播数据中包含目标128位UUID。 - 连接设备后,可在GATT服务列表中找到自定义服务。
内容的提问来源于stack exchange,提问作者Batyr
相关产品推荐
相关产品推荐

