Files
JBao_Heat_Lamp_FW/Doc/电子开关-加热灯罩_固件需求规格书.md
T

36 KiB
Raw Blame History

电子开关-加热灯罩固件需求规格书

内容
产品 电子开关-加热灯罩(爬宠)
芯片 JL7016 / AC7016CBR28
工程 电子开关-加热灯罩_soc_JieLi-JL7016
产品码 ProdtCode=CBR0productCode=PRCBR0
协议 见宝 SOC 55AAV1.0.0
文档版本 V1.0.0
日期 2026.09.08
作者 cyWu
读者 WorkBuddy / 固件开发
参考工程 D:\Jianbao\Project\产品\爬宠\电子开关-加热垫\电子开关-加热垫_soc_JieLi-JL7016(业务层 / 过零继电器 / 联动仲裁 / 定时 / 运行记录 / 按键灯效)
框架参考 D:\Jianbao\Project\产品\爬宠\温湿度风扇\温湿度风扇_soc_JieLi-JL701620ms tick + 500ms loop
灯效参考 D:\Jianbao\Project\产品\宠物\门夹弹力绳\门夹弹力绳_soc_JieLi-JL7016(红绿双通道、OTA 红绿交替)
加热垫规格 电子开关-加热垫_固件需求规格书.md V1.0.1(同板行为以加热垫为准,本页只写灯罩差量与 DP 重编号)

V1.0.02026-09-08): 首版共同理解。硬件、过零继电器、短按、联动、定时开停、31 天运行记录、故障码 2、出厂复位 与加热垫相同。差量:灯泡剩余寿命(额定 2,592,000 秒,按实际吸合每秒减 1,到 0 报粘性告警 1,灯仍可亮);DP9 当前温度改为 WO;定时配置 11 字节且无模式字节(使能/类型/星期/开始结束时分,Byte7–10 预留);DP2 总工作时长本版恒 0。

本文是 已拍板的共同理解,不是草稿。实现时以本文为准;DP 表以 dp_definition.json + 本文第 10.2 / 11.1 / 13.1 / 13.2 / 15 章为准。与加热垫冲突时:引脚、过零、仲裁、联动回差跟加热垫;DP 号、定时 11 字节、寿命跟本文。


0. WorkBuddy 开干前必读(硬约束)

  1. 业务层从加热垫迁,不要从零写。apps/usr_appapps/usr_periphusr_le_code 桥接、usr_jb_main.c 编排,再按本文改 DP 号、定时 11 字节、寿命模块。程序框架必须和加热垫 / 风扇一致:20ms usr_timer_tick(硬件定时器中断)+ 500ms usr_timer_loop(BLE 任务上下文)。配对/OTA/写 VM 只准在任务上下文落地。禁止在 20ms ISR 里 syscfg_write、切 BLE 广播、跑联动/定时/寿命告警上报。
  2. 不要把业务算法写进 jb_product.c 只把 DP 写入 gDevData 并转调 app_*。寿命扣秒、复位、告警放 app_light_life
  3. 不要抄门夹的电池 / 低电 / 深睡开机判定。 本机 220V 常电、无电池。上电即开机;拔电即关,无软关机。
  4. 不要抄风扇的 PWM 风扇 / 数码管 / AHT20 / 通风强力。 执行器是继电器 + 过零;温度来自扫描或 DP9,不是本机传感器。
  5. MAX_PACKAGE_LEN 必须为 512。 生成值 128 装不下 DP20 run_record(124B),11×7 定时 + 寿命 DP 全量一包也会超过 256。禁止再生成回 128。
  6. DP9 current_temp 为 WO。 生成 JSON 是 RO,必须改成 WO。已连接时服务器下发;设备不上报;断连扫描到的温度不填 DP9。
  7. 本版写忽略: DP1 温控开关、DP2 总工作时长(上报恒 0)、DP5 温控时间、DP6 模式、DP7/8 最低最高温度。DP21 心跳忽略。
  8. 每个模块必须能用 board_pin.hBOARD_xxx_ENABLE 单独关掉 仍能编过、能配网上报。寿命用 BOARD_LIGHT_LIFE_ENABLE
  9. 日志全部 英文。中文只出现在注释和本文。
  10. 编码风格对齐加热垫 + 工程师规范:文件小写+下划线,g_ / s_ 前缀,枚举 Pascal_t,接口 xxx_init / xxx_run 或 tick / xxx_get_status
  11. 现有工程是协议骨架。 没有 apps/usr_app、没有 board_pin.h、没有 usr_le_code 实现。app_main.c 里若存在上电强制 usr_goto_pair=1 必须删掉,改为上电进工作态(已配对走重连广播)。
  12. 调试 UART TX = PC5。关掉会与 PA1 / PA7 / PA8 / PB1 / PC3 冲突的 demo 外设。
  13. 遵守第 0.1 节编码与注释。 散落 static、把排障过程复制进每个函数、只写不读的计数,一律视为未完成。
  14. 定时 11 字节布局与加热垫 8 字节不兼容。 禁止把加热垫 cfg[2]=类型、cfg[4]=开始小时 原样拿来用。必须按第 11.1 节:Byte1=类型,Byte3=开始小时。
  15. 协议字段名 保持 lighe_remain_time(生成拼写),不要改成 light_remain_time

