750 lines
27 KiB
C
750 lines
27 KiB
C
/* ================================================================
|
||
* 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 指定要打包的 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 等待。主动上报 / 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 停止 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];
|
||
|
||
/* 按命令码统一分发 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;
|
||
}
|