- tags
- [IoT, bootstrap, 配网, 4G, 服务发现, 信任根]
- created
- 2026-04-24
- updated
- 2026-04-24
- sources
- [raw/snippets/bk7258-build/iot-architecture-onboarding.md]
定义¶
IoT 设备启动后必须知道"开机第一个该连谁"——即去哪里拿 MQTT broker 地址、TLS 证书、设备身份。这是 IoT 版本的 service discovery 问题。WiFi 设备可以通过 BLE 配网时由 APP 当场推送,4G 设备没有蓝牙就不能这么做——固件总得知道某个入口作为信任根。本页梳理业内 5 种主流方案 + 信任根的本质问题,以及它对 sentino-iot 扩展到 4G 形态时的工程含义。
背景:WiFi 模式的"配网时下发 MQTT 接入点"¶
sentino-iot 当前 BK7258 + WiFi 形态走的是这个路径:APP 通过蓝牙下发的 JSON 不只包含 WiFi SSID + 密码,还包含 MQTT 接入信息(8 字段):
| 字段 | 含义 | 写到哪 |
|---|---|---|
sid |
WiFi SSID | Config_Net_info->Wifi_Info.Ssid |
pw |
WiFi password | Config_Net_info->Wifi_Info.Key |
mq |
MQTT host 域名 | Config_Net_info->Mqtt_Info.Mqtt_Host |
iport |
MQTT 端口 | Config_Net_info->Mqtt_Info.Mqtt_Port |
bid |
绑定 ID(用户绑设备的关系凭证) | Config_Net_info->Bind_Id |
userId |
用户 ID | Config_Net_info->User_Id |
country |
国家码(默认 CN) | Config_Net_info->Country_Code |
tz |
时区(默认 Asia/Shanghai) | Config_Net_info->User_Tz_Str |
→ MQTT 接入地址不是固件硬编码的,由 APP 在配网时动态下发,存到 Flash。这种设计的好处:APP 端可以根据用户区域、账号路由到不同的 MQTT 集群(国内/海外、生产/测试),固件出厂后不用换。
→ Sentino 当前实际接入点是 mqtt-iot.sentino.jp:8883(Sentino 自建 MQTT 集群),不是腾讯云原厂常量。这一条事实在排查"MQTT 连不上"的问题时容易被误以为是固件硬编码——实际是 Flash 里的配网信息。
4G 设备的核心约束:没有蓝牙怎么办¶
4G 设备:
- SIM 卡是运营商烧录的凭证(IMSI/Ki),上电就能联网
- 没有"配网"概念——rino_ble/、rino_app/rino_config_net.c、rino_wifi/ 在 4G 项目里全都用不上
- 但固件总得知道"开机第一个该连谁",否则啥都干不了
这跟后台的 service discovery 是同一类问题。业内 5 种主流方案,按"灵活度 vs 复杂度"排序:
5 种方案对比¶
方案 A:出厂硬编码¶
#define MQTT_HOST "mqtt-iot.sentino.jp"
#define MQTT_PORT 8883
- 适用:单一品牌、单一 broker、不分区域
- 缺点:换 broker 必须 OTA 全量固件;不能多租户路由
- 谁用:早期消费 IoT、白牌设备
方案 B:引导服务器重定向 (Bootstrap Server) ★ 行业主流¶
固件硬编码一个轻量引导地址,开机先连它,引导服务器返回真正的 broker 地址:
开机 → 4G 拨号上网 → HTTPS POST https://bootstrap.sentino.jp/v1/lookup
Body: {"device_id":"xxx","region":"JP","fw":"1.0.5"}
↓
应答: {"mqtt_host":"mqtt-iot-jp.sentino.jp","mqtt_port":8883,"expires":3600}
↓
缓存到 Flash → 连 MQTT
下次开机直接读 Flash 缓存,跳过引导
引导服务器可以根据: - 设备区域(SIM 卡国家码、IP 地理位置)→ 路由到最近 broker - 租户/品牌(设备 ID 前缀)→ 路由到对应集群 - 灰度/AB 测(按 device_id 分桶)→ 部分设备连新 broker
这是腾讯云/阿里云/AWS IoT Core 的标准做法: - AWS:Fleet Provisioning by Claim(设备出厂烧 claim cert,首次连用 claim cert 申请独立 cert,5 分钟内换证。详见 AWS IoT 官方) - 阿里云:一型一密(动态注册) - 腾讯云:动态注册(dynamic register)
方案 C:DNS 间接寻址(最优雅)¶
固件硬编码一个逻辑域名,靠 DNS 把它解析到具体集群:
- GeoDNS:日本来的解析到日本节点
- CNAME:今天指向 cluster-a,明天切到 cluster-b
- TTL 5 分钟:可以快速切换
- 好处:固件零改动就能切集群,运维成本最低
- 缺点:不能按设备做差异化路由(DNS 看不到 device_id)
方案 D:HTTP 拉配置文件¶
固件硬编码一个配置 URL,开机拉 JSON:
GET https://config.sentino.jp/devices/xxx/config.json
→ {"mqtt_host":"...", "mqtt_port":8883, "ca_cert_url":"...", ...}
- 跟方案 B 类似,但服务端可以是纯静态文件 + CDN
- 缺点:要单独维护一个文件分发系统
方案 E:LwM2M Bootstrap(电信/工业标准)¶
NB-IoT/LTE-M 设备的标准做法。固件烧一个 BS server URI:
固件硬编码: coaps://bootstrap.operator.com:5684
设备连 BS server (CoAP over DTLS)
BS server 用 LwM2M 协议把真正的 server 配置写进设备 Object 1
- 适用:电信运营商、工业 IoT、NB-IoT
- 消费 IoT 用得少
信任根问题:不可能"零硬编码"¶
无论哪种方案,固件里总得硬编码"某个入口"作为信任根。区别只是入口的形态:
| 方案 | 硬编码的是什么 | 信任怎么验证 |
|---|---|---|
| A 硬编码 | MQTT host:port | TLS 证书(pin 在固件里) |
| B 引导 | Bootstrap URL | TLS 证书 + 应答签名 |
| C DNS | 逻辑域名 | TLS 证书 + DNSSEC |
| D HTTP | 配置 URL | TLS 证书 + 文件签名 |
| E LwM2M | BS server URI | DTLS PSK / 证书 |
→ 不可能完全"零硬编码",否则设备会被任何 DNS 投毒/中间人接管。问题不是要不要硬编码,而是硬编码什么 + 怎么验证。
选型决策树¶
要不要让 APP 当场参与配置?
├─ 要 → 蓝牙配网(Sentino 当前 WiFi 项目方案)
└─ 不要
├─ 要按设备做差异化路由(多租户/灰度)?
│ ├─ 要 → 方案 B 引导服务器
│ └─ 不要
│ ├─ 要按区域路由?
│ │ ├─ 要 → 方案 C DNS(GeoDNS)
│ │ └─ 不要 → 方案 A 硬编码
│ └─ 偏静态 → 方案 D HTTP 配置文件
└─ 是 NB-IoT 运营商方案 → 方案 E LwM2M Bootstrap
Sentino 已有基础设施(方案 B 留位)¶
Sentino BK7258 项目的 rino_iot_sdk 已经为方案 B(动态注册)留好了基础设施,只是当前 WiFi 流程不走它:
| 文件 | 作用 |
|---|---|
rino_iot_sdk/factory_test/factory_dynamic_register.c |
动态注册客户端(连引导服务器拿三元组) |
rino_iot_sdk/factory_test/factory_dynamic_register.h:17 |
Register_Test_Mqtt_Client_Task_Start(host, port, cbs) |
rino_iot_common.h:22 DYN_REG_SERVER_URL |
引导服务器域名常量(腾讯版本,需替换为 Sentino) |
纯 4G 版本最自然的接入路径:
1. 固件硬编码 bootstrap.sentino.jp(替换上述常量)
2. 4G 拨号上网后第一件事:HTTPS 请求引导服务器
3. 引导服务器返回 mqtt_host/mqtt_port/triple/user_id
4. 缓存到 Flash → 连 MQTT
5. 下次开机直接读缓存,跳过引导
→ 完全不需要蓝牙,且保留"换集群不换固件"的能力——这是方案 B 在 Sentino 4G 形态产品上的具体落地。
4G 设备的三芯片架构(Sentino SDK 已预留)¶
虽然当前 BK7258 + WiFi 项目不用 4G,但 SDK 里 rino_iot_sdk/rino_protocol/rino_soc_mcu_protocol.{h,c} 预留了 4G 协议代码——属于另一种产品形态:
BK7258 (SoC) ─UART1─ 外挂 MCU (低功耗,电池供电,PIR/电量) ─UART2─ CAT1 模组 (移远 EG800G-EULD)
↑ ↑
业务大脑 接入运营商网络
典型场景:电池供电的低功耗联网设备(户外摄像头、智能门锁、无人值守传感器)。平时外挂 MCU 深度睡眠,PIR 触发或定时唤醒,唤醒后通过 CAT1 上报数据,BK7258(如果存在)只在需要 WiFi 配置/OTA 时才启动。
SoC-MCU 协议关键命令:
- SOC_MCU_CMD_GET_RSSI (0x90) — 获取 CAT1 信号强度
- SOC_MCU_CMD_CAT1_INIT (0xC0) — 命令 CAT1 重新初始化
- SOC_MCU_CMD_GET_CAT1_STATE (0xC1) — 获取 CAT1 状态
- SOC_MCU_CMD_EXT_UART_SET (0x93) — 透传扩展串口数据(让 SoC 直接发 AT 指令到 CAT1)
- SOC_MCU_CMD_REPORT_RSSI (0xD4) — MCU 主动上报信号强度
注意 SoC 不直接说 AT 指令,而是通过 SOC_MCU_CMD_EXT_UART_SET 让外挂 MCU 把数据透传到 CAT1 那条串口。AT 指令的脏活在外挂 MCU 那边——这是把"通信协议复杂度"和"业务大脑"物理隔离的典型嵌入式做法。
协议帧格式:Header(0xAB) | Length(2B) | Cmd(1B) | Data(nB) | XOR(1B),小端序、异或校验、错误码 0~5——嵌入式 UART 自定义二进制协议的典型样式。
4G 项目特有考量¶
| 维度 | 说明 |
|---|---|
| 流量成本 | 物联网卡按 MB 计费,要拉长 MQTT keepalive、压缩 payload |
| 信号波动 | 隧道/电梯掉网常态,必须做断线重连 + 离线消息缓存到 Flash |
| 功耗管理 | NB-IoT 靠 PSM/eDRX 深度睡眠,秒级 vs 分钟级上报差几十倍电池寿命 |
| 运营商管理 | 物联网卡有实名制、专网、机卡绑定政策,换 SIM 可能停卡 |
| 位置定位 | 没 WiFi 但有基站定位 (LBS) / GPS / AGPS |
| 远程升级 | 4G 流量贵,必须用差分升级(只下发 patch) |
| TCP 长连接 | 运营商 NAT 超时一般 5~10 分钟,MQTT keepalive 必须小于这个 |
对 Sentino 战略的工程含义¶
- 当前 BK7258 + WiFi + BLE 配网形态是消费 IoT 主流——只要客户场景在室内固定位置(玩偶、故事机、桌面陪伴),不需要切换到 4G
- 如果未来扩展户外/移动场景(车载玩偶、户外宠物追踪、儿童手表),方案 B 引导服务器 + 现有动态注册基础设施可以支撑,不需要重新设计
- 可以做混合形态产品——同一套 SDK + 业务代码,BSP 层切换 + bootstrap 路径分流,WiFi 和 4G 双形态客户都能服务(这是 embedded-firmware-layering 分层架构的复用价值)
- 客户对话时的话术:当客户问"你们设备要不要后台改硬件支持 4G?"——答"BSP 层重写 + bootstrap 路径切换,业务零改动",体现工程成熟度
相关概念¶
- iot-platform-fundamentals — IoT 概念对照("配网是 IoT 最特殊的环节"上位陈述)
- embedded-firmware-layering — 分层架构使 4G 切换成为 BSP 层局部改动
- ble-provisioning — WiFi 项目的"配网时下发 MQTT 接入点"路径
- device-lifecycle — 出厂 → 配网 → 绑定 → 运行的生命周期上下文
- mqtt-device-protocol — MQTT 5.0 协议本身(与 broker 地址来源无关)
- bk7258-firmware — BK7258 BSP 层的具体硬件依赖
- sentino-iot — 当前形态 + 4G 扩展可能性