Skip to content
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

Best approach for high bandwidth transfer? #141

Open
cmorganBE opened this issue Feb 4, 2024 · 13 comments
Open

Best approach for high bandwidth transfer? #141

cmorganBE opened this issue Feb 4, 2024 · 13 comments

Comments

@cmorganBE
Copy link

I'd like to stream data as quickly as possible via a notify characteristic. In the past I've done this like the nrf example did, by just calling the characteristic update function as quickly as possible, https://github.com/NordicPlayground/nrf52-ble-image-transfer-demo/blob/b1338a7eb983aa5794fffa956c5928a0074f70c6/ble_image_transfer_service.c#L353.

For esp-nimble-cpp it wasn't clear how to know if the data was accepted into the stack:

while(data_to_send)
{
   pCharacteristic->setValue(xxx);
   pCharacteristic->notify();

  // how to know if the stack accepted the data?
}
@h2zero
Copy link
Owner

h2zero commented Feb 6, 2024

There is a callback that is called when the notification is sent. Also your connection interval parameter will determine the rate that you can send packets, minimum 7.5ms.

@cmorganBE
Copy link
Author

@h2zero do you mean the NimBLECharacteristicCallbacks::onNotify() callback or is there a better location for this event that you were proposing?

tkx on the connection interval parameter, I'll adjust that down once I get the proof of concept in place.

@cmorganBE
Copy link
Author

btw @h2zero, this is a very nice library. I was doing it with nimble directly and there is a TON of syntactic overhead, so much that I was looking at wrapping the api for easier use, searched and found this and it helps a ton to take care of the very verbose syntax necessary to set this stuff up.

@cmorganBE
Copy link
Author

@h2zero, sorry to bug you but I'm close to getting back to this. Were you referring to the NimBLECharacteristicCallbacks::onNotify() callback to determine when I'd be good to send the next notify?

@mickeyl
Copy link

mickeyl commented Feb 11, 2024

Not wanting to derail your issue, but let me join in here, since I'm having a similar question. My goal is the same (low latency high bandwidth), although I'm only using GATT for the discovery part. I'm then using my L2CAP extensions for esp-nimble-cpp to open an L2CAP connection from iOS and transfer data in a stream oriented way.

I wonder how I could make sure that I'm a) using the LE 2M PHY for the L2CAP connection and b) using the "best" connection parameters to maximize throughput.

Where/How can I tweak all those parameters?

(@cmorganBE BTW., perhaps L2CAP would be an option for you as well, since it has way less overhead than transferring the data via GATT).

@mickeyl
Copy link

mickeyl commented Feb 11, 2024

Using PacketLogger on macOS, I now found out that the default setting seem to indeed enable the LE 2M PHY:

Bildschirmfoto 2024-02-11 um 17 52 01

I also found the proper place in the GAP service connection callback to adjust the connection parameters.
That said, no changes there seem to have a measurable effect. I'm afraid I'm already at the maximum bandwidth using BLE. I'm getting roughly 13KB/sec (net payload) with an ESP32S3 talking to macOS. I would guess iOS devices being no different.

@cmorganBE
Copy link
Author

@mickeyl would the l2cap approach work with ble services on ios and android devices? Support for easy mobile integration and minimal effort on the sw side would be preferred if the performance was 13KB/sec via GATT, that's fast enough for my use.

@mickeyl
Copy link

mickeyl commented Feb 12, 2024

@cmorganBE In fact, I developed the L2CAP support for esp-nimble-cpp exactly because it's faster (no GATT encapsulation) and way simpler to interact with in iOS and Android, since you don't need special care to fragment your packages. At least for iOS, I get a nice stream-based abstraction when I have opened an L2CAP connection channel ­– and from what I have seen in the Android API, it's the very same.

Why ­– despite all these measures ­– the bandwidth is still lower than I'd like, is a story for further debugging with BLE low level tools, I'm afraid.

@cmorganBE
Copy link
Author

@mickeyl is your L2CAP support in mainline esp-nimble-cpp? Are there examples of how to make use of it to transfer data? Normally I'd dig in more but I have some schedule constraints on time here.

Oh, and can I mix and match L2CAP usage and gatt if I want characteristics with notify, indicate, read/write etc?

@mickeyl
Copy link

mickeyl commented Feb 12, 2024

@cmorganBE Not yet in mainline, I maintain a fork. Although I have been using it for some months now without any problems, it's not yet ready for inclusion before adding an example.

Yes, L2CAP usage is mix-and-match with GATT. In fact, you will have to use GATT in order to discover the device, before opening an L2CAP channel.

I'm pretty sure it would very well fit your example, but if you're short on time, it might pose a risk for you. I try to submit it within the next few weeks, but I'm right in the middle of ramping up production of my first ESP32S3-based hardware, so time is also tight for me ;-)

@cmorganBE
Copy link
Author

@h2zero sorry to ping again, is NimBLECharacteristicCallbacks::onNotify() the callback where enough processing has been performed that the stack is ready for the next notify to be called?

@h2zero
Copy link
Owner

h2zero commented Feb 13, 2024

Sorry for the late response, yes that is the callback I was referring to.

@cmorganBE
Copy link
Author

@h2zero if I call:

NimBLECharacteristic* pLogs;

                pLogs->setValue(logEntries[logIndex]);
                pLogs->notify();

from inside of the onNotify it looks like the notify() is triggering an onNotify() and this ends up in a recursive loop.

    void onNotify(NimBLECharacteristic* pCharacteristic) override
    {
        printf("%s : onNotify(), value: %s\n",
               pCharacteristic->getUUID().toString().c_str(),
               pCharacteristic->getValue().c_str());

                // this isn't exactly the code, i do bounds checking in the real case
                pLogs->setValue(logEntries[logIndex]);
                logIndex++;
                pLogs->notify();            
    }

Correct approach is to instead signal to an external thread from onNotify() and have that call setValue() and notify()?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants