P0/P1/P2功能实现:接入业务组合根(按键/LED/过零/配网/OTA),联动、11B定时、运行记录、灯泡寿命(DP3/DP4/DP25)、CBR0 VM、512分包
This commit is contained in:
@@ -1,18 +1,36 @@
|
||||
/* ================================================================
|
||||
* jb_product.c — 用户层实现(请填补空白处)
|
||||
* 设备: 电子开关-加热灯罩 | 生成时间: 2026-09-08 09:59:54
|
||||
* jb_product.c — 用户层实现(薄适配:只写 DP,不写业务算法)
|
||||
* 设备: 电子开关-加热灯罩 | 生成: 2026-09-08(V1.0.0 → P1 接线)
|
||||
*
|
||||
* ══ 开发流程 ══
|
||||
* 1. 实现硬件抽象: jbGetTimerCount(定时器由 SOC SDK 提供)
|
||||
* 发送由 SOC SDK 的 ble_send_data 承担,协议层已直接调用,无需 uartWrite
|
||||
* 2. userInit() 设置 DP 默认值与外设初始化
|
||||
* 3. userHandle() 用户数据采集
|
||||
* 4. jbEventProcess() 处理 APP 下发的控制命令
|
||||
* 5. 主动上报 / OTA 的网关应答在收到 REPORTCMD_V2 后调用 jbReportAckCheck 清除等待
|
||||
* ================================================================
|
||||
*/
|
||||
* 发送由 SOC SDK 的 ble_send_data 承担,协议层已直接调用
|
||||
* 2. userInit() 设置 DP 默认值
|
||||
* 3. userHandle() 用户数据采集(本机无传感器,DP 由 app_function_poll 同步)
|
||||
* 4. jbEventProcess() APP 下行:只写 gDevData 并转调 app_*,不在这里写业务
|
||||
*
|
||||
* ⚠ 规格书第 0 章第 2 条:**业务算法一律不进本文件**。
|
||||
* 继电器 → app_relay / app_arbiter;定时存储 → app_timer_task;
|
||||
* 灯泡寿命 → app_light_life;持久化 → app_nvm;出厂重置 → app_function。
|
||||
* ⚠ DP0 上报**实际吸合态**:本文件的赋值只表达"手动请求",app_function_poll
|
||||
* (20ms)会把它覆盖为过零后的真值,协议层差分上报读到的即实际态。
|
||||
* ⚠ DP3 剩余寿命 / DP25 告警由 app_light_life 维护并写 gDevData,本文件只做
|
||||
* APP 写 DP3/DP4 时的入口转发。
|
||||
* ================================================================ */
|
||||
|
||||
#include "jb_product.h"
|
||||
#include "app_relay.h"
|
||||
#include "app_arbiter.h"
|
||||
#include "app_linkage.h"
|
||||
#include "app_timer_task.h"
|
||||
#include "app_nvm.h"
|
||||
#include "app_function.h"
|
||||
#include "app_light_life.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[JB_PRD]"
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 全局变量
|
||||
@@ -22,86 +40,61 @@ jb_data_point_t gDevData;
|
||||
static volatile uint32_t timerMs;
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* userInit — DP 默认值
|
||||
* userInit — DP 默认值(剩余寿命/告警在 app_light_life_init 与
|
||||
* app_nvm_load 恢复,这里先置 0 与首上电语义一致)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
void userInit(void)
|
||||
{
|
||||
memset(&gDevData, 0, sizeof(jb_data_point_t));
|
||||
|
||||
gDevData.heat_switch = 0; /* DP0 灯泡开关 0=关 1=开 */
|
||||
gDevData.temp_control_switch = 0; /* DP1 温控开关 0=关 1=开 */
|
||||
gDevData.light_work_time = 0; /* DP2 灯泡总工作时长 */
|
||||
gDevData.lighe_remain_time = 0; /* DP3 灯泡剩余寿命 */
|
||||
gDevData.light_reset = 0; /* DP4 灯泡复位 0=关 1=开 */
|
||||
memset(gDevData.temp_control_time, 0, 4); /* DP5 温控时间 */
|
||||
gDevData.mode = 0; /* MODE_0 (??) */
|
||||
gDevData.min_temp = 0.0f; /* DP7 最低温度 (float) */
|
||||
gDevData.max_temp = 0.0f; /* DP8 最高温度 (float) */
|
||||
gDevData.current_temp = 0.0f; /* DP9 当前温度 (float) */
|
||||
gDevData.linkage_delete = 0; /* DP10 联动删除 0=关 1=开 */
|
||||
memset(gDevData.linkage_config, 0, 20); /* DP11 联动配置 */
|
||||
gDevData.time_en = 0; /* DP12 定时使能 0=关 1=开 */
|
||||
memset(gDevData.time_config1, 0, 11); /* DP13 定时配置1 */
|
||||
gDevData.heat_switch = 0; /* DP0 灯泡开关 0=关 1=开(由 VM 恢复) */
|
||||
gDevData.temp_control_switch = 0; /* DP1 温控开关(本版写忽略) */
|
||||
gDevData.light_work_time = 0; /* DP2 灯泡总工作时长(写忽略,上报恒 0) */
|
||||
gDevData.lighe_remain_time = 0; /* DP3 灯泡剩余寿命(app_light_life 维护) */
|
||||
gDevData.light_reset = 0; /* DP4 灯泡复位(WO,脉冲) */
|
||||
memset(gDevData.temp_control_time, 0, 4); /* DP5 温控时间(本版写忽略) */
|
||||
gDevData.mode = 0; /* DP6 模式(本版写忽略) */
|
||||
gDevData.min_temp = 0.0f; /* DP7 最低温度(本版写忽略) */
|
||||
gDevData.max_temp = 0.0f; /* DP8 最高温度(本版写忽略) */
|
||||
gDevData.current_temp = 0.0f; /* DP9 当前温度(WO,只收不下发) */
|
||||
gDevData.linkage_delete = 0; /* DP10 联动删除(脉冲;P2 接) */
|
||||
memset(gDevData.linkage_config, 0, 20); /* DP11 联动配置(P2 接) */
|
||||
gDevData.time_en = 0; /* DP12 定时使能(由 VM 恢复) */
|
||||
memset(gDevData.time_config1, 0, 11); /* DP13 定时配置1(11B) */
|
||||
memset(gDevData.time_config2, 0, 11); /* DP14 定时配置2 */
|
||||
memset(gDevData.time_config3, 0, 11); /* DP15 定时配置3 */
|
||||
memset(gDevData.time_config4, 0, 11); /* DP16 定时配置4 */
|
||||
memset(gDevData.time_config5, 0, 11); /* DP17 定时配置5 */
|
||||
memset(gDevData.time_config6, 0, 11); /* DP18 定时配置6 */
|
||||
memset(gDevData.time_config7, 0, 11); /* DP19 定时配置7 */
|
||||
memset(gDevData.run_record, 0, 124); /* DP20 运行记录 */
|
||||
gDevData.heartbeat = 0; /* DP21 心跳 0=关 1=开 */
|
||||
memset(gDevData.run_record, 0, 124); /* DP20 运行记录(RO,app_runrec 填) */
|
||||
gDevData.heartbeat = 0; /* DP21 心跳(本版忽略) */
|
||||
gDevData.fault_en = 0; /* DP22 设备故障使能 */
|
||||
gDevData.fault_code = 0; /* DP23 设备故障 */
|
||||
gDevData.fault_code = 0; /* DP23 设备故障(RO,过零超时=2) */
|
||||
gDevData.alarm_en = 0; /* DP24 设备告警使能 */
|
||||
gDevData.alarm_code = 0; /* DP25 设备告警 */
|
||||
gDevData.factory_reset = 0; /* DP26 恢复出厂设置 0=关 1=开 */
|
||||
gDevData.alarm_code = 0; /* DP25 设备告警(RO,剩余寿命推导) */
|
||||
gDevData.factory_reset = 0; /* DP26 恢复出厂设置(WO,写 1 触发) */
|
||||
gDevData.reserva_dp1 = 0; /* DP27 预留DP1 */
|
||||
gDevData.reserva_dp2 = 0; /* DP28 预留DP2 */
|
||||
gDevData.reserva_dp3 = 0; /* DP29 预留DP3 */
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* userHandle — 用户数据采集
|
||||
* 协议层自动检测变化并上报(注意:此处是上报,不是下发控制)
|
||||
* userHandle — 用户数据采集(500ms 由 usr_timer_loop 调用)
|
||||
* 本机无传感器:DP0 由 app_function_poll(20ms)同步实际态;
|
||||
* DP20 运行记录由 app_runrec、DP3/DP25 由 app_light_life 在任务上下文灌;
|
||||
* 断连扫描温度(P2)只进联动引擎,不填 DP9
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
void userHandle(void)
|
||||
{
|
||||
/* ── 可上报 DP (RO / RW): 用户数据采集 ── */
|
||||
// gDevData.heat_switch = read_sensor(); /* DP0 灯泡开关 */
|
||||
// gDevData.temp_control_switch = read_sensor(); /* DP1 温控开关 */
|
||||
// gDevData.light_work_time = read_sensor(); /* DP2 灯泡总工作时长 */
|
||||
// gDevData.lighe_remain_time = read_sensor(); /* DP3 灯泡剩余寿命 */
|
||||
// memcpy(gDevData.temp_control_time, sensor_read(), 4); /* DP5 温控时间 */
|
||||
// gDevData.mode = read_sensor(); /* DP6 模式 */
|
||||
// gDevData.min_temp = 23.5f; /* DP7 最低温度 (float, scale=0.1), 直接填物理量 */
|
||||
// gDevData.max_temp = 23.5f; /* DP8 最高温度 (float, scale=0.1), 直接填物理量 */
|
||||
// gDevData.current_temp = 23.5f; /* DP9 当前温度 (float, scale=0.1), 直接填物理量 */
|
||||
// gDevData.linkage_delete = read_sensor(); /* DP10 联动删除 */
|
||||
// memcpy(gDevData.linkage_config, sensor_read(), 20); /* DP11 联动配置 */
|
||||
// gDevData.time_en = read_sensor(); /* DP12 定时使能 */
|
||||
// memcpy(gDevData.time_config1, sensor_read(), 11); /* DP13 定时配置1 */
|
||||
// memcpy(gDevData.time_config2, sensor_read(), 11); /* DP14 定时配置2 */
|
||||
// memcpy(gDevData.time_config3, sensor_read(), 11); /* DP15 定时配置3 */
|
||||
// memcpy(gDevData.time_config4, sensor_read(), 11); /* DP16 定时配置4 */
|
||||
// memcpy(gDevData.time_config5, sensor_read(), 11); /* DP17 定时配置5 */
|
||||
// memcpy(gDevData.time_config6, sensor_read(), 11); /* DP18 定时配置6 */
|
||||
// memcpy(gDevData.time_config7, sensor_read(), 11); /* DP19 定时配置7 */
|
||||
// memcpy(gDevData.run_record, sensor_read(), 124); /* DP20 运行记录 */
|
||||
// gDevData.fault_en = read_sensor(); /* DP22 设备故障使能 */
|
||||
// gDevData.fault_code = read_sensor(); /* DP23 设备故障 */
|
||||
// gDevData.alarm_en = read_sensor(); /* DP24 设备告警使能 */
|
||||
// gDevData.alarm_code = read_sensor(); /* DP25 设备告警 */
|
||||
// gDevData.reserva_dp1 = read_sensor(); /* DP27 预留DP1 */
|
||||
// gDevData.reserva_dp2 = read_sensor(); /* DP28 预留DP2 */
|
||||
// gDevData.reserva_dp3 = read_sensor(); /* DP29 预留DP3 */
|
||||
/* 无本机温度/湿度采集 */
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbEventProcess — 0x03 APP 控制事件处理(下行 / DP 赋值方向)
|
||||
*
|
||||
* 协议层自动解包 0x03 帧后调用本函数。
|
||||
* info->dp_ids[] = 被控制的 DP ID 列表
|
||||
* ctrl 已填入下发的值;复制到 gDevData 并执行动作
|
||||
* ⚠ 只做两件事:DP 写入 gDevData + 转调 app_* 模块。业务算法一律
|
||||
* 不进本文件(规格书第 0 章第 2 条 / 15 章)。
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
{
|
||||
@@ -114,217 +107,141 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
{
|
||||
switch (info->dp_ids[i])
|
||||
{
|
||||
|
||||
/* ── DP0 灯泡开关 (bool) ── */
|
||||
/* ── DP0 灯泡开关:写 = 手动抢权,请求开/关(等过零)。
|
||||
* gDevData 值会被 app_function_poll 同步为实际吸合态再上报 ── */
|
||||
case DP_ID_HEAT_SWITCH:
|
||||
if (ctrl->heat_switch)
|
||||
{
|
||||
gDevData.heat_switch = 1;
|
||||
// TODO: 开 — 执行动作
|
||||
}
|
||||
else
|
||||
{
|
||||
gDevData.heat_switch = 0;
|
||||
// TODO: 关 — 执行动作
|
||||
}
|
||||
gDevData.heat_switch = ctrl->heat_switch ? 1 : 0;
|
||||
app_arbiter_manual_intervene(); /* 手动动作:联动/定时让位 */
|
||||
app_relay_request(gDevData.heat_switch);
|
||||
log_info("dp0 light request=%d\n", gDevData.heat_switch);
|
||||
break;
|
||||
|
||||
/* ── DP1 温控开关 (bool) ── */
|
||||
case DP_ID_TEMP_CONTROL_SWITCH:
|
||||
if (ctrl->temp_control_switch)
|
||||
{
|
||||
gDevData.temp_control_switch = 1;
|
||||
// TODO: 开 — 执行动作
|
||||
}
|
||||
else
|
||||
{
|
||||
gDevData.temp_control_switch = 0;
|
||||
// TODO: 关 — 执行动作
|
||||
}
|
||||
/* ── DP1/DP2/DP5~8:本版写忽略(规格书 9 章 / 15 章)。
|
||||
* 不更新 gDevData → 不动作、不回显、不落盘 ── */
|
||||
case DP_ID_TEMP_CONTROL_SWITCH: /* DP1 温控开关 */
|
||||
case DP_ID_LIGHT_WORK_TIME: /* DP2 灯泡总工作时长(恒 0) */
|
||||
case DP_ID_TEMP_CONTROL_TIME: /* DP5 温控时间 */
|
||||
case DP_ID_MODE: /* DP6 模式 */
|
||||
case DP_ID_MIN_TEMP: /* DP7 最低温度 */
|
||||
case DP_ID_MAX_TEMP: /* DP8 最高温度 */
|
||||
break;
|
||||
|
||||
/* ── DP2 灯泡总工作时长 (uint32) ── */
|
||||
case DP_ID_LIGHT_WORK_TIME:
|
||||
gDevData.light_work_time = ctrl->light_work_time;
|
||||
// TODO: 根据 ctrl->light_work_time 执行动作
|
||||
break;
|
||||
|
||||
/* ── DP3 灯泡剩余寿命 (uint32) ── */
|
||||
/* ── DP3 灯泡剩余寿命(13.2):APP 写立刻生效,0~0xFFFFFFFF 不封顶。
|
||||
* set_remain 内部同步 gDevData + 推导 alarm_code + 立刻落盘 ── */
|
||||
case DP_ID_LIGHE_REMAIN_TIME:
|
||||
gDevData.lighe_remain_time = ctrl->lighe_remain_time;
|
||||
// TODO: 根据 ctrl->lighe_remain_time 执行动作
|
||||
app_light_life_set_remain(ctrl->lighe_remain_time);
|
||||
break;
|
||||
|
||||
/* ── DP4 灯泡复位 (bool) ── */
|
||||
/* ── DP4 灯泡复位(WO,13.2):写 1 → 回额定值 2,592,000、告警 0、
|
||||
* 立刻落盘;写 0 无动作;WO 不上报,值清回 0 ── */
|
||||
case DP_ID_LIGHT_RESET:
|
||||
if (ctrl->light_reset)
|
||||
{
|
||||
gDevData.light_reset = 1;
|
||||
// TODO: 开 — 执行动作
|
||||
}
|
||||
else
|
||||
{
|
||||
gDevData.light_reset = 0;
|
||||
// TODO: 关 — 执行动作
|
||||
if (ctrl->light_reset) {
|
||||
app_light_life_reset();
|
||||
}
|
||||
gDevData.light_reset = 0;
|
||||
break;
|
||||
|
||||
/* ── DP5 温控时间 (raw) ── */
|
||||
case DP_ID_TEMP_CONTROL_TIME:
|
||||
memcpy(gDevData.temp_control_time, ctrl->temp_control_time, 4);
|
||||
// TODO: 根据 gDevData.temp_control_time 执行动作 (长度 4 字节)
|
||||
/* ── DP9 当前温度(WO):服务器下发 → 联动用温度(10.2/8 章)。
|
||||
* 协议层不打包不上报;联动引擎关时也只存 RAM ── */
|
||||
case DP_ID_CURRENT_TEMP:
|
||||
gDevData.current_temp = ctrl->current_temp;
|
||||
app_linkage_set_connected_temp(ctrl->current_temp);
|
||||
break;
|
||||
|
||||
/* ── DP6 模式 (enum) ── */
|
||||
case DP_ID_MODE:
|
||||
gDevData.mode = ctrl->mode;
|
||||
// TODO: 根据 ctrl->mode 执行动作
|
||||
break;
|
||||
|
||||
/* ── DP7 最低温度 (float, scale=0.1) ── */
|
||||
case DP_ID_MIN_TEMP:
|
||||
gDevData.min_temp = ctrl->min_temp;
|
||||
// TODO: 根据 ctrl->min_temp 执行动作 (已是浮点物理量)
|
||||
break;
|
||||
|
||||
/* ── DP8 最高温度 (float, scale=0.1) ── */
|
||||
case DP_ID_MAX_TEMP:
|
||||
gDevData.max_temp = ctrl->max_temp;
|
||||
// TODO: 根据 ctrl->max_temp 执行动作 (已是浮点物理量)
|
||||
break;
|
||||
|
||||
/* ── DP10 联动删除 (bool) ── */
|
||||
/* ── DP10 联动删除(脉冲,10.4):写 1 清配置并让出灯,
|
||||
* 然后清回 0 再上报;写 0 = 无操作 ── */
|
||||
case DP_ID_LINKAGE_DELETE:
|
||||
if (ctrl->linkage_delete)
|
||||
{
|
||||
gDevData.linkage_delete = 1;
|
||||
// TODO: 开 — 执行动作
|
||||
}
|
||||
else
|
||||
{
|
||||
gDevData.linkage_delete = 0;
|
||||
// TODO: 关 — 执行动作
|
||||
if (ctrl->linkage_delete) {
|
||||
app_linkage_delete();
|
||||
gDevData.linkage_delete = 0; /* 脉冲:清回 0 供上报 */
|
||||
}
|
||||
break;
|
||||
|
||||
/* ── DP11 联动配置 (raw) ── */
|
||||
/* ── DP11 联动配置:20B memcpy + 存 VM + 清手动作废 + 立刻重判(10.5)── */
|
||||
case DP_ID_LINKAGE_CONFIG:
|
||||
memcpy(gDevData.linkage_config, ctrl->linkage_config, 20);
|
||||
// TODO: 根据 gDevData.linkage_config 执行动作 (长度 20 字节)
|
||||
app_linkage_set_config();
|
||||
break;
|
||||
|
||||
/* ── DP12 定时使能 (bool) ── */
|
||||
/* ── DP12 定时使能:写 gDevData;引擎每 500ms 读 time_en(11.2)── */
|
||||
case DP_ID_TIME_EN:
|
||||
if (ctrl->time_en)
|
||||
{
|
||||
gDevData.time_en = 1;
|
||||
// TODO: 开 — 执行动作
|
||||
}
|
||||
else
|
||||
{
|
||||
gDevData.time_en = 0;
|
||||
// TODO: 关 — 执行动作
|
||||
}
|
||||
gDevData.time_en = ctrl->time_en ? 1 : 0;
|
||||
app_timer_set_en(gDevData.time_en);
|
||||
break;
|
||||
|
||||
/* ── DP13 定时配置1 (raw) ── */
|
||||
/* ── DP13~19 定时配置:11B 原样存 gDevData(规格书 11.1);
|
||||
* 引擎按窗口起止/星期直接读这些字段 ── */
|
||||
case DP_ID_TIME_CONFIG1:
|
||||
memcpy(gDevData.time_config1, ctrl->time_config1, 11);
|
||||
// TODO: 根据 gDevData.time_config1 执行动作 (长度 11 字节)
|
||||
app_timer_set_config(1);
|
||||
break;
|
||||
|
||||
/* ── DP14 定时配置2 (raw) ── */
|
||||
case DP_ID_TIME_CONFIG2:
|
||||
memcpy(gDevData.time_config2, ctrl->time_config2, 11);
|
||||
// TODO: 根据 gDevData.time_config2 执行动作 (长度 11 字节)
|
||||
app_timer_set_config(2);
|
||||
break;
|
||||
|
||||
/* ── DP15 定时配置3 (raw) ── */
|
||||
case DP_ID_TIME_CONFIG3:
|
||||
memcpy(gDevData.time_config3, ctrl->time_config3, 11);
|
||||
// TODO: 根据 gDevData.time_config3 执行动作 (长度 11 字节)
|
||||
app_timer_set_config(3);
|
||||
break;
|
||||
|
||||
/* ── DP16 定时配置4 (raw) ── */
|
||||
case DP_ID_TIME_CONFIG4:
|
||||
memcpy(gDevData.time_config4, ctrl->time_config4, 11);
|
||||
// TODO: 根据 gDevData.time_config4 执行动作 (长度 11 字节)
|
||||
app_timer_set_config(4);
|
||||
break;
|
||||
|
||||
/* ── DP17 定时配置5 (raw) ── */
|
||||
case DP_ID_TIME_CONFIG5:
|
||||
memcpy(gDevData.time_config5, ctrl->time_config5, 11);
|
||||
// TODO: 根据 gDevData.time_config5 执行动作 (长度 11 字节)
|
||||
app_timer_set_config(5);
|
||||
break;
|
||||
|
||||
/* ── DP18 定时配置6 (raw) ── */
|
||||
case DP_ID_TIME_CONFIG6:
|
||||
memcpy(gDevData.time_config6, ctrl->time_config6, 11);
|
||||
// TODO: 根据 gDevData.time_config6 执行动作 (长度 11 字节)
|
||||
app_timer_set_config(6);
|
||||
break;
|
||||
|
||||
/* ── DP19 定时配置7 (raw) ── */
|
||||
case DP_ID_TIME_CONFIG7:
|
||||
memcpy(gDevData.time_config7, ctrl->time_config7, 11);
|
||||
// TODO: 根据 gDevData.time_config7 执行动作 (长度 11 字节)
|
||||
app_timer_set_config(7);
|
||||
break;
|
||||
|
||||
/* ── DP21 心跳 (bool) ── */
|
||||
/* ── DP20 运行记录:RO,禁止 APP 写(规格书 15 章)── */
|
||||
case DP_ID_RUN_RECORD:
|
||||
break;
|
||||
|
||||
/* ── DP21 心跳:本版忽略 ── */
|
||||
case DP_ID_HEARTBEAT:
|
||||
if (ctrl->heartbeat)
|
||||
{
|
||||
gDevData.heartbeat = 1;
|
||||
// TODO: 开 — 执行动作
|
||||
}
|
||||
else
|
||||
{
|
||||
gDevData.heartbeat = 0;
|
||||
// TODO: 关 — 执行动作
|
||||
}
|
||||
break;
|
||||
|
||||
/* ── DP22 设备故障使能 (uint8) ── */
|
||||
/* ── DP22/DP24 使能:原样存(本版不做位级过滤,13 章"若有写入")── */
|
||||
case DP_ID_FAULT_EN:
|
||||
gDevData.fault_en = ctrl->fault_en;
|
||||
// TODO: 根据 ctrl->fault_en 执行动作
|
||||
app_nvm_mark_dirty(); /* 使能要持久化 */
|
||||
break;
|
||||
|
||||
/* ── DP24 设备告警使能 (uint8) ── */
|
||||
case DP_ID_ALARM_EN:
|
||||
gDevData.alarm_en = ctrl->alarm_en;
|
||||
// TODO: 根据 ctrl->alarm_en 执行动作
|
||||
app_nvm_mark_dirty();
|
||||
break;
|
||||
|
||||
/* ── DP26 恢复出厂设置 (bool) ── */
|
||||
/* ── DP23/DP25 故障/告警:RO,禁止 APP 写 ── */
|
||||
case DP_ID_FAULT_CODE:
|
||||
case DP_ID_ALARM_CODE:
|
||||
break;
|
||||
|
||||
/* ── DP26 恢复出厂设置:清业务 VM(留配对)+ RAM 复位(含寿命回
|
||||
* 额定 2,592,000、告警 0),不断开不软复位(规格书第 14 章)── */
|
||||
case DP_ID_FACTORY_RESET:
|
||||
if (ctrl->factory_reset)
|
||||
{
|
||||
gDevData.factory_reset = 1;
|
||||
// TODO: 开 — 执行动作
|
||||
}
|
||||
else
|
||||
{
|
||||
gDevData.factory_reset = 0;
|
||||
// TODO: 关 — 执行动作
|
||||
if (ctrl->factory_reset) {
|
||||
app_function_factory_reset();
|
||||
gDevData.factory_reset = 0; /* WO:清回 0 */
|
||||
log_info("dp26 factory reset\n");
|
||||
}
|
||||
break;
|
||||
|
||||
/* ── DP27 预留DP1 (uint32) ── */
|
||||
/* ── DP27~29 预留:存 RAM,不实现动作 ── */
|
||||
case DP_ID_RESERVA_DP1:
|
||||
gDevData.reserva_dp1 = ctrl->reserva_dp1;
|
||||
// TODO: 根据 ctrl->reserva_dp1 执行动作
|
||||
break;
|
||||
|
||||
/* ── DP28 预留DP2 (uint32) ── */
|
||||
case DP_ID_RESERVA_DP2:
|
||||
gDevData.reserva_dp2 = ctrl->reserva_dp2;
|
||||
// TODO: 根据 ctrl->reserva_dp2 执行动作
|
||||
break;
|
||||
|
||||
/* ── DP29 预留DP3 (uint32) ── */
|
||||
case DP_ID_RESERVA_DP3:
|
||||
gDevData.reserva_dp3 = ctrl->reserva_dp3;
|
||||
// TODO: 根据 ctrl->reserva_dp3 执行动作
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -338,14 +255,13 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
* 返回 0=接受升级(协议层应答 JB_OK + MaxDataLen)
|
||||
* 非 0=拒绝,返回值即错误码(JB_OTA_*)
|
||||
*
|
||||
* ⚠ SOC 侧 OTA 尚未实现:当前仅应答,不执行任何升级动作。 */
|
||||
* ⚠ SOC 侧协议 OTA 未实现:拒绝接收,避免 APP 继续推数据。
|
||||
* 按键 12s OTA 仍走 usr_app_ota_init(规格书 15 章:协议 OTA 可 stub,
|
||||
* 禁止假装升级成功后擦用户区)。 */
|
||||
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 数据(含分片序号)*/
|
||||
@@ -353,35 +269,32 @@ void jbOnOtaData(const uint8_t *data, uint16_t len,
|
||||
uint16_t shardIdx, uint16_t totalShards)
|
||||
{
|
||||
(void)data; (void)len; (void)shardIdx; (void)totalShards;
|
||||
// TODO: 写入 Flash,累计 CRC32
|
||||
// 全部数据接收完成后调用 jbSendOtaResult(JB_OK, mcuVersion)
|
||||
/* TODO(P2): 写入 Flash,累计 CRC32;全部接收后调用 jbSendOtaResult */
|
||||
}
|
||||
|
||||
/* 0x36 / 0x38 停止 OTA */
|
||||
void jbOnOtaStop(void)
|
||||
{
|
||||
// TODO: 终止 OTA,释放资源
|
||||
/* TODO(P2): 终止 OTA,释放资源 */
|
||||
}
|
||||
|
||||
/* 任意 MCU→BLE 请求的 ACK 回调(cmd 标识是哪条请求的应答,result 为结果码)
|
||||
- SOC 仅保留 OTA 主动请求应答: OTA完成 0x35 / 停止 0x39
|
||||
- 可在本回调中设置"XX 完成/失败"标志,供主循环判断"确认成功才继续" */
|
||||
/* 任意 MCU→BLE 请求的 ACK 回调 */
|
||||
void jbOnAck(uint8_t cmd, uint8_t result)
|
||||
{
|
||||
(void)cmd;
|
||||
(void)result;
|
||||
// TODO: switch(cmd){ case CMD_OTA_COMPLETE_ACK: gOtaDone = (result==JB_OK); break; case CMD_OTA_STOP_MCU_ACK: ... }
|
||||
/* TODO(P2): switch(cmd){ case CMD_OTA_COMPLETE_ACK: ... } */
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 硬件抽象(按实际平台实现)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* ── 定时器 ── */
|
||||
/* ── 定时器:由 usr_jb_main 的 20ms 节拍按 1ms 步进调用 jbTimerIrq() ── */
|
||||
void jbTimerIrq(void)
|
||||
{
|
||||
timerMs++;
|
||||
} /* 在 1ms 中断中调用本函数 */
|
||||
}
|
||||
|
||||
uint32_t jbGetTimerCount(void)
|
||||
{
|
||||
|
||||
@@ -211,14 +211,7 @@ static uint16_t jbPackDps(const jb_data_point_t *dp,
|
||||
jbWriteU16BE(&out[valIdx], (uint16_t)_raw);
|
||||
valIdx += 2;
|
||||
}
|
||||
/* DP9 当前温度 (float, 2B, RO (float, scale=0.1)) */
|
||||
if (mask[DP_ID_CURRENT_TEMP / 8] & (1 << (DP_ID_CURRENT_TEMP % 8)))
|
||||
{
|
||||
bitmap[DP_ID_CURRENT_TEMP / 8] |= (1 << (DP_ID_CURRENT_TEMP % 8));
|
||||
int16_t _raw = (int16_t)(dp->current_temp * 10.0f);
|
||||
jbWriteU16BE(&out[valIdx], (uint16_t)_raw);
|
||||
valIdx += 2;
|
||||
}
|
||||
/* DP9 当前温度:WO,不上报,不打包 */
|
||||
/* DP10 联动删除 (bool, 1B, RW) */
|
||||
if (mask[DP_ID_LINKAGE_DELETE / 8] & (1 << (DP_ID_LINKAGE_DELETE % 8)))
|
||||
{
|
||||
@@ -472,6 +465,20 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
|
||||
info->dp_ids[info->count++] = DP_ID_MAX_TEMP;
|
||||
}
|
||||
}
|
||||
/* DP9 当前温度 (float, 2B, WO (float, scale=0.1)) */
|
||||
if (bitmap[DP_ID_CURRENT_TEMP / 8] & (1 << (DP_ID_CURRENT_TEMP % 8)))
|
||||
{
|
||||
if (valIdx + 2 <= valLen)
|
||||
{
|
||||
int16_t _raw = (int16_t)jbReadU16BE(&values[valIdx]);
|
||||
dp->current_temp = (float)_raw * 0.1f;
|
||||
valIdx += 2;
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_CURRENT_TEMP;
|
||||
}
|
||||
}
|
||||
/* DP10 联动删除 (bool, 1B, RW) */
|
||||
if (bitmap[DP_ID_LINKAGE_DELETE / 8] & (1 << (DP_ID_LINKAGE_DELETE % 8)))
|
||||
{
|
||||
@@ -748,12 +755,7 @@ static uint8_t jbBuildDiffMask(const jb_data_point_t *last,
|
||||
mask[DP_ID_MAX_TEMP / 8] |= (1 << (DP_ID_MAX_TEMP % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP9 当前温度 */
|
||||
if ((int16_t)(last->current_temp * 10.0f) != (int16_t)(cur->current_temp * 10.0f))
|
||||
{
|
||||
mask[DP_ID_CURRENT_TEMP / 8] |= (1 << (DP_ID_CURRENT_TEMP % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP9 当前温度:WO,变化不上报 */
|
||||
/* DP10 联动删除 */
|
||||
if (last->linkage_delete != cur->linkage_delete)
|
||||
{
|
||||
@@ -814,12 +816,7 @@ static uint8_t jbBuildDiffMask(const jb_data_point_t *last,
|
||||
mask[DP_ID_TIME_CONFIG7 / 8] |= (1 << (DP_ID_TIME_CONFIG7 % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP20 运行记录 */
|
||||
if (memcmp(last->run_record, cur->run_record, 124))
|
||||
{
|
||||
mask[DP_ID_RUN_RECORD / 8] |= (1 << (DP_ID_RUN_RECORD % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP20 运行记录:不进差分(秒累加会刷 0x07);0x05 读 / 10min 全量仍带 */
|
||||
/* DP22 设备故障使能 */
|
||||
if (last->fault_en != cur->fault_en)
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
#define PKT_HEAD_0 0x55
|
||||
#define PKT_HEAD_1 0xAA
|
||||
#define MAX_PACKAGE_LEN 128
|
||||
#define MAX_PACKAGE_LEN 512
|
||||
#define JB_OTA_MAX_PAYLOAD 96 /* OTA 单包 Payload 上限(Byte); 须 <= MAX_PACKAGE_LEN - 11(0x32 帧开销) */
|
||||
|
||||
#define DP_COUNT 30
|
||||
@@ -127,7 +127,7 @@ typedef enum
|
||||
DP_ID_MODE = 6, /* 模式 (enum, RW) */
|
||||
DP_ID_MIN_TEMP = 7, /* 最低温度 (float, RW) */
|
||||
DP_ID_MAX_TEMP = 8, /* 最高温度 (float, RW) */
|
||||
DP_ID_CURRENT_TEMP = 9, /* 当前温度 (float, RO) */
|
||||
DP_ID_CURRENT_TEMP = 9, /* 当前温度 (float, WO) */
|
||||
DP_ID_LINKAGE_DELETE = 10, /* 联动删除 (bool, RW) */
|
||||
DP_ID_LINKAGE_CONFIG = 11, /* 联动配置 (raw, RW) */
|
||||
DP_ID_TIME_EN = 12, /* 定时使能 (bool, RW) */
|
||||
@@ -199,7 +199,7 @@ typedef struct
|
||||
uint8_t mode; /* DP6 模式 */
|
||||
float min_temp; /* DP7 最低温度 (float, scale=0.1) */
|
||||
float max_temp; /* DP8 最高温度 (float, scale=0.1) */
|
||||
float current_temp; /* DP9 当前温度 (float, scale=0.1) */
|
||||
float current_temp; /* DP9 当前温度 (float, scale=0.1, WO) */
|
||||
uint8_t linkage_delete;/* DP10 联动删除 */
|
||||
uint8_t linkage_config[20];/* DP11 联动配置 */
|
||||
uint8_t time_en; /* DP12 定时使能 */
|
||||
|
||||
+166
-15
@@ -1,18 +1,53 @@
|
||||
/******************************************************************************
|
||||
* @file usr_jb_main.c
|
||||
* @brief CBR0 业务组合根(加热灯罩):只编排调用顺序,不写业务算法
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.09.04
|
||||
* @version V1.2.0
|
||||
* @history
|
||||
* - V1.2.0, 2026.09.08, cyWu, 灯罩适配:接入灯泡寿命模块(规格书 13.2)
|
||||
* - V1.1.0, 2026.09.04, cyWu, 精简注释
|
||||
* - V1.0.0, 2026.09.04, cyWu, P0 组合根
|
||||
*
|
||||
* @note jb_product.c 不写业务。20ms tick 禁止写 VM / 切 BLE;
|
||||
* 配对、OTA、落盘在 500ms usr_timer_loop(任务上下文)。
|
||||
* 灯泡寿命:tick 在 ISR 扣秒,loop 做 60s 节流同步与边沿立刻落盘。
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "app_config.h"
|
||||
#include "app_main.h"
|
||||
#include "app_task.h"
|
||||
#include "user_cfg.h"
|
||||
#include "vm.h"
|
||||
#include "sys_time.h"
|
||||
|
||||
#include "jb_protocol.h"
|
||||
#include "jb_product.h"
|
||||
#include "jb_ringbuffer.h"
|
||||
#include "jb_common.h"
|
||||
#include "usr_le_product.h"
|
||||
#include "usr_le_api.h"
|
||||
#include "bsp_hw.h"
|
||||
#include "bsp_zc.h"
|
||||
#include "app_pair.h"
|
||||
#include "app_function.h"
|
||||
#include "app_relay.h"
|
||||
#include "app_zc.h"
|
||||
#include "app_led.h"
|
||||
#include "app_arbiter.h"
|
||||
#include "app_linkage.h"
|
||||
#include "app_runrec.h"
|
||||
#include "app_timer_task.h"
|
||||
#include "app_th_scan.h"
|
||||
#include "app_light_life.h"
|
||||
#include "app_nvm.h"
|
||||
#include "key_manager.h"
|
||||
|
||||
/* 向 usr_le_code 库提供产品身份(换产品只改 jb_protocol.h 宏即可) */
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[USR_JB]"
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/* 向 usr_le_code 提供产品身份(换产品只改 jb_protocol.h 宏即可) */
|
||||
const uint8_t g_usr_le_prodcode[USR_LE_PRODCODE_LEN] = {
|
||||
JB_CATEGORY_CODE[0], JB_CATEGORY_CODE[1],
|
||||
JB_CATEGORY_CODE[2], JB_CATEGORY_CODE[3]
|
||||
@@ -22,28 +57,144 @@ const uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN] = {
|
||||
JB_PRODUCT_CODE[3], JB_PRODUCT_CODE[4], JB_PRODUCT_CODE[5]
|
||||
};
|
||||
|
||||
void usr_timer_1_ms();
|
||||
static int jb_timer_id=0;
|
||||
#define USR_TIMER_IDLE_MS 20 /**< 全局统一节拍(规格书第 3 章) */
|
||||
|
||||
void usr_jb_init()
|
||||
static void usr_timer_tick(void *priv);
|
||||
static void usr_log_boot_version(void);
|
||||
|
||||
/**
|
||||
* @brief 20ms 业务节拍(硬件定时器中断)
|
||||
* @param priv 未使用
|
||||
* @note 禁止写 VM / 切 BLE / printf
|
||||
* @return 无
|
||||
*/
|
||||
static void usr_timer_tick(void *priv)
|
||||
{
|
||||
uint16_t dt = USR_TIMER_IDLE_MS;
|
||||
uint16_t i;
|
||||
|
||||
(void)priv;
|
||||
for (i = 0; i < dt; i++) {
|
||||
jbTimerIrq();
|
||||
}
|
||||
|
||||
user_key_scan(dt); /* 内部完成 5s/12s 长按分级,只置标志 */
|
||||
user_key_handle(); /* 派发短按:切灯 */
|
||||
app_zc_poll(dt); /* 只做 100ms 无沿超时兜底(切变在过零 ISR) */
|
||||
app_function_poll(); /* 同步 DP0 = 实际继电器态 */
|
||||
app_runrec_tick(dt); /* 实际吸合才累计;满 10 分钟只打脏,不写 flash */
|
||||
app_light_life_tick(dt); /* 实际吸合才扣寿命秒;60s 节流只置标志(13.2) */
|
||||
app_nvm_tick(dt); /* 变化检测 + 500ms 防抖计时(不写 flash!) */
|
||||
app_led_run(dt); /* 灯跟继电器实际态;内部推进配对会话 */
|
||||
}
|
||||
|
||||
#if (BOARD_ZC_ENABLE && BOARD_ZC_SW_POLL_1MS)
|
||||
/**
|
||||
* @brief 过零 1ms 软件采样兜底
|
||||
* @param priv 未使用
|
||||
* @return 无
|
||||
*/
|
||||
static void usr_timer_1ms(void *priv)
|
||||
{
|
||||
(void)priv;
|
||||
bsp_zc_scan_1ms();
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 上电打印固件版本与编译时间,方便串口对应当前是哪一包
|
||||
* @return 无
|
||||
*/
|
||||
static void usr_log_boot_version(void)
|
||||
{
|
||||
log_info("sw V%d.%d.%d hw V%d.%d.%d build %s %s\n",
|
||||
JB_MCU_SW_VERSION_MAJOR, JB_MCU_SW_VERSION_MINOR, JB_MCU_SW_VERSION_PATCH,
|
||||
JB_MCU_HW_VERSION_MAJOR, JB_MCU_HW_VERSION_MINOR, JB_MCU_HW_VERSION_PATCH,
|
||||
__DATE__, __TIME__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 业务初始化(上电即开机)
|
||||
* @return 无
|
||||
*/
|
||||
void usr_jb_init(void)
|
||||
{
|
||||
usr_log_boot_version(); /* UART 已在 app_main 起来,最先能看到版本 */
|
||||
|
||||
bsp_hw_init();
|
||||
app_led_init(); /* 先于一切业务:灯效事件表要最先装载 */
|
||||
|
||||
jbInit();
|
||||
userInit();
|
||||
if(jb_timer_id==0)jb_timer_id = sys_s_hi_timer_add(NULL, usr_timer_1_ms, TIMER_PERIOD_MS);
|
||||
|
||||
app_relay_init(); /* 执行器(请求/实际态)先于过零与仲裁 */
|
||||
app_zc_init(); /* PA1 双沿 → 继电器切变(app_relay 回调) */
|
||||
app_arbiter_init(); /* 占用仲裁(手动>定时>联动)先于联动/定时引擎 */
|
||||
app_linkage_init();
|
||||
app_runrec_init();
|
||||
app_timer_init();
|
||||
app_th_scan_init();
|
||||
app_light_life_init(); /* 剩余寿命先置额定值(VM 有效时下方 nvm_load 覆盖) */
|
||||
|
||||
app_nvm_init();
|
||||
app_nvm_load(); /* 回填灯泡请求/联动/定时/31 天记录/剩余寿命(13.2) */
|
||||
|
||||
app_function_init();
|
||||
|
||||
user_key_init();
|
||||
/* 上电时键若还按着:只锁短按(避免一上电就切灯),长按 5s/12s 分级照常生效,
|
||||
* 所以"插电时就按住 5s 进配对"这一用法仍然成立 */
|
||||
user_key_lock_if_held();
|
||||
|
||||
/* priority=1 常驻注册,永不重挂(220V 常电不需要低功耗) */
|
||||
usr_timer_add(NULL, usr_timer_tick, USR_TIMER_IDLE_MS, 1);
|
||||
|
||||
#if (BOARD_ZC_ENABLE && BOARD_ZC_SW_POLL_1MS)
|
||||
/* 过零软件兜底采样:真实 1ms 节拍(P33 中断之外的第二道保险) */
|
||||
usr_timer_add(NULL, usr_timer_1ms, 1, 1);
|
||||
#endif
|
||||
log_info("timer started, period=%ums, zc_irq=%d, zc_poll_1ms=%d\n",
|
||||
(unsigned)USR_TIMER_IDLE_MS, BOARD_ZC_USE_IRQ,
|
||||
(BOARD_ZC_ENABLE && BOARD_ZC_SW_POLL_1MS) ? 1 : 0);
|
||||
}
|
||||
|
||||
void usr_timer_1_ms()
|
||||
/**
|
||||
* @brief BLE 收到见宝载荷,送入协议环形缓冲
|
||||
* @param data 载荷指针
|
||||
* @param len 载荷长度,单位字节
|
||||
* @return 无
|
||||
*/
|
||||
void usr_jb_le_recieve_data(u8 *data, u16 len)
|
||||
{
|
||||
jbTimerIrq();
|
||||
jbPutData(data, len);
|
||||
}
|
||||
|
||||
void usr_jb_le_recieve_data(u8* data,u16 len)
|
||||
{
|
||||
jbPutData(data,len);
|
||||
}
|
||||
|
||||
void usr_timer_loop()///500ms
|
||||
/**
|
||||
* @brief 约 500ms:协议、自动化引擎、配对/OTA 落地(任务上下文)
|
||||
* @return 无
|
||||
*/
|
||||
void usr_timer_loop(void)
|
||||
{
|
||||
userHandle();
|
||||
jbProtocolHandle(&gDevData);
|
||||
app_th_scan_tick(); /* P2:断连时 500ms 重连广播/500ms 扫描交替 */
|
||||
app_linkage_run(500); /* P2:联动引擎(任务上下文;仲裁见 app_arbiter) */
|
||||
app_timer_run(500); /* P2:定时引擎(RTC 门闩 + 窗判断) */
|
||||
app_runrec_rotate_if_needed(); /* P2:跨天旋转/落盘/灌 DP20 */
|
||||
app_light_life_loop(); /* 灯泡寿命:60s 节流同步 / 边沿立刻落盘(13.2) */
|
||||
app_function_loop(); /* VM 防抖落盘 + 配对/OTA 动作落地 */
|
||||
|
||||
#if (BOARD_ZC_ENABLE && BOARD_ZC_DIAG_LOG)
|
||||
/* 台上诊断:确认过零 FPIN 中断真的在跑(edge 持续不涨 = 前端没信号/中断没起来) */
|
||||
{
|
||||
static uint32_t s_zc_cnt_last;
|
||||
uint32_t cnt = bsp_zc_edge_count();
|
||||
uint32_t last = bsp_zc_last_edge_ms();
|
||||
uint32_t ago = last ? ((uint32_t)timer_get_ms() - last) : 0xFFFFFFFFu;
|
||||
log_info("zc diag: edge=%u(+%u) ago=%ums lvl=%d req=%d act=%d\n",
|
||||
cnt, (uint32_t)(cnt - s_zc_cnt_last), ago, (int)bsp_zc_level(),
|
||||
(int)app_relay_get_request(), (int)app_relay_get_actual());
|
||||
s_zc_cnt_last = cnt;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user