Closed xiaokun-oo closed 5 years ago
现象: 例 发送 AA AA AA 接收就是 AA AA D5
@xiaokun-oo 接收数据问题检查下 serial模块收发数据格式问题,比如代码处理数据有误, 这边建议你可以考虑PyQt自带的模块:https://github.com/PyQt5/PyQt/blob/master/QSerialPort/SerialDebugAssistant.py
现象: 例 发送 AA AA AA 接收就是 AA AA D5
代码
``` import sys import serial import serial.tools.list_ports import datetime import os import configparser from serial.tools.list_ports import comports from PyQt5 import QtCore, QtWidgets from PyQt5.QtCore import QTimer from PyQt5.QtWidgets import QMessageBox from Ui_primaryUI import Ui_MainWindow # 声明 主界面 class MyWindow(QtWidgets.QMainWindow, Ui_MainWindow): # 继承QWidget和Ui_Form # 定义信号 portis_opened = QtCore.pyqtSignal() # 端口已打开信号 portis_closed = QtCore.pyqtSignal() # 端口已关闭信号 statusupdate = QtCore.pyqtSignal(str) # 状态更新信号 new_txdata = QtCore.pyqtSignal(object) # 新发送数据信号 txresend = QtCore.pyqtSignal(object) datatoplot = QtCore.pyqtSignal(object) def __init__(self): super(MyWindow, self).__init__() # 超级加载 self.setupUi(self) # 初始化界面 self.ser = serial.Serial() # 实例化串口 self.CreateItems() # 设置实例 self.CreateSignalSlot() # 设置信号与槽 self.InitSetting() # 初始化配置/加载上一次使用的数据 self.RefreshCOM() # 第一次打开先加载一次端口 # self.init() # 初始化接收发送数量 self.receive_num = 0 self.ReceiveNumlabel.setText('Receive: ' + '{:d}'.format(self.receive_num)) self.send_num = 0 self.SendNumlabel.setText('Send: ' + '{:d}'.format(self.send_num)) # 设置实例 def CreateItems(self): # 定时器类 self.timer_receive = QTimer(self) # 初始化 接收 定时器 self.timer_receive.timeout.connect(self.ReceiveData) # 定时接收 self.timer_send = QTimer(self) # 初始化 发送 定时器 # self.timer_send.timeout.connect(self.SendData) # 定时发送 self.timer_date = QTimer(self) # 初始化 时间 定时器 self.timer_date.timeout.connect(self.RefreshTime) # 当定时结束时调用 RefreshTime() 刷新时间 self.timer_date.start(500) # 设置计时间隔 500ms 并启动 # 设置信号与槽 def CreateSignalSlot(self): self.pushButton_Scanning.clicked.connect(self.RefreshCOM) # 刷新串口按钮信号 self.pushButton_Connect_break.clicked.connect(self.OpenOrCloseSerial) # 点击 开启/关闭 按钮信号 # self.ser.readyRead.connect(self.ReceiveData) # 接收数据 '''串口收发函数''' def RefreshCOM(self): # 串口号更新 port_list = serial.tools.list_ports.comports() # 获取所有的串口端号 if len(port_list) == 0: # 如果串口列表为0,即没有端口 self.comboBox_Serial_slogans.clear() else: for port, desc, hwid in comports(): # 识别串口 if self.comboBox_Serial_slogans.findText(port) != -1: # 如果combox中已经有该串口,则不选要再次添加 pass else: self.comboBox_Serial_slogans.addItem(port) # 将串口号添加到Box def InitSetting(self): # 加载配置文件 global config if not os.path.exists('setting.ini'): # 检测是否存在配置文件 open('setting.ini', 'w') # 创建配置文件 config = configparser.ConfigParser() # 加载现有配置文件 config.read('setting.ini') # 读取ini文件 if not config.has_section('globals'): # 如果为空 config['globals'] = {'baudrate': '115200', 'stop': '1', 'data': '8', 'parity': 'NONE', 'string': 'Hello'} # 向配置内写入数据 with open('setting.ini', 'w') as configile: # with用法,先打开数据再写 config.write(configile) # 把配置文件的数据加载到窗口 self.comboBox_Baud_rate.setCurrentText(config.get('globals', 'baudrate')) # 波特率 self.comboBox_Stop_bit.setCurrentText(config.get('globals', 'stop')) # 停止位 self.comboBox_Data_bits.setCurrentText(config.get('globals', 'data')) # 数据位 self.comboBox_Calibration_bit.setCurrentText(config.get('globals', 'parity')) # 校验位 self.textBrowser_basic_Tx.insertPlainText(config.get('globals', 'string')) # 发送窗口 def OpenOrCloseSerial(self): # 打开/关闭串口操作 if not self.ser.isOpen(): # 如果串口没有打开 try: # self.ser.timeout = 1 # 读超时设置 # self.ser.xonxoff = 0 # 软件流控 self.ser.port = self.comboBox_Serial_slogans.currentText() # 用户选择的 端口号 写入 try: # 这里主要是处理自定义波特率时非法输入设置的 self.ser.baudrate = int(self.comboBox_Baud_rate.currentText()) # 用户选择的 波特率 写入 except Exception: QMessageBox.critical(self, 'Message', '请输入正确的波特率') return None self.ser.bytesize = int(self.comboBox_Data_bits.currentText()) # 用户选择的 数据位 写入 self.ser.stopbits = int(self.comboBox_Stop_bit.currentText()) # 用户选择的 停止位 写入 self.controlbit = self.comboBox_Flow_control.currentText() # 用户选择的 流控 写入 serial_parity = self.comboBox_Calibration_bit.currentText() # 获取用户选择的校验位 if serial_parity == 'NONE': self.ser.parity = serial.PARITY_EVEN # 设置当前的文本校验方式为 无校验 elif serial_parity == 'ODD': self.ser.parity = serial.PARITY_ODD # 设置当前的文本校验方式为 奇校验 elif serial_parity == 'EVEN': self.ser.parity = serial.PARITY_EVEN # 设置当前的文本校验方式为 偶校验 elif serial_parity == 'MARK': self.ser.parity = serial.PARITY_MARK # 设置当前的文本校验方式为 固定值1 elif serial_parity == 'SPACE': self.ser.parity = serial.PARITY_SPACE # 设置当前的文本校验方式为 固定值0 else: return None self.ser.open() # 开启串口 self.timer_receive.start(5) # 设置计时间隔 1ms 并启动 except Exception: QMessageBox.critical(self, 'Message', '没有可用的串口或当前串口被占用') else: self.comboBox_Serial_slogans.setEnabled(False) # 关闭串口选择功能 self.comboBox_Baud_rate.setEnabled(False) # 关闭波特率选择功能 self.comboBox_Data_bits.setEnabled(False) # 关闭数据位选择功能 self.comboBox_Stop_bit.setEnabled(False) # 关闭停止位选择功能 self.comboBox_Calibration_bit.setEnabled(False) # 关闭校验位选择功能 self.comboBox_Flow_control.setEnabled(False) # 关闭流控选择功能 self.pushButton_Connect_break.setText(u'关闭串口') # ‘打开串口’改为‘关闭串口’ else: # 如果串口已经打开,再次点击 self.ser.close() # 关闭串口 self.timer_receive.stop() # 关闭定时器 self.comboBox_Serial_slogans.setEnabled(True) # 关闭串口选择功能 self.comboBox_Baud_rate.setEnabled(True) # 关闭波特率选择功能 self.comboBox_Data_bits.setEnabled(True) # 关闭数据位选择功能 self.comboBox_Stop_bit.setEnabled(True) # 关闭停止位选择功能 self.comboBox_Calibration_bit.setEnabled(True) # 关闭校验位选择功能 self.comboBox_Flow_control.setEnabled(True) # 关闭流控选择功能 self.pushButton_Connect_break.setText(u'打开串口') # ‘关闭串口’改为‘打开串口’ def RefreshTime(self): # 刷新界面右下角的时间显示,并显示 current = datetime.datetime.now() self.Timelabel.setText('当前时间 ' + '{:02d}'.format(current.hour) + ':{:02d}'.format(current.minute) + ':{:02d}'.format(current.second)) def on_comboBox_Baud_rate_activated(self): # 选择自定义波特率后 if self.comboBox_Baud_rate.currentText() == 'Custom': self.comboBox_Baud_rate.setEditable(True) # 打开可编辑功能 else: self.comboBox_Baud_rate.setEditable(False) # 关闭可编辑功能 def on_HexDisplayCheckBox_stateChanged(self): # 16进制显示复选框状态发生变化 temp_string = self.textBrowser_basic_Rx.toPlainText() # 读取输出框的数据 if temp_string != '': # 数据非空 if self.radioButton_Receive_HEX.checkState(): # 如果选择十六进制显示 temp_hex = temp_string.encode('gbk') # 解码 display_hex = '' for i in range(0, len(temp_hex)): display_hex = display_hex + '{:02X}'.format(temp_hex[i]) + ' ' # 转换为十六进制 self.textBrowser_basic_Rx.clear() # 清空输入框 self.textBrowser_basic_Rx.insertPlainText(display_hex) # 输出十六进制数据 if not self.HexDisplayCheckBox.checkState(): # 没有选择十六进制显示 temp_hex = self.textBrowser_basic_Tx.toPlainText() # 获取textedit文本内容 temp_hex = temp_hex.strip() # 清除前后的空格 display_string = bytes.fromhex(temp_hex).decode('gbk') # 转换成字符串 self.textBrowser_basic_Rx.clear() # 清除输出框 self.textBrowser_basic_Rx.insertPlainText(display_string) # 插入数据 else: return None def ReceiveData(self): # 接收数据 if self.ser.isOpen(): # 是否打开串口 try: receive_bytes_num = self.ser.inWaiting() # 获取Output buffer中的字节数 except Exception: # 串口拔出错误 self.timer_receive.stop() # 关闭接收定时器 self.timer_send.stop() # 关闭发送定时器 self.ser.close() # 关闭串口 self.ser = None # 串口无效 if receive_bytes_num > 0: # 接收数据非空 data = self.ser.read(receive_bytes_num) # 读取串口数据 print(receive_bytes_num) # 测试:打印串口数据 out_s = '' if self.radioButton_Receive_HEX.isChecked(): # 十六进制显示 for i in range(receive_bytes_num): out_s = out_s + '{:02X}'.format(data[i]) + ' ' if self.checkBox_Receive_Automatic_Line_Break.isChecked(): self.textBrowser_basic_Rx.append(out_s) else: self.textBrowser_basic_Rx.insertPlainText(out_s) else: if self.checkBox_Receive_Automatic_Line_Break.isChecked(): self.textBrowser_basic_Rx.append(data.decode('iso-8859-1')) else: self.textBrowser_basic_Rx.insertPlainText(data.decode('iso-8859-1')) self.receive_num = self.receive_num + receive_bytes_num # 统计接收字符的数量 self.ReceiveNumlabel.setText('Receive: ' + '{:d}'.format(self.receive_num)) # 显示接收数据字节数 else: return None if __name__ == '__main__': # 如果整个程序是主函数 # QApplication相当于main函数,也就是整个程序(很多文件)的主入口函数。 # 对于GUI程序必须至少有一个这样的实例来让程序运行。 app = QtWidgets.QApplication(sys.argv) # 生成 mywindow 类的实例 myshow = MyWindow() # 生成mywindow类的实例 myshow myshow.show() # myshow调用show方法 # 调用sys库的exit退出方法,条件是app.exec_(),也就是整个窗口关闭。 # 有时候退出程序后,sys.exit(app.exec_())会报错,改用app.exec_()就没事 # https://stackoverflow.com/questions/25719524/difference-between-sys-exitapp-exec-and-app-exec sys.exit(app.exec_()) ```