adabru / BleWinrtDll

BLE for Unity 3d in Windows without UWP.
Do What The F*ck You Want To Public License
170 stars 53 forks source link

Microbit Bluetooth UART service data reading problem #64

Open nmitrak opened 6 months ago

nmitrak commented 6 months ago

Hi @adabru, as i wrote in a previous post i managed to connect unity and then hololens 2 with a microbit and read its internal temperature sensor value by subscribing to the respective ServiceId and CharacteristicId. But now i have a second temperature sensor that i have connected to a microbit's pin and wrote a program for the microbit in order for the temperature value of the sensor (a simple number) to be transmitted though the Bluetooth Uart service which i enabled on the microbit. Although i managed to send a set of characters from Unity to the microbit , through your BlewinrtDll project , by using the Demo's Write function , which uses BleApi.SendData, i cannot read any data coming from the microbit. It seems to me that when unity polls data from the CharacteristicId {6e400002-b5a3-f393-e0a9-e50e24dcca9e} of the Bluetooth UART Service nothing comes out. Subscribing to this service doesnt seem to detect value changes when using your demo(?) The funny thing is that i can read the values coming from microbit through this bluetooth Uart service when using an android phone ,with a custom made application with appinventor and that the Microsoft 's Sample BluetoothLE app from Hololens 2 can subscribe to this CharacteristicId and seems to be detecting value changes but cannot recognize the data format. I would appreciate any ideas . Again thank you very much.

nmitrak commented 6 months ago

After some research it seems that for that particular bluetooth uart service microbit sends raw binary data. Maybe unity polls for incoming data that are not in raw binary format

adabru commented 6 months ago

Hi nmitrak, binary format should be fine for BleWinrtDll. In the Microsoft sample, you can try setting a breakpoint or a log output at https://github.com/microsoft/Windows-universal-samples/blob/main/Samples/BluetoothLE/cs/Scenario2_Client.xaml.cs#L447

nmitrak commented 1 week ago

Thank you again for your answer. Just coming back again to the same problem after a long period of time. I read somewhere that in order to receive messages from the bluetooth uart service of Micro:bit you have to enable notification, writing "0200" on the Client Characteristic Configuration Descriptor (CCCD) of the UART Service Characteristic.(The CCCD is 2902). Is this possible with BleWinrtDLL you compiled? I would like to thank you once more for your contribution in solving this problem.

nmitrak commented 1 week ago

Dear @adabru After searching the issues section i found tufeixp's code that might do the job. Coding in C++ is not my strong point but could you tell me please if i add this code to Bluewirtdll.cpp in order to reuse the function PollData without modification, after scanning the characteristics , should I ignore the SubscribeCharacteristic function, call ReadData function and then the PollData? Or call ReadData and directly get the data coming from BLE device?

Thank you so much for your support!

fire_and_forget ReadDataAsync(BLEData data) {
    {
        lock_guard lock(dataQueueLock);
    }
    auto characteristic = co_await retrieveCharacteristic(data.deviceId, data.serviceUuid, data.characteristicUuid);
    GattReadResult result = co_await characteristic.ReadValueAsync();
    if (result.Status() == GattCommunicationStatus::Success) {
        auto dataReader = DataReader::FromBuffer(result.Value());
        auto output = dataReader.ReadString(dataReader.UnconsumedBufferLength());

        BLEData data;
        wcscpy_s(data.characteristicUuid, sizeof(data.characteristicUuid) / sizeof(wchar_t), to_hstring(characteristic.Uuid()).c_str());
        wcscpy_s(data.serviceUuid, sizeof(data.serviceUuid) / sizeof(wchar_t), to_hstring(characteristic.Service().Uuid()).c_str());
        wcscpy_s(data.deviceId, sizeof(data.deviceId) / sizeof(wchar_t), characteristic.Service().Device().DeviceId().c_str());

        data.size = output.size();
        memcpy(data.buf, output.data(), data.size);
        {
            lock_guard queueGuard(dataQueueLock);
            dataQueue.push(data);
            dataQueueSignal.notify_one();
        }
        //saveError(L"%s:%d read done: %s", __WFILE__, __LINE__, output.c_str());
    }
}
void ReadData(BLEData* data)
{
ReadDataAsync(*data);
}
adabru commented 1 week ago

Subscribing to a characteristic writes to the CCCD:

https://github.com/adabru/BleWinrtDll/blob/03b4dcf71a4152958074b90cb5459cf575456bb7/BleWinrtDll/BleWinrtDll.cpp#L482-L490

nmitrak commented 1 week ago

Dear @adabru thank you very for your answer and for clarifying this point. Nevertheless microbit is sending data through bluetooth uart and HoloLens doesn't retrieve them when subscribing to a characteristic id and polling data. I tested the communication between microbit and Hololens 2 with Microsoft's bluetooth sample and data comes in but of unknown format. That's why i thought of implementing the ReadDataAsync function as you suggested at an older post. But being a little bit of a newbie at this made me wondering how this function works as it doesn't seem to take any account of descriptors Again thank you very much for your time

Edit: Once more you are right @adabru and thank you , the CCCD is writen with Subscription to the Characteristic. The only difference is that in order to read data from Micro:bit bluetooth UART you have to change the GattClientCharacteristicConfigurationDescriptorValue argument from Notify to Indicate. Then the data polling works like a charm. But I assume that this will work only in UART polling.

fire_and_forget SubscribeCharacteristicAsync(wchar_t* deviceId, wchar_t* serviceId, wchar_t* characteristicId, bool* result) { 
    try { 
        auto characteristic = co_await retrieveCharacteristic(deviceId, serviceId, characteristicId); 
        if (characteristic != nullptr) { 
            auto status = co_await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue::Indicate); 
            if (status != GattCommunicationStatus::Success) 
                saveError(L"%s:%d Error subscribing to characteristic with uuid %s and status %d", __WFILE__, __LINE__, characteristicId, status); 
            else { 
                Subscription *subscription = new Subscription();