BLE简介
BLE蓝牙是蓝牙2.0以上的蓝牙模块,经典蓝牙是蓝牙2.0以下的蓝牙。蓝牙分为客户端和服务器两端,经典蓝牙可以通过socket编程进行客户端与服务器之间的通信(与网络socket相似),BLE蓝牙则无法使用这种方式进行通信。BLE蓝牙下有服务、特征值,所谓的BLE蓝牙通信其实就是对特征值的一个读写操作。微软的Windows下的API关于BLE蓝牙的C++API相对较少,C#的API会相对较多一点。我实现这个蓝牙的demo则是使用了Qt进行一个开发。
需要注意的是Qt中开发所用到的QBlueTooth库在Windows下的只支持win10以上系统,而且需要Qt版本在5.15以上,编译器一定要用MSVC编译,否则无法支持Windows平台。
Qt5BLE蓝牙开发
本文环境为Qt5.15.2
若要在Qt中使用蓝牙库需要在.pro文件添加一下一句话
QT += bluetooth
BLE蓝牙开发流程
首先讲一下我自己对BLE蓝牙的理解,BLE蓝牙有名字和地址,蓝牙设备下又有服务,服务有特定的的uuid,可以理解为服务的识别码,服务下有特征,BLE蓝牙服务可以有0-3个特征,分别是读、写、特征值通知这三个。如果服务拥有读特征,就可以从此服务中读消息,如果服务拥有写特征,就可以发送消息给服务,如果服务有特征值改变通知这个特征,则当服务的特征值发生改变时会发送消息通知。
- 蓝牙的权限问题(Windows平台下不存在此问题,可以忽略,在安卓平台下不管是Qt还是Java都需要考虑这个问题)
- 搜索周边蓝牙
- 连接蓝牙
- 获取服务
- 获取服务下特征,每个服务下都有0-3个特性,分别对应了读,写,以及特性发生改变时是否通知
- 与BLE设备通信(其实就是对某个服务进行读写操作)
Bluetooth在Qt中所涉及Class与APIQt中所涉及到的蓝牙头文件与类
#include <QBluetoothLocalDevice>
#include <QBluetoothUuid>
#include <QBluetoothDeviceDiscoveryAgent>
#include <QLowEnergyService>
#include <QLowEnergyController>
在windows平台和ios平台下QBluetoothLocalDevice类无法使用
因为平台不会公开任何可能在本地Bluetooth设备上提供信息的数据或API
QBluetoothDeviceDiscoveryAgent -- 蓝牙搜索类(BLE蓝牙或经典蓝牙都使用此类进行搜索)
LowEnergyController -- BLE蓝牙设备进行访问类
QLowEnergyService -- BLE蓝牙服务类
具体实现
1.实现附近BLE蓝牙的搜索
QBluetoothDeviceDiscoveryAgent *Discovery = new QBluetoothDeviceDiscoveryAgent;
Discovery->setLowEnergyDiscoveryTimeout(30000);//设置搜索时间为30000us
//当搜索蓝牙完成之后会发射[Signal]finished()
connect(Discovery, SIGNAL(finished()),
this, SLOT(findFinish()));
//找到蓝牙设备会发送[Signal]deviceDiscovered(QBluetoothDeviceInfo)
connect(Discovery, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
this, SLOT(addBlueToothDevicesToList(QBluetoothDeviceInfo)));
//start启动搜索,若查找为BLE蓝牙需要写入参数
//QBluetoothDeviceDiscoveryAgent::LowEnergyMethod
Discovery->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
2.实现连接BLE蓝牙
//void QLowEnergyController::creatCentral(const QBluetoothDeviceInfo &remotedevice,QObject *parent)
//参数:remotedevice -- 远端设备的信息 parent -- 父类
//返回值:无
//功能:初始化对远程蓝牙访问的控制器
mController = QLowEnergyController::createCentral(currentDevice,this);
connect(mController, &QLowEnergyController::serviceDiscovered,this, &MainWindow::serviceDiscovered);//扫描目标BLE服务,获取一次触发一次
connect(mController, &QLowEnergyController::discoveryFinished,this, &MainWindow::serviceScanDone); //扫描完成之后会触发此信号
connect(mController, static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),
this, [this](QLowEnergyController::Error error) {
Q_UNUSED(error);
QMessageBox::information(this,tr("Info"),tr("Cannot connect to remote device."));
});//连接出错
connect(mController, &QLowEnergyController::connected, this, [this]() {
QMessageBox::information(this,tr("Info"),tr("Controller connected. Search services..."));
mController->discoverServices();
});//连接成功
connect(mController, &QLowEnergyController::disconnected, this, [this]() {
QMessageBox::information(this,tr("Info"),tr("LowEnergy controller disconnected"));
});//断开连接
mController->connectToDevice();//建立连接,若连接没有出错,会自动搜索连接设备的服务
3.获取服务
//mController->connectToDevice();建立连接后若无出错,会自动进行远程设备的服务扫描
//当服务被找到时触发此槽函数
//服务被找到
void MainWindow::serviceDiscovered(const QBluetoothUuid & serviceUuid)
{
QLowEnergyService *service = mController->createServiceObject(serviceUuid);
//同步服务
if (!service)
{
QMessageBox::information(NULL,"error","Cannot create service for uuid");
return;
}
connect(service, &QLowEnergyService::stateChanged, this,&MainWindow::serviceStateChanged);
//服务状态值发生改变
connect(service, &QLowEnergyService::characteristicChanged, this,&MainWindow::BleServiceCharacteristicChanged);
//特征值发生改变
connect(service, &QLowEnergyService::characteristicRead, this,&MainWindow::BleServiceCharacteristicRead);
//读信息成功
connect(service, SIGNAL(characteristicWritten(QLowEnergyCharacteristic,QByteArray)),this, SLOT(BleServiceCharacteristicWrite(QLowEnergyCharacteristic,QByteArray)));
//写信息成功
if(service->state() == QLowEnergyService::DiscoveryRequired)
service->discoverDetails();
//扫描特征值
serviceList.append(service);
}
//服务状态值改变会触发此槽函数
//当蓝牙服务被扫描到时状态值会发生改变,改变为QLowEnergyService::ServiceDiscovered
void MainWindow::serviceStateChanged(QLowEnergyService::ServiceState s)
{
if(s == QLowEnergyService::ServiceDiscovered)
{
//QMessageBox::information(NULL,"tips",QObject::tr("Service synchronization"));
//TODO....
}
}
4.扫描服务特征值
void MainWindow::searchCharacteristic()
{
if(currentService)
{
memset(m_Characteristic,0,sizeof(m_Characteristic));
QList<QLowEnergyCharacteristic> list = currentService->characteristics();
//characteristics 获取详细特性
foreach(QLowEnergyCharacteristic c,list)
{
/*如果QLowEnergyCharacteristic对象有效,则返回true,否则返回false*/
if(c.isValid())
{
//返回特征的属性。
//这些属性定义了特征的访问权限。
//保存特性(读、写、改变时是否通知)
if(c.properties() & QLowEnergyCharacteristic::WriteNoResponse || c.properties() & QLowEnergyCharacteristic::Write)//write
// if(c.properties() & QLowEnergyCharacteristic::Write)
{
ui->checkBox_write->setChecked(true);
m_Characteristic[0] = c;
}
else
ui->checkBox_write->setChecked(false);
if(c.properties() & QLowEnergyCharacteristic::Read)//读
{
ui->checkBox_read->setChecked(true);
m_Characteristic[1] = c;
}
else
ui->checkBox_read->setChecked(true);
if(c.properties() & QLowEnergyCharacteristic::Notify)//特性发生改变时是否通知
{
ui->checkBox_notify->setChecked(true);
m_Characteristic[2] = c;
}
else
ui->checkBox_notify->setChecked(false);
}
}
}
}
5.BLE蓝牙的读、写操作
//void QLowEnergyService::writeCharacteristic(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue, QLowEnergyService::WriteMode mode = WriteWithResponse)
//参数:characteristic -- 当前服务的某个特性值 newValue -- 写入数据 WriteMode mode -- 写入模式
//返回值:无
//功能:发送信息给BLE
currentService->writeCharacteristic(m_Characteristic[0], QByteArray(text.toUtf8()));
//发送消息成功触发此槽函数
void MainWindow::BleServiceCharacteristicWrite(const QLowEnergyCharacteristic &c, const QByteArray &value)
{
QString str(c.uuid().toString());
QString str2("instructions %1 send to success!");
QString str3 = str + QString(":") + str2.arg(QString(value));
QMessageBox::information(NULL,"tips",str3);
}
//void QLowEnergyService::readCharacteristic(const QLowEnergyCharacteristic &characteristic)
//参数:characteristic -- 当前服务的某个特性值
//返回值:无
//功能:从BLE中读取信息
currentService->readCharacteristic(m_Characteristic[1]);
//读到消息触发此槽函数
void MainWindow::BleServiceCharacteristicRead(const QLowEnergyCharacteristic &c, const QByteArray &value)
{
QTextCodec *codec = QTextCodec::codecForName("GBK");//指定QString的编码方式
QString showMsg = c.uuid().toString() + codec->toUnicode(value);//Unicode编码格式输出信息
QString valuetoHexString = value.toHex();//16进制输出信息
qDebug()<<value;
qDebug()<<valuetoHexString;
ui->listWidget_recv->addItem(showMsg);
ui->listWidget_recv->setCurrentRow(ui->listWidget_recv->count()-1);
}
源码下载
https://download.csdn.net/download/weixin_46276101/19686524?spm=1001.2014.3001.5503