Qt for Android 使用BLE串口蓝牙发送数据

最近搞毕设,买了个串口BLE蓝牙想配合Qt for Android做一个自制的遥控器,参考了很多qt蓝牙的文章,最后弄成apk发现连接不了我的蓝牙,折腾了好久发现Qt官方的蓝牙套接字只适用于SPP传输的蓝牙,而BLE蓝牙则有专门实现代码,说实话比蓝牙套接字复杂了好几百倍,而且官方的示例工程是一些有特殊功能的蓝牙,不太能移植到单单发送数据的串口蓝牙。由于我对蓝牙研究也不是很深,参考了很多大佬的博客,发现了一个比较好,功能相近的项目,下面是链接

Qt for Androidble蓝牙串口助手

但是进去后界面排版有点不好看,也没有源码可以提供,并且我只是要一个发送数据的功能,所以经过改造,实现了可以发送数据的功能了,下面贴下关键步骤的代码

    //搜索设备
    m_deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
    m_deviceDiscoveryAgent->setLowEnergyDiscoveryTimeout(20000);	//设置超时时间
    connect(m_deviceDiscoveryAgent,SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), this,
            SLOT(addDevice(QBluetoothDeviceInfo)));//搜索到设备操作
    connect(m_deviceDiscoveryAgent, SIGNAL(finished()), this, SLOT(scanFinished()));//搜索结束
    connect(this, SIGNAL(returnAddress(QBluetoothDeviceInfo)), this, SLOT(createCtl(QBluetoothDeviceInfo)));				//开始连接目标设备
    connect(ui->bluemsg,SIGNAL(cursorPositionChanged()),this,SLOT(autoScroll()));	//信息一直在最下面显示

    m_deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);	//开始进行搜索
    ui->blue_search->setEnabled(false);

上面相当于是初始化了,头文件需要什么也可以从代码里看到。

void MainWindow::addDevice(const QBluetoothDeviceInfo &info)
{
    if (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration)
    {//判断是否是BLE设备
        QString label = QString("%1 %2").arg(info.address().toString()).arg(info.name());//按顺序显示地址和设备名称
        QList<QListWidgetItem *> items = ui->bluelist->findItems(label, Qt::MatchExactly);//检查设备是否已存在,避免重复添加
        if (items.empty())
        {//不存在则添加至设备列表
            QListWidgetItem *item = new QListWidgetItem(label);
            ui->bluelist->addItem(item);
            m_devices.append(info);
        }
    }
}

上面的是搜索的时候查找到设备会触发的槽函数,即将搜索到的设备添加进listwidget就可以了

void MainWindow::on_blue_connect_clicked(bool checked)
{
    if(ui->bluelist->currentItem()->text().isEmpty()){}//确认选取有效
    else
    {
        QString bltAddress = ui->bluelist->currentItem()->text().left(17);//获取选择的地址
        for (int i = 0; i<m_devices.count(); i++)
        {
            if(m_devices.at(i).address().toString().left(17) == bltAddress)//地址对比
            {
                QBluetoothDeviceInfo choosenDevice = m_devices.at(i);
                emit returnAddress(choosenDevice);//发送设备信息
                m_deviceDiscoveryAgent->stop();//停止搜索服务
                break;
            }
        }
    }
}

这里是连接前的函数,我这里通过选中listwidget的项后按button触发,自己也可以双击listwidget实现。代码中会发送一个带设备信息的信号,然后开始连接

void MainWindow::createCtl(QBluetoothDeviceInfo info)
{
    m_control = QLowEnergyController::createCentral(info, this);
    connect(m_control, &QLowEnergyController::serviceDiscovered,
            this,&MainWindow::serviceDiscovered);	//发现了目标设备后触发的操作

    connect(m_control, &QLowEnergyController::discoveryFinished,
            this, &MainWindow::serviceScanDone);	//配置服务

    connect(m_control, static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),
            this, [this](QLowEnergyController::Error error) {
        Q_UNUSED(error);
        ui->bluemsg->append("Cannot connect to remote device.");	//错误连接
        });

    connect(m_control, &QLowEnergyController::connected, this, [this]() {
        ui->bluemsg->append("Controller connected. Search services...\n");	//成功连接触发的槽函数
        m_control->discoverServices();
        isconnected=true;
    });

    connect(m_control, &QLowEnergyController::disconnected, this, [this]() {
        ui->bluemsg->append("LowEnergy controller disconnected");	//错误连接
    });

    //connect
    ui->bluemsg->append("start to connect\n");

    m_control->connectToDevice();			//开始连接目标设备
}

开始连接又是一堆槽函数,所以BLE写起来确实比蓝牙套接字麻烦很多。关于这段就只能建议去参考下官方或者其他大佬的博客了

void MainWindow::serviceDiscovered(const QBluetoothUuid &gatt)
{
        ui->bluemsg->insertPlainText(QString("%1").arg(gatt.toString()));
        m_foundHeartRateService = true;
}

推荐这个槽函数这样写,这样就能测试自己的蓝牙可以使用什么Uuid,然后下一步直接抄显示的对应Uuid就行了

void MainWindow::serviceScanDone()
{
    //setInfo("Service scan done.");
    ui->bluemsg->append("Service scan done.");

    m_service = m_control->createServiceObject(QBluetoothUuid(serviceUuid),
                                                       this);//这里的serviceUuid就是自己找的,可以通过上面方法找
    if(m_service)
    {
        ui->bluemsg->append("服务建立成功\n");

        m_service->discoverDetails();
    }
    else
    {
        ui->bluemsg->append("Service not found");
        return;
    }
    connect(m_service, &QLowEnergyService::stateChanged, this,
            &MainWindow::serviceStateChanged);
    connect(m_service, &QLowEnergyService::characteristicChanged, this,
            &MainWindow::BleServiceCharacteristicChanged);
    connect(m_service, &QLowEnergyService::characteristicRead, this,
            &MainWindow::BleServiceCharacteristicRead);
    connect(m_service, SIGNAL(characteristicWritten(QLowEnergyCharacteristic,QByteArray)),
            this, SLOT(BleServiceCharacteristicWrite(QLowEnergyCharacteristic,QByteArray)));

    if(m_service->state()==QLowEnergyService::DiscoveryRequired)
    {
        m_service->discoverDetails();
    }
    else
    {
        searchCharacteristic();
    }
}

又是一堆连接槽函数,其实就是对BLE的服务配置(因为不同BLE功能太多了-_-),一开始的Uuid那部分要填写正确,上面那一部就能准确知道自己BLE有什么Uuid可用,但是关键发送数据其实只要获取写权限就可以了,读数据也是一样。不需要很多杂七杂八的功能,所以其实写出来也并没有做很多操作

void MainWindow::serviceStateChanged(QLowEnergyService::ServiceState s)
{
    if(s == QLowEnergyService::ServiceDiscovered)
    {
        ui->bluemsg->append("服务已同步\n");
        searchCharacteristic();
    }

}

只要能显示这个“服务已同步”就说明你配置的Uuid是可用的

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->bluemsg->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->bluemsg->insertPlainText("写描述符!\n");
                    }
                }
            }
    }
}

这里就是获取读写权限的特性了

void MainWindow::SendMsg(QString text)
{
    QByteArray array=text.toLocal8Bit();

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

void MainWindow::BleServiceCharacteristicWrite(const QLowEnergyCharacteristic &c, const QByteArray &value)
{
    ui->bluemsg->append(QString("指令%1发送成功").arg(QString(value)));
}

上面那个函数就是发送数据的函数了,可以自己改造成其他类型,SendModeSelect为0就可以了,下面那段就是发送成功后信号会触发的槽函数

其他的没有写出来的函数基本都是些跟我要的功能的,可以自己写输出信息来验证步骤的成功。

下面是成功连接的图片
在这里插入图片描述

发布了6 篇原创文章 · 获赞 0 · 访问量 235

猜你喜欢

转载自blog.csdn.net/qq_35342292/article/details/104170372