Bluetooth 使用pyserial列出com端口,而无需重新连接蓝牙设备

Bluetooth 使用pyserial列出com端口,而无需重新连接蓝牙设备,bluetooth,pyserial,Bluetooth,Pyserial,我正在构建一个GUI,通过COM端口连接到windows上的蓝牙设备,这很简单。在启动期间,Toolbar类列出了可用的COM端口 class ToolBar(QtGui.QMainWindow): def __init__(self, parent=None): QtGui.QMainWindow.__init__(self) self.menu = self.menuBar() #self.menu.installEventFilter(self) self

我正在构建一个GUI,通过COM端口连接到windows上的蓝牙设备,这很简单。在启动期间,Toolbar类列出了可用的COM端口

class ToolBar(QtGui.QMainWindow):
def __init__(self, parent=None):
    QtGui.QMainWindow.__init__(self)

    self.menu = self.menuBar()
    #self.menu.installEventFilter(self)
    self.coms = QMenu('COM Selection', self)
    self.group = QActionGroup(self.coms)
    self.checkSerial = serial.tools.list_ports.comports()

    self.populate()

    for text in self.texts:
        action = QAction(text, self.coms, checkable=True, checked=text==self.texts[0])
        self.coms.addAction(action)
        self.group.addAction(action)
    self.group.setExclusive(True)
    self.group.triggered.connect(self.onTriggered)
    self.menu.addMenu(self.coms)

def populate(self):
    self.texts = [p.device for p in self.checkSerial]
    self.checkSerial.clear()
    
    return False
    
def onTriggered(self, action):
    print(action.text())
    Constants.comPort = action.text()
当按下开始-停止按钮时,MainWindow类中的closeCom函数读取用户选择以连接到COM。但是,COM中的一个端口已通过serial.tools.list_端口列出,它似乎已连接并在我尝试连接以读取数据时引发。如果我启动GUI而不调用工具栏,这样就不会列出COM,那么在脚本中以文本形式输入COM端口就没有问题

    def closeCom(self):
    if self.log==False:
        self.serialPort.close()
        print('stopped logging')  
        self.timer.stop()
        self.saver.stop()
        self.file.close()
    if self.log==True:

        self.portName = 'COM4'
        self.portName = Constants.comPort
        baudRate = 9600
        self.serialPort = serialPlot(self.portName, baudRate)   # initializes all required variables
        self.serialPort.readSerialStart()   #data starting to save
我曾尝试使用蓝牙,因为在查找设备时此模块似乎没有连接,尽管我找不到如何使用pyserial中的设备地址连接到设备的示例

import bluetooth   

devices = bluetooth.discover_devices(lookup_names=True) 
print(type(devices))   
print("Devices found: %s" % len(devices))   
for item in devices:     
    print(item)
总之,我的问题是,在pyserial中列出COM后,我是否可以清除它们以便连接,如果没有,我是否可以使用蓝牙库找到的地址进行连接。我愿意接受建议,因为我整个星期都在坚持这一点

这是完整的代码,以防有所帮助

非常感谢

import serial
import serial.tools.list_ports
import time
import threading
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QHBoxLayout, QLabel, QSizePolicy, QSlider, QSpacerItem, \
    QVBoxLayout, QWidget
from PyQt5.QtWidgets import QAction, QActionGroup, QMenu, QApplication    
import pyqtgraph as pg

import sys
import queue

import bluetooth   

devices = bluetooth.discover_devices(lookup_names=True) 
print(type(devices))   
print("Devices found: %s" % len(devices))   
for item in devices:     
    print(item)

#import win32com.client
#wmi = win32com.client.GetObject("winmgmts:")
#for serial in wmi.InstancesOf("Win32_SerialPort"):
      # print (serial.Name, serial.Description)
       
       
app = QtGui.QApplication(sys.argv)
    

class Constants(object):
    #class for storing Gui constants & user input
    #all static rather than object elements
    
    #Varible to store com port that is being logged
    comPort="select com port"

class ToolBar(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self)

        self.menu = self.menuBar()
        #self.menu.installEventFilter(self)
        self.coms = QMenu('COM Selection', self)
        self.group = QActionGroup(self.coms)
        self.checkSerial = serial.tools.list_ports.comports()

        self.populate()

        for text in self.texts:
            action = QAction(text, self.coms, checkable=True, checked=text==self.texts[0])
            self.coms.addAction(action)
            self.group.addAction(action)
        self.group.setExclusive(True)
        self.group.triggered.connect(self.onTriggered)
        self.menu.addMenu(self.coms)
    
    def populate(self):
        self.texts = [p.device for p in self.checkSerial]
        self.checkSerial.clear()
        
        return False
        
    def onTriggered(self, action):
        print(action.text())
        Constants.comPort = action.text()

