这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » 【STM32WBA55CG开发板】DIY蓝牙控制取暖器

共1条 1/1 1 跳转至

【STM32WBA55CG开发板】DIY蓝牙控制取暖器

菜鸟
2024-12-14 18:29:37     打赏

参与《NUCLEO-WBA55CG开发板》试用中,我是准备DIY一台蓝牙远程控制的取暖器,蓝牙控制可以使用手机的微信小程,也可以使用web控件,我选择了python的bleak库进行设计,结合PyQt5实现GUI的桌面程序。

【界面设计】

使用QtDesigner进行设计,目前想到的功能,主要是展示从WBA55CG上采集到的温湿度进行展示。执行温度的设计,开关机,还有可以扫描到自己的设备,可指定设备进行连接。界面如下:

image.png

【代码设计】

1、首先使用PyUIC把ui文件转化为py文件,转换后实现界面.py文件:

view plaincopy to clipboardprint?

  1. # -*- coding: utf-8 -*-  

  2.   

  3. # Form implementation generated from reading ui file 'wbg55.ui'  

  4. #  

  5. # Created by: PyQt5 UI code generator 5.15.9  

  6. #  

  7. # WARNING: Any manual changes made to this file will be lost when pyuic5 is  

  8. # run again.  Do not edit this file unless you know what you are doing.  

  9.   

  10.   

  11. from PyQt5 import QtCore, QtGui, QtWidgets  

  12.   

  13.   

  14. class Ui_MainWindow(object):  

  15.     def setupUi(self, MainWindow):  

  16.         MainWindow.setObjectName("MainWindow")  

  17.         MainWindow.resize(800, 600)  

  18.         self.centralwidget = QtWidgets.QWidget(MainWindow)  

  19.         self.centralwidget.setObjectName("centralwidget")  

  20.         self.bntConnect = QtWidgets.QPushButton(self.centralwidget)  

  21.         self.bntConnect.setGeometry(QtCore.QRect(50, 270, 81, 41))  

  22.         font = QtGui.QFont()  

  23.         font.setPointSize(16)  

  24.         self.bntConnect.setFont(font)  

  25.         self.bntConnect.setObjectName("bntConnect")  

  26.         self.device_list = QtWidgets.QListWidget(self.centralwidget)  

  27.         self.device_list.setGeometry(QtCore.QRect(50, 70, 311, 191))  

  28.         self.device_list.setObjectName("device_list")  

  29.         self.lcdNumberTemp = QtWidgets.QLCDNumber(self.centralwidget)  

  30.         self.lcdNumberTemp.setGeometry(QtCore.QRect(380, 80, 91, 71))  

  31.         font = QtGui.QFont()  

  32.         font.setPointSize(36)  

  33.         self.lcdNumberTemp.setFont(font)  

  34.         self.lcdNumberTemp.setObjectName("lcdNumberTemp")  

  35.         self.labelLinkState = QtWidgets.QLabel(self.centralwidget)  

  36.         self.labelLinkState.setGeometry(QtCore.QRect(40, 370, 291, 31))  

  37.         self.labelLinkState.setObjectName("labelLinkState")  

  38.         self.pushButtonConnect = QtWidgets.QPushButton(self.centralwidget)  

  39.         self.pushButtonConnect.setGeometry(QtCore.QRect(50, 40, 311, 31))  

  40.         font = QtGui.QFont()  

  41.         font.setPointSize(12)  

  42.         self.pushButtonConnect.setFont(font)  

  43.         self.pushButtonConnect.setObjectName("pushButtonConnect")  

  44.         self.label_2 = QtWidgets.QLabel(self.centralwidget)  

  45.         self.label_2.setGeometry(QtCore.QRect(381, 40, 91, 31))  

  46.         font = QtGui.QFont()  

  47.         font.setPointSize(16)  

  48.         self.label_2.setFont(font)  

  49.         self.label_2.setObjectName("label_2")  

  50.         self.label_3 = QtWidgets.QLabel(self.centralwidget)  

  51.         self.label_3.setGeometry(QtCore.QRect(530, 40, 81, 31))  

  52.         font = QtGui.QFont()  

  53.         font.setPointSize(16)  

  54.         self.label_3.setFont(font)  

  55.         self.label_3.setObjectName("label_3")  

  56.         self.bntADD = QtWidgets.QPushButton(self.centralwidget)  

  57.         self.bntADD.setGeometry(QtCore.QRect(530, 230, 75, 41))  

  58.         font = QtGui.QFont()  

  59.         font.setPointSize(16)  

  60.         self.bntADD.setFont(font)  

  61.         self.bntADD.setObjectName("bntADD")  

  62.         self.bntDec = QtWidgets.QPushButton(self.centralwidget)  

  63.         self.bntDec.setGeometry(QtCore.QRect(530, 290, 75, 41))  

  64.         font = QtGui.QFont()  

  65.         font.setPointSize(16)  

  66.         self.bntDec.setFont(font)  

  67.         self.bntDec.setObjectName("bntDec")  

  68.         self.lcdNumberSet = QtWidgets.QLCDNumber(self.centralwidget)  

  69.         self.lcdNumberSet.setGeometry(QtCore.QRect(400, 240, 121, 91))  

  70.         font = QtGui.QFont()  

  71.         font.setPointSize(36)  

  72.         self.lcdNumberSet.setFont(font)  

  73.         self.lcdNumberSet.setObjectName("lcdNumberSet")  

  74.         self.label_4 = QtWidgets.QLabel(self.centralwidget)  

  75.         self.label_4.setGeometry(QtCore.QRect(410, 180, 151, 31))  

  76.         font = QtGui.QFont()  

  77.         font.setPointSize(16)  

  78.         self.label_4.setFont(font)  

  79.         self.label_4.setObjectName("label_4")  

  80.         self.bntOnOFF = QtWidgets.QPushButton(self.centralwidget)  

  81.         self.bntOnOFF.setGeometry(QtCore.QRect(190, 280, 91, 41))  

  82.         font = QtGui.QFont()  

  83.         font.setPointSize(16)  

  84.         self.bntOnOFF.setFont(font)  

  85.         self.bntOnOFF.setObjectName("bntOnOFF")  

  86.         self.labelData = QtWidgets.QLabel(self.centralwidget)  

  87.         self.labelData.setGeometry(QtCore.QRect(40, 320, 401, 31))  

  88.         self.labelData.setObjectName("labelData")  

  89.         self.lcdNumberHum = QtWidgets.QLCDNumber(self.centralwidget)  

  90.         self.lcdNumberHum.setGeometry(QtCore.QRect(520, 80, 81, 71))  

  91.         font = QtGui.QFont()  

  92.         font.setPointSize(36)  

  93.         self.lcdNumberHum.setFont(font)  

  94.         self.lcdNumberHum.setObjectName("lcdNumberHum")  

  95.         MainWindow.setCentralWidget(self.centralwidget)  

  96.         self.menubar = QtWidgets.QMenuBar(MainWindow)  

  97.         self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))  

  98.         self.menubar.setObjectName("menubar")  

  99.         MainWindow.setMenuBar(self.menubar)  

  100.         self.statusbar = QtWidgets.QStatusBar(MainWindow)  

  101.         self.statusbar.setObjectName("statusbar")  

  102.         MainWindow.setStatusBar(self.statusbar)  

  103.   

  104.         self.retranslateUi(MainWindow)  

  105.         QtCore.QMetaObject.connectSlotsByName(MainWindow)  

  106.   

  107.     def retranslateUi(self, MainWindow):  

  108.         _translate = QtCore.QCoreApplication.translate  

  109.         MainWindow.setWindowTitle(_translate("MainWindow""MainWindow"))  

  110.         self.bntConnect.setText(_translate("MainWindow""连接"))  

  111.         self.labelLinkState.setText(_translate("MainWindow""连接状态:"))  

  112.         self.pushButtonConnect.setText(_translate("MainWindow""扫描蓝牙设备"))  

  113.         self.label_2.setText(_translate("MainWindow""室温"))  

  114.         self.label_3.setText(_translate("MainWindow""湿度"))  

  115.         self.bntADD.setText(_translate("MainWindow""增大"))  

  116.         self.bntDec.setText(_translate("MainWindow""减小"))  

  117.         self.label_4.setText(_translate("MainWindow""温度设定"))  

  118.         self.bntOnOFF.setText(_translate("MainWindow""关机"))  

  119.         self.labelData.setText(_translate("MainWindow""接收到数据:"))  

