944 lines
33 KiB
C
944 lines
33 KiB
C
/* ================================================================
|
||
* 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;
|
||
}
|