#include #include #include #include #include #include #include #include #include #include #include // 串口设备路径,根据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 成功返回接收到的数据(不包括地址、功能码和CRC),失败返回空向量 */ std::vector readRegister(int fd, uint8_t slave_addr, uint16_t reg_addr, uint16_t reg_count) { std::vector 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 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(response.begin() + 2, response.end() - 2); } /** * @brief 写入单个寄存器数据(用于修改地址或波特率) * @param fd 串口文件描述符 * @param slave_addr 从站地址(通常是0xFF广播地址,用于修改) * @param reg_addr 寄存器地址 * @param value 要写入的值 * @return bool 成功返回true,失败返回false */ bool writeSingleRegister(int fd, uint8_t slave_addr, uint16_t reg_addr, uint16_t value) { std::vector 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 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& data) { if (data.size() < 2) return NAN; int16_t raw_value = (data[0] << 8) | data[1]; return static_cast(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; }