This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Bluetooth LE Windows 10 using WinRT C++ code works if device NOT paired. Fails with unreachable if device is paired..

The following code snippet works if the Bluetooth LE device is NOT paired. After pairing the same device, the call to WriteClientCharacteristicConfigurationDescriptorAsync() fails with UNREACHABLE. No PIN code required for pairing.

Can anyone please help? The code will become part of a library with Java JNI bindings, so needs to be in C++. That's why I'm using the WinRT stuff. I'm using VS 2017 target SDK 10.0.18362.0 running on Windows 10 1903.

#include "pch.h"

#include <combaseapi.h>

using namespace winrt;
using namespace Windows::Foundation;
using namespace winrt::Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Windows::Devices::Bluetooth;
using namespace Windows::Foundation::Collections;
using namespace Windows::Devices::Bluetooth::Advertisement;
using namespace Windows::Devices::Bluetooth::GenericAttributeProfile;

#define NU_SERVICE			"{6E400001-B5A3-F393-E0A9-E50E24DCCA9E}"
#define TX_CHARACTERISTIC	"{6E400002-B5A3-F393-E0A9-E50E24DCCA9E}"
#define RX_CHARACTERISTIC	"{6E400003-B5A3-F393-E0A9-E50E24DCCA9E}"

std::wstring guidToString(GUID uuid) {
	std::wstring guid;
	WCHAR* wszUuid = NULL;
	if(::UuidToString(&uuid, (RPC_WSTR*) &wszUuid) == RPC_S_OK) {
		guid = wszUuid;
		::RpcStringFree((RPC_WSTR*) &wszUuid);
	}
	return guid;
}

void str2ba(const char *straddr, unsigned long long *btaddr) {
	int i;
	unsigned int aaddr[6];
	unsigned long long tmpaddr = 0;

	if (sscanf_s(straddr, "%02x:%02x:%02x:%02x:%02x:%02x", &aaddr[0], &aaddr[1], &aaddr[2], &aaddr[3], &aaddr[4], &aaddr[5]) != 6) {
		return;
	}
	*btaddr = 0;
	for (i = 0; i < 6; i++) {
		tmpaddr = (unsigned long long) (aaddr[i] & 0xff);
		*btaddr = ((*btaddr) << 8) + tmpaddr;
	}
}

IAsyncAction OpenDevice(unsigned long long deviceAddress) {
	auto device = co_await BluetoothLEDevice::FromBluetoothAddressAsync(deviceAddress);

	std::wcout << std::hex <<
		"Device Information: " << std::endl <<
		"\tName     :" << device.Name().c_str() << std::endl <<
		"\tAddress  :" << device.BluetoothAddress() << std::endl <<
		"\tStatus   :" << (device.ConnectionStatus() == BluetoothConnectionStatus::Connected ? "Connected" : "Disconnected") << std::endl <<
		"\tDeviceId :" << device.DeviceId().c_str()  << std::endl <<
		std::endl;

	GUID nusGUID, txcGUID, rxcGUID;
	CLSIDFromString(TEXT(NU_SERVICE), &nusGUID);
	CLSIDFromString(TEXT(TX_CHARACTERISTIC), &txcGUID);
	CLSIDFromString(TEXT(RX_CHARACTERISTIC), &rxcGUID);

	auto services = co_await device.GetGattServicesAsync();//BluetoothCacheMode::Cached);
	for(GenericAttributeProfile::GattDeviceService const & s : services.Services()) {
		std::wcout << std::hex << "\t\tService - Guid: [" << guidToString(s.Uuid()) << "]" << std::endl;

		auto characteristics = co_await s.GetCharacteristicsAsync();
		for(GenericAttributeProfile::GattCharacteristic const & c : characteristics.Characteristics()) {
			std::wcout << std::hex << "\t\tCharacteristic - Guid: [" << guidToString(c.Uuid()) << "]" << std::endl;

			if(c.CharacteristicProperties() == GattCharacteristicProperties::Notify) {
				printf("Notify supported\n");

				GattCommunicationStatus status = co_await c.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue::Notify);
				switch (status) {
				case GattCommunicationStatus::AccessDenied:
					printf("access denied\n");
					break;
				case GattCommunicationStatus::ProtocolError:
					printf("protocol error\n");
					break;
				case GattCommunicationStatus::Unreachable:
					printf("unreachable\n");
					break;
				case GattCommunicationStatus::Success:
					c.ValueChanged([](GattCharacteristic const& charateristic, GattValueChangedEventArgs const& args) {
						std::wcout << std::hex <<
							"\t\tNotified GattCharacteristic - Guid: [" << guidToString(charateristic.Uuid()) << "]" << std::endl;
					});

					//
					// Code to write to BT device ommitted.... Sleep 5 secs then exit.
					//
					Sleep(5000);
				}
			}
		}
	}

	device.Close();
}

int main() {
	init_apartment();

	// Connect to a specific Bluetooth device.
	unsigned long long deviceAddress;
	str2ba("db:e7:df:00:52:32", &deviceAddress);
	printf("device = %lld\n", deviceAddress);

	try {
		OpenDevice(deviceAddress).get();
	} catch (const std::exception& e) {
		std::cout << e.what() << std::endl;
		return FALSE;
	}
	return TRUE;
}

Parents Reply
  • Yes. I've tried that. I was hoping somebody else had had this issue and figured out a clever way round it.

    Our software needs to work with paired devices. I'm going to have to implement a nasty workaround - get the user to pair the device, make a note of it in a "cache file", then unpair it to get the thing connecting. Next time round I can offer the user a union of paired devices and devices from the cache file.

    I think the Windows BLE code is broken, but their Bluetooth team is small and unable / unwilling to address this despite it being a repeatable bug. I guess that's why people use UART dongles with Windows!

Children
No Data
Related