Hi3863 已配网后回连:从开机到上云的完整流程
1. 引言
设备完成首次配网后,netFlag == 2(CHECKED)且 loginInfo(含 devId/secret/psk/url)已持久化在 flash 中。此时设备重启,无需再次配网,只需完成"Wi-Fi 回连 → 云端登录"即可重新上线。
开机 → 读取持久化凭据 → 回连 Wi-Fi → 读取 loginInfo → 云端登录 → 在线
本文梳理已配网设备从开机到重新上云的完整过程,重点解释为什么配网完成后重启能成功上云,以及配网中途断电重启为什么会卡死。涉及代码均以 foundation/communication/iot_connect 为准。
2. 核心结论
- 已配网设备重启后能上云,是因为 loginInfo 持久化在 flash 中,云 FSM 走 BIND 分支直接用 loginInfo 登录,不依赖 registerInfo(RAM)。
- 配网中途断电重启后无法上云,是因为 loginInfo 尚未生成(未注册)、registerInfo 仅存 RAM 已丢失,云 FSM 的 INIT 状态两条路径都走不通,永久卡在 INIT。
3. 数据持久化对比
| 数据 | 存储位置 | 持久化 | 重启后 | 说明 |
|---|---|---|---|---|
| WiFi 账密 (netFlag) | flash (conn_data) | 是 | 保留 | svc_conn_conf.c → SecurityStoreCommit |
| 注册信息 (registerInfo) | RAM (g_regFlag) | 否 | 丢失 | `` 纯 static 变量 |
| 登录信息 (loginInfo) | flash (dev_data) | 是 | 保留 | config_login_info.c → ConfigInfoSave |
| 设备ID (devId) | flash (dev_data) | 是 | 保留 | CONFIG_INFO_KEY_DEVICE_ID,判定绑定状态的依据 |
关键:registerInfo 纯 RAM,且每次开机被 `` 主动清零。
4. 开机回连流程(netFlag=2 正常回连)
4.1 StartWifiConnectService 执行(本次发起连接)
StartWifiConnectService()
├─ ConnectConfigInfoInit() → netFlag = CHECKED
│ → ConfigIsNetInfoChecked() = true
└─ 按设备级别分流:
├─ MINI(瘦设备):
│ ConfigIsNetInfoChecked()==true
│ → SchedTimerAdd(300ms) → WifiInitialConnectDelayCallback
│ → ConnSvcActionConnectWifi()
│ (300ms 延迟是为等 Wi-Fi 驱动/链路就绪)
└─ 非 MINI(富设备):
ConfigIsNetInfoNeedVerify() || Checked()
→ 立即 ConnSvcActionConnectWifi()
4.2 回连执行
ConnSvcActionConnectWifi()
├─ 发布 CONNECTING 事件
├─ IotcConnectWifi()
└─ netFlag 已是 CHECKED(非 NEED_VERIFY)
→ 不再挂 60s 验证定时器
WifiStatusCheckTimerCallback 检测到网络已恢复
└─ 设备继续走云端 M2M_CLOUD 登录(含 3s 初始退避)→ 进入正常在线状态
4.3 开机统一决策表
| 绑定状态 | netFlag | 清理凭据 | 触发连接 | 连接动作 | 结果 |
|---|---|---|---|---|---|
| 未绑定 | 1 | ✓ 清理 → netFlag=0 | ✗ 不连 | — | 回配网 |
| 未绑定 | 2 | ✓ 清理 → netFlag=0 | ✗ 不连 | — | 回配网 |
| 已绑定 | 1 | ✗ 不清 | ✓ 连 | 验证连接(60s 验证窗口) | 回连上云 |
| 已绑定 | 2 | ✗ 不清 | ✓ 连 | 直接连接 | 回连上云 |
| 任意 | 0 | ✗ 不清 | ✗ 不连 | — | 等配网 |
未绑定设备取不到 loginInfo(
GetLoginInfo返回 isExist=false),云端 FSM 卡死 INIT 永远上不了云,故命中清理时强制回配网。
5. 云 FSM INIT 状态决策逻辑
CloudFsmInitHandler
│
├─ GetBindStatus()
│ └─ IsDeviceBinded() → 检查 flash 中 CONFIG_INFO_KEY_DEVICE_ID 是否非空
│
├─ 分支A: bindStatus == BIND / REVOKE(设备已绑定)
│ │
│ ├─ DevSvcProxyGetLoginInfo(&isExist, &loginInfo)
│ │ └─ GetLoginInfo()
│ │ ├─ IsDeviceBinded() == false → isExist=false → return cur(卡死)
│ │ └─ IsDeviceBinded() == true → isExist=true
│ │ └─ ConfigGetLoginInfo() → 从 flash 读取 devId/secret/psk/url
│ │ └─ 任一字段缺失 → ret != IOTC_OK → return cur(卡死)
│ │
│ └─ isExist && ret==OK → CREATE_LINK ✓
│
└─ 分支B: bindStatus == NOT_BIND(设备未绑定)
│
├─ DevSvcProxyGetRegisterInfo(&isExist, ®Info)
│ └─ GetRegisterInfo()
│ └─ IsRegisterInfoExist() → 读 g_regFlag(RAM)
│ └─ g_regFlag == false → isExist=false → return cur(卡死)★
│
└─ isExist && ret==OK → CREATE_LINK(进入注册流程)✓
6. 配网成功后重启为什么能成功上云
6.1 前提:首次配网完整完成(不断电)
首次配网在同一上电周期内跑完了完整流程,云端注册成功,loginInfo 已写入 flash:
flash 状态(配网完成后的快照):
conn_data: netFlag=2 (CHECKED) ✓
dev_data:
devId = "xxx-xxx-xxx" ✓ ← ConfigSaveLoginInfo 写入
secret = "yyy" ✓ ← 云端注册响应返回
psk = [16字节] ✓ ← 从 registerInfo 继承
url = "https:
backupUrl = "https:
6.2 重启后的完整成功流程
┌─ 重启上电 ──────────────────────────────────────────────────────────────┐
│ │
│ ① 框架初始化 (FwkInitAll) │
│ DEP级别: ConfigClearRegisterInfo → 清 g_regFlag=false(不影响) │
│ FWK级别: DeviceServiceStart │
│ → ConfigInfoInit() │
│ → SecurityStorePull("dev_data") │
│ → 从 flash 加载到 g_storeInfo │
│ devId="xxx-xxx-xxx" ← 非空! │
│ secret="yyy" │
│ psk=[16字节] │
│ url="https:
│ │
│ ② WiFi 回连 │
│ ConnectConfigInfoInit() → SecurityStorePull("conn_data") │
│ → netFlag=2 (CHECKED) │
│ StartWifiConnectService: │
│ ConfigIsNetInfoChecked() == true │
│ → 300ms 延迟 → WifiInitialConnectDelayCallback │
│ → ConnSvcActionConnectWifi() │
│ → IotcConnectWifi: EnableWifi + AddDeviceConfig + ConnectTo │
│ → WiFi 连接成功 │
│ │
│ ③ 网络检测 │
│ WifiStatusCheckTimerCallback (1s周期轮询): │
│ IsNetworkConnected() == true │
│ → EventBusPublishSync(WIFI_NET_CONNECT) │
│ → netFlag 仍为 2(已是CHECKED,直接 return,不重复设置) │
│ │
│ ④ 云 FSM 上云(关键成功路径) │
│ M2mFsmTimerCallback → UtilsFsmRunning → CloudFsmInitHandler: │
│ │
│ GetBindStatus() │
│ → IsDeviceBinded() │
│ → ConfigInfoGet(CONFIG_INFO_KEY_DEVICE_ID) │
│ → devId = "xxx-xxx-xxx" ← 非空! │
│ → return true │
│ → DEV_BIND_STATUS_BIND ✓ │
│ │
│ BIND 分支: │
│ → DevSvcProxyGetLoginInfo(&isExist, &loginInfo) │
│ → GetLoginInfo() │
│ → IsDeviceBinded() == true → isExist = true │
│ → ConfigGetLoginInfo() │
│ → ConfigInfoGet(DEVICE_ID) → devId ✓ │
│ → ConfigInfoGet(LOGIN_SECRET) → secret ✓ │
│ → ConfigInfoGet(LOGIN_PSK) → psk ✓ │
│ → ConfigInfoGet(PSK_ID) → pskId ✓ │
│ → ConfigInfoGet(PSK_VALID) → valid ✓ │
│ → ConfigInfoGet(LOGIN_URL) → url ✓ │
│ → ConfigInfoGet(LOGIN_BACKUP_URL) → backupUrl ✓ │
│ → 全部读取成功,ret=IOTC_OK │
│ │
│ → return CREATE_LINK ✓ ← 成功跳出 INIT! │
│ │
│ ⑤ 云端连接与登录 │
│ CREATE_LINK: │
│ → M2mCloudLinkCreate → 建立链路 │
│ → M2mCloudBackoffInit → 退避3s │
│ │
│ CONNECT: │
│ → ConnSvcProxyIsNetConnected() == true ✓ │
│ → M2mCloudLinkConnect → 连接云端服务器 │
│ │
│ LOGIN: │
│ → M2mCloudBuildLoginRequest → 用 loginInfo.secret/psk 构建登录请求 │
│ → 发送到云端 │
│ → 云端验证通过 → 返回成功 │
│ → → ONLINE ✓ │
│ │
│ 结果: WiFi ✓ + 云端 ONLINE ✓ │
└────────────────────────────────────────────────────────────────────────┘
7. 配网中途断电重启为什么会卡死
7.1 问题现象
设备配网后重启,WiFi 回连成功(netFlag: 1→2),但云端无任何日志输出,无法上云。
7.2 断电时序
┌─ 第一次上电,App 配网 ─────────────────────────────────────────────────┐
│ │
│ App 发 netCfg JSON(devId/psk/code/url + ssid/password) │
│ → registerInfo → RAM (g_regFlag=true) │
│ → WiFi 账密 → flash (netFlag=1) │
│ → WiFi 连接成功 │
│ → netFlag: 1→2 (CHECKED) │
│ │
│ 云 FSM INIT → NOT_BIND → registerInfo 存在 → CREATE_LINK │
│ → CONNECT → PSK → REGISTER ... │
│ │
│ ★★★ 此时断电(注册未完成)★★★ │
│ │
│ flash 状态: RAM 状态(丢失): │
│ netFlag = 2 g_regFlag = (丢失) │
│ devId = (空) g_devRegInfo = (丢失) │
│ loginInfo = (不存在) │
└───────────────────────────────────────────────────────────────────────┘
↓ 断电重启
┌─ 第二次上电 ──────────────────────────────────────────────────────────┐
│ │
│ 开机初始化: │
│ core_prof_init.c DEP级别: ConfigClearRegisterInfo → 清零(已是零) │
│ WiFi 驱动初始化 │
│ BLE 广播启动 │
│ 设备服务启动 → ConfigInfoInit → 从 flash 加载 dev_data │
│ │
│ WiFi 连接服务启动: │
│ StartWifiConnectService: │
│ netFlag=2 (CHECKED) → 延迟 300ms → ConnSvcActionConnectWifi │
│ WiFi 连接成功 ✓ │
│ │
│ 云 FSM 定时器 tick → CloudFsmInitHandler: │
│ GetBindStatus() │
│ → IsDeviceBinded(): 读 CONFIG_INFO_KEY_DEVICE_ID │
│ → devId 为空 → NOT_BIND │
│ 分支B: NOT_BIND │
│ → IsRegisterInfoExist(): 读 g_regFlag │
│ → g_regFlag = false(RAM丢失 + 开机已清零) │
│ → isExist = false │
│ → return cur(卡在 INIT) │
│ │
│ 结果: WiFi 已连接,但云 FSM 永久卡在 INIT,无任何云日志输出 │
└───────────────────────────────────────────────────────────────────────┘
7.3 卡死根因
云 FSM 的 INIT 状态有两条路径(绑定/未绑定),分别依赖 loginInfo 和 registerInfo:
- BIND 分支:依赖 loginInfo(flash),但配网中途断电时 loginInfo 尚未生成。
- NOT_BIND 分支:依赖 registerInfo(RAM),但 RAM 已丢失且开机被清零。
两条路径都走不通,FSM 永久卡在 INIT。
8. 成功 vs 失败 的唯一区别
成功(配网完成后重启) 失败(配网中途断电重启)
──────────────────────── ────────────────────────
flash: devId 有(注册成功后写入) 空(未注册)
flash: loginInfo 完整(secret/psk/url) 不存在
RAM: registerInfo 已清(不影响,走BIND路径) 丢失(需走NOT_BIND路径但无数据)
CloudFsmInitHandler: → BIND → loginInfo 存在 → NOT_BIND → registerInfo 不存在
→ CREATE_LINK ✓ → return cur(卡死)✗
一句话总结:配网成功后 loginInfo 持久化在 flash 中,重启后云 FSM 走 BIND 路径直接用 loginInfo 登录,不依赖 registerInfo(RAM),所以能成功上云。
9. 三种场景对比
场景1:正常配网(不断电)
App 下发 registerInfo + WiFi账密 → WiFi连接 → 云注册成功 → loginInfo写入flash → 绑定
↓
下次开机: BIND分支 → 读 loginInfo → CREATE_LINK → LOGIN → ONLINE ✓
场景2:配网中途断电重启(问题场景)
App 下发 registerInfo + WiFi账密 → WiFi连接 → ★断电★
↓
重启: WiFi回连 ✓, 但 registerInfo 丢失(RAM), loginInfo 不存在(未注册)
→ 云 FSM 卡 INIT ✗
场景3:已绑定设备换 WiFi
设备已绑定(loginInfo在flash) → App 下发新WiFi账密 → netFlag=1 → WiFi连接
↓
重启: WiFi回连 ✓, IsDeviceBinded()=true(devId在flash)
→ BIND分支 → 读 loginInfo(flash) → CREATE_LINK → LOGIN → ONLINE ✓
(此场景可正常上云,因为 loginInfo 已在 flash 中)
10. loginInfo 的完整生命周期
10.1 何时诞生
loginInfo 不是 App 直接下发的,而是设备向云端注册成功后,由云端返回:
PSK 握手成功
→ REGISTER 状态:发送注册请求(含 code/devId/psk)
→ 云端返回注册响应
→ M2mCloudParseRegisterResponse
→ ParseRegRespSenseInfo
→ DevSvcProxyRecvLoginInfo(json) ← 从注册响应JSON中提取
→ DeviceServiceRecvLoginInfo
→ ConfigSaveLoginInfo(&info) ← 写入 flash (dev_data)
→ CONFIG_INFO_KEY_DEVICE_ID ← devId 持久化
→ CONFIG_INFO_KEY_LOGIN_SECRET ← secret 持久化
→ CONFIG_INFO_KEY_LOGIN_PSK ← psk 持久化
→ CONFIG_INFO_KEY_LOGIN_URL ← url 持久化
→ ConfigInfoSave() ← flush 到 flash
10.2 loginInfo 的字段来源
| loginInfo 字段 | 来源 | 说明 |
|---|---|---|
| devId | 云端注册响应 JSON | 设备在云端的唯一标识 |
| secret | 云端注册响应 JSON | 登录密钥 |
| psk | registerInfo.psk(注册时用的) | 从注册信息继承 |
| url | registerInfo.url(注册时用的) | 从注册信息继承 |
| backupUrl | registerInfo.backupUrl | 从注册信息继承 |
10.3 何时使用
后续每次开机,设备已绑定(devId 在 flash 中):
CloudFsmInitHandler → BIND 分支
→ 读取 loginInfo(从 flash)
→ CREATE_LINK → CONNECT → LOGIN(用 loginInfo 登录)
→ ONLINE
11. registerInfo 的完整生命周期
11.1 诞生
App 通过 BLE netCfg 服务下发,与 WiFi 账密在同一 JSON 中:
PutBleSvcNetCfg
→ NetCfgProcessFromJson
→ DevSvcProxyRecvBindInfo(json) ← 解析 registerInfo
→ DeviceServiceRecvBindingInfo
→ ConfigSaveRegisterInfo(&info) ← 写入 RAM (g_devRegInfo)
→ g_regFlag = true ← RAM 标记
→ ConnSvcProxySetNetCfgInfo(json) ← 解析 WiFi 账密
→ netFlag = 1 (NEED_VERIFY) ← 写入 flash (conn_data)
11.2 使用
首次注册时,云 FSM 走 NOT_BIND 分支:
CloudFsmInitHandler → NOT_BIND 分支
→ IsRegisterInfoExist() == true (g_regFlag)
→ 读取 registerInfo(从 RAM)
→ CREATE_LINK → CONNECT → PSK → REGISTER
→ 注册成功 → loginInfo 写入 flash → 设备绑定
11.3 死亡(三种方式)
- 开机清除:`` 注册了
ConfigClearRegisterInfo作为FWK_INIT_LVL_DEP初始化单元,每次开机执行 - 恢复出厂:`` 收到
IOTC_CORE_COMM_EVENT_MAIN_RESTORE事件时清除 - 注册成功后清 RAM:`` 清零
ctx->authInfo.regInfo(但不清全局 g_devRegInfo)
12. 运行期断线自动重连
已配网设备(netFlag=2)在运行期断线时的自动重连机制:
WifiStatusCheckTimerCallback (每 1s)
│
├─ IsNetworkConnected() 变为 false(断线)
│ ├─ 发布 WIFI_NET_DISCONNECT 事件
│ ├─ ConfigIsNetInfoChecked()? → true (netFlag==2)
│ └─ 启动 20s 重连定时器
│ WifiReconnectTimerCallback → IotcConnectWifi() ← 每 20s 重连一次
│
└─ IsNetworkConnected() 变为 true(恢复)
├─ 发布 WIFI_NET_CONNECT 事件
├─ 停止重连定时器
└─ ConfigIsNetInfoChecked()? → true → return(无需升级)
13. 代码位置索引
| 功能 | 文件 | 函数 |
|---|---|---|
| 云FSM INIT决策 | m2m_cloud_fsm.c | CloudFsmInitHandler |
| 绑定状态判定 | config_login_info.c | IsDeviceBinded |
| loginInfo 读取 | config_login_info.c | ConfigGetLoginInfo |
| loginInfo 写入 | config_login_info.c | ConfigSaveLoginInfo |
| loginInfo 从云端接收 | svc_cloud_login.c | DeviceServiceRecvLoginInfo |
| 注册响应解析→保存loginInfo | m2m_cloud_reg.c | ParseRegRespSenseInfo |
| registerInfo 读取 | config_register_info.c | ConfigGetRegisterInfo |
| registerInfo 写入(RAM) | config_register_info.c | ConfigSaveRegisterInfo |
| registerInfo 存在判定 | config_register_info.c | IsRegisterInfoExist |
| registerInfo 开机清除 | core_prof_init.c | CORE_PROFILE[] "reg_info" |
| registerInfo 从App接收 | svc_cloud_setup.c | DeviceServiceRecvBindingInfo |
| netFlag 管理 | svc_conn_conf.c | ConfigSetNetInfoFlag |
| netFlag 持久化 | svc_conn_conf.c | SecurityStoreCommit |
| WiFi连接服务启动 | wifi_svc_conn.c | StartWifiConnectService |
| 断线自动重连 | svc_conn_action.c | WifiReconnectTimerCallback |
14. 总结
已配网设备回连上云的核心是 loginInfo 的持久化:
- loginInfo 落 flash,重启后云 FSM 走 BIND 分支直接登录,不依赖 RAM 中的 registerInfo。
- registerInfo 纯 RAM,只服务于首次注册,注册成功合成 loginInfo 后使命完成。
- 配网中途断电是危险点:此时 loginInfo 未生成、registerInfo 已丢失,设备会卡在云 FSM 的 INIT 状态无法上云,需要重新配网。
三个核心数据(netFlag、loginInfo、registerInfo)的持久化策略,决定了设备在"配网"与"回连"两条路径上的行为差异。理解这三者的生命周期,是排查嵌入式 IoT 设备"WiFi 连上了但上不了云"这类问题的关键。
更多推荐
所有评论(0)