class serialPlot:

    def __init__(self, serialPort = 'COM', serialBaud = 9600):
        self.port = serialPort
        self.baud = serialBaud

        self.isRun = True
        self.isReceiving = False
        self.thread = None

        #creating text file for data save
        self.file = open("data.txt","w+")
        header = "elapsed time milliseconds, rpm, L power watts, R power watts, \n"
        self.file.write(str(header))
        self.file.close()

        print('Trying to connect to: ' + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
        try:
            self.serialConnection = serial.Serial(serialPort, serialBaud, timeout=4)
            print('Connected to ' + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
        except:
            print("Failed to connect with " + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
            
        #timer for logging
        self.elaspedTime = QtCore.QTime()
        self.elaspedTime.start()
        TimeAxisItem.startTime = self.elaspedTime

    def readSerialStart(self):
        if self.thread == None:
            self.thread = threading.Thread(target=self.backgroundThread)
            self.thread.start()
            # Block till we start receiving values
            while self.isReceiving != True:
                time.sleep(0.1)

    def dataSave(self):

        self.file = open("data.txt", "a+")
        self.file.write(self.valueRead.replace("\r\n","\n"))
        self.file.close()


    def backgroundThread(self):    # retrieve data
        time.sleep(1.0)  # give some buffer time for retrieving data
        self.serialConnection.reset_input_buffer()
        while (self.isRun):
            #self.serialConnection.readinto(self.rawData)
            self.rawData = self.serialConnection.readline()
            self.isReceiving = True
            #print(self.rawData)
            self.valueRead  = self.rawData.decode(encoding='UTF-8',errors='ignore')
            self.dataArray = self.valueRead.split('\r\n')
            self.data = self.dataArray[0].split(',')

            try:
                
                dataCollect.time.append(self.elaspedTime.elapsed())
                dataCollect.rpm.append(float(self.data[1]))
                dataCollect.L_power.append(float(self.data[2]))
                dataCollect.R_power.append(float(self.data[3]))

            except ValueError and IndexError:
                dataCollect.time.append(0)
                dataCollect.rpm.append(0)
                dataCollect.L_power.append(0)
                dataCollect.R_power.append(0)
                
            self.dataSave()#save the data

    def close(self):
        self.isRun = False
        self.thread.join()
        self.serialConnection.close()
        print('Disconnected...')
        

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self, parent=None)
        
        self.Tool_Bar = ToolBar(self)
        self.setMenuWidget(self.Tool_Bar)
        
        self.central_widget = QtGui.QStackedWidget()
        self.setCentralWidget(self.central_widget)
        
        self.login_widget = LoginWidget(self)

        self.login_widget.startStop.clicked.connect(self.changeState)
        self.login_widget.startStop.clicked.connect(self.plotter)
        
        self.login_widget.Close.clicked.connect(self.closeGui)
        
        self.login_widget.saveData.clicked.connect(self.saveData)
        
        self.central_widget.addWidget(self.login_widget)
        
        self.log=False


        
        #creating text file for data save
        self.file = open("data.txt","w+")
        header = "elapsed time milliseconds, rpm, L power watts, R power watts, \n"
        self.file.write(str(header))
        self.file.close()
        
        #data
        for i in range(1000):
            dataCollect.time.append(0)
            dataCollect.rpm.append(0)
            dataCollect.L_power.append(0)
            dataCollect.R_power.append(0)
        
        
        self.login_widget.w1.slider.valueChanged.connect(self.update_plot)


    def update_plot(self):
        
        value = dataCollect.value
        dataCollect.time = queue.deque(maxlen = value)
        dataCollect.rpm = queue.deque(maxlen = value)
        dataCollect.L_power = queue.deque(maxlen = value)
        dataCollect.R_power = queue.deque(maxlen = value)
        
    def closeGui(self):
        QtGui.QApplication(sys.argv).exit()
                
                
    def closeCom(self):
        if self.log==False:
            self.serialPort.close()
            print('stopped logging')  
            self.timer.stop()
            self.saver.stop()
            self.file.close()
        if self.log==True:

            self.portName = 'COM4'
            self.portName = Constants.comPort
            baudRate = 9600
            self.serialPort = serialPlot(self.portName, baudRate)   # initializes all required variables
            self.serialPort.readSerialStart()   #data starting to save


    def changeState(self):
        self.log = not self.log
        self.closeCom()
        
        
            
    def Display(self):

        #update labels
        rpm = str(sum([dataCollect.rpm[i] for i in range(80)])/80)
        self.login_widget.LiveValueRPM.setText(rpm)
        L = str(sum([dataCollect.L_power[i] for i in range(80)])/80)
        self.login_widget.LiveValueLeft.setText(L)
        R = str(sum([dataCollect.R_power[i] for i in range(80)])/80)
        self.login_widget.LiveValueRight.setText(R)
        
        QtGui.QApplication.processEvents()

        
    def updater(self):
        

        self.curve0.setData(dataCollect.time, dataCollect.L_power, clear=True)
        self.curve1.setData(dataCollect.time, dataCollect.R_power, clear=True)
        self.curve2.setData(dataCollect.time, dataCollect.rpm, clear=True)            

        QtGui.QApplication.processEvents()
            
    def plotter(self):
        
        #self.login_widget.plot.getPlotItem().addLegend()
        
        self.curve0 = self.login_widget.plot.getPlotItem().plot(clear=True,pen='b', name='rpm')
        self.curve1 = self.login_widget.plot.getPlotItem().plot(pen='g', name='Left Power')
        self.curve2 = self.login_widget.plot.getPlotItem().plot(pen='r', name='Right Power')
        
        self.login_widget.plot.getPlotItem().setLabel('bottom', 'Time [hrs,mins,secs]')
        self.login_widget.plot.getPlotItem().setLabel('left', "Power [W] or [rpm]")
        
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.updater)
        self.timer.start(10)

        self.saver = QtCore.QTimer()
        self.saver.timeout.connect(self.Display)
        self.saver.start(1000)


    def saveData(self):
        

        file_name = QtGui.QFileDialog.getSaveFileName(self,"Save file", "default name here", ".txt;;.csv" )
        saveLocation = file_name[0]
        fileExtension = file_name[1]

        #The data saved during logging as txt file
        data=[]
        with open("data.txt", "r") as f:
            for line in f:
                print(len(line))
                if len(line)  < 64:
                    data.append(line.rstrip())
            
        #saving the data in the preferred form and file location for user
        with open(''.join([saveLocation,fileExtension]), "w", encoding='utf-8') as f:
            for s in data:
                f.write(s)
                f.write("\n")


        
class TimeAxisItem(pg.AxisItem):
    """Internal timestamp for x-axis"""
    startTime = QtCore.QTime().currentTime()
    
    def __init__(self, *args, **kwargs):
        super(TimeAxisItem, self).__init__(*args, **kwargs)

    def tickStrings(self, values, scale, spacing):
        """Function overloading the weak default version to provide timestamp"""
        
        return [TimeAxisItem.startTime.addMSecs(value).toString('hh:mm:ss') for value in values]         
        #return [QtCore.QTime().currentTime().toString('hh:mm:ss') for value in values]  

class dataCollect(object):
    
    time = queue.deque(maxlen = 1000)
    rpm = queue.deque(maxlen = 1000)
    L_power = queue.deque(maxlen = 1000)
    R_power = queue.deque(maxlen = 1000)
    
    
class Slider(QWidget):
    def __init__(self, parent=None):
        super().__init__()
        hbox = QHBoxLayout()
        self.slider = QSlider()
        self.slider.setOrientation(Qt.Horizontal)
        self.slider.setTickPosition(QSlider.TicksBelow)
        self.slider.setTickInterval(100)
        self.slider.setMinimum(0)
        self.slider.setMaximum(1000)
        self.slider.setValue(500)
        self.slider.valueChanged.connect(self.changedValue)
        #self.label = QLabel("50")
        #self.label.setFont(QtGui.QFont("Sanserif", 15))
        hbox.addWidget(self.slider)
        #hbox.addWidget(self.label)
        self.setLayout(hbox)
        #self.show()


    def changedValue(self):
        size = self.slider.value()
        #self.label.setText(str(size)) 
        dataCollect.value = size#number of data points to set graph
        #print(size)
               

class LoginWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self)
        
        hbox = QtGui.QGridLayout(self)
        
        L0_layout = QtGui.QGridLayout()

        L1_layout = QtGui.QGridLayout()
        R_layout = QtGui.QGridLayout()

        L0_widgets = QtGui.QFrame()
        L0_widgets.setFrameShape(QtGui.QFrame.StyledPanel)
        L0_widgets.setLayout(L0_layout)

        L1_widgets = QtGui.QFrame()
        L1_widgets.setFrameShape(QtGui.QFrame.StyledPanel)
        L1_widgets.setLayout(L1_layout)
        
        self.startStop = QtGui.QPushButton('Start / Stop Plotting')
        L0_layout.addWidget(self.startStop, 1, 0)
        
        self.Close = QtGui.QPushButton('Close')
        L0_layout.addWidget(self.Close, 2, 0)
        
        self.saveData = QtGui.QPushButton('Save Data')
        L0_layout.addWidget(self.saveData, 3, 0)
        
        #create space
        self.Blank = QtGui.QLabel()
        L0_layout.addWidget(self.Blank, 4, 0)
        
        #set bold text for vaule display
        myFont=QtGui.QFont()
        myFont.setBold(True)
        
        #live readout left power
        self.LeftPower = QtGui.QLabel('Average Left Power (W)')
        self.LeftPower.setFont(myFont)
        self.LeftPower.setStyleSheet("font: 15pt")
        self.LeftPower.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        L0_layout.addWidget(self.LeftPower, 5, 0)
        
        self.LiveValueLeft = QtGui.QLabel("0")
        self.LiveValueLeft.setFont(myFont)
        #self.LiveValueLeft.setStyleSheet("font: 15pt")
        self.LiveValueLeft.setStyleSheet("QLabel{color: green; font: 15pt}")
        self.LiveValueLeft.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        L0_layout.addWidget(self.LiveValueLeft, 6, 0)
        
        #live readout right power
        self.RightPower = QtGui.QLabel('Average Right Power (W)')
        self.RightPower.setFont(myFont)
        self.RightPower.setStyleSheet("font: 15pt")
        self.RightPower.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        L0_layout.addWidget(self.RightPower, 7, 0)
        
        self.LiveValueRight = QtGui.QLabel("0")
        self.LiveValueRight.setFont(myFont)
        #self.LiveValueRight.setStyleSheet("font: 15pt")
        self.LiveValueRight.setStyleSheet("QLabel{color: red; font: 15pt}")
        self.LiveValueRight.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        L0_layout.addWidget(self.LiveValueRight, 8, 0)
        
        
        #live readout rpm
        self.RPM = QtGui.QLabel('Average rpm')
        self.RPM.setFont(myFont)
        self.RPM.setStyleSheet("font: 15pt")
        self.RPM.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        L0_layout.addWidget(self.RPM, 9, 0)
        
        self.LiveValueRPM = QtGui.QLabel("0")
        self.LiveValueRPM.setFont(myFont)
        #self.LiveValueRPM.setStyleSheet("font: 15pt")
        self.LiveValueRPM.setStyleSheet("QLabel{color: blue; font: 15pt}")
        self.LiveValueRPM.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        L0_layout.addWidget(self.LiveValueRPM, 10, 0)
        
        self.AxisAdjust = QtGui.QLabel('Move Silder to Adjust X Axis')
        self.AxisAdjust.setFont(myFont)
        self.AxisAdjust.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        L0_layout.addWidget(self.AxisAdjust, 11, 0)
        
        #self.w1 = CustomWidget()
        self.w1 = Slider()
        L0_layout.addWidget(self.w1, 12, 0)
        
        self.plot = pg.PlotWidget()
        self.plot = pg.PlotWidget(axisItems={'bottom': TimeAxisItem(orientation='bottom')})
        self.plot.plotItem.setMouseEnabled(x=False, y=False)
        self.plot.showGrid(x=True,y=True,alpha=1)
        self.plot.addLegend()
        
        splitter1 = QtGui.QSplitter(QtCore.Qt.Horizontal)
        splitter1.addWidget(L0_widgets)

        splitter3 = QtGui.QSplitter(QtCore.Qt.Horizontal)
        splitter3.addWidget(splitter1)
        splitter3.addWidget(self.plot)
        hbox.addWidget(splitter3)
        
        self.setGeometry(300, 300, 300, 200)
        self.setLayout(hbox)

        
