# LoRaWAN 端数据格式与解析用户使用文档

本文档面向系统集成商、网络服务器（NS）后端运营人员，说明本设备（STM32WL 电池供电 LoRaWAN 温湿度墨水屏传感器）通过 LoRaWAN 空口上传与下发的**数据格式**及**解析方法**。

---

## 1. 基本信息

| 项目 | 值 | 说明 |
|------|-----|------|
| 频段 | CN470（中国 470MHz） | 标准 8 信道入网方案 |
| 上行端口（FPort） | 2 | 传感器业务数据 |
| 下行端口（FPort） | 3 | 仅用于设备工作模式（Class）切换 |
| 默认工作模式 | Class A | |
| 默认确认方式 | 确认帧（Confirmed） | 每帧要求网关 ACK 保证送达并即时反映断网；可通过 NFC 改为非确认帧省电（此时每 5 次上行自动发一次确认帧探针检测断网） |
| 默认上报周期 | 2 分钟 | 可通过 NFC 修改（1 分钟 ~ 24 小时） |
| 上行编码 | CayenneLPP | 与 TTN / ChirpStack / TTI 等主流平台兼容 |

> 部署提示：CN470 入网使用起始最低速率（SF12）+ 速率自适应（ADR）开启。入网后由网络服务器下发指令逐步提速，从而降低功耗、延长续航。若速率长期不收敛，设备会持续在低速高耗电状态发送，影响续航，部署后应抽检设备实际速率。

> 配置修改提示（2026-09 起）：设备经 NFC 修改配置时，只有改动**入网核心配置**（OTAA/ABP 模式、凭证来源、DevEUI/AppKey/DevAddr/会话密钥等）才会触发重新入网；仅修改上报周期、确认方式、温度单位、温湿度补偿、速率/功率等业务参数会**直接应用、不重新入网**，当前连接不断开。详见《NFC 移动端集成指南》。
>
> 会话提示：设备**不持久化 LoRaWAN 会话**（`CONTEXT_MANAGEMENT_ENABLED = 0`），**每次上电都会重新入网**（OTAA 每次重新 Join）；网络服务器需允许设备重复入网。

---

## 2. 上行数据格式（设备 → 网络服务器）

上行帧负载采用 **CayenneLPP（低功耗协议）** 编码。每一帧由若干「信道 + 类型 + 数值」的组合构成：

```
[信道 1 字节][类型 1 字节][数值 N 字节]  [信道][类型][数值] ...
```

- **信道**：逻辑编号，用来区分不同物理量
- **类型**：数据类型编号，决定数值占用几个字节
- **数值**：实际测量值，采用定点小数（整数放大后存储），大端序（高位在前）

### 2.1 设备上传的数据项

| 物理量 | 信道 | 类型编号 | 数值长度 | 还原公式 | 单位 |
|--------|------|---------|---------|----------|------|
| 温度 | 0 | 0x67 | 2 字节 | `数值 ÷ 10` | 摄氏度或华氏度（见 2.3 单位说明） |
| 相对湿度 | 1 | 0x68 | 1 字节 | `数值 ÷ 2` | 百分比 |
| 电池电量 | 2 | 0x00 | 1 字节 | `数值`（直接读取） | 百分比（1~100，100=满电） |
| 时间戳 | 3 | 0x09 | 6 字节 | 前 4 字节=Unix 秒（大端），后 2 字节=毫秒 | Unix 秒 |

> 类型编号遵循 myDevices CayenneLPP 通用标准：0x67=温度，0x68=相对湿度，0x00=数字量输入（此处用作电池电量 1~100），0x09=GPS 时间戳。
> 时间戳语义：实时帧=发送时刻；断网补传帧=缓存采集时刻（设备断网期间本地缓存、联网后补传）。设备已与网关同步过时间（LoRaWAN DeviceTimeReq）时为 Unix 秒；从未同步过的设备为相对秒（自设备 RTC 基准零点计，不具日历含义）。

