1158 lines
41 KiB
C
1158 lines
41 KiB
C
/* ================================================================
|
||
* jb_protocol.c — 见宝 IOT 协议实现(自动生成)
|
||
* 设备: 喷淋器 | 生成时间: 2026-09-09 10:22:58
|
||
* ================================================================
|
||
*/
|
||
|
||
#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 — 组装协议帧并经 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 喷淋开关 (bool, 1B, RW) */
|
||
if (mask[DP_ID_SPRAY_SWITCH / 8] & (1 << (DP_ID_SPRAY_SWITCH % 8)))
|
||
{
|
||
bitmap[DP_ID_SPRAY_SWITCH / 8] |= (1 << (DP_ID_SPRAY_SWITCH % 8));
|
||
out[valIdx++] = dp->spray_switch ? 1 : 0;
|
||
}
|
||
/* DP2 模式 (enum, 1B, RW) */
|
||
if (mask[DP_ID_MODE / 8] & (1 << (DP_ID_MODE % 8)))
|
||
{
|
||
bitmap[DP_ID_MODE / 8] |= (1 << (DP_ID_MODE % 8));
|
||
out[valIdx++] = (uint8_t)dp->mode;
|
||
}
|
||
/* DP3 单次喷淋时长 (uint8, 1B, RW) */
|
||
if (mask[DP_ID_ONCE_SPRAY_TIME / 8] & (1 << (DP_ID_ONCE_SPRAY_TIME % 8)))
|
||
{
|
||
bitmap[DP_ID_ONCE_SPRAY_TIME / 8] |= (1 << (DP_ID_ONCE_SPRAY_TIME % 8));
|
||
out[valIdx++] = (uint8_t)dp->once_spray_time;
|
||
}
|
||
/* DP4 连续喷淋时长 (uint8, 1B, RW) */
|
||
if (mask[DP_ID_CONTINUOUS_SPRAY_TIME / 8] & (1 << (DP_ID_CONTINUOUS_SPRAY_TIME % 8)))
|
||
{
|
||
bitmap[DP_ID_CONTINUOUS_SPRAY_TIME / 8] |= (1 << (DP_ID_CONTINUOUS_SPRAY_TIME % 8));
|
||
out[valIdx++] = (uint8_t)dp->continuous_spray_time;
|
||
}
|
||
/* DP7 喷淋配置 (raw, 8B, RW) */
|
||
if (mask[DP_ID_SPRAY_CONFIG / 8] & (1 << (DP_ID_SPRAY_CONFIG % 8)))
|
||
{
|
||
bitmap[DP_ID_SPRAY_CONFIG / 8] |= (1 << (DP_ID_SPRAY_CONFIG % 8));
|
||
memcpy(&out[valIdx], dp->spray_config, 8);
|
||
valIdx += 8;
|
||
}
|
||
/* DP8 联动删除 (bool, 1B, RW) */
|
||
if (mask[DP_ID_LINKAGE_DELETE / 8] & (1 << (DP_ID_LINKAGE_DELETE % 8)))
|
||
{
|
||
bitmap[DP_ID_LINKAGE_DELETE / 8] |= (1 << (DP_ID_LINKAGE_DELETE % 8));
|
||
out[valIdx++] = dp->linkage_delete ? 1 : 0;
|
||
}
|
||
/* DP9 联动配置 (raw, 20B, RW) */
|
||
if (mask[DP_ID_LINKAGE_CONFIG / 8] & (1 << (DP_ID_LINKAGE_CONFIG % 8)))
|
||
{
|
||
bitmap[DP_ID_LINKAGE_CONFIG / 8] |= (1 << (DP_ID_LINKAGE_CONFIG % 8));
|
||
memcpy(&out[valIdx], dp->linkage_config, 20);
|
||
valIdx += 20;
|
||
}
|
||
/* DP10 喷淋计划总开关 (bool, 1B, RW) */
|
||
if (mask[DP_ID_SPRAY_PLAN_EN / 8] & (1 << (DP_ID_SPRAY_PLAN_EN % 8)))
|
||
{
|
||
bitmap[DP_ID_SPRAY_PLAN_EN / 8] |= (1 << (DP_ID_SPRAY_PLAN_EN % 8));
|
||
out[valIdx++] = dp->spray_plan_en ? 1 : 0;
|
||
}
|
||
/* DP11 喷淋计划1 (raw, 15B, RW) */
|
||
if (mask[DP_ID_SPRAY_PLAN1 / 8] & (1 << (DP_ID_SPRAY_PLAN1 % 8)))
|
||
{
|
||
bitmap[DP_ID_SPRAY_PLAN1 / 8] |= (1 << (DP_ID_SPRAY_PLAN1 % 8));
|
||
memcpy(&out[valIdx], dp->spray_plan1, 15);
|
||
valIdx += 15;
|
||
}
|
||
/* DP12 喷淋计划2 (raw, 15B, RW) */
|
||
if (mask[DP_ID_TIME_CONFIG2 / 8] & (1 << (DP_ID_TIME_CONFIG2 % 8)))
|
||
{
|
||
bitmap[DP_ID_TIME_CONFIG2 / 8] |= (1 << (DP_ID_TIME_CONFIG2 % 8));
|
||
memcpy(&out[valIdx], dp->time_config2, 15);
|
||
valIdx += 15;
|
||
}
|
||
/* DP13 喷淋计划3 (raw, 15B, RW) */
|
||
if (mask[DP_ID_TIME_CONFIG3 / 8] & (1 << (DP_ID_TIME_CONFIG3 % 8)))
|
||
{
|
||
bitmap[DP_ID_TIME_CONFIG3 / 8] |= (1 << (DP_ID_TIME_CONFIG3 % 8));
|
||
memcpy(&out[valIdx], dp->time_config3, 15);
|
||
valIdx += 15;
|
||
}
|
||
/* DP14 喷淋计划4 (raw, 15B, RW) */
|
||
if (mask[DP_ID_TIME_CONFIG4 / 8] & (1 << (DP_ID_TIME_CONFIG4 % 8)))
|
||
{
|
||
bitmap[DP_ID_TIME_CONFIG4 / 8] |= (1 << (DP_ID_TIME_CONFIG4 % 8));
|
||
memcpy(&out[valIdx], dp->time_config4, 15);
|
||
valIdx += 15;
|
||
}
|
||
/* DP15 喷淋计划5 (raw, 15B, RW) */
|
||
if (mask[DP_ID_TIME_CONFIG5 / 8] & (1 << (DP_ID_TIME_CONFIG5 % 8)))
|
||
{
|
||
bitmap[DP_ID_TIME_CONFIG5 / 8] |= (1 << (DP_ID_TIME_CONFIG5 % 8));
|
||
memcpy(&out[valIdx], dp->time_config5, 15);
|
||
valIdx += 15;
|
||
}
|
||
/* DP16 喷淋计划6 (raw, 15B, RW) */
|
||
if (mask[DP_ID_TIME_CONFIG6 / 8] & (1 << (DP_ID_TIME_CONFIG6 % 8)))
|
||
{
|
||
bitmap[DP_ID_TIME_CONFIG6 / 8] |= (1 << (DP_ID_TIME_CONFIG6 % 8));
|
||
memcpy(&out[valIdx], dp->time_config6, 15);
|
||
valIdx += 15;
|
||
}
|
||
/* DP17 喷淋计划7 (raw, 15B, RW) */
|
||
if (mask[DP_ID_TIME_CONFIG7 / 8] & (1 << (DP_ID_TIME_CONFIG7 % 8)))
|
||
{
|
||
bitmap[DP_ID_TIME_CONFIG7 / 8] |= (1 << (DP_ID_TIME_CONFIG7 % 8));
|
||
memcpy(&out[valIdx], dp->time_config7, 15);
|
||
valIdx += 15;
|
||
}
|
||
/* DP18 运行记录 (raw, 124B, RO) */
|
||
if (mask[DP_ID_RUN_RECORD / 8] & (1 << (DP_ID_RUN_RECORD % 8)))
|
||
{
|
||
bitmap[DP_ID_RUN_RECORD / 8] |= (1 << (DP_ID_RUN_RECORD % 8));
|
||
memcpy(&out[valIdx], dp->run_record, 124);
|
||
valIdx += 124;
|
||
}
|
||
/* DP20 设备故障使能 (uint8, 1B, RW) */
|
||
if (mask[DP_ID_FAULT_EN / 8] & (1 << (DP_ID_FAULT_EN % 8)))
|
||
{
|
||
bitmap[DP_ID_FAULT_EN / 8] |= (1 << (DP_ID_FAULT_EN % 8));
|
||
out[valIdx++] = (uint8_t)dp->fault_en;
|
||
}
|
||
/* DP21 设备故障 (uint8, 1B, RO) */
|
||
if (mask[DP_ID_FAULT_CODE / 8] & (1 << (DP_ID_FAULT_CODE % 8)))
|
||
{
|
||
bitmap[DP_ID_FAULT_CODE / 8] |= (1 << (DP_ID_FAULT_CODE % 8));
|
||
out[valIdx++] = (uint8_t)dp->fault_code;
|
||
}
|
||
/* DP22 设备告警使能 (uint8, 1B, RW) */
|
||
if (mask[DP_ID_ALARM_EN / 8] & (1 << (DP_ID_ALARM_EN % 8)))
|
||
{
|
||
bitmap[DP_ID_ALARM_EN / 8] |= (1 << (DP_ID_ALARM_EN % 8));
|
||
out[valIdx++] = (uint8_t)dp->alarm_en;
|
||
}
|
||
/* DP23 设备告警 (uint8, 1B, RO) */
|
||
if (mask[DP_ID_ALARM_CODE / 8] & (1 << (DP_ID_ALARM_CODE % 8)))
|
||
{
|
||
bitmap[DP_ID_ALARM_CODE / 8] |= (1 << (DP_ID_ALARM_CODE % 8));
|
||
out[valIdx++] = (uint8_t)dp->alarm_code;
|
||
}
|
||
/* DP25 预留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;
|
||
}
|
||
/* DP26 预留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;
|
||
}
|
||
/* DP27 预留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 喷淋开关 (bool, 1B, RW) */
|
||
if (bitmap[DP_ID_SPRAY_SWITCH / 8] & (1 << (DP_ID_SPRAY_SWITCH % 8)))
|
||
{
|
||
if (valIdx < valLen)
|
||
{
|
||
dp->spray_switch = (values[valIdx++] != 0);
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_SPRAY_SWITCH;
|
||
}
|
||
}
|
||
/* DP2 模式 (enum, 1B, RW) */
|
||
if (bitmap[DP_ID_MODE / 8] & (1 << (DP_ID_MODE % 8)))
|
||
{
|
||
if (valIdx < valLen)
|
||
{
|
||
dp->mode = values[valIdx++];
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_MODE;
|
||
}
|
||
}
|
||
/* DP3 单次喷淋时长 (uint8, 1B, RW) */
|
||
if (bitmap[DP_ID_ONCE_SPRAY_TIME / 8] & (1 << (DP_ID_ONCE_SPRAY_TIME % 8)))
|
||
{
|
||
if (valIdx < valLen)
|
||
{
|
||
dp->once_spray_time = values[valIdx++];
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_ONCE_SPRAY_TIME;
|
||
}
|
||
}
|
||
/* DP4 连续喷淋时长 (uint8, 1B, RW) */
|
||
if (bitmap[DP_ID_CONTINUOUS_SPRAY_TIME / 8] & (1 << (DP_ID_CONTINUOUS_SPRAY_TIME % 8)))
|
||
{
|
||
if (valIdx < valLen)
|
||
{
|
||
dp->continuous_spray_time = values[valIdx++];
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_CONTINUOUS_SPRAY_TIME;
|
||
}
|
||
}
|
||
/* DP5 当前温度 (float, 2B, WO (float, scale=0.1)) */
|
||
if (bitmap[DP_ID_CURRENT_TEMP / 8] & (1 << (DP_ID_CURRENT_TEMP % 8)))
|
||
{
|
||
if (valIdx + 2 <= valLen)
|
||
{
|
||
int16_t _raw = (int16_t)jbReadU16BE(&values[valIdx]);
|
||
dp->current_temp = (float)_raw * 0.1f;
|
||
valIdx += 2;
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_CURRENT_TEMP;
|
||
}
|
||
}
|
||
/* DP6 当前湿度 (uint8, 1B, WO) */
|
||
if (bitmap[DP_ID_CURRENT_HUMI / 8] & (1 << (DP_ID_CURRENT_HUMI % 8)))
|
||
{
|
||
if (valIdx < valLen)
|
||
{
|
||
dp->current_humi = values[valIdx++];
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_CURRENT_HUMI;
|
||
}
|
||
}
|
||
/* DP7 喷淋配置 (raw, 8B, RW) */
|
||
if (bitmap[DP_ID_SPRAY_CONFIG / 8] & (1 << (DP_ID_SPRAY_CONFIG % 8)))
|
||
{
|
||
if (valIdx + 8 <= valLen)
|
||
{
|
||
memcpy(dp->spray_config, &values[valIdx], 8);
|
||
valIdx += 8;
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_SPRAY_CONFIG;
|
||
}
|
||
}
|
||
/* DP8 联动删除 (bool, 1B, RW) */
|
||
if (bitmap[DP_ID_LINKAGE_DELETE / 8] & (1 << (DP_ID_LINKAGE_DELETE % 8)))
|
||
{
|
||
if (valIdx < valLen)
|
||
{
|
||
dp->linkage_delete = (values[valIdx++] != 0);
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_LINKAGE_DELETE;
|
||
}
|
||
}
|
||
/* DP9 联动配置 (raw, 20B, RW) */
|
||
if (bitmap[DP_ID_LINKAGE_CONFIG / 8] & (1 << (DP_ID_LINKAGE_CONFIG % 8)))
|
||
{
|
||
if (valIdx + 20 <= valLen)
|
||
{
|
||
memcpy(dp->linkage_config, &values[valIdx], 20);
|
||
valIdx += 20;
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_LINKAGE_CONFIG;
|
||
}
|
||
}
|
||
/* DP10 喷淋计划总开关 (bool, 1B, RW) */
|
||
if (bitmap[DP_ID_SPRAY_PLAN_EN / 8] & (1 << (DP_ID_SPRAY_PLAN_EN % 8)))
|
||
{
|
||
if (valIdx < valLen)
|
||
{
|
||
dp->spray_plan_en = (values[valIdx++] != 0);
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_SPRAY_PLAN_EN;
|
||
}
|
||
}
|
||
/* DP11 喷淋计划1 (raw, 15B, RW) */
|
||
if (bitmap[DP_ID_SPRAY_PLAN1 / 8] & (1 << (DP_ID_SPRAY_PLAN1 % 8)))
|
||
{
|
||
if (valIdx + 15 <= valLen)
|
||
{
|
||
memcpy(dp->spray_plan1, &values[valIdx], 15);
|
||
valIdx += 15;
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_SPRAY_PLAN1;
|
||
}
|
||
}
|
||
/* DP12 喷淋计划2 (raw, 15B, RW) */
|
||
if (bitmap[DP_ID_TIME_CONFIG2 / 8] & (1 << (DP_ID_TIME_CONFIG2 % 8)))
|
||
{
|
||
if (valIdx + 15 <= valLen)
|
||
{
|
||
memcpy(dp->time_config2, &values[valIdx], 15);
|
||
valIdx += 15;
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG2;
|
||
}
|
||
}
|
||
/* DP13 喷淋计划3 (raw, 15B, RW) */
|
||
if (bitmap[DP_ID_TIME_CONFIG3 / 8] & (1 << (DP_ID_TIME_CONFIG3 % 8)))
|
||
{
|
||
if (valIdx + 15 <= valLen)
|
||
{
|
||
memcpy(dp->time_config3, &values[valIdx], 15);
|
||
valIdx += 15;
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG3;
|
||
}
|
||
}
|
||
/* DP14 喷淋计划4 (raw, 15B, RW) */
|
||
if (bitmap[DP_ID_TIME_CONFIG4 / 8] & (1 << (DP_ID_TIME_CONFIG4 % 8)))
|
||
{
|
||
if (valIdx + 15 <= valLen)
|
||
{
|
||
memcpy(dp->time_config4, &values[valIdx], 15);
|
||
valIdx += 15;
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG4;
|
||
}
|
||
}
|
||
/* DP15 喷淋计划5 (raw, 15B, RW) */
|
||
if (bitmap[DP_ID_TIME_CONFIG5 / 8] & (1 << (DP_ID_TIME_CONFIG5 % 8)))
|
||
{
|
||
if (valIdx + 15 <= valLen)
|
||
{
|
||
memcpy(dp->time_config5, &values[valIdx], 15);
|
||
valIdx += 15;
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG5;
|
||
}
|
||
}
|
||
/* DP16 喷淋计划6 (raw, 15B, RW) */
|
||
if (bitmap[DP_ID_TIME_CONFIG6 / 8] & (1 << (DP_ID_TIME_CONFIG6 % 8)))
|
||
{
|
||
if (valIdx + 15 <= valLen)
|
||
{
|
||
memcpy(dp->time_config6, &values[valIdx], 15);
|
||
valIdx += 15;
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG6;
|
||
}
|
||
}
|
||
/* DP17 喷淋计划7 (raw, 15B, RW) */
|
||
if (bitmap[DP_ID_TIME_CONFIG7 / 8] & (1 << (DP_ID_TIME_CONFIG7 % 8)))
|
||
{
|
||
if (valIdx + 15 <= valLen)
|
||
{
|
||
memcpy(dp->time_config7, &values[valIdx], 15);
|
||
valIdx += 15;
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_TIME_CONFIG7;
|
||
}
|
||
}
|
||
/* DP19 心跳 (bool, 1B, WO) */
|
||
if (bitmap[DP_ID_HEARTBEAT / 8] & (1 << (DP_ID_HEARTBEAT % 8)))
|
||
{
|
||
if (valIdx < valLen)
|
||
{
|
||
dp->heartbeat = (values[valIdx++] != 0);
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_HEARTBEAT;
|
||
}
|
||
}
|
||
/* DP20 设备故障使能 (uint8, 1B, RW) */
|
||
if (bitmap[DP_ID_FAULT_EN / 8] & (1 << (DP_ID_FAULT_EN % 8)))
|
||
{
|
||
if (valIdx < valLen)
|
||
{
|
||
dp->fault_en = values[valIdx++];
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_FAULT_EN;
|
||
}
|
||
}
|
||
/* DP22 设备告警使能 (uint8, 1B, RW) */
|
||
if (bitmap[DP_ID_ALARM_EN / 8] & (1 << (DP_ID_ALARM_EN % 8)))
|
||
{
|
||
if (valIdx < valLen)
|
||
{
|
||
dp->alarm_en = values[valIdx++];
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_ALARM_EN;
|
||
}
|
||
}
|
||
/* DP24 恢复出厂设置 (bool, 1B, WO) */
|
||
if (bitmap[DP_ID_FACTORY_RESET / 8] & (1 << (DP_ID_FACTORY_RESET % 8)))
|
||
{
|
||
if (valIdx < valLen)
|
||
{
|
||
dp->factory_reset = (values[valIdx++] != 0);
|
||
}
|
||
if (info && info->count < DP_ID_MAX)
|
||
{
|
||
info->dp_ids[info->count++] = DP_ID_FACTORY_RESET;
|
||
}
|
||
}
|
||
/* DP25 预留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;
|
||
}
|
||
}
|
||
/* DP26 预留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;
|
||
}
|
||
}
|
||
/* DP27 预留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 = 全部未变化
|
||
* DP18 运行记录:今天累计秒数相对上次上报满 JB_RUNREC_DIFF_SEC 才进差分;
|
||
* 其它日期格变化(跨天旋转)立刻进差分。0x05 读 / 10min 全量仍带完整 124B。
|
||
* ════════════════════════════════════════════════════════════════ */
|
||
static uint32_t jbRunrecTodaySec(const uint8_t *rec)
|
||
{
|
||
return ((uint32_t)rec[0] << 24) | ((uint32_t)rec[1] << 16)
|
||
| ((uint32_t)rec[2] << 8) | (uint32_t)rec[3];
|
||
}
|
||
|
||
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->spray_switch != cur->spray_switch)
|
||
{
|
||
mask[DP_ID_SPRAY_SWITCH / 8] |= (1 << (DP_ID_SPRAY_SWITCH % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP2 模式 */
|
||
if (last->mode != cur->mode)
|
||
{
|
||
mask[DP_ID_MODE / 8] |= (1 << (DP_ID_MODE % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP3 单次喷淋时长 */
|
||
if (last->once_spray_time != cur->once_spray_time)
|
||
{
|
||
mask[DP_ID_ONCE_SPRAY_TIME / 8] |= (1 << (DP_ID_ONCE_SPRAY_TIME % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP4 连续喷淋时长 */
|
||
if (last->continuous_spray_time != cur->continuous_spray_time)
|
||
{
|
||
mask[DP_ID_CONTINUOUS_SPRAY_TIME / 8] |= (1 << (DP_ID_CONTINUOUS_SPRAY_TIME % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP7 喷淋配置 */
|
||
if (memcmp(last->spray_config, cur->spray_config, 8))
|
||
{
|
||
mask[DP_ID_SPRAY_CONFIG / 8] |= (1 << (DP_ID_SPRAY_CONFIG % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP8 联动删除 */
|
||
if (last->linkage_delete != cur->linkage_delete)
|
||
{
|
||
mask[DP_ID_LINKAGE_DELETE / 8] |= (1 << (DP_ID_LINKAGE_DELETE % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP9 联动配置 */
|
||
if (memcmp(last->linkage_config, cur->linkage_config, 20))
|
||
{
|
||
mask[DP_ID_LINKAGE_CONFIG / 8] |= (1 << (DP_ID_LINKAGE_CONFIG % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP10 喷淋计划总开关 */
|
||
if (last->spray_plan_en != cur->spray_plan_en)
|
||
{
|
||
mask[DP_ID_SPRAY_PLAN_EN / 8] |= (1 << (DP_ID_SPRAY_PLAN_EN % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP11 喷淋计划1 */
|
||
if (memcmp(last->spray_plan1, cur->spray_plan1, 15))
|
||
{
|
||
mask[DP_ID_SPRAY_PLAN1 / 8] |= (1 << (DP_ID_SPRAY_PLAN1 % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP12 喷淋计划2 */
|
||
if (memcmp(last->time_config2, cur->time_config2, 15))
|
||
{
|
||
mask[DP_ID_TIME_CONFIG2 / 8] |= (1 << (DP_ID_TIME_CONFIG2 % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP13 喷淋计划3 */
|
||
if (memcmp(last->time_config3, cur->time_config3, 15))
|
||
{
|
||
mask[DP_ID_TIME_CONFIG3 / 8] |= (1 << (DP_ID_TIME_CONFIG3 % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP14 喷淋计划4 */
|
||
if (memcmp(last->time_config4, cur->time_config4, 15))
|
||
{
|
||
mask[DP_ID_TIME_CONFIG4 / 8] |= (1 << (DP_ID_TIME_CONFIG4 % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP15 喷淋计划5 */
|
||
if (memcmp(last->time_config5, cur->time_config5, 15))
|
||
{
|
||
mask[DP_ID_TIME_CONFIG5 / 8] |= (1 << (DP_ID_TIME_CONFIG5 % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP16 喷淋计划6 */
|
||
if (memcmp(last->time_config6, cur->time_config6, 15))
|
||
{
|
||
mask[DP_ID_TIME_CONFIG6 / 8] |= (1 << (DP_ID_TIME_CONFIG6 % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP17 喷淋计划7 */
|
||
if (memcmp(last->time_config7, cur->time_config7, 15))
|
||
{
|
||
mask[DP_ID_TIME_CONFIG7 / 8] |= (1 << (DP_ID_TIME_CONFIG7 % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP18 运行记录:今天秒数差分满 1 分钟,或后 30 天格变了(跨天) */
|
||
{
|
||
uint32_t last_today = jbRunrecTodaySec(last->run_record);
|
||
uint32_t cur_today = jbRunrecTodaySec(cur->run_record);
|
||
uint32_t delta = (cur_today > last_today)
|
||
? (cur_today - last_today)
|
||
: (last_today - cur_today);
|
||
|
||
if ((delta >= JB_RUNREC_DIFF_SEC)
|
||
|| memcmp(&last->run_record[4], &cur->run_record[4], 120))
|
||
{
|
||
mask[DP_ID_RUN_RECORD / 8] |= (1 << (DP_ID_RUN_RECORD % 8));
|
||
hasChange = 1;
|
||
}
|
||
}
|
||
/* DP20 设备故障使能 */
|
||
if (last->fault_en != cur->fault_en)
|
||
{
|
||
mask[DP_ID_FAULT_EN / 8] |= (1 << (DP_ID_FAULT_EN % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP21 设备故障 */
|
||
if (last->fault_code != cur->fault_code)
|
||
{
|
||
mask[DP_ID_FAULT_CODE / 8] |= (1 << (DP_ID_FAULT_CODE % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP22 设备告警使能 */
|
||
if (last->alarm_en != cur->alarm_en)
|
||
{
|
||
mask[DP_ID_ALARM_EN / 8] |= (1 << (DP_ID_ALARM_EN % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP23 设备告警 */
|
||
if (last->alarm_code != cur->alarm_code)
|
||
{
|
||
mask[DP_ID_ALARM_CODE / 8] |= (1 << (DP_ID_ALARM_CODE % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP25 预留DP1 */
|
||
if (last->reserva_dp1 != cur->reserva_dp1)
|
||
{
|
||
mask[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP26 预留DP2 */
|
||
if (last->reserva_dp2 != cur->reserva_dp2)
|
||
{
|
||
mask[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8));
|
||
hasChange = 1;
|
||
}
|
||
/* DP27 预留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);
|
||
/* 未带 DP18 时保留上次记录基准,避免喷淋开关等上报把 1 分钟差分冲掉 */
|
||
if (!(diffMask[DP_ID_RUN_RECORD / 8] & (1u << (DP_ID_RUN_RECORD % 8))))
|
||
{
|
||
uint8_t keep[124];
|
||
|
||
memcpy(keep, jbProtocol.lastData.run_record, 124);
|
||
memcpy(&jbProtocol.lastData, data, sizeof(jb_data_point_t));
|
||
memcpy(jbProtocol.lastData.run_record, keep, 124);
|
||
}
|
||
else
|
||
{
|
||
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]);
|
||
|
||
/* 应答: 结果码 + 最大单包长度(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);
|
||
}
|
||
|
||
/* 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;
|
||
}
|