找回密码
 立即注册
Qt开源社区 门户 查看内容

QT软件开发-第十三章 基于QT5蓝牙设备开发 13.5

2019-5-7 15:32| 发布者: admin| 查看: 12784| 评论: 1

摘要: 点击上方蓝字关注我们13.5 低功耗蓝牙设备的访问13.5.1 QLowEnergyController类介绍QLowEnergyController类提供对低功耗蓝牙设备的访问。QLowEnergyController充当蓝牙低功耗开发的入口点。低功耗蓝牙定义了两种类型 ...


点击上方蓝字关注我们




13.5 低功耗蓝牙设备的访问

13.5.1 QLowEnergyController类介绍


QLowEnergyController类提供对低功耗蓝牙设备的访问。

QLowEnergyController充当蓝牙低功耗开发的入口点。

低功耗蓝牙定义了两种类型的设备: 外围设备角色和中央设备角色。

每个角色执行不同的任务。外围设备提供中央设备使用的数据。

一个例子: 可能外围设备是一个湿度传感器,它可以测量冬季花园的湿度。诸如移动电话之类的设备可以读取传感器的值,在这种情况下,传感器是外围设备,手机充当中央设备。

通过createCentral()方法创建中心角色的控制器,实现诸如服务发现和状态跟踪等功能。

在中心角色中创建控制器对象后,第一步是通过connectToDevice()建立连接。一旦建立连接,控制器的状态()将变为QLowEnergyController :: ConnectedState,并发出connected()信号。有一点很重要,例如基于BlueZ的Linux等平台无法将两个连接的QLowEnergyController实例维护到同一个远程设备。在这种情况下,对connectToDevice()的第二次调用可能会失败。这种限制可能在未来某个阶段消失。 disconnectFromDevice()函数用于中断现有连接。

建立连接后的第二步是发现远程外围设备提供的服务。此过程通过discoverServices()启动,并且一旦发现discoveryFinished()信号就完成了。发现的服务可以通过services()函数来枚举。

最后一步是创建服务对象。 createServiceObject()函数充当每个服务对象的工厂,并期望服务UUID作为参数。调用上下文应该拥有返回的QLowEnergyService实例的所有权。

一旦控制器从远程低功耗蓝牙设备断开,任何QLowEnergyService,QLowEnergyCharacteristic或QLowEnergyDescriptor实例(稍后由此控制器的连接创建)都将失效。

外设角色中的控制器是通过createPeripheral()工厂方法创建的。这样一个对象本身就是一个外围设备,可以实现广告服务等功能,并允许客户获得有关特征值更改的通知。

在外设角色中创建控制器对象后,第一步是通过调用addService()来填充提供给客户端设备的GATT服务集。之后,可以调用startAdvertising()让设备广播一些数据,并根据所做广告的类型来侦听来自GATT客户端的传入连接。

另请参阅QLowEnergyService,QLowEnergyCharacteristic,QLowEnergyDescriptor,QLowEnergyAdvertisingParameters

和QLowEnergyAdvertisingData。

13.5.2 常用的函数介绍

(1) 连接到低功耗蓝牙设备


void QLowEnergyController::connectToDevice()

如果控制器的state()不等于UnconnectedState,则此函数不执行任何操作。 一旦连接成功建立,connected()信号就会发出。

(2) 断开连接


void QLowEnergyController::disconnectFromDevice()

 断开与远程设备的连接。

当前连接断开,会导致QLowEnergyService,QLowEnergyCharacteristic或QLowEnergyDescriptor实例都会自动失效。 一旦这些对象中的任何一个变得无效,即使此控制器对象重新连接,它们仍然无效。

如果控制器处于未连接状态,此功能不执行任何操作。

(3) 创建目标服务


QLowEnergyService *QLowEnergyController::createServiceObject(const QBluetoothUuid &serviceUuid, QObject *parent = Q_NULLPTR)

创建由serviceUuid表示的服务的一个实例。 serviceUuid参数必须通过services()获得。

调用者获得返回指针的所有权,并可以传递父参数作为默认所有者。

如果在远程设备上没有找到serviceUuid的服务,或者控制器断开连接,则此函数返回空指针。

该函数也可以为二级服务返回实例。 服务之间的包含关系可以通过QLowEnergyService :: includedServices()来表示。

如果使用相同的服务UUID多次调用此函数,则返回的QLowEnergyService实例共享其内部数据。 因此,如果其中一个实例启动发现服务详细信息,则其他实例也会自动转换到发现状态。

(4) 返回远程设备的UUID列表


href="../qtcore/qlist.html" QList<QBluetoothUuid> QLowEnergyController::services() const

如果控制器位于CentralRole中,则返回远程设备提供的服务列表。 否则,结果未指定。

该列表包含所有主要和次要服务。

(5) 获取错误信息


Error error() const

QString errorString() const

错误枚举值:

QLowEnergyController::NoError

没有发生错误。

QLowEnergyController::UnknownError

出现未知错误。

QLowEnergyController::UnknownRemoteDeviceError

无法找到传递给此类构造函数的远程Bluetooth Low Energy设备。

QLowEnergyController::NetworkError

尝试读取或写入远程设备失败。

QLowEnergyController::InvalidBluetoothAdapterError

传递给此类构造函数的本地蓝牙设备无法找到,或者没有本地蓝牙设备。

QLowEnergyController::ConnectionError

尝试连接到远程设备失败。 这个值是由Qt 5.5引入的。

QLowEnergyController::AdvertisingError

尝试开始广告失败。 这个值是由Qt 5.7引入的。

(6) 获取本地蓝牙适配器地址


QBluetoothAddress QLowEnergyController::localAddress() const

返回用于通信的本地蓝牙适配器的地址。

如果请求此类实例使用默认适配器,但在创建此类实例时没有默认适配器,则返回的QBluetoothAddress将为空。

(7) 返回远程低功耗蓝牙的设备地址


QBluetoothAddress QLowEnergyController::remoteAddress() const

返回远程蓝牙低功耗设备的地址。

对于CentralRole中的控制器,此值始终是创建控制器对象时传入的值。 对于PeripheralRole中的控制器,此值是当前连接的客户端设备的地址。 特别是,如果控制器当前不在ConnectedState中,则该地址将无效。

(8) 返回远程蓝牙的名称


QString QLowEnergyController::remoteName() const

如果控制器位于CentralRole中,则返回远程Bluetooth Low Energy设备的名称。 否则结果是未指定的。

(9) 返回控制器的当前状态


ControllerState QLowEnergyController::state() const

状态枚举值:

QLowEnergyController::UnconnectedState

控制器未连接到远程设备。

QLowEnergyController::ConnectingState

控制器正在尝试连接到远程设备。

QLowEnergyController::ConnectedState

控制器连接到远程设备。

QLowEnergyController::DiscoveringState

控制器正在检索远程设备提供的服务列表。

QLowEnergyController::DiscoveredState

控制器已经发现了远程设备提供的所有服务。

QLowEnergyController::ClosingState

控制器即将与远程设备断开连接。

QLowEnergyController::AdvertisingState

控制器目前正在广告数据。 这个值是由Qt 5.7引入的。

(10) 创建中央角色的控制器


QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDeviceInfo &remoteDevice, QObject *parent = nullptr)

返回CentralRole中此类的新对象,并具有父对象。 remoteDevice指的是将来建立连接的设备。

控制器使用本地默认蓝牙适配器进行连接管理。

(11) 启动服务查找功能


void QLowEnergyController::discoverServices()

当设备连接成功之后才可以调用,用于查找设备的UUID服务列表。

13.5.3 常用的信号列表

(1) 连接建立成功的通知信号


void QLowEnergyController::connected()

当控制器成功连接远程Low Energy设备(如果控制器处于CentralRole中)或连接到控制器的远程Low Energy设备(如果控制器位于PeripheralRole中),则会发出此信号。

(2) 连接参数发生改变


void QLowEnergyController::connectionUpdated(const QLowEnergyConnectionParameters &newParameters)

当连接参数改变时发出此信号。 这可能是由于调用requestConnectionUpdate()或由于其他原因而发生的,例如因为连接的另一端请求了新参数。 新值可以从newParameters中检索。

这个函数是在Qt 5.7中引入的。

(3) 断开连接参数


void QLowEnergyController::disconnected()

当控制器与远程低功耗设备断开连接时,该信号会发出。

(4) 发现新的服务


void QLowEnergyController::serviceDiscovered(const QBluetoothUuid &newService)

每次发现新服务时都会发出此信号。 newService参数包含找到的服务的UUID。

只有当控制器处于CentralRole中时才能发出此信号。

(5) 服务发现完成


void QLowEnergyController::discoveryFinished()

正在运行的服务发现完成时发出此信号。 如果发现过程发生错误,则不发射信号。

(6) 错误提示信号


void QLowEnergyController::error(QLowEnergyController::Error newError)

该信号在发生错误时发出。 newError参数描述发生的错误。

注意:此类中信号错误超载。 要使用函数指针语法连接到此函数,您必须在静态转换中指定信号类型,如下例所示:

connect(lowEnergyController, static_cast<void(QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),

      [=](QLowEnergyController::Error newError){ /* ... */ });

13.6 低功耗蓝牙设备的数据收发

13.6.1 QLowEnergyService类介绍


QLowEnergyService类表示蓝牙低功耗设备上的单个服务。

QLowEnergyService可以访问蓝牙低功耗服务的详细信息。允许读取和写入所包含的数据并通知数据更改。

蓝牙低功耗外围设备可以包含多种服务。 每个服务又可能包括更多的服务。 该类代表外围设备的单个服务,并通过QLowEnergyController :: createServiceObject()创建。 type()指示此服务是主服务(顶级服务)还是服务是否是另一服务的一部分。 每个服务可能包含一个或多个特征,每个特征可能包含描述符。 最终的结构可能如下图所示:



13-6-1图


  • 服务交互

一旦服务对象首次被创建,其细节尚未被发现。这由它的当前state()为DiscoveryRequired来表示。只能检索serviceUuid()和serviceName()。

调用discoverDetails()时会触发其包含的服务,特性和描述符的发现。在发现期间,state()从DiscoveryRequired通过DiscoveringServices转换到其最终的ServiceDiscovered状态。该转换通过stateChanged()信号进行广告。一旦知道了详细信息,所有包含的特征,描述符和包含的服务都是已知的,并且可以读取或写入。

特性和描述符的值可分别通过QLowEnergyCharacteristic和QLowEnergyDescriptor检索。但是,直接读取或写入这些属性需要服务对象。 readCharacteristic()函数尝试重新读取特征的值。尽管最初的服务发现可能已经获得了一个值,但是如果特征值在没有提供任何通知的情况下不断发生变化,则可能需要此调用。

如果读取尝试成功,则发出characteristicRead()信号。未能读取该值会触发CharacteristicReadError。 writeCharacteristic()函数会尝试为给定特性写入新值。如果写入尝试成功,则发出characteristicWritten()信号。写入失败会触发CharacteristicWriteError。描述符的读写符合相同的模式。

13.6.2 常用函数介绍

(1) 返回UUID的匹配特性


QLowEnergyCharacteristic QLowEnergyService::characteristic(const QBluetoothUuid &uuid) const

返回uuid的匹配特性; 否则为无效特征。

如果此服务discoverDetails()实例尚未调用或者没有与uuid匹配的特征,则返回的特征无效。

(2) 返回关联的所有特性


QList<QLowEnergyCharacteristic> QLowEnergyService::characteristics() const

返回与此QLowEnergyService实例关联的所有特征。

如果此服务discoverDetails()实例尚未调用或者没有已知特征,则返回的列表为空。

(3) 开始发现服务所包含的: 服务,特征和描述符


void QLowEnergyService::discoverDetails()

发现过程通过stateChanged()信号指示。

(4) 判断特征是否属于此服务


booQLowEnergyService::contains(const QLowEnergyCharacteristic &characteristic) const

boocontains(const QLowEnergyDescriptor &descriptor) const

如果特征属于此服务,则返回true; 否则为false。

如果characteristics()包含特征,则特征属于服务。

(5) 读取特征值


void QLowEnergyService::readCharacteristic(const QLowEnergyCharacteristic &characteristic)

读取特征的值。 如果操作成功,则发出characteristicRead()信号; 否则设置CharacteristicReadError。 一般来说,如果设置了QLowEnergyCharacteristic :: Read属性,则该特性是可读的。

(6) 读取描述符的值


void QLowEnergyService::readDescriptor(const QLowEnergyDescriptor &descriptor)

读取描述符的值,如果操作成功,则发射descriptorRead()信号; 否则设置DescriptorReadError。

(7) 返回服务的UUID的和名称


QString serviceName() const

QBluetoothUuid serviceUuid() const

(8) 写入新的特征值


void QLowEnergyService::writeCharacteristic(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue, WriteMode mode = WriteWithResponse)

写入newValue作为特性的值。如果操作成功,则发出characteristicWritten()信号;否则将设置CharacteristicWriteError。

(9) 写入新的描述符值


void QLowEnergyService::writeDescriptor(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue)

13.6.3 程序案例:BLE低功耗蓝牙调试助手


(配套代码CH13-02)

(1) mainwindow.cpp文件代码


#include "mainwindow.h"

#include "ui_mainwindow.h"

/*

 * 设置QT界面的样式

*/

void MainWindow::SetStyle(const QString &qssFile) {

    QFile file(qssFile);

    if (file.open(QFile::ReadOnly)) {

        QString qss = QLatin1String(file.readAll());

        qApp->setStyleSheet(qss);

        QString PaletteColor = qss.mid(20,7);

        qApp->setPalette(QPalette(QColor(PaletteColor)));

        file.close();

    }

    else

    {

        qApp->setStyleSheet("");

    }

}

//#蓝牙串口服务

//SerialPortServiceClass_UUID = '{00001101-0000-1000-8000-00805F9B34FB}'

//Service UUID 0xFEE0 主服务

//static const QLatin1String serviceUuid("0000FEE0-0000-1000-8000-00805F9B34FB");

//这个字符串里面的内容就是串口模式的Uuid

