Files
mcu-codegen-template/App/Protocol/jb_protocol.c
T

944 lines
33 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ================================================================
* 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 指定要打包的 DPbit 序号 = 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 停止 OTAMCU 主动,等待 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;
}