/* ================================================================ * 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(); }