Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
11 KiB
蓝牙配网与账号绑定:完整精简版
日期:2026-09-13|状态:待实现的协议约定
手机/电脑与设备使用 BLE GATT 通信;设备联网后通过 HTTPS 联系平台。业务数据使用 UTF-8 JSON。
本文按新的精简约定实现,不与之前详细草案的报文混用。核心配网内容只有三项:Wi-Fi 名称、Wi-Fi 密码、绑定令牌。加密、分包由蓝牙通信模块统一处理。
1. 搜索并选择设备
- 用户先登录平台,长按设备配网键 3 秒。
- 设备进入配网模式,开启约 5 分钟蓝牙广播,名称为
EchoEar-000001,末尾编号与外壳标识一致。 - 用户点击平台的“添加设备”。页面按配网 Service UUID 筛选,浏览器弹出设备选择框。
- 用户选择编号对应的设备,页面连接设备的 GATT 服务。
网页通过用户手机/电脑的蓝牙发现设备,设备此时不需要先连接 Wi-Fi。网页须通过 HTTPS 打开,并使用支持 Web Bluetooth 的浏览器。浏览器约束
精简版约定以下固定 UUID;它们是本方案新拟定的值,前端与固件必须一致:
| 用途 | UUID | 操作 |
|---|---|---|
| 配网服务 | 8e7a2000-6f4b-4a2d-9c31-7d824f5e6100 |
用于发现并连接设备 |
| 接收入口 RX | 8e7a2001-6f4b-4a2d-9c31-7d824f5e6100 |
页面 Write with response,设备接收 |
| 应答入口 TX | 8e7a2002-6f4b-4a2d-9c31-7d824f5e6100 |
设备 Indicate,页面订阅接收 |
设备广播包放配网服务 UUID,完整名称放扫描响应包。UUID 表示服务类型,不表示设备归属;名称相同也不能视为同一台设备。
2. 连接并读取设备信息
页面连接后先订阅 TX,使用设备二维码中的配网凭据完成安全握手,再通过 RX 发送:
{"cmd":"get_info"}
设备通过 TX 返回:
{"device_id":"KP01-000000000001","model":"ECHOEAR-S3"}
device_id 是设备出厂写入、后台已登记的唯一编号;model 是设备型号。页面核对二维码与返回编号,显示设备型号和编号。这里只传设备身份信息,不传用户账号。
安全握手由前端、固件共用的通信模块完成;配网凭据属于设备,不是平台账号密码。当前 ESP32-S3 工程沿用乐鑫 Protocomm Security 2 patch 1,底层约定见第 8 节。
3. 平台申请绑定令牌
页面把选中的 device_id 提交给后台:
POST /api/v1/web/ble-bind-tokens
{"device_id":"KP01-000000000001"}
后台从当前登录态取得用户账号,检查设备是否允许绑定,返回:
{"bind_token":"后台生成的一次性绑定令牌"}
bind_token 同时对应“当前账号 + 目标设备 + 本次绑定申请”,固定有效期 5 分钟。由后台生成 32 个随机字节并编码为 Base64URL,不需要另加 session_id 或 setup_nonce。用户不填写这个字段,页面自动取得。
每次开始新一轮配网,后台生成新令牌,并在同一事务中使该账号、该设备此前未提交的申请失效;已经成功的绑定不受影响。页面只有在尚未发送配置或旧任务明确失败时才申请新令牌;旧任务结果不明时先查询,不能自动重开申请。
4. 页面发送 Wi-Fi 和绑定信息
用户输入设备要连接的 Wi-Fi 名称和密码,点击“开始配网”。页面通过 RX 发送:
{
"cmd":"configure",
"wifi_ssid":"MyHome",
"wifi_password":"example-password",
"bind_token":"后台生成的一次性绑定令牌"
}
cmd 表示这条消息要做什么;其余三项就是配网内容。因为 RX 共用,保留 cmd 用来区分“读取信息、提交配置、查询进度”。
| 字段 | 必须 | 约定 |
|---|---|---|
| cmd | 是 | 固定为 configure |
| wifi_ssid | 是 | Wi-Fi 名称,UTF-8 编码后 1~32 字节 |
| wifi_password | 是 | 本版支持 WPA2-Personal 密码,8~63 个可打印 ASCII 字符 |
| bind_token | 是 | 第 3 步后台返回的原值,32 字节的 Base64URL 编码,无填充 |
本版按当前 EchoEar 的 2.4GHz Wi-Fi 使用;开放网络、企业网络和 WPA3-only 不在本版范围内。Wi-Fi 密码只通过加密蓝牙给设备,不上传后台;平台登录密码也不发给设备。
设备完整收到并校验后,保存待处理任务,通过 TX 返回:
{"status":"received"}
received 只代表已收到配置。设备屏幕提示“确认配网与绑定”,用户在 60 秒内短按确认后开始执行;确认关联当前这份配置,由设备内部记录,不增加消息字段。
5. 设备连接 Wi-Fi,并向后台绑定
设备固件执行以下步骤:
- 解析 wifi_ssid、wifi_password、bind_token,完成本次实体确认。
- 调用 Wi-Fi 驱动连接路由器,状态变为 wifi_connecting。
- 取得 IP 后保存可用 Wi-Fi 配置,状态变为 binding。
- 使用设备自身的认证凭据,通过 HTTPS 请求平台绑定接口。
POST /api/v1/device/ble-bind
{"bind_token":"设备通过蓝牙收到的绑定令牌"}
设备编号由既有设备认证请求头提供,body 不重复传。认证沿用平台已有 DEVREQ1;仅提交一个 device_id 字符串不算设备认证。
后台验证令牌、设备身份、账号权限及当前归属,在同一个事务中建立绑定并消费令牌,然后返回:
{"status":"success"}
设备收到后台成功结果并保存后,才把本地状态改为 success。设备已绑定其他账号时后台拒绝,不覆盖原归属。AI 服务激活独立处理,不阻塞账号绑定完成。
6. 页面查询进度,结束配网
页面每隔约 2 秒通过 RX 发送:
{"cmd":"get_status"}
设备通过 TX 返回当前状态;例如:
{"status":"wifi_connecting"}
失败时多一个 message 字段:
{"status":"failed","message":"Wi-Fi连接失败,请检查网络名称和密码"}
| status | 页面含义 |
|---|---|
| idle | 尚未收到配置 |
| received | 已收到,等待设备上的确认 |
| wifi_connecting | 正在连接 Wi-Fi |
| binding | 正在确认账号绑定结果 |
| success | 网络可用、后台绑定成功、设备结果已保存 |
| failed | 本次未完成,展示 message |
读取设备信息、提交配置、查询状态必须串行:前一个应答处理完再发送下一个请求。设备只返回请求对应的应答,不插入主动进度推送。配置执行在设备后台进行,查询状态不会重新触发配网。
页面看到 success 后显示“设备添加成功”并断开。设备成功后最多保留蓝牙 30 秒用于读取结果,然后关闭配网服务;关闭蓝牙不影响 Wi-Fi 和绑定。
7. 必要的失败处理
- 同一份配置重复发送: bind_token 同时作为任务标识。相同令牌、相同 Wi-Fi 参数只返回原任务状态,不重复执行;相同令牌携带不同参数则返回 failed,提示先结束原申请。设备一次只处理一个任务,新令牌不能覆盖进行中的任务。
- 蓝牙断开: 不取消已经受理的任务。窗口仍有效时可以重新握手并查询当前任务;页面也可向下面的后台接口查询原申请。
- 设备已提交但没有收到回包: 使用原令牌重试
/api/v1/device/ble-bind。后台先检查同一申请是否已成功,再判断未提交令牌是否过期;同一有效绑定返回原成功结果,不建立第二条绑定。 - 暂时无法确认云端结果: 保持 binding 并恢复查询,不直接报“绑定失败”,也不发起第二次绑定。页面等待超过 2 分钟时显示“仍在确认,可稍后查看”。
- Wi-Fi 连接失败: 返回 failed 和原因,保留原可用配置。用户修改网络后创建新的绑定申请再提交。
- 云端成功但设备保存失败: 后台绑定保留,设备恢复本地保存;页面提示“已绑定,设备正在同步”,不要求重新绑定。
页面查询云端申请:
POST /api/v1/web/ble-bind-status
{"bind_token":"本次绑定令牌"}
后台只允许原申请账号查询,返回 status=pending / success / failed;failed 时带 message。这里的 success 只表示后台绑定已完成,不能证明设备已经保存结果。令牌放请求正文,不放 URL。
三个 HTTP 接口均为拟新增。上文 HTTP 响应示例展示业务 data 内容,实际外层沿用平台现有响应封装;网页接口使用现有登录 Cookie 和 CSRF 校验,设备接口使用现有设备认证。后台不返回或记录 Wi-Fi 密码,设备日志不输出密码与令牌。
8. 给开发的底层约定
以下由通信模块实现,页面业务只调用“读信息、配网、查状态”:
- 使用 ESP-IDF v5.5.1 Protocomm Security 2 patch 1 完成双向认证及加密;二维码保存设备配网凭据,单设备云端认证密钥仍留在设备和后台。握手 protobuf、密钥和 nonce 规则直接遵循官方实现,不另设计密码算法。官方组件、固定版本实现
- 为了知道完整消息在哪里结束,每条传输消息只加 3 字节头:
类型(1字节)+ 内容长度(2字节,小端)+ 内容。类型 0 是官方握手 protobuf,类型 1 是加密后的完整 JSON;内容长度为 1~2048 字节,业务密文最少 17 字节。握手完成前拒绝类型 1,完成后拒绝类型 0。 - 先加密完整 JSON,再加头,最后每次最多 20 字节顺序写入 RX。头只在整条消息开头出现一次;设备按长度收齐后才解密处理。TX 应答用相同格式分片。前端等待每次写入应答,设备等待每次 Indication 确认后再发下一片。
- 字段名按本文固定;不接受重复字段、未知字段或非法 UTF-8。最长业务 JSON 为 2032 字节,以留出 16 字节认证标签;不要把一个 GATT 分片当作完整 JSON。
- 同连接一次只允许一个未完成请求;10 秒无收发进展或 45 秒仍未收齐完整消息就断开,重新握手。半条消息和旧加密会话不续用;业务结果通过原 bind_token 恢复。两轮握手消息的发送顺序与加密计数直接交给官方安全层。
- 设备接收回调只组包并投递任务,Wi-Fi 和 HTTPS 在后台执行;完整业务请求应在 5 秒内开始返回应答。蓝牙和 Wi-Fi 模块均不解释“用户账号”,账号归属由后台令牌记录决定。
设备只在配网窗口内接收新配置,已受理任务可在窗口结束后继续完成。业务层不再单列配网窗口随机号;安全握手仍保留其内部随机数,令牌到期、一次消费、当前任务约束及实体确认仍须执行。
当前工作区设备尚需实现该蓝牙服务,本文件没有修改固件或发布后台接口。联调至少验证:正常成功、密码错误、重复提交、蓝牙断开、后台回包丢失、已绑定设备拒绝覆盖。