蓝牙客户端QBluetoothSocket的使用——Qt For Android

news2024/11/19 13:14:49

了解蓝牙

经典蓝牙和低功耗蓝牙差异

经典蓝牙(Bluetooth Classic):分为基本速率/增强数据速率(BR/EDR), 79个信道,在2.4GHz的(ISM)频段。支持点对点设备通信,主要用于实现无线音频流传输,已成为无线扬声器、耳机和车载娱乐系统背后的标准无线电协议。经典蓝牙还支持数据传输程序,包括移动打印。

低功耗蓝牙(Bluetooth Low Energy):为低功耗操作而设计的。工作在2.4GHz无授权ISM频段,共40个信道传输数据。蓝牙LE支持多种通信拓扑,点对点,广播,Mesh。使蓝牙能够支持创建可靠的、大规模的网络。LE也被广泛用于设备定位技术,满足高精度室内定位服务的需求。

经典蓝牙和低功耗蓝牙差异 - 知乎

蓝牙的配对和连接的建立过程

taodudu.cc/news/show-3161112.html?action=onClick

Qt蓝牙模块

支持的平台

 Qt 5.14 adds a native Win32 port supporting Classic Bluetooth on Windows 7 or newer, and Bluetooth LE on Windows 8 or newer. It must be enabled at build time by configuration option -native-win32-bluetooth. The UWP backend is used by default if this option is not set and the Win32 target platform supports the required UWP APIs (minimal requirement is Windows 10 version 1507, with slightly improved service discovery since Windows 10 version 1607).

使用

环境

版本:Qt15.15.2

语言:c++

平台:Android 11

蓝牙类型:经典蓝牙(Bluetooth Classic)

代码和说明

使用前在pro文件加载蓝牙模块

QT += bluetooth

大家都有使用蓝牙的经历,一般步骤是:检查蓝牙状态(是否打开)->查找附近的设备->配对->连接->数据的传输,下面就按照这个流程介绍代码。

  • 检查蓝牙状态(是否打开)

QBluetoothLocalDevice提供了获取和设置本地蓝牙设备状态的功能。 

可通过

QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const

来获取当前主机的模式,这里只判断是否是未打开,若未打开,可用powerOn() 打开蓝牙,不过调用后,立马再调用创建连接会失败,可能硬件还没准备好。

   QBluetoothLocalDevice* m_pLocalDev;
   m_pLocalDev = new QBluetoothLocalDevice(this);

   //检查蓝牙是否未打开
   if(m_pLocalDev->hostMode()==QBluetoothLocalDevice::HostPoweredOff)
    {
        m_pLocalDev->powerOn(); //打开蓝牙
        emit errorBTDevPowerOff();
        return false;
    }

  •  查找附近的蓝牙设备

 QBluetoothDeviceDiscoveryAgent 类可发现附近的蓝牙设备

void BluetoothDevice::BluetoothDevice()
{
    ...
   //初始化

    QBluetoothDeviceDiscoveryAgent* m_pDevDiscoveryAgent;
    m_pDevDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);


    connect(m_pDevDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered,     this, &BluetoothDevice::onDeviceDiscovered);
    connect(m_pDevDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &BluetoothDevice::onDiscoverFinished);
    connect(m_pDevDiscoveryAgent, QOverload<QBluetoothDeviceDiscoveryAgent::Error>::of(&QBluetoothDeviceDiscoveryAgent::error),
            this, &BluetoothDevice::onDiscoverFinished);

   ...
}


void BluetoothDevice::refreshBTDevices()
{
    ...
    //开始查找附近蓝牙设备,根据需求这里限定为经典蓝牙
    m_pDevDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::ClassicMethod);

}

//查找到的蓝牙设备的信息
void BluetoothDevice::onDeviceDiscovered(const QBluetoothDeviceInfo &device)
{
    //MAC地址
    const QString& macAddr = device.address().toString();
    //设备名称
    const QString& name= device.name();

   //判读检测到设备是否与本地设备配对
    bool isPaired = false;
   if (pairingStatus == QBluetoothLocalDevice::Paired || pairingStatus == 
    QBluetoothLocalDevice::AuthorizedPaired )
       isPaired =true;
   
} 
//查找结束
void BluetoothDevice::onDiscoverFinished()
{
    emit discoveryFinished();
}

 上方确实获取一些附近的蓝牙设备,但是经常获取不全。情况是这样的:我的手机可查找到待开发设备(后续称设备)的蓝牙,但是设备没有查找到手机蓝牙,所以然后我用手机主动发起配对,是可以成功配对的,后续可以收发文件或者信息。在配对成功后,设备是可以看到我手机的名称和蓝牙MAC地址的,但是QBluetoothDeviceDiscoveryAgent 类仍无法查找到我的手机。

