用hc-05d模塊實(shí)現(xiàn)usb轉(zhuǎn)ttl模塊功能
hc-05d模塊特點(diǎn)
hc-05d是支持spp和ble,固件是主從一體的,默認(rèn)情況下進(jìn)入透?jìng)髂J?br>
默認(rèn)波特率是9600
bleak和pybluez是Python中兩個(gè)不同的藍(lán)牙通信庫(kù)
pybluez:僅支持經(jīng)典藍(lán)牙(BR/EDR),如藍(lán)牙2.x/3.x/4.x
bleak:專(zhuān)為低功耗藍(lán)牙(BLE/Bluetooth 4.0+)設(shè)計(jì)
目標(biāo)板波特率是115200,故將hc05d模塊波特率改為115200
用python實(shí)現(xiàn)的
代碼
點(diǎn)擊查看代碼
import sys
import asyncio
from bleak import BleakScanner, BleakClient
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,
QPushButton, QListWidget, QTextEdit, QLabel,
QWidget, QLineEdit, QComboBox, QFrame)
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QFont
class BLEClientApp(QMainWindow):
def __init__(self):
super().__init__()
self.client = None
self.write_char = None
self.setWindowTitle("BLE Client")
self.setGeometry(100, 100, 800, 600)
self.setup_ui()
def setup_ui(self):
central_widget = QWidget()
main_layout = QVBoxLayout()
# 控制面板
control_panel = QHBoxLayout()
self.scan_btn = QPushButton("Scan")
self.connect_btn = QPushButton("Connect")
self.disconnect_btn = QPushButton("Disconnect")
control_panel.addWidget(self.scan_btn)
control_panel.addWidget(self.connect_btn)
control_panel.addWidget(self.disconnect_btn)
# 發(fā)送面板
send_panel = QHBoxLayout()
self.send_input = QLineEdit()
self.encoding_combo = QComboBox()
self.encoding_combo.addItems(["utf-8", "ascii", "latin1"])
self.send_btn = QPushButton("Send")
send_panel.addWidget(self.send_input, 4)
send_panel.addWidget(self.encoding_combo, 1)
send_panel.addWidget(self.send_btn, 1)
# 設(shè)備列表和數(shù)據(jù)展示
self.device_list = QListWidget()
# 接收數(shù)據(jù)區(qū)域
received_data_layout = QVBoxLayout()
received_data_layout.addWidget(QLabel("Received Data:"))
self.data_display = QTextEdit()
self.data_display.setReadOnly(True)
# 創(chuàng)建清除按鈕區(qū)域
clear_button_layout = QHBoxLayout()
clear_button_layout.addStretch() # 添加彈性空間使按鈕靠右
self.clear_btn = QPushButton("Clear")
self.clear_btn.setFixedWidth(80) # 設(shè)置固定寬度
clear_button_layout.addWidget(self.clear_btn)
# 將數(shù)據(jù)展示區(qū)域和清除按鈕添加到接收數(shù)據(jù)布局
received_data_layout.addWidget(self.data_display)
received_data_layout.addLayout(clear_button_layout)
self.status_label = QLabel("Status: Disconnected")
# 布局組裝
main_layout.addLayout(control_panel)
main_layout.addWidget(QLabel("Devices:"))
main_layout.addWidget(self.device_list)
main_layout.addLayout(received_data_layout)
main_layout.addWidget(QLabel("Send Data:"))
main_layout.addLayout(send_panel)
main_layout.addWidget(self.status_label)
central_widget.setLayout(main_layout)
self.setCentralWidget(central_widget)
# 事件綁定
self.scan_btn.clicked.connect(self.start_scan)
self.connect_btn.clicked.connect(self.connect_to_device)
self.disconnect_btn.clicked.connect(self.disconnect_device)
self.send_btn.clicked.connect(self.send_data)
self.clear_btn.clicked.connect(self.clear_data_display) # 綁定清除按鈕
# 異步事件循環(huán)
self.loop = asyncio.get_event_loop()
self.timer = QTimer()
self.timer.timeout.connect(self.process_events)
self.timer.start(100)
def process_events(self):
self.loop.stop()
self.loop.run_forever()
def log_message(self, message):
self.data_display.append(message)
def clear_data_display(self):
"""清空接收數(shù)據(jù)區(qū)域"""
self.data_display.clear()
def start_scan(self):
self.device_list.clear()
self.log_message("Scanning...")
async def scan():
devices = await BleakScanner.discover()
for d in devices:
self.device_list.addItem(f"{d.name or 'Unknown'} - {d.address}")
self.log_message(f"Found {len(devices)} devices")
asyncio.run_coroutine_threadsafe(scan(), self.loop)
def connect_to_device(self):
selected = self.device_list.currentItem()
if not selected:
self.log_message("No device selected")
return
device_address = selected.text().split(" - ")[-1]
self.log_message(f"Connecting to {device_address}...")
async def connect():
try:
self.client = BleakClient(device_address)
await self.client.connect()
self.status_label.setText(f"Connected: {device_address}")
# 查找特征
for service in self.client.services:
for char in service.characteristics:
if "write" in char.properties:
self.write_char = char
max_length = char.max_write_without_response if hasattr(char, 'max_write_without_response') else None
self.log_message(f"Writable: {char.uuid} (Max length: {max_length})")
if "notify" in char.properties:
await self.client.start_notify(
char.uuid,
lambda s, d: self.log_message(
f"{bytes(d).decode('utf-8', errors='replace')}"
)
)
self.log_message(f"Characteristic properties: {char.properties}")
self.log_message(f"Descriptors: {char.descriptors}")
except Exception as e:
self.log_message(f"Connection error: {str(e)}")
asyncio.run_coroutine_threadsafe(connect(), self.loop)
def disconnect_device(self):
if not self.client:
return
async def disconnect():
await self.client.disconnect()
self.status_label.setText("Disconnected")
self.client = None
self.write_char = None
asyncio.run_coroutine_threadsafe(disconnect(), self.loop)
def send_data(self):
if not self.client or not self.write_char:
self.log_message("Not ready for sending")
return
text = self.send_input.text()
if not text:
return
encoding = self.encoding_combo.currentText()
async def send():
try:
data = text.encode(encoding, errors='replace')
# 獲取特征值的最大寫(xiě)入長(zhǎng)度
max_length = self.write_char.max_write_without_response if hasattr(self.write_char, 'max_write_without_response') else 20
if max_length is None:
max_length = 20 # 默認(rèn)值
self.log_message(f"Data length: {len(data)}, Max write length: {max_length}")
# 如果數(shù)據(jù)長(zhǎng)度小于等于最大長(zhǎng)度,直接發(fā)送
if len(data) <= max_length:
await self.client.write_gatt_char(self.write_char.uuid, data)
self.log_message(f"TX: {text} ({encoding})")
else:
# 分塊發(fā)送數(shù)據(jù)
for i in range(0, len(data), max_length):
chunk = data[i:i+max_length]
await self.client.write_gatt_char(self.write_char.uuid, chunk)
self.log_message(f"Sent chunk {i//max_length + 1}: {chunk.hex()}")
self.log_message(f"TX: {text} ({encoding}) [Sent in {((len(data)-1)//max_length)+1} chunks]")
except Exception as e:
self.log_message(f"Send error: {str(e)}")
#self.log_message("Send")
asyncio.run_coroutine_threadsafe(send(), self.loop)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = BLEClientApp()
window.show()
sys.exit(app.exec_())
vs code軟件

運(yùn)行示例

打包成windows下exe文件
搜不到藍(lán)牙,還不知道為什么

如果,感到此時(shí)的自己很辛苦,那告訴自己:容易走的都是下坡路。堅(jiān)持住,因?yàn)槟阏谧呱掀侣罚哌^(guò)去,你就一定會(huì)有進(jìn)步。如果,你正在埋怨命運(yùn)不眷顧,開(kāi)導(dǎo)自己:命,是失敗者的借口;運(yùn),是成功者的謙詞。命運(yùn)從來(lái)都是掌握在自己的手中,埋怨,只是一種懦弱的表現(xiàn);努力,才是人生的態(tài)度。

浙公網(wǎng)安備 33010602011771號(hào)