generated from guanyuankai/bonus-edge-proxy
195 lines
7.9 KiB
C++
195 lines
7.9 KiB
C++
|
|
#include <iostream>
|
|||
|
|
#include <fcntl.h>
|
|||
|
|
#include <unistd.h>
|
|||
|
|
#include <termios.h>
|
|||
|
|
#include <cstring>
|
|||
|
|
#include <cstdint>
|
|||
|
|
#include <vector>
|
|||
|
|
#include <iomanip>
|
|||
|
|
#include <chrono>
|
|||
|
|
// --- 协议定义 (必须与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<uint8_t> 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<std::chrono::milliseconds>(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;
|
|||
|
|
}
|