设备可查找到,Qt不行,所以尝试使用Java的方法,直接获取配对设备列表,这个方法是可以的:

package org.qtproject.example.testBluetooth;

import org.qtproject.qt5.android.bindings.QtActivity;

import android.bluetooth.*;
import java.util.Set;
import java.lang.String;
import java.util.ArrayList;


public class MainActivity extends QtActivity
{

    ... 
    public String[] getBondedDevices(boolean isBLE)
    {
        BluetoothAdapter BTAdapter = BluetoothAdapter.getDefaultAdapter();
        Set<BluetoothDevice> bondedDevice = BTAdapter.getBondedDevices();
        ArrayList<String> list = new ArrayList<String>();
        for (BluetoothDevice bt : bondedDevice)
        {
            int deviceType = bt.getType();
            if (!isBLE && (deviceType == BluetoothDevice.DEVICE_TYPE_CLASSIC || deviceType == BluetoothDevice.DEVICE_TYPE_DUAL))
                list.add(bt.getAddress() + " " + bt.getName());
            else if (isBLE && (deviceType == BluetoothDevice.DEVICE_TYPE_LE || deviceType == BluetoothDevice.DEVICE_TYPE_DUAL))
                list.add(bt.getAddress() + " " + bt.getName());
        }
        String[] result = (String[]) list.toArray(new String[list.size()]);
        return result;
    }


}

AndroidManifest.xml 修改

由于java中继承了QtActivity,所以xml中名字要进行更改

<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.example.testBluetooth.MainActivity" android:label="configSignal" android:screenOrientation="unspecified" android:launchMode="singleTop">

c++中调用

#ifdef Q_OS_ANDROID
void BluetoothDevice::getBondedTarget()
{
    QAndroidJniEnvironment env;
    QtAndroid::PermissionResult r = QtAndroid::checkPermission("android.permission.ACCESS_FINE_LOCATION");
    if(r == QtAndroid::PermissionResult::Denied)
    {
        QtAndroid::requestPermissionsSync(QStringList() << "android.permission.ACCESS_FINE_LOCATION");
        r = QtAndroid::checkPermission("android.permission.ACCESS_FINE_LOCATION");
        if(r == QtAndroid::PermissionResult::Denied)
        {
            qDebug() << "failed to request";
        }
    }
    qDebug() << "has permission";
    QAndroidJniObject array = QtAndroid::androidActivity().callObjectMethod("getBondedDevices", "(Z)[Ljava/lang/String;", false);
    int arraylen = env->GetArrayLength(array.object<jarray>());
    qDebug() << "arraylen:" << arraylen ;

    m_bondedDevices.clear();

    for(int i = 0; i < arraylen; i++)
    {
        QString info = QAndroidJniObject::fromLocalRef(env->GetObjectArrayElement(array.object<jobjectArray>(), i)).toString();
        BTDeviceInfo devInfo;
        devInfo.macAddr = info.left(info.indexOf(' '));
        devInfo.name = info.right(info.length() - info.indexOf(' ') - 1);
        devInfo.isPaired = true;

        m_bondedDevices.append(devInfo);
    }

}
#endif

  •  配对

上面我们已经获取附近蓝牙设备的配对状态了,配对的话可以手动配对,或者可以试试下面的方法,不过我测试时由于手机蓝牙搜不到,所以是手动配对的,没有尝试代码请求配对。

void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing)

  • 创建连接

 Qt封装了蓝牙客户端(QBluetoothSocket)和蓝牙服务端(QBluetoothServer),跟Tcp的使用很像:Socket尝试连接服务器,Server监测连接,双方都可读写。

以客户端的身份QBluetoothSocket,请求连接服务器。服务器就是之前配对好的手机或者别的设备,注意手机端要打开蓝牙服务器QBluetoothServer,不然是不可能连接成功的。

  • 连接服务器

可以看到,需要传入服务器地址、uuid和打开模式。服务器地址 就是配对的蓝牙MAC地址;uuid有很多值,不同的功能uuid不同,可看一下枚举 enum QBluetoothUuid::ServiceClassUuid 了解一下;打开模式就不用说了。

void QBluetoothSocket::connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode = ReadWrite)

代码示例

//初始化

QBluetoothSocket* m_pSocket;
QByteArray m_buf;
QString m_strServerUUID;
QString m_strMACAddr;

m_pSocket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol,this);
m_strServerUUID = QBluetoothUuid(QBluetoothUuid::SerialPort).toString();

