Files

1158 lines
41 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ================================================================
* jb_protocol.c — 见宝 IOT 协议实现(自动生成)
* 设备: 喷淋器 | 生成时间: 2026-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 指定要打包的 DPbit 序号 = DP ID
* 返回 outBuf 总字节数(bitmap + values
* 仅打包 RO / RW 方向的 DP
* ════════════════════════════════════════════════════════════════ */
static uint16_t jbPackDps(const jb_data_point_t *dp,
const uint8_t *mask, uint8_t *outBuf)
{
uint8_t *bitmap = outBuf;
uint8_t *out = outBuf + DP_BITMAP_SIZE;
uint16_t valIdx = 0;
memset(bitmap, 0, DP_BITMAP_SIZE);
/* DP0 总开关 (bool, 1B, RW) */
if (mask[DP_ID_POWER / 8] & (1 << (DP_ID_POWER % 8)))
{
bitmap[DP_ID_POWER / 8] |= (1 << (DP_ID_POWER % 8));
out[valIdx++] = dp->power ? 1 : 0;
}
/* DP1 喷淋开关 (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 停止 OTAMCU 主动,等待 ACK 重传)*/
int32_t jbNotifyOtaStop(void)
{
uint8_t sn = jbProtocol.sn++;
return jbSendReqWithAck(CMD_OTA_STOP_MCU, sn, NULL, 0);
}
/* 0x34 OTA 升级完成(result + MCU 版本 3B,等待 ACK 重传)*/
int32_t jbSendOtaResult(uint8_t result, const uint8_t *mcuVersion)
{
uint8_t sn = jbProtocol.sn++;
uint8_t payload[4];
payload[0] = result;
if (mcuVersion)
{
memcpy(&payload[1], mcuVersion, 3);
}
else
{
memset(&payload[1], 0, 3);
}
return jbSendReqWithAck(CMD_OTA_COMPLETE, sn, payload, 4);
}
/* ════════════════════════════════════════════════════════════════
* jbProtocolHandle — 协议主循环(在 while(1) 中调用)
* ════════════════════════════════════════════════════════════════ */
int32_t jbProtocolHandle(jb_data_point_t *data)
{
if (!data)
{
return -1;
}
/* ACK 超时重传 */
jbAckRetry();
/* 提取一帧 */
uint16_t pktLen = 0;
int8_t ret = jbGetOnePacket(jbProtocol.rxBuf, &pktLen);
uint8_t cmd;
if (ret == 0)
{
cmd = jbProtocol.rxBuf[4];
/* 按命令码统一分发 BLE → MCU 的所有帧(含 MCU 主动请求的应答)*/
switch (cmd)
{
/* ── APP-BLE 数据 ── */
case CMD_CONTROL:
jbHandleControl(jbProtocol.rxBuf, pktLen);
break;
case CMD_READ:
jbHandleRead(jbProtocol.rxBuf, pktLen, data);
break;
/* ── BLE-MCU OTA ── */
case CMD_OTA_NOTIFY:
jbHandleOtaNotify(jbProtocol.rxBuf, pktLen);
break;
case CMD_OTA_DATA:
jbHandleOtaData(jbProtocol.rxBuf, pktLen);
break;
case CMD_OTA_STOP_BLE:
jbHandleOtaStop(jbProtocol.rxBuf, pktLen);
break;
/* 主动上报 / OTA 完成&停止 的网关应答均走 REPORTCMD_V2 → jbReportAckCheck */
default:
break;
}
}
jbReportPolicy(data);
return 0;
}