Files
JBao_Sprayer_FW/Doc/喷淋器_固件需求规格书.md

39 KiB
Raw Permalink Blame History

喷淋器固件需求规格书

内容
产品 喷淋器(爬宠)
芯片 JL7016 / AC7016CBR28
工程 喷淋器_soc_JieLi-JL7016
产品码 ProdtCode=CBR2productCode=PRCBR2
协议 见宝 SOC 55AAV1.0.0
文档版本 V1.0.2
日期 2026.09.09
作者 cyWu
读者 WorkBuddy / 固件开发
参考工程 D:\Jianbao\Project\产品\爬宠\电子开关-加热垫\电子开关-加热垫_soc_JieLi-JL7016(框架 / 仲裁 / 联动 / 运行记录 / 配对 OTA / NVM
框架源头 D:\Jianbao\Project\产品\爬宠\温湿度风扇\温湿度风扇_soc_JieLi-JL701620ms + 500ms、按键分级、扫描时隙)
灯驱参考 D:\Jianbao\Project\产品\宠物\门夹弹力绳\门夹弹力绳_soc_JieLi-JL7016(红绿双通道硬件;事件表不要抄加热垫互斥灯

V1.0.22026-09-09): 红灯跟 BLE 连接态:已连接常亮;已配对未连接(上电重连 / 断连)500ms 慢闪。避免上电把「已绑定」当成「已联网」。

V1.0.12026-09-09): 灯脚改为与加热垫相同:PA8 绿 / PA7 红(低亮)。红绿通道独立语义不变。

V1.0.02026-09-09): 首版共同理解。5V 常电双脚水泵;两键两灯;红绿通道独立;单击按上次配置跑「时长 × 次数 + 间隔」;APP 写 DP7 立刻开喷;湿度联动回差;7 条计划到点触发一轮(不是时间窗);运行记录计水泵实际转动秒;MAX_PACKAGE_LEN=512

本文是 已拍板的共同理解,不是草稿。实现时以本文为准;DP 表以 dp_definition.json + 本文第 10.2 / 11.2 / 12.1 / 14.1 / 16 章为准。


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

  1. 程序框架必须和加热垫 / 风扇一致。 组合根 usr_jb_main.c20ms usr_timer_tick(硬件定时器中断)+ 500ms usr_timer_loop(BLE 任务上下文)。配对 / OTA / 写 VM 只准在任务上下文落地(app_defer / app_function_loop)。禁止在 20ms ISR 里 syscfg_write、切 BLE 广播、跑联动 / 计划引擎。
  2. 不要把业务算法写进 jb_product.c 只把 DP 写入 gDevData 并转调 app_*
  3. 不要抄门夹的电池 / 低电 / 深睡开机判定。 本机 5V 常电、无电池。上电即开机;拔电即关,无软关机。
  4. 不要抄加热垫的过零 / 继电器,也不要抄风扇的 PWM / 数码管 / AHT20。 执行器是 PC2 + PA6 双脚水泵;湿度来自扫描或 DP6,不是本机传感器。
  5. MAX_PACKAGE_LEN 必须为 512。 生成值是 128,装不下 DP18 run_record(124B)。改完禁止再生成回 128。OTA 单包 JB_OTA_MAX_PAYLOAD 可保持 96。
  6. DP6 current_humi 为 WO。 已连接时服务器下发;设备不上报;断连扫描到的湿度不填 DP6。
  7. DP5 current_temp 本版忽略。 联动只看湿度。
  8. 本版写忽略、不实现动作: DP0 总开关、DP2 模式、DP3 单次时长、DP4 连续时长、DP5 当前温度、DP19 心跳。DP21 / DP23 保持 0(无缺水检测)。DP20 / DP22 原样存、不做位过滤。
  9. 每个模块必须能用 board_pin.hBOARD_xxx_ENABLE 单独关掉 仍能编过、能配网上报。
  10. 日志全部 英文。中文只出现在注释和本文。
  11. 编码风格对齐风扇 + 工程师规范:文件小写+下划线,g_ / s_ 前缀,枚举 Pascal_t,接口 xxx_init / xxx_run 或 tick / xxx_get_status
  12. 现有工程是协议骨架。 没有 apps/usr_app、没有 board_pin.happ_main.c 里上电强制 usr_goto_pair=1 必须删掉,改为上电进工作态(已配对走重连广播)。
  13. 调试 UART TX = PC5。关掉会与 PA8 / PA7 / PB1 / PA0 / PC2 / PA6 冲突的 demo 外设(IR 键、PWM LED、demo I2C 等)。
  14. 红灯 PA7、绿灯 PA8。 与加热垫灯脚相同。禁止再把两灯脚写反。
  15. 灯是两路独立通道。 喷淋 / 空闲只改绿灯,不要去关红灯;配网闪红灯时不要去关绿灯。只有 OTA 和配对失败动画临时占用双灯。禁止抄加热垫「开红关绿、互斥」事件表。
  16. 上电绝不自动喷。 不从 VM 恢复 DP1。泵两脚上电默认低。
  17. 遵守第 0.1 节编码与注释。 散落 static、把排障过程复制进每个函数、只写不读的计数,一律视为未完成。

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_last_levels_initialized 并列。正例:static PumpCtx_t s_pump
  3. 不要只写不读的诊断变量。 需要看泵是否在转,用已有 getter + 一条日志。
  4. 文件头三件事:做什么、一两行硬约束、history。 同一条约束只写一次。禁止把排障日记复制到每个 @note
  5. 函数注释短。 @brief 一行;@param / @return 按需;@note 只写本函数独特约束。不要复述代码。
  6. 注释解释为什么,不写排障日记。 「两脚必须同时高,泵才会转」是为什么;「我改了三次 GPIO 才好」不要进源码。
  7. P0 路径不堆死字段。 没有 getter、没有日志、P2 也没用上的计数不要先占着。

