上一章:【STM32WBA55CG开发板】QT蓝牙数据通信应用与交互——1、通信架构搭建
Hi~ 我又回来了。我看了一些其它朋友们做的,都很优秀啊,我也得多多向大佬们学习啊
这次使用Qt Creator设计一个简单的上位机,实现PC和单片机的数据交互,经过了一段时间的踩坑,在正式介绍之前我要说个注意事项:
在windows上开发BT 或者 BLE,必须使用msvc,如果使用MingGW,会搜索不到设备。必须使用QT 5.15.2 或以上的QT版本,不然无法使用高级搜索(有的设备蓝牙搜索不到)
由于仅仅是个通信的框架,所以界面设计的比较简陋,如图:
由于时间仓促,没有封装蓝牙连接的类,所以直接把代码贴出来吧。
1、首先需要搜索蓝牙设备
void Widget::on_pushButtonScan_clicked() { discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); //discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::NoMethod); //discoveryAgent->start(); } //=================================Agent Scan Device================================== //find device... void Widget::BT_deviceDiscovered(const QBluetoothDeviceInfo &device){ QString deviceName=device.name().toUtf8(); QString deviceAddress=device.address().toString(); qDebug("device name:%s\r\n",qPrintable(deviceName)); qDebug("device address:%s\r\n",qPrintable(deviceAddress)); //判断是否是ESP BLE设备 if ((deviceName.indexOf("ST")!=-1) //||(deviceName.indexOf("ESP")!=-1) ) { QString label = QString("%1 %2").arg(deviceName).arg(deviceAddress);//按顺序显示地址和设备名称 deviceList.append(label); //QList<QListWidgetItem *> items = ui->comboBoxDevice->findItems(label, Qt::MatchExactly);//检查设备是否已存在,避免重复添加 //if (items.empty()) {//不存在则添加至设备列表 //QListWidgetItem *item = new QListWidgetItem(label); ui->comboBoxDevice->addItems(deviceList); bleDevInfoList.append(device); } } } //find finished void Widget::BT_scanFinished(){ qDebug("BT discover finished "); isFinishDiscover=true; }
蓝牙搜索到之后,会自动的将搜索到的设备添加到列表中。
2、连接蓝牙,如果连接成功,会将Services添加到第二个列表里。
void Widget::on_pushButtonConnect_clicked() { QThread::msleep(1000); if(isFinishDiscover){ connect_device(!deviceIsConnected); } } //按下连接按键的操作 void Widget::connect_device(bool isConnect){ if(isConnect){ qDebug("正在连接..."); btController=QLowEnergyController::createCentral(bleDevInfoList.at(ui->comboBoxDevice->currentIndex()),this); //扫描目标BLE服务,获取一次触发一次 connect(btController, &QLowEnergyController::serviceDiscovered,this, &Widget::ble_ServiceDiscovered); //扫描完成之后会触发此信号 connect(btController, &QLowEnergyController::discoveryFinished,this, &Widget::ble_ServiceScanDone); //连接出错 connect(btController, static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error), this, [this](QLowEnergyController::Error error) { qDebug("connect device error!!"); Q_UNUSED(error); QMessageBox::information(this,tr("Info"),tr("Cannot connect to remote device.")); }); //连接成功 connect(btController, &QLowEnergyController::connected, this, [this]() { qDebug("connected device successed!!"); QMessageBox::information(this,tr("Info"),tr("Controller connected. Search services...")); btController->discoverServices(); }); //断开连接 connect(btController, &QLowEnergyController::disconnected, this, [this]() { qDebug("disconnected device!!"); QMessageBox::information(this,tr("Info"),tr("LowEnergy controller disconnected")); }); //事件更新信号 connect(btController, &QLowEnergyController::stateChanged, this, &Widget::ble_SeviceStateChanged); //建立连接,若连接没有出错,会自动搜索连接设备的服务 btController->connectToDevice(); }else{ btController->disconnectFromDevice(); delete btController; ui->comboBoxDevice_Service->clear(); ui->comboBoxDevice_Char->clear(); } } //获取Service服务被找到 void Widget::ble_ServiceDiscovered(const QBluetoothUuid & serviceUuid) { bleServiceUUIDList.append(serviceUuid); QString uuid =serviceUuid.toString(); ui->comboBoxDevice_Service->addItem(uuid); qDebug("Service find %s!",qPrintable(uuid)); } //Service 获取完毕 void Widget::ble_ServiceScanDone(){ qDebug("Service scan done!"); }
3、通过Services 创建服务。如果创建成功,会自动扫描特征,并且再次添加到列表
//服务状态值改变会触发此槽函数 //当蓝牙服务被扫描到时状态值会发生改变,改变为QLowEnergyService::ServiceDiscovered void Widget::ble_ServiceStateChanged(QLowEnergyService::ServiceState s) { Q_UNUSED(s); QLowEnergyCharacteristic bleCharacteristic; if(s == QLowEnergyService::ServiceDiscovered) { //QMessageBox::information(NULL,"tips",QObject::tr("Service synchronization")); //TODO.... m_charList = m_service->characteristics(); for(int i=0; i<m_charList.size(); i++){ QString uuid=m_charList.at(i).value(); QString charName=m_charList.at(i).name(); ui->comboBoxDevice_Char->addItem(charName+uuid); } } } //创建服务 void Widget::on_pushButtonCreatService_clicked() { QThread::msleep(1000); QBluetoothUuid m_serviceUUID; m_serviceUUID=bleServiceUUIDList.at(ui->comboBoxDevice_Service->currentIndex()); m_service=btController->createServiceObject(m_serviceUUID); if(m_service==NULL){ qDebug("创建ble服务失败"); }else{ qDebug("创建ble服务成功"); //同步服务 if (!m_service) { QMessageBox::information(NULL,"error","Cannot create service for uuid"); return; } //监听服务状态值发生改变 connect(m_service, &QLowEnergyService::stateChanged, this,&Widget::ble_ServiceStateChanged); //特征值发生改变 connect(m_service, &QLowEnergyService::characteristicChanged, this,&Widget::ble_CharacteristicChanged); //char 成功读 connect(m_service, &QLowEnergyService::characteristicRead, this,&Widget::ble_ServiceCharacteristicRead); //char 成功写 connect(m_service, SIGNAL(characteristicWritten(QLowEnergyCharacteristic,QByteArray)),this, SLOT(ble_ServiceCharacteristicWrite(QLowEnergyCharacteristic,QByteArray))); QThread::msleep(1000); //服务详情发现函数 m_service->discoverDetails(); } }
4、最后就是特征的一些操作了,包括读写,notify等等
//=================================Char operation================================== void Widget::on_pushButton_Read_clicked() { m_character=m_charList.at(ui->comboBoxDevice_Char->currentIndex()); m_service->readCharacteristic(m_character); } void Widget::on_pushButton_Write_clicked() { QString str=ui->lineEdit_Write->text(); QByteArray byte = str.toLatin1(); m_character=m_charList.at(ui->comboBoxDevice_Char->currentIndex()); m_service->writeCharacteristic(m_character,byte); } void Widget::ble_CharacteristicChanged(QLowEnergyCharacteristic characteristic, QByteArray newValue) { Q_UNUSED(characteristic); int len = newValue.size(); if(len > 0){ qDebug(qPrintable(QString("rec:").append(newValue.toHex()))); int val=newValue.data()[0]; QString nowValueStr=QString::number(val); //ui->label_Notification->setText(nowValueStr); } } //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 Widget::ble_ServiceCharacteristicWrite(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 Widget::ble_ServiceCharacteristicRead(const QLowEnergyCharacteristic &c, const QByteArray &value) { QString charUUID = c.uuid().toString(); QString valuetoHexString = value.toHex();//16进制输出信息 QString readStr = QString(value); qDebug(qPrintable("read char:"+readStr)); qDebug(qPrintable("read char raw:"+value)); ui->label_Read->setText(readStr); } /* * * Unknown = 0x00, Broadcasting = 0x01, Read = 0x02, WriteNoResponse = 0x04, Write = 0x08, Notify = 0x10, Indicate = 0x20, WriteSigned = 0x40, ExtendedProperty = 0x80 * */ void Widget::on_pushButtonNotify_clicked() { int index=ui->comboBoxDevice_Char->currentIndex(); if(m_service!=NULL){ if(ui->pushButtonNotify->text()=="打开通知"){ ui->pushButtonNotify->setText("关闭通知"); ui->pushButtonNotify->setStyleSheet("color: rgb(255, 0, 0);"); m_descriptor = m_charList.at(index).descriptor(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration); if(m_descriptor.isValid()){ m_service->writeDescriptor(m_descriptor,QByteArray::fromHex("0100")); } }else if(ui->pushButtonNotify->text()=="关闭通知"){ ui->pushButtonNotify->setText("打开通知"); ui->pushButtonNotify->setStyleSheet("color: rgb(0, 0 0);"); m_descriptor = m_charList.at(index).descriptor(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration); if(m_descriptor.isValid()){ m_service->writeDescriptor(m_descriptor,QByteArray::fromHex("0000")); } } }else{ } }
目前已经可以实现基本通信。完整代码已经上传到gitee上了,需要的可以参考一下:https://gitee.com/enderman1/eepw/tree/master/ble_rw_base
基于此,下一个帖子我会做一个QT + STM32 的具体应用,敬请期待吧!