MainWindow::MainWindow(QWidget *parent) :

    QMainWindow(parent),

    ui(new Ui::MainWindow)

{

    ui->setupUi(this);

    this->SetStyle(":/qss/blue.css");     //设置样式表

    this->setWindowTitle("串口调试助手"); //设置标题

    this->setWindowIcon(QIcon(":/wbyq.ico")); //设置图标

    /*1. 实例化蓝牙相关的对象*/

    discoveryAgent = new QBluetoothDeviceDiscoveryAgent();

    localDevice = new QBluetoothLocalDevice();

    /*2. 关联蓝牙设备相关的信号*/

    /*2.1 关联发现设备的槽函数,当扫描发现周围的蓝牙设备时,会发出deviceDiscovered信号*/

    connect(discoveryAgent,

            SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),

            this,

            SLOT(addBlueToothDevicesToList(QBluetoothDeviceInfo))

            );

    /*3. 检查蓝牙的状态,用于设置按钮的初始状态*/

    /*3.1 检查蓝牙是否开启*/

    if(localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff)

    {

            //如果蓝牙处于关闭状态

            ui->pushButton_OpenBluetooth->setEnabled(true);   //打开按钮

            ui->pushButton_CloseBluetooth->setEnabled(false); //关闭按钮

    }

    else    //如果蓝牙处于开启状态

    {

            ui->pushButton_OpenBluetooth->setEnabled(false);//打开按钮

            ui->pushButton_CloseBluetooth->setEnabled(true);//关闭按钮

             ui->pushButton_BluetoothScan->setEnabled(true); //设置扫描按钮可用

    }

    /*3.2 设置标签显示本地蓝牙的名称*/

    QString name_info("本机蓝牙:");

    name_info+=localDevice->name();

    ui->label_BluetoothName->setText(name_info);

     ui->pushButton_StopScan->setEnabled(false);      //设置停止扫描蓝牙的按钮不可用

     ui->plainTextEdit_BluetoothInfiShow->setEnabled(false); //设置不可编辑

     m_control=NULL; //初始值

     m_service=NULL;  //初始值

     SendModeSelect=0;

     SendMaxMode=0;

}

MainWindow::~MainWindow()

{

    delete ui;

    delete discoveryAgent;

    delete localDevice;

}

void MainWindow::on_pushButton_OpenBluetooth_clicked()

{

    /*请求打开蓝牙设备*/

    localDevice->powerOn();

    ui->pushButton_OpenBluetooth->setEnabled(false);//打开按钮

    ui->pushButton_CloseBluetooth->setEnabled(true);//关闭按钮

    ui->pushButton_BluetoothScan->setEnabled(true); //设置扫描按钮可用

}

void MainWindow::on_pushButton_CloseBluetooth_clicked()

{

    /*关闭蓝牙设备*/

    localDevice->setHostMode(QBluetoothLocalDevice::HostPoweredOff);

    ui->pushButton_OpenBluetooth->setEnabled(true);//打开按钮

    ui->pushButton_CloseBluetooth->setEnabled(false);//关闭按钮

    ui->pushButton_BluetoothScan->setEnabled(false); //设置扫描按钮不可用

}

void MainWindow::on_pushButton_BluetoothScan_clicked()

{

     /*开始扫描周围的蓝牙设备*/

    discoveryAgent->start();

    ui->comboBox_BluetoothDevice->clear(); //清除条目

    ui->pushButton_BluetoothScan->setEnabled(false); //设置扫描按钮不可用

    ui->pushButton_StopScan->setEnabled(true);     //设置停止扫描按钮可用

}

void MainWindow::on_pushButton_DeviceVisible_clicked()

{

    /*设置蓝牙可见,可以被周围的设备搜索到,在Android上,此模式最多只能运行5分钟。*/

    localDevice->setHostMode( QBluetoothLocalDevice::HostDiscoverable);

}

void MainWindow::on_pushButton_StopScan_clicked()

{

    /*停止扫描周围的蓝牙设备*/

    discoveryAgent->stop();

    ui->pushButton_StopScan->setEnabled(false);     //设置停止扫描按钮不可用

    ui->pushButton_BluetoothScan->setEnabled(true); //设置扫描按钮可用

}

/*当扫描到周围的设备时会调用当前的槽函数*/

void MainWindow::addBlueToothDevicesToList(const QBluetoothDeviceInfo &info)

{

   // QString labe= QString("%1 %2").arg(info.name()).arg(info.address().toString());

    QString labe= QString("%1 %2").arg(info.address().toString()).arg(info.name());

    ui->comboBox_BluetoothDevice->addItem(label); //添加字符串到comboBox上

}

