363 lines
13 KiB
C
363 lines
13 KiB
C
/* ================================================================
|
||
* jb_product.c — 用户层实现(请填补空白处)
|
||
* 设备: 侠客猫激光 | 生成时间: 2026-07-28 15:42:40
|
||
*
|
||
* ══ 开发流程 ══
|
||
* 1. 实现硬件抽象: uartInit / timerInit / uartWrite / jbGetTimerCount
|
||
* 2. userInit() 设置 DP 默认值与外设初始化
|
||
* 3. userHandle() 用户数据采集
|
||
* 4. jbEventProcess() 处理 APP 下发的控制命令
|
||
* 5. main.c 模板:
|
||
*
|
||
* int main(void)
|
||
* {
|
||
* uartInit();
|
||
* timerInit();
|
||
* jbInit();
|
||
* userInit();
|
||
* while (1)
|
||
* {
|
||
* userHandle();
|
||
* jbProtocolHandle(&gDevData);
|
||
* }
|
||
* }
|
||
* ================================================================
|
||
*/
|
||
|
||
#include "jb_product.h"
|
||
#include "jb_port.h"
|
||
#include "main.h"
|
||
#include "ota_ab.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;
|
||
}
|
||
|
||
/* ════════════════════════════════════════════════════════════════
|
||
* BLE → MCU 回调(按需实现)
|
||
* ════════════════════════════════════════════════════════════════ */
|
||
|
||
/* 0x14 工作状态同步(含 RSSI)*/
|
||
void jbOnWorkState(uint8_t state, int8_t rssi)
|
||
{
|
||
(void)state;
|
||
(void)rssi;
|
||
// Bit0=已配网 Bit1=外网 Bit2=App在线 Bit3=BLE连接 Bit4=OTA Bit5=配对
|
||
// TODO: 按状态位更新状态指示;rssi: >=-60 优秀, -61~-70 良好, -71~-80 一般, -81~-90 较差, <-90 极差
|
||
}
|
||
|
||
/* 0x1A BLE 通知 MCU 重启 */
|
||
void jbOnBleRestartRequest(void)
|
||
{
|
||
ota_abort();
|
||
mcuRestart();
|
||
}
|
||
|
||
/* 0x30 OTA 升级请求
|
||
* 返回 0=接受升级(协议层应答 JB_OK + MaxDataLen)
|
||
* 非 0=拒绝,返回值即错误码(JB_OTA_*) */
|
||
int8_t jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32)
|
||
{
|
||
/* 版本校验:拒绝「版本一致」与「降级」,只允许升级到更高版本。 */
|
||
const uint32_t curVer = ((uint32_t)JB_MCU_SW_VERSION_MAJOR << 16) |
|
||
((uint32_t)JB_MCU_SW_VERSION_MINOR << 8) |
|
||
(uint32_t)JB_MCU_SW_VERSION_PATCH;
|
||
appPrintf(LOG_NOTIC, "OTA notify: ver=%06X cur=%06X len=%lu crc=%08X\r\n",
|
||
(unsigned int)fwVersion, (unsigned int)curVer,
|
||
(unsigned long)fwLength, (unsigned int)fwCrc32);
|
||
|
||
if (fwVersion == curVer)
|
||
{
|
||
appPrintf(LOG_NOTIC, "OTA reject: same version, no need to upgrade\r\n");
|
||
return JB_OTA_NO_NEED; /* 0x20 版本一致,无需升级 */
|
||
}
|
||
if (fwVersion < curVer)
|
||
{
|
||
appPrintf(LOG_NOTIC, "OTA reject: version lower than current\r\n");
|
||
return JB_OTA_VER_LOW; /* 0x21 版本低于当前,拒绝降级 */
|
||
}
|
||
|
||
if ((fwLength == 0) || (fwLength > OTA_SLOT_MAX_SIZE))
|
||
{
|
||
appPrintf(LOG_ERROR, "OTA reject: length %lu out of range\r\n", (unsigned long)fwLength);
|
||
return JB_OTA_SPACE_ERR;
|
||
}
|
||
if (ota_start(fwLength, fwCrc32) != 0)
|
||
{
|
||
return JB_OTA_STATE_ERR;
|
||
}
|
||
return JB_OK;
|
||
}
|
||
|
||
/* 0x32 OTA 数据(含分片序号)*/
|
||
void jbOnOtaData(const uint8_t *data, uint16_t len, uint16_t shardIdx, uint16_t totalShards)
|
||
{
|
||
if (ota_receive_chunk(data, len) != 0)
|
||
{
|
||
appPrintf(LOG_ERROR, "OTA chunk write fail (shard %u/%u)\r\n", shardIdx, totalShards);
|
||
return;
|
||
}
|
||
|
||
/* 以 0x30 的 fwLength 收满为准,不依赖分片序号 0/1 基 */
|
||
if (ota_is_download_complete())
|
||
{
|
||
appPrintf(LOG_NOTIC, "OTA download complete (shard %u/%u), verify...\r\n",
|
||
shardIdx, totalShards);
|
||
if (ota_finish() == 0)
|
||
{
|
||
appPrintf(LOG_NOTIC, "OTA verify OK, rebooting...\r\n");
|
||
HAL_Delay(100);
|
||
mcuRestart();
|
||
}
|
||
else
|
||
{
|
||
uint8_t ver[3] = {
|
||
JB_MCU_SW_VERSION_MAJOR,
|
||
JB_MCU_SW_VERSION_MINOR,
|
||
JB_MCU_SW_VERSION_PATCH};
|
||
appPrintf(LOG_ERROR, "OTA verify FAILED\r\n");
|
||
(void)jbSendOtaResult(JB_OTA_FILE_ERR, ver);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 0x36 / 0x38 停止 OTA */
|
||
void jbOnOtaStop(void)
|
||
{
|
||
ota_abort();
|
||
}
|
||
|
||
/* 0x1D 时间同步应答 */
|
||
void jbOnTimeSync(uint32_t timestamp)
|
||
{
|
||
/* Unix 时间戳(秒);后续可在此写入 MCU RTC */
|
||
appPrintf(LOG_NOTIC, "RTC time sync: timestamp=%lu\r\n",
|
||
(unsigned long)timestamp);
|
||
}
|
||
|
||
/* 任意 MCU→BLE 请求的 ACK 回调(cmd 标识是哪条请求的应答,result 为结果码)
|
||
- 如配对 0x11 / 产测 0x21 / 拍照 0x23 / 录像 0x25 / 音频 0x27 / OTA完成 0x35 / 停止 0x39
|
||
- 可在本回调中设置"XX 完成/失败"标志,供主循环判断"确认成功才继续" */
|
||
void jbOnAck(uint8_t cmd, uint8_t result)
|
||
{
|
||
(void)cmd;
|
||
(void)result;
|
||
// TODO: switch(cmd){ case CMD_PAIRING_ACK: gPairingDone = (result==JB_OK); break; ... }
|
||
}
|
||
|
||
/* ════════════════════════════════════════════════════════════════
|
||
* 硬件抽象(按实际平台实现)
|
||
* ════════════════════════════════════════════════════════════════ */
|
||
|
||
/* ── 定时器 ── */
|
||
void jbTimerIrq(void)
|
||
{
|
||
timerMs++;
|
||
} /* 在 1ms 中断中调用本函数 */
|
||
|
||
uint32_t jbGetTimerCount(void)
|
||
{
|
||
return timerMs;
|
||
}
|
||
|
||
void timerInit(void)
|
||
{
|
||
timerMs = 0;
|
||
jb_port_timer_init();
|
||
}
|
||
|
||
/* ── UART ── */
|
||
void uartInit(void)
|
||
{
|
||
jb_port_uart_init();
|
||
}
|
||
|
||
int32_t uartWrite(uint8_t *buf, uint32_t len)
|
||
{
|
||
return jb_port_uart_write(buf, len);
|
||
}
|
||
|
||
/* ── 系统 ── */
|
||
void mcuRestart(void)
|
||
{
|
||
NVIC_SystemReset();
|
||
}
|