Timing issue a C++/winRT BLE connection attempt?

0

I am using C++/winRT UWP to discover and connect to Bluetooth Low Energy devices. I am using the advertisment watcher to look for advertisements from devices I can support. This works.

Then I pick one to connect to. The connection procedure is a little weird by my way of thinking but according to the microsoft docs one Calls this FromBluetoothAddressAsync() with the BluetoothAddress and two things happen; one gets the BluetoothLEDevice AND a connection attempt is made. One needs to register a handler for the connection status changed event BUT you can't do that until you get the BluetoothLEDevice.

Is there a timing issue causing the exception? Has the connection already happened BEFORE I get the BluetoothLEDevice object? Below is the code and below that is the log:

void BtleHandler::connectToDevice(BluetoothLEAdvertisementReceivedEventArgs eventArgs)
{
    OutputDebugStringA("Connect to device called\n");
    // My God this async stuff took me a while to figure out! See https://msdn.microsoft.com/en-us/magazine/mt846728.aspx

    IAsyncOperation<Windows::Devices::Bluetooth::BluetoothLEDevice> async =  // assuming the address type is how I am to behave ..
     BluetoothLEDevice::FromBluetoothAddressAsync(eventArgs.BluetoothAddress(), BluetoothAddressType::Random);
    bluetoothLEDevice = async.get();
    OutputDebugStringA("BluetoothLEDevice returned\n");
    bluetoothLEDevice.ConnectionStatusChanged({ this, &BtleHandler::onConnectionStatusChanged });

    // This method not only gives you the device but it also initiates a connection
}

The above code generates the following log:

New advertisment/scanResponse with UUID 00001809-0000-1000-8000-00805F9B34FB
New ad/scanResponse with name Philips ear thermometer and UUID 00001809-0000-1000-8000-00805F9B34FB
Connect to device called 
ERROR here--> onecoreuap\drivers\wdm\bluetooth\user\winrt\common\bluetoothutilities.cpp(509)\Windows.Devices.Bluetooth.dll!03BEFDD6: (caller: 03BFB977) ReturnHr(1) tid(144) 80070490 Element not found.
ERROR here--> onecoreuap\drivers\wdm\bluetooth\user\winrt\device\bluetoothledevice.cpp(428)\Windows.Devices.Bluetooth.dll!03BFB9B7: (caller: 03BFAF01) ReturnHr(2) tid(144) 80070490 Element not found.
BluetoothLEDevice returned
Exception thrown at 0x0F5CDF2F (WindowsBluetoothAdapter.dll) in BtleScannerTest.exe: 0xC0000005: Access violation reading location 0x00000000.

It sure looks like there is a timing issue. But if it is, I have no idea how to resolve it. I cannot register for the event if I don't have a BluetoothLEDevice object! I cannot figure out a way to get the BluetoothLEDevice object without invoking a connection.

================================ UPDATE =============================

Changed the methods to IAsyncAction and used co_await as suggested by @IInspectable. No difference. The problem is clearly that the registered handler is out of scope or something is wrong with it. I tried a get_strong() instead of a 'this' in the registration, but the compiler would not accept it (said identifier 'get_strong()' is undefined). However, if I commented out the registration, no exception is thrown but I still get these log messages

onecoreuap\drivers\wdm\bluetooth\user\winrt\common\bluetoothutilities.cpp(509)\Windows.Devices.Bluetooth.dll!0F27FDD6: (caller: 0F28B977) ReturnHr(3) tid(253c) 80070490 Element not found.
onecoreuap\drivers\wdm\bluetooth\user\winrt\device\bluetoothledevice.cpp(428)\Windows.Devices.Bluetooth.dll!0F28B9B7: (caller: 0F28AF01) ReturnHr(4) tid(253c) 80070490 Element not found.

But the program continues to run an I continue to discover and connect. But since I can't get the connection event it is kind of useless at this stage.

bluetooth-lowenergy
c++-winrt
asked on Stack Overflow Apr 19, 2019 by Brian Reinhold • edited Apr 22, 2019 by Brian Reinhold

2 Answers

0

I hate my answer. But after asynching and co-routining everything under the sun, the problem is unsolvable by me:

This method

bluetoothLEDevice = co_await BluetoothLEDevice::FromBluetoothAddressAsync(eventArgs.BluetoothAddress(), BluetoothAddressType::Random);

returns NULL. That should not happen and there is not much I can do about it. I read that as a broken BLE API.

A BTLE Central should be able to do as follows

  • Discover a device if new then:
  • If user selects connect, connect to the device
  • perform service discovery
  • read/write/enable characteristics as needed
  • handle indications/notifications

If at any time the peripheral sends a security request or insufficient authentication error, start pairing

repeat the action that caused the insufficient authentication.

On disconnect, save the paired and bonded state if the device is pairable.

On rediscovery of the device, if unpaired (not a pairable device)

  • repeat above

If paired and bonded

  • start encryption
  • work with the device; no need to re-enable or do service discovery

========================= MORE INFO ===================================

This is what the log shows when the method is called

Connect to device called
onecoreuap\drivers\wdm\bluetooth\user\winrt\common\bluetoothutilities.cpp(509)\Windows.Devices.Bluetooth.dll!0496FDD6: (caller: 0497B977) ReturnHr(1) tid(3b1c) 80070490 Element not found.
onecoreuap\drivers\wdm\bluetooth\user\winrt\device\bluetoothledevice.cpp(428)\Windows.Devices.Bluetooth.dll!0497B9B7: (caller: 0497AF01) ReturnHr(2) tid(3b1c) 80070490 Element not found.
BluetoothLEDevice returned is NULL. Can't register

Since the BluetoothLEDevice is NULL, I do not attempt to register.

================= MORE INFO ===================

I should also add that taking an over-the-air sniff reveals that there is never a connection event. Though the method is supposed to initiate a connection as well as return the BluetoothLEDevice object, it ends up doing neither. My guess is that the method requires more pre-use setup of the system that only the DeviceWatcher does. The AdvertisementWatcher probably does not.

answered on Stack Overflow Apr 22, 2019 by Brian Reinhold • edited Apr 26, 2019 by Brian Reinhold
-1

In BLE you always have to wait for every operation to complete.
I am not an expert in C++, but in C# the async connection procedure returns a bool if it was successful.
In C++ the IAsyncOperation does not have a return type, so there is no way to know if the connection procedure was successful or completed.
You will have to await the IAsyncOperation and make sure that you have a BluetoothLEDevice object, before you attach the event handler.
To await an IAsyncOperation there is a question/answer on how to await anIAsyncOperation: How to wait for an IAsyncAction? How to wait for an IAsyncAction?

answered on Stack Overflow Apr 20, 2019 by GrooverFromHolland

User contributions licensed under CC BY-SA 3.0