资讯动态

ESP-IDF BLE ANCS 配对失败:从广播到加密密钥,五步定位卡点

发布时间:2026/9/8 15:35:48 来源:尧图企业网站定制
ESP-IDF BLE ANCS 配对失败:从广播到加密密钥,五步定位卡点【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idfESP32 连 iPhone 读通知时,ESP-IDF BLE ANCS 配对失败是个高频问题:iOS 弹不出配对框、输入 passkey 超时、或者配对成功却找不到 ANCS 服务。这篇给一条递进式排查路线:先用症状自查失败发生在哪一段,再沿广播、配对参数、passkey、加密后服务发现四个检查点逐步走,每次只改一个变量,用串口日志确认是否通过。1. 自查症状:配对失败卡在哪一段 配对不是一步,它串了广播、连接、SMP 配对、加密、GATT 发现五段。先把现象对号入座,后面每一步只解决一个变量。你看到的现象大概率卡点先查什么iPhone 列表里没有设备广播/可发现广播是否真的启动,广播数据里有什么能连上,但 iOS 始终不弹配对框配对参数(SMP)安全等级、MITM、SM 特性开关弹了配对框,输完 passkey 却失败passkey 交换PASSKEY_ACTION是否被处理、是否超时配对成功,日志报ANCS service not found加密后的 GATT 发现发现时机是否在加密完成之后第一次正常,断电重连后失败绑定记录(NVS)密钥是否持久化、旧 bond 是否冲突对照 NimBLE ANCS 示例的 README,ANCS 服务 UUID 是7905F431-B5CE-4E99-A40F-4B1E122D00D0,三个特征(Notification Source / Control Point / Data Source)全部要求授权访问——所以加密没成功和配对失败在你看来很像,但排查路径完全不同。先分清是哪种。2. 机制速览:从连接成立到拿到密钥的四段把流程压缩成四段,后面每一步对应其中一段:广播/扫描:两端互相发现,建立连接。ESP32 侧的广播数据只承载 Flags、设备名、16-bit 服务 UUID 列表这类字段——注意,ANCS 服务 UUID 是 128-bit 的,本来就不会出现在 16-bit UUID 字段里,别指望在广播里看到ANCS。连接参数交换与 MTU:iOS 作为 Central,连接参数由它主导。SMP 配对与绑定:交换密钥(passkey 参与),bond 记录写入 NVS,随后启用链路加密。NimBLE 侧对应BLE_GAP_EVENT_ENC_CHANGE事件;Bluedroid 侧对应ESP_GAP_BLE_SEC_REQ_EVT→ESP_GAP_BLE_AUTH_CMPL_EVT。加密后的 GATT 操作:加密确认后才能发现 ANCS 服务、订阅 Notification Source、写 Control Point。下面按广播 → 配对参数 → passkey → 加密后服务发现的顺序排查。3. 第一步:核对广播与可发现性是否工作 检查点:设备到底在不在广播,广播数据里是什么。启动 NimBLE 示例后,串口里应出现这几行(来自示例 README 的 Example Output):I (476) NimBLE: GAP procedure initiated: stop advertising. I (476) NimBLE: Device Address: I (476) NimBLE: GAP procedure initiated: advertise;来源:examples/bluetooth/nimble/ble_ancs/README.md如果连GAP procedure initiated: advertise;都没有,问题在启动流程(地址初始化、nimble_port_init返回值),先别往安全方向查。再看广播内容。NimBLE 示例里广播声明的是一个 16-bit UUID:static const ble_uuid16_t adv_uuids16[] { BLE_UUID16_INIT(0x1811) }; fields.uuids16 adv_uuids16; fields.num_uuids16 1;来源:examples/bluetooth/nimble/ble_ancs/main/main.cBluedroid 示例同理,广播里放的是 HID 服务 UUID(0x1812)加ESP_BLE_APPEARANCE_GENERIC_HID:uint8_t hidd_service_uuid128[] { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x12, 0x18, 0x00, 0x00, }; static esp_ble_adv_data_t adv_config { .appearance ESP_BLE_APPEARANCE_GENERIC_HID, .p_service_uuid hidd_service_uuid128, ... };来源:examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs_demo.c这就是第一个常见认知差:官方示例的广播数据里都没有 ANCS 服务 UUID,这是协议限制,不是你配错。抓包(手机 sniffer 应用)核对你项目广播数据与示例一致即可。通过标准:日志有GAP procedure initiated: advertise;,且广播间隔、名称、UUID 列表与你的预期一致。4. 第二步:核对配对参数与 sdkconfig 安全配置 检查点:SM 特性是否启用、配对方式(SC 还是 legacy)、MITM 是否开启、bond 是否打开。先看示例工程sdkconfig.defaults的最小配置基线:CONFIG_BT_ENABLEDy CONFIG_BTDM_CTRL_MODE_BLE_ONLYy CONFIG_BT_BLUEDROID_ENABLEDn CONFIG_BT_NIMBLE_ENABLEDy CONFIG_BT_NIMBLE_NVS_PERSISTy来源:examples/bluetooth/nimble/ble_ancs/sdkconfig.defaultsCONFIG_BT_NIMBLE_NVS_PERSISTy这一行别忽略:它决定 bond 密钥是否落 NVS,直接影响重连是否还要重新配对,第 7 节回归会用到。NimBLE 的安全相关 Kconfig 在 NimBLE 安全 Kconfig(Component config → Bluetooth下),核对这几项:选项默认作用CONFIG_BT_NIMBLE_SECURITY_ENABLEy打开 SM(SMP)特性CONFIG_BT_NIMBLE_SM_LEGACYylegacy 配对CONFIG_BT_NIMBLE_SM_SCySecure Connections(4.2)CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_ENCRYPTIONy链路加密能力CONFIG_BT_NIMBLE_SM_LVL00 无安全 / 1 未认证加密 / 2 认证加密 / 3 LE SC 128-bit 密钥但注意,编译期开关之外,运行时还有一处:ble_hs_cfg.sm_*。示例里是这么写的:ble_hs_cfg.store_status_cb ble_store_util_status_rr; ble_hs_cfg.sm_bonding 1; ble_hs_cfg.sm_our_key_dist | BLE_SM_PAIR_KEY_DIST_ENC; ble_hs_cfg.sm_their_key_dist | BLE_SM_PAIR_KEY_DIST_ENC; ble_hs_cfg.sm_sc 0;来源:examples/bluetooth/nimble/ble_ancs/main/main.csm_sc 0表示运行时关掉 Secure Connections、走 legacy 配对——即使 Kconfig 里BT_NIMBLE_SM_SC是 y。iOS 两种都支持,但你要清楚自己项目实际走哪种。排查时两个地方都要看,只看 sdkconfig 会漏掉运行时配置。Bluedroid 侧对应的是认证请求模式与加密切换:esp_bt_auth_req_t auth_req ESP_LE_AUTH_REQ_SC_MITM_BOND; esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, auth_req, sizeof(uint8_t)); ... esp_ble_set_encryption(param-open.remote_bda, ESP_BLE_SEC_ENCRYPT_MITM);来源:examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs_demo.c(后者在ESP_GAP_BLE_SEC_REQ_EVT分支内)ESP_LE_AUTH_REQ_SC_MITM_BOND已把MITM 保护 绑定一起带上。如果你的代码用了更弱的 auth_req,或ESP_GAP_BLE_SEC_REQ_EVT里没有调esp_ble_set_encryption,iOS 端的配对框就会出现异常行为。通过标准:Kconfig 的 SM 特性、加密能力为 y;sm_bonding/ auth_req 带 bond 与 MITM;两处配置的配对方式一致且符合你的预期。5. 第三步:核对 passkey 响应与 iOS 端弹窗检查点:passkey 交换是否有响应、是否超时。NimBLE 侧,配对走到 passkey 阶段会收到BLE_GAP_EVENT_PASSKEY_ACTION,示例的处理方式:if (event-passkey.params.action BLE_SM_IOACT_DISP) { pkey.passkey 123456; /* demo 固定值 */ rc ble_sm_inject_io(event-passkey.conn_handle, pkey); }来源:examples/bluetooth/nimble/ble_ancs/main/main.c(节选)串口日志会打出PASSKEY_ACTION_EVENT started和Enter passkey 123456 on the peer side——这时 iOS 端弹出的是输入 6 位配对码的框,码值显示在设备侧(示例直接写死 123456,注释里明确标注仅演示用)。如果你没看到PASSKEY_ACTION_EVENT,说明 SMP 流程根本没走到这一步,回到第 4 节查参数;如果看到Timeout! Rejecting the key,是你没有在超时窗口内响应。对照关系记一条:iOS 弹什么框,取决于双方 IO 能力协商的结果。设备声明显示型时,iOS 弹输入框;声明比较数字(comparison)时,弹Y/N确认框;两边能力声明不一致,就会看到弹框了但怎么输都不对。通过标准:日志出现 passkey 事件、你完成了注入/确认,iOS 端配对框成功关闭并提示已配对。6. 第四步:核对加密完成后的 ANCS 服务发现检查点:加密是否真的完成,发现是否发生在加密之后,服务 UUID 对不对。NimBLE 示例把服务发现挂在BLE_GAP_EVENT_ENC_CHANGE之后,失败路径也有明确日志:case BLE_GAP_EVENT_ENC_CHANGE: if (event-enc_change.status ! 0) { MODLOG_DFLT(ERROR, encryption failed; status%d\n, event-enc_change.status); return 0; } rc ble_gattc_disc_svc_by_uuid(event-enc_change.conn_handle, APPLE_NC_UUID.u, ancs_service_discovered_cb, NULL);来源:examples/bluetooth/nimble/ble_ancs/main/main.c对照三条日志锚点:encryption failed; status...→ 加密协商本身失败,回第 4、5 节;ANCS service not found→ 加密通了,但对端没暴露 ANCS(见第 8 节iOS 不保证 ANCS 一直存在);Found Notification Source characteristic→ 发现成功,可以进入订阅。Bluedroid 侧对应检查ESP_GAP_BLE_AUTH_CMPL_EVT里的param-auth_cmpl.status,非 0 即认证失败,原因码能区分是用户拒绝还是协议层错误。通过标准:ENC_CHANGE(或 AUTH_CMPL)状态为 0,随后日志出现 Notification Source / Data Source / Control Point 三个特征被发现。7. 验证与回归:确认问题解决了修完之后,按这三条回归,防止只修了单条路径:基线先行:把官方 NimBLE ANCS 示例原样烧录,走一遍 iPhone 配对。示例能走通,说明你的硬件、射频、iOS 版本没问题,差异一定在你项目的配置里——这是最快的二分法。完整配对路径:全新设备(或清过 NVS)从零配对一次,确认 passkey 弹窗、配对成功、三特征发现齐全;核对配对失败日志中各锚点(advertise → passkey → ENC_CHANGE → characteristic discovered)依次出现。重连路径:断电重启两端再连。bond 在 NVS 里(示例靠CONFIG_BT_NIMBLE_NVS_PERSISTy),第二次连接不应再走 passkey,直接加密成功。如果重连失败,通常是旧 bond 记录与新身份不匹配,清 NVS 重新配对。8. 避坑与后续三个容易踩的坑:passkey 别拿示例值上生产。示例写死 123456,代码注释明说仅供演示。真机要么每次生成随机值,要么按产品形态调整 IO 能力组合(无屏设备常用 No Input No Output),并同步验证 iOS 端弹窗行为。iOS 不保证 ANCS 一直存在。README 原文:Due to the nature of iOS, the ANCS is not guaranteed to always be present.正确做法是订阅 GATT 的 Service Changed 特征,监控 ANCS 的发布与撤除,而不是假设连上就有。换过身份/密钥后清 NVS。示例对BLE_GAP_EVENT_REPEAT_PAIRING的处理是ble_store_util_delete_peer删掉旧 peer 再重试。你调试时改过设备身份或开过调试密钥,又不清 NVS,就会撞上旧记录在、新配对乱的状态。后续优化两个方向:一是把 bond 持久化纳入正常流程验证(第二次免 passkey 加密应成为测试项,而不只是能连上);二是围绕连接参数与广播间隔做功耗调优——配对链路稳定之后,这部分才是通知类配件的日常大头。【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价