/*

在说蓝牙设备连接之前,不得不提一个非常重要的概念,就是蓝牙的Uuid,引用一下百度的:

在蓝牙中,每个服务和服务属性都唯一地由"全球唯一标识符" (UUID)来校验。

正如它的名字所暗示的,每一个这样的标识符都要在时空上保证唯一。

UUID类可表现为短整形(16或32位)和长整形(128位)UUID。

他提供了分别利用String和16位或32位数值来创建类的构造函数,提供了一个可以比较两个UUID(如果两个都是128位)的方法,还有一个可以转换一个UUID为一个字符串的方法。

UUID实例是不可改变的(immutable),只有被UUID标示的服务可以被发现。

在Linux下你用一个命令uuidgen -t可以生成一个UUID值;

在Windows下则执行命令uuidgen 。UUID看起来就像如下的这个形式:2d266186-01fb-47c2-8d9f-10b8ec891363。当使用生成的UUID去创建一个UUID对象,你可以去掉连字符。

*/

//发送数据

void MainWindow::on_pushButton_SendData_clicked()

{

    QString text=ui->lineEdit_SendData->text();

    QByteArray array=text.toLocal8Bit();

    /*写入newValue作为特性的值。

     如果操作成功,将发射characteristicWritten()信号;

    低功耗设备: 每次最多写20个字节

    */

      m_service->writeCharacteristic(m_writeCharacteristic[SendModeSelect],array, m_writeMode);

}

//清空收到的数据

void MainWindow::on_pushButton_Clear_clicked()

{

    ui->plainTextEdit_BluetoothInfiShow->setPlainText("");

}

//连接蓝牙

void MainWindow::on_pushButton_ConnectDev_clicked()

{

    QString text = ui->comboBox_BluetoothDevice->currentText();

    int index = text.indexOf(' ');

    if(index == -1) return;

    QBluetoothAddress address(text.left(index));

    QString connect_device="开始连接蓝牙设备:\n";

    connect_device+=ui->comboBox_BluetoothDevice->currentText();

    QMessageBox::information(this,tr("连接提示"),connect_device);

    /*低功耗蓝牙设备*/

    if(m_control!=NULL)

    {

        m_control->disconnectFromDevice(); //断开远程设备

        delete m_control;

        m_contro= NULL;

    }

    ui->comboBox_UUID->clear();         //清除显示UUID服务的列表框

    QList<QBluetoothDeviceInfo> info_list=discoveryAgent->discoveredDevices(); //得到扫描的所有设备信息

    for(int i=0;i<info_list.count();i++)

    {

        if(info_list.at(i).address().toString()==text.left(index))

        {

             remoteDevice=info_list.at(i);

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("连接设备:");

       ui->plainTextEdit_BluetoothInfiShow->insertPlainText(remoteDevice.name());

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("\n");      ui->plainTextEdit_BluetoothInfiShow->insertPlainText(remoteDevice.address().toString());

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("\n");

            break;

        }

   }

    //创建中央角色设备

    m_contro= new QLowEnergyController(remoteDevice, this);

    //m_control=QLowEnergyController::createCentral(remoteDevice,this);

    if(m_control==0)

    {

        ui->plainTextEdit_BluetoothInfiShow->insertPlainText("创建中央角色设备失败!\n");

    }

    else

    {

        ui->plainTextEdit_BluetoothInfiShow->insertPlainText("创建中央角色设备成功!\n");

    }

    //每次发现新的服务就会发送此信号

    connect(m_control, SIGNAL(serviceDiscovered(QBluetoothUuid)),this, SLOT(BlueServiceDiscovered(QBluetoothUuid)));

    //正在运行的服务发现完成时发出此信号。

    connect(m_control, SIGNAL(discoveryFinished()),this, SLOT(BlueServiceScanDone()));

    //当控制器成功连接到远程Low Energy设备时,会发出此信号。

     connect(m_control, SIGNAL(connected()),this, SLOT(BlueDeviceConnected()));

    //当控制器从远程低功耗设备断开时发出此信号。

     connect(m_control, SIGNAL(disconnected()),this, SLOT(BlueDeviceDisconnected()));

     //该信号在发生错误时发出。

     connect(m_control, static_cast<void(QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),

           [=](QLowEnergyController::Error error){

         if(error==QLowEnergyController::NoError)

         {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("没有发生错误\n");

         }

         else if(error==QLowEnergyController::UnknownError)

         {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("出现未知错误。\n");

         }

          else if(error==QLowEnergyController::UnknownRemoteDeviceError)

         {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("无法找到传递给此类构造函数的远程Bluetooth Low Energy设备。\n");

         }

         else if(error==QLowEnergyController::NetworkError)

         {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("尝试读取或写入远程设备失败\n");

         }

          else if(error==QLowEnergyController::InvalidBluetoothAdapterError)

         {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("传递给此类构造函数的本地蓝牙设备无法找到,或者没有本地蓝牙设备\n");

         }

          else if(error==QLowEnergyController::InvalidBluetoothAdapterError)

         {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("尝试连接到远程设备失败。\n");

         }

         else

          ui->plainTextEdit_BluetoothInfiShow->insertPlainText("*****未知错误!******\n");

     });

     //连接到远程蓝牙低功耗设备。

      m_control->connectToDevice();

}

//每次发现新的服务,就会调用该槽函数

void MainWindow::BlueServiceDiscovered(const QBluetoothUuid &gatt)

{

     ui->comboBox_UUID->addItem(gatt.toString()); //添加字符串到comboBox上

     ui->plainTextEdit_BluetoothInfiShow->insertPlainText("\n");

     ui->plainTextEdit_BluetoothInfiShow->insertPlainText(gatt.toString());

}

//帮助提示

void MainWindow::on_pushButton_help_clicked()

{

    QMessageBox::information(this,tr("帮助提示"),"本软件用于BLE4.0蓝牙调试\n"

                                             "不支持HC-05系列2.0版本蓝牙\n"

                                             "软件下面按钮用于调整发送模式\n"

                                             "软件作者:DS小龙哥\n"

                                             "BUG反馈:1126626497@qq.com");

}

//默认指定UUID服务

static const QLatin1String serviceUuid("{0000FEE0-0000-1000-8000-00805F9B34FB}");

//正在运行的服务发现完成时发出此信号。

void MainWindow::BlueServiceScanDone()

{

   // ui->plainTextEdit_BluetoothInfiShow->insertPlainText("正在运行的服务发现完成\n");

//    QMessageBox::information(this,tr("帮助提示"),"服务发现完成\n"

//                                             "请选择上方列表中的服务\n"

//                                             "进行连接BLE低功耗蓝牙设备\n");

    /*判断之前有没有连接过*/

    if(m_service!=NULL)

    {

        delete m_service;

        m_service=NULL;

    }

    ui->plainTextEdit_BluetoothInfiShow->insertPlainText("\n选中的服务:");

    ui->plainTextEdit_BluetoothInfiShow->insertPlainText(serviceUuid);

    ui->plainTextEdit_BluetoothInfiShow->insertPlainText("\n");

    /*与设备之间建立服务*/

    m_service=m_control->createServiceObject(QBluetoothUuid(serviceUuid),this);

    if(m_service==NULL)

    {

        ui->plainTextEdit_BluetoothInfiShow->insertPlainText("服务建立失败!\n");

        return;

    }

    else

    {

        ui->plainTextEdit_BluetoothInfiShow->insertPlainText("服务建立成功!\n");

    }

    /*服务状态改变时发出此信号。newState也可以通过state()。*/

    connect(m_service, SIGNAL(stateChanged(QLowEnergyService::ServiceState)),

                this, SLOT(BleServiceServiceStateChanged(QLowEnergyService::ServiceState)));

    /*特性值由事件改变时发出此信号在外设上。 newValue参数包含更新后的值特性*/

    connect(m_service, SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)),

                this, SLOT(BleServiceCharacteristicChanged(QLowEnergyCharacteristic,QByteArray)));

    /*当特征读取请求成功返回其值时,发出此信号。*/

     connect(m_service, SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray)),

                this, SLOT(BleServiceCharacteristicRead(QLowEnergyCharacteristic,QByteArray)));

    /*当特性值成功更改为newValue时,会发出此信号。*/

    connect(m_service, SIGNAL(characteristicWritten(QLowEnergyCharacteristic,QByteArray)),

                this, SLOT(BleServiceCharacteristicWrite(QLowEnergyCharacteristic,QByteArray)));

    /*错误信号*/

    connect(m_service, static_cast<void(QLowEnergyService::*)(QLowEnergyService::ServiceError)>(&QLowEnergyService::error),

         [=](QLowEnergyService::ServiceError newErrorr)

    {

        if(QLowEnergyService::NoError == newErrorr)

        {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("没有发生错误。\n");

        }

        if(QLowEnergyService::OperationError==newErrorr)

        {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("错误: 当服务没有准备好时尝试进行操作!\n");

        }

        if(QLowEnergyService::CharacteristicReadError==newErrorr)

        {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("尝试读取特征值失败!\n");

        }

        if(QLowEnergyService::CharacteristicWriteError==newErrorr)

        {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("尝试为特性写入新值失败!\n");

        }

        if(QLowEnergyService::DescriptorReadError==newErrorr)

        {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("尝试读取描述符值失败!\n");

        }

        if(QLowEnergyService::DescriptorWriteError==newErrorr)

        {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText(" 尝试向描述符写入新值失败!\n");

        }

        if(QLowEnergyService::UnknownError==newErrorr)

        {

             ui->plainTextEdit_BluetoothInfiShow->insertPlainText("与服务交互时发生未知错误!\n");

        }

    });

    if(m_service->state() == QLowEnergyService::DiscoveryRequired)

    {

        m_service->discoverDetails(); //启动服务发现扫描

    }

    else

        searchCharacteristic();

}

