目的
通过自己的软件能够完成搜索蓝牙,并且能够发送认证请求,协商PIN码或者是配对码的功能。
开发方式
Qt + windows api
开发方式说明
因为Qt的蓝牙API在windows系统上不能完成蓝牙发送配对请求的功能,而且对于ble广播PIN码的这种设备需要先添加windows的蓝牙设备,然后调用Qt的蓝牙API进行搜索才能搜索到。 但是Qt的蓝牙socket连接类还是很好用的。所以搜索蓝牙设备和配对的功能就选择调用windows api了。
需要使用到的api
hdf = BluetoothFindFirstDevice( _In_ const BLUETOOTH_DEVICE_SEARCH_PARAMS * pbtsp,_Inout_ BLUETOOTH_DEVICE_INFO * pbtdi);
函数功能是寻找第一个搜索到蓝牙设备的蓝牙适配器设备。
返回的hdf
参数为蓝牙适配器设备的句柄。
_In_ const BLUETOOTH_DEVICE_SEARCH_PARAMS * pbtsp
该参数为设置寻找参数的设备参数,告诉API返回相应参数的蓝牙设备。
BLUETOOTH_DEVICE_SEARCH_PARAMS 结构体参数的解释
BLUETOOTH_DEVICE_SEARCH_PARAMS bdsp = { sizeof(bdsp) };
bdsp.fReturnAuthenticated = 1; // 返回经过身份验证的蓝牙设备
bdsp.fReturnRemembered = 1; // 返回记住的蓝牙设备
bdsp.fReturnUnknown = 1; // 返回未知的蓝牙设备
bdsp.fReturnConnected = 1; // 返回已连接的蓝牙设备
bdsp.fIssueInquiry = 1; // 发出新一轮的查询
bdsp.cTimeoutMultiplier = 5; // 1.28s的增量,1.28 * 5 这个参数设置多少BluetoothFindFirstDevice这个函数就会阻塞多长时间。
bdsp.hRadio = NULL; // 有效的本地蓝牙句柄,如果是NULL,在所有本地蓝牙无线电上进行查询
_Inout_ BLUETOOTH_DEVICE_INFO * pbtdi
返回的是寻找到的蓝牙设备信息。
BLUETOOTH_DEVICE_INFO 参数结构体的解释
typedef struct _BLUETOOTH_DEVICE_INFO {
_Field_range_(==, sizeof(BLUETOOTH_DEVICE_INFO_STRUCT))
DWORD dwSize; // size, in bytes, of this structure - must be the sizeof(BLUETOOTH_DEVICE_INFO)
BLUETOOTH_ADDRESS Address; // Bluetooth address
ULONG ulClassofDevice; // Bluetooth "Class of Device"
BOOL fConnected; // Device connected/in use
BOOL fRemembered; // Device remembered
BOOL fAuthenticated; // Device authenticated/paired/bonded
SYSTEMTIME stLastSeen; // Last time the device was seen
SYSTEMTIME stLastUsed; // Last time the device was used for other than RNR, inquiry, or SDP
WCHAR szName[ BLUETOOTH_MAX_NAME_SIZE ]; // Name of the device 这里BLUETOOTH_MAX_NAME_SIZE 为248
} BLUETOOTH_DEVICE_INFO_STRUCT;
res = BluetoothAuthenticateDevice(_In_opt_ HWND hwndParent, _In_opt_ HANDLE hRadio, _Inout_ BLUETOOTH_DEVICE_INFO * pbtbi, _In_reads_opt_(ulPasskeyLength) PWSTR pszPasskey, _In_ ULONG ulPasskeyLength)
这个函数是蓝牙发送认证请求的函数
res`为返回的错误码
搜索蓝牙的具体示例
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
qDebug() << "WSAStartup failed";
}
BLUETOOTH_DEVICE_SEARCH_PARAMS bdsp = { sizeof(bdsp) };
bdsp.fReturnAuthenticated = 1; // 返回经过身份验证的蓝牙设备
bdsp.fReturnRemembered = 0; // 返回记住的蓝牙设备
bdsp.fReturnUnknown = 1; // 返回未知的蓝牙设备
bdsp.fReturnConnected = 1; // 返回已连接的蓝牙设备
bdsp.fIssueInquiry = 1; // 发出新一轮的查询
bdsp.cTimeoutMultiplier = 10; // 1.28s的增量,1.28 * 20
bdsp.hRadio = NULL; // 有效的本地蓝牙句柄,如果是NULL,在所有本地蓝牙无线电上进行查询
// 蓝牙设备结构体
BLUETOOTH_DEVICE_INFO bdi[20];
for (int i = 0; i < 20; i++) {
bdi[i].dwSize = sizeof(bdi[i]);
}
BLUETOOTH_FIND_RADIO_PARAMS radioParams = { sizeof(BLUETOOTH_FIND_RADIO_PARAMS) };
HANDLE hRadio = NULL;
// 查找第一个可用的蓝牙适配器
HBLUETOOTH_RADIO_FIND hFind = BluetoothFindFirstRadio(&radioParams, &hRadio);
if(hFind == NULL || hRadio == NULL)
{
qDebug() << "BluetoothFindFirstRadio failed";
}
HBLUETOOTH_DEVICE_FIND hdf = BluetoothFindFirstDevice(&bdsp, &bdi[0]); // 返回的hdf 句柄留给 BluetoothFindNextDevice使用
qDebug() << "bdi =" << QString::fromWCharArray(bdi[0].szName);
if (hdf == NULL) {
qDebug() << "BluetoothFindFirstDevice hdf failed";
}
int count = 0;
do {
// 如果扫到了第一个蓝牙设备
QString bluetooth_name = QString::fromWCharArray(bdi[count].szName);
qDebug() << "bluetooth_name =" << bluetooth_name;
count++;
qDebug() << "count =" << count;
if(count >= 20)
{
break;
}
} while (BluetoothFindNextDevice(hdf, &bdi[count]));
对指定蓝牙发送认证请求示例
QString path = QCoreApplication::applicationDirPath() + "/devtype.ini";
QSettings inifile(path,QSettings::IniFormat);
QString pin_info = inifile.value("pin/pincode").toString();
if(pin_info == "")
{
QMessageBox::critical(this,"错误","pin码为空");
return;
}
BLUETOOTH_DEVICE_INFO devInfo = dev_list.at(i);
if(devInfo.fAuthenticated == false)
{
/*将字符串转换为宽字符串*/
PWSTR pwstr;
size_t size = pin_info.toStdString().size();
wchar_t buffer[BLUETOOTH_MAX_PASSKEY_SIZE + 1] = {0};
MultiByteToWideChar(CP_ACP, 0, pin_info.toStdString().c_str(), size, buffer, size * sizeof(wchar_t));
pwstr = buffer;
int pwstrlength = sizeof(pwstr);
DWORD res = BluetoothAuthenticateDevice(NULL,NULL,&devInfo,pwstr,pwstrlength); //自动输入pin码和蓝牙配对。
if(res != ERROR_SUCCESS)
{
qDebug() << "bt failed";
QMessageBox::information(this,tr("warn"),tr("请检查是否已经与本背夹配对过,可以点击搜索,更新显示状态"));
return;
}
}
上述示例是使用了从ini文件中自动读取pin码,进行自动填充连接蓝牙。
如果需要手动输入pin码可以将BluetoothAuthenticateDevice(NULL,NULL,&devInfo,pwstr,pwstrlength)
中的pwstr
更改为NULL。pwstrlength
更改为NULL。pwstrlength
这个参数在pwstr
为NULL时是不起作用的。但是当你需要指定pin码时,长度一定要是pin码的长度。
在这里还有一个需要注意的点是pin码的转换问题,pin码的数据类型是Qt的QString,需要转换为windows api需要的PSTR。这个地方很坑。
注意事项
- 在windows+QT蓝牙开发中,编译器一定要选用MSVC编译器,不要使用Mingw编译器,Mingw编译器编译不了Qt的蓝牙库。
- 根据官方说明,自己的系统版本一定要是WIN10的一个版本往上,因为这样QT的相关蓝牙库能够调用WinRT的蓝牙相关API。