/* ================================================================ * jb_product.c — 用户层实现(请填补空白处) * 设备: 侠客猫激光 | 生成时间: 2026-07-31 17:08:51 * * ══ 开发流程 ══ * 1. 实现硬件抽象: jbGetTimerCount(定时器由 SOC SDK 提供) * 发送由 SOC SDK 的 ble_send_data 承担,协议层已直接调用,无需 uartWrite * 2. userInit() 设置 DP 默认值与外设初始化 * 3. userHandle() 用户数据采集 * 4. jbEventProcess() 处理 APP 下发的控制命令 * 5. 主动上报 / OTA 的网关应答在收到 REPORTCMD_V2 后调用 jbReportAckCheck 清除等待 * 6. main.c / 调用方需周期性调用 jbReportAckCheck(cmd, cnt, int0) * ================================================================ */ #include "jb_product.h" /* ════════════════════════════════════════════════════════════════ * 全局变量 * ════════════════════════════════════════════════════════════════ */ jb_data_point_t gDevData; static volatile uint32_t timerMs; /* ════════════════════════════════════════════════════════════════ * userInit — DP 默认值 * ════════════════════════════════════════════════════════════════ */ void userInit(void) { memset(&gDevData, 0, sizeof(jb_data_point_t)); gDevData.power = 0; /* DP0 开关 0=关 1=开 */ gDevData.play_mode = 0; /* PLAY_MODE_0 (关) */ gDevData.hand_mode = 0; /* HAND_MODE_0 (关) */ gDevData.alm_low_battery = 0; /* DP3 低电量报警 0=关 1=开 */ gDevData.beep_trig = 0; /* DP4 声音开关 0=关 1=开 */ gDevData.battery = 0; /* DP5 电量上报 */ gDevData.shake_wake = 0; /* DP6 震动触发开机 0=关 1=开 */ memset(gDevData.no_disturb_enable, 0, 13); /* DP7 勿扰模式开关 */ gDevData.reserva_dp1 = 0; /* DP8 预留DP1 */ gDevData.reserva_dp2 = 0; /* DP9 预留DP2 */ gDevData.reserva_dp3 = 0; /* DP10 预留DP3 */ } /* ════════════════════════════════════════════════════════════════ * userHandle — 用户数据采集 * 协议层自动检测变化并上报(注意:此处是上报,不是下发控制) * ════════════════════════════════════════════════════════════════ */ void userHandle(void) { /* ── 可上报 DP (RO / RW): 用户数据采集 ── */ // gDevData.power = read_sensor(); /* DP0 开关 */ // gDevData.play_mode = read_sensor(); /* DP1 自动模式 */ // gDevData.alm_low_battery = read_sensor(); /* DP3 低电量报警 */ // gDevData.beep_trig = read_sensor(); /* DP4 声音开关 */ // gDevData.battery = read_sensor(); /* DP5 电量上报 */ // gDevData.shake_wake = read_sensor(); /* DP6 震动触发开机 */ // memcpy(gDevData.no_disturb_enable, sensor_read(), 13); /* DP7 勿扰模式开关 */ // gDevData.reserva_dp1 = read_sensor(); /* DP8 预留DP1 */ // gDevData.reserva_dp2 = read_sensor(); /* DP9 预留DP2 */ // gDevData.reserva_dp3 = read_sensor(); /* DP10 预留DP3 */ } /* ════════════════════════════════════════════════════════════════ * jbEventProcess — 0x03 APP 控制事件处理(下行 / DP 赋值方向) * * 协议层自动解包 0x03 帧后调用本函数。 * info->dp_ids[] = 被控制的 DP ID 列表 * ctrl 已填入下发的值;复制到 gDevData 并执行动作 * ════════════════════════════════════════════════════════════════ */ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl) { if (!info || !ctrl) { return -1; } for (uint8_t i = 0; i < info->count; i++) { switch (info->dp_ids[i]) { /* ── DP0 开关 (bool) ── */ case DP_ID_POWER: if (ctrl->power) { gDevData.power = 1; // TODO: 开 — 执行动作 } else { gDevData.power = 0; // TODO: 关 — 执行动作 } break; /* ── DP1 自动模式 (enum) ── */ case DP_ID_PLAY_MODE: gDevData.play_mode = ctrl->play_mode; switch (ctrl->play_mode) { case PLAY_MODE_0: /* 关 */ // TODO: 处理 关 break; case PLAY_MODE_1: /* mode1 */ // TODO: 处理 mode1 break; case PLAY_MODE_2: /* mode2 */ // TODO: 处理 mode2 break; case PLAY_MODE_3: /* mode3 */ // TODO: 处理 mode3 break; case PLAY_MODE_4: /* mode4 */ // TODO: 处理 mode4 break; default: break; } break; /* ── DP2 手动模式 (enum) ── */ case DP_ID_HAND_MODE: gDevData.hand_mode = ctrl->hand_mode; switch (ctrl->hand_mode) { case HAND_MODE_0: /* 关 */ // TODO: 处理 关 break; case HAND_MODE_1: /* 顺时针 */ // TODO: 处理 顺时针 break; case HAND_MODE_2: /* 逆时针 */ // TODO: 处理 逆时针 break; default: break; } break; /* ── DP4 声音开关 (bool) ── */ case DP_ID_BEEP_TRIG: if (ctrl->beep_trig) { gDevData.beep_trig = 1; // TODO: 开 — 执行动作 } else { gDevData.beep_trig = 0; // TODO: 关 — 执行动作 } break; /* ── DP6 震动触发开机 (bool) ── */ case DP_ID_SHAKE_WAKE: if (ctrl->shake_wake) { gDevData.shake_wake = 1; // TODO: 开 — 执行动作 } else { gDevData.shake_wake = 0; // TODO: 关 — 执行动作 } break; /* ── DP7 勿扰模式开关 (raw) ── */ case DP_ID_NO_DISTURB_ENABLE: memcpy(gDevData.no_disturb_enable, ctrl->no_disturb_enable, 13); // TODO: 根据 gDevData.no_disturb_enable 执行动作 (长度 13 字节) break; /* ── DP8 预留DP1 (uint32) ── */ case DP_ID_RESERVA_DP1: gDevData.reserva_dp1 = ctrl->reserva_dp1; // TODO: 根据 ctrl->reserva_dp1 执行动作 break; /* ── DP9 预留DP2 (uint32) ── */ case DP_ID_RESERVA_DP2: gDevData.reserva_dp2 = ctrl->reserva_dp2; // TODO: 根据 ctrl->reserva_dp2 执行动作 break; /* ── DP10 预留DP3 (uint32) ── */ case DP_ID_RESERVA_DP3: gDevData.reserva_dp3 = ctrl->reserva_dp3; // TODO: 根据 ctrl->reserva_dp3 执行动作 break; default: break; } } return 0; } /* 0x30 OTA 升级请求 */ void jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32) { (void)fwVersion; (void)fwLength; (void)fwCrc32; // TODO: 检查 Flash 空间与版本,决定是否接收升级 // 允许: 直接返回(协议层默认应答 JB_OK + MaxDataLen=128) // 拒绝: 在 jbHandleOtaNotify 中改为 jbSendFrame(CMD_OTA_NOTIFY_ACK, sn, 错误码, 3) } /* 0x32 OTA 数据(含分片序号)*/ 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) } /* 0x36 / 0x38 停止 OTA */ void jbOnOtaStop(void) { // TODO: 终止 OTA,释放资源 } /* 任意 MCU→BLE 请求的 ACK 回调(cmd 标识是哪条请求的应答,result 为结果码) - SOC 仅保留 OTA 主动请求应答: OTA完成 0x35 / 停止 0x39 - 可在本回调中设置"XX 完成/失败"标志,供主循环判断"确认成功才继续" */ 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: ... } } /* ════════════════════════════════════════════════════════════════ * 硬件抽象(按实际平台实现) * ════════════════════════════════════════════════════════════════ */ /* ── 定时器 ── */ void jbTimerIrq(void) { timerMs++; } /* 在 1ms 中断中调用本函数 */ uint32_t jbGetTimerCount(void) { return timerMs; } /* SOC: 发送由 SOC SDK 的 ble_send_data 承担,无需 uartInit / uartWrite; * 定时器由 SOC SDK 提供,仅保留 jbTimerIrq / jbGetTimerCount 接入即可。 */