之前做了一个具有经典蓝牙通讯功能的Windows上位机软件,在网上学习了相关博客以及参考了官方经典蓝牙例程之后,总结出了使用Qt建立经典蓝牙通讯的步骤,附带相关源码,作为分享
我使用的Qt
版本是5.15,使用的CMake
构建项目。
整体开发使用的IDE
是Qt Creator
,采用的方式是基于widgets
的ui
设计界面、C++写逻辑的方式。
编译使用的是Desktop Qt 5.15.2 MINGW 64-bit
CMake
配置经典蓝牙通讯需要用到Qt
的蓝牙模块,需要添加Bluetooth
模块:
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Widgets Bluetooth) #寻找Bluetooth模块
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets Bluetooth) #寻找Bluetooth模块
在add_executable
之后设置target_link_libraries
:
target_link_libraries(bluetooth_serial_host_computer PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Bluetooth #添加蓝牙
)
#include //本地设备
#include //设备发现
#include //蓝牙套接字
#include //蓝牙uuid
#include //服务发现
#include //服务信息
建立经典蓝牙通讯的基本步骤如下:
首先是在构造函数中的初始化操作:
//创建设备发现对象
m_deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
m_btLocalDevice = new QBluetoothLocalDevice(); //创建本地设备对象
m_btSocket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol); //蓝牙套接字
QBluetoothAddress adapterAddress = m_btLocalDevice->address(); //使用默认蓝牙适配器
m_serviceDiscoveryAgent = new QBluetoothServiceDiscoveryAgent(adapterAddress); //创建服务发现对象
//连接信号与槽(deviceDiscoveryAgent是代码中自己声明的对象,ui中没有,无法自动生成槽函数;需要自己写槽函数,自己连接)
/*-------------------- 初始化设备列表与服务列表 -----------------*/
//设置设备列表
QListWidgetItem* item = new QListWidgetItem();
//创建自定义窗口,放入到listwidget中
BluetoothDeviceCell* btDevCell = new BluetoothDeviceCell();
//设置item的高
item->setSizeHint(QSize(ui->deviceListWidget->width(), btDevCell->height()));
//设置label显示
//第一个加进去的item在最上面,相当于标题
btDevCell->m_btName->setText("名称");
btDevCell->m_btAddr->setText("地址");
btDevCell->m_btRssi->setText("信号强度");
//将item加入到listwidget中
ui->deviceListWidget->addItem(item);
//设置item的窗口为自定义的窗口
ui->deviceListWidget->setItemWidget(item, btDevCell);
//设置服务列表的标题
QListWidgetItem* items = new QListWidgetItem();
//创建自定义窗口,放入到listwidget中
BluetoothDeviceCell* btDevCells = new BluetoothDeviceCell();
//设定item的尺寸
items->setSizeHint(QSize(ui->serviceListWidget->width(), btDevCells->height()));
//设置label的显示
//第一个加进去的item在最上面,相当于标题
btDevCells->m_btName->setText("名称");
btDevCells->m_btAddr->setText("服务Uuid");
btDevCells->m_btRssi->setText("空");
//将item加入到listwidget中
ui->serviceListWidget->addItem(items);
//设置item的窗口为自定义的窗口
ui->serviceListWidget->setItemWidget(items, btDevCells);
/*------------- m_deviceDiscoveryAgent设备搜索对象的信号槽 -------------*/
connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BluetoothConnect::deviceDiscoveredSlot);//发现设备
connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &BluetoothConnect::deviceSearchFinishedSlot);//搜索完毕
void (QBluetoothDeviceDiscoveryAgent:: *deviceSearchErrorOccurred)(QBluetoothDeviceDiscoveryAgent::Error) = &QBluetoothDeviceDiscoveryAgent::error;//有重载
connect(m_deviceDiscoveryAgent, deviceSearchErrorOccurred, this, &BluetoothConnect::deviceSearchErrorOccurredSlot);//设备搜索发生错误
/*------------- m_btSocket蓝牙套接字的信号槽 ----------------*/
void (QBluetoothSocket:: *socketErrorOccurred)(QBluetoothSocket::SocketError) = &QBluetoothSocket::error;
connect(m_btSocket, socketErrorOccurred, this, &BluetoothConnect::socketErrorOccurredSlot); //错误处理槽函数
connect(m_btSocket, &QBluetoothSocket::connected, this, &BluetoothConnect::socketConnectedSlot);//连接成功
/*------------- m_serviceDiscoveryAgent服务发现对象的信号槽 ---------------*/
connect(m_serviceDiscoveryAgent, &QBluetoothServiceDiscoveryAgent::serviceDiscovered, this, &BluetoothConnect::serviceDiscoveredSlot); //发现一个服务
connect(m_serviceDiscoveryAgent, &QBluetoothServiceDiscoveryAgent::finished, this, &BluetoothConnect::serviceSearchFinishedSlot); //服务搜索完毕
deviceListWidget和serviceListWidget是我在ui中设置的两个列表组件,用来存放设备和服务信息。
利用QBluetoothDeviceDiscoveryAgent
的start
方法即可开启设备搜索:
void BluetoothConnect::on_searchButton_clicked() //点击Search按钮后开始搜索设备
{
//如果已经搜索过1次,那么设备列表可能会大于1,需要先清空设备列表
//但是标题也会被清除,所以重新添加标题
if(ui->deviceListWidget->count() > 1)
{
ui->deviceListWidget->clear();
//设置设备列表
QListWidgetItem* item = new QListWidgetItem();
//创建自定义窗口,放入到listwidget中
BluetoothDeviceCell* btDevCell = new BluetoothDeviceCell();
//设置item的高
item->setSizeHint(QSize(ui->deviceListWidget->width(), btDevCell->height()));
//设置label显示
btDevCell->m_btName->setText("名称");
btDevCell->m_btAddr->setText("地址");
btDevCell->m_btRssi->setText("信号强度");
//将item加入到listwidget中
ui->deviceListWidget->addItem(item);
//设置item的窗口为自定义的窗口
ui->deviceListWidget->setItemWidget(item, btDevCell);
}
m_deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::ClassicMethod); //开启经典蓝牙设备搜索
ui->searchButton->setEnabled(false);//搜索过程中,search按钮不可点击
ui->statusLabel->setText("Searching for devices......");
}
QBluetoothDeviceDiscoveryAgent::ClassicMethod
是以经典方式进行搜索,可以发现经典蓝牙和低功耗蓝牙设备。
发现一个设备的槽函数:
void BluetoothConnect::deviceDiscoveredSlot(QBluetoothDeviceInfo btDevInfo) //发现设备后将设备添加到listwidget中
{
QListWidgetItem* item = new QListWidgetItem(); //声明一个item
BluetoothDeviceCell* btDevCell = new BluetoothDeviceCell(); //声明一个蓝牙设备单元
item->setSizeHint(QSize(ui->deviceListWidget->width(), btDevCell->height())); //设定item的尺寸
//搜索到的设备信息存到设备单元中
btDevCell->m_btName->setText(btDevInfo.name());
btDevCell->m_btAddr->setText(btDevInfo.address().toString());
btDevCell->m_btRssi->setText(QString::number(btDevInfo.rssi()));
//将设备单元添加到deviceListWidget中
ui->deviceListWidget->addItem(item);
ui->deviceListWidget->setItemWidget(item,btDevCell);
//设备信息list新增一个设备信息
m_devInfos.append(btDevInfo);
}
设备搜索完成的槽函数:
void BluetoothConnect::deviceSearchFinishedSlot() //设备搜索完成
{
ui->statusLabel->setText("Double Click a device to searhc for services");
QMessageBox::about(this, "提示", "设备搜索完成");
// qDebug() << "搜索已完成";
ui->searchButton->setEnabled(true); //一次搜索完之后才可以重新点击search键
}
设备搜索出现错误的槽函数:
void BluetoothConnect::deviceSearchErrorOccurredSlot(QBluetoothDeviceDiscoveryAgent::Error error) //搜索蓝牙设备出现错误
{
qDebug() << error;
//警告对话框
QMessageBox::warning(this, "警告!", "搜索蓝牙设备发生错误,请检查蓝牙是否开启");
}
激活设备列表的设备,搜索这个选定设备的服务:
void BluetoothConnect::on_deviceListWidget_itemActivated(QListWidgetItem *item) //点击设备列表中的item
{
/***
* 点击设备后,搜索设备的服务。点击服务后,根据服务和设备进行连接。并将socket的准备接收与主窗口BluetoothDebugger的revDataTextEdit的显示连接
*/
//在开始service搜索之前先重置服务列表
//如果已经搜索过1次,那么服务列表可能会大于1,需要先清空服务列表
//但是标题也会被清除,所以重新添加标题
if(ui->serviceListWidget->count() > 1)
{
ui->serviceListWidget->clear();
//设置服务列表的标题
QListWidgetItem* items = new QListWidgetItem();
//创建自定义窗口,放入到listwidget中
BluetoothDeviceCell* btDevCells = new BluetoothDeviceCell();
//设定item的尺寸
items->setSizeHint(QSize(ui->serviceListWidget->width(), btDevCells->height()));
//设置label的显示
//第一个加进去的item在最上面,相当于标题
btDevCells->m_btName->setText("名称");
btDevCells->m_btAddr->setText("服务Uuid");
btDevCells->m_btRssi->setText("空");
//将item加入到listwidget中
ui->serviceListWidget->addItem(items);
//设置item的窗口为自定义的窗口
ui->serviceListWidget->setItemWidget(items, btDevCells);
}
QWidget* widget = ui->deviceListWidget->itemWidget(item); //将点击的item信息取出来
BluetoothDeviceCell* btDevCell = (BluetoothDeviceCell*) widget; //强制类型转换为DeviceCell设备单元
qDebug() << btDevCell->m_btAddr->text(); //输出设备地址
m_btAddress = QBluetoothAddress(btDevCell->m_btAddr->text());//记录下来选中的设备的地址.后边点击service进行连接时,使用该地址
m_serviceDiscoveryAgent->setRemoteAddress(QBluetoothAddress(btDevCell->m_btAddr->text())); //要搜索的服务是当前激活的这个设备的服务
m_serviceDiscoveryAgent->start(); //开始搜索
ui->statusLabel->setText("Searching for services......");
//!!!!不同设备所允许的服务不同,需要寻找设备支持的服务有什么
}
发现一个服务的槽函数,将服务信息添加到listWidget
以及自己的list
中:
void BluetoothConnect::serviceDiscoveredSlot(const QBluetoothServiceInfo& serviceInfo) //发现服务
{
//serviceInfo中没有设备地址
if(serviceInfo.serviceName().isEmpty())
return;
//!!!用serviceClassUuids才能得到有效的Uuid
QList btUuids = serviceInfo.serviceClassUuids();
//每个list中只有1个元素
qDebug() << btUuids[0];
QBluetoothUuid btUuid = btUuids[0];
QListWidgetItem* item = new QListWidgetItem();
BluetoothDeviceCell* btDevCell = new BluetoothDeviceCell();
item->setSizeHint(QSize(ui->serviceListWidget->width(), btDevCell->height()));
btDevCell->m_btName->setText(serviceInfo.serviceName());
btDevCell->m_btAddr->setText(btUuid.toString());
btDevCell->m_btRssi->setText("");
ui->serviceListWidget->addItem(item);
ui->serviceListWidget->setItemWidget(item, btDevCell);
m_btUuids.append(btUuid);
}
服务搜索完成的槽函数:
void BluetoothConnect::serviceSearchFinishedSlot() //服务搜索完成
{
ui->statusLabel->setText("Double Click a service to connect to bluetooth");
QMessageBox::about(this, "Hint", "Service Search Finished!");
}
激活服务列表的服务的槽函数:
void BluetoothConnect::on_serviceListWidget_itemActivated(QListWidgetItem *item) //激活服务列表的项目
{
QWidget* widget = ui->serviceListWidget->itemWidget(item); //将点击的item信息取出来
BluetoothDeviceCell* btDevCell = (BluetoothDeviceCell*) widget; //强制类型转换为DeviceCell设备单元
qDebug() << m_btAddress << btDevCell->m_btAddr->text();
m_btSocket->connectToService(m_btAddress,QBluetoothUuid(btDevCell->m_btAddr->text()),QIODevice::ReadWrite); //根据设备地址与Uuid连接
ui->statusLabel->setText("Bluetooth connecting......");
}
使用socket
套接字连接成功后,经典蓝牙通讯即建立:
void BluetoothConnect::socketConnectedSlot() //socket连接成功
{
ui->statusLabel->setText("");
QMessageBox::about(this, "Information", "Socket Connected Successfully!");
//隐去Bluetooth Connect窗口
this->close();
}
socket
发生错误的槽函数:
void BluetoothConnect::socketErrorOccurredSlot(QBluetoothSocket::SocketError error) //socket发生错误
{
switch(error)
{
case QBluetoothSocket::NoSocketError:
break;
case QBluetoothSocket::UnknownSocketError:
qDebug() << "Error: Unknown Socket Error!";
break;
case QBluetoothSocket::RemoteHostClosedError:
qDebug() << "Error: Remote Host Closed Error!";
break;
case QBluetoothSocket::HostNotFoundError:
qDebug() << "Error: Host Not Found Error!";
break;
case QBluetoothSocket::ServiceNotFoundError:
qDebug() << "Error: Service Not Found Error!";
break;
case QBluetoothSocket::NetworkError:
qDebug() << "Error: Network Error!";
break;
case QBluetoothSocket::UnsupportedProtocolError:
qDebug() << "Error: Unsupported Protocol Error!";
break;
case QBluetoothSocket::OperationError:
qDebug() << "Error: Operation Error!";
break;
}
}
接收数据使用socket
,将readyRead
的signal
与槽函数连接:
void BluetoothDebugger::socketReadyReadSlot() //socket准备好读取
{
char data[100];
qint64 len = m_btConnect->m_btSocket->read((char*)data,100);
QByteArray byteArray((char*)data, len);
if(m_isClose == false) //窗口显示打开
{
ui->revDataTextBrowser->append("接收:");
ui->revDataTextBrowser->append(byteArray);//添加到窗口
}
}
发送数据也是使用socket
套接字:
void BluetoothDebugger::on_sendButton_clicked() //点击发送按钮
{
//被发送的数据
QByteArray dataSent = ui->sendDataLineEdit->text().toUtf8();
if(m_isClose == false) //窗口显示打开,蓝牙发送使能;窗口显示关闭,蓝牙发送失能
{
//将发送的数据也打印到窗口上
ui->revDataTextBrowser->append("发送:");
ui->revDataTextBrowser->append(dataSent);
//通过socket发送数据
m_btConnect->m_btSocket->write(dataSent);
}
}
以上即为使用Qt建立经典蓝牙通讯的基本步骤。