2
votes

I have a device with a custom service which sends sensor data in a very high rate using the BLE notification feature.

I'm using the the following API on a Windows 10 machine: https://msdn.microsoft.com/en-us/library/windows/hardware/jj159880(v=vs.85).aspx

I'm searching the device by the custom service ID using SetupDi API, and then "connect" to it using CreateFile.

When I pair the device with Windows for the first time it immediately shows "Connected" in the Bluetooth Settings window, and then when I run my app it works perfectly fine (I receive data at high rate). If I close my app it changes the status in the Settings window to "Paired" instead of connected (Which I assume is fine). When I open my app again it connects and changes the status in the Settings to "Connected" again but now I receive the data at a much lower rate for some reason. (the data itself is correct). If I disconnect it via the Bluetooth Settings windows by clicking "Remove Device" and then pair it again like I did before it works again at an high rate for the first time.

I know it's not a problem with the device itself because it works fine with Android and other BLE supported platforms.

Any idea what might causing this issue?

Here is the code I'm using:

GUID serviceGuid = StringToGUID(GEM_SERVICE_GUID);

HDEVINFO info = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_DEVICEINTERFACE);
SP_DEVICE_INTERFACE_DATA data;
data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

int i = 0;

while (SetupDiEnumDeviceInterfaces(info, NULL, &guid, i, &data))
{
    i++;
}

if (GetLastError() != ERROR_NO_MORE_ITEMS)
{
    // TODO throw
}

DWORD requiredSize;

if (!SetupDiGetDeviceInterfaceDetail(info, &data, NULL, 0, &requiredSize, NULL))
{
    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    {
        // TODO throw
    }
}

PSP_DEVICE_INTERFACE_DETAIL_DATA details = (PSP_DEVICE_INTERFACE_DETAIL_DATA)std::malloc(requiredSize);
details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

if (!SetupDiGetDeviceInterfaceDetail(info, &data, details, requiredSize, NULL, NULL))
{
    // TODO throw
}

m_service = CreateFile(details->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if (m_service == INVALID_HANDLE_VALUE)
{
    // TODO throw
    return;
}

BTH_LE_GATT_CHARACTERISTIC combinedDataChar = FindCharacteristicByUUID(m_service, COMBINED_DATA_CHAR_HANDLE);
BTH_LE_GATT_DESCRIPTOR desc = FindDescriptorByType(m_service, &combinedDataChar, ClientCharacteristicConfiguration);

BTH_LE_GATT_DESCRIPTOR_VALUE val;
RtlZeroMemory(&val, sizeof(val));
val.DescriptorType = ClientCharacteristicConfiguration;
val.ClientCharacteristicConfiguration.IsSubscribeToNotification = TRUE;

HRESULT res = BluetoothGATTSetDescriptorValue(m_service, &desc, &val, BLUETOOTH_GATT_FLAG_NONE);

if (res != S_OK)
{
    // TODO throw
}

BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION chars;
chars.NumCharacteristics = 1;
chars.Characteristics[0] = combinedDataChar;

res = BluetoothGATTRegisterEvent(m_service, CharacteristicValueChangedEvent, &chars, OnValueChanged, NULL, &m_registrationHandle, BLUETOOTH_GATT_FLAG_NONE);

if (res != S_OK)
{
    // TODO throw
}

EDIT:

The code for the OnValueChanged callback:

void OnValueChanged(BTH_LE_GATT_EVENT_TYPE eventType, PVOID eventOutParameter, PVOID context)
{
    BLUETOOTH_GATT_VALUE_CHANGED_EVENT* e = (BLUETOOTH_GATT_VALUE_CHANGED_EVENT*)eventOutParameter;

    std::cout << e->CharacteristicValue->DataSize << std::endl;
}
1

1 Answers

0
votes

I believe you receive data through OnValueChanged callback? It's not provided in your code, and the problem could be somewhere inside it. If you hesitate to provide its code, I suggest that you perform the following experiments during the 'bad' session:

  1. Remove all of the code from it except for incrementing some counter to know the data rate.
  2. Measure CPU load. If it's nearing a full core load, you're CPU-bound.
  3. Use a profiler of your choice to check where your code spends time.

Now that the code for OnValueChanged is available:

  1. Output to console at high rate could be the bottleneck. I suggest that you only count events and output count once a few seconds, like that:

    static DWORD lastTicks = GetTickCount();
    static DWORD count = 0;
    count++;
    
    DWORD ticksElapsed = GetTickCount() - lastTicks;
    if (ticksElapsed > 5000)
    {
        std::cout << "Rate: " << (double(count) / ticksElapsed) << " / sec" << std::endl;
        lastTicks = GetTickCount();
        count = 0;
    }
    
  2. Do more tests (for example, 5 pairings, 5 connects after each pairing) and provide the event rates. It could happen that the drop in rate is actually related to something else, not re-pairing.