//搜索特性

void MainWindow::searchCharacteristic()

{

    if(m_service)

    {

        QList<QLowEnergyCharacteristic> list=m_service->characteristics();

        qDebug()<<"list.count()="<<list.count();

        //characteristics 获取详细特性

        SendMaxMode=list.count();  //设置模式选择上限

        for(int i=0;i<list.count();i++)

        {

            QLowEnergyCharacteristic c=list.at(i);

            /*如果QLowEnergyCharacteristic对象有效,则返回true,否则返回false*/

            if(c.isValid())

            {

//                返回特征的属性。

//                这些属性定义了特征的访问权限。

               if(c.properties() & QLowEnergyCharacteristic::WriteNoResponse || c.properties() & QLowEnergyCharacteristic::Write)

               // if(c.properties() & QLowEnergyCharacteristic::Write)

                {

                    ui->plainTextEdit_BluetoothInfiShow->insertPlainText("具有写权限!\n");

                    m_writeCharacteristic[i] = c;  //保存写权限特性

                    if(c.properties() & QLowEnergyCharacteristic::WriteNoResponse)

//                        如果使用此模式写入特性,则远程外设不应发送写入确认。

//                        无法确定操作的成功,并且有效负载不得超过20个字节。

//                        一个特性必须设置QLowEnergyCharacteristic :: WriteNoResponse属性来支持这种写模式。

//                         它的优点是更快的写入操作,因为它可能发生在其他设备交互之间。

                        m_writeMode = QLowEnergyService::WriteWithoutResponse;

                    else

                        m_writeMode = QLowEnergyService::WriteWithResponse;

                    //如果使用此模式写入特性,则外设应发送写入确认。

                    //如果操作成功,则通过characteristicWritten()信号发出确认。

                    //否则,发出CharacteristicWriteError。

                    //一个特性必须设置QLowEnergyCharacteristic :: Write属性来支持这种写模式。

                }

                if(c.properties() & QLowEnergyCharacteristic::Read)

                {

                    m_readCharacteristic = c; //保存读权限特性

                }

                //描述符定义特征如何由特定客户端配置。

                m_notificationDesc = c.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);

                //值为真

                if(m_notificationDesc.isValid())

                {

                    //写描述符

                    m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0100"));

                  //   m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("FEE1"));

                    ui->plainTextEdit_BluetoothInfiShow->insertPlainText("写描述符!\n");

                }

            }

        }

    }

}

//当控制器成功连接到远程Low Energy设备时,会发出此信号。

void MainWindow::BlueDeviceConnected()

{

    ui->plainTextEdit_BluetoothInfiShow->insertPlainText("成功连接设备!\n");

    //启动发现服务Services

    m_control->discoverServices();

//    QList<QBluetoothUuid> uuid_list=m_control->services(); //获取已经查找成功的服务

//    for(int i=0;i<uuid_list.count();i++)

//    {

//        ui->comboBox_UUID->addItem(uuid_list.at(i).toString()); //添加字符串到comboBox上

//    }

//    if(uuid_list.count()<=0)

//    {

//        ui->plainTextEdit_BluetoothInfiShow->insertPlainText("没有查找到UUID服务!\n");

//    }

}

//当控制器从远程低功耗设备断开时发出此信号。

void MainWindow::BlueDeviceDisconnected()

{

    ui->plainTextEdit_BluetoothInfiShow->insertPlainText("成功断开!\n");

}

//当前选中的服务

void MainWindow::on_comboBox_UUID_currentIndexChanged(const QString &arg1)

{

}

void MainWindow::BleServiceServiceStateChanged(QLowEnergyService::ServiceState s)

{

    //ui->plainTextEdit_BluetoothInfiShow->insertPlainText("服务状态改变时发出此信号!\n");

    if(s == QLowEnergyService::ServiceDiscovered)  //所有细节都已同步

    {

        ui->plainTextEdit_BluetoothInfiShow->insertPlainText("所有细节都已发现!\n");

        searchCharacteristic();

    }

}

//读取到数据

void MainWindow::BleServiceCharacteristicChanged(const QLowEnergyCharacteristic &c,

                                                 const QByteArray &value)

{

   // ui->plainTextEdit_BluetoothInfiShow->insertPlainText("特性值由事件改变时发出此信号在外设上!\n");

    ui->plainTextEdit_BluetoothInfiShow->insertPlainText(QString(value));

    //移动滚动条到底部

    QScrollBar *scrollbar = ui->plainTextEdit_BluetoothInfiShow->verticalScrollBar();

    if(scrollbar)

    {

        scrollbar->setSliderPosition(scrollbar->maximum());

    }

}