### 2.2 典型帧示例（十六进制）

一帧包含四项（温度 + 湿度 + 电池 + 时间戳）：

```
00 67 00 C8   01 68 64   02 00 30   03 09 66 2A 8C E4 00 00
```

逐段解析：

| 内容 | 含义 |
|------|------|
| `00 67 00C8` | 信道 0，温度，数值 `0x00C8 = 200` → `200 ÷ 10 = 20.0 ℃` |
| `01 68 64` | 信道 1，湿度，数值 `0x64 = 100` → `100 ÷ 2 = 50.0 %` |
| `02 00 30` | 信道 2，电池，数值 `0x30 = 48` → 电量 **48%**（1~100 直接值） |
| `03 09 662A8CE4 0000` | 信道 3，GPS 时间戳，`0x662A8CE4 = 1714551012`（示例值）→ Unix 秒，毫秒 `0x0000` |

即：温度 20.0℃、湿度 50.0%、电池 48%、采集时刻 1714551012（Unix 秒）。

> 时间戳为可选解析项：不需按采集时间归档的服务器可跳过信道 3（6 字节），不影响温度/湿度/电池解析。

### 2.3 温度单位注意事项（重要）

设备上报的温度数值语义取决于设备的温度单位设置（可由手机 NFC 写入 `temp_unit` 切换，同时影响墨水屏与上行）：

- **摄氏度模式（默认）**：数值 = `摄氏度 × 10`，按摄氏度解析即可。
- **华氏度模式**：设备会先把摄氏度换算成华氏度再编码，数值 = `华氏度 × 10`。

> **网络服务器必须与设备使用相同的温度单位口径**，否则会出现系统性偏差（℃ 与 ℉ 相差约 1.8 倍）。设备当前单位可经 NFC 状态/配置读取确认（配置区 `temp_unit` 字段），建议服务器侧按设备实际单位解析，或固定将设备设置为摄氏度并强制服务器按摄氏度解析。

### 2.4 解析步骤（通用）

1. 确认收到的是 **FPort = 2** 的上行帧。
2. 从帧首开始，每 3 步循环：
   - 读 1 字节得信道号；
   - 读 1 字节得类型编号；
   - 根据类型编号读取对应长度的数值，按上表公式还原。
3. 信道 0 对应温度，信道 1 对应湿度，信道 2 对应电池电量（1 字节），信道 3 对应时间戳（6 字节，可选解析）。

### 2.5 断网缓存与补传机制

设备在断网期间把采集数据落盘到内部 Flash 环形缓存，链路恢复后再逐条补传，保证断网期间的数据不丢失（在服务器侧可按**采集时刻**归档）。

#### （1）存储（在哪、存多少、是否可靠）

- **介质**：MCU 内部 Flash 环形缓存，占用 2 个页（合计 4 KB），**最多 512 条**记录。
- **每条记录 8 字节**：时间戳（4B）+ 温度×10（2B）+ 湿度×10（2B）。
- **环形淘汰**：写满当前页后擦除「较旧页」再续写，写满后自动淘汰最旧一批（环形语义）。
- **掉电安全**：每条为**单次 8 字节双字编程（原子）**，写入过程中掉电不会破坏已存记录。
- **上电恢复**：开机扫描两页有效记录，恢复写指针与计数（只读，不写 Flash）。

#### （2）触发（什么情况开始缓存）

满足以下**任一**即判为"断网"，进入缓存：

- **未入网**（无有效会话）；或
- **已入网但连续多次确认帧未获网关 ACK**（默认连续 2 次，2026-09 起新增）——会话可能仍有效，但链路已不可达。

断网期间按**上报周期**（`tx_duty_cycle_ms`）限频写入，保证缓存密度与在线上报一致。

> **缓存节奏与「入网重试退避」解耦（2026-09-17 起）**：入网失败后的指数退避（30s→翻倍→15min）**只决定「何时再试一次 Join」**，不再决定唤醒周期。未入网/退避期间设备仍按**上报周期**唤醒采样并写缓存；只有「退避剩余 < 上报周期」时才提前醒来重试入网。因此**退避不会让离线缓存变稀疏**（修复前退避期间缓存间隔会被拉长到 4/8/16/15 分钟）。

> 未确认帧（Unconfirmed）模式下没有逐帧 ACK，设备靠**周期性确认帧探针**（每 5 次上行一次）来探测链路，从而触发/结束上述判定。

> **断网判定窗口不再有数据空洞（2026-09-17 起）**：判定断网需要连续 2 次确认帧无 ACK，而从「网关消失」到「判定成立」之间发出的实时帧，原本既送不到也不进缓存，复网补传后历史里会缺一段（Unconfirmed 模式下最长约 5 帧 ≈ 10 分钟）。现在设备把这批**在途采样**暂存在 RAM 环中：一旦确认帧 **NACK**（或判定链路不可达），立即按采集时刻补写进缓存，复网后一并补传。
>
> 代价：若这段上行其实已被网关收到（只是 ACK 丢失），该条会**重复上报一次**（帧内时间戳相同），服务器可按信道 3 时间戳去重。

#### （3）补传（链路恢复后如何回传）

仅当**链路可达**（既非未入网、也无连续无 ACK）时才补传：

| 维度 | 规则 |
|------|------|
| 顺序 | **最旧优先**，逐条上发 |
| 帧格式 | 与实时帧完全一致（FPort=2，CayenneLPP 编码）|
| 时间戳 | 该条记录的**真实采集时刻**（非发送时刻），服务器据此归档 |
| 送达判定 | 补传帧一律使用**确认帧**，**收到网关 ACK 才视为送达并推进**下一条 |
| 发送节奏 | **背靠背**：一条确认后立即发下一条，由 LoRaWAN 空口时间 + 接收窗口自然限速（通常远快于上报周期）；若被 MAC 拒绝（BUSY/占空比），按 MAC 给出的等待**快速重试**（BUSY=5s），不会被抬到一个上报周期 |
| 补传中再次断网 | **暂停补传**但**保留进度与缓存**，链路恢复后**从该条继续**（不重复已确认送达的记录）|
| 结束 | 全部缓存记录均获 ACK 送达后，**清空缓存**，恢复纯实时上报 |

> 关键点：**"推进/清缓存"以网关 ACK 为准，而非"已排入发送队列"**。因此补传帧即便暂时发不出去，也只会暂停，绝不会在未送达时把缓存清掉。

#### （4）时序示意

```
实时上报 ──断网──▶ 按周期写缓存（最多 512 条，最旧自动淘汰）
                        │
                  链路恢复（收到 ACK）
                        ▼
        逐条补传（最旧优先 · 确认帧 · ACK 才推进）
          ├─ 收到 ACK ─▶ 下一条 … ─▶ 全部送达 ─▶ 清空缓存 ─▶ 回到实时上报
          └─ 未获 ACK ─▶ 暂停补传 · 保留缓存 ─▶ 等链路恢复后重发该条
```

#### （5）服务器/集成方注意

- 补传帧与实时帧**同端口（FPort=2）、同编码**，请以**帧内时间戳**（信道 3）区分/归档"采集时刻"。
- 缓存容量上限 **512 条**：长时间断网且无网关时，超出后**最旧数据被覆盖**。
- 首次入网前（从未同步时间）时间戳为相对秒；同步过（DeviceTimeReq）后为 Unix 秒。**断网后的行为详见 §2.6**。
- **补传帧的时间戳在「补传时」换算**（缓存内只存 RTC 原生秒，不固化时区偏移）：因此「写入缓存时还没同步、补传前已同步」的记录，补传出去同样是**正确的绝对 Unix 秒**（2026-09-17 修复；修复前会固化相对秒，表现为 1970 年附近的错误时间，且可能与后续记录不单调）。
- 缓存溢出告警：写满 512 条后淘汰最旧一页时，串口打印 `DLOG: cache FULL, oldest page (256 recs) discarded, data loss`。

### 2.6 时间戳时基与断网后的行为

**时基构成**：设备时间 = **MCU RTC 日历**（LSE 驱动，电池域保持）+ **RTC 备份寄存器中的偏移量**。

- 与网关同步后，MAC 把「网关时间 − RTC 日历」的**偏移**写入 **RTC 备份寄存器**（`SysTimeSet`）；
- `SysTimeGet()` 实时返回「RTC 日历 + 偏移」，因此**只要 VBAT 不断，时间就持续正确走时**（可穿越 STOP2 与软复位，不依赖 LoRaWAN 连接）。

**同步时机**：入网成功后**第一个上行**即捎带 MAC 命令 `DeviceTimeReq`；收到 `DeviceTimeAns` 后写入偏移；之后**每 1 小时**重同步一次（校正 RTC 漂移）。需网络服务器支持 `DeviceTimeReq`（ChirpStack 需在 Device Profile 中允许 Device Time）。

> **2026-09-23 起改为「NFC 优先 / 服务器仅兜底」**：只要设备有过一次**手机 NFC 授时**，就不再请求 `DeviceTimeReq`；即便收到 `DeviceTimeAns` 也不采用（设备时钟从 NFC 基准继续走、不跳变）。服务器时间仅在"从未贴过卡"时用作兜底。详见 §5 更新记录。

> **时间同步状态机（2026-09-19 起）**：设备用三态（未同步 / 已请求待 Ans / 已同步）管理同步，只有"已同步"才认为本地时间可信。三条自愈规则：
> 1. **Ans 超时**：请求发出后 90 秒内未收到应答，即退回"未同步"，下个上行重试；
> 2. **结果校验**：同步结果须落在 2020-01-01 ~ 2100-01-01 之间，否则拒绝并继续重试（防止把错误偏移固化）；
> 3. **重试分级**：从未同步成功 → 每 60 秒重试；曾成功过或连续失败 ≥5 次 → 每 300 秒重试。
>
> 若 NS 未回复 `DeviceTimeReq`，**且设备从未有过 NFC 授时**，设备会在未同步期间持续捎带该 1 字节 MAC 命令（数据帧不受影响）；此时设备时间戳的**相对顺序与间隔仍然正确**（RTC 独立走时、速率 1:1），但绝对零点会持续偏离，需在服务器侧按设备实际偏差校正（或贴一次卡授时）。已有 NFC 授时的设备则完全不再发送该 MAC 命令。
>
> 另外：**链路不可达期间不发时间同步请求**（请求到不了网关，且会白白刷新节流计时）；链路恢复后的第一个上行立即补一次。

**断网后的时间戳（重点）**：

| 情形 | 缓存 / 补传帧的时间戳 |
|------|----------------------|
| **入网并同步过，之后断网** | **仍是绝对时间（Unix 秒）**，即**真实采集时刻**。断网期间 RTC 独立走时，时间戳继续正确递增；恢复联网后可**直接按采集时刻归档** |
| 断网期间 RTC 漂移 | 断网时无法每 1h 重同步，LSE 漂移会累积（典型 ppm 级，长时间断网可能有秒级误差），但**仍是绝对时间** |
| **从未同步过**（从未入网 / 从未收到 Ans）| 相对秒（以设备 RTC 基准零点计，不具日历含义）|
| **VBAT 断电（更换电池）后** | RTC 与备份寄存器清零 → 回到**相对时间**；下次入网收到 `DeviceTimeAns` 后重新变为绝对时间 |

> 结论：只要设备**曾经入网同步过**，断网期间缓存与后续补传的时间戳就是**可用的绝对时间**；只有「从未同步过」或「换电池后尚未再同步」的设备才需按相对时间处理。

---

## 3. 下行数据格式（网络服务器 → 设备）

本设备对下行帧的处理**仅限工作模式（Class）切换**，其余端口与内容一律忽略。

| FPort | 含义 | 负载格式 | 行为 |
|-------|------|----------|------|
| 3 | 工作模式切换 | 1 字节：`0`=Class A，`1`=Class B，`2`=Class C | 切换设备工作模式 |
| 其他 | 未定义 | — | 设备仅记录接收，不做任何业务响应 |

下行负载（FPort=3）单字节取值表：

| 字节值 | 目标工作模式 |
|--------|------------|
| 0x00 | Class A |
| 0x01 | Class B |
| 0x02 | Class C |

> 说明：除工作模式切换外，本设备**不提供其他 LoRaWAN 下行指令通道**。若需修改上报周期、温度单位、确认方式等参数，请通过 **NFC（手机贴卡）** 写入配置，而非 LoRaWAN 下行。

### 3.1 NFC 显示页面选择（非 LoRaWAN 指令，配置字段）

墨水屏支持两种显示界面，可由**手机贴卡（NFC 配置）**选择，设备上电后默认显示主界面。该控制**不走 LoRaWAN 下行**（下行端口仅用于 Class 切换），而是写进 NFC 配置结构体的 `reserved[4]` 字段（结构体绝对 **offset 115**）：

| `reserved[4]` 值 | 显示界面 | 说明 |
|------------------|----------|------|
| `0x00` | **主界面（页面 A）** | 默认界面 |
| `0x01` | **详情页（页面 B）** | 大号时间 + 温湿度组合显示 |
| `0xFF` | **不修改** | 保留设备当前显示界面，仅用于"本次只改其他配置、不动页面"的场景 |

**手机端操作（与授时/其他配置共用同一 128 字节配置包）**：

1. 构造 `NFC_Config_t`（128 字节，CRC 覆盖 offset 0~125）；
2. 将 `reserved[4]`（整个 reserved 区的第 5 字节）写为 `0x00` / `0x01` / `0xFF`；
3. 其余字段必须按当前设备配置回填（`activation_type`、`cred_source`、凭证等填实际值；不授时时 `reserved[0..3]` 填 `0`），避免误触发重新入网；
4. 写入 EEPROM，手机离开后设备应用配置并按该字段切换 `UI_SetDisplayPage()`。

> 与上行数据的关系：显示页面**不影响**任何 LoRaWAN 上行帧内容（上行仍由 §2 的 CayenneLPP 编码决定），仅改变墨水屏本地呈现。服务器侧无需、也无法通过空口读取/设置该选择。完整字段定义见《NFC 移动端集成指南》§3 主表与 `references/data-contracts.md`。

---

## 4. 对接清单（Checklist）

- [ ] 网络服务器信道计划设为 **CN470**（与设备一致，否则无法入网）
- [ ] 应用解码器监听 **FPort=2**，按第 2 节 CayenneLPP 规则解析温度 / 湿度 / 电池（可选时间戳）
- [ ] 确认设备温度单位口径（默认摄氏度，NFC 可切华氏），避免 ℃/℉ 解析偏差
- [ ] 如需按采集时间归档历史数据，解析信道 3 GPS 时间戳（补传帧为采集时刻）
- [ ] 下行仅 **FPort=3** 用于工作模式切换；配置变更走 NFC，不走 LoRaWAN
- [ ] 入网后关注速率是否收敛（是否从低速 SF12 上调），以保障续航
- [ ] 明确配置修改的入网触发规则：设备切换 OTAA/ABP 或改写凭证会**重新入网**（产生新会话，服务器需按新凭证识别）；仅改上报周期等业务参数**不重新入网**（连接不中断）
- [ ] 设备**不持久化 LoRaWAN 会话**（每次上电重新入网，OTAA 每次重新 Join），服务器需允许重复入网

---

## 5. 固件更新记录

> 仅记录对**网络服务器 / 集成方**有影响的变化；配置结构体（NFC 区，v0x03）自 2026-08 起未变。

### 2026-09-23 时间来源仲裁改为「NFC 优先 / 服务器仅兜底」