2、在main.py中,我们首先要安排PyQt5、并安装bleak蓝牙库:

image.png


具体的安装方法,大家可以自行百度。

3、主要代码介绍:

第一是蓝牙扫描,我当创建了一个类BluetoothScanner用于扫描蓝牙设备,扫描结束后,使用pyqtSignal异步发送给主函数,用于界面显示。

view plaincopy to clipboardprint?

  1. class BluetoothScanner(QThread):  

  2.     device_found_signal = pyqtSignal(dict)  

  3.   

  4.     def __init__(self, parent=None):  

  5.         super().__init__(parent)  

  6.   

  7.     def run(self):  

  8.         asyncio.run(self.scan())  

  9.   

  10.     async def scan(self):  

  11.         devices = await BleakScanner.discover()  

  12.         for device in devices:  

  13.             self.device_found_signal.emit({'name': device.name, 'address': device.address})  

  14.             print({'name': device.name, 'address': device.address})  

第二是,处理与蓝牙设备的连接、数据接收与发送:其代码如下:

view plaincopy to clipboardprint?

  1. class BluetoothReceiver(QThread):  

  2.     data_received_signal = pyqtSignal(dict)  

  3.     connection_status_signal = pyqtSignal(str)  

  4.     def __init__(self, device_address):  

  5.         super().__init__()  

  6.         print("init")  

  7.         self.device_address = device_address  

  8.         self.client = None  

  9.         self.receive_task = None  

  10.         self.connected = False  

  11.         print("init end address:" + str(self.device_address))  

  12.   

  13.     def run(self):  

  14.         print("recive run")  

  15.         asyncio.run(self.connect())  

  16.   

  17.   

  18.     async def connect(self):  

  19.         try:  

  20.             # 基于MAC地址查找设备  

  21.             device = await BleakScanner.find_device_by_address(  

  22.                 self.device_address, cb=dict(use_bdaddr=False)  # use_bdaddr判断是否是MOC系统  

  23.             )  

  24.             if device is None:  

  25.                 print("could not find device with address '%s'",self.device_address)  

  26.                 return  

  27.                 # 事件定义  

  28.   

  29.             self.client = BleakClient(self.device_address,disconnected_callback=self.disconnect)  

  30.             await self.client.connect()  

  31.             await self.client.start_notify(par_notification_characteristic,self.receive_data)  

  32.             self.connected = True  

  33.             self.connection_status_signal.emit("已连接")  

  34.             while True:  

  35.                 await self.client.write_gatt_char(par_write_characteristic, send_str)  

  36.                 await asyncio.sleep(1.0)  # 每休眠1秒发送一次  

  37.                 await self.client.write_gatt_char(par_write_characteristic, send_str1)  

  38.                 await asyncio.sleep(1.0)  

  39.             # self.receive_task = asyncio.create_task(self.receive_data())  

  40.         except Exception as e:  

  41.             self.connection_status_signal.emit(f"连接失败: {e}")  

  42.     async def receive_data(self,characteristic, data):  

  43.         try:  

  44.             Temp = data[0]*10 + data[1]  

  45.             Hum = data[2]*10 + data[3]  

  46.             self.data_received_signal.emit({'Temp': Temp, 'Hum': Hum})  

  47.         except Exception as e:  

  48.             self.connection_status_signal.emit(f"接收数据出错: {e}")  

  49.   

  50.     async def disconnect(self):  

  51.         if self.client:  

  52.             await self.client.disconnect()  

  53.             self.connected = False  

  54.             self.connection_status_signal.emit("已断开连接")  

