添加普冉 PY32F040 OTA 双工程代码生成模板
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user