How to refresh Services and Characteristics values after reconnecting when using QtBluetooth?
-
wrote on 24 Apr 2024, 13:10 last edited by swjqq
I encountered some issues when implementing OTA functionality on BLE devices using
QtBluetooth
(6.5.3, MSVC 2019, based on lowenergyscanner) on Windows 11. The OTA process is as follows:- Write 0x0 to
OTA CONTROL
characteristic - Device will disconnect on its own
- Reconnect with device (it will have
OTA DATA
characteristic this time) - Write 0x0 to
OTA CONTROL
again - Start writing to
OTA DATA
characteristic - Write 0x3 to
OTA CONTROL
to end upload
Perform steps 1-3 using QtBluetooth, but after reconnecting, the
OTA DATA
characteristic cannot be found (see Figure 1). However, when I usedBluetooth LE Explorer
to perform steps 1-3, and after reconnecting, it was able to find theOTA DATA
characteristic (see Figure 2). Is it possible to refresh the services and characteristic s after reconnecting? - Write 0x0 to
-
wrote on 25 Apr 2024, 09:00 last edited by
@swjqq said in How to refresh Services and Characteristics values after reconnecting when using QtBluetooth?:
Is it possible to refresh the services and characteristic s after reconnecting?
Have you tried connecting a slot to the
QLowEnergyController::connected()
signal that invokesQLowEnergyController::discoverServices()
, then wait forPokitDevicePrivate::discoveryFinished()
signal before looking for the service and characteristic?Cheers.
-
@swjqq said in How to refresh Services and Characteristics values after reconnecting when using QtBluetooth?:
Is it possible to refresh the services and characteristic s after reconnecting?
Have you tried connecting a slot to the
QLowEnergyController::connected()
signal that invokesQLowEnergyController::discoverServices()
, then wait forPokitDevicePrivate::discoveryFinished()
signal before looking for the service and characteristic?Cheers.
wrote on 25 Apr 2024, 15:09 last edited by swjqq@Paul-Colby Thank you for your reply.
Have you tried connecting a slot to the QLowEnergyController::connected() signal that invokes QLowEnergyController::discoverServices(), then wait for PokitDevicePrivate::discoveryFinished() signal before looking for the service and characteristic?
I think I have done it(see the following pictures). Furthermore, after executing step 1, even if I close the
lowenergyscanner
, reopen it, and reconnect to the target BLE device, the services and characteristics remain the same as before, and theOTA DATA
characteristic still cannot be found. However, after executing step 1, close thelowenergyscanner
, turn off Bluetooth and reopen Bluetooth in the settings application of Win11, reopenlowenergyscanner
and reconnect to the target BLE device, then it can refresh services and characteristics and find theOTA DATA
characteristic(see the final picture).
-
@Paul-Colby Thank you for your reply.
Have you tried connecting a slot to the QLowEnergyController::connected() signal that invokes QLowEnergyController::discoverServices(), then wait for PokitDevicePrivate::discoveryFinished() signal before looking for the service and characteristic?
I think I have done it(see the following pictures). Furthermore, after executing step 1, even if I close the
lowenergyscanner
, reopen it, and reconnect to the target BLE device, the services and characteristics remain the same as before, and theOTA DATA
characteristic still cannot be found. However, after executing step 1, close thelowenergyscanner
, turn off Bluetooth and reopen Bluetooth in the settings application of Win11, reopenlowenergyscanner
and reconnect to the target BLE device, then it can refresh services and characteristics and find theOTA DATA
characteristic(see the final picture).
wrote on 26 Apr 2024, 01:32 last edited byHi @swjqq, the code looks pretty reasonable to me. A couple of quick things:
- just to be sure, you are re-creating the
controller
right? - I would add some logging to
Device::serviceDetailsDiscovered()
- partly because there are othernewState
values not being explicitly handled, and it would be go at least know when the anti-hang fallback is triggered. That said, I don't think that's the issue. - consider enabling Qt's Bluetooth debug log categories - especially
qt.bluetooth
andqt.bluetooth.windows
, and possiblyqt.bluetooth.winrt.service.thread
. eg you could set an environment variable, something likeQT_LOGGING_RULES="qt.bluetooth.debug=true;qt.bluetooth.*.debug=true"
That said,
even if I close the
lowenergyscanner
, reopen it, and reconnect to the target BLE device, the services and characteristics remain the same as before, and theOTA DATA
characteristic still cannot be found.this suggests that underlying BLE driver (ie Windows in your case) is caching the device/service details. It would be interesting to try your code on Linux or macOS for comparison (they will also cache in lots of ways, but differently, so might be interesting).
The following is all speculation (I don't have a Windows platform handy to test, nor a device that works like yours)... I suspect what you need is to reset the Window's BT driver's cache. I have no idea how to do that, but it appears that Qt's BLE WinRT code does have an internal implementation for closing BLE services via
ComPtr<IClosable>
. Tracing Qt's code back, it seems like this only really gets called when discovering. You might suspect the "close" would happen on destruction, but in fact it looks like that's only if anmAbortRequested
flags is set, which appears to be set only when you explicitly requested the disconnect. Again, just speculating, but I think the BLE model here (at least for Linux/BlueZ and WinRT) is that the OS keeps connections to the BLE device, and then "leases" them out to client applications (possibly one at a time, or shared, depending on many factors).Anyway, it seems that the WinRT implementation does "clear the previous services cache" when (re)discovering services. However, it does this by iterating the currently known services, and closing them one by one. In the case of restarting the application, for example, there are no existing services to close, so (presumably) nothing to ask the OS to close, and ultimately nothing to get refreshed.
So... a bit of a long shot, but I would try:
- explicitly disconnecting the device - immediately after writing your characteristic, and/or immediately after receiving the disconnect signal (I'd try all combinations of either/both);
- explicitly try to re-discover services after the initial discovery fails to find the new characteristic (the first discovery would register the WinRT BLE device with Qt, the second would close it before re-discovering).
Be sure to enable Qt's BLE logging as well.
Finally, it's also worth noting there were some WinRT BLE discovery improvements between Qt 6.5.3 and Qt 5.7, so definitely try upgrading Qt first if you can.
Good luck!
- just to be sure, you are re-creating the
-
Hi @swjqq, the code looks pretty reasonable to me. A couple of quick things:
- just to be sure, you are re-creating the
controller
right? - I would add some logging to
Device::serviceDetailsDiscovered()
- partly because there are othernewState
values not being explicitly handled, and it would be go at least know when the anti-hang fallback is triggered. That said, I don't think that's the issue. - consider enabling Qt's Bluetooth debug log categories - especially
qt.bluetooth
andqt.bluetooth.windows
, and possiblyqt.bluetooth.winrt.service.thread
. eg you could set an environment variable, something likeQT_LOGGING_RULES="qt.bluetooth.debug=true;qt.bluetooth.*.debug=true"
That said,
even if I close the
lowenergyscanner
, reopen it, and reconnect to the target BLE device, the services and characteristics remain the same as before, and theOTA DATA
characteristic still cannot be found.this suggests that underlying BLE driver (ie Windows in your case) is caching the device/service details. It would be interesting to try your code on Linux or macOS for comparison (they will also cache in lots of ways, but differently, so might be interesting).
The following is all speculation (I don't have a Windows platform handy to test, nor a device that works like yours)... I suspect what you need is to reset the Window's BT driver's cache. I have no idea how to do that, but it appears that Qt's BLE WinRT code does have an internal implementation for closing BLE services via
ComPtr<IClosable>
. Tracing Qt's code back, it seems like this only really gets called when discovering. You might suspect the "close" would happen on destruction, but in fact it looks like that's only if anmAbortRequested
flags is set, which appears to be set only when you explicitly requested the disconnect. Again, just speculating, but I think the BLE model here (at least for Linux/BlueZ and WinRT) is that the OS keeps connections to the BLE device, and then "leases" them out to client applications (possibly one at a time, or shared, depending on many factors).Anyway, it seems that the WinRT implementation does "clear the previous services cache" when (re)discovering services. However, it does this by iterating the currently known services, and closing them one by one. In the case of restarting the application, for example, there are no existing services to close, so (presumably) nothing to ask the OS to close, and ultimately nothing to get refreshed.
So... a bit of a long shot, but I would try:
- explicitly disconnecting the device - immediately after writing your characteristic, and/or immediately after receiving the disconnect signal (I'd try all combinations of either/both);
- explicitly try to re-discover services after the initial discovery fails to find the new characteristic (the first discovery would register the WinRT BLE device with Qt, the second would close it before re-discovering).
Be sure to enable Qt's BLE logging as well.
Finally, it's also worth noting there were some WinRT BLE discovery improvements between Qt 6.5.3 and Qt 5.7, so definitely try upgrading Qt first if you can.
Good luck!
wrote on 26 Apr 2024, 10:20 last edited by swjqqThis post is deleted! - just to be sure, you are re-creating the
-
Hi @swjqq, the code looks pretty reasonable to me. A couple of quick things:
- just to be sure, you are re-creating the
controller
right? - I would add some logging to
Device::serviceDetailsDiscovered()
- partly because there are othernewState
values not being explicitly handled, and it would be go at least know when the anti-hang fallback is triggered. That said, I don't think that's the issue. - consider enabling Qt's Bluetooth debug log categories - especially
qt.bluetooth
andqt.bluetooth.windows
, and possiblyqt.bluetooth.winrt.service.thread
. eg you could set an environment variable, something likeQT_LOGGING_RULES="qt.bluetooth.debug=true;qt.bluetooth.*.debug=true"
That said,
even if I close the
lowenergyscanner
, reopen it, and reconnect to the target BLE device, the services and characteristics remain the same as before, and theOTA DATA
characteristic still cannot be found.this suggests that underlying BLE driver (ie Windows in your case) is caching the device/service details. It would be interesting to try your code on Linux or macOS for comparison (they will also cache in lots of ways, but differently, so might be interesting).
The following is all speculation (I don't have a Windows platform handy to test, nor a device that works like yours)... I suspect what you need is to reset the Window's BT driver's cache. I have no idea how to do that, but it appears that Qt's BLE WinRT code does have an internal implementation for closing BLE services via
ComPtr<IClosable>
. Tracing Qt's code back, it seems like this only really gets called when discovering. You might suspect the "close" would happen on destruction, but in fact it looks like that's only if anmAbortRequested
flags is set, which appears to be set only when you explicitly requested the disconnect. Again, just speculating, but I think the BLE model here (at least for Linux/BlueZ and WinRT) is that the OS keeps connections to the BLE device, and then "leases" them out to client applications (possibly one at a time, or shared, depending on many factors).Anyway, it seems that the WinRT implementation does "clear the previous services cache" when (re)discovering services. However, it does this by iterating the currently known services, and closing them one by one. In the case of restarting the application, for example, there are no existing services to close, so (presumably) nothing to ask the OS to close, and ultimately nothing to get refreshed.
So... a bit of a long shot, but I would try:
- explicitly disconnecting the device - immediately after writing your characteristic, and/or immediately after receiving the disconnect signal (I'd try all combinations of either/both);
- explicitly try to re-discover services after the initial discovery fails to find the new characteristic (the first discovery would register the WinRT BLE device with Qt, the second would close it before re-discovering).
Be sure to enable Qt's BLE logging as well.
Finally, it's also worth noting there were some WinRT BLE discovery improvements between Qt 6.5.3 and Qt 5.7, so definitely try upgrading Qt first if you can.
Good luck!
wrote on 26 Apr 2024, 12:58 last edited by swjqq@Paul-Colby Thanks for your reply again.
It would be interesting to try your code on Linux or macOS for comparison (they will also cache in lots of ways, but differently, so might be interesting).
I only tried the code on Linux for comparison (I don't have a macOS platform) and found that it didn't cache the service list. The application could find the
OTA DATA
characteristic after performing steps 1-3.1.explicitly disconnecting the device - immediately after writing your characteristic, and/or immediately after receiving the disconnect signal (I'd try all combinations of either/both);
I tried all combinations on Windows 11, but none of them worked.
2.explicitly try to re-discover services after the initial discovery fails to find the new characteristic (the first discovery would register the WinRT BLE device with Qt, the second would close it before re-discovering).
I tried re-discover services and characteristics through two buttons, but none of them worked. The function
QLowEnergyController::discoverServices()
does nothing since it has been called immediately in the slot triggered by theQLowEnergyController::connected()
signal. According to Qt help project, the best workaround is to temporarily turn Bluetooth off. So is it possible to turn Bluetooth off by code?Anyway, it seems that the WinRT implementation does "clear the previous services cache" when (re)discovering services. However, it does this by iterating the currently known services, and closing them one by one.
Please see BluetoothLE example of Windows-universal-samples and the following codes (found at Windows Kits\10\Include\10.0.22621.0\cppwinrt\winrt):
template <typename D> WINRT_IMPL_AUTO(winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDeviceServicesResult>) consume_Windows_Devices_Bluetooth_IBluetoothLEDevice3<D>::GetGattServicesAsync(winrt::Windows::Devices::Bluetooth::BluetoothCacheMode const& cacheMode) const { void* operation{}; check_hresult(WINRT_IMPL_SHIM(winrt::Windows::Devices::Bluetooth::IBluetoothLEDevice3)->GetGattServicesWithCacheModeAsync(static_cast<int32_t>(cacheMode), &operation)); return winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDeviceServicesResult>{ operation, take_ownership_from_abi }; }
In QLowEnergyControllerPrivateWinRT: : discoverServices(), I think it should use
GetGattServicesWithCacheModeAsync
rather thanGetGattServicesAsync
. In obtainCharList, I think it should useGetCharacteristicsWithCacheModeAsync
rather thanGetCharacteristicsAsync
. I have tried to modified the source codes (see the following pictures) and found it was worked. Now the application could find theOTA DATA
characteristic after performing steps 1-3, but the speed of finding services and characteristics slowed down significantly. - just to be sure, you are re-creating the
1/6