connect(m_pSocket,&QBluetoothSocket::connected,this,&BluetoothConnection::onConnected);    connect(m_pSocket,&QBluetoothSocket::disconnected,this,&BluetoothConnection::onDisconnect);
connect(m_pSocket,&QBluetoothSocket::readyRead,this,&BluetoothConnection::onReadyRead);
connect(m_pSocket, QOverload<QBluetoothSocket::SocketError>::of(&QBluetoothSocket::error),
            this, &BluetoothConnection::onErrorOccurred);


//连接服务器 (uuid 可传入,可默认)
void BluetoothConnection::connectToSerive(QString strAddr, QString strUuid)
{
    if(m_pSocket->state() == QBluetoothSocket::ConnectedState)
    {
        if(strAddr == m_strMACAddr && strUuid == m_strServerUUID)
        {
            return;
        }
        else
            disconnect();
    }


    if(strUuid != "")
        m_strServerUUID = strUuid;
    m_strMACAddr = strAddr;

    m_pSocket->connectToService(QBluetoothAddress(strAddr), QBluetoothUuid(m_strServerUUID),QIODevice::ReadWrite);
}
  •  数据传输处理

数据传输处理这部分,代码就不展示了,需求不同,代码不同。

结束语

安卓的蓝牙客户端大概就是这样了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/641306.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

响应式数据大屏开发rem、%、vh/vm

前言 响应式数据大屏开发rem、%、vh/vm 我们在开发数据大屏的时候难免会需要解决响应式问题 &#xff0c;那么响应式是什么呢&#xff1f; 响应式&#xff1a;响应式布局是元素随着屏幕发生宽高大小变化 盒子布局发生变化 通俗的来说&#xff1a; 自适应&#xff1a;元素随着…

设置全局loading

为什么要设置全局loading&#xff1f; 在项目开发过程中&#xff0c;请求接口的时候延迟没有数据&#xff0c;页面感觉狠卡顿&#xff0c;这个时候就要用loading来做一个延迟界面。 但是每个界面都写loading的话就会很复杂&#xff0c;所以今天给大家带来了一个全局loading的…

吴恩达471机器学习入门课程2第2周——手写数字识别(0到9)

手写数字识别的神经网络0-9 1、导包2、ReLU激活函数3 - Softmax函数4 - 神经网络4.1 问题陈述4.2 数据集4.2.1 可视化数据 4.3 模型表示批次和周期损失 (cost) 4.4 预测 使用神经网络来识别手写数字0-9。 1、导包 import numpy as np import tensorflow as tf from keras.mod…

人工智能时代已经开启,它是40年来最重大的技术革命

重读比尔盖茨关于AI的长文《The Age of AI has begun —— Artificial intelligence is as revolutionary as mobile phones and the Internet. 》&#xff08;开启AI时代&#xff1a;人工智能&#xff0c;比肩智能手机和互联网的革命&#xff09;&#xff0c;有了新的见解&…

电脑卡怎么办?4个方法让电脑流畅运行!

案例&#xff1a;我的电脑刚买的时候使用起来很流畅&#xff0c;但用久了之后就越来越卡&#xff0c;有没有办法可以让电脑流畅运行&#xff1f; 电脑是我们日常生活中必不可少的工具&#xff0c;但有时我们会遇到电脑卡顿的问题&#xff0c;这不仅会影响工作效率&#xff0c;…

VS2017 如何引入动态库(图文教程:libwebsocket为例)

目录 1、把想要的库放进适当的位置&#xff1b;&#xff08;以libwebsocket动态库为例&#xff09; 2、将库的头文件包含进来 3、添加对应的库目录 4、链接器——输入中&#xff0c;添加具体的依赖项 5、看当前的动态库&#xff0c;还会依赖其他什么动态库 1、把想要的库放进…

有了这些开源 Icon 库,妈妈再也不担心我的 UI 太丑啦!

Remix Icon Remix Icon 是一套面向设计师和开发者的开源图标库&#xff0c;所有的图标均可免费用于个人项目和商业项目。 与拼凑混搭的图标库不同&#xff0c;Remix Icon 的每一枚图标都是由设计师按照统一规范精心绘制的&#xff0c;在拥有完美像素对齐的基础上&#xff0c;…

分享两个转为数字艺术从业者服务的网站

01 地的数字艺术师、3D设计师、动画制作师和游戏开发人员等人才&#xff0c;为他们提供了多种服务和解决方案。 首先&#xff0c;NewCGer为数字艺术从业者提供了一个交流和学习的平台。该网站上有丰富的行业资讯、技术文章和研究报告等内容&#xff0c;能够及时了解到最新的数…

深度学习应用篇-推荐系统[11]:推荐系统的组成、场景转化指标(pv点击率,uv点击率,曝光点击率)、用户数据指标等评价指标详解

【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍&#xff1a;【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化…

Socket TCP/IP协议数据传输过程中的粘包和分包问题

Socket TCP/IP协议数据传输过程中的粘包和分包问题 一&#xff1a;通过图解法来描述一下分包和粘包&#xff0c;这样客户更清晰直观的了解&#xff1a; 下面对上面的图进行解释&#xff1a; 1.正常情况&#xff1a;如果Socket Client 发送的数据包&#xff0c;在Socket Server…

PowerBI 开发 第23篇:共享数据集

Power BI共享数据集的优点是&#xff1a;只要数据集刷新&#xff0c;那么引用该数据集的报表都会自动刷新&#xff0c;节省了报表数据刷新的时间和算力&#xff0c;缺点是&#xff1a;使用共享数据集的报表&#xff0c;虽然可以新增Measure(Measure仅存在于本地报表中&#xff…

The baby-bust economy “婴儿荒”经济 | 经济学人20230603版社论双语精翻

2023年6月3日《经济学人》&#xff08;The Economist&#xff09;封面文章暨社论&#xff08;Leaders&#xff09;精选&#xff1a;《“婴儿荒”经济》&#xff08;“The baby-bust economy”&#xff09;。 baby-bust即“婴儿荒”&#xff08;生育低谷&#xff09;&#xff0c…

Unity Shader Graph Ase三者分别有什么不一样的地方?

什么是Shader&#xff1f; 着色器 (Shader) 应用于计算机图形学领域&#xff0c;指一组供计算机图形资源在执行渲染任务的时使用的指令&#xff0c;用于计算机图形的颜色或明暗。但近来&#xff0c;它也能用于处理一些特殊的效果&#xff0c;或者视频后处理。通俗的说&#xf…

机器学习:基于AdaBoost算法模型对信用卡是否违约进行识别

系列文章目录 作者&#xff1a;i阿极 作者简介&#xff1a;数据分析领域优质创作者、多项比赛获奖者&#xff1a;博主个人首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞&#x1f44d;收藏&#x1f4c1;评论&#x1f4d2;…

低代码开发重要工具:jvs-form(表单引擎)2.1.7功能清单及新增功能介绍

jvs-form 2.1.7 版本功能清单 在低代码开发平台中&#xff0c;表单是用于收集和编辑数据的页面。它通常用于创建、更新或查看单个记录的详细信息。 jvs-form是jvs快速开发平台的8大引擎的其中之一&#xff0c;它的特点&#xff1a; 与动态模型联动&#xff0c;支持动态的调整…

Python心经(6)

目录 callable super type&#xff08;&#xff09;获取对应类型 isinstance判断对象是否是某个类或者子类的实例 issubclass&#xff0c;判断对象是不是类的子孙类 python3的异常处理 反射&#xff1a; 心经第三节和第五节都写了些面向对象的&#xff0c;这一节补充一…

黑苹果 或者 Mac 因 mds资源占用过高,导致频繁死机

开机后不久&#xff0c;风扇狂转&#xff0c;温度升高&#xff0c;然后死机&#xff0c;关机。 1. 使用 “Apple 诊断”测试 Mac 先看看硬件层面是否有问题。 使用“Apple 诊断”测试 Mac。 这款 Mac 的处理器是 Intel &#xff0c;开启 Mac&#xff0c;然后在 Mac 启动时立…

java的File

一、File &#xff08;一&#xff09;新建File对象 File对象表示一个路径&#xff0c;可以是文件路径&#xff0c;也可以是文件夹路径&#xff1b;这个路径可以是存在的&#xff0c;也可以是不存在的。 File类常见的构造方法&#xff1a; 例如&#xff1a; 注意&#xff1a;因…

母线差动保护(一)

与其他的主设备保护相比&#xff0c;母线保护的要求更为苛刻。当变电站母线发生故障时&#xff0c;如不及时切除故障&#xff0c;将会损坏众多电力设备&#xff0c;破坏系统的稳定性&#xff0c;甚至导致电力系统瓦解。如果母线保护拒动&#xff0c;也会造成大面积的停电。因此…

微服务外网部署灵活配置方案(不改代码适配apm和日志中心)

1.综述 之前微服务在进行部署时&#xff0c;有关日志中心和apm相关的配置都是放在代码相应的配置文件中的。 日志中心: /src/main/resources/logback-spring.xml /PIPELINE/docker/flume/hosts apm: /PIPELINE/docker/apm/apm_agent_dev.config /PIPELINE/docker/apm/ap…