iOS Question [B4X] BLE refresh available Services and Characteristics - Missing Feature?

Mike1970

Well-Known Member
Licensed User
Longtime User
Hi everyone, I noticed an important missing feature in the BLE library.
It seems to not be possibile to refresh the ServiceList, given the fact that the only place where you can get it is in the "Connected" event....

I'have a BLE Device that exposes a Service only after you complete a procedure... how can I get the updated ServiceList after that?
I need to understand it both for iOS and Android.

(for testing purposes I use the "nRF Connect" Nordic App on Android, and it has the "refresh services" button)

Thanks in advance
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Start with B4A. It should work assuming that you know the service UUID.

You can get all services with:
B4X:
Dim gatt As JavaObject = manager.As(JavaObject).GetField("gatt")
Dim services As List = gatt.RunMethod("getServices", Null)
For Each service As JavaObject In services
 Log(service.RunMethod("getUuid", Null)
Next
 
Upvote 0

Mike1970

Well-Known Member
Licensed User
Longtime User
Start with B4A. It should work assuming that you know the service UUID.

You can get all services with:
B4X:
Dim gatt As JavaObject = manager.As(JavaObject).GetField("gatt")
Dim services As List = gatt.RunMethod("getServices", Null)
For Each service As JavaObject In services
 Log(service.RunMethod("getUuid", Null)
Next
Ok thanks for Android solution! I will try I later.


At the moment I'm developing the iOS part.. I wanted to be sure, more important, that can be done here as well.
How can I do the same thing with iPhone?
 
Last edited:
Upvote 0

Mike1970

Well-Known Member
Licensed User
Longtime User
Start with B4A. It should work assuming that you know the service UUID.

You can get all services with:
B4X:
Dim gatt As JavaObject = manager.As(JavaObject).GetField("gatt")
Dim services As List = gatt.RunMethod("getServices", Null)
For Each service As JavaObject In services
 Log(service.RunMethod("getUuid", Null)
Next

I tried to send data to the service using the UUID (that I know in advance) ignoring the fact that the service was not discovered... but actually the device is advertising it (tested with nRF connect)

This is the result on B4i
<B4IExceptionWrapper: Error Domain=caught_exception Code=0 "Service not found" UserInfo={NSLocalizedDescription=Service not found}>

So seems to be mandatory to perform a refresh of the services list before I can send data... any solution to this?
I don't now the native language, but maybe this article can be useful(?)
 
Last edited:
Upvote 0

Mike1970

Well-Known Member
Licensed User
Longtime User
This is expected. It works differently in Android and iOS. My answer was relevant for Android.
As I wrote I recommend you to start in this case with B4A. You can make a simple test and get it working.

This is not supported out of the box in iBLE library but it is probably possible with some work.
First of all, thank you very much for the answer.

let me explain 😊:
I’m currently making both the ble device (with an Esp32) and the app needed to use this device.

I want to implement a sort of custom security mechanism… and actually I successfully did it on device-side, so no problem there now.

But this mechanism NEEDS the fact that the connected smartphone must be able to refresh the advertised services while connected.

given the fact that I have to develop both Android and iOS app, I need that both can do this, as long as one of the them can't, I have to abandon the idea of doing so… and change the device firmware to work in another way.

so if it works with Android is cool, but if you tell me that I can’t do it also for iOS, I will don’t use it neither for Android.
The client-side logic must be identical.
 
Upvote 0

Mike1970

Well-Known Member
Licensed User
Longtime User
start with a working proof of concept
Hi, i tried implementing that function in the BLEExample code, but it does not work.
Newest services does not appear (with nRF Connect i see them after refreshing).
 

Attachments

  • BLEExample_RefreshServices.zip
    12.8 KB · Views: 77
Upvote 0

Mike1970

Well-Known Member
Licensed User
Longtime User
You can force it to discover services again with:
B4X:
manager.As(JavaObject).GetField("gatt").RunMethod("discoverServices", Null)
It should cause the Connected event to be raised again with a new list of services.
So it became something like this?
B4X:
Sub Refresh_Services
    Dim gatt As JavaObject = manager.As(JavaObject).GetField("gatt")
    gatt.RunMethod("discoverServices", Null)
    Dim services As List = gatt.RunMethod("getServices", Null)
    For Each service As JavaObject In services
        Log(service.RunMethod("getUuid", Null))
    Next
End Sub

after calling this function i see the "connected" output again, and some "retries", but the additional services now showed up.
It's supposed to behave like this?
 
Upvote 0

Mike1970

Well-Known Member
Licensed User
Longtime User
No. Call the code I posted and then check the Services parameter in the Connected event.
Ohhh ok only the last line you posted.

So I did like this:
B4X:
Sub btnRefresh_Click
    Dim gatt As JavaObject = manager.As(JavaObject).GetField("gatt")
    gatt.RunMethod("discoverServices", Null)
End Sub

Ouptut console:
-----------------------------------------------------------------------------------------------
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create (first time) **
Call B4XPages.GetManager.LogEvents = True to enable logging B4XPages events.
** Activity (main) Resume **
Found: Revalue ECU, C0:49:EF:D2:29:1A, RSSI = -42, (MyMap) {-1=[B@8640ee3, 9=[B@5b5b0e0, 10=[B@780bf99, 18=[B@588625e, 0=[B@bbeb63f}
connecting

Discovering services.
Connected
Setting descriptor. Success = true
writing descriptor: true
Setting descriptor. Success = true
writing descriptor: true
<--- here i pressed the refresh button
Connected
Setting descriptor. Success = true
writing descriptor: false
retries: 4
retries: 3
retries: 2
-----------------------------------------------------------------------------------------------



All new services appeared as wanted in _Connected event sub. 🆗



I think we are ok with the Proof of Concept, this is what I was thinking to do both in Android and iOS.
Now.. I don't know if someone has an idea on how to do the same thing we just did for Android, but meanwhile i've started a stackoverflow thread and the only answer I got is the following:
Call -[CBPeripheral discoverServices:] a second time.
Make sure that your device sends a Service Changed indication so that the OS knows to re-scan. (iOS doesn't necessarily honor this as tightly as the spec allows, but just follow the spec and it should work correctly.)
 
Upvote 0

Mike1970

Well-Known Member
Licensed User
Longtime User
You can force a services refresh in B4i with:
B4X:
Dim peripheral As NativeObject = manager.GetPeripheralObject
Dim del As NativeObject = peripheral.GetField("delegate")
del.RunMethod("peripheral:didDiscoverServices:", Array(peripheral, Null))
The Connected event will be raised when it completes.
I tried but this is the output when i call it:

B4X:
Services discovery completed.
Unexpected event (missing RaisesSynchronousEvents): manager_connected:
 
Upvote 0

Mike1970

Well-Known Member
Licensed User
Longtime User
Ignore it.
sorry I wasn't clear, but the services list in the _connected event that fires after I refresh the services is empty

Schermata 2023-03-13 alle 12.45.02.png
 
Upvote 0

Mike1970

Well-Known Member
Licensed User
Longtime User
Try it with a different peripheral
I do not have another peripheral to test.
I used an ESP32.

The thing is that giving the case that I'm missing something that iOS needs on the esp32, however I should AT LEAST see the services I see at the beginning, not the new ones maybe.. but the others that I saw before disappeared too..

I can do a test on esp32:
I DO NOT start new services to see If is that the problem or not... At this point, in B4i, the second time the _connected event fires I should see the same exact list as the first time it fired.
 
Upvote 0

Mike1970

Well-Known Member
Licensed User
Longtime User
I can do a test on esp32
Ok the result are:
  • IF the device DOES NOT mutate its services runtime -> the B4i refresh function works and gives back the same list
  • IF the device DOES mutate its services runtime -> the B4i refresh function does not work and return an empty list
Now... I don't know if iOS side is there something else that could cause the problem.
The only thing I heard about is the one that on Stack Overflow was told me:

Make sure that your device sends a Service Changed indication so that the OS knows to re-scan
I need to understand how to do it with esp32 and Arduino framework, then, if still does not work must be something on iOS side.
And if not, I will abandon this route..
 
Upvote 0
Top