Compare commits

...
6 Commits
27 changed files with 660 additions and 415 deletions
+4
View File
@@ -843,6 +843,10 @@
<Option compilerVar="CC" /> <Option compilerVar="CC" />
</Unit> </Unit>
<Unit filename="apps/usr_app/app_th.h" /> <Unit filename="apps/usr_app/app_th.h" />
<Unit filename="apps/usr_app/app_th_adv.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="apps/usr_app/app_th_adv.h" />
<Unit filename="apps/usr_app/app_timer_task.c"> <Unit filename="apps/usr_app/app_timer_task.c">
<Option compilerVar="CC" /> <Option compilerVar="CC" />
</Unit> </Unit>
+27 -23
View File
@@ -7,14 +7,16 @@
| 工程 | `温湿度风扇_soc_JieLi-JL7016` | | 工程 | `温湿度风扇_soc_JieLi-JL7016` |
| 产品码 | ProdtCode=`CBR3`productCode=`PRCBR3` | | 产品码 | ProdtCode=`CBR3`productCode=`PRCBR3` |
| 协议 | 见宝 SOC 55AAV1.0.02026-09-02 重生:删条件 DP,加联动 / 运行记录 / 心跳预留) | | 协议 | 见宝 SOC 55AAV1.0.02026-09-02 重生:删条件 DP,加联动 / 运行记录 / 心跳预留) |
| 文档版本 | V3.0.0 | | 文档版本 | V3.0.1 |
| 日期 | 2026.09.02 | | 日期 | 2026.09.04 |
| 作者 | cyWu | | 作者 | cyWu |
| 读者 | WorkBuddy / 固件开发 | | 读者 | WorkBuddy / 固件开发 |
| 参考工程 | `D:\Jianbao\Project\产品\宠物\门夹弹力绳\门夹弹力绳_soc_JieLi-JL7016` | | 参考工程 | `D:\Jianbao\Project\产品\宠物\门夹弹力绳\门夹弹力绳_soc_JieLi-JL7016` |
| 新协议生成工程 | `D:\Jianbao\Project\产品\爬宠\温湿度风扇\温湿度风扇_soc_JieLi-JL7016_新协议` | | 新协议生成工程 | `D:\Jianbao\Project\产品\爬宠\温湿度风扇\温湿度风扇_soc_JieLi-JL7016_新协议` |
> **V3.0.0 变更(2026-09-02):** 协议 DP 重生。删除 `cond_en` / `cond_config1~7`7 组条件引擎整段作废)。新增 DP5 `linkage_delete`、DP6 `linkage_config`20B,缸体 + 温湿度 min/max)、DP21 `run_record`31 天运行秒数)、DP22 `heartbeat`(本版不做)。故障/告警/复位号后移,`factory_reset` 改为 DP27。联动按实时温湿度回差开 1 档 / 关风扇,不改 mode;仲裁改为手动 > 定时 > 联动。运行记录按风扇实际在转累加秒,满 10 分钟写 VM。生成协议 `MAX_PACKAGE_LEN` 必须改回 **256**124B 的 `run_record` 装不进 128)。旧条件 VM **不迁移** > **V3.0.1 变更(2026-09-04):** DP 号收紧为连续 0~24。`time_en` 13→7`time_config1~7` 14~20→8~14`run_record` 21→15`heartbeat` 22→16`fault_en/code` 23/24→17/18`alarm_en/code` 25/26→19/20`factory_reset` 27→21`reserva_dp1~3` 28~30→22~24。712 不再空置。以 `jb_protocol.h` / `dp_definition.json` 为准
>
> **V3.0.0 变更(2026-09-02):** 协议 DP 重生。删除 `cond_en` / `cond_config1~7`7 组条件引擎整段作废)。新增 DP5 `linkage_delete`、DP6 `linkage_config`20B,缸体 + 温湿度 min/max)、当时号为 DP21 `run_record`(现 DP15)、DP22 `heartbeat`(现 DP16,本版不做)。故障/告警/复位号后移,当时 `factory_reset` 为 DP27(现 DP21)。联动按实时温湿度回差开 1 档 / 关风扇,不改 mode;仲裁改为手动 > 定时 > 联动。运行记录按风扇实际在转累加秒,满 10 分钟写 VM。生成协议 `MAX_PACKAGE_LEN` 必须改回 **256**124B 的 `run_record` 装不进 128)。旧条件 VM **不迁移**。
> >
> **V2.2.0 变更(2026-08-31):** 顺着 V2.1.0 删 `app_power` 的思路,继续清掉同一类"保留但无调用者"的死代码:删除 `app_function_soft_off()` / `app_function_is_power_on()` / `app_function_power_on_silent()``gDevData.power` 恒为 1、写死在 `app_function_init()`(不再需要 `s_func.app_power` 状态字段);`usr_on_key_short()` 里对应的"软关态唤醒"死分支一并删除。 > **V2.2.0 变更(2026-08-31):** 顺着 V2.1.0 删 `app_power` 的思路,继续清掉同一类"保留但无调用者"的死代码:删除 `app_function_soft_off()` / `app_function_is_power_on()` / `app_function_power_on_silent()``gDevData.power` 恒为 1、写死在 `app_function_init()`(不再需要 `s_func.app_power` 状态字段);`usr_on_key_short()` 里对应的"软关态唤醒"死分支一并删除。
> >
@@ -32,7 +34,7 @@
## 0. WorkBuddy 开干前必读(硬约束) ## 0. WorkBuddy 开干前必读(硬约束)
1. **先改协议再写业务。**`_新协议` 工程拷贝已生成的 `jb_protocol.*` / `dp_definition.json` / `jb_product` 骨架,再把现有 `jb_product.c` 业务回调迁过去(禁止用空壳 TODO 覆盖业务)。拷贝后立刻把 `MAX_PACKAGE_LEN` 从生成值 **128 改回 256**,否则 DP21 `run_record`124B)组不出完整帧。 1. **先改协议再写业务。**`_新协议` 工程拷贝已生成的 `jb_protocol.*` / `dp_definition.json` / `jb_product` 骨架,再把现有 `jb_product.c` 业务回调迁过去(禁止用空壳 TODO 覆盖业务)。拷贝后立刻把 `MAX_PACKAGE_LEN` 从生成值 **128 改回 256**,否则 DP15 `run_record`124B)组不出完整帧。
2. **不要把业务算法写进 `jb_product.c`。** 只把 DP 写入 `gDevData` 并转调 `app_*`。组合根是 `usr_jb_main.c`(照门夹)。 2. **不要把业务算法写进 `jb_product.c`。** 只把 DP 写入 `gDevData` 并转调 `app_*`。组合根是 `usr_jb_main.c`(照门夹)。
3. **不要抄门夹的电池 / 低电拒绝开机。** 本机 USB 常电、无电池。**上电即开机**(V2.0.0 起,不再长按 3s 才开机);拔电即关,无软关机深睡。 3. **不要抄门夹的电池 / 低电拒绝开机。** 本机 USB 常电、无电池。**上电即开机**(V2.0.0 起,不再长按 3s 才开机);拔电即关,无软关机深睡。
4. **不要把调试 UART 放在 PC5。** 调试 UART TX = **PG6**`IO_PORTG_06`)。PC5 是 AHT20 SDA(独立使用)。 4. **不要把调试 UART 放在 PC5。** 调试 UART TX = **PG6**`IO_PORTG_06`)。PC5 是 AHT20 SDA(独立使用)。
@@ -42,7 +44,7 @@
8. 日志全部 **英文**。中文只出现在注释和本文。 8. 日志全部 **英文**。中文只出现在注释和本文。
9. 编码风格对齐门夹 + 工程师规范:文件小写+下划线,`g_` / `s_` 前缀,枚举 `Pascal_t`,接口 `xxx_init` / `xxx_run` 或 tick / `xxx_get_status` 9. 编码风格对齐门夹 + 工程师规范:文件小写+下划线,`g_` / `s_` 前缀,枚举 `Pascal_t`,接口 `xxx_init` / `xxx_run` 或 tick / `xxx_get_status`
10. **禁止继续实现 7 组条件引擎。** `app_cond` 整模块替换为联动(`app_linkage`);旧 `CFG_USER_CBR3_COND` 作废,不读旧 49B。 10. **禁止继续实现 7 组条件引擎。** `app_cond` 整模块替换为联动(`app_linkage`);旧 `CFG_USER_CBR3_COND` 作废,不读旧 49B。
11. **DP22 心跳本版忽略。** 温湿度仍按协议 2s 变化防抖 / 10min 全量上报,不因 12s 无心跳停报。 11. **DP16 心跳本版忽略。** 温湿度仍按协议 2s 变化防抖 / 10min 全量上报,不因 12s 无心跳停报。
--- ---
@@ -511,7 +513,7 @@ usr_rtc_get_time() 失败 → 全部定时不执行(等同 time_en=0)
- APP 改联动/定时、单次定时自毁、档位/模式稳定变化:500ms 防抖(避免短按循环打爆 Flash) - APP 改联动/定时、单次定时自毁、档位/模式稳定变化:500ms 防抖(避免短按循环打爆 Flash)
- 运行记录:风扇累计转满 **10 分钟** 写一次;**跨天旋转时立刻写**(见 13.1) - 运行记录:风扇累计转满 **10 分钟** 写一次;**跨天旋转时立刻写**(见 13.1)
### 13.1 运行记录(DP21 `run_record`RO124B ### 13.1 运行记录(DP15 `run_record`RO124B
31 天 × 4 字节,每格 uint32 **大端**,单位秒: 31 天 × 4 字节,每格 uint32 **大端**,单位秒:
@@ -530,13 +532,15 @@ usr_rtc_get_time() 失败 → 全部定时不执行(等同 time_en=0)
- 对时成功:按本地日历把数组左移 N 天(跨了几天就移几天),新今天从 0 或当天已累加值继续。 - 对时成功:按本地日历把数组左移 N 天(跨了几天就移几天),新今天从 0 或当天已累加值继续。
- 掉电再上电:先把 VM 里 31 格灌进 `gDevData.run_record`;未对时继续往 slot[0] 加。 - 掉电再上电:先把 VM 里 31 格灌进 `gDevData.run_record`;未对时继续往 slot[0] 加。
**上报:** 今天累计秒数相对上次 0x07 上报满 **60 秒** 才进差分(`JB_RUNREC_DIFF_SEC`);跨天旋转立刻带 DP15。APP 0x05 读、10 分钟全量仍带完整 124B。其它 DP 上报时不刷新运行记录基准。
**落盘:** 每累计运行满 600 秒写一次 VM。跨天立刻写。拔电没有软关机钩子,**接受最多丢不满 10 分钟的秒数**。 **落盘:** 每累计运行满 600 秒写一次 VM。跨天立刻写。拔电没有软关机钩子,**接受最多丢不满 10 分钟的秒数**。
`BOARD_RUNREC_ENABLE=0`:不累加、DP21 报全 0。 `BOARD_RUNREC_ENABLE=0`:不累加、DP15 报全 0。
--- ---
## 14. 出厂重置(DP27 写 1 ## 14. 出厂重置(DP21 写 1
- **清全部业务 VM**(第 13 章列表,含联动 20B 与 31 天运行记录)。 - **清全部业务 VM**(第 13 章列表,含联动 20B 与 31 天运行记录)。
- **保留 BLE 配对,不断开、不软复位。** - **保留 BLE 配对,不断开、不软复位。**
@@ -547,7 +551,7 @@ usr_rtc_get_time() 失败 → 全部定时不执行(等同 time_en=0)
## 15. BLE / DP 行为摘要 ## 15. BLE / DP 行为摘要
身份:`CBR3` / `PRCBR3`。帧格式见已生成 `jb_protocol.h`(以 `_新协议` 拷贝后、`MAX_PACKAGE_LEN=256` 的头文件为准)。DP 号 712 空置,不要填回条件配置。 身份:`CBR3` / `PRCBR3`。帧格式见已生成 `jb_protocol.h`(以 `_新协议` 拷贝后、`MAX_PACKAGE_LEN=256` 的头文件为准)。DP 号 024 连续,不要填回条件配置。
| DP | 处理 | | DP | 处理 |
|---|---| |---|---|
@@ -557,16 +561,16 @@ usr_rtc_get_time() 失败 → 全部定时不执行(等同 time_en=0)
| 3/4 温湿度 | 仅上报。变化防抖 2s,全量 10min(协议已有) | | 3/4 温湿度 | 仅上报。变化防抖 2s,全量 10min(协议已有) |
| 5 linkage_delete | 第 10.4 节,脉冲 | | 5 linkage_delete | 第 10.4 节,脉冲 |
| 6 linkage_config | 20 字节 memcpy + 存 VM + 第 10.5 节立刻重判 | | 6 linkage_config | 20 字节 memcpy + 存 VM + 第 10.5 节立刻重判 |
| 13 time_en | 见第 11 章 | | 7 time_en | 见第 11 章 |
| 14~20 time_config | 8 字节 + 存 VM | | 8~14 time_config | 8 字节 + 存 VM |
| 21 run_record | RO,由 `app_runrec` 填 124B,禁止 APP 写 | | 15 run_record | RO,由 `app_runrec` 填 124B,禁止 APP 写。差分满 1 分钟或跨天才 0x07 |
| 22 heartbeat | 本版忽略(写了也不停温湿度上报) | | 16 heartbeat | 本版忽略(写了也不停温湿度上报) |
| 23 fault_en | 原样存,P0 不做位级过滤 | | 17 fault_en | 原样存,P0 不做位级过滤 |
| 24 fault_code | 0 无 / 1 传感器故障 / 2 预留 | | 18 fault_code | 0 无 / 1 传感器故障 / 2 预留 |
| 25 alarm_en | 原样存 | | 19 alarm_en | 原样存 |
| 26 alarm_code | 保持 0 | | 20 alarm_code | 保持 0 |
| 27 factory_reset | 第 14 章 | | 21 factory_reset | 第 14 章 |
| 28~30 | 存 RAM,可不实现动作 | | 22~24 reserva | 存 RAM,可不实现动作 |
OTA:运行态按住 12s 走门夹 `usr_app_ota_init`(在 `app_function_loop` 任务上下文调用,灯效 200ms 闪)。`jbOnOtaNotify` 等协议 OTA **P2 可继续 stub**,注释写明未实现,禁止假装升级成功后擦用户区。 OTA:运行态按住 12s 走门夹 `usr_app_ota_init`(在 `app_function_loop` 任务上下文调用,灯效 200ms 闪)。`jbOnOtaNotify` 等协议 OTA **P2 可继续 stub**,注释写明未实现,禁止假装升级成功后擦用户区。
@@ -635,7 +639,7 @@ apps/usr_app/
- [ ] DP0 写 0 风扇不变、设备不关 - [ ] DP0 写 0 风扇不变、设备不关
- [ ] VM:改档/改模式/写下联动与定时后断电,重新上电从周期头恢复 mode - [ ] VM:改档/改模式/写下联动与定时后断电,重新上电从周期头恢复 mode
- [ ] 定时引擎可只存不跑(`BOARD_TIMER_ENABLE=0` 仍能保存 DP - [ ] 定时引擎可只存不跑(`BOARD_TIMER_ENABLE=0` 仍能保存 DP
- [ ] 新协议 DP 号:读不到旧的 cond_config;能读写 DP6 20B、能读 DP21 - [ ] 新协议 DP 号:读不到旧的 cond_config;能读写 DP6 20B、能读 DP15
### P2 — 自动化 ### P2 — 自动化
@@ -717,7 +721,7 @@ apps/usr_app/
| 风险 | 要求 | | 风险 | 要求 |
|---|---| |---|---|
| JSON 对 DP6 只写了「温度 int8、湿度/版本 uint8」 | 以本文第 10.2 节 **先 min 后 max** 为准,不要猜成单阈值 | | JSON 对 DP6 只写了「温度 int8、湿度/版本 uint8」 | 以本文第 10.2 节 **先 min 后 max** 为准,不要猜成单阈值 |
| 生成协议 `MAX_PACKAGE_LEN=128` | **必须改 256**,否则 DP21 组帧失败 | | 生成协议 `MAX_PACKAGE_LEN=128` | **必须改 256**,否则 DP15 组帧失败 |
| 旧条件 49B VM | 不迁移、不解析;当无联动 | | 旧条件 49B VM | 不迁移、不解析;当无联动 |
| CT1642 鬼影/异显微亮 | IO 独立不解决:单锁存 + 移位实时输出的固有特性,软件靠"值不变不刷新"/提速分时压;残余微亮需 Q2-Q9 加 1~10k 硬件下拉 | | CT1642 鬼影/异显微亮 | IO 独立不解决:单锁存 + 移位实时输出的固有特性,软件靠"值不变不刷新"/提速分时压;残余微亮需 Q2-Q9 加 1~10k 硬件下拉 |
| 10k 上拉偏弱 | 软件 I2C 放慢;不行再提硬件改 4.7k(SDA 建议外部 4.7k | | 10k 上拉偏弱 | 软件 I2C 放慢;不行再提硬件改 4.7k(SDA 建议外部 4.7k |
@@ -735,9 +739,9 @@ apps/usr_app/
1.`_新协议` 拷贝 `jb_protocol.*` / `dp_definition.json``MAX_PACKAGE_LEN` 改 256;把现有 `jb_product.c` 业务迁到新 DP 号(不要用空壳覆盖)。 1.`_新协议` 拷贝 `jb_protocol.*` / `dp_definition.json``MAX_PACKAGE_LEN` 改 256;把现有 `jb_product.c` 业务迁到新 DP 号(不要用空壳覆盖)。
2. `app_nvm`:丢掉 cond 49B,改为 linkage 20B + run_record;升版本。 2. `app_nvm`:丢掉 cond 49B,改为 linkage 20B + run_record;升版本。
3. 新建 `app_linkage`(替换 `app_cond`),仲裁 owner 改为 LINKAGE。 3. 新建 `app_linkage`(替换 `app_cond`),仲裁 owner 改为 LINKAGE。
4. 新建 `app_runrec`:20ms 累加、10 分钟脏、对时旋转、灌 DP21。 4. 新建 `app_runrec`:20ms 累加、10 分钟脏、对时旋转、灌 DP15
5. `app_disp` 闪烁改 8.2(出 [min,max] 就闪)。 5. `app_disp` 闪烁改 8.2(出 [min,max] 就闪)。
6. `jbEventProcess`:DP5 脉冲删除、DP6 重写重判、DP22 忽略、DP27 出厂重置。 6. `jbEventProcess`:DP5 脉冲删除、DP6 重写重判、DP16 忽略、DP21 出厂重置。
7. 按第 17 章 P2 用例逐条验。 7. 按第 17 章 P2 用例逐条验。
每一步都保持对应 `BOARD_xxx_ENABLE` 可关。 每一步都保持对应 `BOARD_xxx_ENABLE` 可关。
+1
View File
@@ -468,6 +468,7 @@ c_SRC_FILES := \
apps/usr_app/app_fan.c \ apps/usr_app/app_fan.c \
apps/usr_app/app_disp.c \ apps/usr_app/app_disp.c \
apps/usr_app/app_th.c \ apps/usr_app/app_th.c \
apps/usr_app/app_th_adv.c \
apps/usr_app/app_linkage.c \ apps/usr_app/app_linkage.c \
apps/usr_app/app_runrec.c \ apps/usr_app/app_runrec.c \
apps/usr_app/app_timer_task.c \ apps/usr_app/app_timer_task.c \
+2 -2
View File
@@ -4,7 +4,7 @@
* @author cyWu <1917507415@qq.com> * @author cyWu <1917507415@qq.com>
* @date 2026.09.02 * @date 2026.09.02
* @version V3.0.0(规格书 V3.0.0):app_cond → app_linkage/app_runrec * @version V3.0.0(规格书 V3.0.0):app_cond → app_linkage/app_runrec
* 出厂重置改清联动 20B + 31 天运行记录(DP27,规格书第 14 章) * 出厂重置改清联动 20B + 31 天运行记录(DP21,规格书第 14 章)
******************************************************************************/ ******************************************************************************/
#include "system/includes.h" /* 必须先于 app_main.hu8/u16/u32 等类型由 SDK 定义 */ #include "system/includes.h" /* 必须先于 app_main.hu8/u16/u32 等类型由 SDK 定义 */
@@ -81,7 +81,7 @@ void app_function_poll(void)
{ {
gDevData.fan_gear = app_fan_get_gear(); /* DP2 必须等于当前真实档位 */ gDevData.fan_gear = app_fan_get_gear(); /* DP2 必须等于当前真实档位 */
gDevData.mode = app_mode_get_active(); gDevData.mode = app_mode_get_active();
/* DP24 fault_code:传感器连续失败报 1;2 预留,温湿度出范围只走显示闪烁不上报(8.2) */ /* DP18 fault_code:传感器连续失败报 1;2 预留,温湿度出范围只走显示闪烁不上报(8.2) */
gDevData.fault_code = app_th_is_fault() ? 1 : 0; gDevData.fault_code = app_th_is_fault() ? 1 : 0;
} }
+1 -1
View File
@@ -4,7 +4,7 @@
* @author cyWu <1917507415@qq.com> * @author cyWu <1917507415@qq.com>
* @date 2026.09.02 * @date 2026.09.02
* @version V3.0.0(规格书 V3.0.0):app_cond → app_linkage/app_runrec * @version V3.0.0(规格书 V3.0.0):app_cond → app_linkage/app_runrec
* 出厂重置改 DP27、清联动 20B 与 31 天运行记录 * 出厂重置改 DP21、清联动 20B 与 31 天运行记录
* *
* @note 本模块只做"按键 → 动作"和"状态 → DP",不写业务算法。 * @note 本模块只做"按键 → 动作"和"状态 → DP",不写业务算法。
* 档位/模式/联动/运行记录的执行在各自 app_* 模块(规格书 0 章第 2 条)。 * 档位/模式/联动/运行记录的执行在各自 app_* 模块(规格书 0 章第 2 条)。
+3 -3
View File
@@ -67,9 +67,9 @@ typedef struct {
uint8_t ver; /**< CBR3_VM_VER */ uint8_t ver; /**< CBR3_VM_VER */
uint8_t mode; /**< 逻辑模式 0/1/2 */ uint8_t mode; /**< 逻辑模式 0/1/2 */
uint8_t fan_gear; /**< 真实档位 0~3(模式未激活时即手动档) */ uint8_t fan_gear; /**< 真实档位 0~3(模式未激活时即手动档) */
uint8_t time_en; /**< DP13 定时总开关 */ uint8_t time_en; /**< DP7 定时总开关 */
uint8_t fault_en; /**< DP23 */ uint8_t fault_en; /**< DP17 */
uint8_t alarm_en; /**< DP25 */ uint8_t alarm_en; /**< DP19 */
uint8_t rsv[2]; /**< 对齐到 10B(原 cond_en 位置作废) */ uint8_t rsv[2]; /**< 对齐到 10B(原 cond_en 位置作废) */
} __packed Cbr3StateVm_t; } __packed Cbr3StateVm_t;
+1 -1
View File
@@ -75,7 +75,7 @@ void app_nvm_mark_dirty(void);
/** /**
* @brief 出厂重置:清全部业务 VM(保留 BLE 配对,规格书第 14 章) * @brief 出厂重置:清全部业务 VM(保留 BLE 配对,规格书第 14 章)
* @note 由 jb_product DP27 调用;RAM 复位由调用方完成 * @note 由 jb_product DP21 调用;RAM 复位由调用方完成
* @return 无 * @return 无
*/ */
void app_nvm_reset(void); void app_nvm_reset(void);
+4 -4
View File
@@ -12,9 +12,9 @@
* - app_runrec_tick20ms 中断):只把在转的毫秒折成 pending 秒, * - app_runrec_tick20ms 中断):只把在转的毫秒折成 pending 秒,
* **不碰 s_rr.rec[]、不打日志、不写 flash**(跨天旋转在任务侧)。 * **不碰 s_rr.rec[]、不打日志、不写 flash**(跨天旋转在任务侧)。
* - app_runrec_rotate_if_needed500ms 任务):收下 pending 加到 * - app_runrec_rotate_if_needed500ms 任务):收下 pending 加到
* slot[0]、每拍灌 DP21、跨天旋转、满 10 分钟 / 跨天才写 VM。 * slot[0]、每拍灌 DP15、跨天旋转、满 10 分钟 / 跨天才写 VM。
* DP21 每 500ms 更新到 gDevData,供读请求和 10min 全量上报 * DP15 每 500ms 更新到 gDevData0x05 读立刻拿到当前秒
* 差分变化上报不带 DP21(避免每秒推 124B) * 差分满 1 分钟才 0x0710min 全量仍带
* 拔电没有软关机钩子,接受最多丢不满 10 分钟的秒数(13.1 已拍板)。 * 拔电没有软关机钩子,接受最多丢不满 10 分钟的秒数(13.1 已拍板)。
******************************************************************************/ ******************************************************************************/
@@ -197,7 +197,7 @@ void app_runrec_rotate_if_needed(void)
} }
} }
/* 每拍灌 DP21:读请求 / 10min 全量能拿到当前秒数;差分上报不带本 DP */ /* 每拍灌 DP15:0x05 读立刻拿到当前秒;差分满 1 分钟才 0x07;10min 全量仍带 */
app_runrec_pack(gDevData.run_record); app_runrec_pack(gDevData.run_record);
if (need_vm) { if (need_vm) {
app_nvm_save_runrec(); /* 满 10 分钟或跨天 / 首次对时才写 flash */ app_nvm_save_runrec(); /* 满 10 分钟或跨天 / 首次对时才写 flash */
+2 -2
View File
@@ -13,8 +13,8 @@
* N 天;掉电上电先把 VM 的 31 格灌回 RAM。 * N 天;掉电上电先把 VM 的 31 格灌回 RAM。
* 落盘:每累计运行满 BOARD_RUN_RECORD_SAVE_SEC(600s) 写一次 VM * 落盘:每累计运行满 BOARD_RUN_RECORD_SAVE_SEC(600s) 写一次 VM
* 跨天旋转**立刻写**。拔电无软关机钩子,接受丢不满 10 分钟的秒数。 * 跨天旋转**立刻写**。拔电无软关机钩子,接受丢不满 10 分钟的秒数。
* DP21:任务上下文每 500ms 灌进 gDevData读请求 / 10min 全量用) * DP15:任务上下文每 500ms 灌进 gDevData0x05 读立刻拿到当前秒
* 差分变化上报不带本 DP,避免每秒推 124B * 差分满 1 分钟才 0x07;10min 全量仍带)
* *
* ⚠ 上下文分工(规格书第 0/13 章,2026-08-31 事故定论): * ⚠ 上下文分工(规格书第 0/13 章,2026-08-31 事故定论):
* - app_runrec_tick() = 20ms **中断上下文**:只折 pending 秒, * - app_runrec_tick() = 20ms **中断上下文**:只折 pending 秒,
+3 -3
View File
@@ -6,7 +6,7 @@
* @version V1.3.0,对外温湿度与显示/上报绑死(T>=0.5℃ / H>=2% * @version V1.3.0,对外温湿度与显示/上报绑死(T>=0.5℃ / H>=2%
* *
* @note 规格书 8.4:联动、数码管、DP3/DP4 都用对外温湿度;采样失败保持 * @note 规格书 8.4:联动、数码管、DP3/DP4 都用对外温湿度;采样失败保持
* 上一对外值继续判,直到连续 3 次失败显示 "--"DP24 * 上一对外值继续判,直到连续 3 次失败显示 "--"DP18
* fault_code=1)。对外值首次跟上,之后 * fault_code=1)。对外值首次跟上,之后
* |ΔT|>=BOARD_TH_TEMP_PUB_DELTA_X10、|ΔH|>=BOARD_TH_HUMI_PUB_DELTA * |ΔT|>=BOARD_TH_TEMP_PUB_DELTA_X10、|ΔH|>=BOARD_TH_HUMI_PUB_DELTA
* 才改,避免小数抖动导致显示、APP、联动各跳。 * 才改,避免小数抖动导致显示、APP、联动各跳。
@@ -67,10 +67,10 @@ uint8_t app_th_is_fault(void);
uint8_t app_th_last_sample_ok(void); uint8_t app_th_last_sample_ok(void);
/** /**
* @brief 取走一次「本周期采样成功」事件(供 usr_le_adv 的 500ms 任务上下文) * @brief 取走一次「本周期采样成功」事件(供 app_th_adv 的 500ms 任务上下文)
* @note ⚠ 生产者是 app_th_poll(20ms 硬件定时器中断上下文),那里只置 * @note ⚠ 生产者是 app_th_poll(20ms 硬件定时器中断上下文),那里只置
* volatile pending,不开关广播、不打日志、不写 flash。 * volatile pending,不开关广播、不打日志、不写 flash。
* 消费者(usr_ble_adv_updataBLE 线程任务上下文)调本函数取走, * 消费者(app_th_adv_tick,由 usr_ble_adv_updata 调用)调本函数取走,
* 内部 local_irq_disable 保护,取到即清。 * 内部 local_irq_disable 保护,取到即清。
* 只在真正采样成功(last_ok 那次)时产生,失败/fault 不产生, * 只在真正采样成功(last_ok 那次)时产生,失败/fault 不产生,
* 温度、湿度都是对外值(与数码管/DP3/DP4 一致)。 * 温度、湿度都是对外值(与数码管/DP3/DP4 一致)。
+206
View File
@@ -0,0 +1,206 @@
/******************************************************************************
* @file app_th_adv.c
* @brief CBR3 自定义温湿度广播窗实现
* @author cyWu <1917507415@qq.com>
* @date 2026.09.03
* @version V1.0.0
* @history
* - V1.0.0, 2026.09.03, cyWu, 从 usr_le_adv.c 拆出
******************************************************************************/
#include "app_th_adv.h"
#include "app_th.h"
#include "app_linkage.h"
#include "board_pin.h"
#include "usr_le_api.h"
#include "system/includes.h"
#include "app_main.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[TH_ADV]"
#define LOG_INFO_ENABLE
#include "debug.h"
extern void usr_set_adv_interval(u16 val);
extern u16 usr_get_adv_interval(void);
#define TH_ADV_DATA_MAX 31
#define TH_ADV_WINDOW_TICKS 2 /**< 500ms × 2 = 1s */
#define TH_ADV_FAST_INTERVAL 80 /**< 窗内快广播,暂停 60s 变慢 */
#define TH_ADV_TYPE_FLAGS 0x01
#define TH_ADV_TYPE_LOCAL_NAME 0x09
#define TH_ADV_TYPE_MANUFACTURER 0xFF
typedef struct {
uint8_t ticks; /**< >0 = 窗进行中,剩余 500ms 拍数 */
uint16_t interval; /**< 进窗前提速前的间隔;0=进窗前已是快广播 */
} AppThAdvCtx_t;
static AppThAdvCtx_t s_th_adv;
/**
* @brief 配对 / OTA / 已连接 / 未配网 → 不允许发温湿度窗
* @return 1=阻塞,0=允许
*/
static uint8_t th_adv_blocked(void)
{
if (usr_ble_is_connected()) {
return 1;
}
if (usr_var.usr_goto_pair || usr_var.usr_goto_updata ||
usr_var.usr_ota_succ_flag) {
return 1;
}
if (usr_get_pair_flag() == 0) {
return 1;
}
return 0;
}
/**
* @brief 组自定义温湿度 ADFlags + Complete Local Name + Manufacturer 0xFFFF
* @param tank 缸体编号(非 0)
* @param ver 配置版本
* @param temp_x10 对外温度 ×10
* @param humi 对外湿度 %
* @return 无
*/
static void th_adv_encode(uint16_t tank, uint8_t ver, int16_t temp_x10, uint8_t humi)
{
uint8_t buf[TH_ADV_DATA_MAX];
uint8_t *p = buf;
char dec[5];
uint8_t digits = 0;
uint16_t v = tank;
int32_t t_int;
uint8_t name_len;
*p++ = 0x02;
*p++ = TH_ADV_TYPE_FLAGS;
*p++ = 0x06;
while (v) {
dec[digits++] = (char)('0' + (v % 10));
v /= 10;
}
name_len = (uint8_t)(5 + digits); /* "JBao-" + tank 位数 */
*p++ = (uint8_t)(1 + name_len);
*p++ = TH_ADV_TYPE_LOCAL_NAME;
*p++ = 'J';
*p++ = 'B';
*p++ = 'a';
*p++ = 'o';
*p++ = '-';
while (digits) {
*p++ = (uint8_t)dec[--digits];
}
t_int = temp_x10 / 10;
if (t_int < -128) {
t_int = -128;
}
if (t_int > 127) {
t_int = 127;
}
if (humi > 100) {
humi = 100;
}
*p++ = 0x06;
*p++ = TH_ADV_TYPE_MANUFACTURER;
*p++ = 0xFF;
*p++ = 0xFF;
*p++ = ver;
*p++ = (uint8_t)t_int;
*p++ = humi;
usr_ble_adv_set_raw(buf, (uint8_t)(p - buf));
}
/**
* @brief 结束温湿度窗:还原广播间隔,可选切回见宝回连
* @param back_to_reconn 1=组回连包(窗自然到期);0=只清窗
* @return 无
*/
static void th_adv_stop(uint8_t back_to_reconn)
{
if (s_th_adv.ticks == 0) {
return;
}
s_th_adv.ticks = 0;
if (s_th_adv.interval != 0) {
usr_set_adv_interval(s_th_adv.interval);
s_th_adv.interval = 0;
}
if (back_to_reconn) {
usr_ble_adv_encode_reconn();
log_info("th adv win end, back to reconn\n");
}
}
/**
* @brief 丢掉温湿度窗(不组回连包)
* @return 无
*/
void app_th_adv_reset(void)
{
s_th_adv.ticks = 0;
s_th_adv.interval = 0;
}
/**
* @brief 温湿度窗是否占用广播
* @return 1=窗进行中,0=空闲
*/
uint8_t app_th_adv_is_active(void)
{
return (s_th_adv.ticks != 0) ? 1 : 0;
}
/**
* @brief 温湿度窗状态机(500ms 一拍)
* @return 无
*/
void app_th_adv_tick(void)
{
#if !BOARD_TH_ENABLE
return;
#else
int16_t t_x10 = 0;
uint8_t humi = 0;
uint16_t tank;
if (s_th_adv.ticks) {
if (th_adv_blocked()) {
th_adv_stop(0);
} else if (s_th_adv.ticks > 1) {
s_th_adv.ticks--;
} else {
/* 最后一拍必须先 stop(里头要求 ticks!=0),再组回连包 */
th_adv_stop(1);
}
}
if (th_adv_blocked()) {
return;
}
if (!app_th_take_sample(&t_x10, &humi)) {
return;
}
tank = app_linkage_get_tank_id();
if (tank == 0) {
return;
}
if (s_th_adv.ticks == 0) {
s_th_adv.interval = (usr_get_adv_interval() != TH_ADV_FAST_INTERVAL) ?
usr_get_adv_interval() : 0;
usr_set_adv_interval(TH_ADV_FAST_INTERVAL);
}
th_adv_encode(tank, app_linkage_get_cfg_ver(), t_x10, humi);
s_th_adv.ticks = TH_ADV_WINDOW_TICKS;
log_info("th adv: tank=%u ver=%u T=%dC H=%u%%\n",
tank, app_linkage_get_cfg_ver(), (int)(t_x10 / 10), humi);
#endif
}
+41
View File
@@ -0,0 +1,41 @@
/******************************************************************************
* @file app_th_adv.h
* @brief CBR3 自定义温湿度广播窗(已绑定未连接时插 1s,再切回见宝回连)
* @author cyWu <1917507415@qq.com>
* @date 2026.09.03
* @version V1.0.0
* @history
* - V1.0.0, 2026.09.03, cyWu, 从 usr_le_adv.c 拆出
*
* @note 给 nRF / 自研扫描器看,不是见宝 TLV。GATT 0x2A00 不动。
* 进窗条件:未连接 + 已配网 + 非配对 + 非 OTA + tank_id≠0
* + 本周期 AHT20 采样成功。窗长 500ms × 2。
* ⚠ 只在 usr_ble_adv_updata500ms 任务上下文)里 tick
* app_th 中断侧只置 pending,本模块是唯一消费者。
******************************************************************************/
#ifndef __APP_TH_ADV_H__
#define __APP_TH_ADV_H__
#include <stdint.h>
/**
* @brief 丢掉温湿度窗(不组回连包)
* @note usr_ble_adv_init 会自己组绑定/回连,进 init 前调本函数清窗即可
* @return 无
*/
void app_th_adv_reset(void);
/**
* @brief 温湿度窗状态机(500ms 一拍,须在写协议栈广播数据之前调用)
* @return 无
*/
void app_th_adv_tick(void);
/**
* @brief 温湿度窗是否占用广播
* @return 1=窗进行中(暂停 60s 变慢广播),0=空闲
*/
uint8_t app_th_adv_is_active(void);
#endif /* __APP_TH_ADV_H__ */
+2 -2
View File
@@ -242,7 +242,7 @@ void app_timer_run(uint16_t dt)
} }
/** /**
* @brief APP 写 DP13 定时总开关:存 gDevData + 触发 VM 持久化 * @brief APP 写 DP7 定时总开关:存 gDevData + 触发 VM 持久化
* @param en 0=关,1=开 * @param en 0=关,1=开
* @note 引擎关闭时仍保存(P1 验收);置 1 后下个 tick 补窗判断 * @note 引擎关闭时仍保存(P1 验收);置 1 后下个 tick 补窗判断
* @return 无 * @return 无
@@ -254,7 +254,7 @@ void app_timer_set_en(uint8_t en)
} }
/** /**
* @brief APP 写 DP14~20 定时配置:触发 VM 持久化(数据已在 gDevData * @brief APP 写 DP8~14 定时配置:触发 VM 持久化(数据已在 gDevData
* @param idx 1~7 * @param idx 1~7
* @note 引擎关闭时仍保存 * @note 引擎关闭时仍保存
* @return 无 * @return 无
+2 -2
View File
@@ -31,14 +31,14 @@ void app_timer_init(void);
void app_timer_run(uint16_t dt); void app_timer_run(uint16_t dt);
/** /**
* @brief APP 写 DP13 定时总开关 * @brief APP 写 DP7 定时总开关
* @param en 0=关,1=开 * @param en 0=关,1=开
* @return 无 * @return 无
*/ */
void app_timer_set_en(uint8_t en); void app_timer_set_en(uint8_t en);
/** /**
* @brief APP 写 DP14~20 定时配置(8 字节) * @brief APP 写 DP8~14 定时配置(8 字节)
* @param idx 1~7 * @param idx 1~7
* @return 无 * @return 无
*/ */
+26 -26
View File
@@ -50,30 +50,30 @@ void userInit(void)
gDevData.current_humi = 0; /* DP4 当前湿度 */ gDevData.current_humi = 0; /* DP4 当前湿度 */
gDevData.linkage_delete = 0; /* DP5 联动删除(脉冲,写 1 触发后清回 0) */ gDevData.linkage_delete = 0; /* DP5 联动删除(脉冲,写 1 触发后清回 0) */
memset(gDevData.linkage_config, 0, 20); /* DP6 联动配置(20B10.2 布局) */ memset(gDevData.linkage_config, 0, 20); /* DP6 联动配置(20B10.2 布局) */
gDevData.time_en = 0; /* DP13 定时使能 0=关 1=开 */ gDevData.time_en = 0; /* DP7 定时使能 0=关 1=开 */
memset(gDevData.time_config1, 0, 8); /* DP14 定时配置1 */ memset(gDevData.time_config1, 0, 8); /* DP8 定时配置1 */
memset(gDevData.time_config2, 0, 8); /* DP15 定时配置2 */ memset(gDevData.time_config2, 0, 8); /* DP9 定时配置2 */
memset(gDevData.time_config3, 0, 8); /* DP16 定时配置3 */ memset(gDevData.time_config3, 0, 8); /* DP10 定时配置3 */
memset(gDevData.time_config4, 0, 8); /* DP17 定时配置4 */ memset(gDevData.time_config4, 0, 8); /* DP11 定时配置4 */
memset(gDevData.time_config5, 0, 8); /* DP18 定时配置5 */ memset(gDevData.time_config5, 0, 8); /* DP12 定时配置5 */
memset(gDevData.time_config6, 0, 8); /* DP19 定时配置6 */ memset(gDevData.time_config6, 0, 8); /* DP13 定时配置6 */
memset(gDevData.time_config7, 0, 8); /* DP20 定时配置7 */ memset(gDevData.time_config7, 0, 8); /* DP14 定时配置7 */
memset(gDevData.run_record, 0, 124); /* DP21 运行记录(RO,由 app_runrec 填) */ memset(gDevData.run_record, 0, 124); /* DP15 运行记录(RO,由 app_runrec 填) */
gDevData.heartbeat = 0; /* DP22 心跳(本版忽略) */ gDevData.heartbeat = 0; /* DP16 心跳(本版忽略) */
gDevData.fault_en = 0; /* DP23 设备故障使能 */ gDevData.fault_en = 0; /* DP17 设备故障使能 */
gDevData.fault_code = 0; /* DP24 设备故障 */ gDevData.fault_code = 0; /* DP18 设备故障 */
gDevData.alarm_en = 0; /* DP25 设备告警使能 */ gDevData.alarm_en = 0; /* DP19 设备告警使能 */
gDevData.alarm_code = 0; /* DP26 设备告警(保持 0 */ gDevData.alarm_code = 0; /* DP20 设备告警(保持 0 */
gDevData.factory_reset = 0; /* DP27 恢复出厂设置(WO,写 1 触发) */ gDevData.factory_reset = 0; /* DP21 恢复出厂设置(WO,写 1 触发) */
gDevData.reserva_dp1 = 0; /* DP28 预留DP1 */ gDevData.reserva_dp1 = 0; /* DP22 预留DP1 */
gDevData.reserva_dp2 = 0; /* DP29 预留DP2 */ gDevData.reserva_dp2 = 0; /* DP23 预留DP2 */
gDevData.reserva_dp3 = 0; /* DP30 预留DP3 */ gDevData.reserva_dp3 = 0; /* DP24 预留DP3 */
} }
/* ════════════════════════════════════════════════════════════════ /* ════════════════════════════════════════════════════════════════
* userHandle — 用户数据采集(500ms 由 usr_timer_loop 调用) * userHandle — 用户数据采集(500ms 由 usr_timer_loop 调用)
* 只填温湿度上报 DP;档位/模式/fault 由 app_function_poll20ms)同步; * 只填温湿度上报 DP;档位/模式/fault 由 app_function_poll20ms)同步;
* DP21 运行记录由 app_runrec 在任务上下文灌 * DP15 运行记录由 app_runrec 在任务上下文灌
* ════════════════════════════════════════════════════════════════ */ * ════════════════════════════════════════════════════════════════ */
void userHandle(void) void userHandle(void)
{ {
@@ -135,13 +135,13 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
app_linkage_set_config(); app_linkage_set_config();
break; break;
/* ── DP13 定时使能 ── */ /* ── DP7 定时使能 ── */
case DP_ID_TIME_EN: case DP_ID_TIME_EN:
gDevData.time_en = ctrl->time_en ? 1 : 0; gDevData.time_en = ctrl->time_en ? 1 : 0;
app_timer_set_en(gDevData.time_en); app_timer_set_en(gDevData.time_en);
break; break;
/* ── DP14~20 定时配置(8 字节)── */ /* ── DP8~14 定时配置(8 字节)── */
case DP_ID_TIME_CONFIG1: case DP_ID_TIME_CONFIG1:
memcpy(gDevData.time_config1, ctrl->time_config1, 8); memcpy(gDevData.time_config1, ctrl->time_config1, 8);
app_timer_set_config(1); app_timer_set_config(1);
@@ -171,16 +171,16 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
app_timer_set_config(7); app_timer_set_config(7);
break; break;
/* ── DP21 运行记录:RO,禁止 APP 写(规格书第 15 章)── */ /* ── DP15 运行记录:RO,禁止 APP 写(规格书第 15 章)── */
case DP_ID_RUN_RECORD: case DP_ID_RUN_RECORD:
break; break;
/* ── DP22 心跳:本版忽略(规格书 0 章第 11 条 / 15 章)。 /* ── DP16 心跳:本版忽略(规格书 0 章第 11 条 / 15 章)。
* 写了也不停温湿度上报,不做 12s 停报 ── */ * 写了也不停温湿度上报,不做 12s 停报 ── */
case DP_ID_HEARTBEAT: case DP_ID_HEARTBEAT:
break; break;
/* ── DP23/25 使能:原样存,P0 不做位级过滤 ── */ /* ── DP17/19 使能:原样存,P0 不做位级过滤 ── */
case DP_ID_FAULT_EN: case DP_ID_FAULT_EN:
gDevData.fault_en = ctrl->fault_en; gDevData.fault_en = ctrl->fault_en;
break; break;
@@ -188,7 +188,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
gDevData.alarm_en = ctrl->alarm_en; gDevData.alarm_en = ctrl->alarm_en;
break; break;
/* ── DP27 恢复出厂设置(原 DP25:清业务 VM(留配对)+ RAM 复位, /* ── DP21 恢复出厂设置:清业务 VM(留配对)+ RAM 复位,
* 不断开不软复位(规格书第 14 章)── */ * 不断开不软复位(规格书第 14 章)── */
case DP_ID_FACTORY_RESET: case DP_ID_FACTORY_RESET:
gDevData.factory_reset = ctrl->factory_reset ? 1 : 0; gDevData.factory_reset = ctrl->factory_reset ? 1 : 0;
@@ -197,7 +197,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
} }
break; break;
/* ── DP28~30 预留:存 RAM,可不实现动作 ── */ /* ── DP22~24 预留:存 RAM,可不实现动作 ── */
case DP_ID_RESERVA_DP1: case DP_ID_RESERVA_DP1:
gDevData.reserva_dp1 = ctrl->reserva_dp1; gDevData.reserva_dp1 = ctrl->reserva_dp1;
break; break;
+82 -49
View File
@@ -201,107 +201,107 @@ static uint16_t jbPackDps(const jb_data_point_t *dp,
memcpy(&out[valIdx], dp->linkage_config, 20); memcpy(&out[valIdx], dp->linkage_config, 20);
valIdx += 20; valIdx += 20;
} }
/* DP13 定时使能 (bool, 1B, RW) */ /* DP7 定时使能 (bool, 1B, RW) */
if (mask[DP_ID_TIME_EN / 8] & (1 << (DP_ID_TIME_EN % 8))) if (mask[DP_ID_TIME_EN / 8] & (1 << (DP_ID_TIME_EN % 8)))
{ {
bitmap[DP_ID_TIME_EN / 8] |= (1 << (DP_ID_TIME_EN % 8)); bitmap[DP_ID_TIME_EN / 8] |= (1 << (DP_ID_TIME_EN % 8));
out[valIdx++] = dp->time_en ? 1 : 0; out[valIdx++] = dp->time_en ? 1 : 0;
} }
/* DP14 定时配置1 (raw, 8B, RW) */ /* DP8 定时配置1 (raw, 8B, RW) */
if (mask[DP_ID_TIME_CONFIG1 / 8] & (1 << (DP_ID_TIME_CONFIG1 % 8))) if (mask[DP_ID_TIME_CONFIG1 / 8] & (1 << (DP_ID_TIME_CONFIG1 % 8)))
{ {
bitmap[DP_ID_TIME_CONFIG1 / 8] |= (1 << (DP_ID_TIME_CONFIG1 % 8)); bitmap[DP_ID_TIME_CONFIG1 / 8] |= (1 << (DP_ID_TIME_CONFIG1 % 8));
memcpy(&out[valIdx], dp->time_config1, 8); memcpy(&out[valIdx], dp->time_config1, 8);
valIdx += 8; valIdx += 8;
} }
/* DP15 定时配置2 (raw, 8B, RW) */ /* DP9 定时配置2 (raw, 8B, RW) */
if (mask[DP_ID_TIME_CONFIG2 / 8] & (1 << (DP_ID_TIME_CONFIG2 % 8))) if (mask[DP_ID_TIME_CONFIG2 / 8] & (1 << (DP_ID_TIME_CONFIG2 % 8)))
{ {
bitmap[DP_ID_TIME_CONFIG2 / 8] |= (1 << (DP_ID_TIME_CONFIG2 % 8)); bitmap[DP_ID_TIME_CONFIG2 / 8] |= (1 << (DP_ID_TIME_CONFIG2 % 8));
memcpy(&out[valIdx], dp->time_config2, 8); memcpy(&out[valIdx], dp->time_config2, 8);
valIdx += 8; valIdx += 8;
} }
/* DP16 定时配置3 (raw, 8B, RW) */ /* DP10 定时配置3 (raw, 8B, RW) */
if (mask[DP_ID_TIME_CONFIG3 / 8] & (1 << (DP_ID_TIME_CONFIG3 % 8))) if (mask[DP_ID_TIME_CONFIG3 / 8] & (1 << (DP_ID_TIME_CONFIG3 % 8)))
{ {
bitmap[DP_ID_TIME_CONFIG3 / 8] |= (1 << (DP_ID_TIME_CONFIG3 % 8)); bitmap[DP_ID_TIME_CONFIG3 / 8] |= (1 << (DP_ID_TIME_CONFIG3 % 8));
memcpy(&out[valIdx], dp->time_config3, 8); memcpy(&out[valIdx], dp->time_config3, 8);
valIdx += 8; valIdx += 8;
} }
/* DP17 定时配置4 (raw, 8B, RW) */ /* DP11 定时配置4 (raw, 8B, RW) */
if (mask[DP_ID_TIME_CONFIG4 / 8] & (1 << (DP_ID_TIME_CONFIG4 % 8))) if (mask[DP_ID_TIME_CONFIG4 / 8] & (1 << (DP_ID_TIME_CONFIG4 % 8)))
{ {
bitmap[DP_ID_TIME_CONFIG4 / 8] |= (1 << (DP_ID_TIME_CONFIG4 % 8)); bitmap[DP_ID_TIME_CONFIG4 / 8] |= (1 << (DP_ID_TIME_CONFIG4 % 8));
memcpy(&out[valIdx], dp->time_config4, 8); memcpy(&out[valIdx], dp->time_config4, 8);
valIdx += 8; valIdx += 8;
} }
/* DP18 定时配置5 (raw, 8B, RW) */ /* DP12 定时配置5 (raw, 8B, RW) */
if (mask[DP_ID_TIME_CONFIG5 / 8] & (1 << (DP_ID_TIME_CONFIG5 % 8))) if (mask[DP_ID_TIME_CONFIG5 / 8] & (1 << (DP_ID_TIME_CONFIG5 % 8)))
{ {
bitmap[DP_ID_TIME_CONFIG5 / 8] |= (1 << (DP_ID_TIME_CONFIG5 % 8)); bitmap[DP_ID_TIME_CONFIG5 / 8] |= (1 << (DP_ID_TIME_CONFIG5 % 8));
memcpy(&out[valIdx], dp->time_config5, 8); memcpy(&out[valIdx], dp->time_config5, 8);
valIdx += 8; valIdx += 8;
} }
/* DP19 定时配置6 (raw, 8B, RW) */ /* DP13 定时配置6 (raw, 8B, RW) */
if (mask[DP_ID_TIME_CONFIG6 / 8] & (1 << (DP_ID_TIME_CONFIG6 % 8))) if (mask[DP_ID_TIME_CONFIG6 / 8] & (1 << (DP_ID_TIME_CONFIG6 % 8)))
{ {
bitmap[DP_ID_TIME_CONFIG6 / 8] |= (1 << (DP_ID_TIME_CONFIG6 % 8)); bitmap[DP_ID_TIME_CONFIG6 / 8] |= (1 << (DP_ID_TIME_CONFIG6 % 8));
memcpy(&out[valIdx], dp->time_config6, 8); memcpy(&out[valIdx], dp->time_config6, 8);
valIdx += 8; valIdx += 8;
} }
/* DP20 定时配置7 (raw, 8B, RW) */ /* DP14 定时配置7 (raw, 8B, RW) */
if (mask[DP_ID_TIME_CONFIG7 / 8] & (1 << (DP_ID_TIME_CONFIG7 % 8))) if (mask[DP_ID_TIME_CONFIG7 / 8] & (1 << (DP_ID_TIME_CONFIG7 % 8)))
{ {
bitmap[DP_ID_TIME_CONFIG7 / 8] |= (1 << (DP_ID_TIME_CONFIG7 % 8)); bitmap[DP_ID_TIME_CONFIG7 / 8] |= (1 << (DP_ID_TIME_CONFIG7 % 8));
memcpy(&out[valIdx], dp->time_config7, 8); memcpy(&out[valIdx], dp->time_config7, 8);
valIdx += 8; valIdx += 8;
} }
/* DP21 运行记录 (raw, 124B, RO) */ /* DP15 运行记录 (raw, 124B, RO) */
if (mask[DP_ID_RUN_RECORD / 8] & (1 << (DP_ID_RUN_RECORD % 8))) if (mask[DP_ID_RUN_RECORD / 8] & (1 << (DP_ID_RUN_RECORD % 8)))
{ {
bitmap[DP_ID_RUN_RECORD / 8] |= (1 << (DP_ID_RUN_RECORD % 8)); bitmap[DP_ID_RUN_RECORD / 8] |= (1 << (DP_ID_RUN_RECORD % 8));
memcpy(&out[valIdx], dp->run_record, 124); memcpy(&out[valIdx], dp->run_record, 124);
valIdx += 124; valIdx += 124;
} }
/* DP23 设备故障使能 (uint8, 1B, RW) */ /* DP17 设备故障使能 (uint8, 1B, RW) */
if (mask[DP_ID_FAULT_EN / 8] & (1 << (DP_ID_FAULT_EN % 8))) if (mask[DP_ID_FAULT_EN / 8] & (1 << (DP_ID_FAULT_EN % 8)))
{ {
bitmap[DP_ID_FAULT_EN / 8] |= (1 << (DP_ID_FAULT_EN % 8)); bitmap[DP_ID_FAULT_EN / 8] |= (1 << (DP_ID_FAULT_EN % 8));
out[valIdx++] = (uint8_t)dp->fault_en; out[valIdx++] = (uint8_t)dp->fault_en;
} }
/* DP24 设备故障 (uint8, 1B, RO) */ /* DP18 设备故障 (uint8, 1B, RO) */
if (mask[DP_ID_FAULT_CODE / 8] & (1 << (DP_ID_FAULT_CODE % 8))) if (mask[DP_ID_FAULT_CODE / 8] & (1 << (DP_ID_FAULT_CODE % 8)))
{ {
bitmap[DP_ID_FAULT_CODE / 8] |= (1 << (DP_ID_FAULT_CODE % 8)); bitmap[DP_ID_FAULT_CODE / 8] |= (1 << (DP_ID_FAULT_CODE % 8));
out[valIdx++] = (uint8_t)dp->fault_code; out[valIdx++] = (uint8_t)dp->fault_code;
} }
/* DP25 设备告警使能 (uint8, 1B, RW) */ /* DP19 设备告警使能 (uint8, 1B, RW) */
if (mask[DP_ID_ALARM_EN / 8] & (1 << (DP_ID_ALARM_EN % 8))) if (mask[DP_ID_ALARM_EN / 8] & (1 << (DP_ID_ALARM_EN % 8)))
{ {
bitmap[DP_ID_ALARM_EN / 8] |= (1 << (DP_ID_ALARM_EN % 8)); bitmap[DP_ID_ALARM_EN / 8] |= (1 << (DP_ID_ALARM_EN % 8));
out[valIdx++] = (uint8_t)dp->alarm_en; out[valIdx++] = (uint8_t)dp->alarm_en;
} }
/* DP26 设备告警 (uint8, 1B, RO) */ /* DP20 设备告警 (uint8, 1B, RO) */
if (mask[DP_ID_ALARM_CODE / 8] & (1 << (DP_ID_ALARM_CODE % 8))) if (mask[DP_ID_ALARM_CODE / 8] & (1 << (DP_ID_ALARM_CODE % 8)))
{ {
bitmap[DP_ID_ALARM_CODE / 8] |= (1 << (DP_ID_ALARM_CODE % 8)); bitmap[DP_ID_ALARM_CODE / 8] |= (1 << (DP_ID_ALARM_CODE % 8));
out[valIdx++] = (uint8_t)dp->alarm_code; out[valIdx++] = (uint8_t)dp->alarm_code;
} }
/* DP28 预留DP1 (uint32, 4B, RW) */ /* DP22 预留DP1 (uint32, 4B, RW) */
if (mask[DP_ID_RESERVA_DP1 / 8] & (1 << (DP_ID_RESERVA_DP1 % 8))) if (mask[DP_ID_RESERVA_DP1 / 8] & (1 << (DP_ID_RESERVA_DP1 % 8)))
{ {
bitmap[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8)); bitmap[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8));
jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp1); jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp1);
valIdx += 4; valIdx += 4;
} }
/* DP29 预留DP2 (uint32, 4B, RW) */ /* DP23 预留DP2 (uint32, 4B, RW) */
if (mask[DP_ID_RESERVA_DP2 / 8] & (1 << (DP_ID_RESERVA_DP2 % 8))) if (mask[DP_ID_RESERVA_DP2 / 8] & (1 << (DP_ID_RESERVA_DP2 % 8)))
{ {
bitmap[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8)); bitmap[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8));
jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp2); jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp2);
valIdx += 4; valIdx += 4;
} }
/* DP30 预留DP3 (uint32, 4B, RW) */ /* DP24 预留DP3 (uint32, 4B, RW) */
if (mask[DP_ID_RESERVA_DP3 / 8] & (1 << (DP_ID_RESERVA_DP3 % 8))) if (mask[DP_ID_RESERVA_DP3 / 8] & (1 << (DP_ID_RESERVA_DP3 % 8)))
{ {
bitmap[DP_ID_RESERVA_DP3 / 8] |= (1 << (DP_ID_RESERVA_DP3 % 8)); bitmap[DP_ID_RESERVA_DP3 / 8] |= (1 << (DP_ID_RESERVA_DP3 % 8));
@@ -387,7 +387,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_LINKAGE_CONFIG; info->dp_ids[info->count++] = DP_ID_LINKAGE_CONFIG;
} }
} }
/* DP13 定时使能 (bool, 1B, RW) */ /* DP7 定时使能 (bool, 1B, RW) */
if (bitmap[DP_ID_TIME_EN / 8] & (1 << (DP_ID_TIME_EN % 8))) if (bitmap[DP_ID_TIME_EN / 8] & (1 << (DP_ID_TIME_EN % 8)))
{ {
if (valIdx < valLen) if (valIdx < valLen)
@@ -399,7 +399,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_TIME_EN; info->dp_ids[info->count++] = DP_ID_TIME_EN;
} }
} }
/* DP14 定时配置1 (raw, 8B, RW) */ /* DP8 定时配置1 (raw, 8B, RW) */
if (bitmap[DP_ID_TIME_CONFIG1 / 8] & (1 << (DP_ID_TIME_CONFIG1 % 8))) if (bitmap[DP_ID_TIME_CONFIG1 / 8] & (1 << (DP_ID_TIME_CONFIG1 % 8)))
{ {
if (valIdx + 8 <= valLen) if (valIdx + 8 <= valLen)
@@ -412,7 +412,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG1; info->dp_ids[info->count++] = DP_ID_TIME_CONFIG1;
} }
} }
/* DP15 定时配置2 (raw, 8B, RW) */ /* DP9 定时配置2 (raw, 8B, RW) */
if (bitmap[DP_ID_TIME_CONFIG2 / 8] & (1 << (DP_ID_TIME_CONFIG2 % 8))) if (bitmap[DP_ID_TIME_CONFIG2 / 8] & (1 << (DP_ID_TIME_CONFIG2 % 8)))
{ {
if (valIdx + 8 <= valLen) if (valIdx + 8 <= valLen)
@@ -425,7 +425,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG2; info->dp_ids[info->count++] = DP_ID_TIME_CONFIG2;
} }
} }
/* DP16 定时配置3 (raw, 8B, RW) */ /* DP10 定时配置3 (raw, 8B, RW) */
if (bitmap[DP_ID_TIME_CONFIG3 / 8] & (1 << (DP_ID_TIME_CONFIG3 % 8))) if (bitmap[DP_ID_TIME_CONFIG3 / 8] & (1 << (DP_ID_TIME_CONFIG3 % 8)))
{ {
if (valIdx + 8 <= valLen) if (valIdx + 8 <= valLen)
@@ -438,7 +438,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG3; info->dp_ids[info->count++] = DP_ID_TIME_CONFIG3;
} }
} }
/* DP17 定时配置4 (raw, 8B, RW) */ /* DP11 定时配置4 (raw, 8B, RW) */
if (bitmap[DP_ID_TIME_CONFIG4 / 8] & (1 << (DP_ID_TIME_CONFIG4 % 8))) if (bitmap[DP_ID_TIME_CONFIG4 / 8] & (1 << (DP_ID_TIME_CONFIG4 % 8)))
{ {
if (valIdx + 8 <= valLen) if (valIdx + 8 <= valLen)
@@ -451,7 +451,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG4; info->dp_ids[info->count++] = DP_ID_TIME_CONFIG4;
} }
} }
/* DP18 定时配置5 (raw, 8B, RW) */ /* DP12 定时配置5 (raw, 8B, RW) */
if (bitmap[DP_ID_TIME_CONFIG5 / 8] & (1 << (DP_ID_TIME_CONFIG5 % 8))) if (bitmap[DP_ID_TIME_CONFIG5 / 8] & (1 << (DP_ID_TIME_CONFIG5 % 8)))
{ {
if (valIdx + 8 <= valLen) if (valIdx + 8 <= valLen)
@@ -464,7 +464,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG5; info->dp_ids[info->count++] = DP_ID_TIME_CONFIG5;
} }
} }
/* DP19 定时配置6 (raw, 8B, RW) */ /* DP13 定时配置6 (raw, 8B, RW) */
if (bitmap[DP_ID_TIME_CONFIG6 / 8] & (1 << (DP_ID_TIME_CONFIG6 % 8))) if (bitmap[DP_ID_TIME_CONFIG6 / 8] & (1 << (DP_ID_TIME_CONFIG6 % 8)))
{ {
if (valIdx + 8 <= valLen) if (valIdx + 8 <= valLen)
@@ -477,7 +477,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG6; info->dp_ids[info->count++] = DP_ID_TIME_CONFIG6;
} }
} }
/* DP20 定时配置7 (raw, 8B, RW) */ /* DP14 定时配置7 (raw, 8B, RW) */
if (bitmap[DP_ID_TIME_CONFIG7 / 8] & (1 << (DP_ID_TIME_CONFIG7 % 8))) if (bitmap[DP_ID_TIME_CONFIG7 / 8] & (1 << (DP_ID_TIME_CONFIG7 % 8)))
{ {
if (valIdx + 8 <= valLen) if (valIdx + 8 <= valLen)
@@ -490,7 +490,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG7; info->dp_ids[info->count++] = DP_ID_TIME_CONFIG7;
} }
} }
/* DP22 心跳 (bool, 1B, WO) */ /* DP16 心跳 (bool, 1B, WO) */
if (bitmap[DP_ID_HEARTBEAT / 8] & (1 << (DP_ID_HEARTBEAT % 8))) if (bitmap[DP_ID_HEARTBEAT / 8] & (1 << (DP_ID_HEARTBEAT % 8)))
{ {
if (valIdx < valLen) if (valIdx < valLen)
@@ -502,7 +502,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_HEARTBEAT; info->dp_ids[info->count++] = DP_ID_HEARTBEAT;
} }
} }
/* DP23 设备故障使能 (uint8, 1B, RW) */ /* DP17 设备故障使能 (uint8, 1B, RW) */
if (bitmap[DP_ID_FAULT_EN / 8] & (1 << (DP_ID_FAULT_EN % 8))) if (bitmap[DP_ID_FAULT_EN / 8] & (1 << (DP_ID_FAULT_EN % 8)))
{ {
if (valIdx < valLen) if (valIdx < valLen)
@@ -514,7 +514,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_FAULT_EN; info->dp_ids[info->count++] = DP_ID_FAULT_EN;
} }
} }
/* DP25 设备告警使能 (uint8, 1B, RW) */ /* DP19 设备告警使能 (uint8, 1B, RW) */
if (bitmap[DP_ID_ALARM_EN / 8] & (1 << (DP_ID_ALARM_EN % 8))) if (bitmap[DP_ID_ALARM_EN / 8] & (1 << (DP_ID_ALARM_EN % 8)))
{ {
if (valIdx < valLen) if (valIdx < valLen)
@@ -526,7 +526,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_ALARM_EN; info->dp_ids[info->count++] = DP_ID_ALARM_EN;
} }
} }
/* DP27 恢复出厂设置 (bool, 1B, WO) */ /* DP21 恢复出厂设置 (bool, 1B, WO) */
if (bitmap[DP_ID_FACTORY_RESET / 8] & (1 << (DP_ID_FACTORY_RESET % 8))) if (bitmap[DP_ID_FACTORY_RESET / 8] & (1 << (DP_ID_FACTORY_RESET % 8)))
{ {
if (valIdx < valLen) if (valIdx < valLen)
@@ -538,7 +538,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_FACTORY_RESET; info->dp_ids[info->count++] = DP_ID_FACTORY_RESET;
} }
} }
/* DP28 预留DP1 (uint32, 4B, RW) */ /* DP22 预留DP1 (uint32, 4B, RW) */
if (bitmap[DP_ID_RESERVA_DP1 / 8] & (1 << (DP_ID_RESERVA_DP1 % 8))) if (bitmap[DP_ID_RESERVA_DP1 / 8] & (1 << (DP_ID_RESERVA_DP1 % 8)))
{ {
if (valIdx + 4 <= valLen) if (valIdx + 4 <= valLen)
@@ -551,7 +551,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_RESERVA_DP1; info->dp_ids[info->count++] = DP_ID_RESERVA_DP1;
} }
} }
/* DP29 预留DP2 (uint32, 4B, RW) */ /* DP23 预留DP2 (uint32, 4B, RW) */
if (bitmap[DP_ID_RESERVA_DP2 / 8] & (1 << (DP_ID_RESERVA_DP2 % 8))) if (bitmap[DP_ID_RESERVA_DP2 / 8] & (1 << (DP_ID_RESERVA_DP2 % 8)))
{ {
if (valIdx + 4 <= valLen) if (valIdx + 4 <= valLen)
@@ -564,7 +564,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_RESERVA_DP2; info->dp_ids[info->count++] = DP_ID_RESERVA_DP2;
} }
} }
/* DP30 预留DP3 (uint32, 4B, RW) */ /* DP24 预留DP3 (uint32, 4B, RW) */
if (bitmap[DP_ID_RESERVA_DP3 / 8] & (1 << (DP_ID_RESERVA_DP3 % 8))) if (bitmap[DP_ID_RESERVA_DP3 / 8] & (1 << (DP_ID_RESERVA_DP3 % 8)))
{ {
if (valIdx + 4 <= valLen) if (valIdx + 4 <= valLen)
@@ -582,7 +582,15 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
/* ════════════════════════════════════════════════════════════════ /* ════════════════════════════════════════════════════════════════
* jbBuildDiffMask — 比较上次与当前值,为变化的可上报 DP 置 mask 位 * jbBuildDiffMask — 比较上次与当前值,为变化的可上报 DP 置 mask 位
* 返回: 1 = 有 DP 发生变化, 0 = 全部未变化 * 返回: 1 = 有 DP 发生变化, 0 = 全部未变化
* DP15 运行记录:今天累计秒数相对上次上报满 JB_RUNREC_DIFF_SEC 才进差分;
* 其它日期格变化(跨天旋转)立刻进差分。0x05 读 / 10min 全量仍带完整 124B。
* ════════════════════════════════════════════════════════════════ */ * ════════════════════════════════════════════════════════════════ */
static uint32_t jbRunrecTodaySec(const uint8_t *rec)
{
return ((uint32_t)rec[0] << 24) | ((uint32_t)rec[1] << 16)
| ((uint32_t)rec[2] << 8) | (uint32_t)rec[3];
}
static uint8_t jbBuildDiffMask(const jb_data_point_t *last, static uint8_t jbBuildDiffMask(const jb_data_point_t *last,
const jb_data_point_t *cur, const jb_data_point_t *cur,
uint8_t *mask) uint8_t *mask)
@@ -632,93 +640,106 @@ static uint8_t jbBuildDiffMask(const jb_data_point_t *last,
mask[DP_ID_LINKAGE_CONFIG / 8] |= (1 << (DP_ID_LINKAGE_CONFIG % 8)); mask[DP_ID_LINKAGE_CONFIG / 8] |= (1 << (DP_ID_LINKAGE_CONFIG % 8));
hasChange = 1; hasChange = 1;
} }
/* DP13 定时使能 */ /* DP7 定时使能 */
if (last->time_en != cur->time_en) if (last->time_en != cur->time_en)
{ {
mask[DP_ID_TIME_EN / 8] |= (1 << (DP_ID_TIME_EN % 8)); mask[DP_ID_TIME_EN / 8] |= (1 << (DP_ID_TIME_EN % 8));
hasChange = 1; hasChange = 1;
} }
/* DP14 定时配置1 */ /* DP8 定时配置1 */
if (memcmp(last->time_config1, cur->time_config1, 8)) if (memcmp(last->time_config1, cur->time_config1, 8))
{ {
mask[DP_ID_TIME_CONFIG1 / 8] |= (1 << (DP_ID_TIME_CONFIG1 % 8)); mask[DP_ID_TIME_CONFIG1 / 8] |= (1 << (DP_ID_TIME_CONFIG1 % 8));
hasChange = 1; hasChange = 1;
} }
/* DP15 定时配置2 */ /* DP9 定时配置2 */
if (memcmp(last->time_config2, cur->time_config2, 8)) if (memcmp(last->time_config2, cur->time_config2, 8))
{ {
mask[DP_ID_TIME_CONFIG2 / 8] |= (1 << (DP_ID_TIME_CONFIG2 % 8)); mask[DP_ID_TIME_CONFIG2 / 8] |= (1 << (DP_ID_TIME_CONFIG2 % 8));
hasChange = 1; hasChange = 1;
} }
/* DP16 定时配置3 */ /* DP10 定时配置3 */
if (memcmp(last->time_config3, cur->time_config3, 8)) if (memcmp(last->time_config3, cur->time_config3, 8))
{ {
mask[DP_ID_TIME_CONFIG3 / 8] |= (1 << (DP_ID_TIME_CONFIG3 % 8)); mask[DP_ID_TIME_CONFIG3 / 8] |= (1 << (DP_ID_TIME_CONFIG3 % 8));
hasChange = 1; hasChange = 1;
} }
/* DP17 定时配置4 */ /* DP11 定时配置4 */
if (memcmp(last->time_config4, cur->time_config4, 8)) if (memcmp(last->time_config4, cur->time_config4, 8))
{ {
mask[DP_ID_TIME_CONFIG4 / 8] |= (1 << (DP_ID_TIME_CONFIG4 % 8)); mask[DP_ID_TIME_CONFIG4 / 8] |= (1 << (DP_ID_TIME_CONFIG4 % 8));
hasChange = 1; hasChange = 1;
} }
/* DP18 定时配置5 */ /* DP12 定时配置5 */
if (memcmp(last->time_config5, cur->time_config5, 8)) if (memcmp(last->time_config5, cur->time_config5, 8))
{ {
mask[DP_ID_TIME_CONFIG5 / 8] |= (1 << (DP_ID_TIME_CONFIG5 % 8)); mask[DP_ID_TIME_CONFIG5 / 8] |= (1 << (DP_ID_TIME_CONFIG5 % 8));
hasChange = 1; hasChange = 1;
} }
/* DP19 定时配置6 */ /* DP13 定时配置6 */
if (memcmp(last->time_config6, cur->time_config6, 8)) if (memcmp(last->time_config6, cur->time_config6, 8))
{ {
mask[DP_ID_TIME_CONFIG6 / 8] |= (1 << (DP_ID_TIME_CONFIG6 % 8)); mask[DP_ID_TIME_CONFIG6 / 8] |= (1 << (DP_ID_TIME_CONFIG6 % 8));
hasChange = 1; hasChange = 1;
} }
/* DP20 定时配置7 */ /* DP14 定时配置7 */
if (memcmp(last->time_config7, cur->time_config7, 8)) if (memcmp(last->time_config7, cur->time_config7, 8))
{ {
mask[DP_ID_TIME_CONFIG7 / 8] |= (1 << (DP_ID_TIME_CONFIG7 % 8)); mask[DP_ID_TIME_CONFIG7 / 8] |= (1 << (DP_ID_TIME_CONFIG7 % 8));
hasChange = 1; hasChange = 1;
} }
/* DP21 运行记录:每秒都在变,不参与差分上报(避免 2s 防抖推 124B)。 /* DP15 运行记录:今天秒数差分满 1 分钟,或后 30 天格变了(跨天) */
* 读请求与 REPORT_PERIOD 全量仍带当前 gDevData.run_record。 */ {
/* DP23 设备故障使能 */ uint32_t last_today = jbRunrecTodaySec(last->run_record);
uint32_t cur_today = jbRunrecTodaySec(cur->run_record);
uint32_t delta = (cur_today > last_today)
? (cur_today - last_today)
: (last_today - cur_today);
if ((delta >= JB_RUNREC_DIFF_SEC)
|| memcmp(&last->run_record[4], &cur->run_record[4], 120))
{
mask[DP_ID_RUN_RECORD / 8] |= (1 << (DP_ID_RUN_RECORD % 8));
hasChange = 1;
}
}
/* DP17 设备故障使能 */
if (last->fault_en != cur->fault_en) if (last->fault_en != cur->fault_en)
{ {
mask[DP_ID_FAULT_EN / 8] |= (1 << (DP_ID_FAULT_EN % 8)); mask[DP_ID_FAULT_EN / 8] |= (1 << (DP_ID_FAULT_EN % 8));
hasChange = 1; hasChange = 1;
} }
/* DP24 设备故障 */ /* DP18 设备故障 */
if (last->fault_code != cur->fault_code) if (last->fault_code != cur->fault_code)
{ {
mask[DP_ID_FAULT_CODE / 8] |= (1 << (DP_ID_FAULT_CODE % 8)); mask[DP_ID_FAULT_CODE / 8] |= (1 << (DP_ID_FAULT_CODE % 8));
hasChange = 1; hasChange = 1;
} }
/* DP25 设备告警使能 */ /* DP19 设备告警使能 */
if (last->alarm_en != cur->alarm_en) if (last->alarm_en != cur->alarm_en)
{ {
mask[DP_ID_ALARM_EN / 8] |= (1 << (DP_ID_ALARM_EN % 8)); mask[DP_ID_ALARM_EN / 8] |= (1 << (DP_ID_ALARM_EN % 8));
hasChange = 1; hasChange = 1;
} }
/* DP26 设备告警 */ /* DP20 设备告警 */
if (last->alarm_code != cur->alarm_code) if (last->alarm_code != cur->alarm_code)
{ {
mask[DP_ID_ALARM_CODE / 8] |= (1 << (DP_ID_ALARM_CODE % 8)); mask[DP_ID_ALARM_CODE / 8] |= (1 << (DP_ID_ALARM_CODE % 8));
hasChange = 1; hasChange = 1;
} }
/* DP28 预留DP1 */ /* DP22 预留DP1 */
if (last->reserva_dp1 != cur->reserva_dp1) if (last->reserva_dp1 != cur->reserva_dp1)
{ {
mask[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8)); mask[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8));
hasChange = 1; hasChange = 1;
} }
/* DP29 预留DP2 */ /* DP23 预留DP2 */
if (last->reserva_dp2 != cur->reserva_dp2) if (last->reserva_dp2 != cur->reserva_dp2)
{ {
mask[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8)); mask[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8));
hasChange = 1; hasChange = 1;
} }
/* DP30 预留DP3 */ /* DP24 预留DP3 */
if (last->reserva_dp3 != cur->reserva_dp3) if (last->reserva_dp3 != cur->reserva_dp3)
{ {
mask[DP_ID_RESERVA_DP3 / 8] |= (1 << (DP_ID_RESERVA_DP3 % 8)); mask[DP_ID_RESERVA_DP3 / 8] |= (1 << (DP_ID_RESERVA_DP3 % 8));
@@ -864,7 +885,19 @@ static void jbReportPolicy(jb_data_point_t *data)
(now - jbProtocol.lastChangeTime > REPORT_DEBOUNCE)) (now - jbProtocol.lastChangeTime > REPORT_DEBOUNCE))
{ {
jbSendReport(data, diffMask); jbSendReport(data, diffMask);
memcpy(&jbProtocol.lastData, data, sizeof(jb_data_point_t)); /* 未带 DP15 时保留上次记录基准,避免档位/模式等上报把 1 分钟差分冲掉 */
if (!(diffMask[DP_ID_RUN_RECORD / 8] & (1u << (DP_ID_RUN_RECORD % 8))))
{
uint8_t keep[124];
memcpy(keep, jbProtocol.lastData.run_record, 124);
memcpy(&jbProtocol.lastData, data, sizeof(jb_data_point_t));
memcpy(jbProtocol.lastData.run_record, keep, 124);
}
else
{
memcpy(&jbProtocol.lastData, data, sizeof(jb_data_point_t));
}
jbProtocol.lastChangeTime = now; jbProtocol.lastChangeTime = now;
jbProtocol.lastReportTime = now; jbProtocol.lastReportTime = now;
return; return;
+39 -38
View File
@@ -23,10 +23,10 @@
* ════════════════════════════════════════════════════════════════ */ * ════════════════════════════════════════════════════════════════ */
#define PKT_HEAD_0 0x55 #define PKT_HEAD_0 0x55
#define PKT_HEAD_1 0xAA #define PKT_HEAD_1 0xAA
/* ⚠ 生成器给的是 128,必须改回 256:DP21 run_record 124B,加 bitmap(4B) 与 /* ⚠ 生成器给的是 128,必须改回 256:DP15 run_record 124B,加 bitmap(4B) 与
* cmd/sn/cksum 开销后 128B 组不出完整帧(规格书第 0 章第 1 条 / 第 19 章)。 * cmd/sn/cksum 开销后 128B 组不出完整帧(规格书第 0 章第 1 条 / 第 19 章)。
* 每次从生成器重新拷贝 jb_protocol.h 后都要重新确认本值。 */ * 每次从生成器重新拷贝 jb_protocol.h 后都要重新确认本值。 */
#define MAX_PACKAGE_LEN 256 #define MAX_PACKAGE_LEN 512
#define JB_OTA_MAX_PAYLOAD 96 /* OTA 单包 Payload 上限(Byte); 须 <= MAX_PACKAGE_LEN - 11(0x32 帧开销) */ #define JB_OTA_MAX_PAYLOAD 96 /* OTA 单包 Payload 上限(Byte); 须 <= MAX_PACKAGE_LEN - 11(0x32 帧开销) */
#define DP_COUNT 25 #define DP_COUNT 25
@@ -36,6 +36,7 @@
#define SEND_MAX_NUM 3 /* 最大重传次数 */ #define SEND_MAX_NUM 3 /* 最大重传次数 */
#define REPORT_DEBOUNCE 2000 /* 变化上报防抖时间(ms) */ #define REPORT_DEBOUNCE 2000 /* 变化上报防抖时间(ms) */
#define REPORT_PERIOD 600000 /* 定时全量上报周期(ms) */ #define REPORT_PERIOD 600000 /* 定时全量上报周期(ms) */
#define JB_RUNREC_DIFF_SEC 60 /* DP15 差分:今天秒数相对上次上报满 1 分钟才带 */
/* Device identity: 取自 Excel 设备信息表的 ProdtCode / productCode。 /* Device identity: 取自 Excel 设备信息表的 ProdtCode / productCode。
* JB_CATEGORY_CODE: 必须 4 字符 ASCII,供 BLE 广播 prodcode 使用 * JB_CATEGORY_CODE: 必须 4 字符 ASCII,供 BLE 广播 prodcode 使用
@@ -128,24 +129,24 @@ typedef enum
DP_ID_CURRENT_HUMI = 4, /* 当前湿度 (uint8, RO) */ DP_ID_CURRENT_HUMI = 4, /* 当前湿度 (uint8, RO) */
DP_ID_LINKAGE_DELETE = 5, /* 联动删除 (bool, RW) */ DP_ID_LINKAGE_DELETE = 5, /* 联动删除 (bool, RW) */
DP_ID_LINKAGE_CONFIG = 6, /* 联动配置 (raw, RW) */ DP_ID_LINKAGE_CONFIG = 6, /* 联动配置 (raw, RW) */
DP_ID_TIME_EN = 13, /* 定时使能 (bool, RW) */ DP_ID_TIME_EN = 7, /* 定时使能 (bool, RW) */
DP_ID_TIME_CONFIG1 = 14, /* 定时配置1 (raw, RW) */ DP_ID_TIME_CONFIG1 = 8, /* 定时配置1 (raw, RW) */
DP_ID_TIME_CONFIG2 = 15, /* 定时配置2 (raw, RW) */ DP_ID_TIME_CONFIG2 = 9, /* 定时配置2 (raw, RW) */
DP_ID_TIME_CONFIG3 = 16, /* 定时配置3 (raw, RW) */ DP_ID_TIME_CONFIG3 = 10, /* 定时配置3 (raw, RW) */
DP_ID_TIME_CONFIG4 = 17, /* 定时配置4 (raw, RW) */ DP_ID_TIME_CONFIG4 = 11, /* 定时配置4 (raw, RW) */
DP_ID_TIME_CONFIG5 = 18, /* 定时配置5 (raw, RW) */ DP_ID_TIME_CONFIG5 = 12, /* 定时配置5 (raw, RW) */
DP_ID_TIME_CONFIG6 = 19, /* 定时配置6 (raw, RW) */ DP_ID_TIME_CONFIG6 = 13, /* 定时配置6 (raw, RW) */
DP_ID_TIME_CONFIG7 = 20, /* 定时配置7 (raw, RW) */ DP_ID_TIME_CONFIG7 = 14, /* 定时配置7 (raw, RW) */
DP_ID_RUN_RECORD = 21, /* 运行记录 (raw, RO) */ DP_ID_RUN_RECORD = 15, /* 运行记录 (raw, RO) */
DP_ID_HEARTBEAT = 22, /* 心跳 (bool, WO) */ DP_ID_HEARTBEAT = 16, /* 心跳 (bool, WO) */
DP_ID_FAULT_EN = 23, /* 设备故障使能 (uint8, RW) */ DP_ID_FAULT_EN = 17, /* 设备故障使能 (uint8, RW) */
DP_ID_FAULT_CODE = 24, /* 设备故障 (uint8, RO) */ DP_ID_FAULT_CODE = 18, /* 设备故障 (uint8, RO) */
DP_ID_ALARM_EN = 25, /* 设备告警使能 (uint8, RW) */ DP_ID_ALARM_EN = 19, /* 设备告警使能 (uint8, RW) */
DP_ID_ALARM_CODE = 26, /* 设备告警 (uint8, RO) */ DP_ID_ALARM_CODE = 20, /* 设备告警 (uint8, RO) */
DP_ID_FACTORY_RESET = 27, /* 恢复出厂设置 (bool, WO) */ DP_ID_FACTORY_RESET = 21, /* 恢复出厂设置 (bool, WO) */
DP_ID_RESERVA_DP1 = 28, /* 预留DP1 (uint32, RW) */ DP_ID_RESERVA_DP1 = 22, /* 预留DP1 (uint32, RW) */
DP_ID_RESERVA_DP2 = 29, /* 预留DP2 (uint32, RW) */ DP_ID_RESERVA_DP2 = 23, /* 预留DP2 (uint32, RW) */
DP_ID_RESERVA_DP3 = 30, /* 预留DP3 (uint32, RW) */ DP_ID_RESERVA_DP3 = 24, /* 预留DP3 (uint32, RW) */
DP_ID_MAX DP_ID_MAX
} dp_id_t; } dp_id_t;
@@ -210,24 +211,24 @@ typedef struct
uint8_t current_humi; /* DP4 当前湿度 */ uint8_t current_humi; /* DP4 当前湿度 */
uint8_t linkage_delete;/* DP5 联动删除 */ uint8_t linkage_delete;/* DP5 联动删除 */
uint8_t linkage_config[20];/* DP6 联动配置 */ uint8_t linkage_config[20];/* DP6 联动配置 */
uint8_t time_en; /* DP13 定时使能 */ uint8_t time_en; /* DP7 定时使能 */
uint8_t time_config1[8];/* DP14 定时配置1 */ uint8_t time_config1[8];/* DP8 定时配置1 */
uint8_t time_config2[8];/* DP15 定时配置2 */ uint8_t time_config2[8];/* DP9 定时配置2 */
uint8_t time_config3[8];/* DP16 定时配置3 */ uint8_t time_config3[8];/* DP10 定时配置3 */
uint8_t time_config4[8];/* DP17 定时配置4 */ uint8_t time_config4[8];/* DP11 定时配置4 */
uint8_t time_config5[8];/* DP18 定时配置5 */ uint8_t time_config5[8];/* DP12 定时配置5 */
uint8_t time_config6[8];/* DP19 定时配置6 */ uint8_t time_config6[8];/* DP13 定时配置6 */
uint8_t time_config7[8];/* DP20 定时配置7 */ uint8_t time_config7[8];/* DP14 定时配置7 */
uint8_t run_record[124];/* DP21 运行记录 */ uint8_t run_record[124];/* DP15 运行记录 */
uint8_t heartbeat; /* DP22 心跳 */ uint8_t heartbeat; /* DP16 心跳 */
uint8_t fault_en; /* DP23 设备故障使能 */ uint8_t fault_en; /* DP17 设备故障使能 */
uint8_t fault_code; /* DP24 设备故障 */ uint8_t fault_code; /* DP18 设备故障 */
uint8_t alarm_en; /* DP25 设备告警使能 */ uint8_t alarm_en; /* DP19 设备告警使能 */
uint8_t alarm_code; /* DP26 设备告警 */ uint8_t alarm_code; /* DP20 设备告警 */
uint8_t factory_reset;/* DP27 恢复出厂设置 */ uint8_t factory_reset;/* DP21 恢复出厂设置 */
uint32_t reserva_dp1; /* DP28 预留DP1 */ uint32_t reserva_dp1; /* DP22 预留DP1 */
uint32_t reserva_dp2; /* DP29 预留DP2 */ uint32_t reserva_dp2; /* DP23 预留DP2 */
uint32_t reserva_dp3; /* DP30 预留DP3 */ uint32_t reserva_dp3; /* DP24 预留DP3 */
} jb_data_point_t; } jb_data_point_t;
/* 控制事件信息 */ /* 控制事件信息 */
+2 -2
View File
@@ -4,7 +4,7 @@
* @author cyWu <1917507415@qq.com> * @author cyWu <1917507415@qq.com>
* @date 2026.09.02 * @date 2026.09.02
* @version V3.0.0,规格书 V3.0.0:条件引擎(app_cond)整模块删除,换 * @version V3.0.0,规格书 V3.0.0:条件引擎(app_cond)整模块删除,换
* app_linkageDP5/DP6,联动)+ app_runrecDP2131 天运行记录); * app_linkageDP5/DP6,联动)+ app_runrecDP1531 天运行记录);
* 20ms 节拍加 app_runrec_tick500ms 任务上下文加 * 20ms 节拍加 app_runrec_tick500ms 任务上下文加
* app_linkage_run / app_runrec_rotate_if_needed * app_linkage_run / app_runrec_rotate_if_needed
* *
@@ -185,6 +185,6 @@ void usr_timer_loop(void)
jbProtocolHandle(&gDevData); jbProtocolHandle(&gDevData);
app_timer_run(500); /* 定时引擎(RTC 门闩 + 窗判断;窗结束会调联动重判) */ app_timer_run(500); /* 定时引擎(RTC 门闩 + 窗判断;窗结束会调联动重判) */
app_linkage_run(500); /* 联动引擎(任务上下文;仲裁见 app_arbiter */ app_linkage_run(500); /* 联动引擎(任务上下文;仲裁见 app_arbiter */
app_runrec_rotate_if_needed(); /* 收下 pending、每拍灌 DP21;满 10 分钟/跨天才写 VM */ app_runrec_rotate_if_needed(); /* 收下 pending、每拍灌 DP15;满 10 分钟/跨天才写 VM */
app_function_loop(); /* 任务上下文:VM 防抖落盘 + 配对/OTA 动作落地 */ app_function_loop(); /* 任务上下文:VM 防抖落盘 + 配对/OTA 动作落地 */
} }
+67 -29
View File
@@ -673,51 +673,89 @@ int bleproto_appdata_serialize(bleproto_appdata_t *appdata,
int bleproto_appdata_wrap_raw(uint8_t *txbuf, int bleproto_appdata_wrap_raw(uint8_t *txbuf,
uint16_t txbuf_size, uint16_t txbuf_size,
uint8_t packetlen,
const bleproto_appdata_header_t *header, const bleproto_appdata_header_t *header,
const uint8_t *payload, const uint8_t *payload,
uint16_t payload_len) uint16_t payload_len)
{ {
uint16_t total; uint8_t frame_max;
uint16_t totalframe;
uint16_t need;
uint16_t offset = 0;
uint16_t data_offset = 0;
uint16_t i;
if (!txbuf || !header) { if (!txbuf || !header || packetlen <= BLEPROTO_APPDATA_HEADR_LEN) {
BLEPROTO_T_E("Invalid parameters"); printf("Invalid parameters");
return -1; return -1;
} }
if (payload_len > 0 && !payload) { if (payload_len > 0 && !payload) {
BLEPROTO_T_E("Invalid payload"); printf("Invalid payload");
return -1;
}
/* 单帧 this_frame_len 为 uint8,超过 255 需走分包 serialize */
if (payload_len > 255u) {
BLEPROTO_T_E("payload too long for single frame");
return -1; return -1;
} }
total = (uint16_t)(BLEPROTO_APPDATA_HEADR_LEN + payload_len); /* 与 serialize 相同:一帧总长 packetlen,其中 8B 头,其余为 payload */
if (txbuf_size < total) { frame_max = (uint8_t)(packetlen - BLEPROTO_APPDATA_HEADR_LEN);
BLEPROTO_T_E("txbuf too short");
if (payload_len == 0) {
totalframe = 1;
} else {
totalframe = (uint16_t)((payload_len + frame_max - 1u) / frame_max);
}
if (totalframe > 255u) {
printf("payload too long, frames %u>255\n", (unsigned)totalframe);
return -1; return -1;
} }
/* buf[0]: version[2:0] | datafmt[3:3] | msgtype[7:4] */ need = (uint16_t)((uint16_t)totalframe * BLEPROTO_APPDATA_HEADR_LEN + payload_len);
txbuf[0] = UTILS_BITFIELD_SET(header->version, 2, 0) | if (txbuf_size < need) {
UTILS_BITFIELD_SET(header->datafmt, 3, 3) | printf("txbuf too short %u<%u\n", (unsigned)txbuf_size, (unsigned)need);
UTILS_BITFIELD_SET(header->msgtype, 7, 4); return -1;
/* buf[1]: msgid[3:0] | encrypt[4:4] | reserve[7:5] */
txbuf[1] = UTILS_BITFIELD_SET(header->msgid, 3, 0) |
UTILS_BITFIELD_SET(header->encrypt, 4, 4) |
UTILS_BITFIELD_SET(header->reserve, 7, 5);
txbuf[2] = 1; /* totalframe */
txbuf[3] = 0; /* frameseq:单帧为 0 */
txbuf[4] = (uint8_t)payload_len; /* this frame payload len */
txbuf[5] = header->serviceid;
proto_bytes_put_le16(&txbuf[6], payload_len);
if (payload_len > 0) {
memcpy(&txbuf[BLEPROTO_APPDATA_HEADR_LEN], payload, payload_len);
} }
return (int)total; for (i = 0; i < totalframe; i++) {
uint8_t this_len;
uint8_t frameseq;
if (totalframe == 1u) {
this_len = (uint8_t)payload_len;
frameseq = 0; /* 单帧序号为 0 */
} else if ((uint16_t)(i + 1u) < totalframe) {
this_len = frame_max;
frameseq = (uint8_t)(i + 1u); /* 分包从 1 起 */
} else {
this_len = (uint8_t)(payload_len - data_offset);
frameseq = (uint8_t)(i + 1u);
}
/* buf[0]: version[2:0] | datafmt[3:3] | msgtype[7:4] */
txbuf[offset + 0] = UTILS_BITFIELD_SET(header->version, 2, 0) |
UTILS_BITFIELD_SET(header->datafmt, 3, 3) |
UTILS_BITFIELD_SET(header->msgtype, 7, 4);
/* buf[1]: msgid[3:0] | encrypt[4:4] | reserve[7:5] */
txbuf[offset + 1] = UTILS_BITFIELD_SET(header->msgid, 3, 0) |
UTILS_BITFIELD_SET(header->encrypt, 4, 4) |
UTILS_BITFIELD_SET(header->reserve, 7, 5);
txbuf[offset + 2] = (uint8_t)totalframe;
txbuf[offset + 3] = frameseq;
txbuf[offset + 4] = this_len;
txbuf[offset + 5] = header->serviceid;
proto_bytes_put_le16(&txbuf[offset + 6], payload_len);
offset = (uint16_t)(offset + BLEPROTO_APPDATA_HEADR_LEN);
if (this_len != 0) {
memcpy(&txbuf[offset], &payload[data_offset], this_len);
offset = (uint16_t)(offset + this_len);
data_offset = (uint16_t)(data_offset + this_len);
}
}
if (totalframe > 1u) {
printf("wrap_raw split %uB -> %u frames, out %uB\n",
(unsigned)payload_len, (unsigned)totalframe, (unsigned)offset);
}
return (int)offset;
} }
int bleproto_appdata_deserialize(uint8_t * buf, int bleproto_appdata_deserialize(uint8_t * buf,
+8 -4
View File
@@ -586,17 +586,21 @@ int bleproto_appdata_serialize(bleproto_appdata_t *appdata,
uint16_t size); uint16_t size);
/** /**
* @brief 将已打包好的 payload 包装成单帧 appdata分包) * @brief 将已打包好的 payload 包装成 appdata超过单帧则按 packetlen 分包)
* @param txbuf 输出缓冲 * @param txbuf 输出缓冲(须能放下 每帧8B头 × 帧数 + payload
* @param txbuf_size 输出缓冲大小 * @param txbuf_size 输出缓冲大小
* @param packetlen BLE GATT 单包有效长度,与 serialize 相同(本板 247
* @param header 应用层头(datalen/totalframe/frameseq 由本函数填写) * @param header 应用层头(datalen/totalframe/frameseq 由本函数填写)
* @param payload 已序列化的 service 数据;可为 NULLpayload_len==0 * @param payload 已序列化的 service 数据;可为 NULLpayload_len==0
* @param payload_len payload 长度,须 <=255(单帧 pthis_frame_len 为 u8 * @param payload_len 应用数据总长度
* @return >0 总字节数;<0 失败 * @return >0 总字节数;<0 失败
* @note 小包场景(JB 55AA 透传等)用此接口,避免 bleproto_appdata_t 大缓冲 * @note 切帧规则与 serialize 一致:单帧 payload = packetlen - 8
* 不超过则 totalframe=1、frameseq=0;超过则中间帧填满、末帧剩余,
* frameseq 从 1 递增。避免走 bleproto_appdata_t 大缓冲。
*/ */
int bleproto_appdata_wrap_raw(uint8_t *txbuf, int bleproto_appdata_wrap_raw(uint8_t *txbuf,
uint16_t txbuf_size, uint16_t txbuf_size,
uint8_t packetlen,
const bleproto_appdata_header_t *header, const bleproto_appdata_header_t *header,
const uint8_t *payload, const uint8_t *payload,
uint16_t payload_len); uint16_t payload_len);
+39 -193
View File
@@ -1,16 +1,15 @@
/****************************************************************************** /******************************************************************************
* @file usr_le_adv.c * @file usr_le_adv.c
* @brief BLE 广播 / 配对 / OTA 广播切换 + 自定义温湿度广播窗 * @brief BLE 广播 / 配对 / OTA 广播切换
* @author cyWu <1917507415@qq.com> * @author cyWu <1917507415@qq.com>
* @date 2026.09.03 * @date 2026.09.07
* @version V1.2.0新增「已绑定未连接」时的 1s 温湿度广播窗 * @version V1.4.0慢广播改由 BOARD_BLE_SLOW_ADV_ENABLE / SEC 控制
* @history * @history
* - V1.4.0, 2026.09.07, cyWu, 常供电默认不切慢广播,秒数可配
* - V1.0.0, 2026.08.03, cyWu, 首次发布 * - V1.0.0, 2026.08.03, cyWu, 首次发布
* - V1.1.0, 2026.08.19, cyWu, 对齐模组注释与助手;保留 SOC 上电绑广播策略 * - V1.1.0, 2026.08.19, cyWu, 对齐模组注释与助手;保留 SOC 上电绑广播策略
* - V1.2.0, 2026.09.03, cyWu, 已绑定+未连接+有缸体+本周期采样成功 → 发 1s * - V1.2.0, 2026.09.03, cyWu, 已绑定未连接温湿度窗(随后拆出)
* 自定义温湿度广播(3 段 ADFlags / Complete Local Name / Manufacturer * - V1.3.0, 2026.09.03, cyWu, 温湿度窗迁到 app_th_adv,本文件只留钩子
* 0xFFFF),随后切回见宝回连包。配置中/未配网/tank=0/采样失败/OTA/已连接
* 不发。app_th 只置 pending,本文件(500ms 任务上下文)负责组包与切回。
******************************************************************************/ ******************************************************************************/
#include "system/includes.h" #include "system/includes.h"
@@ -20,8 +19,9 @@
#include "bleproto.h" #include "bleproto.h"
#include "bleproto_api.h" #include "bleproto_api.h"
#include "usr_le_product.h" #include "usr_le_product.h"
#include "app_th.h" #include "usr_le_api.h"
#include "app_linkage.h" #include "app_th_adv.h"
#include "board_pin.h"
#define LOG_TAG_CONST APP #define LOG_TAG_CONST APP
#define LOG_TAG "[LE_ADV]" #define LOG_TAG "[LE_ADV]"
@@ -48,17 +48,7 @@
* SOC 决策表(与模组不同:未配网也发绑定广播,便于上电配网): * SOC 决策表(与模组不同:未配网也发绑定广播,便于上电配网):
* usr_goto_pair==1 → 绑定广播 * usr_goto_pair==1 → 绑定广播
* paired_flag==0 → 绑定广播 * paired_flag==0 → 绑定广播
* paired_flag==1 → 回连广播(可被下面的温湿度窗临时顶替 1s * paired_flag==1 → 回连广播(可被 app_th_adv 临时顶替 1s
*
* 自定义温湿度广播窗(V1.2.0):
* 同时满足「未连接 + paired_flag==1 + usr_goto_pair==0 + 非 OTA + tank_id!=0
* + 本周期 AHT20 采样成功」→ 发 1s(500ms × 2 拍)自定义包,之后 encode4reconn
* 切回见宝回连包。整包三段,按蓝牙 AD 结构 [Len][Type][Data…] 组:
* 02 01 06
* (1+n) 09 "JBao-<tank>"tank 十进制无前导零)
* 06 FF FF FF <ver> <temp int8> <humi uint8>
* 这不是见宝 TLV,不用 encode4heartbeat;见宝回连仍用 encode4reconn。
* 给 nRF 和以后自研扫描器看,GATT 0x2A00 / gap_device_name 不动。
******************************************************************************/ ******************************************************************************/
extern u8 muti_adv_data[31]; extern u8 muti_adv_data[31];
@@ -66,27 +56,10 @@ extern u8 muti_adv_data_len;
/** 广播数据最大长度(BLE 4.x 31 字节) */ /** 广播数据最大长度(BLE 4.x 31 字节) */
#define USR_ADV_DATA_MAX 31 #define USR_ADV_DATA_MAX 31
/** 温湿度窗持续拍数:500ms × 2 = 1s */ #define USR_LE_ADV_TICK_MS 500u /* 与 sys_timer_add(usr_ble_adv_updata) 一致 */
#define USR_TH_ADV_WINDOW_TICKS 2 #if BOARD_BLE_SLOW_ADV_ENABLE
/** 温湿度窗内的快广播间隔(窗内暂停「60s 变慢广播」) */ #define USR_LE_SLOW_ADV_TICKS ((BOARD_BLE_SLOW_ADV_SEC * 1000u) / USR_LE_ADV_TICK_MS)
#define USR_TH_ADV_FAST_INTERVAL 80 #endif
/** AD TypeFlags(与见宝包一致) */
#define USR_AD_TYPE_FLAGS 0x01
/** AD TypeComplete Local Name */
#define USR_AD_TYPE_LOCAL_NAME 0x09
/** AD TypeManufacturer Specific Data(禁止用 0x16 Service Data,那是见宝在用的) */
#define USR_AD_TYPE_MANUFACTURER 0xFF
/** 温湿度窗状态:>0 = 窗进行中,值 = 剩余 500ms 拍数 */
static u8 s_th_win_ticks = 0;
/** 窗内提速前的广播间隔,0 = 进窗时本来就是快广播,出窗不还原 */
static u16 s_th_win_interval = 0;
static void usr_adv_encode_reconn(void);
static void usr_adv_encode_th(u16 tank, u8 ver, int16_t temp_x10, u8 humi);
static void usr_th_adv_stop(u8 back_to_reconn);
static void usr_th_adv_tick(void);
/* 定义在 usr_le_recieve.c,库内共用,不对外导出 */ /* 定义在 usr_le_recieve.c,库内共用,不对外导出 */
extern bleproto_authsetup_req_t usr_auth_data; extern bleproto_authsetup_req_t usr_auth_data;
@@ -206,9 +179,8 @@ void usr_ble_adv_init(void)
memcpy(prodcode, g_usr_le_prodcode, BLEPROTO_ADV_TLV_LENGTH_PRODCODE); memcpy(prodcode, g_usr_le_prodcode, BLEPROTO_ADV_TLV_LENGTH_PRODCODE);
memcpy(pcode, g_usr_le_pcode, BLE_PROTO_ADV_LENGTH_PCODE); memcpy(pcode, g_usr_le_pcode, BLE_PROTO_ADV_LENGTH_PCODE);
/* 广播策略重置:清掉温湿度窗(若有),间隔由本函数统一置 80 */ /* 产品温湿度窗清掉;间隔由本函数统一置 80 */
s_th_win_ticks = 0; app_th_adv_reset();
s_th_win_interval = 0;
usr_set_adv_interval(80); usr_set_adv_interval(80);
if (usr_var.usr_ota_succ_flag) if (usr_var.usr_ota_succ_flag)
@@ -247,7 +219,7 @@ void usr_ble_adv_init(void)
usr_ble_adv_updata(); usr_ble_adv_updata();
if (le_timer_handle == 0) if (le_timer_handle == 0)
{ {
le_timer_handle = sys_timer_add(NULL, usr_ble_adv_updata, 500); le_timer_handle = sys_timer_add(NULL, usr_ble_adv_updata, USR_LE_ADV_TICK_MS);
} }
} }
@@ -265,7 +237,7 @@ u8 usr_get_pair_flag(void)
* @note 只组包并置 usr_adv_flag=1,真正下发由 usr_ble_adv_updata 完成 * @note 只组包并置 usr_adv_flag=1,真正下发由 usr_ble_adv_updata 完成
* @return 无 * @return 无
*/ */
static void usr_adv_encode_reconn(void) void usr_ble_adv_encode_reconn(void)
{ {
uint8_t manucode[BLEPROTO_ADV_TLV_LENGTH_MANUCODE] = {0}; uint8_t manucode[BLEPROTO_ADV_TLV_LENGTH_MANUCODE] = {0};
uint8_t prodcode[BLEPROTO_ADV_TLV_LENGTH_PRODCODE]; uint8_t prodcode[BLEPROTO_ADV_TLV_LENGTH_PRODCODE];
@@ -281,163 +253,33 @@ static void usr_adv_encode_reconn(void)
} }
/** /**
* @brief 组自定义温湿度广播包(三段 AD,非见宝 TLV,给 nRF/自研扫描器) * @brief 用调用方组好的 AD 覆盖广播 payload
* @note 整包:02 01 06 / (1+n) 09 "JBao-<tank>" / 06 FF FF FF ver T H * @param data 广播数据
* Company ID 0xFFFF 小端;温度 int8 ℃(向 0 取整,钳位 [-128,127]); * @param len 长度(1~31
* 湿度 uint8 %。不动 GATT 0x2A00 / gap_device_name。
* @param tank 缸体编号(非 0)
* @param ver 配置版本(linkage_config[6]
* @param temp_x10 温度 ×10
* @param humi 湿度 %
* @return 无 * @return 无
*/ */
static void usr_adv_encode_th(u16 tank, u8 ver, int16_t temp_x10, u8 humi) void usr_ble_adv_set_raw(const uint8_t *data, uint8_t len)
{ {
u8 *p = muti_adv_data; if (!data || (len == 0) || (len > USR_ADV_DATA_MAX)) {
char dec[5]; return;
u8 digits = 0;
u16 v = tank;
int32_t t_int;
u8 name_len;
/* 段1 Flags(与见宝一致) */
*p++ = 0x02;
*p++ = USR_AD_TYPE_FLAGS;
*p++ = 0x06;
/* 段2 Complete Local Name "JBao-<tank 十进制无前导零>" */
while (v) {
dec[digits++] = (char)('0' + (v % 10));
v /= 10;
} }
name_len = (u8)(5 + digits); /* "JBao-" + tank 位数 */ memcpy(muti_adv_data, data, len);
*p++ = (u8)(1 + name_len); /* AD Length = Type(1) + Data */ muti_adv_data_len = len;
*p++ = USR_AD_TYPE_LOCAL_NAME;
*p++ = 'J';
*p++ = 'B';
*p++ = 'a';
*p++ = 'o';
*p++ = '-';
while (digits) {
*p++ = (u8)dec[--digits]; /* 反序数组正序发 → 高位在前 */
}
/* 段3 Manufacturer SpecificCompany 0xFFFF(小端) + ver + T(int8) + H */
t_int = temp_x10 / 10; /* C 整除即向 0 取整 */
if (t_int < -128) {
t_int = -128;
}
if (t_int > 127) {
t_int = 127;
}
if (humi > 100) {
humi = 100;
}
*p++ = 0x06; /* Type(1) + Company(2) + ver/T/H(3) */
*p++ = USR_AD_TYPE_MANUFACTURER;
*p++ = 0xFF; /* Company ID 0xFFFF 低字节 */
*p++ = 0xFF; /* Company ID 0xFFFF 高字节 */
*p++ = ver;
*p++ = (u8)t_int;
*p++ = humi;
muti_adv_data_len = (u8)(p - muti_adv_data);
usr_var.usr_adv_flag = 1; usr_var.usr_adv_flag = 1;
} }
/**
* @brief 结束温湿度窗:还原广播间隔,可选切回见宝回连包
* @param back_to_reconn 1=组回连包(窗自然到期用);
* 0=只清窗(配对/OTA/连接切换时包由各自流程组)
* @return 无
*/
static void usr_th_adv_stop(u8 back_to_reconn)
{
if (s_th_win_ticks == 0) {
return;
}
s_th_win_ticks = 0;
if (s_th_win_interval != 0) {
/* 进窗前是慢广播 → 还原;进窗前本来就是快广播则不还原 */
usr_set_adv_interval(s_th_win_interval);
s_th_win_interval = 0;
}
if (back_to_reconn) {
usr_adv_encode_reconn();
log_info("th adv win end, back to reconn\n");
}
}
/**
* @brief 温湿度窗状态机(usr_ble_adv_updata 500ms 拍,任务上下文)
* @note 进窗条件同时满足:未连接 + paired_flag==1 + 非配对 + 非 OTA +
* tank_id!=0 + 本周期 AHT20 采样成功(take 到的才是本次新值)。
* 窗长 500ms × 2;窗内再来采样成功 → 刷新段 3 并重计 2 拍。
* ⚠ app_th 中断侧只置 pending,本函数是唯一消费者。
* @return 无
*/
static void usr_th_adv_tick(void)
{
int16_t t_x10 = 0;
u8 humi = 0;
u8 got;
u16 tank;
/* 窗进行中:先处理强制结束(配对 / OTA / 连接切换 / 清配网) */
if (s_th_win_ticks) {
if (usr_ble_is_connected() || usr_var.usr_goto_pair ||
(usr_auth_data.paired_flag == 0) || usr_var.usr_goto_updata ||
usr_var.usr_ota_succ_flag) {
usr_th_adv_stop(0);
} else if (s_th_win_ticks > 1) {
s_th_win_ticks--;
} else {
/* 最后一拍:必须先 stop(里头要求 ticks!=0),再组回连包。
* 若先减成 0 再 stop,会被 if (ticks==0) return 直接丢掉回连。 */
usr_th_adv_stop(1);
}
}
if (usr_ble_is_connected() || usr_var.usr_goto_pair ||
(usr_auth_data.paired_flag == 0) || usr_var.usr_goto_updata ||
usr_var.usr_ota_succ_flag) {
return;
}
got = app_th_take_sample(&t_x10, &humi);
if (!got) {
return;
}
tank = app_linkage_get_tank_id();
if (tank == 0) {
/* 未配缸体:一直回连,不发温湿度窗 */
return;
}
if (s_th_win_ticks == 0) {
/* 进窗:记下当前间隔并提速(窗内暂停「60s 变慢广播」) */
s_th_win_interval = (usr_get_adv_interval() != USR_TH_ADV_FAST_INTERVAL) ?
usr_get_adv_interval() : 0;
usr_set_adv_interval(USR_TH_ADV_FAST_INTERVAL);
}
/* 进窗 / 窗内刷新:组包(usr_adv_flag=1,下一行刷新块立即下发) */
usr_adv_encode_th(tank, app_linkage_get_cfg_ver(), t_x10, humi);
s_th_win_ticks = USR_TH_ADV_WINDOW_TICKS;
log_info("th adv: tank=%u ver=%u T=%dC H=%u%%\n",
tank, app_linkage_get_cfg_ver(), (int)(t_x10 / 10), humi);
}
/** /**
* @brief 周期处理广播刷新(由 500ms 定时器调用) * @brief 周期处理广播刷新(由 500ms 定时器调用)
* @note 仅当 usr_adv_flag==1 时:停广播 → 写入 muti_adv_data → 再开广播 * @note 仅当 usr_adv_flag==1 时:停广播 → 写入 muti_adv_data → 再开广播
*/ */
void usr_ble_adv_updata(void) void usr_ble_adv_updata(void)
{ {
#if BOARD_BLE_SLOW_ADV_ENABLE
static u16 usr_goto_slow_adv = 0; static u16 usr_goto_slow_adv = 0;
#endif
/* 温湿度窗状态机:先于广播刷新执行,进/出窗当拍立即生效 */ /* 产品温湿度窗:先于广播刷新,进/出窗当拍立即生效 */
usr_th_adv_tick(); app_th_adv_tick();
if (usr_var.usr_adv_flag) if (usr_var.usr_adv_flag)
{ {
@@ -449,24 +291,27 @@ void usr_ble_adv_updata(void)
ble_trans_module_enable(1); ble_trans_module_enable(1);
} }
#if BOARD_BLE_SLOW_ADV_ENABLE
if (!usr_ble_is_connected()) if (!usr_ble_is_connected())
{ {
/* 未连接:回连快广播(80)持续约 60s500ms×120)后切慢广播 /* 未连接:回连快广播(80)持续 BOARD_BLE_SLOW_ADV_SEC 后切慢广播(800
* 温湿度窗内暂停计数(窗内强制快广播,窗长 1s */ * 温湿度窗内暂停计数(窗内强制快广播) */
if (usr_get_adv_interval() == 80) if (usr_get_adv_interval() == 80)
{ {
if ((usr_var.usr_goto_pair == 0) && usr_auth_data.paired_flag) if ((usr_var.usr_goto_pair == 0) && usr_auth_data.paired_flag)
{ {
if ((usr_var.usr_ota_succ_flag == 0) && (s_th_win_ticks == 0)) if ((usr_var.usr_ota_succ_flag == 0) && (app_th_adv_is_active() == 0))
{ {
usr_goto_slow_adv++; usr_goto_slow_adv++;
printf("usr_goto_slow_adv = %d\n", usr_goto_slow_adv);
} }
if (usr_goto_slow_adv >= 120) if ((USR_LE_SLOW_ADV_TICKS > 0) &&
(usr_goto_slow_adv >= USR_LE_SLOW_ADV_TICKS))
{ {
usr_goto_slow_adv = 0; usr_goto_slow_adv = 0;
usr_set_adv_interval(80 * 10); usr_set_adv_interval(80 * 10);
usr_var.usr_adv_flag = 1; usr_var.usr_adv_flag = 1;
printf("ble switch slow adv, after %us\n",
(unsigned)BOARD_BLE_SLOW_ADV_SEC);
} }
} }
} }
@@ -479,6 +324,7 @@ void usr_ble_adv_updata(void)
{ {
usr_goto_slow_adv = 0; usr_goto_slow_adv = 0;
} }
#endif
if (usr_get_ota_status() == 0) if (usr_get_ota_status() == 0)
{ {
+19
View File
@@ -49,6 +49,25 @@ uint8_t usr_get_pair_flag(void);
*/ */
void usr_app_ota_init(void); void usr_app_ota_init(void);
/**
* @brief BLE 是否已连接
* @return 1=已连接,0=未连接
*/
uint8_t usr_ble_is_connected(void);
/**
* @brief 组见宝回连广播包并标记下一拍下发
* @note 只组包,真正写入协议栈由 usr_ble_adv_updata 完成
*/
void usr_ble_adv_encode_reconn(void);
/**
* @brief 用调用方组好的 AD 覆盖广播 payload 并标记下一拍下发
* @param data 广播数据(最长 31 字节)
* @param len 长度
*/
void usr_ble_adv_set_raw(const uint8_t *data, uint8_t len);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
+15 -7
View File
@@ -2,11 +2,12 @@
* @file usr_le_recieve.c * @file usr_le_recieve.c
* @brief BLE 收包入口:bleproto 解析后分发配网 / 对时 / JB 协议 / 组应答 * @brief BLE 收包入口:bleproto 解析后分发配网 / 对时 / JB 协议 / 组应答
* @author cyWu <1917507415@qq.com> * @author cyWu <1917507415@qq.com>
* @date 2026.08.19 * @date 2026.09.07
* @version V1.1.0 * @version V1.2.0
* @history * @history
* - V1.0.0, 2026.08.03, cyWu, 首次发布 * - V1.0.0, 2026.08.03, cyWu, 首次发布
* - V1.1.0, 2026.08.19, cyWu, 对齐模组结构;废弃 JSON OTA;修 AUTHDELETE serviceid * - V1.1.0, 2026.08.19, cyWu, 对齐模组结构;废弃 JSON OTA;修 AUTHDELETE serviceid
* - V1.2.0, 2026.09.07, cyWu, JB 上报超 255 走 wrap_raw 自动分包;加大 txbuf
******************************************************************************/ ******************************************************************************/
#include "system/includes.h" #include "system/includes.h"
@@ -27,15 +28,21 @@
#define LOG_INFO_ENABLE #define LOG_INFO_ENABLE
#include "debug.h" #include "debug.h"
/* GATT 分包:业务包 ≈ bleproto头 + V2 开销 + JB 帧(<=128)1024 足够 */ /* 收:GATT 拼包缓冲。发:V2 + bleproto 多帧(单帧总长 247,与 ATT 对齐) */
#define USR_LE_RX_BUF_LEN 1024 #define USR_LE_RX_BUF_LEN 1024
#define USR_LE_V2_WRAP_OVERHEAD 32u /* V2 头开销JB 帧最大 128 → 约 160 */ #define USR_LE_V2_WRAP_OVERHEAD 32u /* V2 头开销 */
#define USR_LE_V2_MAX_LEN (512u + USR_LE_V2_WRAP_OVERHEAD)
#define USR_LE_APPDATA_PACKETLEN 247u /* 对齐 ATT_LOCAL_PAYLOAD_SIZE8B头+239B数据 */
#define USR_LE_APPDATA_FRAME_PAYLOAD (USR_LE_APPDATA_PACKETLEN - 8u)
#define USR_LE_TX_BUF_LEN (USR_LE_V2_MAX_LEN + \
8u * ((USR_LE_V2_MAX_LEN + USR_LE_APPDATA_FRAME_PAYLOAD - 1u) / \
USR_LE_APPDATA_FRAME_PAYLOAD))
#define USR_LE_V2_CMD_REPORT 100u /* REPORTCMD_V2 内层 cmd */ #define USR_LE_V2_CMD_REPORT 100u /* REPORTCMD_V2 内层 cmd */
#define USR_LE_V2_CMD_RUN 110u /* RUNCMD_V2 内层 cmd */ #define USR_LE_V2_CMD_RUN 110u /* RUNCMD_V2 内层 cmd */
#define USR_LE_JB_CMD_OFF 4u /* 55 AA | LEN_H LEN_L | CMD */ #define USR_LE_JB_CMD_OFF 4u /* 55 AA | LEN_H LEN_L | CMD */
static int encode_len = 0; static int encode_len = 0;
static u8 txbuf[384] = {0}; static u8 txbuf[USR_LE_TX_BUF_LEN];
static u8 rxbuf[USR_LE_RX_BUF_LEN] = {0}; static u8 rxbuf[USR_LE_RX_BUF_LEN] = {0};
static u8 s_last_runcmd_msgid = 0; /* RUNCMD_V2 应答须回填最近 msgid */ static u8 s_last_runcmd_msgid = 0; /* RUNCMD_V2 应答须回填最近 msgid */
@@ -75,7 +82,7 @@ static void usr_le_fill_rsp_header(bleproto_appdata_desc_t *out,
*/ */
static void usr_le_encode_and_send(bleproto_appdata_desc_t *desc) static void usr_le_encode_and_send(bleproto_appdata_desc_t *desc)
{ {
encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), desc); encode_len = bleproto_appdata_encode(txbuf, sizeof(txbuf),USR_LE_APPDATA_PACKETLEN, desc);
if (encode_len > 0) if (encode_len > 0)
{ {
printf("encode_len = %d\n", encode_len); printf("encode_len = %d\n", encode_len);
@@ -365,7 +372,8 @@ void ble_send_data(u8 *data, u16 len)
return; return;
} }
elen = bleproto_appdata_wrap_raw(txbuf, sizeof(txbuf), &hdr, v2_buf, (uint16_t)plen); elen = bleproto_appdata_wrap_raw(txbuf, sizeof(txbuf), USR_LE_APPDATA_PACKETLEN,
&hdr, v2_buf, (uint16_t)plen);
printf("ble_send_data jb_cmd=0x%02X active=%d encode_len=%d msgid=%d\n", printf("ble_send_data jb_cmd=0x%02X active=%d encode_len=%d msgid=%d\n",
jb_cmd, is_active_send, elen, hdr.msgid); jb_cmd, is_active_send, elen, hdr.msgid);
if (elen <= 0) if (elen <= 0)
+32 -2
View File
@@ -2,8 +2,11 @@
* @file board_pin.h * @file board_pin.h
* @brief CBR3 温湿度风扇板级引脚、电平与时序(换板只改本文件) * @brief CBR3 温湿度风扇板级引脚、电平与时序(换板只改本文件)
* @author cyWu <1917507415@qq.com> * @author cyWu <1917507415@qq.com>
* @date 2026.08.29 * @date 2026.09.04
* @version V1.0.0首次发布(P0 Bring-up * @version V1.1.0数码管转机 180°(BOARD_DISP_ROTATE_180
* @history
* - V1.1.0, 2026.09.04, cyWu, 转机后温湿窗口/十个位对调 + 段码旋转
* - V1.0.0, 2026.08.29, cyWu, 首次发布(P0 Bring-up
* *
* @note 产品 GPIO 全部集中到本文件;板级 board_jl701n_demo_cfg.h 只保留 * @note 产品 GPIO 全部集中到本文件;板级 board_jl701n_demo_cfg.h 只保留
* UART0_TX=PG6 一项改动。CT1642 用 PG0(CLK)/PG1(DATA)AHT20 用 * UART0_TX=PG6 一项改动。CT1642 用 PG0(CLK)/PG1(DATA)AHT20 用
@@ -32,6 +35,18 @@
#define BOARD_RUNREC_ENABLE 1 /* 31 天运行记录;P2 开 */ #define BOARD_RUNREC_ENABLE 1 /* 31 天运行记录;P2 开 */
#define BOARD_NVM_ENABLE 1 /* VM 持久化;P1 开 */ #define BOARD_NVM_ENABLE 1 /* VM 持久化;P1 开 */
/*============================================================================*/
/* BLE 广播 */
/*============================================================================*/
/**
* 未连接回连快广播(间隔 80)超时后是否切慢广播(间隔 800)。
* 本机常供电,默认 0:一直快广播,方便网关回连。
* 1=使能;超时秒数见 BOARD_BLE_SLOW_ADV_SECusr_ble_adv_updata 每 500ms 计一次)。
*/
#define BOARD_BLE_SLOW_ADV_ENABLE 0
#define BOARD_BLE_SLOW_ADV_SEC 60 /**< 使能为 1 时:快广播持续秒数后再切慢广播 */
/*============================================================================*/ /*============================================================================*/
/* GPIO */ /* GPIO */
/*============================================================================*/ /*============================================================================*/
@@ -126,11 +141,26 @@
* (bit0 先发 → 落到 Q2 引脚;PCB 上 Q2 接哪段即"软件 A 段"对应的物理段) * (bit0 先发 → 落到 Q2 引脚;PCB 上 Q2 接哪段即"软件 A 段"对应的物理段)
* COM 位选 → 物理管位映射(2026-08-29 SCAN_TEST=2 实测定型): * COM 位选 → 物理管位映射(2026-08-29 SCAN_TEST=2 实测定型):
* C1=湿度十位 C2=湿度个位 C3=温度十位 C4=温度个位(默认假定温/湿相反,已对调) * C1=湿度十位 C2=湿度个位 C3=温度十位 C4=温度个位(默认假定温/湿相反,已对调)
*
* 转机 180°(2026-09-04):整板相对窗口旋转后,视觉上
* ① 每只管子倒过来:A↔D、B↔E、C↔F,G/DP 不动(段码在 bsp_ct1642_send_frame 里重映射)
* ② 温/湿窗口对调,且十位/个位左右对调。
* 未改软件时典型现象:25/57 会看成「L5 / 52」(7 倒过来像 L)。
* 0 = 旧板方向;1 = 转机后的量产方向。
*/ */
#define BOARD_DISP_ROTATE_180 1
#if BOARD_DISP_ROTATE_180
#define BOARD_DISP_COM_TEMP_TEN 0x02 /**< C2:转机后落到温度十位 */
#define BOARD_DISP_COM_TEMP_ONE 0x01 /**< C1:转机后落到温度个位 */
#define BOARD_DISP_COM_HUMI_TEN 0x08 /**< C4:转机后落到湿度十位 */
#define BOARD_DISP_COM_HUMI_ONE 0x04 /**< C3:转机后落到湿度个位 */
#else
#define BOARD_DISP_COM_TEMP_TEN 0x04 /**< C3 = 温度十位(实测定型) */ #define BOARD_DISP_COM_TEMP_TEN 0x04 /**< C3 = 温度十位(实测定型) */
#define BOARD_DISP_COM_TEMP_ONE 0x08 /**< C4 = 温度个位(实测定型) */ #define BOARD_DISP_COM_TEMP_ONE 0x08 /**< C4 = 温度个位(实测定型) */
#define BOARD_DISP_COM_HUMI_TEN 0x01 /**< C1 = 湿度十位(实测定型) */ #define BOARD_DISP_COM_HUMI_TEN 0x01 /**< C1 = 湿度十位(实测定型) */
#define BOARD_DISP_COM_HUMI_ONE 0x02 /**< C2 = 湿度个位(实测定型) */ #define BOARD_DISP_COM_HUMI_ONE 0x02 /**< C2 = 湿度个位(实测定型) */
#endif
/** /**
* 扫段调试模式(P0 台上定型用,2026-08-29 已全部定型,正常固件保持 0): * 扫段调试模式(P0 台上定型用,2026-08-29 已全部定型,正常固件保持 0):
+9 -2
View File
@@ -2,8 +2,11 @@
* @file bsp_ct1642.c * @file bsp_ct1642.c
* @brief CBR3 CT1642 类数码管驱动实现(软件移位) * @brief CBR3 CT1642 类数码管驱动实现(软件移位)
* @author cyWu <1917507415@qq.com> * @author cyWu <1917507415@qq.com>
* @date 2026.08.29 * @date 2026.09.04
* @version V1.0.1IO 分离:CT1642 独立 PG0=CLK/PG1=DATA,无总线共享机制 * @version V1.1.0,转机 180°:段码 A↔D/B↔E/C↔FBOARD_DISP_ROTATE_180
* @history
* - V1.1.0, 2026.09.04, cyWu, 数码管整板旋转 180° 后补偿段码
* - V1.0.1, 2026.08.29, cyWu, IO 分离:CT1642 独立 PG0=CLK/PG1=DATA
* *
* @note 驱动协议严格参照 CT1642 手册(与 CSDN yitx 一致): * @note 驱动协议严格参照 CT1642 手册(与 CSDN yitx 一致):
* 一帧 = 18bitCLK 上升沿采样 DATADATA 只在 CLK=低时改变): * 一帧 = 18bitCLK 上升沿采样 DATADATA 只在 CLK=低时改变):
@@ -143,6 +146,10 @@ static void ct1642_xfer(uint8_t seg, uint8_t com)
*/ */
void bsp_ct1642_send_frame(uint8_t seg, uint8_t com) void bsp_ct1642_send_frame(uint8_t seg, uint8_t com)
{ {
#if BOARD_DISP_ROTATE_180
/* 管倒过来看:视觉 A/B/C 落在物理 D/E/F,视觉 D/E/F 落在物理 A/B/CG/DP 不动 */
seg = (uint8_t)(((seg & 0x07u) << 3) | ((seg & 0x38u) >> 3) | (seg & 0xC0u));
#endif
// ct1642_xfer(0, 0); // ct1642_xfer(0, 0);
ct1642_xfer(seg, com); /* 单帧:18bit 移位 + 锁存 + 输出,无消影预帧 */ ct1642_xfer(seg, com); /* 单帧:18bit 移位 + 锁存 + 输出,无消影预帧 */
} }
+5 -2
View File
@@ -2,8 +2,11 @@
* @file bsp_ct1642.h * @file bsp_ct1642.h
* @brief CBR3 CT1642 类数码管驱动:18bit 移位、锁存、送出 * @brief CBR3 CT1642 类数码管驱动:18bit 移位、锁存、送出
* @author cyWu <1917507415@qq.com> * @author cyWu <1917507415@qq.com>
* @date 2026.08.29 * @date 2026.09.04
* @version V1.0.1IO 分离:CT1642 独立 PG0=CLK/PG1=DATA(飞线),AHT20 * @version V1.1.0,转机 180° 段码补偿(见 BOARD_DISP_ROTATE_180
* @history
* - V1.1.0, 2026.09.04, cyWu, send_frame 内按宏做 A↔D/B↔E/C↔F
* - V1.0.1, 2026.08.29, cyWu, IO 分离:CT1642 独立 PG0=CLK/PG1=DATA(飞线),AHT20
* 独占 PC4/PC5,无总线共享机制 * 独占 PC4/PC5,无总线共享机制
* *
* @note PG0=CLKPG1=DATA,独立引脚、无总线锁,可随时调用。 * @note PG0=CLKPG1=DATA,独立引脚、无总线锁,可随时调用。
+18 -18
View File
@@ -106,7 +106,7 @@
"values": {} "values": {}
}, },
{ {
"id": 13, "id": 7,
"display_name": "定时使能", "display_name": "定时使能",
"key": "time_en", "key": "time_en",
"kind": "normal", "kind": "normal",
@@ -121,7 +121,7 @@
} }
}, },
{ {
"id": 14, "id": 8,
"display_name": "定时配置1", "display_name": "定时配置1",
"key": "time_config1", "key": "time_config1",
"kind": "normal", "kind": "normal",
@@ -133,7 +133,7 @@
"values": {} "values": {}
}, },
{ {
"id": 15, "id": 9,
"display_name": "定时配置2", "display_name": "定时配置2",
"key": "time_config2", "key": "time_config2",
"kind": "normal", "kind": "normal",
@@ -145,7 +145,7 @@
"values": {} "values": {}
}, },
{ {
"id": 16, "id": 10,
"display_name": "定时配置3", "display_name": "定时配置3",
"key": "time_config3", "key": "time_config3",
"kind": "normal", "kind": "normal",
@@ -157,7 +157,7 @@
"values": {} "values": {}
}, },
{ {
"id": 17, "id": 11,
"display_name": "定时配置4", "display_name": "定时配置4",
"key": "time_config4", "key": "time_config4",
"kind": "normal", "kind": "normal",
@@ -169,7 +169,7 @@
"values": {} "values": {}
}, },
{ {
"id": 18, "id": 12,
"display_name": "定时配置5", "display_name": "定时配置5",
"key": "time_config5", "key": "time_config5",
"kind": "normal", "kind": "normal",
@@ -181,7 +181,7 @@
"values": {} "values": {}
}, },
{ {
"id": 19, "id": 13,
"display_name": "定时配置6", "display_name": "定时配置6",
"key": "time_config6", "key": "time_config6",
"kind": "normal", "kind": "normal",
@@ -193,7 +193,7 @@
"values": {} "values": {}
}, },
{ {
"id": 20, "id": 14,
"display_name": "定时配置7", "display_name": "定时配置7",
"key": "time_config7", "key": "time_config7",
"kind": "normal", "kind": "normal",
@@ -205,7 +205,7 @@
"values": {} "values": {}
}, },
{ {
"id": 21, "id": 15,
"display_name": "运行记录", "display_name": "运行记录",
"key": "run_record", "key": "run_record",
"kind": "normal", "kind": "normal",
@@ -217,7 +217,7 @@
"values": {} "values": {}
}, },
{ {
"id": 22, "id": 16,
"display_name": "心跳", "display_name": "心跳",
"key": "heartbeat", "key": "heartbeat",
"kind": "normal", "kind": "normal",
@@ -232,7 +232,7 @@
} }
}, },
{ {
"id": 23, "id": 17,
"display_name": "设备故障使能", "display_name": "设备故障使能",
"key": "fault_en", "key": "fault_en",
"kind": "normal", "kind": "normal",
@@ -244,7 +244,7 @@
"values": {} "values": {}
}, },
{ {
"id": 24, "id": 18,
"display_name": "设备故障", "display_name": "设备故障",
"key": "fault_code", "key": "fault_code",
"kind": "fault", "kind": "fault",
@@ -261,7 +261,7 @@
"level": 2 "level": 2
}, },
{ {
"id": 25, "id": 19,
"display_name": "设备告警使能", "display_name": "设备告警使能",
"key": "alarm_en", "key": "alarm_en",
"kind": "normal", "kind": "normal",
@@ -273,7 +273,7 @@
"values": {} "values": {}
}, },
{ {
"id": 26, "id": 20,
"display_name": "设备告警", "display_name": "设备告警",
"key": "alarm_code", "key": "alarm_code",
"kind": "alarm", "kind": "alarm",
@@ -286,7 +286,7 @@
"level": 1 "level": 1
}, },
{ {
"id": 27, "id": 21,
"display_name": "恢复出厂设置", "display_name": "恢复出厂设置",
"key": "factory_reset", "key": "factory_reset",
"kind": "normal", "kind": "normal",
@@ -300,7 +300,7 @@
} }
}, },
{ {
"id": 28, "id": 22,
"display_name": "预留DP1", "display_name": "预留DP1",
"key": "reserva_dp1", "key": "reserva_dp1",
"kind": "normal", "kind": "normal",
@@ -312,7 +312,7 @@
"values": {} "values": {}
}, },
{ {
"id": 29, "id": 23,
"display_name": "预留DP2", "display_name": "预留DP2",
"key": "reserva_dp2", "key": "reserva_dp2",
"kind": "normal", "kind": "normal",
@@ -324,7 +324,7 @@
"values": {} "values": {}
}, },
{ {
"id": 30, "id": 24,
"display_name": "预留DP3", "display_name": "预留DP3",
"key": "reserva_dp3", "key": "reserva_dp3",
"kind": "normal", "kind": "normal",