注意事项:

1.qt版本必须在5.14.2及以上

2.开发环境必须使用msvc 如果使用MingGW会导致无法发现任何设备

3. 必须添加qt中 bluetooth 库,qt中开发在.pro中添加 QT += quick bluetooth,vs中开发在创建qt项目时勾选bluetooth即可

4.使用bluetooth流程为 先使用QBluetoothDeviceDiscoveryAgent 扫描设备,获取到扫描的设备之后通过QLowEnergyController连接设备得到需要连接设备服务的uuid,然后通过服务的uuid连接设备,之后在获取特征(Write,Read,Notify),每一个特征都有对应的独立uuid,再通过特征去进行对应的操作

具体流程

1.扫描设备

bool Device::DevicesScan(int timeout)
{
        m_deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
        m_deviceDiscoveryAgent->setLowEnergyDiscoveryTimeout(timeout);    //设置超时时间
        connect(m_deviceDiscoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), this,
            SLOT(addDevice(QBluetoothDeviceInfo)));//搜索到设备操作
        connect(m_deviceDiscoveryAgent, SIGNAL(finished()), this, SLOT(scanFinished()));//搜索结束
        connect(m_deviceDiscoveryAgent, QOverload<QBluetoothDeviceDiscoveryAgent::Error>::of(&QBluetoothDeviceDiscoveryAgent::error),
            this, &Device::deviceScanError);
        //connect(ui->bluemsg,SIGNAL(cursorPositionChanged()),this,SLOT(autoScroll()));    //信息一直在最下面显示
        m_deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);    //开始进行搜索
        return true;
    }


void Device::addDevice(const QBluetoothDeviceInfo &info) //每当扫描出一个设备就会触发该函数
{
    if (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration)
    {//判断是否是BLE设备
        QString address = info.address().toString();
        QString name = info.name();
        QString uuid = info.deviceUuid().toString();
        QString text = QString("address :%1,name :%2,uuid :%3").arg(address).arg(name).arg(uuid);
        QHash <quint16, QByteArray> s = info.manufacturerData();
        QHash <quint16, QByteArray>::const_iterator i = s.constBegin();
        while (i != s.constEnd()) {
                qDebug() << text << text << "key:" << i.key() << "value:" << i.value();
                m_devices.append(info);
            i++;
        }
    }
}

void Device::scanFinished() //当扫描完成之后就会触发该函数
{

}
 

2.连接设备

