/** * @file protocol.c * @brief 协议处理函数实现 * @details 该文件包含了协议解析和处理的相关函数,主要用于处理从 ESP12F * 模块接收到的数据。 * @author Beihong Wang * @date 2026-04-01 */ #include "protocol.h" #include "bsp_uart.h" #include "checksum.h" #include "cmsis_os.h" #include "elog.h" #include #include /* 定义日志 TAG */ #define Protocol_TAG "Protocol" /* 引用在 freertos.c 中定义的消息队列句柄 */ extern osMessageQueueId_t CmdQueueHandle; /** * @brief 协议处理函数 * @details 严格按照协议文档:校验范围 = 帧头 + 命令 + 数据 * 即从下标 0 开始,一直加到最后一个冒号之前 */ void Protocol_HandleMessage(uint8_t *data, uint16_t len) { if (data == NULL) return; // 1. 基础检查:长度必须足够,且必须以 '#' 结尾 if (len < 10 || data[len - 1] != '#') { elog_w(Protocol_TAG, "协议错误:长度不足或帧尾错误 (len: %d)", len); return; } // 2. 寻找校验位前的分隔符 // 协议格式:LOGI:CMD:DATA:CS# // 我们需要找到最后一个冒号 ':' 的位置,它前面是数据,后面是校验位 int last_colon_pos = -1; for (int i = 0; i < len; i++) { if (data[i] == ':') { last_colon_pos = i; } } // 如果找不到冒号,说明格式错误 if (last_colon_pos == -1 || last_colon_pos < 5) { elog_w(Protocol_TAG, "协议错误:找不到分隔符 ':' 或位置非法"); return; } // 3. 提取接收到的校验位 (从 ASCII 转为 Hex 数值) // 校验位紧跟在 last_colon_pos 之后,长度为 2 字节 char recv_cs_hex_str[3] = {0}; // 防止越界 if (last_colon_pos + 3 >= len) { elog_w(Protocol_TAG, "协议错误:校验位数据越界"); return; } recv_cs_hex_str[0] = data[last_colon_pos + 1]; recv_cs_hex_str[1] = data[last_colon_pos + 2]; unsigned int received_checksum = 0; sscanf(recv_cs_hex_str, "%02X", &received_checksum); // 4. 计算本地校验和 // 【核心修改点】 // 严格按照协议文档:从下标 0 开始,长度为 last_colon_pos // 也就是计算 "LOGI:SP:080" 的累加和 uint8_t calculated_checksum = Calculate_CheckSum(data, 0, (uint16_t)last_colon_pos); // 5. 对比校验和 if (calculated_checksum == (uint8_t)received_checksum) { elog_i(Protocol_TAG, "✅ 校验通过!执行指令: %s", (char *)data); /* 提取有效载荷发送到消息队列 */ char cmd_payload[16] = {0}; // 将 "LOGI:" 之后到最后一个冒号之前的内容作为指令 uint16_t payload_len = last_colon_pos - 5; uint16_t copy_len = (payload_len > 15) ? 15 : payload_len; if (copy_len > 0) { memcpy(cmd_payload, &data[5], copy_len); } osStatus_t status = osMessageQueuePut(CmdQueueHandle, cmd_payload, 0, 0); if (status != osOK) { elog_e(Protocol_TAG, "Protocol: Queue put failed: %d", status); } } else { elog_w(Protocol_TAG, "❌ 校验失败!计算值: 0x%02X, 接收值: 0x%02X", calculated_checksum, (uint8_t)received_checksum); // 辅助调试:打印实际参与计算的数据段 char debug_buf[32] = {0}; if (last_colon_pos < 32) { memcpy(debug_buf, data, last_colon_pos); elog_i(Protocol_TAG, " -> 单片机正在计算这段数据的校验和: [%s]", debug_buf); elog_i(Protocol_TAG, " -> 请检查上位机是否也是按照此范围计算累加和"); } } } #define CarCtrlTask_TAG "CarCtrlTask" void CarCtrl_Task(void *argument) { /* USER CODE BEGIN CarCtrl_Task */ char cmd_payload[16]; /* Infinite loop */ for (;;) { /* 从消息队列中获取数据,阻塞等待 */ if (osMessageQueueGet(CmdQueueHandle, cmd_payload, NULL, osWaitForever) == osOK) { elog_i(CarCtrlTask_TAG, "CarCtrl: Received command (ASCII: %s) from queue", cmd_payload); /* 可以在这里添加根据 cmd_payload 控制小车的逻辑 */ } // osDelay(1); // osMessageQueueGet 已经是阻塞的,不需要额外的 osDelay } /* USER CODE END CarCtrl_Task */ }