了解蓝牙
经典蓝牙和低功耗蓝牙差异
经典蓝牙(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);
}
- 数据传输处理
数据传输处理这部分,代码就不展示了,需求不同,代码不同。
结束语
安卓的蓝牙客户端大概就是这样了。