使用python+Pyqt5实现串口调试助手
python可以利用serial模块来和串口设备进行485或者232通讯。
当然,网上这类串口调试助手的小程序有很多,不过这些程序要么是要收费,只能试用30天,要么是不好用。
况且,别人写的程序,你只能使用,无法取出其中的数据来进行处理,所以,如果可以自己写一个程序,既方便使用,又可以随时随地使用其中的数据。
软件:python3.10
pycharm2021
硬件:window10电脑
串口485设备(国产流量计)
串口转usb线(电脑不带串口,只能转接)
准备好了后,就可以开始写程序了。
串口通讯程序首先要对串口进行设置,如波特率、数据位、停止位、校验位等。可以称之为初始化:
初始化:
def serial_init(self): #初始化 self.port = self.port_set.currentText() self.bps = int(self.baud_set.currentText()) self.timeout = float(self.timeout_set.text()) try: self.ser = serial.Serial(port=self.port,baudrate=self.bps,bytesize=8,parity='N',stopbits=1) print(self.ser) if self.ser.is_open: print('串口正常') except Exception as e: QMessageBox.warning(self, 'tips!', str(e), QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Ok) print('异常:', e)
初始化完成后,需要将串口打开,只有串口成功打开,才可以进行后面的数据传输。python中的serial模块,提供serial.isopen来进行检查。当然,此处需要注意的是,在串口serial实例化的时候,实际上已经同时打开串口了。所以,如果在初始化程序里建立了实例化:
self.ser = serial.Serial(port=self.port,baudrate=self.bps,bytesize=8,parity='N',stopbits=1)
那么也可以不用在重复打开串口了。
串口打开:
def open_serial(self): #打开串口 try: self.ser.open() except Exception as e: QMessageBox.warning(self, 'tips!', str(e), QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Ok) print('异常:', e)
串口关闭:
def close_serial(self): #关闭串口 try: self.ser.close() except Exception as e: QMessageBox.warning(self,'tips!',str(e),QMessageBox.Ok|QMessageBox.Cancel,QMessageBox.Ok) print('异常:', e)
调试完成后,最好关闭串口,有始有终。
在完成上面的初始化和打开步骤后,就可以进行数据的读、写了,本例中使用了与流量计进行通讯,读取流量计实时流量。该流量计读、写数据时有专门的格式:
所以,从电脑端发送指令时,需要按照流量计的格式发送,才能得到正确的反馈。485是半双工传输,即同一时间只能进行读或者写,读、写不能同时进行。
本例中,读取流量指令格式为:
01 03 00 10 00 02 C5 CE
实际操作中,我将流量计设备地址修改为2,即
02 03 00 10 00 02 C5 CE
发送数据:
def send_data(self): ct = datetime.datetime.now() #获取当前系统时间 ct_str = ct.strftime("%Y-%m-%d %H:%M:%S") #格式化当前系统时间(字符形式) try: self.senddata_s=self.le_senddata.text() self.senddata=bytes.fromhex(self.senddata_s) #字符转成字节 self.ser.write(self.senddata) #self.senddata_str=self.senddata.hex() self.senddata_str_fg=self.str_fenge(self.senddata_s) #self.le_senddata.setText(self.senddata.hex()) self.le_recdata.append('\n' + '[' + ct_str+ ']' + ' ' + self.senddata_str_fg + '\n') #print(self.senddata) time.sleep(0.1) self.read_data_size() except Exception as e: QMessageBox.warning(self, 'tips!', str(e), QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Ok)
读取数据:
def read_data_size(self): ct=datetime.datetime.now() ct_str=ct.strftime("%Y-%m-%d %H:%M:%S") try: #self.size=10 self.read_data=self.ser.read_all() #print(self.read_data) self.read_data_str=self.read_data.hex() #字节转成16进制字符显示 #re.findall(r'.{3}',self.read_data_str) self.read_data_str_fg=self.str_fenge(self.read_data_str) #print(self.read_data_str) self.le_recdata.append('\n'+'['+ct_str+']'+' '+self.read_data_str_fg+'\n') except Exception as e: QMessageBox.warning(self, 'tips!', str(e), QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Ok) #return self.read_data
serial的读取数据时,有几种方式,read()、read_all()、readline(),read()就是按照字节数size大小读取缓存区字节,read_all()即读取所有数据,readline()读取缓存区最新一行数据。
完整程序:
from numpy.lib.function_base import place import serial import serial.tools.list_ports import sys import re import os import time import datetime import threading from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * class Serialwindow(QWidget): def __init__(self) -> None: super().__init__() self.initUI() def initUI(self): #self.stylefile = 'E:\\100proworld2021\opencvtestpro01\爬虫程序\爬虫-style.qss' #self.qssstyle = CommonHelper.readQSS(self.stylefile) self.port = '' self.bps = 0 self.timeout = 0.0 self.senddata=bytes() self.read_data='' self.btn_plist=QPushButton('获取可用串口',self) self.btn_plist.setGeometry(20,20,60,20) self.btn_plist.clicked.connect(self.get_serial_info) self.btn_plist.adjustSize() self.btn_ser_init=QPushButton('初始化',self) self.btn_ser_init.setGeometry(20,50,60,20) self.btn_ser_init.clicked.connect(self.serial_init) self.btn_ser_init.adjustSize() self.btn_open=QPushButton('打开串口',self) self.btn_open.setGeometry(20,80,60,20) self.btn_open.clicked.connect(self.open_serial) self.btn_open.adjustSize() self.btn_close=QPushButton('关闭串口',self) self.btn_close.setGeometry(20,110,60,20) self.btn_close.clicked.connect(self.close_serial) self.btn_close.adjustSize() self.btn_read_data=QPushButton('读取数据',self) self.btn_read_data.setGeometry(20,140,60,20) self.btn_read_data.clicked.connect(self.read_data_size) self.btn_read_data.adjustSize() self.btn_write_data=QPushButton('发送数据',self) self.btn_write_data.setGeometry(40,460,60,20) self.btn_write_data.clicked.connect(self.send_data) self.btn_write_data.adjustSize() #Qcombobox self.port_set=QComboBox(self) self.port_set.setGeometry(500,20,100,20) self.port_set.addItems(['COM1']) #self.port_set.activated.connect(self.Qcombo) self.lbl_port_set=QLabel(self) self.lbl_port_set.setGeometry(420,20,60,20) self.lbl_port_set.setText('串口号:') self.baud_set=QComboBox(self) self.baud_set.setGeometry(500,50,100,20) self.baud_set.addItems(['9600','19200','38400','115200']) self.lbl_baud_set=QLabel(self) self.lbl_baud_set.setGeometry(420,50,60,20) self.lbl_baud_set.setText('波特率:') self.stopbit_set=QComboBox(self) self.stopbit_set.setGeometry(500,80,100,20) self.stopbit_set.addItems(['0','1']) self.lbl_stopbit_set = QLabel(self) self.lbl_stopbit_set.setGeometry(420, 80, 60, 20) self.lbl_stopbit_set.setText('停止位:') self.parity_set=QComboBox(self) self.parity_set.setGeometry(500,110,100,20) self.parity_set.addItems(['无','奇校验','偶校验']) self.lbl_parity_set = QLabel(self) self.lbl_parity_set.setGeometry(420, 110, 60, 20) self.lbl_parity_set.setText('校验位:') self.databit_set=QComboBox(self) self.databit_set.setGeometry(500,140,100,20) self.databit_set.addItems(['8','7']) self.lbl_databit_set=QLabel(self) self.lbl_databit_set.setGeometry(420,140,60,20) self.lbl_databit_set.setText('数据位:') self.timeout_set=QLineEdit(self) self.timeout_set.setGeometry(500,170,100,20) self.timeout_set.setText('1000') self.lbl_timeout_set=QLabel(self) self.lbl_timeout_set.setGeometry(420,170,60,20) self.lbl_timeout_set.setText('超时设置:') self.lbl_timeout_set_2=QLabel(self) self.lbl_timeout_set_2.setGeometry(610,170,60,20) self.lbl_timeout_set_2.setText('ms') # self.le_senddata=QLineEdit(self) self.le_senddata.setGeometry(120,460,300,20) self.le_senddata.setText('010300100002C5CE') self.le_recdata=QTextEdit(self) self.le_recdata.setGeometry(120,220,600,200) self.setGeometry(100,100,800,600) self.setWindowTitle('简易串口调试助手-菌尘') # self.setStyleSheet(self.qssstyle) self.show() def Qcombo(self): print(self.port_set.currentText()) print(self.baud_set.currentText()) print(self.stopbit_set.currentText()) print(self.parity_set.currentText()) print(self.databit_set.currentText()) print(self.timeout_set.text()) def get_serial_info(self): #获取可用串口列表 #打印可用串口列表 #self.need_serial = '' self.plist = list(serial.tools.list_ports.comports()) if len(self.plist) <= 0: print('未找到串口') qm = QMessageBox.warning(self, '提示窗口', '未找到串口!请检查接线和电脑接口。', QMessageBox.Ok|QMessageBox.Cancel,QMessageBox.Ok) if qm == QMessageBox.Yes: print('Yes') else: print('No') else: for i in list(self.plist): self.port_set.addItem(i.name) #return self.need_serial #print(self.plist) def serial_init(self): #初始化 self.port = self.port_set.currentText() self.bps = int(self.baud_set.currentText()) self.timeout = float(self.timeout_set.text()) try: self.ser = serial.Serial(port=self.port,baudrate=self.bps,bytesize=8,parity='N',stopbits=1) print(self.ser) if self.ser.is_open: print('串口正常') except Exception as e: QMessageBox.warning(self, 'tips!', str(e), QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Ok) print('异常:', e) def open_serial(self): #打开串口 try: self.ser.open() except Exception as e: QMessageBox.warning(self, 'tips!', str(e), QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Ok) print('异常:', e) def close_serial(self): #关闭串口 try: self.ser.close() except Exception as e: QMessageBox.warning(self,'tips!',str(e),QMessageBox.Ok|QMessageBox.Cancel,QMessageBox.Ok) print('异常:', e) def read_data_size(self): ct=datetime.datetime.now() ct_str=ct.strftime("%Y-%m-%d %H:%M:%S") try: #self.size=10 self.read_data=self.ser.read_all() #print(self.read_data) self.read_data_str=self.read_data.hex() #字节转成16进制字符显示 #re.findall(r'.{3}',self.read_data_str) self.read_data_str_fg=self.str_fenge(self.read_data_str) #print(self.read_data_str) self.le_recdata.append('\n'+'['+ct_str+']'+' '+self.read_data_str_fg+'\n') except Exception as e: QMessageBox.warning(self, 'tips!', str(e), QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Ok) #return self.read_data def read_data_line(self): self.read_data=self.ser.readline() return self.read_data def send_data(self): ct = datetime.datetime.now() #获取当前系统时间 ct_str = ct.strftime("%Y-%m-%d %H:%M:%S") #格式化当前系统时间(字符形式) try: self.senddata_s=self.le_senddata.text() self.senddata=bytes.fromhex(self.senddata_s) #字符转成字节 self.ser.write(self.senddata) #self.senddata_str=self.senddata.hex() self.senddata_str_fg=self.str_fenge(self.senddata_s) #self.le_senddata.setText(self.senddata.hex()) self.le_recdata.append('\n' + '[' + ct_str+ ']' + ' ' + self.senddata_str_fg + '\n') #print(self.senddata) time.sleep(0.1) self.read_data_size() except Exception as e: QMessageBox.warning(self, 'tips!', str(e), QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Ok) def read_data_alway(self, way): print('开始接受数据:') while True: try: if self.ser.inWaiting: if(way == 0 ): for i in range(self.ser.inWaiting): print('接收ascII数据:'+str(self.read_data_size(1))) data1 = self.read_data_size(1).hex() data2 = int(data1, 16) print('接收到16进制数据:'+data1+'接收到10进制数据:'+str(data2)) if(way == 1 ): data = self.ser.read_all() except Exception as e: print('异常:', e) def str_fenge(self,A): ''' 对字符串进行按长度分割,并在中间加入其他字符,如空格、短横等 ''' b = re.findall(r'.{2}',A) c = ' '.join(b) #print(c) return c if __name__ == '__main__': app = QApplication(sys.argv) ex = Serialwindow() sys.exit(app.exec_())
上面的程序经过测试,可以与流量计正常通讯。
到此这篇关于使用python+Pyqt5实现串口调试助手的文章就介绍到这了,更多相关python Pyqt5串口调试助手内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!