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.cSecurityStoreCommit
注册信息 (registerInfo)RAM (g_regFlag)丢失`` 纯 static 变量
登录信息 (loginInfo)flash (dev_data)保留config_login_info.cConfigInfoSave
设备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, &regInfo)
    │   └─ 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: 12 (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 状态有两条路径(绑定/未绑定),分别依赖 loginInforegisterInfo

  • 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登录密钥
pskregisterInfo.psk(注册时用的)从注册信息继承
urlregisterInfo.url(注册时用的)从注册信息继承
backupUrlregisterInfo.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 分支:

CloudFsmInitHandlerNOT_BIND 分支
  → IsRegisterInfoExist() == true (g_regFlag)
  → 读取 registerInfo(从 RAM)
  → CREATE_LINKCONNECTPSKREGISTER
  → 注册成功 → loginInfo 写入 flash → 设备绑定

11.3 死亡(三种方式)

  1. 开机清除:`` 注册了 ConfigClearRegisterInfo 作为 FWK_INIT_LVL_DEP 初始化单元,每次开机执行
  2. 恢复出厂:`` 收到 IOTC_CORE_COMM_EVENT_MAIN_RESTORE 事件时清除
  3. 注册成功后清 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.cCloudFsmInitHandler
绑定状态判定config_login_info.cIsDeviceBinded
loginInfo 读取config_login_info.cConfigGetLoginInfo
loginInfo 写入config_login_info.cConfigSaveLoginInfo
loginInfo 从云端接收svc_cloud_login.cDeviceServiceRecvLoginInfo
注册响应解析→保存loginInfom2m_cloud_reg.cParseRegRespSenseInfo
registerInfo 读取config_register_info.cConfigGetRegisterInfo
registerInfo 写入(RAM)config_register_info.cConfigSaveRegisterInfo
registerInfo 存在判定config_register_info.cIsRegisterInfoExist
registerInfo 开机清除core_prof_init.cCORE_PROFILE[] "reg_info"
registerInfo 从App接收svc_cloud_setup.cDeviceServiceRecvBindingInfo
netFlag 管理svc_conn_conf.cConfigSetNetInfoFlag
netFlag 持久化svc_conn_conf.cSecurityStoreCommit
WiFi连接服务启动wifi_svc_conn.cStartWifiConnectService
断线自动重连svc_conn_action.cWifiReconnectTimerCallback

14. 总结

已配网设备回连上云的核心是 loginInfo 的持久化

  • loginInfo 落 flash,重启后云 FSM 走 BIND 分支直接登录,不依赖 RAM 中的 registerInfo。
  • registerInfo 纯 RAM,只服务于首次注册,注册成功合成 loginInfo 后使命完成。
  • 配网中途断电是危险点:此时 loginInfo 未生成、registerInfo 已丢失,设备会卡在云 FSM 的 INIT 状态无法上云,需要重新配网。

三个核心数据(netFlag、loginInfo、registerInfo)的持久化策略,决定了设备在"配网"与"回连"两条路径上的行为差异。理解这三者的生命周期,是排查嵌入式 IoT 设备"WiFi 连上了但上不了云"这类问题的关键。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