注意事项:切忌不可在扫描设备对应的uuid过程中连接对应的uuid然后进行QLowEnergyService::discoverDetails操作,这样导致discoverDetails程序没有任何反应直接卡死,一定要在采用QLowEnergyController::discoveryFinished触发之后QTimer::singleShot(1000, [=] { this->serviceScanDone(); }方式调用


void Device::ConnectDevice(QString address) {
    for (int i = 0; i < m_devices.count(); i++)
    {
        if (m_devices.at(i).address().toString().left(17) == address)//地址对比
        {
            QBluetoothDeviceInfo choosenDevice = m_devices.at(i);
            ControllerConnect(choosenDevice);//发送设备信息
            m_deviceDiscoveryAgent->stop();//停止搜索服务            
            break;
        }
    }
}


void Device::ControllerConnect(QBluetoothDeviceInfo info)
{
 
    m_control = QLowEnergyController::createCentral(info, this);
          //开始连接目标设备
    
    connect(m_control, &QLowEnergyController::serviceDiscovered,
        this, &Device::serviceDiscovered);    //发现了目标设备后触发的操作

    connect(m_control, &QLowEnergyController::discoveryFinished, [=] {
            QTimer::singleShot(1000, [=] { this->serviceScanDone(); });
        });    //配置服务
    connect(m_control, &QLowEnergyController::disconnected,
        this, &Device::deviceDisconnected);
    //connect(m_control, static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),
    //    this, [this](QLowEnergyController::Error error) {
    //        Q_UNUSED(error);
    //    });

    connect(m_control, &QLowEnergyController::connected, this, &Device::ControllerConnected);

    connect(m_control, &QLowEnergyController::disconnected, this, &Device::ControllerDisconnected);

    m_control->connectToDevice();
  
}


void Device::ControllerConnected() {
    //成功连接触发的槽函数    
    m_control->discoverServices();
}
void Device::ControllerDisconnected() {
    //错误连接
    
}

void Device::serviceScanDone() {
    for (auto nextlist : m_BleHandleList) {
        nextlist->CreateServic();
    }
}

void Device::serviceDiscovered(const QBluetoothUuid& gatt)
{
    try {
        QList <QBluetoothUuid> uuids = m_control->services();
        //ui->bluemsg->insertPlainText(QString("%1").arg(gatt.toString()));
        //m_foundHeartRateService = true;

        BleHandle* ble = new BleHandle(m_control, gatt);
        m_BleHandleList.append(ble);
    }
    catch (...) {
        qDebug() << "serviceDiscovered error";
    }
    
}

3.连接对应的uuid服务,获取对应的特征

备注:Notify的特征的数据回传触发的是QLowEnergyService::characteristicChanged,并不是QLowEnergyService::characteristicRead


bool BleHandle::CreateServic() {
    m_service = m_control->createServiceObject(QBluetoothUuid(serviceUuid), this);
    if (!m_service)
    {
        return false;
    }
    connect(m_service, &QLowEnergyService::stateChanged, this,&BleHandle::serviceStateChanged);
    connect(m_service, &QLowEnergyService::characteristicChanged, this,&BleHandle::BleServiceCharacteristicChanged);
    connect(m_service, SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray)),
        this, SLOT(BleServiceCharacteristicWrite(QLowEnergyCharacteristic, QByteArray)));
    connect(m_service, SIGNAL(characteristicRead(QLowEnergyCharacteristic, QByteArray)),
        this, SLOT(BleServiceCharacteristicRead(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();
    }
    
    return true;
}


void BleHandle::BleServiceCharacteristicChanged(const QLowEnergyCharacteristic& c, const QByteArray& value) {//特征为Notify收到ble回传的数据会触发该函数

}

void BleHandle::serviceStateChanged(QLowEnergyService::ServiceState s) {
    qDebug() << "serviceStateChanged:" << s;
    if (s == QLowEnergyService::ServiceDiscovered)
    {
        //        ui->bluemsg->append("服务已同步\n");
       // emit fromBle("服务已同步");

        searchCharacteristic();
    }
}

void BleHandle::BleServiceCharacteristicRead(const QLowEnergyCharacteristic& c, const QByteArray& value) { //特征为red时收到ble回传的数据会触发该函数

}


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


void BleHandle::searchCharacteristic()
{
    if (m_service)
    {
        QList<QLowEnergyCharacteristic> list = m_service->characteristics();
        qDebug() << "list.count()=" << list.count() << "uuid" << m_service->serviceUuid().toString();
        //characteristics 获取详细特性
        SendMaxMode = list.count();  //设置模式选择上限
        for (int i = 0; i < list.count(); i++)
        {
            
            qDebug() << "index " << i << "uuid" << list.at(i).uuid().toString();
            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 = c;  //保存写权限特性
                    if (c.properties() & QLowEnergyCharacteristic::WriteNoResponse)
                    {
                       //                        如果使用此模式写入特性,则远程外设不应发送写入确认。
                      //                        无法确定操作的成功,并且有效负载不得超过20个字节。
                      //                        一个特性必须设置QLowEnergyCharacteristic :: WriteNoResponse属性来支持这种写模式。
                      //                         它的优点是更快的写入操作,因为它可能发生在其他设备交互之间。
                        m_writeMode = QLowEnergyService::WriteWithoutResponse;
                        qDebug() << "WriteWithoutResponse " << "uuid" << list.at(i).uuid().toString();
                    }
                    else
                    {
                        //如果使用此模式写入特性,则外设应发送写入确认。
                        //如果操作成功,则通过characteristicWritten()信号发出确认。
                        //否则,发出CharacteristicWriteError。
                        //一个特性必须设置QLowEnergyCharacteristic :: Write属性来支持这种写模式。
                        m_writeMode = QLowEnergyService::WriteWithResponse;
                        qDebug() << "WriteWithResponse " << "uuid" << list.at(i).uuid().toString();
                    }
                 
                }

                if (c.properties() & QLowEnergyCharacteristic::Read)
                {
                    m_readCharacteristic = c; //保存读权限特性
                    qDebug() << "Read " << "uuid" << list.at(i).uuid().toString();
                }
                
                if (c.properties() & QLowEnergyCharacteristic::Notify)
                {
                    QLowEnergyCharacteristicData* character = new QLowEnergyCharacteristicData();
                    character->setUuid(c.uuid());
                    character->setValue(c.value());
                    character->setProperties(c.properties());
                    
                    m_notifyCharacteristic = c; //保存通知权限特性
                    qDebug() << "Notify " << "uuid" << list.at(i).uuid().toString();
                }
                //描述符定义特征如何由特定客户端配置。
                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 BleHandle::SendMsg(QString text) //发送消息
{
    QByteArray array = text.toLocal8Bit();

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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