BluetoothLowEnergy.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// BluetoothLowEnergy.cpp : Defines the entry point for the console application. | |
// | |
#include "stdafx.h" | |
#pragma warning (disable: 4068) | |
#include <windows.h> | |
#include <stdio.h> | |
#include <tchar.h> | |
#include <setupapi.h> | |
#include <devguid.h> | |
#include <regstr.h> | |
#include <bthdef.h> | |
#include <bluetoothleapis.h> | |
#include <iostream> | |
#include <sstream> | |
#include <string> | |
#include <locale> | |
#define MOSS_DEVICE_UUID "{88880001-b5a3-f393-e0a9-e50e24dc1234}" | |
namespace util | |
{ | |
std::string to_narrow(const wchar_t *s, char def = '?', const std::locale& loc = std::locale()) | |
{ | |
std::ostringstream stm; | |
while (*s != L'\0') | |
{ | |
stm << std::use_facet< std::ctype<wchar_t> >(loc).narrow(*s++, def); | |
} | |
return stm.str(); | |
} | |
} | |
void HandleBLENotification(BTH_LE_GATT_EVENT_TYPE EventType, PVOID EventOutParameter, PVOID Context) | |
{ | |
printf("notification obtained "); | |
PBLUETOOTH_GATT_VALUE_CHANGED_EVENT ValueChangedEventParameters = (PBLUETOOTH_GATT_VALUE_CHANGED_EVENT)EventOutParameter; | |
HRESULT hr; | |
if (0 == ValueChangedEventParameters->CharacteristicValue->DataSize) { | |
hr = E_FAIL; | |
printf("datasize 0\n"); | |
} | |
else { | |
printf("got value\n"); | |
//for(int i=0; i<ValueChangedEventParameters->CharacteristicValue->DataSize;i++) { | |
// printf("%0x",ValueChangedEventParameters->CharacteristicValue->Data[i]); | |
//} | |
// if the first bit is set, then the value is the next 2 bytes. If it is clear, the value is in the next byte | |
//The Heart Rate Value Format bit (bit 0 of the Flags field) indicates if the data format of | |
//the Heart Rate Measurement Value field is in a format of UINT8 or UINT16. | |
//When the Heart Rate Value format is sent in a UINT8 format, the Heart Rate Value | |
//Format bit shall be set to 0. When the Heart Rate Value format is sent in a UINT16 | |
//format, the Heart Rate Value Format bit shall be set to 1 | |
//from this PDF https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=239866 | |
//unsigned heart_rate; | |
//if (0x01 == (ValueChangedEventParameters->CharacteristicValue->Data[0] & 0x01)) { | |
// heart_rate = ValueChangedEventParameters->CharacteristicValue->Data[1] * 256 + ValueChangedEventParameters->CharacteristicValue->Data[2]; | |
//} | |
//else { | |
// heart_rate = ValueChangedEventParameters->CharacteristicValue->Data[1]; | |
//} | |
//printf("%d\n", heart_rate); | |
} | |
} | |
// This function works to get a handle for a BLE device based on its GUID | |
// Copied from http://social.msdn.microsoft.com/Forums/windowshardware/en-US/e5e1058d-5a64-4e60-b8e2-0ce327c13058/erroraccessdenied-error-when-trying-to-receive-data-from-bluetooth-low-energy-devices?forum=wdk | |
// From https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/bad452cb-4fc2-4a86-9b60-070b43577cc9/is-there-a-simple-example-desktop-programming-c-for-bluetooth-low-energy-devices?forum=wdk | |
// Credits to Andrey_sh | |
HANDLE GetBLEHandle(__in GUID AGuid) | |
{ | |
HDEVINFO hDI; | |
SP_DEVICE_INTERFACE_DATA did; | |
SP_DEVINFO_DATA dd; | |
GUID BluetoothInterfaceGUID = AGuid; | |
HANDLE hComm = NULL; | |
hDI = SetupDiGetClassDevs(&BluetoothInterfaceGUID, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); | |
if (hDI == INVALID_HANDLE_VALUE) return NULL; | |
did.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); | |
dd.cbSize = sizeof(SP_DEVINFO_DATA); | |
for (DWORD i = 0; SetupDiEnumDeviceInterfaces(hDI, NULL, &BluetoothInterfaceGUID, i, &did); i++) | |
{ | |
SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData; | |
DeviceInterfaceDetailData.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); | |
DWORD size = 0; | |
if (!SetupDiGetDeviceInterfaceDetail(hDI, &did, NULL, 0, &size, 0)) | |
{ | |
int err = GetLastError(); | |
if (err == ERROR_NO_MORE_ITEMS) break; | |
PSP_DEVICE_INTERFACE_DETAIL_DATA pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)GlobalAlloc(GPTR, size); | |
pInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); | |
if (!SetupDiGetDeviceInterfaceDetail(hDI, &did, pInterfaceDetailData, size, &size, &dd)) | |
break; | |
hComm = CreateFile( | |
pInterfaceDetailData->DevicePath, | |
GENERIC_WRITE | GENERIC_READ, | |
FILE_SHARE_READ | FILE_SHARE_WRITE, | |
NULL, | |
OPEN_EXISTING, | |
0, | |
NULL); | |
GlobalFree(pInterfaceDetailData); | |
} | |
} | |
SetupDiDestroyDeviceInfoList(hDI); | |
return hComm; | |
} | |
int ConnectBLEDevice() | |
{ | |
// Step 1: find the BLE device handle from its GUID | |
GUID AGuid; | |
// GUID can be constructed from "{xxx....}" string using CLSID | |
CLSIDFromString(TEXT(MOSS_DEVICE_UUID), &AGuid); | |
// Get the handle | |
HANDLE hLEDevice = GetBLEHandle(AGuid); | |
// Step 2: Get a list of services that the device advertises | |
// first send 0, NULL as the parameters to BluetoothGATTServices inorder to get the number of | |
// services in serviceBufferCount | |
USHORT serviceBufferCount; | |
//////////////////////////////////////////////////////////////////////////// | |
// Determine Services Buffer Size | |
//////////////////////////////////////////////////////////////////////////// | |
HRESULT hr = BluetoothGATTGetServices( | |
hLEDevice, | |
0, | |
NULL, | |
&serviceBufferCount, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) != hr) { | |
printf("BluetoothGATTGetServices - Buffer Size %d\n", hr); | |
} | |
PBTH_LE_GATT_SERVICE pServiceBuffer = (PBTH_LE_GATT_SERVICE) | |
malloc(sizeof(BTH_LE_GATT_SERVICE) * serviceBufferCount); | |
if (NULL == pServiceBuffer) { | |
printf("pServiceBuffer out of memory\n"); | |
} | |
else { | |
RtlZeroMemory(pServiceBuffer, | |
sizeof(BTH_LE_GATT_SERVICE) * serviceBufferCount); | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
// Retrieve Services | |
//////////////////////////////////////////////////////////////////////////// | |
USHORT numServices; | |
hr = BluetoothGATTGetServices( | |
hLEDevice, | |
serviceBufferCount, | |
pServiceBuffer, | |
&numServices, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (S_OK != hr) { | |
printf("BluetoothGATTGetServices - Buffer Size %d\n", hr); | |
} | |
// Step 3: now get the list of charactersitics. note how the pServiceBuffer is required from step 2 | |
//////////////////////////////////////////////////////////////////////////// | |
// Determine Characteristic Buffer Size | |
//////////////////////////////////////////////////////////////////////////// | |
USHORT charBufferSize; | |
hr = BluetoothGATTGetCharacteristics( | |
hLEDevice, | |
pServiceBuffer, | |
0, | |
NULL, | |
&charBufferSize, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) != hr) { | |
printf("BluetoothGATTGetCharacteristics - Buffer Size %d\n", hr); | |
} | |
PBTH_LE_GATT_CHARACTERISTIC pCharBuffer; | |
if (charBufferSize > 0) { | |
pCharBuffer = (PBTH_LE_GATT_CHARACTERISTIC) | |
malloc(charBufferSize * sizeof(BTH_LE_GATT_CHARACTERISTIC)); | |
if (NULL == pCharBuffer) { | |
printf("pCharBuffer out of memory\n"); | |
} | |
else { | |
RtlZeroMemory(pCharBuffer, | |
charBufferSize * sizeof(BTH_LE_GATT_CHARACTERISTIC)); | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
// Retrieve Characteristics | |
//////////////////////////////////////////////////////////////////////////// | |
USHORT numChars; | |
hr = BluetoothGATTGetCharacteristics( | |
hLEDevice, | |
pServiceBuffer, | |
charBufferSize, | |
pCharBuffer, | |
&numChars, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (S_OK != hr) { | |
printf("BluetoothGATTGetCharacteristics - Actual Data %d\n", hr); | |
} | |
if (numChars != charBufferSize) { | |
printf("buffer size and buffer size actual size mismatch\n"); | |
} | |
} | |
// Step 4: now get the list of descriptors. note how the pCharBuffer is required from step 3 | |
// descriptors are required as we descriptors that are notification based will have to be written | |
// once IsSubcribeToNotification set to true, we set the appropriate callback function | |
// need for setting descriptors for notification according to | |
//http://social.msdn.microsoft.com/Forums/en-US/11d3a7ce-182b-4190-bf9d-64fefc3328d9/windows-bluetooth-le-apis-event-callbacks?forum=wdk | |
PBTH_LE_GATT_CHARACTERISTIC currGattChar; | |
for (int ii = 0; ii <charBufferSize; ii++) { | |
currGattChar = &pCharBuffer[ii]; | |
USHORT charValueDataSize; | |
PBTH_LE_GATT_CHARACTERISTIC_VALUE pCharValueBuffer; | |
/////////////////////////////////////////////////////////////////////////// | |
// Determine Descriptor Buffer Size | |
//////////////////////////////////////////////////////////////////////////// | |
USHORT descriptorBufferSize; | |
hr = BluetoothGATTGetDescriptors( | |
hLEDevice, | |
currGattChar, | |
0, | |
NULL, | |
&descriptorBufferSize, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) != hr) { | |
printf("BluetoothGATTGetDescriptors - Buffer Size %d\n", hr); | |
} | |
PBTH_LE_GATT_DESCRIPTOR pDescriptorBuffer; | |
if (descriptorBufferSize > 0) { | |
pDescriptorBuffer = (PBTH_LE_GATT_DESCRIPTOR) | |
malloc(descriptorBufferSize | |
* sizeof(BTH_LE_GATT_DESCRIPTOR)); | |
if (NULL == pDescriptorBuffer) { | |
printf("pDescriptorBuffer out of memory\n"); | |
} | |
else { | |
RtlZeroMemory(pDescriptorBuffer, descriptorBufferSize); | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
// Retrieve Descriptors | |
//////////////////////////////////////////////////////////////////////////// | |
USHORT numDescriptors; | |
hr = BluetoothGATTGetDescriptors( | |
hLEDevice, | |
currGattChar, | |
descriptorBufferSize, | |
pDescriptorBuffer, | |
&numDescriptors, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (S_OK != hr) { | |
printf("BluetoothGATTGetDescriptors - Actual Data %d\n", hr); | |
} | |
if (numDescriptors != descriptorBufferSize) { | |
printf("buffer size and buffer size actual size mismatch\n"); | |
} | |
for (int kk = 0; kk<numDescriptors; kk++) { | |
PBTH_LE_GATT_DESCRIPTOR currGattDescriptor = &pDescriptorBuffer[kk]; | |
//////////////////////////////////////////////////////////////////////////// | |
// Determine Descriptor Value Buffer Size | |
//////////////////////////////////////////////////////////////////////////// | |
USHORT descValueDataSize; | |
hr = BluetoothGATTGetDescriptorValue( | |
hLEDevice, | |
currGattDescriptor, | |
0, | |
NULL, | |
&descValueDataSize, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) != hr) { | |
printf("BluetoothGATTGetDescriptorValue - Buffer Size %d\n", hr); | |
} | |
PBTH_LE_GATT_DESCRIPTOR_VALUE pDescValueBuffer = (PBTH_LE_GATT_DESCRIPTOR_VALUE)malloc(descValueDataSize); | |
if (NULL == pDescValueBuffer) { | |
printf("pDescValueBuffer out of memory\n"); | |
} | |
else { | |
RtlZeroMemory(pDescValueBuffer, descValueDataSize); | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
// Retrieve the Descriptor Value | |
//////////////////////////////////////////////////////////////////////////// | |
hr = BluetoothGATTGetDescriptorValue( | |
hLEDevice, | |
currGattDescriptor, | |
(ULONG)descValueDataSize, | |
pDescValueBuffer, | |
NULL, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (S_OK != hr) { | |
printf("BluetoothGATTGetDescriptorValue - Actual Data %d\n", hr); | |
} | |
// you may also get a descriptor that is read (and not notify) andi am guessing the attribute handle is out of limits | |
// we set all descriptors that are notifiable to notify us via IsSubstcibeToNotification | |
if (currGattDescriptor->AttributeHandle < 255) { | |
BTH_LE_GATT_DESCRIPTOR_VALUE newValue; | |
RtlZeroMemory(&newValue, sizeof(newValue)); | |
newValue.DescriptorType = ClientCharacteristicConfiguration; | |
newValue.ClientCharacteristicConfiguration.IsSubscribeToNotification = TRUE; | |
hr = BluetoothGATTSetDescriptorValue( | |
hLEDevice, | |
currGattDescriptor, | |
&newValue, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (S_OK != hr) { | |
printf("BluetoothGATTGetDescriptorValue - Actual Data %d\n", hr); | |
} | |
} | |
} | |
} | |
// set the appropriate callback function when the descriptor change value | |
BLUETOOTH_GATT_EVENT_HANDLE EventHandle; | |
if (currGattChar->IsNotifiable) { | |
printf("Setting Notification for ServiceHandle %d\n", currGattChar->ServiceHandle); | |
BTH_LE_GATT_EVENT_TYPE EventType = CharacteristicValueChangedEvent; | |
BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION EventParameterIn; | |
EventParameterIn.Characteristics[0] = *currGattChar; | |
EventParameterIn.NumCharacteristics = 1; | |
hr = BluetoothGATTRegisterEvent( | |
hLEDevice, | |
EventType, | |
&EventParameterIn, | |
HandleBLENotification, | |
NULL, | |
&EventHandle, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (S_OK != hr) { | |
printf("BluetoothGATTRegisterEvent - Actual Data %d\n", hr); | |
} | |
} | |
else if (currGattChar->IsReadable) { | |
//////////////////////////////////////////////////////////////////////////// | |
// Determine Characteristic Value Buffer Size | |
//////////////////////////////////////////////////////////////////////////// | |
hr = BluetoothGATTGetCharacteristicValue( | |
hLEDevice, | |
currGattChar, | |
0, | |
NULL, | |
&charValueDataSize, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) != hr) { | |
printf("BluetoothGATTGetCharacteristicValue - Buffer Size %d\n", hr); | |
} | |
pCharValueBuffer = (PBTH_LE_GATT_CHARACTERISTIC_VALUE)malloc(charValueDataSize); | |
if (NULL == pCharValueBuffer) { | |
printf("pCharValueBuffer out of memory\n"); | |
} | |
else { | |
RtlZeroMemory(pCharValueBuffer, charValueDataSize); | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
// Retrieve the Characteristic Value | |
//////////////////////////////////////////////////////////////////////////// | |
hr = BluetoothGATTGetCharacteristicValue( | |
hLEDevice, | |
currGattChar, | |
(ULONG)charValueDataSize, | |
pCharValueBuffer, | |
NULL, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (S_OK != hr) { | |
printf("BluetoothGATTGetCharacteristicValue - Actual Data %d\n", hr); | |
} | |
// print the characeteristic Value | |
printf("\n Printing a read characterstic "); | |
for (int iii = 0; iii < (int)pCharValueBuffer->DataSize; iii++) {// ideally check ->DataSize before printing | |
printf("%d ", pCharValueBuffer->Data[iii]); | |
} | |
printf("\n"); | |
// Free before going to next iteration, or memory leak. | |
free(pCharValueBuffer); | |
pCharValueBuffer = NULL; | |
} | |
} | |
// go into an inf loop that sleeps. you will ideally see notifications from the HR device | |
while (1){ | |
printf("sleep\n"); | |
Sleep(1000); | |
} | |
CloseHandle(hLEDevice); | |
if (GetLastError() != NO_ERROR && | |
GetLastError() != ERROR_NO_MORE_ITEMS) | |
{ | |
// Insert error handling here. | |
return 1; | |
} | |
return 0; | |
} | |
int ScanBLEDevices() | |
{ | |
HDEVINFO hDI; | |
SP_DEVINFO_DATA did; | |
DWORD i; | |
// Create a HDEVINFO with all present devices. | |
hDI = SetupDiGetClassDevs(&GUID_DEVCLASS_BLUETOOTH, NULL, NULL, DIGCF_PRESENT); | |
if (hDI == INVALID_HANDLE_VALUE) | |
{ | |
return 1; | |
} | |
// Enumerate through all devices in Set. | |
did.cbSize = sizeof(SP_DEVINFO_DATA); | |
for (i = 0; SetupDiEnumDeviceInfo(hDI, i, &did); i++) | |
{ | |
bool hasError = false; | |
DWORD nameData; | |
LPTSTR nameBuffer = NULL; | |
DWORD nameBufferSize = 0; | |
while (!SetupDiGetDeviceRegistryProperty( | |
hDI, | |
&did, | |
SPDRP_FRIENDLYNAME, | |
&nameData, | |
(PBYTE)nameBuffer, | |
nameBufferSize, | |
&nameBufferSize)) | |
{ | |
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) | |
{ | |
if (nameBuffer) delete(nameBuffer); | |
nameBuffer = new wchar_t[nameBufferSize * 2]; | |
} | |
else | |
{ | |
hasError = true; | |
break; | |
} | |
} | |
DWORD addressData; | |
LPTSTR addressBuffer = NULL; | |
DWORD addressBufferSize = 0; | |
while (!SetupDiGetDeviceRegistryProperty( | |
hDI, | |
&did, | |
SPDRP_HARDWAREID, | |
&addressData, | |
(PBYTE)addressBuffer, | |
addressBufferSize, | |
&addressBufferSize)) | |
{ | |
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) | |
{ | |
if (addressBuffer) delete(addressBuffer); | |
addressBuffer = new wchar_t[addressBufferSize * 2]; | |
} | |
else | |
{ | |
hasError = true; | |
break; | |
} | |
} | |
LPTSTR deviceIdBuffer = NULL; | |
DWORD deviceIdBufferSize = 0; | |
while (!SetupDiGetDeviceInstanceId( | |
hDI, | |
&did, | |
deviceIdBuffer, | |
deviceIdBufferSize, | |
&deviceIdBufferSize)) | |
{ | |
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) | |
{ | |
if (deviceIdBuffer) delete(deviceIdBuffer); | |
deviceIdBuffer = new wchar_t[deviceIdBufferSize * 2]; | |
} | |
else | |
{ | |
hasError = true; | |
break; | |
} | |
} | |
if (hasError) | |
{ | |
continue; | |
} | |
std::string name = util::to_narrow(nameBuffer); | |
std::string address = util::to_narrow(addressBuffer); | |
std::string deviceId = util::to_narrow(deviceIdBuffer); | |
std::cout << "Found " << name << " (" << deviceId << ")" << std::endl; | |
} | |
return 0; | |
} | |
int _tmain(int argc, _TCHAR* argv[]) | |
{ | |
int result = ConnectBLEDevice(); | |
std::cin.get(); | |
return result; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment