win32下Qt5BLE蓝牙开发笔记

2023-11-19

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个特征,分别是读、写、特征值通知这三个。如果服务拥有读特征,就可以从此服务中读消息,如果服务拥有写特征,就可以发送消息给服务,如果服务有特征值改变通知这个特征,则当服务的特征值发生改变时会发送消息通知。

  1. 蓝牙的权限问题(Windows平台下不存在此问题,可以忽略,在安卓平台下不管是Qt还是Java都需要考虑这个问题)
  2. 搜索周边蓝牙
  3. 连接蓝牙
  4. 获取服务
  5. 获取服务下特征,每个服务下都有0-3个特性,分别对应了读,写,以及特性发生改变时是否通知
  6. 与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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

win32下Qt5BLE蓝牙开发笔记 的相关文章

随机推荐

  • 关系型数据库的规范化

    规范化是通过修改表以减少冗余和矛盾的一系列动作 关系型数据库定义了3中范式 第一范式 列仅包含原子值 没有重复的组 第二范式 满足第一范式 非部分函数依赖 如果表中一些组合键的 但不是全部 值确定了一个非键列的值 则表包含部分函数依赖 第二
  • LeetCode Java 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。

    给定一个整数数组 nums 和一个目标值 target 请你在该数组中找出和为目标值的那两个整数 并返回他们的数组下标 你可以假设每种输入只会对应一个答案 但是 你不能重复利用这个数组中同样的元素 方法一 遍历 看到这个题便想到数组遍历 就
  • 商城项目 pc----商品详情页

    目录 vue路由滚动行为 排他思想 放大镜 加入购物车操作 项目实战 Promise 特点 用法 then 执行顺序 拓展 async await Promise优缺点 Promise方法 浏览器缓存 为什么需要本地存储呢 window s
  • 思科路由器IPv6各种路由协议配置

    一 基础配置 R1 Router gt ena Router conf t Router config host R1 R1 config int g0 0 R1 config if ipv add 2001 3 1 64 R1 confi
  • Java多线程(四):什么是死锁以及如何解决死锁

    目录 1 什么是死锁 2 死锁产生的原因 3 如何解决死锁问题 3 1 改变环路等待条件 3 2 破坏请求并持有条件 1 什么是死锁 死锁 是指两个或两个以上的进程在执行过程中 由于竞争资源或者由于彼此通信而造成的一种阻塞的现象 若无外力作
  • 【微信小程序】定时器超时处理设置方法【setInterval()和setTimeout()】

    2020年2月13日 0次阅读 共550个字 0条评论 0人点赞 QueenDekimZ Set Timeout Solution setTimeout和setInterval 函数都属于定时任务 一 settimeout延迟一段时间执行函
  • css中float用法

    float浮动 指将指定元素悬浮于所在整体之上 即将垂直排列的元素转换为水平同行显示 平时写出的HTML是具有先后顺序的 对于这个顺序我们称之为标准流 而浮动则是脱离标准流的另一个独立标准 下面给出float定义 float left 左浮
  • 阿里云大佬告诉你为什么学不会设计模式,归根到底还是方法不对

    最近总有读者在后台跟我说 工作几年 自己的代码质量似乎没有什么提升 我觉得他的情况非常典型 很多人应该或多或少都有过类似的经历 毕业几年 几乎一直在做复制黏贴的工作 偶尔会遇到原有业务扩展的需求 想简单应付一下完事的话 也不难 无非就是多加
  • 查询水果价格 (15分)

    给定四种水果 分别是苹果 apple 梨 pear 桔子 orange 葡萄 grape 单价分别对应为3 00元 公斤 2 50元 公斤 4 10元 公斤 10 20元 公斤 首先在屏幕上显示以下菜单 1 apple 2 pear 3 o
  • Hadoop学习——MapReduce的简单介绍及执行步骤

    MapReduce概述 MapReduce是一个分布式的计算框架 编程模型 最初由由谷歌的工程师开发 基于GFS的分布式计算框架 后来Cutting根据 Google Mapreduce 设计了基于HDFS的Mapreduce分布式计算框架
  • IDEA修改内存未生效原因和解决

    修改IDEA安装目录下的idea64 exe vmoptions server Xms1024m Xmx2048m XX ReservedCodeCacheSize 2048m 发现IDEA的内存修改并未生效 右下角显示依然是974M 原因
  • windows下进程间通信的(13种方法)

    Windows进程间的通信 迎风的祺 博客园 windows下进程间通信的 13种方法 phymat nico的专栏 CSDN博客 windows进程间通信
  • eclipse报错 parameterized types are only available if source level is 1.5 or greater

    preface 好久没有更新 blog 了 最近在 写新的项目 今天在用eclipse 出现了之前遇到的问题 这里记录下 问题 eclipse 控制台 报错 parameterized types are only available if
  • CUDA+OPENCV+PYTHON tensorflow 源码环境搭建

    CUDA OPENCV PYTHON tensorflow 源码环境搭建 接上文caffe环境安装 https blog csdn net u012350025 article details 104589982 主机环境ubuntu18
  • 基于C++的带权无向图的实现 (五)- 连通图和连通分量

    该系列文章是本人整理的有关带权无向图的数据结构和算法的分析与实现 若要查看源码可以访问我的github仓库 如有问题或者建议欢迎各位指出 目录 基于C 的带权无向图的实现 一 数据结构 基于C 的带权无向图的实现 二 遍历算法 基于C 的带
  • 台式计算机怎么看有没有开独显,台式机独立显卡怎么样打开

    给台式机插入了独立显卡 但不会打开怎么办呢 下面由学习啦小编给你做出详细的台式机独立显卡打开方法介绍 希望对你有帮助 台式机独立显卡打开方法一 把显示器的数据连接线接到独立显卡上 用的就是独立显卡 把显示器的数据线连接在主板的显示输出口上
  • MyEclipse8.5的安装过程

    1 双击进行安装 点Next接受 选择好路径后 等待安装完毕 2 选择路径 3 进入工作台如下图 4 配置Tomcat 工具栏 Window Preferences 5 Myeclipse Servers Tomcat 选择版本 勾选Ena
  • 2029:【例4.15】水仙花数

    2029 例4 15 水仙花数 时间限制 1000 ms 内存限制 65536 KB 提交数 1247 通过数 720 题目描述 求100 999中的水仙花数 若三位数ABC ABC A3 B3 C3 则称ABC为水仙花数 例如153 13
  • springboot2.x默认采用cglib代理,以及配置jdk动态代理的方法

    众所周知 springboot开启AOP需要在启动类加上注解 EnableAspectJAutoProxy 但开发过程中发现即使不加 EnableAspectJAutoProxy 注解 bean还是被代理过 而且是Cglib代理对象 此时在
  • win32下Qt5BLE蓝牙开发笔记

    BLE简介 BLE蓝牙是蓝牙2 0以上的蓝牙模块 经典蓝牙是蓝牙2 0以下的蓝牙 蓝牙分为客户端和服务器两端 经典蓝牙可以通过socket编程进行客户端与服务器之间的通信 与网络socket相似 BLE蓝牙则无法使用这种方式进行通信 BLE