Files
soc-codegen-template/apps/usr_jb_proto/Protocol/jb_protocol.c
T
2026-08-19 16:05:36 +08:00

750 lines
27 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-31 17:08:51
* ================================================================
*/
#include "jb_protocol.h"
#include "jb_product.h"
#include "jb_ringbuffer.h"
#include "jb_common.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 — 组装协议帧并经 ble_send_data 发送(SOC 无串口)
* 返回帧总长度,< 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);
ble_send_data(buf, total);
return (int32_t)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 等待。主动上报 / OTA 等网关应答均走 REPORTCMD_V2
* 由 jbReportAckCheck() 清除等待;若 SEND_MAX_TIME 内未收到,
* jbAckRetry() 自动重传,最多 SEND_MAX_NUM 次。
* 返回 ble_send_data 结果(<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();
ble_send_data(buf, total);
return (int32_t)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)
{
ble_send_data(jbProtocol.waitAck.buf, jbProtocol.waitAck.len);
jbProtocol.waitAck.retry++;
jbProtocol.waitAck.sendTime = jbGetTimerCount();
}
}
/* ════════════════════════════════════════════════════════════════
* jbReportAckCheck — 网关应答确认(主动上报 / OTA 共用)
*
* 网关通过 REPORTCMD_V2 回传(非 55AA)。成功条件:
* cmd == 0
* gateway_int_cnt == 1
* gateway_int0 == 0
* 满足则清除 waitAck,停止重传,并回调 jbOnAck(wait_cmd, 0)。
*
* 返回: 1=成功并清除等待; 0=未命中/条件不符。
* ════════════════════════════════════════════════════════════════ */
int8_t jbReportAckCheck(uint16_t cmd, uint8_t gateway_int_cnt, int32_t gateway_int0)
{
if (!jbProtocol.waitAck.active)
{
return 0;
}
if (cmd == 0 && gateway_int_cnt == 1 && gateway_int0 == 0)
{
uint8_t wait_cmd = jbProtocol.waitAck.buf[4];
memset(&jbProtocol.waitAck, 0, sizeof(jb_wait_ack_t));
jbOnAck(wait_cmd, 0); /* result=0 表示应答成功 */
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);
}
/* ── 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]);
jbOnOtaNotify(fwVersion, fwLength, fwCrc32);
/* 默认应答: 结果码 + 最大单包长度(2B) */
/* maxDataLen 由 MCU 自身能力决定,填入 0x31 应答,BLE 据此分片 */
uint8_t ack[3];
ack[0] = JB_OK;
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;
jbOnOtaData(pData, dataLen, shardIdx, totalShards);
jbSendAck(CMD_OTA_DATA_ACK, sn, JB_OK);
}
/* ── 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);
}
/* 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];
/* 按命令码统一分发 BLE → MCU 的所有帧(含 MCU 主动请求的应答)*/
switch (cmd)
{
/* ── APP-BLE 数据 ── */
case CMD_CONTROL:
jbHandleControl(jbProtocol.rxBuf, pktLen);
break;
case CMD_READ:
jbHandleRead(jbProtocol.rxBuf, pktLen, data);
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;
/* 主动上报 / OTA 完成&停止 的网关应答均走 REPORTCMD_V2 → jbReportAckCheck */
default:
break;
}
}
jbReportPolicy(data);
return 0;
}