因为实验需要,我要在手机端读取BWT901CL传感器的蓝牙数据,QT中有封装好的用于蓝牙连接以及数据读取的类,因为代码不是我自己写的,因此我就不放完整的代码在上面,但我会尽可能的写的详细一些。
QT在手机端读取传感器的蓝牙数据主要分为三部分:
①开启手机的蓝牙
②搜索周围的蓝牙信号
③蓝牙配对连接与数据传输
④根据蓝牙通讯协议进行数据解算
读取蓝牙数据的前三个部分分别对应QT的三个类,那么要使用这三个类,需要在.pro文件中加入androidextras
QT += androidextras
开启手机的蓝牙
需要用到QBluetoothLocalDevice类,首先包含头文件,并初始化一个该类的对象:
#include <QBluetoothLocalDevice>
QBluetoothLocalDevice *localDevice;
我们就可以使用QBluetoothLocalDevice类中的setHostMode函数来控制手机上的蓝牙,比如开启、关闭等操作。我们打开setHostMode的帮助文档,可以看到类中定义的HostMode有四种。
QBluetoothLocalDevice::HostPoweredOff代表关闭蓝牙
QBluetoothLocalDevice::HostConnectable代表开启蓝牙,远程蓝牙设备可以连接到本地蓝牙设备,前提是它们之前与本地蓝牙设备配对过,或者知道本地蓝牙设备的地址。
QBluetoothLocalDevice::HostDiscoverable代表开启蓝牙,远端蓝牙设备可以发现本地蓝牙设备的存在。设备也将是可连接的,并上电。在Android上,该模式最多只能激活5分钟。
第二个模式与第三个模式不同之处在于你的蓝牙设备之前是否于本地的设备进行过连接,这边建议直接使用第三个模式。
localDevice = new QBluetoothLocalDevice();
localDevice->setHostMode( QBluetoothLocalDevice::HostDiscoverable);
那么此时手机端会弹出关于蓝牙权限的申请,通过申请后即可打开蓝牙。
搜索周围的蓝牙信号
需要用到QBluetoothDeviceDiscoveryAgent类,包含头文件并初始化一个该类的对象:
#include <QBluetoothDeviceDiscoveryAgent>
QBluetoothDeviceDiscoveryAgent *discoveryAgent;
对于QBluetoothDeviceDiscoveryAgent类,其中一个很重要的信号是deviceDiscovered(QBluetoothDeviceInfo),我们可以理解为当他检测到新的蓝牙时就会触发这个信号,但是实际情况可能更加复杂,大多时候该信号返回的是“空的值”,但如果你仅仅是想要搜索周围的蓝牙信号,并记录它们的“姓名”与ID的话,那么就不必继续深究他的运行机制了。
下面我们连接信号与槽,并开启对周围蓝牙的搜索
discoveryAgent =new QBluetoothDeviceDiscoveryAgent();
connect(discoveryAgent,
SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
this,
SLOT(addBlueToothDevicesToList(QBluetoothDeviceInfo))
discoveryAgent->start();
上述的槽函数是我自己定义的一个槽函数,用来接受并处理deviceDiscovered(QBluetoothDeviceInfo)信号发送的信息,下面简单说一下信号中发送的信息:QBluetoothDeviceInfo
在本程序中,我们需要用到的就只有蓝牙的地址以及蓝牙的姓名,我们打开帮助文档可以很容易找到获取这两种信息的成员函数。
蓝牙的地址返回的是QBluetoothAddress类型,因此在槽函数中需要转换至QString用于存储。
QString address = info.address().toString();
QString name = info.name();
最后调用start()用于开启蓝牙搜索。
蓝牙配对连接与数据传输
蓝牙配对连接与数据传输需要用到QBluetoothSocket类,包含头文件并初始化一个该类的对象:
#include <QBluetoothSocket>
QBluetoothSocket *socket;
首先进行蓝牙连接,蓝牙连接部分不是很明白,我直接把代码放到下面了:
static const QLatin1String serviceUuid("00001101-0000-1000-8000-00805F9B34FB");
QBluetoothAddress address(addressPNS3);
socket->connectToService(address, QBluetoothUuid(serviceUuid) ,QIODevice::ReadWrite);
其中,addressPNS3就是我们想要连接的蓝牙的地址。
在蓝牙连接好后,就到了进行数据传输的部分,需要引入套接字协议的概念,套接字本身比较复杂,可以简单的理解为是某种双向通讯的“通讯协议”,或者可以比作USB转Type-c的转接线。在进行QBluetoothSocket类初始化的时候,这个类的其中一个构造函数就包含套接字协议的设置。
我们打开帮助文档,简单查询一下QBluetoothServiceInof::Protocol socketType。
因为我们是需要在手机端进行蓝牙数据的接受,因此要将套接字协议设置为QBluetoothServiceInfo::RfcommProtocol
socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
此外QBluetoothSocket类有三个比较重要的信号,分别是
SIGNAL(connected()) //在蓝牙连接上时触发该信号
SIGNAL(disconnected()) //在蓝牙断开时触发该信号
SIGNAL(readyRead()) //接受到一个蓝牙数据包时触发该信号
我们进行信号与槽的连接,用于在收到readyRead()信号时进行数据包的接受
connect(socket,SIGNAL(readyRead()),
this,SLOT(readBluetoothDataEvent()));
在数据接受部分,我们使用readAll()函数用来读取数据,此函数的返回值是QByteArray类型
QByteArray line = socket->readAll();
QByteArray array = line.toHex();
值得注意的是,因为QT本身QByteArray类的原因,他的数据是以ASCII码形式输出的,因此要使用toHex()函数转换为十六进制(要根据自己的需求转换相应的进制),之后我们可以转换成QString根据蓝牙通讯协议对数据进行解算。
根据蓝牙通讯协议进行数据解算
蓝牙通讯协议部分是我自己敲的代码,我直接附在最后。
蓝牙通讯协议部分有一些值得注意的地方,首先我将数据包转换为QString类型,然后以“55”作为标志进行分割,因为需要校验位的检验,因此我将数据转化为int类型用于校验。
在蓝牙通讯协议中有按位左移的操作,因此按照需求将int型转换为short型进行按位操作,short型转换为double型的这一步不要使用类型转换,否则数据会出错。
最后通过信号进行数据传输,蓝牙通讯协议部分代码如下:
QByteArray line = socket->readAll();
QByteArray array = line.toHex(); //转换为十六进制
QString str = array;
QStringList str_list = str.split("55"); //以“55”为标志进行分割
int num_strList = str_list.size();
for(int i = 0;i < num_strList;i++)
{
QString Data_decode_middle = str_list[i];
if(Data_decode_middle.size()<4) //进行数据长度检验
continue;
QString division = Data_decode_middle.left(2); //数据类型位提取
// qDebug()<<division.toInt()<<endl;
bool ok;
if(division == "50"){
int YY = Data_decode_middle.mid(2,2).toInt(&ok,16); //转换为十进制int型
int MM = Data_decode_middle.mid(4,2).toInt(&ok,16);
int DD = Data_decode_middle.mid(6,2).toInt(&ok,16);
int hh = Data_decode_middle.mid(8,2).toInt(&ok,16);
int mm = Data_decode_middle.mid(10,2).toInt(&ok,16);
int ss = Data_decode_middle.mid(12,2).toInt(&ok,16);
int ms = ((Data_decode_middle.mid(16,2).toInt(&ok,16))<<8) | (Data_decode_middle.mid(14,2).toInt(&ok,16));
int SUM = Data_decode_middle.mid(18,2).toInt(&ok,16);
if(SUM != (165 + YY + MM + DD + hh + mm + ss + ms) % 256) //校验位校验,0x55 + 0x50 为165
continue;
//qDebug()<<YY<<MM<<DD<<hh<<mm<<ss<<ms<<endl;
emit bluetooth_display_time(YY,MM,DD,hh,mm,ss,ms); //数据传输
}
else if (division == "51") {
double g = 9.8;
int AXL = Data_decode_middle.mid(2,2).toInt(&ok,16);
int AXH = Data_decode_middle.mid(4,2).toInt(&ok,16);
int AYL = Data_decode_middle.mid(6,2).toInt(&ok,16);
int AYH = Data_decode_middle.mid(8,2).toInt(&ok,16);
int AZL = Data_decode_middle.mid(10,2).toInt(&ok,16);
int AZH = Data_decode_middle.mid(12,2).toInt(&ok,16);
int TL = Data_decode_middle.mid(14,2).toInt(&ok,16);
int TH = Data_decode_middle.mid(16,2).toInt(&ok,16);
int SUM = Data_decode_middle.mid(18,2).toInt(&ok,16);
if(SUM != (166 + AXL + AXH + AYL + AYH + AZL + AZH + TL + TH) % 256) //0x55 + 0x51 = 166
continue;
short AXHL = (static_cast<short>(AXH)<<8) | static_cast<short>(AXL);
double ax = AXHL;
ax = ax / 32768 * 16 * g;
short AYHL = (static_cast<short>(AYH)<<8) | static_cast<short>(AYL);
double ay = AYHL;
ay = ay/ 32768 * 16 * g;
short AZHL = (static_cast<short>(AZH)<<8) | static_cast<short>(AZL);
double az = AZHL;
az = az / 32768 * 16 * g;
short THL = (static_cast<short>(TH)<<8) | static_cast<short>(TL);
double T = THL;
T = T / 100;
//qDebug()<<ax<<ay<<az<<T<<endl;
emit bluetooth_display_acce(ax,ay,az,T);
}
else if (division == "52") {
int WXL = Data_decode_middle.mid(2,2).toInt(&ok,16);
int WXH = Data_decode_middle.mid(4,2).toInt(&ok,16);
int WYL = Data_decode_middle.mid(6,2).toInt(&ok,16);
int WYH = Data_decode_middle.mid(8,2).toInt(&ok,16);
int WZL = Data_decode_middle.mid(10,2).toInt(&ok,16);
int WZH = Data_decode_middle.mid(12,2).toInt(&ok,16);
int VL = Data_decode_middle.mid(14,2).toInt(&ok,16);
int VH = Data_decode_middle.mid(16,2).toInt(&ok,16);
int SUM = Data_decode_middle.mid(18,2).toInt(&ok,16);
if(SUM != (167 + WXL + WXH + WYL + WYH + WZL + WZH + VL + VH) % 256) //0x55 + 0x52 = 167
continue;
short WXHL = (static_cast<short>(WXH)<<8) | static_cast<short>(WXL);
double wx = WXHL;
wx = wx / 32768 * 2000;
short WYHL = (static_cast<short>(WYH)<<8) | static_cast<short>(WYL);
double wy = WYHL;
wy = wy / 32768 * 2000;
short WZHL = (static_cast<short>(WZH)<<8) | static_cast<short>(WZL);
double wz = WZHL;
wz = wz / 32768 * 2000;
short VHL = (static_cast<short>(VH)<<8) | static_cast<short>(VL);
double V = VHL;
V = V / 100;
emit bluetooth_display_angle_velo(wx,wy,wz,V);
}
else if (division == "53") {
int rollL = Data_decode_middle.mid(2,2).toInt(&ok,16);
int rollH = Data_decode_middle.mid(4,2).toInt(&ok,16);
int pitchL = Data_decode_middle.mid(6,2).toInt(&ok,16);
int pitchH = Data_decode_middle.mid(8,2).toInt(&ok,16);
int yawL = Data_decode_middle.mid(10,2).toInt(&ok,16);
int yawH = Data_decode_middle.mid(12,2).toInt(&ok,16);
int VL = Data_decode_middle.mid(14,2).toInt(&ok,16);
int VH = Data_decode_middle.mid(16,2).toInt(&ok,16);
int SUM = Data_decode_middle.mid(18,2).toInt(&ok,16);
if(SUM != (168 + rollL + rollH + pitchL + pitchH + yawL + yawH + VL + VH) % 256) //0x55 + 0x53 = 168
continue;
short rollHL = (static_cast<short>(rollH)<<8) | static_cast<short>(rollL);
double roll = rollHL;
roll = roll / 32768 * 180;
short pitchHL = (static_cast<short>(pitchH)<<8) | static_cast<short>(pitchL);
double pitch = pitchHL;
pitch = pitch / 32768 * 180;
short yawHL = (static_cast<short>(yawH)<<8) | static_cast<short>(yawL);
double yaw = yawHL;
yaw = yaw / 32768 * 180;
short VHL = (static_cast<short>(VH)<<8) | static_cast<short>(VL);
double Version = VHL;
emit bluetooth_display_angle(roll,pitch,yaw,Version);
}
else if (division == "54") {
int HXL = Data_decode_middle.mid(2,2).toInt(&ok,16);
int HXH = Data_decode_middle.mid(4,2).toInt(&ok,16);
int HYL = Data_decode_middle.mid(6,2).toInt(&ok,16);
int HYH = Data_decode_middle.mid(8,2).toInt(&ok,16);
int HZL = Data_decode_middle.mid(10,2).toInt(&ok,16);
int HZH = Data_decode_middle.mid(12,2).toInt(&ok,16);
int TL = Data_decode_middle.mid(14,2).toInt(&ok,16);
int TH = Data_decode_middle.mid(16,2).toInt(&ok,16);
int SUM = Data_decode_middle.mid(18,2).toInt(&ok,16);
if(SUM != (169 + HXL + HXH + HYL + HYH + HZL + HZH + TL + TH) % 256) //0x55 + 0x54 = 169
continue;
short magnXHL = (static_cast<short>(HXH)<<8) | static_cast<short>(HXL);
double magnx = magnXHL;
short magnYHL = (static_cast<short>(HYH)<<8) | static_cast<short>(HYL);
double magny = magnYHL;
short magnZHL = (static_cast<short>(HZH)<<8) | static_cast<short>(HZL);
double magnz = magnZHL;
short THL = (static_cast<short>(TH)<<8) | static_cast<short>(TL);
double T = THL;
T = T / 100;
emit bluetooth_display_magn(magnx,magny,magnz,T);
}
}