- **设备时间戳基准**：手机 NFC 授时为**优先基准**；`DeviceTimeReq` 只在设备**从未有过 NFC 授时**时才请求一次作为兜底。
- **NFC 授时写入的是本地时间**：手机写入 `reserved[0..3]` 的值是**手机当前显示的本地时间**（带时区，分量按 UTC 解释）对应的 Unix 秒；设备直接以该值为系统时间基准，**墨水屏显示与上行时间戳（信道 3）都使用这个本地时间**，设备端不做任何时区还原/换算。
- **不再被服务器时间覆盖**：已有 NFC 授时的设备即使收到 `DeviceTimeAns` 也不采用（设备时钟从 NFC 基准继续走、不跳变），避免 NS 的 GPS epoch 时间（比 UTC 快约 18 秒闰秒差）覆盖已校准的本地时间。⇒ 对集成方的含义：**不要以服务器时间推断设备时间**，设备上报的时间戳一律为手机 NFC 授时的本地时间（带时区）。
- **无格式影响**：时间戳仍是信道 3 的 Unix 秒；来源可见于 NFC 状态区 `reserved[2]`（0=无/相对秒、1=服务器、2=NFC）。
- Flash 时间锚点保存间隔由 5 分钟放宽为 15 分钟（仅影响"换电池后恢复基准"的偏早量，最多多偏 15 分钟；设备侧年擦除次数降至约 1/3）。

### 2026-09-22 上行静默修复（服务器「只收到 Join、收不到数据」）

**故障现象**：概率性出现在上电 / 入网期间（**入网过程中手机贴卡会显著提高命中率**），设备已入网，但此后一条上行都没有（设备屏上 `TX=0`）。

**根因（设备侧，与报文格式无关）**
- 入网进行中若发起一次发送，`LmHandlerSend()` 会因「未入网」**再发起一次 Join** 并返回 `NO_NETWORK_JOINED`；应用把它误判为「会话丢失」→ 置断网计数 + NFC 状态刷新标志。
- 该标志又把紧随其后的 **TX 定时器唤醒**吞成「仅刷 NFC」而跳过发送函数；而单次（ONESHOT）TX 定时器**只在发送函数末尾重装** ⇒ **发送心跳永久丢失**，设备静默。

**修复后行为（集成方可见）**
- **入网与发送严格互斥**：入网进行中不发数据帧；未入网时由固件显式发起入网（不再借道发送接口）→ 服务器侧不会再出现"反复 Join 却无上行"。
- 新增**发送心跳看门狗**：距上次服务发送超过 2×上报周期仍未按时唤醒 ⇒ 自动重装定时器并补发；正常时**无额外唤醒/空口开销**。
- 手机贴卡不再额外占用空口发一帧；同一突发内的重复发送（3 秒内）被抑制（补传背靠背不受限）。

### 2026-09-17

**行为变更（断网缓存节奏 / 补传速度，详见 §2.5）**
- **缓存节奏与入网退避解耦**：入网失败退避（30s→翻倍→15min）仅决定"何时再试一次 Join"，不再决定唤醒周期；未入网期间仍按**上报周期**采样并写缓存。修复前退避期间缓存间隔会被拉长到 4/8/16/15 分钟，现已消除（缓存密度恒等于上报周期）。
- **补传更稳更快**：补传被 MAC 拒绝（BUSY/占空比）时按 MAC 给出的短等待快速重试（BUSY=5s），不再白等一个上报周期；补传中断（NACK / 掉网）**保留进度**，恢复后从该条继续，不再从最旧重来（避免重复上传已送达记录）。
- **"已入网但网关消失"兜底自愈**：该状态下设备不会再发起 Join，若丢会话的是 NS 侧（NS 重启 / 会话过期），设备会一直"自以为已入网"、上行被丢。现增加兜底：**持续不可达 ≥ 30 分钟**（`LINK_LOST_FORCE_REJOIN_S`，置 0 可关闭）→ 主动 `LmHandlerStop()` + `LmHandlerJoin(true)` **重建 OTAA 会话**（未入网/退避中、以及 ABP 不走此路径）。