在此个类中,首先基于Mac地址来查找设备,查找到后使用start_notfy来创建异步连接,实现对服务的数据异步接收的recive_data回调函数的注册,如果接收到数据进行数据组装通过emit发送给GUI进行数据展示。

同时每隔1秒向服务端发送LED的开关指令,确保长连接。

3、GUI主程序:

view plaincopy to clipboardprint?

  1. class MyMainForm(QMainWindow, Ui_MainWindow):  

  2.   

  3.     def __init__(self, parent=None):  

  4.         super(MyMainForm, self).__init__(parent)  

  5.         self.setupUi(self)  

  6.         self.pushButtonConnect.clicked.connect(self.start_async_task)  

  7.         self.bntConnect.clicked.connect(self.connect_device)  

  8.   

  9.     def start_async_task(self):  

  10.         self.device_list.clear()  

  11.         selected_item = self.device_list.currentItem()  

  12.         self.labelLinkState.setText("正在执行扫描...")  

  13.         async_thread = BluetoothScanner(self)  

  14.         async_thread.finished.connect(self.async_task_finished)  

  15.         async_thread.device_found_signal.connect(self.add_device_to_list)  

  16.         async_thread.start()  

  17.     def async_task_finished(self):  

  18.         self.labelLinkState.setText("扫描完成")  

  19.   

  20.     def add_device_to_list(self, device_info):  

  21.         self.device_list.addItem(f"{device_info['name']} - {device_info['address']}")  

  22.   

  23.     def connect_device(self):  

  24.         selected_item = self.device_list.currentItem()  

  25.         if selected_item:  

  26.             device_address = selected_item.text().split(' - ')[1]  

  27.             self.thread1 = BluetoothReceiver(device_address)  

  28.             self.thread1.finished.connect(self.async_task_finished)  

  29.             self.thread1.connection_status_signal.connect(self.update_status_label)  

  30.             self.thread1.data_received_signal.connect(self.update_data_label)  

  31.             self.thread1.start()  

  32.             print(5)  

  33.   

  34.     def update_data_label(self, data):  

  35.         try:  

  36.             self.labelData.setText(f"接收数据: {data}")  

  37.             self.lcdNumberTemp.display(data['Temp'])  

  38.             self.lcdNumberHum.display(data['Hum'])  

  39.         except Exception as e:  

  40.             print("接收数据解释错误:", str(e))  

  41.     def update_status_label(self, status):  

  42.         self.labelLinkState.setText(f"状态: {status}")  

  43.   

  44.   

  45. if __name__ == "__main__":  

  46.     # 固定的,PyQt5程序都需要QApplication对象。sys.argv是命令行参数列表,确保程序可以双击运行  

  47.     app = QApplication(sys.argv)  

  48.     # 初始化  

  49.     myWin = MyMainForm()  

  50.     # 将窗口控件显示在屏幕上  

  51.     myWin.show()  

  52.     # 程序运行,sys.exit方法确保程序完整退出。  

  53.     sys.exit(app.exec_())  

在GUI类中,对应按键件事,创建对应的任务,并注册信号接收回调函数,对数据进行解析、展示。

【实现效果】

image.png

运行桌面程序后,首先点击扫描,我们的P2S服务会出现在列表框中,选中该行后按连接,在下面的状态提示中会提示连接,室温、湿度的LCD会实现显示开发板的温湿度,同时开发板也会接收到界面发出的指令,第隔一秒闪烁一次。

















关键词: STM32WBA55CG     开发    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]