1. 产品一句话

5V 常供电的爬宠喷淋器:双 GPIO 同时导通才开泵。本地单击喷淋键,按上次 APP 配置跑完「单次时长 × 次数 + 间隔」一轮;APP 可写配置立刻开喷,也可写开关停喷。连着时用服务器下发的湿度做联动;断连时扫描风扇 JBao-{缸体号} 广播取湿度。过干则循环喷、过湿则停、中间维持上一态。7 条喷淋计划到开始时:分各跑一轮(不是加热垫那种时间窗)。水泵实际转动的秒数记 31 天运行记录。


2. 硬件资源

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

功能 MCU 电平 / 说明
红灯 PA7 IO_PORTA_07 低电平亮(与加热垫相同)
绿灯 PA8 IO_PORTA_08 低电平亮(与加热垫相同)
喷淋键 PB1 IO_PORTB_01 低电平按下
配网键 PA0 IO_PORTA_00 低电平按下
水泵总供电 PC2 IO_PORTC_02 高电平把 5V 导到泵 VCC
水泵开关 PA6 IO_PORTA_06 高电平导通泵;必须与 PC2 同时开,泵才工作
调试 UART TX PC5 IO_PORTC_05 只发打印
供电 5V 常电 → 板内 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 键、PWM LED、demo I2C / RDEC 等)。产品灯走 app_led,保持 TCFG_PWMLED_ENABLE=DISABLE

2.2 水泵安全

  • GPIO 上电默认:PC2=0 且 PA6=0
  • 喷水时段:两脚同时置高。
  • 间隔等待、空闲、停止、故障收口:两脚同时拉低。
  • 禁止只开其中一只脚。 写泵接口必须成对操作。
  • 无过零、无继电器模块。切 GPIO 可在 20ms 节拍里直接改(5V 小泵,不是市电触点)。
  • 上电绝不按 VM 自动开泵。

3. 软件架构(对齐加热垫,删过零/继电器,加双键/双脚泵/喷淋作业)