### 2026-09-17 (2) 离线数据完整性与时间戳修复

**时间戳（对归档有影响，详见 §2.5 (5)、§2.6）**
- **补传时间戳改为「补传时换算」**：缓存内统一只存 RTC 原生秒，补传时再叠加当前偏移换算成绝对 Unix 秒。修复前：设备**尚未同步时间**时写入的缓存记录会把相对秒当 Unix 秒固化，复网补传后时间戳完全错误（1970 年附近），且与后续记录可能不单调。修复后只要在补传前同步成功，补传出去即为正确绝对时间。
- **时间同步改为三态状态机（2026-09-19）**：未同步时按 60 秒重试（连续失败 5 次后降为 300 秒），并新增「Ans 超时 90 秒即重试」与「同步结果合理性校验」两道关卡；已同步后每 **1 小时**重同步。链路不可达期间不发请求，恢复后首个上行立即补一次。

**数据完整性（详见 §2.5 (2)）**
- **消除「断网判定窗口」的数据空洞**：判定断网前发出的实时帧原会两头落空（Unconfirmed 模式最长约 5 帧 ≈ 10 分钟）。现把在途采样暂存 RAM 环，确认帧 NACK 或判定不可达时立即补写进缓存，复网后补传。副作用：若该帧其实已到达（仅 ACK 丢失），会**重复上报一次**（同时间戳，可去重）。
- **缓存溢出显式告警**：写满 512 条淘汰最旧页时串口打印 `DLOG: cache FULL, oldest page (256 recs) discarded, data loss`。

### 2026-09-13

**行为变更（用户可见，详见 §2.5）**
- **断网判定扩展**：由「仅未入网」扩展为「未入网 **或** 已入网但连续多次确认帧未获网关 ACK」（默认连续 2 次）。后者期间会话可能仍有效，但设备会开始本地缓存数据。
- **补传改为以网关 ACK 为准**：补传帧强制使用确认帧，**收到网关 ACK 才推进下一条/清除缓存**；未获 ACK 则暂停补传并**保留缓存**，链路恢复后从该条重发，避免「已调度但未送达即清空」导致丢数据。
- **非确认帧探针间隔缩短**：Unconfirmed 模式下，链路探针由每 10 次上行一次改为**每 5 次**（断网检测更快）。

**稳定性修复（对数据格式与解析无影响）**
- 修复启动阶段 HardFault 复位：ThreadX 字节池缓冲未对齐，导致启动时 `STRD` 非对齐访问异常（表现为上电即死机/自动复位）。
- 加固 Flash 双字写入、LoRaWAN AES、射频缓冲的内存对齐，消除同类非对齐访问隐患。

### 2026-09
- 新增「入网核心配置 vs 非入网参数」差异化处理：仅改模式（OTAA/ABP）、凭证来源、凭证时**重新入网**；改上报周期/显示/补偿/速率等业务参数直接应用、**不重新入网**。
- 修复 AUTO 模式（凭据全 0 属合法派生默认）改配置无法触发重新入网的问题。
- 修复 OTAA→ABP 切换后首帧沿用旧帧计数器、被服务器拒收的问题（切换时复位帧计数器）。

### 2026-08
- 默认上报方式改为 **Confirmed**（每帧要求网关 ACK）；可经 NFC 切回 Unconfirmed 省电。
- 上行帧新增信道 2「电池电量」（**1~100**，100=满电）与信道 3「GPS 时间戳」；断网数据本地缓存并在联网后补传（补传帧带**采集时刻**时间戳）。
- `temp_unit` 现在**同时影响上行温度编码**（此前仅影响墨水屏显示）；`datarate`/`tx_power` 在 ADR 关闭时写入即生效。
- 电池电量改为**时间估算**（固定 600 天，与上报周期无关），不再经 ADC 电压测量。
