embassy-rs / nrf-softdevice

Apache License 2.0
269 stars 80 forks source link

Async callback to gatt_server::run #184

Open uhinze opened 1 year ago

uhinze commented 1 year ago

Thank you for this project, I'm starting with Rust and embedded programming and happy to have found this.

My current issue: It doesn't seem possible to make the callback f to gatt_server::run async.

What I want to achieve is: Upon receiving a BLE event, turn on the LED for a couple of seconds, then turn off. Pseudo-code:

led.set_high();
Timer::after(Duration::from_secs(1)).await;
led.set_low();

Happy for any pointers how to implement this another way.

alexmoon commented 1 year ago

It's not possible for the callback to be async. You can do what you're wanting by sending a message or signal from your callback to an async task or spawning an async task from the callback.

sureshjoshi commented 1 year ago

@uhinze Did you come up with a re-usable pattern that you liked? I'm working on something where I'd need this too. Almost identically start something with a timer to turn it off later, concept. e.g. Start scanning for devices with a timeout, and accept a user-generated stop otherwise.

I had originally planned to spawn a task for this - but I wasn't sure how that works at runtime. As in, can a spawned task functionally act as a lambda?

uhinze commented 1 year ago

I just solved the problem on my end. @alexmoon the pointer to channels was very helpful, thank you! Took me a while to figure out you meant this Channel. So now I'm spawning an async task and wait for new messages in the channel in an endless loop.

I additionally had to solve the challenge to be able to use the Server and current Connection in the other task, as I wanted to send a response to the BLE client after successful processing. I solved this using static_cell, Mutex, and RefCell.

@sureshjoshi I'm not sure if your challenge is the same one. I understand you want to interrupt device scanning after some timeout? Not sure how that would work tbh. A task in Embassy from my understanding has a static lifetime, so it's not really meant for a request/response style interaction. Would suggest to open a new issue for this.

sureshjoshi commented 1 year ago

Thanks for the reply!

I think what you've landed on might work close to what I had in mind overall. I had originally imagined selecting on some futures, but I would also want a "clean" scan stop, rather than an interrupted future (e.g. return scanned devices).

My other idea was to do something similar to what you're doing which is an endless task that accepts control messages via a channel.

I'll be testing this out over the weekend - so one way or another, I'll have some sort of solution :)

osiler commented 1 year ago

The solution I found was to use a static cell, and share &'static gatt_server::MyServer as arguments to each task.