Created
November 2, 2015 01:09
-
-
Save tshirtman/52f17ca2096da2bddbca to your computer and use it in GitHub Desktop.
This file contains hidden or 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
//How to get notification values from a HR BLE monitor | |
//Ensure that you have paired the HR BLE monitor with the computer | |
#include <stdio.h> | |
#include <windows.h> | |
#include <setupapi.h> | |
#include <devguid.h> | |
#include <regstr.h> | |
#include <bthdef.h> | |
#include <Bluetoothleapis.h> | |
#pragma comment(lib, "SetupAPI") | |
#pragma comment(lib, "BluetoothApis.lib") | |
#define TO_SEARCH_DEVICE_UUID "{0000180D-0000-1000-8000-00805F9B34FB}" //we use UUID for an HR BLE device | |
//this is the notification function | |
//the way ValueChangedEventParameters is utilized is shown in | |
//HealthHeartRateService::HeartRateMeasurementEvent() | |
//a function in Windows Driver Kit (WDK) 8.0 Samples.zip\C++\WDK 8.0 Samples\Bluetooth Low Energy (LE) Generic Attribute (GATT) Profile Drivers\Solution\WpdHealthHeartRate\HealthHeartRateService.cpp | |
void SomethingHappened( 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("HR "); | |
//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 | |
//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 main( int argc, char *argv[ ], char *envp[ ] ) | |
{ | |
//Step 1: find the BLE device handle from its GUID | |
GUID AGuid; | |
//GUID can be constructed from "{xxx....}" string using CLSID | |
CLSIDFromString(TEXT(TO_SEARCH_DEVICE_UUID), &AGuid); | |
//now 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", 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\r\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", 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", 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\r\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", hr); | |
} | |
if (numChars != charBufferSize) { | |
printf("buffer size and buffer size actual size mismatch\r\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", 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\r\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", hr); | |
} | |
if (numDescriptors != descriptorBufferSize) { | |
printf("buffer size and buffer size actual size mismatch\r\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", hr); | |
} | |
PBTH_LE_GATT_DESCRIPTOR_VALUE pDescValueBuffer = (PBTH_LE_GATT_DESCRIPTOR_VALUE)malloc(descValueDataSize); | |
if (NULL == pDescValueBuffer) { | |
printf("pDescValueBuffer out of memory\r\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", 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", hr); | |
} else { | |
printf("setting notification for serivice handle %d\n", currGattDescriptor->ServiceHandle); | |
} | |
} | |
} | |
} | |
//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, | |
SomethingHappened, | |
NULL, | |
&EventHandle, | |
BLUETOOTH_GATT_FLAG_NONE); | |
if (S_OK != hr) { | |
printf("BluetoothGATTRegisterEvent - Actual Data %d", hr); | |
} | |
} | |
if (currGattChar->IsReadable) {//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", hr); | |
} | |
pCharValueBuffer = (PBTH_LE_GATT_CHARACTERISTIC_VALUE)malloc(charValueDataSize); | |
if (NULL == pCharValueBuffer) { | |
printf("pCharValueBuffer out of memory\r\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", hr); | |
} | |
//print the characeteristic Value | |
//for an HR monitor this might be the body sensor location | |
printf("\n Printing a read (not notifiable) characterstic (maybe) body sensor value"); | |
for(int iii=0; iii< 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){ | |
Sleep(1000); | |
//printf("look for notification"); | |
} | |
CloseHandle(hLEDevice); | |
if ( GetLastError()!=NO_ERROR && | |
GetLastError()!=ERROR_NO_MORE_ITEMS ) | |
{ | |
// Insert error handling here. | |
return 1; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment