添加普冉 PY32F040 OTA 双工程代码生成模板
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,362 @@
|
||||
/* ================================================================
|
||||
* 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();
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/* ================================================================
|
||||
* jb_product.h — 用户层头文件(自动生成)
|
||||
* 设备: 侠客猫激光 | 生成时间: 2026-07-28 15:42:40
|
||||
*
|
||||
* 声明用户需实现的函数。数据结构定义见 jb_protocol.h
|
||||
* ================================================================
|
||||
*/
|
||||
|
||||
#ifndef _JB_PRODUCT_H
|
||||
#define _JB_PRODUCT_H
|
||||
|
||||
#include "jb_protocol.h"
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 硬件配置(按实际 MCU 调整)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
#define UART_BAUDRATE 9600
|
||||
#define TIMER_PERIOD_MS 1
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 版本管理
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
/* MCU software version: Vmajor.minor.patch */
|
||||
#define JB_MCU_SW_VERSION_MAJOR 1
|
||||
#define JB_MCU_SW_VERSION_MINOR 0
|
||||
#define JB_MCU_SW_VERSION_PATCH 0
|
||||
|
||||
/* MCU hardware version: Vmajor.minor.patch */
|
||||
#define JB_MCU_HW_VERSION_MAJOR 1
|
||||
#define JB_MCU_HW_VERSION_MINOR 0
|
||||
#define JB_MCU_HW_VERSION_PATCH 0
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 用户 API
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* 全局设备数据 — 协议层直接读写该变量 */
|
||||
extern jb_data_point_t gDevData;
|
||||
|
||||
/* ==================== 用户必须实现 ============================== */
|
||||
void userInit(void); /* DP 默认值与外设初始化 */
|
||||
void userHandle(void); /* 用户数据采集 */
|
||||
void uartInit(void); /* UART 9600 8N1 */
|
||||
void timerInit(void); /* 1ms 定时器 */
|
||||
void jbTimerIrq(void); /* Call from 1ms timer ISR */
|
||||
void mcuRestart(void); /* 软件复位 */
|
||||
|
||||
/* =========== 协议回调(声明见 jb_protocol.h)================== */
|
||||
/* int8_t jbEventProcess(...); // 0x03 控制事件 */
|
||||
/* void jbOnWorkState(...); // 0x14 状态同步(含RSSI) */
|
||||
/* void jbOnBleRestartRequest(); // 0x1A BLE 通知重启 */
|
||||
/* void jbOnOtaNotify(...); // 0x30 OTA 请求 */
|
||||
/* void jbOnOtaData(...); // 0x32 OTA 数据 */
|
||||
/* void jbOnOtaStop(); // 0x36/0x38 停止 OTA */
|
||||
/* void jbOnTimeSync(...); // 0x1D 时间同步应答 */
|
||||
/* void jbOnAck(...); // 任意请求 ACK(结果) */
|
||||
|
||||
/* ============= 主动请求(声明见 jb_protocol.h)================ */
|
||||
/* int32_t jbRequestPairing(...); // 0x10 请求配对 */
|
||||
/* int32_t jbRequestTime(); // 0x1C 获取时间 */
|
||||
/* int32_t jbRequestBleReset(); // 0x16 重置 BLE */
|
||||
/* int32_t jbRequestBleRestart(); // 0x18 重启 BLE */
|
||||
/* int32_t jbRequestProduction(...); // 0x20 产测模式 */
|
||||
/* int32_t jbRequestPhoto(); // 0x22 开启拍照 */
|
||||
/* int32_t jbRequestRecord(...); // 0x24 开启录像 */
|
||||
/* int32_t jbRequestAudio(...); // 0x26 启动音频 */
|
||||
/* int32_t jbNotifyOtaStop(); // 0x38 通知 BLE 停止 OTA */
|
||||
/* int32_t jbSendOtaResult(...); // 0x34 OTA 结果 */
|
||||
|
||||
#endif /* _JB_PRODUCT_H */
|
||||
@@ -0,0 +1,943 @@
|
||||
/* ================================================================
|
||||
* jb_protocol.c — 见宝 IOT 协议实现(自动生成)
|
||||
* 设备: 侠客猫激光 | 生成时间: 2026-07-28 15:42:40
|
||||
* ================================================================
|
||||
*/
|
||||
|
||||
#include "jb_protocol.h"
|
||||
#include "jb_common.h"
|
||||
#include "jb_product.h"
|
||||
#include "jb_ringbuffer.h"
|
||||
#include "stdio.h"
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 全局实例
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
jb_protocol_t jbProtocol;
|
||||
static jb_ringbuffer_t rb; /* UART 接收缓冲区 */
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbInit — 协议层初始化
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
void jbInit(void)
|
||||
{
|
||||
memset(&jbProtocol, 0, sizeof(jb_protocol_t));
|
||||
jbRbInit(&rb);
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbPutData — UART 中断将数据压入环形缓冲区
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
int32_t jbPutData(uint8_t *buf, uint32_t len)
|
||||
{
|
||||
if (!buf)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
for (uint32_t i = 0; i < len; i++)
|
||||
{
|
||||
jbRbPush(&rb, buf[i]);
|
||||
}
|
||||
return (int32_t)len;
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbSendFrame — 组装协议帧并发送到 UART
|
||||
* 返回帧总长度,< 0 表示出错
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
static int32_t jbSendFrame(uint8_t cmd, uint8_t sn,
|
||||
const uint8_t *payload, uint16_t pLen)
|
||||
{
|
||||
uint8_t buf[MAX_PACKAGE_LEN];
|
||||
uint16_t lenField = pLen + 3; /* CMD + SN + payload + CKSUM */
|
||||
uint16_t total = lenField + 4;
|
||||
|
||||
if (total > MAX_PACKAGE_LEN)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 55 AA | LEN_H LEN_L | CMD | SN | payload | CKSUM */
|
||||
buf[0] = PKT_HEAD_0;
|
||||
buf[1] = PKT_HEAD_1;
|
||||
jbWriteU16BE(&buf[2], lenField);
|
||||
buf[4] = cmd;
|
||||
buf[5] = sn;
|
||||
if (payload && pLen > 0)
|
||||
{
|
||||
memcpy(&buf[6], payload, pLen);
|
||||
}
|
||||
buf[total - 1] = jbChecksum(buf, total);
|
||||
|
||||
return uartWrite(buf, total);
|
||||
}
|
||||
|
||||
/* 发送单字节结果应答 */
|
||||
static void jbSendAck(uint8_t cmd, uint8_t sn, uint8_t result)
|
||||
{
|
||||
jbSendFrame(cmd, sn, &result, 1);
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbGetOnePacket — 从环形缓冲区提取完整帧
|
||||
* 返回: 0 = 成功, 1 = 需要更多数据, -2 = 校验和错误
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
static int8_t jbGetOnePacket(uint8_t *out, uint16_t *outLen)
|
||||
{
|
||||
static uint8_t state = 0;
|
||||
static uint16_t expect = 0;
|
||||
static uint16_t idx = 0;
|
||||
static uint8_t prev = 0;
|
||||
|
||||
while (jbRbAvailable(&rb))
|
||||
{
|
||||
uint8_t b = jbRbPop(&rb);
|
||||
|
||||
if (state == 0)
|
||||
{
|
||||
/* 搜索同步头 55 AA */
|
||||
if (prev == PKT_HEAD_0 && b == PKT_HEAD_1)
|
||||
{
|
||||
out[0] = PKT_HEAD_0;
|
||||
out[1] = PKT_HEAD_1;
|
||||
idx = 2;
|
||||
state = 1;
|
||||
}
|
||||
}
|
||||
else if (state == 1)
|
||||
{
|
||||
/* 接收 LEN(2B) + CMD + SN */
|
||||
out[idx++] = b;
|
||||
if (idx == 6)
|
||||
{
|
||||
expect = 4 + jbReadU16BE(&out[2]);
|
||||
if (expect > MAX_PACKAGE_LEN || expect < 7)
|
||||
{
|
||||
state = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 接收剩余数据 */
|
||||
out[idx++] = b;
|
||||
if (idx >= expect)
|
||||
{
|
||||
state = 0;
|
||||
if (out[expect - 1] == jbChecksum(out, expect))
|
||||
{
|
||||
*outLen = expect;
|
||||
return 0;
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
prev = b;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbPackDps — dataPoint → outBuf[bitmap | values]
|
||||
* mask 指定要打包的 DP(bit 序号 = DP ID)
|
||||
* 返回 outBuf 总字节数(bitmap + values)
|
||||
* 仅打包 RO / RW 方向的 DP
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
static uint16_t jbPackDps(const jb_data_point_t *dp,
|
||||
const uint8_t *mask, uint8_t *outBuf)
|
||||
{
|
||||
uint8_t *bitmap = outBuf;
|
||||
uint8_t *out = outBuf + DP_BITMAP_SIZE;
|
||||
uint16_t valIdx = 0;
|
||||
memset(bitmap, 0, DP_BITMAP_SIZE);
|
||||
|
||||
/* DP0 开关 (bool, 1B, RW) */
|
||||
if (mask[DP_ID_POWER / 8] & (1 << (DP_ID_POWER % 8)))
|
||||
{
|
||||
bitmap[DP_ID_POWER / 8] |= (1 << (DP_ID_POWER % 8));
|
||||
out[valIdx++] = dp->power ? 1 : 0;
|
||||
}
|
||||
/* DP1 自动模式 (enum, 1B, RW) */
|
||||
if (mask[DP_ID_PLAY_MODE / 8] & (1 << (DP_ID_PLAY_MODE % 8)))
|
||||
{
|
||||
bitmap[DP_ID_PLAY_MODE / 8] |= (1 << (DP_ID_PLAY_MODE % 8));
|
||||
out[valIdx++] = (uint8_t)dp->play_mode;
|
||||
}
|
||||
/* DP3 低电量报警 (bool, 1B, RO) */
|
||||
if (mask[DP_ID_ALM_LOW_BATTERY / 8] & (1 << (DP_ID_ALM_LOW_BATTERY % 8)))
|
||||
{
|
||||
bitmap[DP_ID_ALM_LOW_BATTERY / 8] |= (1 << (DP_ID_ALM_LOW_BATTERY % 8));
|
||||
out[valIdx++] = dp->alm_low_battery ? 1 : 0;
|
||||
}
|
||||
/* DP4 声音开关 (bool, 1B, RW) */
|
||||
if (mask[DP_ID_BEEP_TRIG / 8] & (1 << (DP_ID_BEEP_TRIG % 8)))
|
||||
{
|
||||
bitmap[DP_ID_BEEP_TRIG / 8] |= (1 << (DP_ID_BEEP_TRIG % 8));
|
||||
out[valIdx++] = dp->beep_trig ? 1 : 0;
|
||||
}
|
||||
/* DP5 电量上报 (uint8, 1B, RO) */
|
||||
if (mask[DP_ID_BATTERY / 8] & (1 << (DP_ID_BATTERY % 8)))
|
||||
{
|
||||
bitmap[DP_ID_BATTERY / 8] |= (1 << (DP_ID_BATTERY % 8));
|
||||
out[valIdx++] = (uint8_t)dp->battery;
|
||||
}
|
||||
/* DP6 震动触发开机 (bool, 1B, RW) */
|
||||
if (mask[DP_ID_SHAKE_WAKE / 8] & (1 << (DP_ID_SHAKE_WAKE % 8)))
|
||||
{
|
||||
bitmap[DP_ID_SHAKE_WAKE / 8] |= (1 << (DP_ID_SHAKE_WAKE % 8));
|
||||
out[valIdx++] = dp->shake_wake ? 1 : 0;
|
||||
}
|
||||
/* DP7 勿扰模式开关 (raw, 13B, RW) */
|
||||
if (mask[DP_ID_NO_DISTURB_ENABLE / 8] & (1 << (DP_ID_NO_DISTURB_ENABLE % 8)))
|
||||
{
|
||||
bitmap[DP_ID_NO_DISTURB_ENABLE / 8] |= (1 << (DP_ID_NO_DISTURB_ENABLE % 8));
|
||||
memcpy(&out[valIdx], dp->no_disturb_enable, 13);
|
||||
valIdx += 13;
|
||||
}
|
||||
/* DP8 预留DP1 (uint32, 4B, RW) */
|
||||
if (mask[DP_ID_RESERVA_DP1 / 8] & (1 << (DP_ID_RESERVA_DP1 % 8)))
|
||||
{
|
||||
bitmap[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8));
|
||||
jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp1);
|
||||
valIdx += 4;
|
||||
}
|
||||
/* DP9 预留DP2 (uint32, 4B, RW) */
|
||||
if (mask[DP_ID_RESERVA_DP2 / 8] & (1 << (DP_ID_RESERVA_DP2 % 8)))
|
||||
{
|
||||
bitmap[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8));
|
||||
jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp2);
|
||||
valIdx += 4;
|
||||
}
|
||||
/* DP10 预留DP3 (uint32, 4B, RW) */
|
||||
if (mask[DP_ID_RESERVA_DP3 / 8] & (1 << (DP_ID_RESERVA_DP3 % 8)))
|
||||
{
|
||||
bitmap[DP_ID_RESERVA_DP3 / 8] |= (1 << (DP_ID_RESERVA_DP3 % 8));
|
||||
jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp3);
|
||||
valIdx += 4;
|
||||
}
|
||||
|
||||
return DP_BITMAP_SIZE + valIdx;
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbUnpackDps — bitmap+values → dataPoint + event_info
|
||||
* 仅解包 WO / RW 方向的 DP
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
|
||||
uint16_t valLen, jb_data_point_t *dp,
|
||||
jb_event_info_t *info)
|
||||
{
|
||||
uint16_t valIdx = 0;
|
||||
if (info)
|
||||
{
|
||||
info->count = 0;
|
||||
}
|
||||
|
||||
/* DP0 开关 (bool, 1B, RW) */
|
||||
if (bitmap[DP_ID_POWER / 8] & (1 << (DP_ID_POWER % 8)))
|
||||
{
|
||||
if (valIdx < valLen)
|
||||
{
|
||||
dp->power = (values[valIdx++] != 0);
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_POWER;
|
||||
}
|
||||
}
|
||||
/* DP1 自动模式 (enum, 1B, RW) */
|
||||
if (bitmap[DP_ID_PLAY_MODE / 8] & (1 << (DP_ID_PLAY_MODE % 8)))
|
||||
{
|
||||
if (valIdx < valLen)
|
||||
{
|
||||
dp->play_mode = values[valIdx++];
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_PLAY_MODE;
|
||||
}
|
||||
}
|
||||
/* DP2 手动模式 (enum, 1B, WO) */
|
||||
if (bitmap[DP_ID_HAND_MODE / 8] & (1 << (DP_ID_HAND_MODE % 8)))
|
||||
{
|
||||
if (valIdx < valLen)
|
||||
{
|
||||
dp->hand_mode = values[valIdx++];
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_HAND_MODE;
|
||||
}
|
||||
}
|
||||
/* DP4 声音开关 (bool, 1B, RW) */
|
||||
if (bitmap[DP_ID_BEEP_TRIG / 8] & (1 << (DP_ID_BEEP_TRIG % 8)))
|
||||
{
|
||||
if (valIdx < valLen)
|
||||
{
|
||||
dp->beep_trig = (values[valIdx++] != 0);
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_BEEP_TRIG;
|
||||
}
|
||||
}
|
||||
/* DP6 震动触发开机 (bool, 1B, RW) */
|
||||
if (bitmap[DP_ID_SHAKE_WAKE / 8] & (1 << (DP_ID_SHAKE_WAKE % 8)))
|
||||
{
|
||||
if (valIdx < valLen)
|
||||
{
|
||||
dp->shake_wake = (values[valIdx++] != 0);
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_SHAKE_WAKE;
|
||||
}
|
||||
}
|
||||
/* DP7 勿扰模式开关 (raw, 13B, RW) */
|
||||
if (bitmap[DP_ID_NO_DISTURB_ENABLE / 8] & (1 << (DP_ID_NO_DISTURB_ENABLE % 8)))
|
||||
{
|
||||
if (valIdx + 13 <= valLen)
|
||||
{
|
||||
memcpy(dp->no_disturb_enable, &values[valIdx], 13);
|
||||
valIdx += 13;
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_NO_DISTURB_ENABLE;
|
||||
}
|
||||
}
|
||||
/* DP8 预留DP1 (uint32, 4B, RW) */
|
||||
if (bitmap[DP_ID_RESERVA_DP1 / 8] & (1 << (DP_ID_RESERVA_DP1 % 8)))
|
||||
{
|
||||
if (valIdx + 4 <= valLen)
|
||||
{
|
||||
dp->reserva_dp1 = (uint32_t)jbReadU32BE(&values[valIdx]);
|
||||
valIdx += 4;
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_RESERVA_DP1;
|
||||
}
|
||||
}
|
||||
/* DP9 预留DP2 (uint32, 4B, RW) */
|
||||
if (bitmap[DP_ID_RESERVA_DP2 / 8] & (1 << (DP_ID_RESERVA_DP2 % 8)))
|
||||
{
|
||||
if (valIdx + 4 <= valLen)
|
||||
{
|
||||
dp->reserva_dp2 = (uint32_t)jbReadU32BE(&values[valIdx]);
|
||||
valIdx += 4;
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_RESERVA_DP2;
|
||||
}
|
||||
}
|
||||
/* DP10 预留DP3 (uint32, 4B, RW) */
|
||||
if (bitmap[DP_ID_RESERVA_DP3 / 8] & (1 << (DP_ID_RESERVA_DP3 % 8)))
|
||||
{
|
||||
if (valIdx + 4 <= valLen)
|
||||
{
|
||||
dp->reserva_dp3 = (uint32_t)jbReadU32BE(&values[valIdx]);
|
||||
valIdx += 4;
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_RESERVA_DP3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbBuildDiffMask — 比较上次与当前值,为变化的可上报 DP 置 mask 位
|
||||
* 返回: 1 = 有 DP 发生变化, 0 = 全部未变化
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
static uint8_t jbBuildDiffMask(const jb_data_point_t *last,
|
||||
const jb_data_point_t *cur,
|
||||
uint8_t *mask)
|
||||
{
|
||||
uint8_t hasChange = 0;
|
||||
memset(mask, 0, DP_BITMAP_SIZE);
|
||||
|
||||
/* DP0 开关 */
|
||||
if (last->power != cur->power)
|
||||
{
|
||||
mask[DP_ID_POWER / 8] |= (1 << (DP_ID_POWER % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP1 自动模式 */
|
||||
if (last->play_mode != cur->play_mode)
|
||||
{
|
||||
mask[DP_ID_PLAY_MODE / 8] |= (1 << (DP_ID_PLAY_MODE % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP3 低电量报警 */
|
||||
if (last->alm_low_battery != cur->alm_low_battery)
|
||||
{
|
||||
mask[DP_ID_ALM_LOW_BATTERY / 8] |= (1 << (DP_ID_ALM_LOW_BATTERY % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP4 声音开关 */
|
||||
if (last->beep_trig != cur->beep_trig)
|
||||
{
|
||||
mask[DP_ID_BEEP_TRIG / 8] |= (1 << (DP_ID_BEEP_TRIG % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP5 电量上报 */
|
||||
if (last->battery != cur->battery)
|
||||
{
|
||||
mask[DP_ID_BATTERY / 8] |= (1 << (DP_ID_BATTERY % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP6 震动触发开机 */
|
||||
if (last->shake_wake != cur->shake_wake)
|
||||
{
|
||||
mask[DP_ID_SHAKE_WAKE / 8] |= (1 << (DP_ID_SHAKE_WAKE % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP7 勿扰模式开关 */
|
||||
if (memcmp(last->no_disturb_enable, cur->no_disturb_enable, 13))
|
||||
{
|
||||
mask[DP_ID_NO_DISTURB_ENABLE / 8] |= (1 << (DP_ID_NO_DISTURB_ENABLE % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP8 预留DP1 */
|
||||
if (last->reserva_dp1 != cur->reserva_dp1)
|
||||
{
|
||||
mask[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP9 预留DP2 */
|
||||
if (last->reserva_dp2 != cur->reserva_dp2)
|
||||
{
|
||||
mask[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP10 预留DP3 */
|
||||
if (last->reserva_dp3 != cur->reserva_dp3)
|
||||
{
|
||||
mask[DP_ID_RESERVA_DP3 / 8] |= (1 << (DP_ID_RESERVA_DP3 % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
|
||||
return hasChange;
|
||||
}
|
||||
|
||||
/* 置全量掩码(所有可上报 DP)*/
|
||||
static void jbSetFullMask(uint8_t *mask)
|
||||
{
|
||||
memset(mask, 0xFF, DP_BITMAP_SIZE);
|
||||
if (DP_COUNT % 8)
|
||||
{
|
||||
mask[DP_BITMAP_SIZE - 1] &= (1 << (DP_COUNT % 8)) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbSendReqWithAck — 发送请求帧并登记 ACK 重传
|
||||
*
|
||||
* 与 jbSendFrame 不同,本函数把完整帧缓存到 waitAck.buf,
|
||||
* 并启动 ACK 等待。本协议约定 ACK 命令码 = 请求命令码 + 1
|
||||
* (如 0x10→0x11),收到对应 ACK 时由 jbWaitAckCheck() 清除
|
||||
* 等待;若 SEND_MAX_TIME 内未收到,jbAckRetry() 自动重传,
|
||||
* 最多 SEND_MAX_NUM 次。
|
||||
* 返回 uartWrite 结果(<0 出错)。
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
static int32_t jbSendReqWithAck(uint8_t cmd, uint8_t sn,
|
||||
const uint8_t *payload, uint16_t pLen)
|
||||
{
|
||||
uint16_t lenField = pLen + 3; /* CMD + SN + payload + CKSUM */
|
||||
uint16_t total = lenField + 4; /* + 55 AA + LEN(2) */
|
||||
|
||||
if (total > MAX_PACKAGE_LEN)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t buf[MAX_PACKAGE_LEN];
|
||||
buf[0] = PKT_HEAD_0;
|
||||
buf[1] = PKT_HEAD_1;
|
||||
jbWriteU16BE(&buf[2], lenField);
|
||||
buf[4] = cmd;
|
||||
buf[5] = sn;
|
||||
if (payload && pLen > 0)
|
||||
{
|
||||
memcpy(&buf[6], payload, pLen);
|
||||
}
|
||||
buf[total - 1] = jbChecksum(buf, total);
|
||||
|
||||
/* 缓存整帧,供超时重传 */
|
||||
memcpy(jbProtocol.waitAck.buf, buf, total);
|
||||
jbProtocol.waitAck.len = total;
|
||||
jbProtocol.waitAck.active = 1;
|
||||
jbProtocol.waitAck.sn = sn;
|
||||
jbProtocol.waitAck.retry = 0;
|
||||
jbProtocol.waitAck.sendTime = jbGetTimerCount();
|
||||
|
||||
return uartWrite(buf, total);
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbAckRetry — ACK 超时重传
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
static void jbAckRetry(void)
|
||||
{
|
||||
if (!jbProtocol.waitAck.active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (jbProtocol.waitAck.retry >= SEND_MAX_NUM)
|
||||
{
|
||||
memset(&jbProtocol.waitAck, 0, sizeof(jb_wait_ack_t));
|
||||
return;
|
||||
}
|
||||
if (jbGetTimerCount() - jbProtocol.waitAck.sendTime > SEND_MAX_TIME)
|
||||
{
|
||||
uartWrite(jbProtocol.waitAck.buf, jbProtocol.waitAck.len);
|
||||
jbProtocol.waitAck.retry++;
|
||||
jbProtocol.waitAck.sendTime = jbGetTimerCount();
|
||||
}
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbWaitAckCheck — ACK 等待处理
|
||||
*
|
||||
* 收到一个 BLE→MCU 的 ACK 帧后,在每个 ACK 的 case 中调用本函数。
|
||||
* 若当前有等待中的请求,且其命令码 +1 等于本帧命令码
|
||||
* (本协议约定:ACK 命令码 = 请求命令码 + 1,如 0x10→0x11),
|
||||
* 则清除 waitAck、停止重传。
|
||||
*
|
||||
* head: 该 ACK 帧首地址(即 jbProtocol.rxBuf)。
|
||||
* 返回: -1=参数错误; 1=命中并清除等待; 0=未命中(无等待或非对应 ACK)。
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
static int8_t jbWaitAckCheck(const uint8_t *head)
|
||||
{
|
||||
if (NULL == head)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (jbProtocol.waitAck.active &&
|
||||
(jbProtocol.waitAck.buf[4] + 1 == head[4]))
|
||||
{
|
||||
uint8_t cmd = head[4];
|
||||
uint8_t result = head[6]; /* 应答结果字节固定位于偏移 6 */
|
||||
memset(&jbProtocol.waitAck, 0, sizeof(jb_wait_ack_t));
|
||||
jbOnAck(cmd, result);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbSendReport — 发送主动上报 (0x07)
|
||||
*
|
||||
* mask 指定本次上报包含哪些 DP:
|
||||
* - 差分掩码(来自 jbBuildDiffMask) → 仅上报变化的 DP
|
||||
* - 全量掩码(来自 jbSetFullMask) → 定时全量同步
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
static void jbSendReport(jb_data_point_t *data, const uint8_t *mask)
|
||||
{
|
||||
uint8_t payload[MAX_PACKAGE_LEN];
|
||||
uint16_t plen = jbPackDps(data, mask, payload);
|
||||
uint8_t sn = jbProtocol.sn++;
|
||||
jbSendReqWithAck(CMD_REPORT, sn, payload, plen);
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbReportPolicy — 变化上报(差分) + 定时上报(全量)
|
||||
*
|
||||
* 策略:
|
||||
* 1. DP 发生变化 → 仅上报变化的 DP(差分掩码)
|
||||
* 2. 每隔 REPORT_PERIOD ms → 上报全部可上报 DP(全量同步)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
static void jbReportPolicy(jb_data_point_t *data)
|
||||
{
|
||||
uint32_t now = jbGetTimerCount();
|
||||
uint8_t diffMask[DP_BITMAP_SIZE];
|
||||
|
||||
/* ── 1. 变化上报(差分) ── */
|
||||
uint8_t hasChange = jbBuildDiffMask(&jbProtocol.lastData, data, diffMask);
|
||||
if (hasChange && !jbProtocol.waitAck.active &&
|
||||
(now - jbProtocol.lastChangeTime > REPORT_DEBOUNCE))
|
||||
{
|
||||
jbSendReport(data, diffMask);
|
||||
memcpy(&jbProtocol.lastData, data, sizeof(jb_data_point_t));
|
||||
jbProtocol.lastChangeTime = now;
|
||||
jbProtocol.lastReportTime = now;
|
||||
return;
|
||||
}
|
||||
|
||||
/* ── 2. 定时全量上报 ── */
|
||||
if (!jbProtocol.waitAck.active &&
|
||||
(now - jbProtocol.lastReportTime > REPORT_PERIOD))
|
||||
{
|
||||
uint8_t fullMask[DP_BITMAP_SIZE];
|
||||
jbSetFullMask(fullMask);
|
||||
jbSendReport(data, fullMask);
|
||||
memcpy(&jbProtocol.lastData, data, sizeof(jb_data_point_t));
|
||||
jbProtocol.lastReportTime = now;
|
||||
}
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 命令处理函数(BLE → MCU 下行帧)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* ── 0x03 APP 下发控制 ── */
|
||||
static void jbHandleControl(uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t sn = frame[5];
|
||||
uint8_t *bitmap = &frame[6];
|
||||
uint8_t *values = &frame[6 + DP_BITMAP_SIZE];
|
||||
uint16_t valLen = len - 7 - DP_BITMAP_SIZE;
|
||||
|
||||
jb_event_info_t info;
|
||||
jb_data_point_t ctrlData;
|
||||
memset(&ctrlData, 0, sizeof(ctrlData));
|
||||
jbUnpackDps(bitmap, values, valLen, &ctrlData, &info);
|
||||
|
||||
/* 分发给用户层;用户在回调中更新 gDevData */
|
||||
jbEventProcess(&info, &ctrlData);
|
||||
jbSendAck(CMD_CONTROL_ACK, sn, JB_OK);
|
||||
}
|
||||
|
||||
/* ── 0x05 APP 读取请求 ── */
|
||||
static void jbHandleRead(uint8_t *frame, uint16_t len,
|
||||
jb_data_point_t *data)
|
||||
{
|
||||
uint8_t sn = frame[5];
|
||||
uint8_t *reqBitmap = &frame[6];
|
||||
|
||||
uint8_t mask[DP_BITMAP_SIZE];
|
||||
memcpy(mask, reqBitmap, DP_BITMAP_SIZE);
|
||||
|
||||
/* bitmap 全 1(=0xFF...) 表示读取全部 DP */
|
||||
uint8_t allOnes = 1;
|
||||
for (int i = 0; i < DP_BITMAP_SIZE; i++)
|
||||
{
|
||||
if (mask[i] != 0xFF)
|
||||
{
|
||||
allOnes = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allOnes)
|
||||
{
|
||||
jbSetFullMask(mask);
|
||||
}
|
||||
|
||||
uint8_t payload[MAX_PACKAGE_LEN];
|
||||
uint16_t plen = jbPackDps(data, mask, payload);
|
||||
jbSendFrame(CMD_READ_ACK, sn, payload, plen);
|
||||
}
|
||||
|
||||
/* ── 0x01 BLE 请求设备身份信息 ── */
|
||||
static void jbHandleIdentityReq(uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t sn = frame[5];
|
||||
uint8_t payload[64];
|
||||
uint16_t p = 0;
|
||||
(void)len;
|
||||
payload[p++] = JB_OK;
|
||||
jbWriteU16BE(&payload[p], JB_PROTOCOL_VERSION);
|
||||
p += 2;
|
||||
memset(&payload[p], 0, 8);
|
||||
memcpy(&payload[p], JB_CATEGORY_CODE, sizeof(JB_CATEGORY_CODE) - 1);
|
||||
p += 8;
|
||||
memset(&payload[p], 0, 8);
|
||||
memcpy(&payload[p], JB_PRODUCT_CODE, sizeof(JB_PRODUCT_CODE) - 1);
|
||||
p += 8;
|
||||
memset(&payload[p], JB_DEVICE_SN, 16);
|
||||
p += 16;
|
||||
jbWriteU32BE(&payload[p], (uint32_t)(JB_DEVICE_CAPABILITY >> 32));
|
||||
p += 4;
|
||||
jbWriteU32BE(&payload[p], (uint32_t)(JB_DEVICE_CAPABILITY & 0xFFFFFFFFULL));
|
||||
p += 4;
|
||||
jbSendFrame(CMD_IDENTITY_ACK, sn, payload, p);
|
||||
}
|
||||
|
||||
/* ── 0x40 BLE 请求 MCU 信息 ── */
|
||||
static void jbHandleMcuInfoReq(uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t sn = frame[5];
|
||||
uint8_t payload[8];
|
||||
uint16_t p = 0;
|
||||
(void)len;
|
||||
payload[p++] = JB_OK;
|
||||
payload[p++] = JB_MCU_SW_VERSION_MAJOR;
|
||||
payload[p++] = JB_MCU_SW_VERSION_MINOR;
|
||||
payload[p++] = JB_MCU_SW_VERSION_PATCH;
|
||||
payload[p++] = JB_MCU_HW_VERSION_MAJOR;
|
||||
payload[p++] = JB_MCU_HW_VERSION_MINOR;
|
||||
payload[p++] = JB_MCU_HW_VERSION_PATCH;
|
||||
jbSendFrame(CMD_MCU_INFO_ACK, sn, payload, p);
|
||||
}
|
||||
|
||||
/* ── 0x12 心跳(BLE → MCU)── */
|
||||
static void jbHandleHeartbeat(uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t sn = frame[5];
|
||||
jbSendAck(CMD_HEARTBEAT_ACK, sn, JB_OK);
|
||||
}
|
||||
|
||||
/* ── 0x14 BLE 工作状态同步(含 RSSI)── */
|
||||
static void jbHandleWorkState(uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t sn = frame[5];
|
||||
uint8_t state = frame[6];
|
||||
int8_t rssi = (int8_t)frame[7]; /* uint8 补码 → int8 dBm */
|
||||
jbOnWorkState(state, rssi);
|
||||
jbSendAck(CMD_WORK_STATE_ACK, sn, JB_OK);
|
||||
}
|
||||
|
||||
/* ── 0x1A BLE 通知 MCU 重启 ── */
|
||||
static void jbHandleBleRestart(uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t sn = frame[5];
|
||||
jbSendAck(CMD_RESTART_MCU_ACK, sn, JB_OK);
|
||||
jbOnBleRestartRequest();
|
||||
}
|
||||
|
||||
/* ── 0x30 OTA 升级请求 ── */
|
||||
static void jbHandleOtaNotify(uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t sn = frame[5];
|
||||
uint32_t fwVersion = ((uint32_t)frame[6] << 16) | ((uint32_t)frame[7] << 8) | frame[8];
|
||||
uint32_t fwLength = jbReadU32BE(&frame[9]);
|
||||
uint32_t fwCrc32 = jbReadU32BE(&frame[13]);
|
||||
|
||||
/* 应答: 结果码 + 最大单包长度(2B)(协议原格式,3 字节) */
|
||||
int8_t otaRet = jbOnOtaNotify(fwVersion, fwLength, fwCrc32);
|
||||
uint8_t ack[3];
|
||||
ack[0] = (otaRet == 0) ? JB_OK : (uint8_t)otaRet;
|
||||
ack[1] = (uint8_t)(JB_OTA_MAX_PAYLOAD >> 8);
|
||||
ack[2] = (uint8_t)(JB_OTA_MAX_PAYLOAD & 0xFF);
|
||||
jbSendFrame(CMD_OTA_NOTIFY_ACK, sn, ack, 3);
|
||||
}
|
||||
|
||||
/* ── 0x32 OTA 数据(含分片序号)── */
|
||||
static void jbHandleOtaData(uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t sn = frame[5];
|
||||
uint16_t shardIdx = jbReadU16BE(&frame[6]);
|
||||
uint16_t totalShards = jbReadU16BE(&frame[8]);
|
||||
const uint8_t *pData = &frame[10];
|
||||
uint16_t dataLen = (len > 11) ? (len - 11) : 0;
|
||||
/* 先发本包 0x33 应答,再调用户回调满足「先回复、再重启」 */
|
||||
jbSendAck(CMD_OTA_DATA_ACK, sn, JB_OK);
|
||||
jbOnOtaData(pData, dataLen, shardIdx, totalShards);
|
||||
}
|
||||
|
||||
/* ── 0x36 BLE 通知停止 OTA ── */
|
||||
static void jbHandleOtaStop(uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t sn = frame[5];
|
||||
jbOnOtaStop();
|
||||
jbSendAck(CMD_OTA_STOP_BLE_ACK, sn, JB_OK);
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* MCU → BLE 请求 API
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* 0x10 请求配对(cfg_mode: 1=纯蓝牙 2=WIFI双模; param 仅 WIFI 有效)*/
|
||||
int32_t jbRequestPairing(uint8_t cfg_mode, uint16_t timeout_sec,
|
||||
const uint8_t *param, uint8_t param_len)
|
||||
{
|
||||
uint8_t payload[64];
|
||||
uint16_t p = 0;
|
||||
payload[p++] = cfg_mode;
|
||||
jbWriteU16BE(&payload[p], timeout_sec);
|
||||
p += 2;
|
||||
if (param && param_len > 0 && (p + 1 + param_len) <= sizeof(payload))
|
||||
{
|
||||
payload[p++] = param_len;
|
||||
memcpy(&payload[p], param, param_len);
|
||||
p += param_len;
|
||||
}
|
||||
uint8_t sn = jbProtocol.sn++;
|
||||
return jbSendReqWithAck(CMD_PAIRING_REQ, sn, payload, p);
|
||||
}
|
||||
|
||||
/* 0x1C 获取网络时间(等待 ACK,超时重传)*/
|
||||
int32_t jbRequestTime(void)
|
||||
{
|
||||
uint8_t sn = jbProtocol.sn++;
|
||||
return jbSendReqWithAck(CMD_TIME_REQ, sn, NULL, 0);
|
||||
}
|
||||
|
||||
/* 0x16 重置 BLE 模组(恢复出厂,等待 ACK 重传)*/
|
||||
int32_t jbRequestBleReset(void)
|
||||
{
|
||||
uint8_t sn = jbProtocol.sn++;
|
||||
return jbSendReqWithAck(CMD_RESET_BLE, sn, NULL, 0);
|
||||
}
|
||||
|
||||
/* 0x18 重启 BLE 模组(等待 ACK 重传)*/
|
||||
int32_t jbRequestBleRestart(void)
|
||||
{
|
||||
uint8_t sn = jbProtocol.sn++;
|
||||
return jbSendReqWithAck(CMD_RESTART_BLE, sn, NULL, 0);
|
||||
}
|
||||
|
||||
/* 0x20 产测模式(0=退出 1=进入,等待 ACK 重传)*/
|
||||
int32_t jbRequestProduction(uint8_t mode)
|
||||
{
|
||||
uint8_t sn = jbProtocol.sn++;
|
||||
return jbSendReqWithAck(CMD_PRODUCTION, sn, &mode, 1);
|
||||
}
|
||||
|
||||
/* 0x22 开启拍照(预留 2 字节,等待 ACK 重传)*/
|
||||
int32_t jbRequestPhoto(void)
|
||||
{
|
||||
uint8_t sn = jbProtocol.sn++;
|
||||
uint8_t payload[2] = {0, 0};
|
||||
return jbSendReqWithAck(CMD_PHOTO_REQ, sn, payload, 2);
|
||||
}
|
||||
|
||||
/* 0x24 开启录像(duration_sec: 0=关闭; 预留 2 字节,等待 ACK 重传)*/
|
||||
int32_t jbRequestRecord(uint16_t duration_sec)
|
||||
{
|
||||
uint8_t sn = jbProtocol.sn++;
|
||||
uint8_t payload[4];
|
||||
jbWriteU16BE(&payload[0], duration_sec);
|
||||
payload[2] = 0;
|
||||
payload[3] = 0;
|
||||
return jbSendReqWithAck(CMD_RECORD_REQ, sn, payload, 4);
|
||||
}
|
||||
|
||||
/* 0x26 启动音频(audio_id: 音频编号; 预留 2 字节,等待 ACK 重传)*/
|
||||
int32_t jbRequestAudio(uint8_t audio_id)
|
||||
{
|
||||
uint8_t sn = jbProtocol.sn++;
|
||||
uint8_t payload[3];
|
||||
payload[0] = audio_id;
|
||||
payload[1] = 0;
|
||||
payload[2] = 0;
|
||||
return jbSendReqWithAck(CMD_AUDIO_REQ, sn, payload, 3);
|
||||
}
|
||||
|
||||
/* 0x38 通知 BLE 停止 OTA(MCU 主动,等待 ACK 重传)*/
|
||||
int32_t jbNotifyOtaStop(void)
|
||||
{
|
||||
uint8_t sn = jbProtocol.sn++;
|
||||
return jbSendReqWithAck(CMD_OTA_STOP_MCU, sn, NULL, 0);
|
||||
}
|
||||
|
||||
/* 0x34 OTA 升级完成(result + MCU 版本 3B,等待 ACK 重传)*/
|
||||
int32_t jbSendOtaResult(uint8_t result, const uint8_t *mcuVersion)
|
||||
{
|
||||
uint8_t sn = jbProtocol.sn++;
|
||||
uint8_t payload[4];
|
||||
payload[0] = result;
|
||||
if (mcuVersion)
|
||||
{
|
||||
memcpy(&payload[1], mcuVersion, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&payload[1], 0, 3);
|
||||
}
|
||||
return jbSendReqWithAck(CMD_OTA_COMPLETE, sn, payload, 4);
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbProtocolHandle — 协议主循环(在 while(1) 中调用)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
int32_t jbProtocolHandle(jb_data_point_t *data)
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ACK 超时重传 */
|
||||
jbAckRetry();
|
||||
|
||||
/* 提取一帧 */
|
||||
uint16_t pktLen = 0;
|
||||
int8_t ret = jbGetOnePacket(jbProtocol.rxBuf, &pktLen);
|
||||
uint8_t cmd;
|
||||
if (ret == 0)
|
||||
{
|
||||
cmd = jbProtocol.rxBuf[4];
|
||||
printf("cmd: %02X\r\n", cmd);
|
||||
/* 按命令码统一分发 BLE → MCU 的所有帧(含 MCU 主动请求的应答)*/
|
||||
switch (cmd)
|
||||
{
|
||||
/* ── 身份信息 / MCU 信息 ── */
|
||||
case CMD_IDENTITY_REQ:
|
||||
jbHandleIdentityReq(jbProtocol.rxBuf, pktLen);
|
||||
break;
|
||||
case CMD_MCU_INFO_REQ:
|
||||
jbHandleMcuInfoReq(jbProtocol.rxBuf, pktLen);
|
||||
break;
|
||||
|
||||
/* ── APP-BLE 数据 ── */
|
||||
case CMD_CONTROL:
|
||||
jbHandleControl(jbProtocol.rxBuf, pktLen);
|
||||
break;
|
||||
case CMD_READ:
|
||||
jbHandleRead(jbProtocol.rxBuf, pktLen, data);
|
||||
break;
|
||||
|
||||
/* ── 心跳 / 状态同步 ── */
|
||||
case CMD_HEARTBEAT:
|
||||
jbHandleHeartbeat(jbProtocol.rxBuf, pktLen);
|
||||
break;
|
||||
case CMD_WORK_STATE:
|
||||
jbHandleWorkState(jbProtocol.rxBuf, pktLen);
|
||||
break;
|
||||
|
||||
/* ── BLE 通知 MCU 重启 ── */
|
||||
case CMD_RESTART_MCU:
|
||||
jbHandleBleRestart(jbProtocol.rxBuf, pktLen);
|
||||
break;
|
||||
|
||||
/* ── BLE-MCU OTA ── */
|
||||
case CMD_OTA_NOTIFY:
|
||||
jbHandleOtaNotify(jbProtocol.rxBuf, pktLen);
|
||||
break;
|
||||
case CMD_OTA_DATA:
|
||||
jbHandleOtaData(jbProtocol.rxBuf, pktLen);
|
||||
break;
|
||||
case CMD_OTA_STOP_BLE:
|
||||
jbHandleOtaStop(jbProtocol.rxBuf, pktLen);
|
||||
break;
|
||||
|
||||
/* ── BLE → MCU 的 ACK(MCU 主动请求后的应答)──
|
||||
各 ACK 分支统一调用 jbWaitAckCheck:命中等待则清除等待、
|
||||
停止重传,并回调 jbOnAck 通知应答结果;其余帧由 default 忽略。 */
|
||||
case CMD_TIME_ACK:
|
||||
jbWaitAckCheck(jbProtocol.rxBuf);
|
||||
if (pktLen >= 18)
|
||||
{
|
||||
uint32_t ts = jbReadU32BE(&jbProtocol.rxBuf[14]);
|
||||
jbOnTimeSync(ts);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_REPORT_ACK:
|
||||
case CMD_PAIRING_ACK:
|
||||
case CMD_RESET_BLE_ACK:
|
||||
case CMD_RESTART_BLE_ACK:
|
||||
case CMD_PRODUCTION_ACK:
|
||||
case CMD_PHOTO_ACK:
|
||||
case CMD_RECORD_ACK:
|
||||
case CMD_AUDIO_ACK:
|
||||
case CMD_OTA_COMPLETE_ACK:
|
||||
case CMD_OTA_STOP_MCU_ACK:
|
||||
jbWaitAckCheck(jbProtocol.rxBuf);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
jbReportPolicy(data);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
/* ================================================================
|
||||
* jb_protocol.h — 见宝 IOT 协议层(自动生成)
|
||||
* 设备: 侠客猫激光 | DP 数: 11 | Bitmap: 2B
|
||||
* 协议版本: V1.0 | 生成时间: 2026-07-28 15:42:40
|
||||
*
|
||||
* 帧格式: [55 AA] [LEN_H LEN_L] [CMD] [SN] [payload...] [CKSUM]
|
||||
* LEN = CMD(1) + SN(1) + payload + CKSUM(1) = payload + 3
|
||||
* CKSUM = sum(LEN_H .. payload_last) & 0xFF
|
||||
* Bitmap = 每个 DP 占 1 bit,bit 序号 = DP ID(0 起始)
|
||||
* Values = 按 bitmap 置位顺序排列,每个 DP 占完整 byte_length
|
||||
* ================================================================
|
||||
*/
|
||||
|
||||
#ifndef _JB_PROTOCOL_H
|
||||
#define _JB_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 协议常量
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
#define PKT_HEAD_0 0x55
|
||||
#define PKT_HEAD_1 0xAA
|
||||
#define MAX_PACKAGE_LEN 128
|
||||
#define JB_OTA_MAX_PAYLOAD 96 /* OTA 单包 Payload 上限(Byte); 须 <= MAX_PACKAGE_LEN - 11(0x32 帧开销) */
|
||||
|
||||
#define DP_COUNT 11
|
||||
#define DP_BITMAP_SIZE 2
|
||||
|
||||
#define SEND_MAX_TIME 2000 /* ACK 重传超时(ms) */
|
||||
#define SEND_MAX_NUM 3 /* 最大重传次数 */
|
||||
#define REPORT_DEBOUNCE 2000 /* 变化上报防抖时间(ms) */
|
||||
#define REPORT_PERIOD 600000 /* 定时全量上报周期(ms) */
|
||||
|
||||
/* Device identity: change these values for the target product. */
|
||||
#define JB_PROTOCOL_VERSION 0x0100 /* V1.0 */
|
||||
#define JB_CATEGORY_CODE "CBC0"
|
||||
#define JB_PRODUCT_CODE "PICBC0"
|
||||
#define JB_DEVICE_SN 0x00
|
||||
#define JB_DEVICE_CAPABILITY 0ULL
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 命令码(见《MCU方案接入协议通讯文档》)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
typedef enum
|
||||
{
|
||||
/* ── 设备身份信息(2) ── */
|
||||
CMD_IDENTITY_REQ = 0x01, /* BLE → MCU: 请求身份信息 */
|
||||
CMD_IDENTITY_ACK = 0x02, /* MCU → BLE: 身份应答 */
|
||||
|
||||
/* ── APP-BLE 数据(6) ── */
|
||||
CMD_CONTROL = 0x03, /* APP/BLE → MCU: 下发控制 */
|
||||
CMD_CONTROL_ACK = 0x04, /* MCU → APP/BLE: 控制应答 */
|
||||
CMD_READ = 0x05, /* APP/BLE → MCU: 读取请求 */
|
||||
CMD_READ_ACK = 0x06, /* MCU → APP/BLE: 读取应答 */
|
||||
CMD_REPORT = 0x07, /* MCU → APP/BLE: 主动上报 */
|
||||
CMD_REPORT_ACK = 0x08, /* APP/BLE → MCU: 上报应答 */
|
||||
|
||||
/* ── 配对 / 心跳 / 状态(6) ── */
|
||||
CMD_PAIRING_REQ = 0x10, /* MCU → BLE: 请求配对 */
|
||||
CMD_PAIRING_ACK = 0x11, /* BLE → MCU: 配对应答 */
|
||||
CMD_HEARTBEAT = 0x12, /* BLE → MCU: 心跳 */
|
||||
CMD_HEARTBEAT_ACK = 0x13, /* MCU → BLE: 心跳应答 */
|
||||
CMD_WORK_STATE = 0x14, /* BLE → MCU: 状态同步(含RSSI) */
|
||||
CMD_WORK_STATE_ACK= 0x15, /* MCU → BLE: 状态应答 */
|
||||
|
||||
/* ── BLE 模组控制(4) ── */
|
||||
CMD_RESET_BLE = 0x16, /* MCU → BLE: 重置 BLE(恢复出厂) */
|
||||
CMD_RESET_BLE_ACK = 0x17, /* BLE → MCU: 重置应答 */
|
||||
CMD_RESTART_BLE = 0x18, /* MCU → BLE: 重启 BLE */
|
||||
CMD_RESTART_BLE_ACK=0x19, /* BLE → MCU: 重启应答 */
|
||||
CMD_RESTART_MCU = 0x1A, /* BLE → MCU: 通知 MCU 重启 */
|
||||
CMD_RESTART_MCU_ACK=0x1B, /* MCU → BLE: 重启应答 */
|
||||
|
||||
/* ── 时间 / 产测(4) ── */
|
||||
CMD_TIME_REQ = 0x1C, /* MCU → BLE: 获取网络时间 */
|
||||
CMD_TIME_ACK = 0x1D, /* BLE → MCU: 时间应答 */
|
||||
CMD_PRODUCTION = 0x20, /* MCU → BLE: 产测模式 */
|
||||
CMD_PRODUCTION_ACK= 0x21, /* BLE → MCU: 产测应答 */
|
||||
|
||||
/* ── 拍照 / 录像 / 音频(6) ── */
|
||||
CMD_PHOTO_REQ = 0x22, /* MCU → BLE: 开启拍照 */
|
||||
CMD_PHOTO_ACK = 0x23, /* BLE → MCU: 拍照应答 */
|
||||
CMD_RECORD_REQ = 0x24, /* MCU → BLE: 开启录像 */
|
||||
CMD_RECORD_ACK = 0x25, /* BLE → MCU: 录像应答 */
|
||||
CMD_AUDIO_REQ = 0x26, /* MCU → BLE: 启动音频 */
|
||||
CMD_AUDIO_ACK = 0x27, /* BLE → MCU: 音频应答 */
|
||||
|
||||
/* ── OTA(10) ── */
|
||||
CMD_OTA_NOTIFY = 0x30, /* BLE → MCU: OTA 升级请求 */
|
||||
CMD_OTA_NOTIFY_ACK = 0x31, /* MCU → BLE: OTA 应答(含MaxLen) */
|
||||
CMD_OTA_DATA = 0x32, /* BLE → MCU: OTA 数据 */
|
||||
CMD_OTA_DATA_ACK = 0x33, /* MCU → BLE: OTA 数据应答 */
|
||||
CMD_OTA_COMPLETE = 0x34, /* MCU → BLE: OTA 完成 */
|
||||
CMD_OTA_COMPLETE_ACK = 0x35, /* BLE → MCU: OTA 完成应答 */
|
||||
CMD_OTA_STOP_BLE = 0x36, /* BLE → MCU: 停止 OTA */
|
||||
CMD_OTA_STOP_BLE_ACK = 0x37, /* MCU → BLE: 停止应答 */
|
||||
CMD_OTA_STOP_MCU = 0x38, /* MCU → BLE: 通知停止 OTA */
|
||||
CMD_OTA_STOP_MCU_ACK = 0x39, /* BLE → MCU: 停止应答 */
|
||||
|
||||
/* ── MCU 信息(2) ── */
|
||||
CMD_MCU_INFO_REQ = 0x40, /* BLE → MCU: 请求 MCU 信息 */
|
||||
CMD_MCU_INFO_ACK = 0x41, /* MCU → BLE: MCU 信息应答 */
|
||||
} jb_cmd_t;
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 结果码
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
typedef enum
|
||||
{
|
||||
JB_OK = 0x00,
|
||||
JB_PARAM_ERR = 0x01,
|
||||
JB_BUSY = 0x02,
|
||||
JB_NOT_SUPPORT = 0x03,
|
||||
JB_CRC_ERR = 0x04,
|
||||
JB_FAIL = 0x05,
|
||||
/* ── OTA 专用错误码 ── */
|
||||
JB_OTA_NO_NEED = 0x20, /* 无需升级(版本一致) */
|
||||
JB_OTA_VER_LOW = 0x21, /* OTA 版本低于当前版本 */
|
||||
JB_OTA_SPACE_ERR = 0x22, /* Flash 空间不足 */
|
||||
JB_OTA_STATE_ERR = 0x23, /* OTA 状态错误 */
|
||||
JB_OTA_FILE_ERR = 0x24, /* OTA 文件检验失败 */
|
||||
JB_OTA_FAIL = 0x25, /* 升级失败 */
|
||||
} jb_result_t;
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* BLE 工作状态位 (0x14)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
#define WS_CONFIGURED (1 << 0) /* 已配网 */
|
||||
#define WS_INTERNET (1 << 1) /* 已连外网 */
|
||||
#define WS_APP_ONLINE (1 << 2) /* App 在线 */
|
||||
#define WS_BLE_CONNECTED (1 << 3) /* 手机 BLE 已连接 */
|
||||
#define WS_OTA_UPGRADING (1 << 4) /* OTA 升级中 */
|
||||
#define WS_PAIRING_MODE (1 << 5) /* 配对模式激活 */
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 设备能力位图 (0x02 Capability Bitmap, 8B 大端)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
#define CAP_BLE_OTA (1ULL << 0) /* BLE OTA */
|
||||
#define CAP_MCU_OTA (1ULL << 1) /* MCU OTA */
|
||||
#define CAP_APP_DIRECT (1ULL << 2) /* APP 直连 BLE */
|
||||
#define CAP_IPC_GATEWAY (1ULL << 3) /* IPC 网关连接 */
|
||||
#define CAP_PRODUCTION (1ULL << 4) /* 产测模式 */
|
||||
#define CAP_PARAM_CFG (1ULL << 5) /* 参数配置 */
|
||||
#define CAP_LOG (1ULL << 6) /* 日志功能 */
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 身份信息 (0x02) / MCU 信息 (0x41)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* DP ID 枚举(从 Excel 自动生成)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
typedef enum
|
||||
{
|
||||
DP_ID_POWER = 0, /* 开关 (bool, RW) */
|
||||
DP_ID_PLAY_MODE = 1, /* 自动模式 (enum, RW) */
|
||||
DP_ID_HAND_MODE = 2, /* 手动模式 (enum, WO) */
|
||||
DP_ID_ALM_LOW_BATTERY = 3, /* 低电量报警 (bool, RO) */
|
||||
DP_ID_BEEP_TRIG = 4, /* 声音开关 (bool, RW) */
|
||||
DP_ID_BATTERY = 5, /* 电量上报 (uint8, RO) */
|
||||
DP_ID_SHAKE_WAKE = 6, /* 震动触发开机 (bool, RW) */
|
||||
DP_ID_NO_DISTURB_ENABLE = 7, /* 勿扰模式开关 (raw, RW) */
|
||||
DP_ID_RESERVA_DP1 = 8, /* 预留DP1 (uint32, RW) */
|
||||
DP_ID_RESERVA_DP2 = 9, /* 预留DP2 (uint32, RW) */
|
||||
DP_ID_RESERVA_DP3 = 10, /* 预留DP3 (uint32, RW) */
|
||||
DP_ID_MAX
|
||||
} dp_id_t;
|
||||
|
||||
/* DP 数据长度(字节) */
|
||||
#define DP_LEN_POWER 1
|
||||
#define DP_LEN_PLAY_MODE 1
|
||||
#define DP_LEN_HAND_MODE 1
|
||||
#define DP_LEN_ALM_LOW_BATTERY 1
|
||||
#define DP_LEN_BEEP_TRIG 1
|
||||
#define DP_LEN_BATTERY 1
|
||||
#define DP_LEN_SHAKE_WAKE 1
|
||||
#define DP_LEN_NO_DISTURB_ENABLE 13
|
||||
#define DP_LEN_RESERVA_DP1 4
|
||||
#define DP_LEN_RESERVA_DP2 4
|
||||
#define DP_LEN_RESERVA_DP3 4
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* DP 枚举值定义
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
/* 自动模式 */
|
||||
typedef enum
|
||||
{
|
||||
PLAY_MODE_0 = 0, /* 关 */
|
||||
PLAY_MODE_1 = 1, /* mode1 */
|
||||
PLAY_MODE_2 = 2, /* mode2 */
|
||||
PLAY_MODE_3 = 3, /* mode3 */
|
||||
PLAY_MODE_4 = 4, /* mode4 */
|
||||
} PLAY_MODE_t;
|
||||
|
||||
/* 手动模式 */
|
||||
typedef enum
|
||||
{
|
||||
HAND_MODE_0 = 0, /* 关 */
|
||||
HAND_MODE_1 = 1, /* 顺时针 */
|
||||
HAND_MODE_2 = 2, /* 逆时针 */
|
||||
} HAND_MODE_t;
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 数据结构
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* 数据点 — 所有 DP 的当前值 */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t power; /* DP0 开关 */
|
||||
uint8_t play_mode; /* DP1 自动模式 */
|
||||
uint8_t hand_mode; /* DP2 手动模式 */
|
||||
uint8_t alm_low_battery;/* DP3 低电量报警 */
|
||||
uint8_t beep_trig; /* DP4 声音开关 */
|
||||
uint8_t battery; /* DP5 电量上报 */
|
||||
uint8_t shake_wake; /* DP6 震动触发开机 */
|
||||
uint8_t no_disturb_enable[13];/* DP7 勿扰模式开关 */
|
||||
uint32_t reserva_dp1; /* DP8 预留DP1 */
|
||||
uint32_t reserva_dp2; /* DP9 预留DP2 */
|
||||
uint32_t reserva_dp3; /* DP10 预留DP3 */
|
||||
} jb_data_point_t;
|
||||
|
||||
/* 控制事件信息 */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t count;
|
||||
uint8_t dp_ids[DP_ID_MAX];
|
||||
} jb_event_info_t;
|
||||
|
||||
/* ACK 等待 / 重传管理器 */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t active; /* 是否有等待中的请求 */
|
||||
uint8_t sn; /* 帧序列号 */
|
||||
uint8_t retry;
|
||||
uint8_t buf[MAX_PACKAGE_LEN];
|
||||
uint16_t len;
|
||||
uint32_t sendTime;
|
||||
} jb_wait_ack_t;
|
||||
|
||||
/* 协议全局状态 */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t sn; /* 设备 SN 计数器 */
|
||||
uint8_t rxBuf[MAX_PACKAGE_LEN];
|
||||
jb_wait_ack_t waitAck; /* ACK 重传管理器 */
|
||||
jb_data_point_t lastData; /* 上次上报快照 */
|
||||
uint32_t lastReportTime; /* 上次上报时刻 */
|
||||
uint32_t lastChangeTime; /* 上次变化上报时刻 */
|
||||
} jb_protocol_t;
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 全局实例
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
extern jb_protocol_t jbProtocol;
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 协议层 API(自动生成)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
void jbInit(void);
|
||||
int32_t jbPutData(uint8_t *buf, uint32_t len);
|
||||
|
||||
/* MCU → BLE 主动请求 / 通知 */
|
||||
int32_t jbRequestPairing(uint8_t cfg_mode, uint16_t timeout_sec,
|
||||
const uint8_t *param, uint8_t param_len);
|
||||
int32_t jbRequestTime(void);
|
||||
int32_t jbRequestBleReset(void);
|
||||
int32_t jbRequestBleRestart(void);
|
||||
int32_t jbRequestProduction(uint8_t mode);
|
||||
int32_t jbRequestPhoto(void);
|
||||
int32_t jbRequestRecord(uint16_t duration_sec);
|
||||
int32_t jbRequestAudio(uint8_t audio_id);
|
||||
int32_t jbNotifyOtaStop(void);
|
||||
int32_t jbSendOtaResult(uint8_t result, const uint8_t *mcuVersion);
|
||||
|
||||
int32_t jbProtocolHandle(jb_data_point_t *data);
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 硬件抽象(用户实现)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
uint32_t jbGetTimerCount(void); /* ms 计数器 */
|
||||
int32_t uartWrite(uint8_t *buf, uint32_t len); /* UART 发送 */
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 用户回调(用户实现)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
/* 0x03 APP 下发控制事件 */
|
||||
int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *data);
|
||||
|
||||
/* BLE → MCU 事件回调 */
|
||||
void jbOnWorkState(uint8_t state, int8_t rssi); /* 0x14 状态(含RSSI) */
|
||||
void jbOnBleRestartRequest(void); /* 0x1A 请求重启 */
|
||||
int8_t jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32); /* 0x30, 返回0=接受,非0=拒绝码 */
|
||||
void jbOnOtaData(const uint8_t *data, uint16_t len,
|
||||
uint16_t shardIdx, uint16_t totalShards); /* 0x32 */
|
||||
void jbOnOtaStop(void); /* 0x36 / 0x38 停止 */
|
||||
|
||||
/* MCU → BLE 应答回调(收到 BLE 应答时触发)*/
|
||||
void jbOnTimeSync(uint32_t timestamp); /* 0x1D 时间同步 */
|
||||
void jbOnAck(uint8_t cmd, uint8_t result); /* 任意请求 ACK(结果) */
|
||||
|
||||
#endif /* _JB_PROTOCOL_H */
|
||||
Reference in New Issue
Block a user