Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //HeartRateRetriever.h
- #pragma once
- #pragma comment(lib, "SetupAPI")
- #pragma comment(lib, "BluetoothApis.lib")
- #pragma warning( disable : 4068 )
- #define NTDDI_VERSION NTDDI_WIN8
- #define _WIN32_WINNT _WIN32_WINNT_WIN8
- #define TO_SEARCH_DEVICE_UUID "{0000180D-0000-1000-8000-00805F9B34FB}" //we use UUID for an HR BLE device
- #include <iostream>
- #include "Core.h"
- #include "AllowWindowsPlatformTypes.h"
- #include "Windows/COMPointer.h"
- #include "stdio.h"
- #include "windows.h"
- #include "setupapi.h"
- #include "devguid.h"
- #include "regstr.h"
- #include "bthdef.h"
- #include "Bluetoothleapis.h"
- #include "HideWindowsPlatformTypes.h"
- class ANGEL_GREYBOX_API HeartRateRetriever : public FRunnable
- {
- public:
- // Constructor & Destructor
- HeartRateRetriever();
- ~HeartRateRetriever();
- // Thread handling functions
- void EnsureCompletion(); // Function for killing the thread
- void PauseThread(); // Function for pausing the thread
- void ContinueThread(); // Function for continuing/unpausing the thread
- bool IsThreadPaused(); // Function to check the state of the thread
- // FRunnable interface functions
- virtual bool Init();
- virtual uint32 Run();
- virtual void Stop();
- // Bluetooth Windows API functions
- HANDLE GetBLEHandle(__in GUID AGuid);
- static void SomethingElseHappened(BTH_LE_GATT_EVENT_TYPE EventType, PVOID EventOutParameter, PVOID Context);
- // Function to get the heart rate
- int GetHeartRate();
- inline int GetPrevHeartRate() { return prevHeartRate; };
- private:
- // Thread to run the worker FRunnable on
- FRunnableThread* Thread;
- FCriticalSection m_mutex;
- FEvent* m_semaphore;
- // Thread-safe booleans for changing the state of the thread
- FThreadSafeBool m_kill;
- FThreadSafeBool m_pause;
- static int heartRate;
- int prevHeartRate;
- };
- // HeartRateRetriever.cpp
- #include "HeartRateRetriever.h"
- #include <String>
- #include <EngineGlobals.h>
- #include <Runtime/Engine/Classes/Engine/Engine.h>
- int HeartRateRetriever::heartRate = 0;
- HeartRateRetriever::HeartRateRetriever()
- {
- m_kill = false;
- m_pause = false;
- // Initialise FEvent
- m_semaphore = FGenericPlatformProcess::GetSynchEventFromPool(false);
- // Create the worker thread
- Thread = FRunnableThread::Create(this, TEXT("HRR Thread"), 0, TPri_BelowNormal);
- }
- HeartRateRetriever::~HeartRateRetriever()
- {
- if (m_semaphore)
- {
- // Clean up the FEvent
- FGenericPlatformProcess::ReturnSynchEventToPool(m_semaphore);
- m_semaphore = nullptr;
- }
- if (Thread)
- {
- // Clean up the worker thread
- delete Thread;
- Thread = nullptr;
- }
- }
- bool HeartRateRetriever::Init()
- {
- return true;
- }
- HANDLE HeartRateRetriever::GetBLEHandle(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;
- }
- void HeartRateRetriever::SomethingElseHappened(BTH_LE_GATT_EVENT_TYPE EventType, PVOID EventOutParameter, PVOID Context)
- {
- PBLUETOOTH_GATT_VALUE_CHANGED_EVENT ValueChangedEventParameters = (PBLUETOOTH_GATT_VALUE_CHANGED_EVENT)EventOutParameter;
- HRESULT hr;
- if (0 == ValueChangedEventParameters->CharacteristicValue->DataSize) {
- hr = E_FAIL;
- }
- else {
- // 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];
- }
- heartRate = heart_rate;
- }
- }
- uint32 HeartRateRetriever::Run()
- {
- FPlatformProcess::Sleep(0.03);
- while (m_kill == false)
- {
- if (m_pause)
- {
- // FEvent->Wait() will sleep the thread until given a signal Trigger()
- m_semaphore->Wait();
- if (m_kill)
- {
- return 0;
- }
- }
- else {
- //Step 1: find the BLE device handle from its GUID
- GUID AGuid;
- //GUID can be constructed from "{xxx....}" string using CLSID
- #define TEXT(quote) __TEXT(quote)
- CLSIDFromString(TEXT(TO_SEARCH_DEVICE_UUID), &AGuid);
- #define TEXT(x) TEXT_PASTE(x)
- //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) {
- }
- PBTH_LE_GATT_SERVICE pServiceBuffer = (PBTH_LE_GATT_SERVICE)
- malloc(sizeof(BTH_LE_GATT_SERVICE) * serviceBufferCount);
- if (NULL == pServiceBuffer) {
- }
- 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) {
- }
- //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) {
- }
- PBTH_LE_GATT_CHARACTERISTIC pCharBuffer = NULL;
- if (charBufferSize > 0) {
- pCharBuffer = (PBTH_LE_GATT_CHARACTERISTIC)
- malloc(charBufferSize * sizeof(BTH_LE_GATT_CHARACTERISTIC));
- if (NULL == pCharBuffer) {
- }
- 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) {
- }
- if (numChars != charBufferSize) {
- }
- }
- //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) {
- }
- PBTH_LE_GATT_DESCRIPTOR pDescriptorBuffer;
- if (descriptorBufferSize > 0) {
- pDescriptorBuffer = (PBTH_LE_GATT_DESCRIPTOR)
- malloc(descriptorBufferSize
- * sizeof(BTH_LE_GATT_DESCRIPTOR));
- if (NULL == pDescriptorBuffer) {
- }
- else {
- RtlZeroMemory(pDescriptorBuffer, descriptorBufferSize);
- }
- ////////////////////////////////////////////////////////////////////////////
- // Retrieve Descriptors
- ////////////////////////////////////////////////////////////////////////////
- USHORT numDescriptors;
- hr = BluetoothGATTGetDescriptors(
- hLEDevice,
- currGattChar,
- descriptorBufferSize,
- pDescriptorBuffer,
- &numDescriptors,
- BLUETOOTH_GATT_FLAG_NONE);
- if (S_OK != hr) {
- }
- if (numDescriptors != descriptorBufferSize) {
- }
- 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) {
- }
- PBTH_LE_GATT_DESCRIPTOR_VALUE pDescValueBuffer = (PBTH_LE_GATT_DESCRIPTOR_VALUE)malloc(descValueDataSize);
- if (NULL == pDescValueBuffer) {
- }
- else {
- RtlZeroMemory(pDescValueBuffer, descValueDataSize);
- }
- ////////////////////////////////////////////////////////////////////////////
- // Retrieve the Descriptor Value
- ////////////////////////////////////////////////////////////////////////////
- hr = BluetoothGATTGetDescriptorValue(
- hLEDevice,
- currGattDescriptor,
- (ULONG)descValueDataSize,
- pDescValueBuffer,
- NULL,
- BLUETOOTH_GATT_FLAG_NONE);
- if (S_OK != 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 = 1;
- hr = BluetoothGATTSetDescriptorValue(
- hLEDevice,
- currGattDescriptor,
- &newValue,
- BLUETOOTH_GATT_FLAG_NONE);
- if (S_OK != hr) {
- }
- else {
- }
- }
- }
- }
- //set the appropriate callback function when the descriptor change value
- BLUETOOTH_GATT_EVENT_HANDLE EventHandle;
- if (currGattChar->IsNotifiable) {
- 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,
- SomethingElseHappened,
- this,
- &EventHandle,
- BLUETOOTH_GATT_FLAG_NONE);
- if (S_OK != 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) {
- }
- pCharValueBuffer = (PBTH_LE_GATT_CHARACTERISTIC_VALUE)malloc(charValueDataSize);
- if (NULL == pCharValueBuffer) {
- }
- else {
- RtlZeroMemory(pCharValueBuffer, charValueDataSize);
- }
- ////////////////////////////////////////////////////////////////////////////
- // Retrieve the Characteristic Value
- ////////////////////////////////////////////////////////////////////////////
- hr = BluetoothGATTGetCharacteristicValue(
- hLEDevice,
- currGattChar,
- (ULONG)charValueDataSize,
- pCharValueBuffer,
- NULL,
- BLUETOOTH_GATT_FLAG_NONE);
- if (S_OK != hr) {
- }
- // 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 (!m_pause && !m_kill)
- {
- prevHeartRate = heartRate;
- Sleep(1000);
- }
- CloseHandle(hLEDevice);
- if (GetLastError() != NO_ERROR &&
- GetLastError() != ERROR_NO_MORE_ITEMS)
- {
- // Insert error handling here.
- return 1;
- }
- }
- }
- return 0;
- }
- void HeartRateRetriever::PauseThread()
- {
- m_pause = true;
- }
- void HeartRateRetriever::ContinueThread()
- {
- m_pause = false;
- if (m_semaphore)
- {
- // FEvent->Trigger() will wake up the thread
- m_semaphore->Trigger();
- }
- }
- void HeartRateRetriever::Stop()
- {
- m_kill = true;
- m_pause = false;
- if (m_semaphore)
- {
- // Trigger the FEvent in case the thread is sleeping
- m_semaphore->Trigger();
- }
- }
- void HeartRateRetriever::EnsureCompletion()
- {
- Stop();
- if (Thread)
- {
- Thread->WaitForCompletion();
- }
- }
- bool HeartRateRetriever::IsThreadPaused()
- {
- return (bool)m_pause;
- }
- int HeartRateRetriever::GetHeartRate()
- {
- m_mutex.Lock();
- int HR = heartRate;
- m_mutex.Unlock();
- return HR;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement