bonus-edge-proxy/src/test.cc

336 lines
12 KiB
C++
Raw Normal View History

#include <iostream>
#include <vector>
#include <string>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <cstring>
#include <cerrno>
#include <cmath>
#include <sys/ioctl.h>
#include <linux/serial.h>
// 串口设备路径根据RK3588的实际接线调整
// 通常USB转串口设备会被识别为 /dev/ttyUSB0, /dev/ttyUSB1...
// 板载串口可能是 /dev/ttyS1, /dev/ttyS2... 等
const char* SERIAL_PORT = "/dev/ttyS9";
// 默认Modbus参数
const int DEFAULT_BAUDRATE = B9600;
const unsigned char DEFAULT_SLAVE_ADDR = 0x01;
// 功能码定义
#define READ_HOLDING_REGISTERS 0x03
#define WRITE_SINGLE_REGISTER 0x06
// 寄存器地址
enum RegisterAddress {
REG_TEMP = 0x0000,
REG_HUMI = 0x0001,
REG_TEMP_ALARM = 0x0030,
REG_HUMI_ALARM = 0x0031,
REG_DEVICE_ADDR = 0x0002, // 用于写入
REG_BAUDRATE_CODE = 0x0003 // 用于写入
};
// 波特率码映射
const int BAUDRATES[] = {1200, 2400, 4800, 9600, 19200, 38400, 57600};
const unsigned char BAUDRATE_CODES[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
/**
* @brief Modbus RTU的CRC16校验码
* @param data
* @param length
* @return uint16_t CRC16值
*/
uint16_t calculateCRC16(const uint8_t *data, size_t length) {
uint16_t crc = 0xFFFF;
for (size_t i = 0; i < length; ++i) {
crc ^= data[i];
for (int j = 0; j < 8; ++j) {
if (crc & 0x0001) {
crc >>= 1;
crc ^= 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}
/**
* @brief
* @param port_name "/dev/ttyUSB0"
* @param baudrate B9600
* @return int -1
*/
int openSerialPort(const char* port_name, speed_t baudrate) {
int fd = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0) {
std::cerr << "Error opening serial port " << port_name << ": " << strerror(errno) << std::endl;
return -1;
}
// 设置串口参数
termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, baudrate);
cfsetospeed(&options, baudrate);
// 8N1
options.c_cflag &= ~PARENB; // 无校验
options.c_cflag &= ~CSTOPB; // 1位停止位
options.c_cflag &= ~CSIZE; // 清除数据位设置
options.c_cflag |= CS8; // 8位数据位
// 启用接收
options.c_cflag |= (CLOCAL | CREAD);
// 原始输入模式
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// 原始输出模式
options.c_oflag &= ~OPOST;
// 设置超时 (100ms)
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 1; // 0.1s
tcsetattr(fd, TCSANOW, &options);
// 清空输入/输出缓冲区
tcflush(fd, TCIOFLUSH);
// --- 配置RS485为半双工模式根据芯片型号可能需要ioctl ---
// 这是一个通用的方法适用于一些带有转换芯片的USB转串口模块
// serial_rs485 rs485conf;
// memset(&rs485conf, 0, sizeof(rs485conf));
// rs485conf.flags = SER_RS485_ENABLED; // 启用RS485模式
// rs485conf.delay_rts_before_send = 0; // 发送前RTS延时
// rs485conf.delay_rts_after_send = 0; // 发送后RTS延时
// if (ioctl(fd, TIOCSRS485, &rs485conf) < 0) {
// std::cerr << "Warning: Could not set RS485 mode, assuming it's already configured by hardware." << std::endl;
// }
// -----------------------------------------------------------------
return fd;
}
/**
* @brief
* @param baudrate
* @return uint8_t 0xFF
*/
uint8_t getBaudrateCode(int baudrate) {
for (size_t i = 0; i < sizeof(BAUDRATES) / sizeof(BAUDRATES[0]); ++i) {
if (BAUDRATES[i] == baudrate) {
return BAUDRATE_CODES[i];
}
}
return 0xFF;
}
/**
* @brief
* @param fd
* @param slave_addr
* @param reg_addr
* @param reg_count
* @return std::vector<uint8_t> CRC
*/
std::vector<uint8_t> readRegister(int fd, uint8_t slave_addr, uint16_t reg_addr, uint16_t reg_count) {
std::vector<uint8_t> request;
request.push_back(slave_addr);
request.push_back(READ_HOLDING_REGISTERS);
request.push_back(reg_addr >> 8);
request.push_back(reg_addr & 0xFF);
request.push_back(reg_count >> 8);
request.push_back(reg_count & 0xFF);
uint16_t crc = calculateCRC16(request.data(), request.size());
request.push_back(crc & 0xFF);
request.push_back(crc >> 8);
// 发送请求
if (write(fd, request.data(), request.size()) != request.size()) {
std::cerr << "Error writing to serial port." << std::endl;
return {};
}
// 根据协议,标准响应应该是:[地址][功能码][字节数][数据...][CRC_L][CRC_H]
// 预期响应大小 = 5 (头+尾) + reg_count * 2
const int expected_response_size = 5 + reg_count * 2;
std::vector<uint8_t> response(expected_response_size);
int bytes_read = read(fd, response.data(), expected_response_size);
if (bytes_read < expected_response_size) {
std::cerr << "Incomplete response from sensor. Expected " << expected_response_size
<< " bytes, got " << bytes_read << std::endl;
return {};
}
// 验证响应
if (response[0] != slave_addr || response[1] != READ_HOLDING_REGISTERS) {
std::cerr << "Invalid response header." << std::endl;
return {};
}
uint16_t received_crc = (response[bytes_read - 1] << 8) | response[bytes_read - 2];
uint16_t calculated_crc = calculateCRC16(response.data(), bytes_read - 2);
if (received_crc != calculated_crc) {
std::cerr << "CRC check failed." << std::endl;
return {};
}
// 返回有效数据部分从字节数开始到CRC前
return std::vector<uint8_t>(response.begin() + 2, response.end() - 2);
}
/**
* @brief
* @param fd
* @param slave_addr 0xFF广
* @param reg_addr
* @param value
* @return bool truefalse
*/
bool writeSingleRegister(int fd, uint8_t slave_addr, uint16_t reg_addr, uint16_t value) {
std::vector<uint8_t> request;
request.push_back(slave_addr);
request.push_back(WRITE_SINGLE_REGISTER);
request.push_back(reg_addr >> 8);
request.push_back(reg_addr & 0xFF);
request.push_back(value >> 8);
request.push_back(value & 0xFF);
uint16_t crc = calculateCRC16(request.data(), request.size());
request.push_back(crc & 0xFF);
request.push_back(crc >> 8);
// 发送请求
int bytes_written = write(fd, request.data(), request.size());
if (bytes_written != request.size()) {
std::cerr << "Error writing to serial port." << std::endl;
return false;
}
// 读取确认响应
std::vector<uint8_t> response(8);
int bytes_read = read(fd, response.data(), 8);
if (bytes_read != 8) {
std::cerr << "Incomplete response for write operation." << std::endl;
return false;
}
// 验证响应(应该和请求数据一致)
if (memcmp(request.data(), response.data(), 8) == 0) {
return true;
}
std::cerr << "Write confirmation mismatch." << std::endl;
return false;
}
/**
* @brief Modbus数据为实际温湿度值
* @param data
* @return float
*/
float parseSensorData(const std::vector<uint8_t>& data) {
if (data.size() < 2) return NAN;
int16_t raw_value = (data[0] << 8) | data[1];
return static_cast<float>(raw_value) / 10.0f;
}
int main() {
std::cout << "--- RK3588 Test for KLHA JWST-20 Sensor ---" << std::endl;
// 1. 打开串口
int serial_fd = openSerialPort(SERIAL_PORT, DEFAULT_BAUDRATE);
if (serial_fd < 0) {
return 1;
}
std::cout << "Successfully opened serial port: " << SERIAL_PORT << std::endl;
// 2. 读取温湿度数据
std::cout << "\nReading data from sensor at address 0x01..." << std::endl;
// 读取温度
auto temp_data = readRegister(serial_fd, DEFAULT_SLAVE_ADDR, REG_TEMP, 1);
float temperature = NAN;
if (!temp_data.empty()) {
temperature = parseSensorData(temp_data);
std::cout << "Temperature: " << temperature << " °C" << std::endl;
} else {
std::cout << "Failed to read temperature." << std::endl;
}
// 读取湿度
auto humi_data = readRegister(serial_fd, DEFAULT_SLAVE_ADDR, REG_HUMI, 1);
float humidity = NAN;
if (!humi_data.empty()) {
humidity = parseSensorData(humi_data);
std::cout << "Humidity: " << humidity << " %RH" << std::endl;
} else {
std::cout << "Failed to read humidity." << std::endl;
}
// 3. 读取报警状态
auto temp_alarm_data = readRegister(serial_fd, DEFAULT_SLAVE_ADDR, REG_TEMP_ALARM, 1);
if (!temp_alarm_data.empty()) {
uint8_t alarm_status = temp_alarm_data[0];
if (alarm_status == 0x00) std::cout << "Temperature Alarm: None" << std::endl;
else if (alarm_status == 0x01) std::cout << "Temperature Alarm: High" << std::endl;
else if (alarm_status == 0x02) std::cout << "Temperature Alarm: Low" << std::endl;
}
auto humi_alarm_data = readRegister(serial_fd, DEFAULT_SLAVE_ADDR, REG_HUMI_ALARM, 1);
if (!humi_alarm_data.empty()) {
uint8_t alarm_status = humi_alarm_data[0];
if (alarm_status == 0x00) std::cout << "Humidity Alarm: None" << std::endl;
else if (alarm_status == 0x01) std::cout << "Humidity Alarm: High" << std::endl;
else if (alarm_status == 0x02) std::cout << "Humidity Alarm: Low" << std::endl;
}
// 4. (高级功能) 示例:修改传感器波特率
// 注意:修改波特率是一个高风险操作,如果设置错误,且拔码开关不可调,可能导致设备无法通信。
// 下面的代码被注释掉,仅作演示。请谨慎使用!
/*
std::cout << "\n--- Advanced: Modifying Baudrate ---" << std::endl;
int new_baudrate_to_set = 19200;
uint8_t new_baud_code = getBaudrateCode(new_baudrate_to_set);
std::cout << "Attempting to change baudrate to " << new_baudrate_to_set << " (Code: 0x" << std::hex << (int)new_baud_code << std::dec << ")" << std::endl;
// 使用广播地址(0xFF)向所有设备发送波特率修改命令
if (writeSingleRegister(serial_fd, 0xFF, REG_BAUDRATE_CODE, new_baud_code)) {
std::cout << "Baudrate change command sent successfully. Device will restart with new baudrate." << std::endl;
// 重要:必须重新打开串口使用新的波特率
close(serial_fd);
sleep(2); // 给设备重启时间
serial_fd = openSerialPort(SERIAL_PORT, B19200); // 使用新波特率重新打开
if (serial_fd < 0) {
std::cerr << "Failed to reopen port with new baudrate. Device might be unresponsive." << std::endl;
return 1;
}
std::cout << "Reopened port with new baudrate. Attempting to read data again..." << std::endl;
// 重新读取数据来验证
auto new_temp_data = readRegister(serial_fd, DEFAULT_SLAVE_ADDR, REG_TEMP, 1);
if (!new_temp_data.empty()) {
std::cout << "New Temperature: " << parseSensorData(new_temp_data) << " °C" << std::endl;
}
} else {
std::cerr << "Failed to send baudrate change command." << std::endl;
}
*/
// 5. 关闭串口
close(serial_fd);
std::cout << "\nTest finished. Serial port closed." << std::endl;
return 0;
}