From eb47fc28785fcb5b078707b75cd58e3ff1cae5cc Mon Sep 17 00:00:00 2001 From: GuanYuankai Date: Fri, 28 Nov 2025 15:16:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=94=B5=E6=B1=A0=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E8=AF=BB=E5=8F=96=EF=BC=8C=E6=96=87=E4=BB=B6=E4=B8=BA?= =?UTF-8?q?read=5Fbms.cpp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/devices.json | 2 +- read_bms.cpp | 195 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 read_bms.cpp diff --git a/config/devices.json b/config/devices.json index 59c9532..b650012 100644 --- a/config/devices.json +++ b/config/devices.json @@ -1,7 +1,7 @@ { "modbus_rtu_devices": [ { - "enabled": false, + "enabled": true, "device_id": "rtu_temp_sensor_lab", "port_path": "/dev/ttyS7", "baud_rate": 9600, diff --git a/read_bms.cpp b/read_bms.cpp new file mode 100644 index 0000000..2cf2a80 --- /dev/null +++ b/read_bms.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +// --- 协议定义 (必须与STM32端完全一致) --- +#pragma pack(push, 1) // 强制1字节对齐 + +typedef struct { + uint16_t voltage; // 电池电压 mV + int16_t current; // 电池电流 mA + uint8_t capacity; // 电池电量 % + int16_t temp; // 电池温度 0.1K (需转换) + uint16_t rem_capacity; // 剩余容量 + uint16_t chg_voltage; // 充电电压 + uint16_t chg_current; // 充电电流 + uint8_t status_dsg; // 充放电状态 + uint16_t cycle_count; // 循环次数 + uint8_t rsoc; // 相对电量 +} BMS_Payload_t; + +#pragma pack(pop) + +// 常量定义 +const uint8_t FRAME_HEADER_0 = 0xAA; +const uint8_t FRAME_HEADER_1 = 0x55; +const uint8_t FRAME_TAIL_0 = 0x0D; +const uint8_t FRAME_TAIL_1 = 0x0A; +const uint8_t REQUEST_CMD = 0xA5; // 请求指令 +const int EXPECTED_PAYLOAD_LEN = sizeof(BMS_Payload_t); // 应该为 17 + +// --- 串口配置函数 --- +int open_serial_port(const char* portname) { + int fd = open(portname, O_RDWR | O_NOCTTY | O_NDELAY); + if (fd == -1) { + perror("open_serial_port: Unable to open port"); + return -1; + } + + struct termios options; + tcgetattr(fd, &options); + + // 设置波特率 115200 + cfsetispeed(&options, B115200); + cfsetospeed(&options, B115200); + + // 控制模式 (CLOCAL: 本地连接, CREAD: 开启接收) + options.c_cflag |= (CLOCAL | CREAD); + + // 设置 8N1 (8数据位, 无校验, 1停止位) + options.c_cflag &= ~PARENB; // 无校验 + options.c_cflag &= ~CSTOPB; // 1停止位 + options.c_cflag &= ~CSIZE; + options.c_cflag |= CS8; // 8数据位 + + // *** 关键设置:Raw 模式 *** + // 禁用规范模式 (ICANON)、回显 (ECHO)、信号 (ISIG) + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + // 禁用输出处理 (OPOST) + options.c_oflag &= ~OPOST; + // 禁用输入软件流控 (IXON, IXOFF, IXANY) 和 CR/LF 转换 + options.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL | INLCR | IGNCR); + + // 设置读取超时 (VMIN=0, VTIME=10 => 非阻塞,读不到数据等待1秒返回) + // 这里为了简单演示,使用阻塞式读取一点点数据 + options.c_cc[VMIN] = 0; + options.c_cc[VTIME] = 1; // 0.1秒超时 + + tcsetattr(fd, TCSANOW, &options); + fcntl(fd, F_SETFL, 0); // 恢复为阻塞模式 (配合 VMIN/VTIME) + + return fd; +} + +// --- 解析状态机枚举 --- +enum State { + STATE_WAIT_HEADER_0, + STATE_WAIT_HEADER_1, + STATE_WAIT_LEN, + STATE_READ_PAYLOAD, + STATE_WAIT_CHECKSUM, + STATE_WAIT_TAIL_0, + STATE_WAIT_TAIL_1 +}; + +// --- 主程序 --- +int main() { + const char* port = "/dev/ttyS4"; + int serial_fd = open_serial_port(port); + + if (serial_fd < 0) return 1; + + std::cout << "Listening on " << port << " at 115200 baud..." << std::endl; + + // 解析器状态变量 + State current_state = STATE_WAIT_HEADER_0; + std::vector payload_buffer; + uint8_t expected_len = 0; + uint8_t calc_checksum = 0; + uint8_t recv_checksum = 0; + + uint8_t buf[128]; // 临时读取缓冲区 + auto last_req_time = std::chrono::steady_clock::now(); + bool first_run = true; + + while (true) { + // --- 1. 发送请求逻辑 (每秒一次) --- + auto now = std::chrono::steady_clock::now(); + // 如果是第一次运行,或者距离上次发送超过 1 秒 (1000ms) + if (first_run || std::chrono::duration_cast(now - last_req_time).count() >= 1000) { + + // 发送指令 0xA5 + write(serial_fd, &REQUEST_CMD, 1); + + // 打印调试信息 (可选) + // std::cout << "[TX] Request sent" << std::endl; + + last_req_time = now; + first_run = false; + } + + // --- 2. 接收处理逻辑 (保持不变) --- + int n = read(serial_fd, buf, sizeof(buf)); + if (n > 0) { + for (int i = 0; i < n; i++) { + uint8_t byte = buf[i]; + switch (current_state) { + case STATE_WAIT_HEADER_0: + if (byte == FRAME_HEADER_0) current_state = STATE_WAIT_HEADER_1; + break; + case STATE_WAIT_HEADER_1: + if (byte == FRAME_HEADER_1) current_state = STATE_WAIT_LEN; + else if (byte == FRAME_HEADER_0) current_state = STATE_WAIT_HEADER_1; + else current_state = STATE_WAIT_HEADER_0; + break; + case STATE_WAIT_LEN: + expected_len = byte; + if (expected_len == sizeof(BMS_Payload_t)) { + payload_buffer.clear(); payload_buffer.reserve(expected_len); + current_state = STATE_READ_PAYLOAD; + } else { + current_state = STATE_WAIT_HEADER_0; + } + break; + case STATE_READ_PAYLOAD: + payload_buffer.push_back(byte); + if (payload_buffer.size() == expected_len) current_state = STATE_WAIT_CHECKSUM; + break; + case STATE_WAIT_CHECKSUM: + recv_checksum = byte; + current_state = STATE_WAIT_TAIL_0; + break; + case STATE_WAIT_TAIL_0: + if (byte == FRAME_TAIL_0) current_state = STATE_WAIT_TAIL_1; + else current_state = STATE_WAIT_HEADER_0; + break; + case STATE_WAIT_TAIL_1: + if (byte == FRAME_TAIL_1) { + // 计算校验和 + uint8_t calc_sum = 0; + for (uint8_t b : payload_buffer) calc_sum += b; + + if (calc_sum == recv_checksum) { + BMS_Payload_t* bms = (BMS_Payload_t*)payload_buffer.data(); + std::cout << "\n[RX] Data Updated: ------------------------" << std::endl; + std::cout << "Voltage: " << bms->voltage << " mV" << std::endl; + std::cout << "Current: " << bms->current << " mA" << std::endl; + std::cout << "Capacity: " << (int)bms->capacity << " %" << std::endl; + std::cout << "Temp: " << (float)bms->temp * 0.1f << " C" << std::endl; + std::cout << "Rem Cap: " << bms->rem_capacity << " mAh" << std::endl; + std::cout << "Chg Voltage: " << bms->chg_voltage << " mV" << std::endl; + std::cout << "Chg Current: " << bms->chg_current << " mA" << std::endl; + std::cout << "Status DSG: " << (int)bms->status_dsg << std::endl; + std::cout << "Cycle Count: " << bms->cycle_count << std::endl; + std::cout << "RSOC: " << (int)bms->rsoc << " %" << std::endl; + } else { + std::cerr << "Checksum Error!" << std::endl; + } + } + current_state = STATE_WAIT_HEADER_0; + break; + } + } + } else { + // 短暂休眠避免CPU占用过高,但不要睡太久以免错过数据 + usleep(5000); + } + } + close(serial_fd); + return 0; +} \ No newline at end of file