1、喷淋器 P1/P2功能实现

2、红灯按连接态慢闪
This commit is contained in:
2026-09-09 19:57:00 +08:00
parent 074960eb02
commit 0dd65e1552
31 changed files with 2805 additions and 258 deletions
+85 -158
View File
@@ -14,6 +14,17 @@
#include "jb_product.h"
#include "board_pin.h"
#include "app_nvm.h"
#include "app_spray.h"
#include "app_linkage.h"
#include "app_timer_task.h"
#include "app_arbiter.h"
#include "app_function.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[JB_PROD]"
#define LOG_INFO_ENABLE
#include "debug.h"
/* ════════════════════════════════════════════════════════════════
* 全局变量
@@ -72,39 +83,24 @@ void userInit(void)
* ════════════════════════════════════════════════════════════════ */
void userHandle(void)
{
/* ── 可上报 DP (RO / RW): 用户数据采集 ── */
// gDevData.power = read_sensor(); /* DP0 总开关 */
// gDevData.spray_switch = read_sensor(); /* DP1 喷淋开关 */
// gDevData.mode = read_sensor(); /* DP2 模式 */
// gDevData.once_spray_time = read_sensor(); /* DP3 单次喷淋时长 */
// gDevData.continuous_spray_time = read_sensor(); /* DP4 连续喷淋时长 */
// memcpy(gDevData.spray_config, sensor_read(), 8); /* DP7 喷淋配置 */
// gDevData.linkage_delete = read_sensor(); /* DP8 联动删除 */
// memcpy(gDevData.linkage_config, sensor_read(), 20); /* DP9 联动配置 */
// gDevData.spray_plan_en = read_sensor(); /* DP10 喷淋计划总开关 */
// memcpy(gDevData.spray_plan1, sensor_read(), 15); /* DP11 喷淋计划1 */
// memcpy(gDevData.time_config2, sensor_read(), 15); /* DP12 喷淋计划2 */
// memcpy(gDevData.time_config3, sensor_read(), 15); /* DP13 喷淋计划3 */
// memcpy(gDevData.time_config4, sensor_read(), 15); /* DP14 喷淋计划4 */
// memcpy(gDevData.time_config5, sensor_read(), 15); /* DP15 喷淋计划5 */
// memcpy(gDevData.time_config6, sensor_read(), 15); /* DP16 喷淋计划6 */
// memcpy(gDevData.time_config7, sensor_read(), 15); /* DP17 喷淋计划7 */
// memcpy(gDevData.run_record, sensor_read(), 124); /* DP18 运行记录 */
// gDevData.fault_en = read_sensor(); /* DP20 设备故障使能 */
// gDevData.fault_code = read_sensor(); /* DP21 设备故障 */
// gDevData.alarm_en = read_sensor(); /* DP22 设备告警使能 */
// gDevData.alarm_code = read_sensor(); /* DP23 设备告警 */
// gDevData.reserva_dp1 = read_sensor(); /* DP25 预留DP1 */
// gDevData.reserva_dp2 = read_sensor(); /* DP26 预留DP2 */
// gDevData.reserva_dp3 = read_sensor(); /* DP27 预留DP3 */
/* DP1 由 app_function_poll 同步作业态;DP18 由 app_runrec 灌。
* 其余可上报字段在写路径 / VM 加载已就位,这里不再刷传感器。 */
}
/* ════════════════════════════════════════════════════════════════
* jbEventProcess — 0x03 APP 控制事件处理(下行 / DP 赋值方向)
*
* 协议层自动解包 0x03 帧后调用本函数
* info->dp_ids[] = 被控制的 DP ID 列表
* ctrl 已填入下发的值;复制到 gDevData 并执行动作
* 只做两件事:DP 写入 gDevData(合法时)+ 转调 app_* 模块
* 业务算法一律不在这里写(规格书硬约束 2)。
*
* P1 行为(规格书第 9 / 16 章):
* - 写忽略:DP0 / 2 / 3 / 4 / 5 / 19(不拷贝 gDevData,不动作,不回显)
* - DP1:作业开关,转 app_spray(引擎内部幂等:已喷保持,已停保持关)
* - DP6:WO 收下(P2 联动用),不上报(jbPackDps 只打 RO/RW
* - DP7:大端校验(时长 0 或次数 0 整包丢弃)→ 存 VM → 立刻重开一轮
* - DP8:脉冲删除(清 20B + 清回 0);DP9:收下存 VM(P2 联动引擎)
* - DP10~17 / DP20 / DP22:收下 + 存 VM
* - DP21 / 23:保持 0(无缺水检测);DP24:出厂重置(第 15 章)
* ════════════════════════════════════════════════════════════════ */
int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
{
@@ -117,216 +113,150 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
{
switch (info->dp_ids[i])
{
/* ── DP0 总开关 (bool) ── */
/* ── 写忽略:DP0 / 2 / 3 / 4 / 5 / 19(规格书第 9 章)── */
case DP_ID_POWER:
if (ctrl->power)
{
gDevData.power = 1;
// TODO: 开 — 执行动作
}
else
{
gDevData.power = 0;
// TODO: 关 — 执行动作
}
case DP_ID_MODE:
case DP_ID_ONCE_SPRAY_TIME:
case DP_ID_CONTINUOUS_SPRAY_TIME:
case DP_ID_CURRENT_TEMP:
case DP_ID_HEARTBEAT:
break;
/* ── DP1 喷淋开关 (bool) ── */
/* ── DP1 喷淋开关 (bool):作业态开关(请求在 20ms tick 消费)── */
case DP_ID_SPRAY_SWITCH:
if (ctrl->spray_switch)
{
gDevData.spray_switch = 1;
// TODO: 开 — 执行动作
app_arbiter_manual_intervene(); /* 13 章:DP1=1 也是手动抢权 */
app_spray_req_start(); /* 空闲才开;已在喷保持当前轮 */
}
else
{
gDevData.spray_switch = 0;
// TODO: 关 — 执行动作
app_spray_req_stop();
app_arbiter_manual_intervene(); /* 13 章:手动停作废联动 */
}
break;
/* ── DP2 模式 (enum) ── */
case DP_ID_MODE:
gDevData.mode = ctrl->mode;
switch (ctrl->mode)
{
case MODE_0: /* 关闭模式 */
// TODO: 处理 关闭模式
break;
case MODE_1: /* 单次喷淋 */
// TODO: 处理 单次喷淋
break;
case MODE_2: /* 连续喷淋 */
// TODO: 处理 连续喷淋
break;
default:
break;
}
break;
/* ── DP3 单次喷淋时长 (uint8) ── */
case DP_ID_ONCE_SPRAY_TIME:
gDevData.once_spray_time = ctrl->once_spray_time;
// TODO: 根据 ctrl->once_spray_time 执行动作
break;
/* ── DP4 连续喷淋时长 (uint8) ── */
case DP_ID_CONTINUOUS_SPRAY_TIME:
gDevData.continuous_spray_time = ctrl->continuous_spray_time;
// TODO: 根据 ctrl->continuous_spray_time 执行动作
break;
/* ── DP5 当前温度 (float, scale=0.1) ── */
case DP_ID_CURRENT_TEMP:
gDevData.current_temp = ctrl->current_temp;
// TODO: 根据 ctrl->current_temp 执行动作 (已是浮点物理量)
break;
/* ── DP6 当前湿度 (uint8) ── */
/* ── DP6 当前湿度 (uint8, WO):已连接=服务器下发 → 喂联动 ── */
case DP_ID_CURRENT_HUMI:
gDevData.current_humi = ctrl->current_humi;
// TODO: 根据 ctrl->current_humi 执行动作
app_linkage_set_connected_humi(ctrl->current_humi);
break;
/* ── DP7 喷淋配置 (raw) ── */
/* ── DP7 喷淋配置 (raw 8B):大端,非法整包丢弃(规格书 10.2)── */
case DP_ID_SPRAY_CONFIG:
memcpy(gDevData.spray_config, ctrl->spray_config, 8);
// TODO: 根据 gDevData.spray_config 执行动作 (长度 8 字节)
break;
{
uint16_t dur = (uint16_t)(((uint16_t)ctrl->spray_config[0] << 8)
| ctrl->spray_config[1]);
uint8_t cnt = ctrl->spray_config[2];
/* ── DP8 联动删除 (bool) ── */
if ((dur == 0) || (cnt == 0))
{
/* 非法包:不改 RAM / VM,不开喷,保持上一份有效配置 */
log_info("dp7 invalid (dur=%u cnt=%u), dropped\n",
(unsigned)dur, (unsigned)cnt);
break;
}
memcpy(gDevData.spray_config, ctrl->spray_config, 8);
app_nvm_mark_dirty(); /* 存 VM500ms 防抖) */
app_arbiter_manual_intervene(); /* 手动抢权:原联动作废(13 章) */
app_spray_req_restart(); /* 立刻按新参数开一轮(中止旧轮) */
break;
}
/* ── DP8 联动删除 (bool):脉冲清 20B 后清回 0(规格书 11.4)── */
case DP_ID_LINKAGE_DELETE:
if (ctrl->linkage_delete)
{
gDevData.linkage_delete = 1;
// TODO: 开 — 执行动作
}
else
{
gDevData.linkage_delete = 0;
// TODO: 关 — 执行动作
app_linkage_delete(); /* 清 RAM+VM 20B;联动占着则停 */
}
gDevData.linkage_delete = 0; /* 无动作也走脉冲:清回 0 再上报 */
break;
/* ── DP9 联动配置 (raw) ── */
/* ── DP9 联动配置 (raw 20B):存 VM + 清作废 + 立刻重判(11.5)── */
case DP_ID_LINKAGE_CONFIG:
memcpy(gDevData.linkage_config, ctrl->linkage_config, 20);
// TODO: 根据 gDevData.linkage_config 执行动作 (长度 20 字节)
app_linkage_set_config();
break;
/* ── DP10 喷淋计划总开关 (bool) ── */
/* ── DP10 喷淋计划总开关 (bool):存 VM=0 停新触发不拆当前轮 ── */
case DP_ID_SPRAY_PLAN_EN:
if (ctrl->spray_plan_en)
{
gDevData.spray_plan_en = 1;
// TODO: 开 — 执行动作
}
else
{
gDevData.spray_plan_en = 0;
// TODO: 关 — 执行动作
}
gDevData.spray_plan_en = ctrl->spray_plan_en ? 1 : 0;
app_timer_set_en(gDevData.spray_plan_en);
app_nvm_mark_dirty();
break;
/* ── DP11 喷淋计划1 (raw) ── */
/* ── DP11~17 计划 (raw 15B):收下存 VM(到点触发在引擎)── */
case DP_ID_SPRAY_PLAN1:
memcpy(gDevData.spray_plan1, ctrl->spray_plan1, 15);
// TODO: 根据 gDevData.spray_plan1 执行动作 (长度 15 字节)
app_nvm_mark_dirty();
break;
/* ── DP12 喷淋计划2 (raw) ── */
case DP_ID_TIME_CONFIG2:
memcpy(gDevData.time_config2, ctrl->time_config2, 15);
// TODO: 根据 gDevData.time_config2 执行动作 (长度 15 字节)
app_nvm_mark_dirty();
break;
/* ── DP13 喷淋计划3 (raw) ── */
case DP_ID_TIME_CONFIG3:
memcpy(gDevData.time_config3, ctrl->time_config3, 15);
// TODO: 根据 gDevData.time_config3 执行动作 (长度 15 字节)
app_nvm_mark_dirty();
break;
/* ── DP14 喷淋计划4 (raw) ── */
case DP_ID_TIME_CONFIG4:
memcpy(gDevData.time_config4, ctrl->time_config4, 15);
// TODO: 根据 gDevData.time_config4 执行动作 (长度 15 字节)
app_nvm_mark_dirty();
break;
/* ── DP15 喷淋计划5 (raw) ── */
case DP_ID_TIME_CONFIG5:
memcpy(gDevData.time_config5, ctrl->time_config5, 15);
// TODO: 根据 gDevData.time_config5 执行动作 (长度 15 字节)
app_nvm_mark_dirty();
break;
/* ── DP16 喷淋计划6 (raw) ── */
case DP_ID_TIME_CONFIG6:
memcpy(gDevData.time_config6, ctrl->time_config6, 15);
// TODO: 根据 gDevData.time_config6 执行动作 (长度 15 字节)
app_nvm_mark_dirty();
break;
/* ── DP17 喷淋计划7 (raw) ── */
case DP_ID_TIME_CONFIG7:
memcpy(gDevData.time_config7, ctrl->time_config7, 15);
// TODO: 根据 gDevData.time_config7 执行动作 (长度 15 字节)
app_nvm_mark_dirty();
break;
/* ── DP19 心跳 (bool) ── */
case DP_ID_HEARTBEAT:
if (ctrl->heartbeat)
{
gDevData.heartbeat = 1;
// TODO: 开 — 执行动作
}
else
{
gDevData.heartbeat = 0;
// TODO: 关 — 执行动作
}
/* ── 保持 0:DP21 / DP23(无缺水检测,规格书第 9 章)── */
case DP_ID_FAULT_CODE:
case DP_ID_ALARM_CODE:
break;
/* ── DP20 设备故障使能 (uint8) ── */
/* ── DP20 / DP22:原样收下存 VM(本版不做位级过滤)── */
case DP_ID_FAULT_EN:
gDevData.fault_en = ctrl->fault_en;
// TODO: 根据 ctrl->fault_en 执行动作
app_nvm_mark_dirty();
break;
/* ── DP22 设备告警使能 (uint8) ── */
case DP_ID_ALARM_EN:
gDevData.alarm_en = ctrl->alarm_en;
// TODO: 根据 ctrl->alarm_en 执行动作
app_nvm_mark_dirty();
break;
/* ── DP24 恢复出厂设置 (bool) ── */
/* ── DP24 恢复出厂 (bool):清业务 VM 留配对(规格书 15 章)── */
case DP_ID_FACTORY_RESET:
if (ctrl->factory_reset)
{
gDevData.factory_reset = 1;
// TODO: 开 — 执行动作
}
else
{
gDevData.factory_reset = 0;
// TODO: 关 — 执行动作
app_function_factory_reset();
}
gDevData.factory_reset = 0; /* WO 脉冲,不清会一直挂在 RAM */
break;
/* ── DP25 预留DP1 (uint32) ── */
/* ── DP25~27 预留 (uint32):存 RAM,无动作 ── */
case DP_ID_RESERVA_DP1:
gDevData.reserva_dp1 = ctrl->reserva_dp1;
// TODO: 根据 ctrl->reserva_dp1 执行动作
break;
/* ── DP26 预留DP2 (uint32) ── */
case DP_ID_RESERVA_DP2:
gDevData.reserva_dp2 = ctrl->reserva_dp2;
// TODO: 根据 ctrl->reserva_dp2 执行动作
break;
/* ── DP27 预留DP3 (uint32) ── */
case DP_ID_RESERVA_DP3:
gDevData.reserva_dp3 = ctrl->reserva_dp3;
// TODO: 根据 ctrl->reserva_dp3 执行动作
break;
default:
@@ -340,14 +270,11 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
* 返回 0=接受升级(协议层应答 JB_OK + MaxDataLen
* 非 0=拒绝,返回值即错误码(JB_OTA_*)
*
* ⚠ SOC 侧 OTA 未实现:当前仅应答,不执行任何升级动作。 */
* ⚠ SOC 侧协议 OTA 本版未实现:禁止假装升级成功。12s 走杰理双备份。 */
int8_t jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32)
{
(void)fwVersion; (void)fwLength; (void)fwCrc32;
// TODO: 检查 Flash 空间与版本,决定是否接收升级
// 允许: 返回 JB_OK(协议层应答 JB_OK + MaxDataLen
// 拒绝: 返回对应 JB_OTA_* 错误码(如 JB_OTA_VER_LOW / JB_OTA_SPACE_ERR
return JB_OK;
return JB_OTA_STATE_ERR;
}
/* 0x32 OTA 数据(含分片序号)*/