if __name__ == '__main__':
    window = MainWindow()
    window.show()
    app.exec_()
导入序列号
导入serial.tools.list_端口
导入时间
导入线程
从PyQt5导入QtCore、QtGui、QtWidgets
从PyQt5.QtCore导入Qt
从PyQt5.qtwidts导入QApplication、QHBoxLayout、QLabel、QSizePolicy、QSlider、QSpacerItem、\
QVBoxLayout,QWidget
从PyQt5.QtWidgets导入QAction、QActionGroup、QMenu、QApplication
将pyqtgraph作为pg导入
导入系统
导入队列
导入蓝牙
设备=蓝牙。发现设备(查找名称=真)
打印(类型(设备))
打印(“找到的设备:%s”%len(设备))
对于设备中的项目:
打印(项目)
#导入win32com.client
#wmi=win32com.client.GetObject(“winmgmts:”)
#对于wmi.InstancesOf(“Win32_SerialPort”)中的串行端口:
#打印(序列号名称、序列号说明)
app=QtGui.QApplication(sys.argv)
类常量(对象):
#用于存储Gui常量和用户输入的类
#所有静态元素而非对象元素
#用于存储正在记录的com端口的变量
comPort=“选择com端口”
类工具栏(QtGui.QMainWindow):
def uuu init uuu(self,parent=None):
QtGui.QMainWindow.\uuuuu init\uuuuuuu(self)
self.menu=self.menuBar()
#self.menu.installEventFilter(self)
self.coms=QMenu('COM Selection',self)
self.group=QActionGroup(self.coms)
self.checkSerial=serial.tools.list_ports.comports()
self.populate()
对于self.text中的文本:
action=QAction(text,self.com,checkable=True,checked=text==self.text[0])
self.com.addAction(动作)
self.group.addAction(操作)
self.group.setExclusive(True)
self.group.triggered.connect(self.onTriggered)
self.menu.addMenu(self.coms)
def填充(自我):
self.text=[self.checkSerial中p的p.device]
self.checkSerial.clear()
返回错误
定义未触发(自我、动作):
打印(action.text())
Constants.comPort=action.text()
类序列图:
def uuu init uuuuu(self,serialPort='COM',serialBaud=9600):
self.port=serialPort
self.baud=串行波特率
self.isRun=True
self.isReceiving=False
self.thread=None
#创建用于数据保存的文本文件
self.file=open(“data.txt”,“w+”)
header=“已用时间毫秒,rpm,L功率瓦特,R功率瓦特,\n”
self.file.write(str(头))
self.file.close()文件
打印('尝试连接到:'+str(串行端口)+'在'+str(串行波特率)+'波特率'.)
尝试:
self.serialConnection=serial.serial(serialPort,serialBaud,timeout=4)
打印('连接到'+str(串行端口)+'在'+str(串行波特率)+'波特率'.)
除:
打印(“无法连接到“+str(串行端口)+”处的“+str(串行波特率)+”波特率”。)
#日志计时器
self.elaspedTime=QtCore.QTime()
self.elaspedTime.start()
TimeAxisItem.startTime=self.elaspedTime
def readSerialStart(自):
如果self.thread==无:
self.thread=threading.thread(target=self.backgroundThread)
self.thread.start()
#阻止,直到我们开始接收值
在自我接受时!=正确:
睡眠时间(0.1)
def数据保存(自):
self.file=open(“data.txt”,“a+”)
self.file.write(self.valueRead.replace(“\r\n”,“\n”))
self.file.close()文件
def backgroundThread(self):#检索数据
time.sleep(1.0)#为检索数据提供一些缓冲时间
self.serialConnection.reset\u input\u buffer()
而(self.isRun):
#self.serialConnection.readinto(self.rawData)
self.rawData=self.serialConnection.readline()
self.isReceiving=True
#打印(self.rawData)
self.valueRead=self.rawData.decode(encoding='UTF-8',errors='ignore')
self.dataArray=self.valueRead.split('\r\n')
self.data=self.dataArray[0]。拆分(',')
尝试:
dataCollect.time.append(self.elaspedTime.appead())
dataCollect.rpm.append(float(self.data[1]))
dataCollect.L_power.append(float(self.data[2]))
dataCollect.R\u power.append(float(self.data[3]))
除ValueError和Indexer之外:
dataCollect.time.append(0)
dataCollect.rpm.append(0)
dataCollect.L_power.append(0)
dataCollect.R\u power.append(0)
self.dataSave()#保存数据
def关闭(自我):
self.isRun=False
self.thread.join()
self.serialConnection.close()
打印('断开连接…')
类MainW