29 KiB
电子开关-加热垫固件需求规格书
| 项 | 内容 |
|---|---|
| 产品 | 电子开关-加热垫(爬宠) |
| 芯片 | JL7016 / AC7016C(BR28) |
| 工程 | 电子开关-加热垫_soc_JieLi-JL7016 |
| 产品码 | ProdtCode=CBR1,productCode=PRCBR1 |
| 协议 | 见宝 SOC 55AA,V1.0.0 |
| 文档版本 | V1.0.0 |
| 日期 | 2026.09.04 |
| 作者 | cyWu |
| 读者 | WorkBuddy / 固件开发 |
| 参考工程 | D:\Jianbao\Project\产品\爬宠\温湿度风扇\温湿度风扇_soc_JieLi-JL7016(框架 / 按键分级 / 联动仲裁 / 定时 / 运行记录) |
| 灯效参考 | D:\Jianbao\Project\产品\宠物\门夹弹力绳\门夹弹力绳_soc_JieLi-JL7016(红绿双通道、OTA 红绿交替) |
V1.0.0(2026-09-04): 首版共同理解。220V 常电继电器加热垫;过零切继电器;短按开关;开红关绿;5s 配对 / 12s OTA;DP0 加热开关;DP1–5 本版忽略;DP6 由 RO 改为 WO(连着时服务器下发当前温度);断连 500ms 重连广播 / 500ms 扫描风扇
JBao-{缸体号}温湿度广播;联动只看温度回差;定时看使能/类型/星期/时分(Byte1 模式忽略);运行记录计实际吸合秒。加热垫本体恒温,闭合后不会过热。
本文是 已拍板的共同理解,不是草稿。实现时以本文为准;DP 表以 dp_definition.json + 本文第 10.2 / 11.1 / 13.1 / 15 章为准。
0. WorkBuddy 开干前必读(硬约束)
- 程序框架必须和温湿度风扇一致。 组合根
usr_jb_main.c:20msusr_timer_tick(硬件定时器中断)+ 500msusr_timer_loop(BLE 任务上下文)。配对/OTA/写 VM 只准在任务上下文落地(app_defer/app_function_loop)。禁止在 20ms ISR 里syscfg_write、切 BLE 广播、跑联动/定时引擎。 - 不要把业务算法写进
jb_product.c。 只把 DP 写入gDevData并转调app_*。 - 不要抄门夹的电池 / 低电 / 深睡开机判定。 本机 220V 常电、无电池。上电即开机;拔电即关,无软关机。
- 不要抄风扇的 PWM 风扇 / 数码管 / AHT20 / 通风强力。 执行器是继电器 + 过零;温度来自扫描或 DP6,不是本机传感器。
MAX_PACKAGE_LEN必须为 256。 生成值 128 装不下 DP17run_record(124B)。已在jb_protocol.h改过,禁止再生成回 128。- DP6
current_temp为 WO。 已连接时服务器下发;设备不上报;断连扫描到的温度不填 DP6。已在dp_definition.json/jb_protocol.c/jb_product.c改过。 - DP1–5 本版写忽略、不实现动作(温控开关 / 温控时间 / 模式 / 最低最高温度)。DP18 心跳本版忽略。
- 每个模块必须能用
board_pin.h的BOARD_xxx_ENABLE单独关掉 仍能编过、能配网上报。 - 日志全部 英文。中文只出现在注释和本文。
- 编码风格对齐风扇 + 工程师规范:文件小写+下划线,
g_/s_前缀,枚举Pascal_t,接口xxx_init/xxx_run或 tick /xxx_get_status。 - 现有工程是协议骨架。 没有
apps/usr_app、没有board_pin.h。app_main.c里上电强制usr_goto_pair=1必须删掉,改为上电进工作态(已配对走重连广播)。 - 调试 UART TX = PC5(本机无 AHT20,PC5 空闲)。关掉会与 PA1 / PA7 / PA8 / PB1 / PC3 冲突的 demo 外设。
1. 产品一句话
220V 常供电的爬宠加热垫:过零点吸合/断开继电器(避免打火),本地短按开关,BLE 连 APP。APP 可下发加热开关、1 条缸体联动配置、7 组定时。连着时用服务器下发的当前温度做联动;断连时扫描风扇的 JBao-{缸体号} 广播取温度。加热实际吸合的秒数记 31 天运行记录。垫体自身恒温,软件仍按 Tmin/Tmax 回差开停。
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. 软件架构(对齐风扇,删风扇/显示/传感,加继电器/过零/扫描)
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_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):
jbTimerIrq()× dtuser_key_scan+user_key_handleapp_zc_poll(过零超时 100ms 判定;真正切 GPIO 在过零 ISR)app_function_poll(同步 DP0 = 实际继电器态)app_runrec_tick(继电器已吸合则累加秒;满 10 分钟只打脏,不写 flash)app_nvm_tick(变化检测 + 500ms 防抖计时,不写 flash)app_led_run
priority 固定 1、只注册一次,不动态重挂:220V 常电,不需要低功耗。
usr_timer_loop()(BLE 500ms 任务上下文):userHandle()、jbProtocolHandle()、
app_th_scan_tick()(断连时 500ms 重连广播 / 500ms 扫描交替)、
app_linkage_run() / app_timer_run()、app_runrec_rotate_if_needed()、
app_function_loop()(VM 落盘 + 配对/OTA 落地)。
可直接复用风扇源码(拷贝后改引脚/事件表,不要改框架语义):
| 复用 | 路径 | 改什么 |
|---|---|---|
| 按键 FSM | key.c / key.h / key_manager.c |
短按切加热;按住 5s 配对 / 12s OTA |
| 配对会话 | app_pair.c |
120s 超时;灯效改双灯 |
| 延后执行 | app_defer.c |
原样 |
| NVM / 仲裁 / 运行记录 / 定时 | app_nvm / app_arbiter / app_runrec / app_timer_task |
owner 从风扇档位改为继电器;定时动作改为开/关 |
| 联动 | app_linkage |
开停条件改温度回差(第 10.3);温度源改扫描/DP6 |
| RTC | usr_rtc.c |
原样 |
| 协议骨架 | usr_jb_proto |
已生成;MAX_PACKAGE_LEN=256,DP6=WO |
LED 底层 拷门夹双通道(led.c 打开 ALTERNATING_BLINK_ENABLE),不要用风扇单灯裁剪版。事件表按第 6 章。
不要拷贝: app_battery.c、app_boot.c、app_power.c、app_fan.c、app_disp.c、app_th.c、app_mode.c、app_th_adv.c、充电 GPIO、CT1642、AHT20。
4. 模块开关(独立使能 / 屏蔽调试)
全部放 apps/usr_periph/board_pin.h。0 时:对应 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 /* P0/P1 可先 0 */
#define BOARD_TH_SCAN_ENABLE 1 /* 断连扫描;P0/P1 可 0 */
#define BOARD_RUNREC_ENABLE 1
#define BOARD_NVM_ENABLE 1 /* P0 可 0 */
P0 建议先 LINKAGE/TIMER/TH_SCAN/RUNREC/NVM=0,台上只验按键、双灯、过零继电器。
5. 电源与按键
5.1 上电 / 断电
- 上电直接进入工作态,无需按键。流程对齐风扇 5.1:
bsp_hw_init→app_led_init→jbInit/userInit→ 各app_*_init→app_nvm_load→user_key_init+user_key_lock_if_held→ 20ms 节拍。 - 关机 = 拔电。 无软关机、无深睡开机判定。
- 上电若键仍按着:只锁短按(防一上电就切加热),不锁 5s/12s。
- 设备工作态恒开机。DP0 是 加热开关(不是风扇那种预留 power)。
- 无电池、无充电逻辑。
- 冷启动继电器: GPIO 先保持关;加载 VM 后按上次
heat_switch恢复请求;若为开则等过零再吸合。温度没到之前联动不改它(第 13 章)。
5.2 按住分级(运行态,与风扇同一套时序)
| 按住时长 | 行为 |
|---|---|
| < 5000 ms 松开 | 短按:切换加热(见 5.3) |
| 满 5000 ms(仍按住) | 只切配对提示灯(绿灯 100ms 快闪),不清绑定 |
| 满 5s 后、12s 前松开 | 提交配对:usr_goto_pair_mode() + app_pair_start() 120s |
| 满 12000 ms(到点即触发) | 进 OTA,不等松手;同时撤销本次配对 |
配对刻意不在 5s 到点提交 —— 用户可能继续按到 12s 进 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.1 接口
app_relay_init/app_relay_request(uint8_t on)/app_relay_get_actual()/app_relay_get_request()。on=1:pending 开,等过零后 PC3=1。on=0:pending 关,等过零后 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:禁止走「无过零硬开」;关请求仍允许强制断开(安全)。台上可用示波器看 PA1,再用宏强制超时路径。
过零恢复后:新的请求仍按第 7.1 走;fault_code 在下一次成功过零切变后清 0(P2 实现)。P0 可先打日志。
8. 温度来源(无本机传感器)
内部统一用 temp_x10(0.1℃,有符号)。联动比较见 10.3。
| 场景 | 温度从哪来 | 扫描 | DP6 |
|---|---|---|---|
| BLE 已连接 | 服务器写 DP6(int16×0.1 → float) | 停 | WO 收下,不存 VM,不上报 |
| BLE 断连且已配对 | 扫描风扇自定义广播 | 500ms 扫描 / 500ms 重连广播 交替 | 扫描值 不写 DP6 |
| 未配对 / 配对中 / OTA | 不扫描 | 重连或绑定/OTA 广播 | — |
湿度:广播里有,本版 不参与开停,只可打日志。
8.1 风扇侧广播格式(只读约定,加热垫不发这种包)
风扇 app_th_adv.c:
- Complete Local Name:
JBao-{tank_id}(十进制,无前导零),例如缸体 1 →JBao-1 - Manufacturer
0xFFFF后 3 字节:[配置版本 uint8][温度 int8 ℃][湿度 uint8 %]
加热垫匹配规则:
- 名字等于
JBao-+ 本机 DP8 缸体编号(uint16 十进制)。 - 配置版本等于 DP8 Byte6。
- 两者都满足才更新
temp_x10 = 温度int8 * 10。 - 名字不对、版本不对、解析失败:当没收到,保持上一温度和上一加热态(第 10.3 / Q6)。
缸体编号为 0:视为无有效联动,不扫描匹配。
8.2 500ms 时隙
断连已配对:
tick 偶数:见宝重连广播(encode4reconn)
tick 奇数:关广播、开扫描 ≤500ms,回调里滤 JBao- 前缀
已连接 / 配对 / OTA:退出该交替,回到风扇同款 adv 策略
实现时若协议栈不能真正双角色并发,必须时分,不允许「边广播边扫」把时序写死成阻塞。
温度丢失(长时间无匹配包):保持上一态,不因丢包关加热(垫体恒温,闭合不会过热)。
9. 本版不用的模式 / 温控 DP
| DP | 本版 |
|---|---|
| 1 温控开关 | 写忽略 |
| 2 温控时间 | 写忽略 |
| 3 模式 | 写忽略;定时 Byte1 同样忽略 |
| 4 / 5 最低最高温度 | 写忽略(阈值只在 DP8 联动 20B 里) |
无通风/强力状态机,无 app_mode。
10. 联动引擎
设备只有 1 条 20 字节联动配置(DP8)。不改 DP3。
10.1 有效配置
同时满足才跑联动开停:
- 缸体编号 ≠ 0(Byte0–1,uint16 大端)
Tmin ≤ TmaxBOARD_LINKAGE_ENABLE=1
否则视为无联动:不自动开;若原先是联动占着加热则关掉。
湿度 min/max、配置版本:整包 20B 原样落盘。湿度 不参与开停。版本只用于扫描匹配(8.1)。Byte7–19 预留,逻辑忽略。
10.2 20 字节布局(DP8 linkage_config)
| 偏移 | 字段 | 编码 |
|---|---|---|
| Byte0–1 | 缸体编号 | uint16 大端 |
| Byte2 | 最低温度 Tmin |
有符号 int8,单位 ℃ |
| Byte3 | 最高温度 Tmax |
有符号 int8,单位 ℃ |
| Byte4 | 最低湿度 Hmin |
uint8 %,只存不用 |
| Byte5 | 最高湿度 Hmax |
uint8 %,只存不用 |
| Byte6 | 配置版本 | uint8,扫描匹配用 |
| Byte7–19 | 预留 | 只存不用 |
比较用 temp_x10:T < Tmin ⇔ temp_x10 < Tmin * 10;T > Tmax ⇔ temp_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(含等于) |
保持上一态 |
过热关、过冷开。本产品恒温垫,软件仍执行 T > Tmax 关。
10.4 联动删除(DP7)
| 写入 | 行为 |
|---|---|
| 0 | 无操作 |
| 1 | 清 RAM + VM 里 20 字节;若当前是联动占着加热 → 关;然后把 linkage_delete 清回 0 再上报(脉冲) |
10.5 APP 重写 DP8
memcpy 20 字节 → 存 VM → 清手动作废标志 → 立刻按新阈值重判(有有效温度时)。缸体编号写成 0:按无效配置,联动占着则关。
11. 定时引擎(P2,7 组)
11.1 8 字节
| 偏移 | 字段 |
|---|---|
| Byte0 | 使能 |
| Byte1 | 模式 |
| Byte2 | 类型:0=单次,1=每天,2=每周 |
| Byte3 | 星期位图 Bit0=周一 … Bit6=周日(仅类型 2) |
| Byte4 | 开始小时 0~23 |
| Byte5 | 开始分钟 0~59 |
| Byte6 | 结束小时 |
| Byte7 | 结束分钟 |
- 窗开始沿:请求 开 继电器。
- 窗结束沿:请求 关 继电器,然后立刻按第 10.3 判一次联动。
- 窗内保持开(直到被手动打断,见第 12 章)。
- 支持跨午夜:22:00→06:00 = 当晚 22 点到次日 6 点。
- 类型 1:每天该窗。
- 类型 2:仅位图置位的星期;跨午夜以 开始时刻所在的本地星期 判断。
- 类型 0 单次:结束时刻过后把 Byte0 清 0 并写 VM。
- 多组重叠:任一窗内即开;所有窗都结束才走结束关。
11.2 RTC 门闩(强制,与风扇相同)
usr_rtc_get_time() 失败 → 全部定时不执行(等同 time_en=0)
仅 BLE 已连接但从未对时 → 同上
对时成功瞬间 → 立刻补判:若当前落在某条有效窗内,进入该窗(开加热)
无电池,拔电后 RTC 丢失。开机后必须等 APP/网关 usr_rtc_set_from_unix 成功。
time_en=0:所有窗立刻视为结束(关加热),再按 10.3 补判联动。
12. 仲裁(唯一决策表)
优先级 高 → 低:
- 手动:短按、APP 写 DP0
- 定时窗(RTC 有效且
time_en=1且当前落在窗内) - 联动(有效配置且本 tick 满足开/关条件)
| 冲突 | 胜者 | 副作用 |
|---|---|---|
| 手动 vs 正在跑的定时 / 联动 | 手动 | 若原是联动占着 → 本轮联动作废(RAM,不写 VM) |
| 手动 vs 定时窗 | 手动 | 本窗剩余时间不再自动抢回;等到下一窗开始沿或下次对时补窗 |
| 定时窗 vs 联动 | 定时 | 窗内联动不开、不关;窗结束收口后立刻按 10.3 重判 |
| 联动 vs 空闲 | 联动 | 开则请求开,关则请求关 |
作废标志不存 VM。
手动作废后的再武装: 必须先走到第 10.3 的「关」条件(T > Tmax),再重新出现 T < Tmin,才允许联动再开。作废期间丢广播仍保持上一态。
冷启动: 作废标志是 RAM,上电为空。VM 恢复的加热态先执行;有有效温度且无定时窗占用时,允许联动按 10.3 立刻改。
time_en 写成 0:立刻停定时(窗结束语义)。无联动总开关;无效配置 / DP7 删除即停联动。
13. 持久化(VM)
存(冷启动保留):
heat_switch(上次加热请求;上电按此恢复)linkage_config(20B)time_en、time_config1~7(各 8B,含被忽略的 Byte1)fault_en、alarm_en(若有写入)run_record:31 × uint32 秒 + slot[0] 对应的本地日期(对时后才有意义)
不存:
- DP1–5、DP6 当前温度、联动作废标志、过零 pending
linkage_delete(脉冲)- BLE 三元组(
CFG_USER_PAIR_INFO,出厂重置 不清)
上电:加载上述字段,继电器按上次 heat_switch 恢复(开则等过零),再让联动按第 12 章冷启动规则判(无温度则不动)。
首次上电(VM 无效):加热关,联动 20B 全 0,定时全 0,time_en=0,31 天运行记录全 0。
写 VM 时机:
- APP 改联动/定时、单次定时自毁、加热稳定变化:500ms 防抖
- 运行记录:累计吸合满 10 分钟 写一次;跨天旋转时立刻写
13.1 运行记录(DP17 run_record,RO,124B)
31 天 × 4 字节,每格 uint32 大端,单位秒:
| 偏移 | 含义 |
|---|---|
| Byte0–3 | 今天加热垫运行总秒数 |
| Byte4–7 | 昨天 |
| … | … |
| Byte120–123 | 前 30 天 |
什么叫在运行: 过零后继电器 实际已吸合(app_relay_get_actual()==1)。pending 开但还没过零 不算。手动 / 定时 / 联动只要真正吸合都算。
日期轴: 与风扇相同 —— 未对时只往 slot[0] 加、不旋转;对时后按本地日历左移;掉电再上电先灌 VM。
落盘: 每累计运行满 600 秒写一次 VM。跨天立刻写。拔电接受最多丢不满 10 分钟的秒数。
BOARD_RUNREC_ENABLE=0:不累加、DP17 报全 0。
14. 出厂重置(DP23 写 1)
- 清全部业务 VM(第 13 章列表,含联动 20B 与 31 天运行记录)。
- 保留 BLE 配对,不断开、不软复位。
- RAM 中加热关,联动/定时停止,作废标志清掉。
- 换机:运行中 按住 5s 后松手 清配对进绑定广播(见 5.2)。
15. BLE / DP 行为摘要
身份:CBR1 / PRCBR1。帧格式见 jb_protocol.h(MAX_PACKAGE_LEN=256)。
| DP | 处理 |
|---|---|
| 0 heat_switch | 写则手动抢权,请求开/关(等过零)。上报实际吸合 |
| 1–5 | 本版写忽略 |
| 6 current_temp | WO。已连接时服务器下发 → 更新联动用温度。不上报。断连扫描不填 |
| 7 linkage_delete | 第 10.4 节,脉冲 |
| 8 linkage_config | 20 字节 memcpy + 存 VM + 第 10.5 节立刻重判 |
| 9 time_en | 见第 11 章 |
| 10–16 time_config | 8 字节 + 存 VM;Byte1 忽略 |
| 17 run_record | RO,由 app_runrec 填 124B,禁止 APP 写 |
| 18 heartbeat | 本版忽略 |
| 19 fault_en | 原样存,P0 不做位级过滤 |
| 20 fault_code | 0 无 / 1 温度异常(本版不上报) / 2 过零超时等设备故障 |
| 21 alarm_en | 原样存 |
| 22 alarm_code | 保持 0 |
| 23 factory_reset | 第 14 章 |
| 24–26 reserva | 存 RAM,可不实现动作 |
OTA:运行态按住 12s 走 usr_app_ota_init(任务上下文,红绿交替)。协议 OTA(jbOnOtaNotify)P2 可继续 stub,禁止假装升级成功后擦用户区。
配对超时 120s,失败保持开机,灯回「跟继电器」。
断连已配对:500ms 见宝重连广播 ↔ 500ms 扫描(第 8.2)。绑定广播仍用 CBR1 / PRCBR1,不要改成 JBao- 名(那是风扇温湿度窗)。
16. 建议文件清单(新建,风扇已有的拷贝改)
apps/usr_periph/
board_pin.h # 引脚、时序、BOARD_xxx_ENABLE
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 # 拷风扇;短按切加热 + 5s/12s
led.c/.h, led_manager.c/.h # 拷门夹双通道 + 红绿交替
app_led.c/.h # 第 6 章事件表;含 5s 将配网
app_defer.c/.h # 拷风扇
app_pair.c/.h # 拷风扇,灯效改双灯
app_relay.c/.h # 请求 / 实际 / pending
app_th_scan.c/.h # 500ms 交替扫描 JBao-{tank}
app_linkage.c/.h # 第 10 章温度回差
app_runrec.c/.h # 实际吸合计秒
app_timer_task.c/.h # 第 11 章;动作改为开/关
app_nvm.c/.h
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)
目标: 按键、双灯、过零继电器可独立验证。
board_pin.h+ UART TX=PC5,打印正常- 插电即开机(不按键,绿灯常亮,继电器关)
- 短按:继电器在过零沿才变;开红关绿
- 示波器:PA1 过零沿与 PC3 边沿对齐(差应在亚毫秒级,禁止随便 delay 后切)
- 模拟过零丢失:关请求 100ms 强关;开请求 100ms 保持关(可宏强制)
- 按住满 5s → 绿灯 100ms 快闪;松手 → 清绑定进配对
- 按住满 5s 不松手到 12s → 不进配对,红绿交替进 OTA
- 长按进配对/OTA 后不复位(动作走任务上下文)
- 上电强制
usr_goto_pair=1已删除
P0 关闭: BOARD_LINKAGE/TIMER/TH_SCAN/RUNREC/NVM_ENABLE=0
P1 — 最小产品闭环
- 5s 配对,APP 能写 DP0 开关加热(过零后灯和继电器一致,DP0 报实际态)
- 短按同步上报 DP0
- VM:改加热后断电,重新上电按上次开关恢复(开则等过零再吸合)
- DP1–5 写忽略;设备不关、继电器不跟这五个 DP
- DP6 WO:APP/服务器能写下当前温度;设备全量上报 不含 DP6
- 定时 7 组 +
time_en能存;BOARD_TIMER_ENABLE=0时不执行 MAX_PACKAGE_LEN=256,APP 能读完整 124B 运行记录(本阶段记录可仍为 0)
P2 — 自动化
- 断连:500ms 重连广播 / 500ms 扫描;已连接停扫描
- 匹配
JBao-{缸体号}且版本相同才更新温度;湿度不参与开停 - 联动:
T<Tmin开,T>Tmax关,中间保持;无匹配包保持上一态 - 缸体号 0 / min>max 不跑联动
- 手动作废后须先
T>Tmax再T<Tmin才再开 - 定时 RTC 门闩:未对时绝不跑;对时后补窗
- 跨午夜、单次自毁、每天/每周、多组重叠
- 定时窗开始开、结束关,结束立刻把联动判一次;Byte1 忽略
- DP7 脉冲删除;DP8 重写立刻重判
- 运行记录:实际吸合 10 分钟落盘;对时后跨天左移
- 出厂重置清业务留配对
- 过零超时
fault_code=2 - 仲裁表第 12 章逐条用例(见下)
P2 仲裁最小用例(必须写进测试笔记)
- 空闲
T<Tmin→ 加热开;短按关 → 保持关,即使仍低于 Tmin。 - 用例 1 之后先到
T>Tmax,再回到T<Tmin,才再次开。 - 定时窗开始 → 开;窗未结束前保持开;结束时刻关;若此时
T<Tmin→ 联动再开。 - 定时窗内再短按关 → 保持关到窗结束也不要被定时抢回;窗结束若已被手动作废且仍
T<Tmin→ 不要被联动立刻打开。 - 未对时,定时配置使能也不动作。对时后若在窗内立即开。
- 上电 VM 为开、暂无温度 → 保持开(等过零吸合);温度到达后再按 10.3。
T在 [Tmin, Tmax] → 保持上一态。- 断连扫描丢包 → 不关加热(保持上一态)。
- 已连接写 DP6 使
T<Tmin→ 开;断开后扫描到同一缸体更高温度T>Tmax→ 关。
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
19. 风险与不要擅自发挥的点
| 风险 | 要求 |
|---|---|
| 无过零硬切继电器 | 禁止。打火会烧触点 |
| 开请求过零丢失仍吸合 | 禁止。只允许关路径超时强关 |
| 抄风扇「过热开吹」 | 禁止。本机是过冷开、过热关 |
| 湿度参与开停 | 本版禁止 |
| 丢广播就关加热 | 禁止(已拍板保持上一态) |
| 生成协议再导回 DP6=RO / LEN=128 | 禁止;以本文 + 当前源码为准 |
app_main 上电清配对进绑定 |
必须删 |
| 20ms ISR 写 VM / 切 BLE | 禁止(风扇已踩过栈溢出) |
| 协议 OTA stub 返回 OK 当已升级 | 注释标明未升级 |
| 扫描时改 GATT 名 | 不要改 0x2A00;只解析对方 adv |
| 门夹 USB 插入直通开机 / 电池逻辑 | 禁止移植 |
| 把加热开理解成「设备开机灯」 | 开机后关加热是绿灯,不是灭灯 |
20. 给 WorkBuddy 的推荐开工顺序
- 确认
MAX_PACKAGE_LEN=256、DP6=WO(本节交付已改协议文件)。jb_product.c只保留 DP 赋值,业务迁app_*。 board_pin.h+bsp_hw+bsp_zc;删app_main上电强制配对。- 从风扇拷
usr_jb_main框架、key、defer、pair、nvm 骨架;LED 拷门夹双通道。 app_relay:短按 + DP0 + 过零;P0 验收灯和沿。- P1:VM 恢复加热、DP1–5 忽略、定时只存。
- P2:
app_th_scan、app_linkage、app_timer、app_runrec、仲裁用例。
每一步都保持对应 BOARD_xxx_ENABLE 可关。
文档结束。 行为争议以本文第 5~13 章为准;字节布局以第 10.2 / 11.1 / 13.1 节为准。