void MainWindow::BleServiceCharacteristicRead(const QLowEnergyCharacteristic &c,

                                              const QByteArray &value)

{

  //  ui->plainTextEdit_BluetoothInfiShow->insertPlainText("当特征读取请求成功返回其值时\n");

    //ui->plainTextEdit_BluetoothInfiShow->insertPlainText(QString(value));

}

void MainWindow::BleServiceCharacteristicWrite(const QLowEnergyCharacteristic &c,

                                               const QByteArray &value)

{

    //ui->plainTextEdit_BluetoothInfiShow->insertPlainText("当特性值成功更改为newValue时!\n");

   ui->plainTextEdit_BluetoothInfiShow->insertPlainText(QString(value));

}

//发送模式

void MainWindow::on_pushButton_SendMode_clicked()

{

    boook;

    int data = QInputDialog::getInt(this, tr("获取输入模式"),tr("选择模式:"), 0, 0,SendMaxMode,1, &ok);

    if(ok)

    {

        SendModeSelect=data;

    }

}

(2) mainwindow.h文件代码


#ifndef MAINWINDOW_H

#define MAINWINDOW_H

#include <QMainWindow>

#include <QBluetoothDeviceDiscoveryAgent>

#include <QBluetoothLocalDevice>

#include <QBluetoothSocket>

#include "QListWidgetItem"

#include <QMessageBox>

#include <QBluetoothUuid>

#include <QLowEnergyController>

#include <QBluetoothUuid>

#include <QLowEnergyService>

#include <QScrollBar>

#include <QInputDialog>

namespace Ui {

class MainWindow;

}

class MainWindow : public QMainWindow

{

    Q_OBJECT

public:

    explicit MainWindow(QWidget *parent = 0);

    ~MainWindow();

    QBluetoothDeviceDiscoveryAgent *discoveryAgent;  //这个是指扫描周围蓝牙设备!

    QBluetoothLocalDevice *localDevice; //是指配置获取设备的蓝牙状态信息等!

    QLowEnergyController *m_control;

    QLowEnergyService *m_service;

    QBluetoothDeviceInfo remoteDevice;  //用于保存需要连接的设备的信息

    QLowEnergyCharacteristic m_readCharacteristic; //读特性

    QLowEnergyCharacteristic m_writeCharacteristic[5]; //写特性

    QLowEnergyService::WriteMode m_writeMode;

    QLowEnergyDescriptor m_notificationDesc;

    void searchCharacteristic();

    int SendMaxMode; //发送模式

    int SendModeSelect;//选择发送模式

private slots:

    void on_pushButton_OpenBluetooth_clicked();

    void on_pushButton_CloseBluetooth_clicked();

    void on_pushButton_BluetoothScan_clicked();

    void addBlueToothDevicesToList(const QBluetoothDeviceInfo&);  //发现蓝牙设备的槽函数

    void on_pushButton_DeviceVisible_clicked();

    void on_pushButton_StopScan_clicked();

    void on_pushButton_SendData_clicked();

    void on_pushButton_Clear_clicked();

    void SetStyle(const QString &qssFile); //样式表设置函数

    void on_pushButton_ConnectDev_clicked();

    void on_pushButton_help_clicked();

    void BlueServiceDiscovered(const QBluetoothUuid &gatt);

    void BlueServiceScanDone();   //正在运行的服务发现完成时发出此信号。

    void BlueDeviceConnected();   //当控制器成功连接到远程Low Energy设备时,会发出此信号。

    void BlueDeviceDisconnected();//当控制器从远程低功耗设备断开时发出此信号。

    void on_comboBox_UUID_currentIndexChanged(const QString &arg1); //选中的UUID服务

    void BleServiceServiceStateChanged(QLowEnergyService::ServiceState s);//服务状态改变时发出此信号

    void BleServiceCharacteristicChanged(const QLowEnergyCharacteristic &c, const QByteArray &value); //特性值由事件改变时发出此信号在外设上

    void BleServiceCharacteristicRead(const QLowEnergyCharacteristic &c, const QByteArray &value);   //当特征读取请求成功返回其值时

    void BleServiceCharacteristicWrite(const QLowEnergyCharacteristic &c, const QByteArray &value);//当特性值成功更改为newValue时

    void on_pushButton_SendMode_clicked();

private:

    Ui::MainWindow *ui;

};

#endif // MAINWINDOW_H

(3) mainwindow.ui文件




 13-6-2图

(4) 程序运行效果图




 13-6-3图


技术合作与咨询

QQ:1126626497
关注我长按二维码可识别微信号:xl1126626497



----------------------------------------------------------------------------------------------------------------------
我们尊

鲜花

握手

雷人

路过

鸡蛋

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了