You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt5 QtSerialPort读取Hex串口数据出现拆分问题求助

解决QtSerialPort串口Hex数据接收拆分问题

问题原因

串口通信属于流式传输,readyRead信号触发仅代表当前有数据可读,但不一定是完整的一帧数据。你遇到的拆分是串口传输的正常现象,必须通过数据缓存和帧解析逻辑来处理。

解决方案

核心思路是维护全局接收缓冲区,每次收到数据就追加到缓冲区,再按照协议规则(比如固定帧长度)从缓冲区中提取完整帧进行判断。以下是修改后的完整代码:

from PyQt5 import QtCore, QtWidgets, QtSerialPort

class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)

        self.send1_btn = QtWidgets.QPushButton(
            text="Send1",
            clicked=self.send1
        )
        self.send2_btn = QtWidgets.QPushButton(
            text="Send2",
            clicked=self.send2
        )
        self.send3_btn = QtWidgets.QPushButton(
            text="Send3",
            clicked=self.send3
        )

        lay = QtWidgets.QVBoxLayout(self)
        hlay = QtWidgets.QHBoxLayout()
        hlay.addWidget(self.send1_btn)
        hlay.addWidget(self.send2_btn)
        hlay.addWidget(self.send3_btn)
        lay.addLayout(hlay)
        
        self.serial = QtSerialPort.QSerialPort('/dev/ttyUSB0',
        baudRate=QtSerialPort.QSerialPort.Baud57600,
        parity=QtSerialPort.QSerialPort.NoParity,
        dataBits=QtSerialPort.QSerialPort.Data8,
        stopBits=QtSerialPort.QSerialPort.OneStop)
        self.serial.open(QtCore.QIODevice.ReadWrite)
        self.serial.setDataTerminalReady(True)
        self.serial.readyRead.connect(self.handle_ready_Read)
        
        # 初始化全局接收缓冲区
        self.recv_buffer = b''
        # 定义目标完整帧
        self.target_frame = b'\xaa\x02\x3c\x31\x19'
        # 固定帧长度
        self.frame_length = len(self.target_frame)

    QtCore.pyqtSlot()
    def handle_ready_Read(self):
        # 读取当前所有可用数据,追加到缓冲区
        new_data = self.serial.readAll()
        self.recv_buffer += new_data.data()
        
        # 循环检查缓冲区中是否有完整帧
        while len(self.recv_buffer) >= self.frame_length:
            # 提取缓冲区前N字节作为当前帧
            current_frame = self.recv_buffer[:self.frame_length]
            print(f"当前解析帧: {current_frame}")
            
            if current_frame == self.target_frame:
                print("Ok")
            else:
                print("Error")
            
            # 移除已处理的帧,保留剩余未处理数据
            self.recv_buffer = self.recv_buffer[self.frame_length:]

    @QtCore.pyqtSlot()
    def send1(self):    
        # Reset,out service 
        self.serial.write(b'\xCC\x02\x3C\x55\x5F')
        # Syncro
        self.serial.write(b'\xCC\x02\x3C\x24\x2E')
        # Setup 
        self.serial.write(b'\xCC\x04\x3C\x23\x00\x10\x3F')
        # Enable
        self.serial.write(b'\xCC\x02\x3C\x12\x1C')
        
    @QtCore.pyqtSlot()
    def send2(self):
        # value  
        self.serial.write(b'\xCC\x04\x3C\x34\x00\x05\x45')
        self.step1=1
        
    @QtCore.pyqtSlot()
    def send3(self):
        # Confirm Ok 
        self.serial.write(b'\xCC\x02\x3C\x22\x2C')    
        
if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

关键修改点

  1. 添加self.recv_buffer全局缓冲区,存储所有未处理的串口数据
  2. 每次收到新数据后,将其追加到缓冲区,避免数据丢失
  3. 循环检查缓冲区长度是否达标,提取完整帧进行判断,处理后移除已解析的部分
  4. 修正原代码中判断条件的错误(原代码错误使用b'xAAxO2x3Cx31x19 ',正确Hex表示应为b'\xaa\x02\x3c\x31\x19')

扩展说明

如果你的通信协议有更复杂的规则(比如起始标志+长度字段+校验位),可以调整解析逻辑:

  • 先在缓冲区中定位起始标志(如0xaa)
  • 根据协议中的长度字段确定后续需要读取的字节数
  • 等待缓冲区数据足够后,提取完整帧并进行校验

内容的提问来源于stack exchange,提问作者Eric

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 08:17:40