apps/earphone/app_main.c
  └─ usr_rtc_init()
  └─ usr_jb_init()                    ← 唯一组合根
        bsp_hw_init()
        app_led_init()                ← 灯效通道表最先装载
        jbInit() + userInit()
        (上电即开机:无按键等待)
        app_pump_init()
        app_spray_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):

  1. jbTimerIrq() × dt
  2. user_key_scan + user_key_handle(两颗键)
  3. app_spray_poll(作业节拍:时长 / 间隔倒数;到点切泵)
  4. app_function_poll(同步 DP1 = 作业态,不是某一只脚的瞬时电平)
  5. app_runrec_tick(两脚都已导通则累加秒;满 10 分钟只打脏,不写 flash)
  6. app_nvm_tick(变化检测 + 500ms 防抖计时,不写 flash
  7. app_led_run

priority 固定 1、只注册一次,不动态重挂5V 常电,不需要低功耗。

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
配对会话 app_pair.c 120s 超时;红灯通道跟配对态
延后执行 app_defer.c 原样
NVM / 仲裁 / 运行记录 app_nvm / app_arbiter / app_runrec owner 从继电器改为喷淋作业;记录改计泵实际导通秒
联动 app_linkage 开停条件改湿度回差(第 11.3);湿度源改扫描 / DP6
定时 app_timer_task 改成到点触发一轮,不要抄加热垫时间窗
RTC usr_rtc.c 原样
扫描 app_th_scan 取广播湿度,不取温度
协议骨架 usr_jb_proto 已生成;MAX_PACKAGE_LEN 改 512DP6=WO

LED 底层拷门夹双通道(led.c 打开 ALTERNATING_BLINK_ENABLE),事件表按第 6 章 独立通道,不要用加热垫互斥表。

不要拷贝: app_battery.capp_boot.capp_power.capp_fan.capp_disp.capp_th.capp_mode.capp_relay.capp_zc.cbsp_zc.c、充电 GPIO、CT1642、AHT20、过零。


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_PUMP_ENABLE         1
#define BOARD_SPRAY_ENABLE        1   /* 喷淋作业引擎;=0 时键/DP 不转泵 */
#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 上电 / 断电

  • 上电直接进入工作态,无需按键。流程:bsp_hw_initapp_led_initjbInit/userInit → 各 app_*_initapp_nvm_loaduser_key_init + user_key_lock_if_held → 20ms 节拍。
  • 关机 = 拔电。 无软关机、无深睡开机判定。
  • 上电若某键仍按着:只锁该键短按(防一上电就开喷),不锁配网键的 5s / 12s。
  • 设备工作态恒开机。DP0 本版忽略。DP1 是 喷淋作业开关
  • 无电池、无充电逻辑。
  • 冷启动水泵: GPIO 先保持关。VM 恢复配置 / 联动 / 计划 / 运行记录,不恢复 DP1,不开泵。湿度没到之前联动不改它(第 13 章)。
  • 上电灯:绿灯常亮;未配对红灯灭;已配对未连接红灯 500ms 慢闪;BLE 连上后红灯常亮。

5.2 两键分工(运行态)

加热垫是一颗键兼短按 / 配网 / OTA。本机必须拆开,禁止把 5s / 12s 做在喷淋键上。

PB1 喷淋键

事件 行为
单击(松开且按下时长 < 5000 ms 空闲 → 用上次有效配置开一轮(第 10 章);作业中 → 立刻停(第 10.4)
按住 ≥ 5s 无动作(不配网、不进 OTA

PA0 配网键

按住时长 行为
< 5000 ms 松开 短按:无动作
满 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 会话中的按键

场景 PB1 PA0
配对进行中 仍可启停喷淋 忽略再次 5s / 12s
OTA 进行中 忽略 忽略

6. LED(红绿独立通道,低亮)

灯脚:PA7 红、PA8 绿(与加热垫相同)。两路独立,固件改其中一个 GPIO 时禁止顺手改另一个,除非命中下面的「双灯例外」。

6.1 绿灯(喷淋通道)

状态 绿灯
空闲(DP1=关) 常亮
喷淋作业中(含间隔等待,DP1=开) 500ms 亮 / 500ms 灭
上电 常亮(泵关)

绿灯跟 作业态DP1),不是 PA6 / PC2 的瞬时电平。间隔等待也算作业中,绿灯继续闪。

6.2 红灯(配网通道)

状态 红灯
未配对 / 配对失败收尾后
已配对、BLE 未连接(上电重连 / 断连) 500ms 慢闪(空闲 / 喷淋都不要关它)
已配对、BLE 已连接 常亮
配网键满 5s 仍按住、以及配对进行中 100ms 亮 / 100ms 灭(此时即使已配对也改成快闪)
配对成功 若当时已连接则常亮,否则先慢闪,连上再常亮

配网闪红灯期间,绿灯继续按 6.1 闪或常亮,不要关绿灯。

6.3 双灯例外(临时占用两路,结束后各回自己的通道)

事件 图案 结束后
OTA 红绿交替,200ms 半周期,直到升级结束 / 复位
配对失败 / 超时 红绿交替 3s 红灯灭;绿灯回到空闲或喷淋

优先级高→低:OTA → 配对失败动画 → 各走各的通道(红=连接/配网态,绿=作业态)


7. 水泵(双脚同时切)

7.1 接口

  • app_pump_init / app_pump_set(uint8_t on) / app_pump_get()
  • on=1PC2=1 且 PA6=1。
  • on=0PC2=0 且 PA6=0。
  • 禁止导出「只改一只脚」的接口。
  • BOARD_PUMP_ENABLE=0set 直接 returnGPIO 不配。

7.2 谁可以切泵

只有 app_spray 作业引擎调用 app_pump_set。键 / DP / 联动 / 计划都只请求「开一轮 / 停」,不直接改 GPIO。


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

内部统一用 humi(uint8,单位 %)。联动比较见 11.3。

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

DP5 当前温度:本版 写忽略,不参与开停。

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

风扇 app_th_adv.c

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

喷淋器匹配规则:

  1. 名字等于 JBao- + 本机 DP9 缸体编号(uint16 十进制)。
  2. 配置版本等于 DP9 Byte6。
  3. 两者都满足才更新 humi = 湿度uint8
  4. 名字不对、版本不对、解析失败:当没收到,保持上一湿度和上一喷淋态(第 11.3)。

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

8.2 500ms 时隙

断连已配对:

tick 偶数:见宝重连广播(encode4reconn)
tick 奇数:关广播、开扫描 ≤500ms,回调里滤 JBao- 前缀
已连接 / 配对 / OTA:退出该交替,回到风扇同款 adv 策略

实现时若协议栈不能真正双角色并发,必须时分,不允许「边广播边扫」把时序写死成阻塞。

湿度丢失(长时间无匹配包):保持上一态,不因丢包停喷。


9. 本版不用的 DP

DP 本版
0 power 总开关 写忽略
2 mode 模式 写忽略(单次 / 连续那套预留语义作废,喷淋参数只走 DP7 / 计划自带字段)
3 once_spray_time 写忽略
4 continuous_spray_time 写忽略
5 current_temp 写忽略;不上报
19 heartbeat 写忽略
21 fault_code 保持 0
23 alarm_code 保持 0(缺水告警本版无传感器)

app_mode。DP20 / DP22 有写入则原样存 VM。


10. 喷淋作业引擎

设备同一时刻最多跑 一轮 作业。一轮 = 按「单次时长、次数、间隔」把泵开停若干次,然后结束。

10.1 有效配置

来源优先级(只决定 参数,不决定谁有权开):

  1. 计划触发:用 该条计划自己的 时长 / 次数 / 间隔(第 12.1)。
  2. 其它开喷(PB1、DP1=1、写 DP7、联动):用 RAM / VM 里的 DP7。
  3. DP7 无效或从未收过:默认 时长 10s、次数 3、间隔 3s

首次上电 VM 无效:把默认写入 RAM,并作为 DP7 上报,当作已有有效配置。

10.2 DP7 spray_config8 字节)

偏移 字段 编码
Byte01 单次喷淋时长 uint16 大端,单位秒,165535
Byte2 喷淋次数 uint81255
Byte34 喷淋间隔 uint16 大端,单位秒,065535
Byte57 预留 只存不用

默认上电值(十六进制):00 0A 03 00 03 00 00 00

非法包:时长=0 或 次数=0。整包丢弃:不改 RAM / VM,不开喷,保持上一份有效配置。

间隔=0 喷完一次马上喷下一次,中间 不停泵(两脚保持高,直到整轮次数用完)。

APP 写 DP7(合法包):memcpy 8 字节 → 存 VM → 立刻按新参数开一轮。若正在喷:中止旧轮再开新轮。记入手动抢权(第 13 章),DP1 报开。

10.3 一轮怎么跑

count 次循环:
    泵开
    等待 duration 秒
    若还不是最后一次 且 interval > 0:
        泵关
        等待 interval 秒
泵关
作业结束,DP1 报关
  • 整轮期间(含间隔等待)DP1 = 1,绿灯按作业态闪。
  • 轮次自然结束:泵关,DP1 = 0。
  • 20ms 节拍里倒数;不要用阻塞 delay

10.4 停止

任一来源请求停:

  • 立刻两脚拉低,结束作业,DP1 报关。
  • 来源是 PB1 单击(作业中)或 APP 写 DP1=0:手动作废联动(第 13 章)。
  • 若当前轮是 类型 0 单次计划 占用的:该条 Byte0 清 0 并写 VM(被手动中途停也要清,避免单次卡死)。

10.5 其它开喷入口

入口 参数 是否手动抢权
PB1 单击(空闲) 上次有效 DP7
APP 写 DP1=1 上次有效 DP7
APP 写 DP7 新 DP7
计划到点 该条 Byte59 否(计划排队,见 12.3
联动请求开 上次有效 DP7

APP 写 DP1=1 时若已在作业中:保持当前轮,不重开(幂等)。写 DP1=0 时若已空闲:保持关。


11. 联动引擎

设备只有 1 条 20 字节联动配置(DP9)。布局与加热垫 DP8 相同,本版只用湿度字段。

11.1 有效配置

同时满足才跑联动开停:

  • 缸体编号 ≠ 0Byte01uint16 大端)
  • Hmin ≤ Hmax
  • BOARD_LINKAGE_ENABLE=1

否则视为无联动:不自动开;若原先是联动占着作业则停泵。

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

无单独「联动总开关」。停联动:DP8 写 1、或 DP9 缸体写成 0 / min>max、或手动作废。

11.2 20 字节布局(DP9 linkage_config

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

比较用 uint8 湿度:H < HminH > Hmax等于不算越界,走「中间维持上一态」。

已连接:humi = gDevData.current_humi。断连扫描:广播第 3 字节。尚无任何有效湿度:联动不改泵(保持上电关 / 上一态)。

11.3 开停(只看湿度)

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

条件 动作
H < Hmin 请求进入喷淋:用 DP7 循环开一轮又一轮(一轮正常结束后立刻再开,无额外间隔)
H > Hmax 立刻停泵、结束作业
Hmin ≤ H ≤ Hmax(含等于) 保持上一态(上一态在喷就继续循环,上一态停就保持停)

过干开、过湿关。无有效湿度 / 丢包:不改。

动只在 空闲且未被作废 时开下一轮。泵被手动 / 计划占着时联动不抢(第 13 章)。

11.4 联动删除(DP8

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

11.5 APP 重写 DP9

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


12. 喷淋计划引擎(7 组,到点触发一轮)

不要抄加热垫时间窗。 加热垫是开始~结束保持开;本机是「到开始时:分,用该条自己的时长 / 次数 / 间隔跑完一轮就结束」。没有结束时:分。

12.1 15 字节(DP1117

偏移 字段
Byte0 使能 0/1
Byte1 定时类型:0=单次,1=每天,2=每周。其它值该条无效,不触发
Byte2 星期位图 Bit0=周一 … Bit6=周日(仅类型 2)
Byte3 开始小时 023
Byte4 开始分钟 059
Byte56 单次时长 uint16 大端
Byte7 次数 uint8
Byte89 间隔 uint16 大端
Byte1014 预留,只存不用

本版 没有 加热垫那种「Byte1 模式忽略」。Byte1 就是类型。

非法参数(时长=0 或 次数=0):该条本分钟不触发,使能保持原值。

12.2 总开关与 RTC 门闩

DP10 spray_plan_en:所有计划的总开关。

usr_rtc_get_time() 失败  →  全部计划不新触发(等同 plan_en=0 的「不触发」)
仅 BLE 已连接但从未对时 →  同上
plan_en=0                →  不新触发

plan_en=0 或 RTC 无效:不中断 已经在跑的一轮(手动 / APP / 联动仍可停)。

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

对时成功瞬间:若 当前时:分刚好等于 某条有效计划的开始时:分,补触发一次;过了这一分钟不再补(避免下午计划在晚上对时被补喷)。

12.3 触发与排队

每个 500ms 任务 tick

  1. RTC 无效或 plan_en=0:不入队。
  2. 类型 1:每天该时:分触发一次。
  3. 类型 2:仅位图置位的星期,该时:分触发一次。
  4. 类型 0:该时:分触发一次;该轮 正常跑完或被手动停 后把 Byte0 清 0 并写 VM。
  5. 去重:用「上次触发的本地日期 + 时 + 分 + 槽位」防 500ms 任务在同一分钟内连打。
  6. 多条同一分钟:排队,做完一条再做下一条,不重叠喷。
  7. 计划到点时若泵已经在跑(手动 / 上一计划 / 联动):当前轮先跑完,再跑队列,不插队中止
  8. 新触发只入队,真正 app_spray_start 发生在作业空闲且轮到该条时。

12.4 一轮结束之后

  • 计划轮正常结束:出队;立刻按第 11.3 补判联动(湿度仍 < Hmin 且未作废 → 联动接着循环)。
  • 被手动停:作废联动,不再立刻补开;若是单次计划,仍清该条使能。

13. 仲裁(唯一决策表)

优先级 高 → 低

  1. 手动PB1、APP 写 DP1、APP 写 DP7
  2. 计划队列RTC 有效且 plan_en=1 且已到点入队 / 正在跑计划轮)
  3. 联动(有效配置且本 tick 满足开 / 关条件)
冲突 胜者 副作用
手动开(含写 DP7)vs 正在跑的任何轮 手动 中止当前轮,按新来源参数重开;若原是联动占着 → 本轮联动作废RAM,不写 VM
手动停 vs 正在跑的任何轮 手动 立刻停;联动作废;单次计划清使能
计划到点 vs 正在跑 当前轮先跑完 计划入队,不插队
计划轮 vs 联动 计划 计划占着时联动不开下一轮;计划结束收口后立刻按 11.3 重判
联动 vs 空闲 联动 开则开一轮(循环),关则停

作废标志不存 VM。

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

冷启动: 作废标志是 RAM,上电为空。泵保持关。有有效湿度且无计划占用时,允许联动按 11.3 立刻改(过干则开喷)。

plan_en 写成 0:立刻停新触发(不拆当前轮)。无联动总开关;无效配置 / DP8 删除即停联动。


14. 持久化(VM

存(冷启动保留):

  • spray_config8B,含首次默认 10 / 3 / 3)
  • linkage_config20B
  • spray_plan_enspray_plan1 + time_config2~7(各 15B
  • fault_enalarm_en(若有写入)
  • run_record31 × uint32 秒 + slot[0] 对应的本地日期(对时后才有意义)

不存:

  • DP0 / DP2 / DP3 / DP4 / DP5、DP1 作业开关、DP6 当前湿度、联动作废标志、泵 GPIO
  • linkage_delete(脉冲)
  • BLE 三元组(CFG_USER_PAIR_INFO,出厂重置 不清

上电:加载上述字段,泵保持关,DP1=0,再让联动按第 13 章冷启动规则判(无湿度则不动)。

首次上电(VM 无效):DP7=默认 10s / 3 次 / 3s,联动 20B 全 0,计划全 0,spray_plan_en=031 天运行记录全 0DP1=0。

写 VM 时机:

  • APP 改联动 / 计划 / 喷淋配置、单次计划自毁:500ms 防抖
  • 运行记录:累计泵转满 10 分钟 写一次;跨天旋转时立刻写

14.1 运行记录(DP18 run_recordRO124B

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

偏移 含义
Byte03 今天喷淋器运行总秒数
Byte47 昨天
Byte120123 前 30 天

什么叫在运行: app_pump_get()==1PC2 与 PA6 都已导通)。间隔等待、只开了一只脚、作业态为开但泵已关,都不计。手动 / 计划 / 联动只要泵真正在转都算。

日期轴: 与加热垫 / 风扇相同 —— 未对时只往 slot[0] 加、不旋转;对时后按本地日历左移;掉电再上电先灌 VM。

上报: 今天累计秒数相对上次 0x07 上报满 60 秒 才进差分(JB_RUNREC_DIFF_SEC);跨天旋转立刻带 DP18。APP 0x05 读、10 分钟全量仍带完整 124B。其它 DP 上报时不刷新运行记录基准。

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

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


15. 出厂重置(DP24 写 1

  • 清全部业务 VM(第 14 章列表,含 DP7 默认恢复、联动 20B、7 条计划、31 天运行记录)。
  • 保留 BLE 配对,不断开、不软复位。
  • RAM 中泵停、DP1=0,联动 / 计划停止,作废标志清掉;DP7 回到默认 10 / 3 / 3。
  • 换机:运行中 按住配网键 5s 后松手 清配对进绑定广播(见 5.2)。

16. BLE / DP 行为摘要

身份:CBR2 / PRCBR2。帧格式见 jb_protocol.hMAX_PACKAGE_LEN=512)。

DP 处理
0 power 本版写忽略
1 spray_switch 写 1 开一轮(已在喷则保持);写 0 立刻停。上报 作业态(整轮含间隔为 1
24 本版写忽略
5 current_temp 本版写忽略;不上报
6 current_humi WO。已连接时服务器下发 → 更新联动用湿度。不上报。断连扫描不填
7 spray_config 8 字节;合法则存 VM + 立刻开一轮(第 10.2)
8 linkage_delete 第 11.4 节,脉冲
9 linkage_config 20 字节 memcpy + 存 VM + 第 11.5 节立刻重判
10 spray_plan_en 见第 12 章
1117 计划 15 字节 + 存 VM
18 run_record RO,由 app_runrec 填 124B,禁止 APP 写
19 heartbeat 本版忽略
20 fault_en 原样存,P0 不做位级过滤
21 fault_code 保持 0
22 alarm_en 原样存
23 alarm_code 保持 0
24 factory_reset 第 15 章
2527 reserva 存 RAM,可不实现动作

OTA:运行态配网键按住 12s 走 usr_app_ota_init(任务上下文,红绿交替)。协议 OTA(jbOnOtaNotify本版 stub,禁止假装升级成功后擦用户区;加热垫同款直接 JB_OTA_STATE_ERR 也可。

配对超时 120s,失败保持开机,灯回「红灭 + 绿按作业 / 空闲」。

断连已配对:500ms 见宝重连广播 ↔ 500ms 扫描(第 8.2)。绑定广播仍用 CBR2 / PRCBR2,不要改成 JBao- 名(那是风扇温湿度窗)。


17. 建议文件清单(新建,加热垫 / 风扇已有的拷贝改)

apps/usr_periph/
  board_pin.h          # 引脚、时序、BOARD_xxx_ENABLE
  bsp_hw.c/.h          # LED 双通道 / KEY×2 / 泵双脚 GPIO
  usr_rtc.c/.h         # 已有则保留

apps/usr_app/
  key.c/.h, key_manager.c/.h     # 拷风扇;两实例:喷淋短按 + 配网 5s/12s
  led.c/.h, led_manager.c/.h     # 拷门夹双通道 + 红绿交替;事件表按第 6 章
  app_led.c/.h                   # 绿=作业,红=配对;OTA / 失败占双灯
  app_defer.c/.h                 # 拷加热垫 / 风扇
  app_pair.c/.h                  # 拷加热垫,红灯通道跟配对态
  app_pump.c/.h                  # 双脚成对切,禁止单脚接口
  app_spray.c/.h                 # 第 10 章作业引擎(唯一切泵者)
  app_th_scan.c/.h               # 500ms 交替扫描 JBao-{tank},取湿度
  app_linkage.c/.h               # 第 11 章湿度回差
  app_runrec.c/.h                # 泵实际导通计秒
  app_timer_task.c/.h            # 第 12 章到点触发 + 排队
  app_nvm.c/.h
  app_function.c/.h
  app_arbiter.c/.h

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


18. 分阶段交付与验收

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

目标: 双键、双灯、双脚泵可独立验证。

  • board_pin.h + UART TX=PC5,打印正常
  • 插电即开机(不按键,绿灯常亮,泵两脚低;未配对红灯灭;已配对未连接红灯 500ms 慢闪)
  • 单击 PB1:泵两脚同时高,按默认 10s × 3 次、间隔 3s 跑完;作业中绿灯 500ms 闪;结束后绿灯常亮、泵关
  • 作业中再点 PB1:立刻双脚低、绿灯回常亮
  • 示波器 / 逻辑:禁止出现「只高一只脚」
  • 间隔 3s 期间两脚都低,但绿灯仍闪(作业态未结束)
  • PA0 满 5s → 红灯 100ms 闪,绿灯不被关掉(仍常亮或仍闪)
  • 5s 后松手 → 清绑定进配对(红灯继续闪)
  • 满 5s 不松手到 12s → 不进配对,红绿交替进 OTA
  • 长按进配对 / OTA 后不复位(动作走任务上下文)
  • 上电强制 usr_goto_pair=1 已删除
  • 灯脚确认:PA8=绿、PA7=红(与加热垫相同)

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

P1 — 最小产品闭环

  • 5s 配对,APP 能写 DP1 开关喷淋;DP1 报作业态(间隔期间仍为 1)
  • APP 写合法 DP7:立刻按新参数开喷;PB1 再点能停
  • 非法 DP7(时长 0 或次数 0):不改配置、不开喷
  • 短按同步上报 DP1
  • VM:改 DP7 后断电,重新上电 不自动喷,但单击按上次配置喷
  • 首次上电 DP7 上报默认 00 0A 03 00 03 00 00 00
  • DP0 / 2 / 3 / 4 / 5 / 19 写忽略;设备不关、泵不跟这些 DP
  • DP6 WO:APP / 服务器能写下湿度;设备全量上报 不含 DP6 / DP5
  • 计划 7 组 + spray_plan_en 能存;BOARD_TIMER_ENABLE=0 时不执行
  • MAX_PACKAGE_LEN=512,APP 能读完整 124B 运行记录(本阶段记录可仍为 0)

P2 — 自动化

  • 断连:500ms 重连广播 / 500ms 扫描;已连接停扫描
  • 匹配 JBao-{缸体号} 且版本相同才更新湿度;温度不参与开停
  • 联动:H<Hmin 循环喷,H>Hmax 停,中间保持;无匹配包保持上一态
  • 缸体号 0 / min>max 不跑联动
  • 手动作废后须先 H>HmaxH<Hmin 才再开
  • 计划 RTC 门闩:未对时绝不新触发;对时后仅当分钟补触发,不补过点计划
  • 单次自毁(跑完或手动停都清使能)、每天 / 每周、同一分钟排队
  • 计划到点不中止当前轮;当前轮结束后才跑队列;结束后立刻把联动判一次
  • DP8 脉冲删除;DP9 重写立刻重判
  • 运行记录:泵实际导通 10 分钟落盘;对时后跨天左移;间隔秒不计入
  • 出厂重置清业务留配对,DP7 回默认
  • 仲裁表第 13 章逐条用例(见下)

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

  1. 空闲 H<Hmin → 开始循环喷;PB1 停 → 保持停,即使仍低于 Hmin。
  2. 用例 1 之后先到 H>Hmax,再回到 H<Hmin,才再次开。
  3. 计划到点 → 用该条参数开一轮;跑完结束;若此时 H<Hmin 且未作废 → 联动再开
  4. 计划轮未结束时再 PB1 停 → 保持停;单次计划使能被清;即使仍 H<Hmin不要被联动立刻打开。
  5. 手动一轮进行中,另一条计划到点 → 手动轮跑完后再跑计划,不插队中止。
  6. 未对时,计划配置使能也不触发。对时瞬间若刚好在开始时:分则补触发;过了该分钟不补。
  7. 上电 VM 有有效 DP7、暂无湿度 → 不喷;湿度到达后再按 11.3。
  8. H 在 [Hmin, Hmax] → 保持上一态。
  9. 断连扫描丢包 → 不停喷(保持上一态)。
  10. 已连接写 DP6 使 H<Hmin → 开;断开后扫描到同一缸体更高湿度 H>Hmax → 停。
  11. APP 写合法 DP7 → 中止当前轮并按新参数重开,联动作废。
  12. 间隔等待期间 DP1 仍为 1,绿灯仍闪,运行记录 不加 间隔秒。

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

#define BOARD_UART_TX_PIN         IO_PORTC_05
#define BOARD_KEY_SPRAY_PIN       IO_PORTB_01   /* 喷淋键,只短按 */
#define BOARD_KEY_PAIR_PIN        IO_PORTA_00   /* 配网键,5s / 12s */
#define BOARD_LED_RED_PIN         IO_PORTA_07   /* 红灯,低亮;与加热垫相同 */
#define BOARD_LED_GREEN_PIN       IO_PORTA_08   /* 绿灯,低亮;与加热垫相同 */
#define BOARD_PUMP_PWR_PIN        IO_PORTC_02   /* 泵 5V 供电,高导通 */
#define BOARD_PUMP_SW_PIN         IO_PORTA_06   /* 泵开关,高导通 */

#define BOARD_LED_ON_LEVEL        0
#define BOARD_PUMP_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_RECONN_ON_MS    500   /* 已配对未连接:红灯慢闪 */
#define BOARD_LED_RECONN_OFF_MS   500
#define BOARD_LED_SPRAY_ON_MS     500   /* 作业绿灯闪 */
#define BOARD_LED_SPRAY_OFF_MS    500
#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_ADV_SCAN_SLOT_MS    500   /* 重连广播 / 扫描各占一拍 */

#define BOARD_SPRAY_DEFAULT_DUR_S     10
#define BOARD_SPRAY_DEFAULT_COUNT     3
#define BOARD_SPRAY_DEFAULT_INTERVAL_S 3

#define BOARD_RUN_RECORD_DAYS     31
#define BOARD_RUN_RECORD_SAVE_SEC 600

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

风险 要求
只开 PC2 或只开 PA6 禁止。泵接口必须成对
上电按 VM 自动喷 禁止。水会喷出来
灯脚写反(PA8 红 / PA7 绿) 禁止。本机与加热垫相同:PA8 绿 / PA7 红
抄加热垫互斥灯(开红关绿) 禁止。红绿独立,空闲不要关红灯
抄加热垫时间窗定时 禁止。本机是到点触发一轮
抄加热垫过零 / 继电器 禁止
湿度当温度、或温度参与开停 本版禁止
丢广播就停喷 禁止(已拍板保持上一态)
计划过点后对时补喷 禁止;只补「当前这一分钟」
计划到点插队中止手动轮 禁止;排队
生成协议再导回 LEN=128 禁止;必须 512
app_main 上电清配对进绑定 必须删
20ms ISR 写 VM / 切 BLE 禁止(风扇已踩过栈溢出)
一排裸 static / 排障史注释 禁止。≥2 个状态收进一个 s_xxx。详见第 0.1 节
协议 OTA stub 返回 OK 当已升级 注释标明未升级,或直接 JB_OTA_STATE_ERR
扫描时改 GATT 名 不要改 0x2A00;只解析对方 adv
把 DP1 理解成「某一只脚的电平」 DP1 是作业态,间隔期间仍为开
把 DP3 / DP4 当喷淋参数 禁止;参数只走 DP7 和计划自带字段
喷淋键上做 5s / 12s 禁止;配网只在 PA0

21. 给 WorkBuddy 的推荐开工顺序

  1. MAX_PACKAGE_LEN=512jb_product.c 只保留 DP 赋值,业务迁 app_*。删 app_main 上电强制配对。
  2. board_pin.h + bsp_hw(双灯双键双脚泵);UART TX=PC5。
  3. 从加热垫拷 usr_jb_main 框架、defer、pair、nvm 骨架;key 改两实例;LED 拷门夹双通道,事件表按第 6 章重写。
  4. app_pump + app_spray:P0 验收单击默认 10/3/3、中途停、灯独立。
  5. P1DP1 / DP7、VM 不恢复开喷、忽略 DP、计划只存。
  6. P2app_th_scanapp_linkage、到点计划 + 排队、app_runrec、仲裁用例。

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


文档结束。 行为争议以本文第 5~14 章为准;字节布局以第 10.2 / 11.2 / 12.1 / 14.1 节为准。