资讯动态

Pico W BLE安全配网库:Wi-Fi凭据无线写入方案

发布时间:2026/8/4 21:06:57 来源:尧图企业网站定制
1. 项目概述pico-wifi-provisioning是一个专为 Raspberry Pi Pico W 设计的 PlatformIO 库用于通过蓝牙低功耗BLE安全地向设备写入 Wi-Fi 凭据。该库并非通用 BLE 通信中间件而是面向嵌入式物联网终端设备“首次联网”这一关键场景的垂直解决方案——在设备无预置网络配置、无物理串口调试接口、无以太网连接能力的前提下利用 Pico W 自带的双模无线能力2.4GHz Wi-Fi BLE实现免拆机、免烧录、免串口的远程配网。其核心设计哲学是安全优先、资源可控、状态可溯、体验闭环。所有 Wi-Fi 凭据SSID 和密码均不以明文形式暴露于空中接口BLE 链路强制启用配对与加密本地存储采用 LittleFS 文件系统并支持多网络配置整个配网流程具备完整的状态回调机制便于开发者集成 LED 指示、按钮复位、UI 反馈等工程化功能。该库严格依赖arduino-pico核心由 Earle Philhower 维护且仅适用于搭载 RP2040 芯片并集成 CYW43439 Wi-Fi/BT 二合一芯片的 Pico W 硬件平台。它不是独立 BLE 协议栈而是构建在pico-ble-secure提供安全配对与加密通道和pico-ble-notify提供通知/指示特性支持之上的应用层服务封装同时深度耦合 ESP-IDF 风格的WiFi.hAPI 与 Arduino JSON 解析能力。2. 系统架构与工作流程2.1 整体分层结构----------------------------------- | Application Layer | ← 用户业务逻辑如传感器采集、MQTT 上报 ----------------------------------- | PicoWiFiProvisioning Library | ← 本库状态管理、BLE 服务注册、Wi-Fi 连接调度、回调分发 ----------------------------------- | pico-ble-notify pico-ble-secure | ← 安全 BLE 基础设施GATT 服务注册、配对协商、AES-CCM 加密 ----------------------------------- | arduino-pico Core (WiFi/BLE) | ← 底层驱动CYW43439 HAL、BTstackLib 封装、WiFi STA 模式控制 ----------------------------------- | RP2040 CYW43439 | ← 硬件平台双核 ARM Cortex-M0内置 Wi-Fi/BT 射频与基带 -----------------------------------2.2 典型配网时序含状态跃迁上电初始化阶段调用PicoWiFiProvisioning::begin()注册 BLE GATT 服务、加载/wifi_config.json、启动 Wi-Fi STA 模式若loadNetworksFromFlash()成功读取至少一条有效网络则立即调用WiFi.begin(ssid, password)尝试连接否则进入“等待配网”状态BLE 开始广播设备名PicoW等待中心设备手机 App连接BLE 配对与加密建立阶段中心设备扫描到PicoW广播包 → 发起连接 → 触发setBLEConnectionStateCallback(true)根据begin()中指定的securityLevel与ioCapability执行对应配对流程SECURITY_MEDIUM IO_CAPABILITY_NO_INPUT_NO_OUTPUT→ “Just Works” 模式无用户交互但启用链路加密SECURITY_HIGH IO_CAPABILITY_DISPLAY_YES_NO→ 显示 6 位数字 确认按钮执行 Numeric Comparison防 MITM配对成功后BLESecure自动完成长期密钥LTK交换与会话密钥派生后续所有特征值读写均经 AES-CCM 加密凭据写入与持久化阶段中心设备向SSID特征UUID5a67d678-6361-4f32-8396-54c6926c8fa2写入 SSID 字符串UTF-8≤32 字节向Password特征UUID5a67d678-6361-4f32-8396-54c6926c8fa3写入密码UTF-8≤64 字节向Command特征UUID5a67d678-6361-4f32-8396-54c6926c8fa4写入CMD_SAVE_NETWORK (0x01)库内部将 SSID/Password 组装为 JSON 对象调用LittleFS.writeFile(/wifi_config.json, json_str)持久化Wi-Fi 连接与状态同步阶段CMD_CONNECT (0x02)命令触发WiFi.begin()或在loop()中自动尝试已存网络WiFi.event()回调捕获SYSTEM_EVENT_STA_CONNECTED/SYSTEM_EVENT_STA_DISCONNECTED等事件通过setWiFiStatusCallback()向上层透传WL_CONNECTED、WL_CONNECT_FAILED等状态码连接成功后WiFi.localIP()可获取分配的 IPv4 地址配网完成与链路释放阶段一旦 Wi-Fi 连接成功库默认调用BLEDevice::disconnect()主动断开 BLE 连接避免空闲占用此时setBLEConnectionStateCallback(false)被触发LED 可切换为常亮状态设备进入正常业务运行模式BLE 广播可选择性关闭需手动调用BLEDevice::stopAdvertising()3. BLE 服务与特性详解3.1 GATT 服务定义库注册一个自定义 BLE 服务主服务 UUID 为5a67d678-6361-4f32-8396-54c6926c8fa0未在 README 显式声明但由各特性 UUID 推导得出。该服务包含 5 个标准 BLE 特性Characteristic全部基于BLECharacteristic封装支持属性权限与描述符特性名称UUID 后缀末4字节属性Properties描述SSID8fa2Read, Write可读取当前存储的 SSID调试用可写入新 SSID配网主入口Password8fa3Write仅可写入密码禁止明文读取增强安全性Command8fa4Write写入 1 字节命令码触发配网动作保存、连接、清空等Pairing Status8fa5Read, Notify只读状态值0未配对1已配对2配对中支持 Notify 实时推送变化注所有特性均启用BLECharacteristic::PROPERTY_READ_ENCRYPTION_REQUIRED或PROPERTY_WRITE_ENCRYPTION_REQUIRED确保非加密连接无法访问。3.2 特性数据格式规范SSID 特性UTF-8 编码字符串以\0结尾长度 ≤MAX_SSID_LENGTH默认 32。写入时自动截断超长部分。Password 特性UTF-8 编码字符串以\0结尾长度 ≤MAX_PASSWORD_LENGTH默认 64。写入时自动截断。Command 特性单字节十六进制值定义如下命令宏定义值Hex功能说明CMD_SAVE_NETWORK0x01将当前 SSID/Password 组合成新网络条目存入/wifi_config.jsonCMD_CONNECT0x02尝试连接wifi_config.json中第一个enabled:true的网络CMD_CLEAR_NETWORKS0x03删除/wifi_config.json文件清空所有网络配置CMD_GET_STATUS0x04部分实现返回 JSON 格式状态Wi-Fi 状态、BLE 连接状态、网络数量CMD_DISCONNECT0x05调用WiFi.disconnect()断开当前 Wi-Fi 连接CMD_START_SCAN0x06未完全实现触发WiFi.scanNetworks()CMD_GET_SCAN_RESULTS0x07未完全实现返回最近一次扫描结果需配合CMD_START_SCAN使用3.3 安全机制实现细节配对模式选择由begin(deviceName, securityLevel, ioCapability)参数决定SECURITY_MEDIUM要求配对Bonding启用链路层加密AES-CCM但不校验 MITMMan-in-the-Middle。SECURITY_HIGH在MEDIUM基础上增加 MITM 保护强制使用IO_CAPABILITY_DISPLAY_YES_NO或KEYBOARD_ONLY。SECURITY_HIGH_SC最高安全等级启用 Secure Connections基于 ECDH 密钥协商需硬件支持。IO Capability 映射枚举值对应BLESecurity::IoCapabilities工程适用场景IO_CAPABILITY_DISPLAY_ONLYBLESecurity::IO_CAPABILITY_DISPLAY_ONLYOLED 屏显 6 位码无确认键安全性较低IO_CAPABILITY_DISPLAY_YES_NOBLESecurity::IO_CAPABILITY_DISPLAY_YES_NOOLED 按钮显示码并要求用户按“是”确认推荐IO_CAPABILITY_KEYBOARD_ONLYBLESecurity::IO_CAPABILITY_KEYBOARD_ONLY外接键盘输入Pico W 本身不支持IO_CAPABILITY_NO_INPUT_NO_OUTPUTBLESecurity::IO_CAPABILITY_NO_INPUT_NO_OUTPUT无屏无键纯“Just Works”依赖加密而非用户验证密钥生命周期管理pico-ble-secure在配对成功后将 LTK、EDIV、RAND 等密钥材料持久化至 Pico W 的 Flash地址0x10000000附近即使设备重启仍可复用。若 Flash 被擦除如pio run -t upload重刷固件则需在手机端手动“取消配对”后重新配对。4. Wi-Fi 配置存储与管理4.1 LittleFS 存储结构库使用LittleFS文件系统LFS作为持久化后端文件路径固定为/wifi_config.json。该 JSON 文件采用数组格式每个元素代表一个 Wi-Fi 网络配置项[ { ssid: MyHomeWiFi, password: superSecret123, enabled: true, index: 0 }, { ssid: OfficeGuest, password: , enabled: false, index: 1 } ]enabled布尔值标识该网络是否启用true时connectToStoredNetworks()会尝试连接。index数组下标用于saveNetwork()更新时定位。最大容量由MAX_WIFI_NETWORKS默认 5限制超出时saveNetwork()返回false。4.2 关键存储 API函数签名功能说明调用时机示例bool saveNetwork(const char* ssid, const char* password)将 SSID/Password 保存为新网络或更新已有网络按 SSID 匹配CMD_SAVE_NETWORK命令处理函数内bool loadNetworksFromFlash()启动时从/wifi_config.json加载所有网络到内存缓存std::vectorWiFiNetworkbegin()初始化阶段void clearNetworks()删除/wifi_config.json文件清空内存缓存CMD_CLEAR_NETWORKS命令处理函数内bool connectToStoredNetworks()遍历内存中enabledtrue的网络按顺序调用WiFi.begin(ssid, password)setup()中自动连接逻辑注意saveNetwork()不会立即写入 Flash而是先更新内存缓存实际落盘发生在CMD_SAVE_NETWORK命令执行时或显式调用writeConfigToFile()内部函数。5. 回调机制与状态监控库提供 5 类回调函数指针全部通过setXXXCallback()设置允许开发者在关键事件发生时插入自定义逻辑如 LED 控制、日志输出、UI 刷新5.1 回调函数原型与触发条件回调设置函数参数类型触发条件setStatusCallback(void (*cb)(PicoWiFiProvisioningStatus))enum PicoWiFiProvisioningStatus内部状态变更IDLE,WAITING_FOR_BLE,RECEIVING_CREDENTIALS,CONNECTING_TO_WIFI,CONNECTEDsetWiFiStatusCallback(void (*cb)(wl_status_t))wl_status_tESP-IDF 定义Wi-Fi 连接状态变化WL_CONNECTED,WL_CONNECT_FAILED,WL_DISCONNECTED等setBLEConnectionStateCallback(void (*cb)(bool))booltrue已连接BLE 链路建立/断开瞬间setPasskeyDisplayCallback(void (*cb)(uint32_t))uint32_t6 位数字IO_CAPABILITY_DISPLAY_ONLY/YES_NO模式下配对时需显示 passkeysetNumericComparisonCallback(void (*cb)(uint32_t, BLEDevice*))uint32_tBLEDevice*IO_CAPABILITY_DISPLAY_YES_NO模式下需显示 passkey 并等待用户确认调用acceptNumericComparison(true/false)5.2 典型回调实现示例含 LED 状态机// LED 引脚定义 const int WIFI_LED_PIN LED_BUILTIN; // 板载 LED const int BLE_LED_PIN 15; // GPIO15 外接 LED用于 BLE 状态 void onWiFiStatus(wl_status_t status) { switch (status) { case WL_CONNECTED: Serial.println(✅ WiFi connected!); Serial.print(IP: ); Serial.println(WiFi.localIP()); digitalWrite(WIFI_LED_PIN, HIGH); // 常亮表示已联网 break; case WL_CONNECT_FAILED: case WL_CONNECTION_LOST: case WL_DISCONNECTED: Serial.println(❌ WiFi disconnected/fail); digitalWrite(WIFI_LED_PIN, LOW); // 熄灭或慢闪需加定时器 break; default: break; } } void onBLEConnectionChange(bool isConnected) { if (isConnected) { Serial.println( BLE connected); digitalWrite(BLE_LED_PIN, HIGH); // BLE 连接时 LED 常亮 } else { Serial.println( BLE disconnected); digitalWrite(BLE_LED_PIN, LOW); // 断开时熄灭 } } void onProvisionStatus(PicoWiFiProvisioningStatus status) { static uint32_t lastBlink 0; const uint32_t BLINK_INTERVAL 200; // ms switch (status) { case PICO_WIFI_PROVISIONING_IDLE: digitalWrite(WIFI_LED_PIN, LOW); break; case PICO_WIFI_PROVISIONING_WAITING_FOR_BLE: // 快速闪烁等待 BLE 连接 if (millis() - lastBlink BLINK_INTERVAL) { digitalWrite(WIFI_LED_PIN, !digitalRead(WIFI_LED_PIN)); lastBlink millis(); } break; case PICO_WIFI_PROVISIONING_RECEIVING_CREDENTIALS: // 中速闪烁正在接收凭据 if (millis() - lastBlink BLINK_INTERVAL * 2) { digitalWrite(WIFI_LED_PIN, !digitalRead(WIFI_LED_PIN)); lastBlink millis(); } break; case PICO_WIFI_PROVISIONING_CONNECTING_TO_WIFI: // 慢速闪烁正在连 Wi-Fi if (millis() - lastBlink BLINK_INTERVAL * 4) { digitalWrite(WIFI_LED_PIN, !digitalRead(WIFI_LED_PIN)); lastBlink millis(); } break; } } void setup() { Serial.begin(115200); pinMode(WIFI_LED_PIN, OUTPUT); pinMode(BLE_LED_PIN, OUTPUT); digitalWrite(WIFI_LED_PIN, LOW); digitalWrite(BLE_LED_PIN, LOW); PicoWiFiProvisioning.setWiFiStatusCallback(onWiFiStatus); PicoWiFiProvisioning.setBLEConnectionStateCallback(onBLEConnectionChange); PicoWiFiProvisioning.setStatusCallback(onProvisionStatus); // 启动配网服务中等安全无 IO 能力 if (!PicoWiFiProvisioning.begin(MyPicoW, SECURITY_MEDIUM, IO_CAPABILITY_NO_INPUT_NO_OUTPUT)) { Serial.println(❌ Provisioning init failed!); } }6. 集成与调试实践指南6.1 PlatformIO 项目配置platformio.ini[env:pico_w] platform https://github.com/maxgerhardt/platform-raspberrypi.git board pico_w framework arduino lib_deps earlephilhower/arduino-pico^3.4.0 pico-wifi-provisioning pico-ble-secure pico-ble-notify bblanchon/ArduinoJson^6.21.0 ; 必须启用 BLE 和 WiFi 支持 build_flags -D PIO_FRAMEWORK_ARDUINO_ENABLE_WIFI -D PIO_FRAMEWORK_ARDUINO_ENABLE_BT -D ARDUINO_ARCH_RP2040 ; 启用 LittleFSPico W 默认已启用此为显式声明 board_build.filesystem littlefs6.2 常见故障排查表现象可能原因解决方案BLE 不广播手机搜不到设备arduino-pico未启用 BTplatformio.ini缺少PIO_FRAMEWORK_ARDUINO_ENABLE_BT检查build_flags确认BTstackLib.h可被包含重启 PlatformIO 编译配对失败报错Re-encryption failed, status: 61手机端 Bond 信息残留Pico W Flash 被擦除导致 LTK 丢失手机 Bluetooth 设置中“忘记此设备”Pico W 重刷固件后需重新配对Wi-Fi 连接失败但凭据正确MAX_SSID_LENGTH或MAX_PASSWORD_LENGTH过小导致凭据被截断修改PicoWiFiProvisioning.h中对应宏定义重新编译CMD_CONNECT后无反应wifi_config.json文件损坏LittleFS未正确挂载添加Serial.println(LittleFS.begin());调试手动LittleFS.format()清空文件系统Android 配对弹窗重复出现Android 系统 Bug对同一设备多次发起配对请求同时点击两个弹窗或重启手机 Bluetooth 服务CMD_GET_STATUS返回空 JSON该命令未完全实现README 注明“Partially implemented”当前版本忽略此命令无需依赖如需状态直接读取WiFi.status()和BLEDevice::connected()6.3 生产环境加固建议禁用调试串口发布固件前移除Serial.begin()及所有Serial.println()降低功耗与攻击面。启用看门狗在loop()开头添加wdt_reset()防止 BLE/Wi-Fi 驱动死锁导致设备假死。按钮复位网络利用BOOTSEL按键长按3s触发clearNetworks()BLEDevice::restartAdvertising()实现无手机干预的恢复出厂。多网络容灾在onWiFiStatus(WL_DISCONNECTED)中自动调用connectToStoredNetworks()尝试下一个网络提升鲁棒性。7. 移动端配套方案官方提供一个 Flutter 编写的参考 Apppico_wifi_provisioning_flutter_app其核心逻辑如下扫描与连接使用flutter_blue_plus插件扫描PicoW广播包连接后发现自定义服务。配对触发调用device.connect()自动触发配对流程Android/iOS 系统级弹窗。凭据传输用户输入 SSID/Password 后App 将字符串写入SSID/Password特性再写CMD_SAVE_NETWORK。状态监听订阅Pairing Status特性的 Notify实时显示配对进度轮询WiFi.status()获取连接结果。错误处理捕获GATT_ERROR、BLE_DISCONNECTED等异常引导用户重试或检查设备电量。该 App 已验证兼容 Android 10 与 iOS 14源码开源可作为企业定制化配网 App 的起点。对于无 App 开发能力的团队亦可使用 nRF ConnectNordic 官方工具手动写入特性值完成配网适合小批量调试。8. 性能与资源占用分析Flash 占用完整启用所有功能含 BLE Secure Notify ArduinoJson约占用 180–220 KB Flash剩余空间仍充足Pico W 总 Flash 2MB。RAM 占用运行时动态内存峰值约 12–15 KB含 LittleFS 缓存、JSON 解析缓冲区、BLE 连接表。BLE 广播间隔默认 100msBLEDevice::setAdvertisingInterval(100)可修改为 200–500ms 以延长电池寿命若使用电池供电。Wi-Fi 连接耗时从CMD_CONNECT到WL_CONNECTED平均 3–8 秒取决于 AP 信号强度与 DHCP 响应速度。该库未使用 RTOS如 FreeRTOS所有事件在loop()中轮询处理符合 Arduino 生态轻量级定位。若需更高并发性可将PicoWiFiProvisioning::loop()封装为 FreeRTOS 任务但需自行管理WiFi和BLE的线程安全当前 API 非线程安全。9. 安全边界与限制说明不提供 TLS/HTTPS本库仅解决“配网”环节的安全Wi-Fi 连接建立后的应用层通信如 MQTT over TLS需开发者另行实现。无 OTA 更新能力配网完成后固件升级仍需通过 USB 或自研 OTA 方案本库不涉及。不支持 WPA3底层arduino-pico的WiFi.h当前仅支持 WPA/WPA2-PSKWPA3 尚未适配。BLE 服务不可扩展自定义服务 UUID 与特性固定不开放 GATT 服务注册接口避免误用导致安全降级。密码明文存储/wifi_config.json中密码为明文因 Pico W 无硬件安全模块HSM此为权衡易用性与安全性的设计选择高安全场景建议结合外部加密芯片。该库已在多个工业传感器节点、智能照明控制器项目中稳定运行超 18 个月单设备平均配网成功率 99.2%统计样本327 台设备。其设计目标明确——成为 Pico W 设备从“离线”到“在线”的最短可信路径而非通用 BLE 框架。

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

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

免费获取报价