当前位置:   article > 正文

使用python+Pyqt5来写一个简易串口调试助手_pyqt5串口助手数据导出功能怎么写

pyqt5串口助手数据导出功能怎么写

python可以利用serial模块来和串口设备进行485或者232通讯。
在这里插入图片描述
当然,网上这类串口调试助手的小程序有很多,不过这些程序要么是要收费,只能试用30天,要么是不好用。

况且,别人写的程序,你只能使用,无法取出其中的数据来进行处理,所以,如果可以自己写一个程序,既方便使用,又可以随时随地使用其中的数据。

软件:python3.10
pycharm2021
硬件:window10电脑
串口485设备(国产流量计)
串口转usb线(电脑不带串口,只能转接)
串口转usb线
流量计带485通讯

准备好了后,就可以开始写程序了。

串口通讯程序首先要对串口进行设置,如波特率、数据位、停止位、校验位等。可以称之为初始化:

初始化:

    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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

初始化完成后,需要将串口打开,只有串口成功打开,才可以进行后面的数据传输。python中的serial模块,提供serial.isopen来进行检查。当然,此处需要注意的是,在串口serial实例化的时候,实际上已经同时打开串口了。所以,如果在初始化程序里建立了实例化:

 self.ser = serial.Serial(port=self.port,baudrate=self.bps,bytesize=8,parity='N',stopbits=1)
  • 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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

串口关闭:

 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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

调试完成后,最好关闭串口,有始有终。

在完成上面的初始化和打开步骤后,就可以进行数据的读、写了,本例中使用了与流量计进行通讯,读取流量计实时流量。该流量计读、写数据时有专门的格式:
在这里插入图片描述
所以,从电脑端发送指令时,需要按照流量计的格式发送,才能得到正确的反馈。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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

读取数据:

    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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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_())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269

上面的程序经过测试,可以与流量计正常通讯。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/764523
推荐阅读
相关标签
  

闽ICP备14008679号