-
-
Notifications
You must be signed in to change notification settings - Fork 179
No automatic pre discovery #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…24 bytes of heap for a small device (LYWSD03MMC)
…teristicVector instead) saving in total (compared to the current master) 1,508 bytes of program memory and 6,500 bytes of heap for a small device (LYWSD03MMC)
…eading to faster connections, less heap usage and less drainage of the peripheral's battery. Characteristics are retrieved only when needed
This PR needs PR #37 |
@h2zero this is the last 'big' change directed to memory usage and performance that I want to suggest. I have a few other ideas, so maybe I do a few more PRs. What I do want to do is add a lot more
Now the compiler forbids this because of lack of constness. But I will wait with this until you have processed my other PRs |
Yes, I wanted to do some implementation of this at some point, as you noticed a lot of resources are wasted on attributes we may not want, current implementation was a "get it working" one 😄. NimBLE does provide a lot more API's for things, we could even skip discovery all together and just read by uuid etc.. but this is a good start. |
Bluedroid is providing discovery services from cache, which should be implemented, so each next connection and discovery should be faster. To be honest i didnt test it so i dont know if this is implemented properly. This is similar to android (and maybe iOS) behavior. |
@chegewara unfortunately NimBLE doesn't provide this, the developers wanted to leave that up to the app to handle. I have implemented it here by providing a parameter to |
I'm going to take a look into the stack for this one, I like the idea of not pre-fetching all the attributes if possible but I need to see how NimBLE handles that. Ideally we could just get the services/characteristics we want and not the whole database but if NimBLE just fetches all and filters the results that would be bad for time/battery if looking up more than one. Also need a way to get the descriptors. |
That would be nice indeed. I ran into that I did not find a way to get the handle of just one service, only to discover all of them. Then I decided to have the client discover all services on connection (like it does now) and then stop discovering characteristics and descriptors. But if you know a way to retrieve just one service at a time that would also save the discovery (and the heap memory) for unneeded services.
Yes I studied the function to read by uuid as well, but I came to the conclusion that you still need handles for that, and the only way to get the handles was by reading the characteristic (and the services) first.
I can't imagine that this is impossible, but I ran into the problem that the end handle used for retrieving the descriptors of a remote characteristic is the start handle of the next characteristic minus 1 in the current implementation. Since only the needed characteristics are read I could not determine the end handle, but I am sure you know a way! |
... taking up discussion from #53 The connection is much faster without the building of the database. If the stack builds the full database pon first connection, I would expect that connecting would take much longer. So I guess the stack does not build the database, also because a fucntion like ble_gattc_disc_svc_by_uuid exists. |
That’s where the question come from though. The stack may just discover all until it finds that uuid then stop, if the handle is early in the discovery it will be quick. But if that uuid has a handle of say 0xFFF0 then it will have discovered the whole thing and only reported back the one. That’s assuming that’s how it works, my initial investigation says it is but more work required. |
Here is a great description of how this works if you’re interested. |
I'll have a look,thnx! |
I got the answer to my question, looked in the core spec and indeed services and characteristics are discoverable individually! So then the problem becomes how to get the descriptors... I'll have to look into that tonight, I might have it wrong in the library anyway. |
Nice!
Also nice, I will check that tomorrow then
Oops 😰 |
I think looking up one descriptor is ble_gatts_find_dsc. Now only this has to be implemented. And of course the automatic discovery of the descriptors has to be checked... |
That is for the peripheral to get a descriptor from its local database. I have located the source of the trouble with getting the descriptors of a characteristic without getting all of them. It’s due to NimBLE not providing an end_handle which is why I did what’s in the library to compensate. I’ll see if I can patch it and submit a PR there. |
They are making simply assumption that next service/characteristic handle is end of previous end_handle (or next - 1). |
Yep, I looked at the code and the core spec. Seems if you need the end handle you have to ask for the next characteristic. I made a patch to do just that. |
Closed because the ideas of this PR are developed in branch |
Optional no automatic discovery of characteristics and descriptors, leading to faster connections, less heap usage and less drainage of the peripheral's battery. Characteristics are retrieved only when needed.
Calling
BLEClient *pClient = BLEDevice::createClient()
leads to the current behavior. However callingBLEClient *pClient = BLEDevice::createClient(false)
does retrieve all services associated with this peripheral when callingpClient->connect()
but it does not retrieve the characteristics for each service nor the descriptors for each characteristic! This saves a lot of connection time. Heap memory is also saved, because the characteristics and descriptors are not stored in their vectors. Moreover, because the peripheral does not need to send these characteristics and descriptors this saves battery life for the peripheral.When
pClient->getService(...uuid...)->getCharacteristic(...uuid...)
is called, first the characteristic is looked up in the vector. If found that characteristic is returned. If not found, it is retrieved once and stored into the vector (if present in the peripheral of course).For my use case, a LYWSD03MMC temperature / humidity sensor, this leads to the storage of 2 characteristics only (the payload characteristic and the battery characteristic) and no descriptors. Without this PR a total of 32 characteristics are read and also 32 descriptors. In my case this PR saves heap memory, connection time and battery life for 32 - 2 + 32 = 60 objects!
This PR is not yet thoroughly tested (in fact I only tested a few
getCharacteristic()
and acanRead()
), but since it is acting almost the same as with pre retrieval of the characteristics, I think it should work.It is easy to see the behavior by calling
Serial.printf("%s\n", pClient->toString().c_str())
each time a newgetCharacteristic()
is performed. At first a client is populated with services only. The client is 'growing' with new characteristics after each discovery.One drawback is that without automatic pre discovery the descriptors can not be read. For retrieving descriptors also 'on the fly' and end handle is needed. This end handle is one less than the handle for the next characteristic belonging to a service, but since not all characteristics are retrieved, I am not aware of a way to find the end handle in another way. If someone has a good suggestion: welcome!