Skip to content

Instantly share code, notes, and snippets.

@programmarchy
Created March 25, 2015 15:45
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save programmarchy/c9d02e22d58bfab3f8bb to your computer and use it in GitHub Desktop.
Save programmarchy/c9d02e22d58bfab3f8bb to your computer and use it in GitHub Desktop.
BluetoothLowEnergy.cpp
// 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