none
Is there a simple example (desktop programming C++) for Bluetooth Low Energy devices? RRS feed

  • Question

  • Hello

    i am trying to code a simple desktop application (C++ in console) to connect to a custom Bluetooth low energy service. But before i go to that, i need to code a simple application to even get the GATT properties of a BLE device.

    Is there any code sample that i can use? I am having a hard time going through the examples that are given with the GATT**** functions. 

    And the code samples in WDK seem to be of drivers and for metro apps. None for a desktop application.

    thanks a ton!


    • Edited by ihateble Thursday, March 6, 2014 11:26 PM forgot to add C++
    Thursday, March 6, 2014 11:25 PM

Answers

  • So i was able to code a program for reading (read) variables and setting notifications for (notifiable) variables from an HR BLE monitor.

    Pasting it here so that it may help someone looking for an example. (may have minor bugs, but seems to work with an MIO ALPHA HR monitor). Project properties may have to be set to stdcall (Project properties->c/c++->advanced->calling convention)

    //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;
    }


    • Marked as answer by ihateble Friday, March 7, 2014 9:17 AM
    • Edited by ihateble Friday, March 7, 2014 9:19 AM removed unnecessary comments
    Friday, March 7, 2014 9:17 AM

All replies

  • So i was able to code a program for reading (read) variables and setting notifications for (notifiable) variables from an HR BLE monitor.

    Pasting it here so that it may help someone looking for an example. (may have minor bugs, but seems to work with an MIO ALPHA HR monitor). Project properties may have to be set to stdcall (Project properties->c/c++->advanced->calling convention)

    //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;
    }


    • Marked as answer by ihateble Friday, March 7, 2014 9:17 AM
    • Edited by ihateble Friday, March 7, 2014 9:19 AM removed unnecessary comments
    Friday, March 7, 2014 9:17 AM
  • hey,bro,thanks for your share,i have some questions here.Can you help me to solve it?

    I used your sample, and it worked well,and after those step ,i want to call BluetoothGATTSetCharacteristicValue function to transmit some hex data to my device,but it always return 0x80070032.Did i miss something before calling it?if not, do you have some advice for me?

    Thanks a lot.

    Thursday, May 15, 2014 9:44 AM
  • HI,

    you helped me so much, and i'm afraid to ask you 1 more thing: can you poste a snippet of code for the write?

    i do:

     void write(unsigned char * data, long datasize)

    {

    .... some code similar yours ... and finally:

    BTH_LE_GATT_CHARACTERISTIC_VALUE newValue;

    RtlZeroMemory(&newValue,(sizeof(newValue)); newValue.DataSize = sizeof(valueData); newValue.Data = (UCHAR*)&valueData; // Set the new characteristic value hr = BluetoothGATTSetCharacteristicValue(hDevice, parentCharacteristic, &newValue, NULL, BLUETOOTH_GATT_FLAG_NONE);

    like microsoft msda page reports, but

    newValue.Data = (UCHAR*)&valueData;

    say me that "expression must be a modificable Lvalue".....

    any suggestions??

    Thank you!!

    Thursday, July 3, 2014 6:56 PM
  • Excellent piece of code - saved a lot of nightmares! :-)
    Saturday, July 19, 2014 10:16 AM
  • i know its a late reply.

    i think you should (mem)copy the contents of valueData into newValue.Data rather than pointing to it.

    Tuesday, August 26, 2014 4:21 AM
  • So i was able to code a program for reading (read) variables and setting notifications for (notifiable) variables from an HR BLE monitor.

    Pasting it here so that it may help someone looking for an example. (may have minor bugs, but seems to work with an MIO ALPHA HR monitor). Project properties may have to be set to stdcall (Project properties->c/c++->advanced->calling convention)

    //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;
    }


    I can't run this code. I use VS2010 and Windows 8.1. When I debug my project , it show error "cannot open file 'BluetoothAPis.h'" Can you help me?
    Monday, March 30, 2015 9:51 AM
  • Hello,thank you for your help.Now, I can communicate with the BLE devices.But I have a problem that why it takes long time to connect or reconnect to the BLE device sometimes?

    • Edited by rialhuang Friday, May 8, 2015 2:13 AM
    Friday, May 8, 2015 2:11 AM
  • check if you have the windows sdk installed. and i bet you'll atleast need windows kit for 8.0. If you have a later version of VS like 2012 or 2013, the SDK would be included https://developer.mozilla.org/en-US/docs/Windows_SDK_versions
    Monday, May 18, 2015 8:44 PM
  • unlike classical bluetooth, bluetooth LE may have large connection intervals inorder for clients that transmit then sleep for a long time and then wakeup to transmit again. and this doesnot consider the fact that you also have to handle out-of-range for devices.

    ideally, whenever you get a timeout (slave device is not responding for some X amount of time) you do a unregister notification event and then re-register to the handle/notification

    //unregister the notification event

    BluetoothGATTUnregisterEvent(EventHandle, BLUETOOTH_GATT_FLAG_NONE);

    //close the device handle

    if ( !CloseHandle(hLEDevice) ) {

    //print some error

    }

    Sleep(1000);

    //Do the entire notification process including getting the handle again.


    Monday, May 18, 2015 8:54 PM
  • Hello,

    Thanks for this piece of code, but i am having serious issues getting the callback to fire. I am never receiving the notification using this code only when i keep writing a characteristic in the while(1) the notification arrives. it is as if the connection is not kept alive when not executing a write.

    When using below code the notifications are received:

    	while (1){
    		Sleep(100);
    
    		//printf("look for notification\n");
    		UCHAR dataBuffer[sizeof(int)];
    		BTH_LE_GATT_CHARACTERISTIC_VALUE newValue;
    
    		newValue.DataSize = 4;
    		newValue.Data[0] = 1234;
    
    		if (WriteGattChar){
    			hr = BluetoothGATTGetCharacteristicValue(
    				hLEDevice,
    				WriteGattChar,
    				&newValue,
    				NULL,
    				BLUETOOTH_GATT_FLAG_NONE);
    
    			if (hr != S_OK) {
    				printf("Failed to write characteristic\n");
    			}
    		}
    
    	}
    But when just using this nothing happens:
    while (1){
    		Sleep(100);
    }

    Notifications a received on android devices so i know its not the peripheral device. Any help would be greatly appreciated.
    • Edited by Artinis Thursday, July 2, 2015 2:17 PM
    Thursday, July 2, 2015 2:11 PM
  • Hello,

    Thanks for this piece of code, it worked but i am having a issue and i can´t find the problem.

    The program crashes always after 15 minutes. Maybe you have encountered the same problem in the meanwhile.

    What i know is that the crash has something to do with the Notification Callback. The Crash depends on the Connection Intervall. With  Connection Intervall set 10ms it crashes after 15 minutes.

    Greetings



    • Edited by kingkarl237 Wednesday, January 13, 2016 1:11 PM
    Wednesday, January 13, 2016 10:47 AM
  • it should be "BluetoothAPIs.h"
    Saturday, September 17, 2016 9:51 AM
  • Hi Artinis, how did you get notifications from android devices..can u help please
    Tuesday, June 27, 2017 5:12 AM
  • If you use this code in x86,

    Check Heming Wen solution: add CALLBACK before the declaration/definition of your callback routine

    Else I got an exception throw after exiting callback routine.

    Thursday, November 23, 2017 10:56 PM
  • for the 32 bit version, i added FILE_FLAG_OVERLAPPED on the createfile and it work better and replaced the service id by the correct one and i'm able to attach all notifications on  the thingy from nordic semi.

    ;-> ;-> ;-> ;->

    Sunday, February 4, 2018 8:57 AM
  • Do you know how with this code i could get the serial number of the device?? i dont understand how to use that characteristics
    • Proposed as answer by Ripple07 Wednesday, November 6, 2019 7:08 AM
    Friday, February 23, 2018 10:59 AM