/* ================================================================ * 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 = 全部未变化 * ════════════════════════════════════════════════════════════════ */ 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 运行记录 */ if (memcmp(last->run_record, cur->run_record, 124)) { 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); 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; }