You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32(ESP-IDF)设置128位广播UUID服务问题求助

在ESP32(ESP-IDF)中配置128位UUID广播服务的解决方案

问题背景

需要在ESP32上使用ESP-IDF配置128位自定义广播UUID服务,目标UUID为BB930001-3CE1-4720-A753-28C0159DC777。现有代码修改后,在NRF CONNECT中无法搜索到设备,仅能看到1800等默认服务。

核心问题分析

  1. UUID字节序错误:BLE规范要求128位UUID在初始化时使用小端字节序,原代码中的UUID字节排列不符合要求。
  2. 未将自定义UUID加入广播数据:BLE设备的发现依赖广播包,默认广播仅包含设备名称、连接参数等基础信息,不会自动添加自定义服务UUID,必须手动配置广播数据。

修正步骤

1. 正确初始化128位UUID

将目标UUID BB930001-3CE1-4720-A753-28C0159DC777 按小端字节序拆分:

  • 原UUID分段:BB930001 3CE1 4720 A753 28C0159DC777
  • 小端字节序转换:01 00 93 BB E1 3C 20 47 53 A7 77 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();
}

验证方法

  1. 编译烧录修正后的代码到ESP32。
  2. 打开NRF CONNECT应用,扫描BLE设备。
  3. 可看到名称为ESP32-Custom-Svc的设备,其广播数据中包含目标128位UUID。
  4. 连接设备后,可在GATT服务列表中找到自定义服务。

内容的提问来源于stack exchange,提问作者Batyr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 23:09:47