0.1 编码与注释(禁止再犯)

新产品模块(apps/usr_app/*apps/usr_periph/*usr_jb_main.c)必须遵守。从加热垫 / 风扇 / 门夹 原样拷贝key.c / led.c 框架不要借机大改。

  1. 一个模块一个上下文。 两个及以上的模块状态变量必须收进 XxxCtx_t,文件内只留 static XxxCtx_t s_xxx。ISR 与主循环共享的成员在结构体里标 volatile。单个标志可以单独 static(如 app_defer 的位图)。
  2. 禁止一排裸 static。 反例:s_edge_cbs_edge_cnts_rise_cnt 并列。正例:static LightLifeCtx_t s_life
  3. 不要只写不读的诊断变量。 需要看沿是否在跑,用已有 getter + 一条日志。
  4. 文件头三件事:做什么、一两行硬约束、history。 同一条约束只写一次。
  5. 函数注释短。 @brief 一行;@param / @return 按需;@note 只写本函数独特约束。不要复述代码。
  6. 注释解释为什么,不写排障日记。 「必须等过零再切继电器」是为什么;「我改了三次 ISR 才好」不要进源码。
  7. P0 路径不堆死字段。 没有 getter、没有日志、P2 也没用上的字段不要先占着。

1. 产品一句话

220V 常供电的爬宠加热灯罩:过零点吸合/断开继电器(避免打火),本地短按开关,BLE 连 APP。APP 可下发灯泡开关、1 条缸体联动配置、7 组定时。连着时用服务器下发的当前温度做联动;断连时扫描风扇的 JBao-{缸体号} 广播取温度。灯泡实际吸合的秒数记 31 天运行记录,并从额定 30 天工时里扣除剩余寿命;扣到 0 上报到期告警,灯仍可继续亮。


2. 硬件资源

与加热垫 同一块电子开关板、同一套引脚和灯效,负载从加热垫换成灯泡。

2.1 引脚(以用户原理图说明 + 本文为准)

功能 MCU 电平 / 说明
按键 PB1 IO_PORTB_01 低电平按下(若实测相反只改 BOARD_KEY_ACTIVE_LEVEL
过零检测 PA1 IO_PORTA_01 过零时跳变;上升沿和下降沿都算一次过零
绿灯 PA8 IO_PORTA_08 低电平亮
红灯 PA7 IO_PORTA_07 低电平亮
继电器 PC3 IO_PORTC_03 高电平导通(灯泡开)
调试 UART TX PC5 IO_PORTC_05 只发打印
供电 220V 市电 → 隔离电源 3.3V 无电池、无充电管理

板级文件仍指向 CONFIG_BOARD_JL701N_DEMO。产品 GPIO 全部集中到 apps/usr_periph/board_pin.h,并改 board_jl701n_demo_cfg.h

#define TCFG_UART0_TX_PORT    IO_PORTC_05

关掉与产品脚冲突的 demo 外设(IR 键占 PA8、PWM LED 等)。产品灯走 app_led,保持 TCFG_PWMLED_ENABLE=DISABLE

2.2 过零与继电器安全

  • 市电 50Hz:半波 10ms,双沿即约每 10ms 一次过零。
  • 开和关都必须等下一次过零沿再改 PC3,禁止在过零窗口外切继电器(打火)。
  • GPIO 上电默认:继电器关(PC3=0,再按 VM 恢复请求;若要开,等过零再吸合。
  • 过零 ISR 只允许:置沿标志 +(若有 pending)改继电器 GPIO。禁止写 VM / BLE / printf。
  • 超时见第 7.2 节。

3. 软件架构(迁加热垫,加寿命模块,改 DP 号)

apps/earphone/app_main.c
  └─ usr_rtc_init()
  └─ usr_jb_init()                    ← 唯一组合根
        bsp_hw_init()
        app_led_init()                ← 灯效事件表最先装载
        jbInit() + userInit()
        (上电即开机:无按键等待)
        app_relay_init() / app_zc_init()
        app_arbiter_init()
        app_linkage_init() / app_timer_init() / app_runrec_init()
        app_light_life_init()         ← 灯罩新增
        app_th_scan_init()
        app_nvm_init() + app_nvm_load()
        app_function_init()
        user_key_init() + lock_if_held
        usr_timer_add(usr_timer_tick, 20ms, priority=1)   ← 常驻,永不重挂

20ms 业务节拍usr_timer_tick,硬件定时器中断上下文,禁止再开 1ms ISR):

  1. jbTimerIrq() × dt
  2. user_key_scan + user_key_handle
  3. app_zc_poll(过零超时 100ms 判定;真正切 GPIO 在过零 ISR)
  4. app_function_poll(同步 DP0 = 实际继电器态)
  5. app_runrec_tick(继电器已吸合则累加秒;满 10 分钟只打脏,不写 flash)
  6. app_light_life_tick(已吸合且剩余 >0 则扣秒;到 0 只打脏 + 置告警,不写 flash)
  7. app_nvm_tick(变化检测 + 500ms 防抖计时,不写 flash
  8. app_led_run

usr_timer_loop()BLE 500ms 任务上下文):userHandle()jbProtocolHandle()app_th_scan_tick()app_linkage_run() / app_timer_run()app_runrec_rotate_if_needed()app_light_life_loop()(1 分钟节流上报 + 边沿立刻报)、 app_function_loop()(VM 落盘 + 配对/OTA 落地)。

从加热垫拷贝后改:

复用 路径 改什么
按键 / 配对 / 延后 / LED 加热垫 key* / app_pair / app_defer / led* / app_led 短按仍切加热(DP0 灯泡开关);灯效不变
继电器 / 过零 app_relay / app_zc / bsp_zc 原样;fault 走 DP23
联动 / 仲裁 app_linkage / app_arbiter 温度 DP 改为 DP9;联动配置改为 DP11;删除改为 DP10
定时 app_timer_task 按第 11.1 重写 11 字节解析time_en 改为 DP12
运行记录 app_runrec DP 改为 DP20;口径仍是实际吸合秒
NVM app_nvm 增加剩余寿命;定时每组 11B;magic 用灯罩自己的
协议 usr_jb_proto 已生成 30 DP;改 MAX_PACKAGE_LEN=512、DP9=WO、DP3 节流上报
寿命 新建 app_light_life 第 13.2 节

不要拷贝: app_battery.capp_boot.capp_power.capp_fan.capp_disp.capp_th.capp_mode.capp_th_adv.c


4. 模块开关(独立使能 / 屏蔽调试)

全部放 apps/usr_periph/board_pin.h0 时:对应 init/run 直接 return,GPIO 不配,日志打一次 xxx disabled

#define BOARD_LED_ENABLE          1
#define BOARD_KEY_ENABLE          1
#define BOARD_RELAY_ENABLE        1
#define BOARD_ZC_ENABLE           1   /* 过零;=0 时继电器请求仍超时路径强制关,禁止无过零硬开 */
#define BOARD_LINKAGE_ENABLE      1
#define BOARD_TIMER_ENABLE        1
#define BOARD_TH_SCAN_ENABLE      1
#define BOARD_RUNREC_ENABLE       1
#define BOARD_LIGHT_LIFE_ENABLE   1   /* 灯罩新增;=0 时 DP3 仍按 VM/默认上报,不扣秒、不置告警 */
#define BOARD_NVM_ENABLE          1

P0 建议先 LINKAGE/TIMER/TH_SCAN/RUNREC/LIGHT_LIFE/NVM=0,台上只验按键、双灯、过零继电器。


5. 电源与按键

5.1 上电 / 断电

  • 上电直接进入工作态,无需按键。流程对齐加热垫 5.1。
  • 关机 = 拔电。 无软关机、无深睡开机判定。
  • 上电若键仍按着:只锁短按(防一上电就切灯泡),不锁 5s/12s。
  • 设备工作态恒开机。DP0 是 灯泡开关(不是设备电源)。
  • 无电池、无充电逻辑。
  • 冷启动继电器: GPIO 先保持关;加载 VM 后按上次 heat_switch 恢复请求;若为开则等过零再吸合。温度没到之前联动不改它(第 13 章)。
  • 冷启动寿命: 见第 13.2。VM 无效 → 剩余寿命 2,592,000,告警 0。

5.2 按住分级(运行态,与加热垫同一套时序)

按住时长 行为
< 5000 ms 松开 短按:切换灯泡(见 5.3
满 5000 ms(仍按住) 切配对提示灯(绿灯 100ms 快闪),不清绑定、不碰寿命
满 5s 后、12s 前松开 提交配对:usr_goto_pair_mode() + app_pair_start() 120s
满 12000 ms(到点即触发) 进 OTA,不等松手;同时撤销本次配对

⚠ 20ms ISR 只准置标志 + 切灯效。清绑定与 usr_app_ota_init 延后到 app_function_loop()

5.3 开机态按键

事件 行为
短按(<5s 松开) 切换加热请求(开↔关),走手动抢权(第 12 章),同步 DP0
按住 5s 后松开 清绑定 + 进配对(不复位寿命
按住 12s 进 OTA

OTA 进行中:忽略短按和再次 5s/12s。配对进行中:短按仍可切灯泡。


6. LED(红绿双灯,低亮)

灯泡开 = 红灯亮绿灯灭;灯泡关 = 红灯灭绿灯亮。灯表示 继电器实际状态(过零完成之后),不是 pending 请求。到期告警 不改灯效

事件 图案 优先级(高者覆盖)
OTA 红绿交替,200ms 半周期,直到升级结束/复位 最高
按住满 5s / 配对中 绿灯 100ms 亮 / 100ms 灭(红灯灭)
配对成功 可保持绿灯常亮 3s,再回到「跟继电器」
配对失败 红绿交替 3s,然后回到「跟继电器」
灯泡开(实际吸合) 红灯常亮,绿灯灭
灯泡关 绿灯常亮,红灯灭

优先级高→低:OTA → 配对/5s 将配网/配对结果 → 平时跟继电器


7. 继电器 + 过零

与加热垫第 7 章相同,仅 DP 号改为故障走 DP23

7.1 接口

  • app_relay_init / app_relay_request(uint8_t on) / app_relay_get_actual() / app_relay_get_request()
  • on=1pending 开,等过零后 PC3=1。
  • on=0pending 关,等过零后 PC3=0。
  • DP0 上报 实际吸合状态app_relay_get_actual()),让 APP 看见过零完成。

7.2 过零超时(100ms

双沿都算过零。从 pending 置位起:

请求 100ms 内无过零 行为
强制 PC3=0 fault_code=2(设备故障)
不吸合,保持关 清 pending 开,fault_code=2

BOARD_ZC_ENABLE=0:禁止走「无过零硬开」;关请求仍允许强制断开(安全)。

过零恢复后:新的请求仍按第 7.1 走;fault_code 在下一次成功过零切变后清 0。

寿命扣秒与运行记录都看 实际吸合。开请求超时未吸合 → 不扣寿命、不记运行记录


8. 温度来源(无本机传感器)

内部统一用 temp_x10(0.1℃,有符号)。联动比较见 10.3。

场景 温度从哪来 扫描 DP9
BLE 已连接 服务器写 DP9int16×0.1 → float WO 收下,不存 VM,不上报
BLE 断连且已配对 扫描风扇自定义广播 500ms 扫描 / 500ms 重连广播 交替 扫描值 不写 DP9
未配对 / 配对中 / OTA 不扫描 重连或绑定/OTA 广播

湿度:广播里有,本版 不参与开停,只可打日志。

8.1 风扇侧广播格式(只读约定,灯罩不发这种包)

与加热垫 8.1 相同:

  • Complete Local NameJBao-{tank_id}(十进制,无前导零)
  • Manufacturer 0xFFFF 后 3 字节:[配置版本 uint8][温度 int8 ℃][湿度 uint8 %]

匹配规则:

  1. 名字等于 JBao- + 本机 DP11 缸体编号(uint16 十进制)。
  2. 配置版本等于 DP11 Byte6。
  3. 两者都满足才更新 temp_x10 = 温度int8 * 10
  4. 名字不对、版本不对、解析失败:当没收到,保持上一温度和上一加热态。

缸体编号为 0:视为无有效联动,不扫描匹配。

8.2 500ms 时隙

与加热垫 8.2 相同。温度丢失:保持上一态,不因丢包关灯。


9. 本版不用的模式 / 温控 / 总时长 DP

DP 本版
1 温控开关 写忽略
2 灯泡总工作时长 写忽略,上报恒 0(不累计)
5 温控时间 写忽略
6 模式 写忽略
7 / 8 最低最高温度 写忽略(阈值只在 DP11 联动 20B 里)
21 心跳 写忽略

无通风/强力状态机,无 app_mode。定时配置里 没有模式字节(第 11.1)。


10. 联动引擎

设备只有 1 条 20 字节联动配置(DP11)。不改 DP6。

10.1 有效配置

同时满足才跑联动开停:

  • 缸体编号 ≠ 0Byte01uint16 大端)
  • Tmin ≤ Tmax
  • BOARD_LINKAGE_ENABLE=1

否则视为无联动:不自动开;若原先是联动占着加热则关掉。

湿度 min/max、配置版本:整包 20B 原样落盘。湿度 不参与开停。版本只用于扫描匹配(8.1)。Byte7–19 预留,逻辑忽略。

10.2 20 字节布局(DP11 linkage_config

偏移 字段 编码
Byte01 缸体编号 uint16 大端
Byte2 最低温度 Tmin 有符号 int8,单位 ℃
Byte3 最高温度 Tmax 有符号 int8,单位 ℃
Byte4 最低湿度 Hmin uint8 %,只存不用
Byte5 最高湿度 Hmax uint8 %,只存不用
Byte6 配置版本 uint8,扫描匹配用
Byte719 预留 只存不用

比较用 temp_x10T < Tmintemp_x10 < Tmin * 10T > Tmaxtemp_x10 > Tmax * 10等于不算越界,走「中间维持上一态」。

已连接:temp_x10 = (int16_t)(gDevData.current_temp * 10)。断连扫描:int8 ℃ × 10。尚无任何有效温度:联动不改继电器(保持上电恢复值 / 上一态)。

10.3 开停(只看温度)

在有效配置、未被手动作废、仲裁允许联动、且已有有效温度时,每个 500ms 任务 tick 判一次:

条件 动作
T < Tmin 请求开灯,不改其它 DP
T > Tmax 请求关灯
Tmin ≤ T ≤ Tmax(含等于) 保持上一态

过热关、过冷开。到期告警 不阻止 本表。

10.4 联动删除(DP10

写入 行为
0 无操作
1 清 RAM + VM 里 20 字节;若当前是联动占着加热 → 关;然后把 linkage_delete 清回 0 再上报(脉冲)

10.5 APP 重写 DP11

memcpy 20 字节 → 存 VM → 清手动作废标志 → 立刻按新阈值重判(有有效温度时)。缸体编号写成 0:按无效配置,联动占着则关。


11. 定时引擎(P2,7 组,11 字节)

行为与加热垫相同:窗开始开、窗结束关。字节布局 不是 加热垫的 8 字节(加热垫 Byte1=模式、Byte2=类型)。本产品无模式字段。

11.1 11 字节(DP1319

偏移 字段 本版
Byte0 使能
Byte1 类型:0=单次,1=每天,2=每周
Byte2 星期位图 Bit0=周一 … Bit6=周日(仅类型 2)
Byte3 开始小时 0~23
Byte4 开始分钟 0~59
Byte5 结束小时
Byte6 结束分钟
Byte710 预留 原样存 VM,不判断
  • 窗开始沿:请求 继电器。
  • 窗结束沿:请求 继电器,然后立刻按第 10.3 判一次联动。
  • 窗内保持开(直到被手动打断,见第 12 章)。
  • 开始==结束:非法窗,不执行。
  • 支持跨午夜:22:00→06:00 = 当晚 22 点到次日 6 点。
  • 类型 1:每天该窗。
  • 类型 2:仅位图置位的星期;跨午夜以 开始时刻所在的本地星期 判断。
  • 类型 0 单次:结束时刻过后把 Byte0 清 0 并写 VM
  • 多组重叠:任一窗内即开;所有窗都结束才走结束关。
  • 整 11 字节入库、回读(含预留)。

11.2 RTC 门闩(强制,与加热垫相同)

usr_rtc_get_time() 失败  →  全部定时不执行(等同 time_en=0)
仅 BLE 已连接但从未对时 →  同上
对时成功瞬间            →  立刻补判:若当前落在某条有效窗内,进入该窗(开灯)

无电池,拔电后 RTC 丢失。开机后必须等 APP/网关 usr_rtc_set_from_unix 成功。

time_en=0(DP12):所有窗立刻视为结束(关灯),再按 10.3 补判联动。


12. 仲裁(唯一决策表)

与加热垫第 12 章相同。优先级 高 → 低

  1. 手动:短按、APP 写 DP0
  2. 定时窗RTC 有效且 time_en=1 且当前落在窗内)
  3. 联动(有效配置且本 tick 满足开/关条件)
冲突 胜者 副作用
手动 vs 正在跑的定时 / 联动 手动 若原是联动占着 → 本轮联动作废RAM,不写 VM
手动 vs 定时窗 手动 本窗剩余时间不再自动抢回;等到下一窗开始沿或下次对时补窗
定时窗 vs 联动 定时 窗内联动不开、不关;窗结束收口后立刻按 10.3 重判
联动 vs 空闲 联动 开则请求开,关则请求关

作废标志不存 VM。

手动作废后的再武装: 必须先走到第 10.3 的「关」条件(T > Tmax),再重新出现 T < Tmin,才允许联动再开。作废期间丢广播仍保持上一态。

冷启动: 作废标志是 RAM,上电为空。VM 恢复的加热态先执行;有有效温度且无定时窗占用时,允许联动按 10.3 立刻改。

time_en 写成 0:立刻停定时(窗结束语义)。无联动总开关;无效配置 / DP10 删除即停联动。

寿命到期不参与仲裁。 到期只改 alarm_code,不强制关灯、不阻止手动/定时/联动再开。


13. 持久化(VM

存(冷启动保留):

  • heat_switch(上次加热请求;上电按此恢复)
  • linkage_config20B
  • time_entime_config1~7(各 11B,含 Byte710 预留)
  • fault_enalarm_en(若有写入)
  • run_record31 × uint32 秒 + slot[0] 对应的本地日期
  • lighe_remain_time(剩余寿命秒)

不存:

  • DP1、DP2(恒 0)、DP5–8、DP9 当前温度、联动作废标志、过零 pending
  • linkage_deletelight_reset(脉冲 / WO
  • alarm_code(上电由剩余寿命推导,见 13.2
  • BLE 三元组(CFG_USER_PAIR_INFO,出厂重置 不清
  • DP2729 预留(RAM

上电:加载上述字段,继电器按上次 heat_switch 恢复(开则等过零);按第 13.2 恢复寿命与告警;再让联动按第 12 章冷启动规则判。

首次上电(VM 无效):加热关,联动 20B 全 0,定时全 0,time_en=031 天运行记录全 0剩余寿命 = 2,592,000,告警 0。

写 VM 时机:

  • APP 改联动/定时、单次定时自毁、加热稳定变化:500ms 防抖
  • 运行记录:累计吸合满 10 分钟 写一次;跨天旋转时立刻写
  • 剩余寿命:累计吸合满 10 分钟 写一次(可与运行记录同一次);下列边沿立刻写(第 13.2

VM magic 必须用灯罩自己的值(建议 0xCB00),禁止复用加热垫 0xCB01,避免两套固件互刷把 8 字节定时当 11 字节解析。

13.1 运行记录(DP20 run_recordRO124B

31 天 × 4 字节,每格 uint32 大端,单位秒:

偏移 含义
Byte03 今天灯泡运行总秒数
Byte47 昨天
Byte120123 前 30 天

什么叫在运行: 过零后继电器 实际已吸合app_relay_get_actual()==1)。pending 开但还没过零 不算。手动 / 定时 / 联动只要真正吸合都算。到期后只要还在亮,继续累计

日期轴: 未对时只往 slot[0] 加、不旋转;对时后按本地日历左移;掉电再上电先灌 VM。

落盘: 每累计运行满 600 秒写一次 VM。跨天立刻写。拔电接受最多丢不满 10 分钟的秒数。

BOARD_RUNREC_ENABLE=0:不累加、DP20 报全 0。

13.2 灯泡剩余寿命(DP2 / DP3 / DP4 / DP25

额定工时,不是日历:

#define BOARD_LIGHT_RATED_SEC     2592000u  /* 30 * 24 * 3600 */
规则 约定
扣秒条件 app_relay_get_actual()==1 且剩余 > 0,与运行记录同一口径
减到 0 停在 0,不回绕;alarm_code = 1(灯泡到期告警)粘住
到期后加热 不互锁,开停仍走第 7 / 10 / 11 / 12 章
告警推导 remain == 0alarm_code = 1,否则 0alarm_en 不屏蔽
DP2 写忽略,上报恒 0
DP3 写 立刻生效,范围 04,294,967,295,不封顶
DP4 写 1 剩余寿命 = 2,592,000;写 0 无动作;WO 不上报
按键 5s 清绑定 不碰寿命
出厂复位 DP26 剩余寿命回到 2,592,000,告警 0(与第一次开机相同)

边沿副作用(必须同一套): APP 写 DP3、DP4=1、扣到 0、出厂复位:

  1. gDevData.lighe_remain_time
  2. 按值置/清 alarm_code
  3. 立刻写 Flash(任务上下文,禁止 ISR
  4. 立刻上报 DP3 与 DP25(不受 1 分钟节流)

平时写 Flash 约 600 秒实际点灯写一次(允许丢最后几十秒~几分钟)。边沿必须立刻写,避免掉电把已到期「复活」,或 APP 刚改完寿命就丢。

DP3 上报节流:

  • 实际吸合且剩余 >0:距上次 DP3 上报满 墙上 60 秒 才再走变化上报
  • 到期、DP4 复位、APP 写 DP3、读请求、10 分钟全量:立刻带上 DP3
  • 其它 DP 仍 2 秒变化防抖;DP25 随告警边沿立刻报

BOARD_LIGHT_LIFE_ENABLE=0:不扣秒、不因寿命置告警;DP3 若 VM 里有值仍上报,否则 0。

上电加载剩余寿命后立刻按上表推导 alarm_code(不单独存告警)。


14. 出厂重置(DP26 写 1

  • 清全部业务 VM(第 13 章列表,含联动 20B、31 天运行记录、剩余寿命)。
  • RAM:加热关,联动/定时停止,作废标志清掉,剩余寿命 = 2,592,000,告警 0,立刻写 VM 并上报。
  • 保留 BLE 配对,不断开、不软复位。
  • 换机:运行中 按住 5s 后松手 清配对进绑定广播(见 5.2),不复位寿命

15. BLE / DP 行为摘要

身份:CBR0 / PRCBR0。帧格式见 jb_protocol.hMAX_PACKAGE_LEN=512)。DP 共 30 个(ID 029)。

DP Key 处理
0 heat_switch 写则手动抢权,请求开/关(等过零)。上报实际吸合。显示名「灯泡开关」
1 temp_control_switch 本版写忽略
2 light_work_time 本版写忽略,上报恒 0
3 lighe_remain_time 第 13.2 节。RW;固件扣秒;APP 写立刻生效
4 light_reset WO。写 1 把 DP3 置 2,592,000;不上报
5 temp_control_time 本版写忽略
6 mode 本版写忽略
7 / 8 min_temp / max_temp 本版写忽略
9 current_temp WO。已连接时服务器下发 → 更新联动用温度。不上报。断连扫描不填
10 linkage_delete 第 10.4 节,脉冲
11 linkage_config 20 字节 memcpy + 存 VM + 第 10.5 节立刻重判
12 time_en 见第 11 章
1319 time_config1~7 11 字节 + 存 VM;按第 11.1 解析
20 run_record RO,由 app_runrec 填 124B,禁止 APP 写
21 heartbeat 本版忽略
22 fault_en 原样存,本版不做位级过滤
23 fault_code 0 无 / 1 温度异常(本版不上报) / 2 过零超时等设备故障
24 alarm_en 原样存,不屏蔽告警
25 alarm_code 0 无 / 1 灯泡到期告警(由剩余寿命推导)
26 factory_reset 第 14 章
2729 reserva_dp1~3 存 RAM,无动作

OTA:运行态按住 12s 走 usr_app_ota_init(任务上下文,红绿交替)。协议 OTA(jbOnOtaNotify可继续 stub,禁止假装升级成功后擦用户区。

配对超时 120s,失败保持开机,灯回「跟继电器」。

断连已配对:500ms 见宝重连广播 ↔ 500ms 扫描(第 8.2)。绑定广播仍用 CBR0 / PRCBR0,不要改成 JBao- 名。

生成协议必须改动(相对当前骨架):

  1. MAX_PACKAGE_LEN 128 → 512
  2. DP9 current_temp RO → WOpack/diff 排除 DP9
  3. DP3 变化上报加 60s 节流(边沿除外)
  4. dp_definition.json 同步:DP9 dir=wo;定时 11 字节描述改成第 11.1 节表

16. 建议文件清单(从加热垫拷贝改 + 新建寿命)

apps/usr_periph/
  board_pin.h          # 引脚、时序、BOARD_xxx_ENABLE(含 LIGHT_LIFE
  bsp_hw.c/.h          # LED 双通道 / KEY / 继电器 GPIO
  bsp_zc.c/.h          # PA1 双沿中断
  usr_rtc.c/.h         # 已有则保留

apps/usr_app/
  key.c/.h, key_manager.c/.h
  led.c/.h, led_manager.c/.h
  app_led.c/.h
  app_defer.c/.h
  app_pair.c/.h
  app_relay.c/.h
  app_th_scan.c/.h     # 缸体号改读 DP11
  app_linkage.c/.h     # 温度改 DP9,配置改 DP11
  app_runrec.c/.h      # 上报 DP20
  app_timer_task.c/.h  # 第 11.1 节 11 字节
  app_light_life.c/.h  # 新建,第 13.2 节
  app_nvm.c/.h         # magic 0xCB0011B 定时;剩余寿命
  app_function.c/.h    # 出厂复位顺带复位寿命
  app_arbiter.c/.h

usr_jb_main.c 只编排,不写算法。禁止引入 app_fan / app_disp / app_th / app_mode


17. 分阶段交付与验收

P0 — 台上 Bring-up(不依赖 APP

与加热垫 P0 相同(按键、双灯、过零继电器、5s/12s、删上电强制配对)。

P0 关闭: BOARD_LINKAGE/TIMER/TH_SCAN/RUNREC/LIGHT_LIFE/NVM_ENABLE=0

P1 — 最小产品闭环

  • 5s 配对,APP 能写 DP0 开关灯泡(过零后灯和继电器一致,DP0 报实际态)
  • 短按同步上报 DP0
  • VM:改开关后断电,重新上电按上次开关恢复(开则等过零再吸合)
  • DP1 / DP2 / DP58 写忽略;DP2 上报 0
  • DP9 WO:APP/服务器能写下当前温度;设备全量上报 不含 DP9
  • 定时 7 组 11 字节 + time_en 能存;BOARD_TIMER_ENABLE=0 时不执行
  • MAX_PACKAGE_LEN=512,APP 能读完整 124B 运行记录(本阶段记录可仍为 0)
  • 第一次开机 DP3 = 2,592,000DP25 = 0

P2 — 自动化 + 寿命

  • 断连:500ms 重连广播 / 500ms 扫描;已连接停扫描
  • 匹配 JBao-{缸体号} 且版本相同才更新温度;湿度不参与开停
  • 联动:T<Tmin 开,T>Tmax 关,中间保持;无匹配包保持上一态
  • 缸体号 0 / min>max 不跑联动
  • 手动作废后须先 T>TmaxT<Tmin 才再开
  • 定时 RTC 门闩:未对时绝不跑;对时后补窗
  • 跨午夜、单次自毁、每天/每周、多组重叠
  • 定时窗开始开、结束关,结束立刻把联动判一次;按 11.1 解析,不是加热垫 8 字节
  • DP10 脉冲删除;DP11 重写立刻重判
  • 运行记录:实际吸合 10 分钟落盘;对时后跨天左移
  • 出厂重置清业务留配对,寿命回到 2,592,000
  • 过零超时 fault_code=2DP23
  • 仲裁表第 12 章逐条用例(与加热垫相同,DP 号按本文)
  • 寿命: 实际吸合才扣;到 0 停在 0alarm_code=1,灯仍可亮
  • DP4=1 剩余寿命回到 2,592,000,告警清 0,立刻上报
  • APP 写 DP3 任意 uint32 生效;>0 清告警,=0 置告警;立刻落盘
  • 5s 清绑定不改寿命
  • 点灯时 DP3 最多 1 分钟报一次;到期/复位/写 DP3 立刻报
  • 掉电再上电:未到期接着扣;已到期仍为 0 且告警 1

P2 仲裁最小用例(必须写进测试笔记)

与加热垫第 17 章用例 1–9 相同,把「加热」读成「灯泡」、DP6 读成 DP9。额外:

  1. 剩余寿命置 5,实际吸合 5 秒 → DP3=0、DP25=1,继电器保持开(若请求仍为开)。
  2. 用例 10 之后 DP4 写 1 → DP3=2,592,000、DP25=0。
  3. APP 写 DP3=100 后立刻拔电再上电 → 仍约 100(允许未满边沿的秒误差,不允许回到 2,592,000)。
  4. 定时 11 字节:Byte1=1(每天)、Byte36=时分,Byte0=1 → 到点开;若误用加热垫偏移(Byte2 当类型)则本条失败。

18. board_pin.h 时序宏(实现时抄这份数)

#define BOARD_UART_TX_PIN         IO_PORTC_05
#define BOARD_KEY_PIN             IO_PORTB_01
#define BOARD_ZC_PIN              IO_PORTA_01
#define BOARD_LED_GREEN_PIN       IO_PORTA_08
#define BOARD_LED_RED_PIN         IO_PORTA_07
#define BOARD_RELAY_PIN           IO_PORTC_03

#define BOARD_LED_ON_LEVEL        0
#define BOARD_RELAY_ON_LEVEL      1
#define BOARD_KEY_ACTIVE_LEVEL    0

#define BOARD_KEY_PAIR_MS         5000
#define BOARD_KEY_OTA_MS          12000
#define BOARD_PAIR_TIMEOUT_MS     (120u * 1000u)

#define BOARD_LED_FAST_ON_MS      100
#define BOARD_LED_FAST_OFF_MS     100
#define BOARD_LED_OTA_ON_MS       200
#define BOARD_LED_OTA_OFF_MS      200
#define BOARD_LED_PAIR_FAIL_MS    200
#define BOARD_LED_PAIR_FAIL_HOLD_MS  3000

#define BOARD_ZC_TIMEOUT_MS       100
#define BOARD_ADV_SCAN_SLOT_MS    500

#define BOARD_RUN_RECORD_DAYS     31
#define BOARD_RUN_RECORD_SAVE_SEC 600

#define BOARD_LIGHT_RATED_SEC     2592000u
#define BOARD_LIGHT_REPORT_MS     60000u
#define BOARD_LINKAGE_CFG_LEN     20
#define BOARD_TIME_CFG_LEN        11

产品码:

#define JB_CATEGORY_CODE         "CBR0"
#define JB_PRODUCT_CODE          "PRCBR0"

19. 风险与不要擅自发挥的点

风险 要求
无过零硬切继电器 禁止。打火会烧触点
开请求过零丢失仍吸合 禁止。只允许关路径超时强关
抄风扇「过热开吹」 禁止。本机是过冷开、过热关
湿度参与开停 本版禁止
丢广播就关灯 禁止(保持上一态)
生成协议再导回 DP9=RO / LEN=128 禁止;以本文 + 改过的源码为准
把加热垫 8 字节定时偏移用到灯罩 禁止。Byte1 是类型不是模式
到期强制关灯 禁止。只告警
每秒写一次寿命到 Flash 禁止。平时 600s,边沿立刻写
点灯时每 2 秒报 DP3 禁止。最多 60s 一次(边沿除外)
lighe_remain_time 拼写 禁止
复用加热垫 VM magic 禁止
app_main 上电清配对进绑定 必须删
20ms ISR 写 VM / 切 BLE 禁止
一排裸 static / 排障史注释 禁止。详见第 0.1 节
协议 OTA stub 返回 OK 当已升级 注释标明未升级
扫描时改 GATT 名 不要改 0x2A00
门夹 USB / 电池逻辑 禁止移植
把灯泡开理解成「设备开机灯」 开机后关灯是绿灯,不是灭灯
5s 清绑定顺便复位寿命 禁止

20. 给 WorkBuddy 的推荐开工顺序

  1. 确认 MAX_PACKAGE_LEN=512、DP9=WOpack/diff 排除)。jb_product.c 只保留 DP 赋值,业务迁 app_*
  2. 从加热垫拷 usr_app / usr_periph / usr_le_code / usr_jb_main;改产品码 CBR0/PRCBR0;删 app_main 上电强制配对。
  3. board_pin.h 与加热垫相同引脚;加上 BOARD_LIGHT_LIFE_ENABLE 与寿命宏。
  4. 改 DP 映射:联动 DP11、删除 DP10、温度 DP9、定时 DP1219、运行记录 DP20、故障 DP23、告警 DP25、出厂 DP26。
  5. 重写定时 11 字节解析(第 11.1),不要沿用 cfg[2]/cfg[4]
  6. P0:继电器 + 灯 + 按键验收。
  7. P1:VM 恢复开关、忽略 DP、定时只存、首次 DP3=2,592,000。
  8. P2:扫描/联动/定时引擎/运行记录/仲裁用例。
  9. 新建 app_light_life:扣秒、DP4、写 DP3、告警、60s 节流、边沿立刻落盘。

每一步都保持对应 BOARD_xxx_ENABLE 可关。提交前对照第 0.1 节:一个模块一个 s_xxx,注释写为什么,不要排障日记。


附录 A. 与加热垫 DP 号对照

加热垫 灯罩 备注
0 加热开关 0 灯泡开关 同一字段 heat_switch
1 温控开关 1 忽略
2 总工作时长 新增,恒 0
3 剩余寿命 新增
4 灯泡复位 新增,WO
2 温控时间 5 忽略
3 模式 6 忽略
4 / 5 最低最高温度 7 / 8 忽略
6 当前温度 WO 9 当前温度 WO 生成物曾误标 RO,必须改
7 / 8 联动删除/配置 10 / 11
9 time_en 12
1016 定时 8B 1319 定时 11B 布局不同
17 运行记录 20
18 心跳 21 忽略
19 / 20 故障 22 / 23
21 / 22 告警 24 / 25 灯罩启用告警 1
23 出厂 26 额外复位寿命
2426 预留 2729

文档结束。 行为争议以本文第 5~13 章为准;字节布局以第 10.2 / 11.1 / 13.1 / 13.2 节为准。