rust-embedded / embedded-hal

A Hardware Abstraction Layer (HAL) for embedded systems
Apache License 2.0
2.01k stars 202 forks source link

Asynchronous embedded-hal traits roadmap #356

Closed eldruin closed 10 months ago

eldruin commented 2 years ago

We have started a project to develop a asynchronous versions of the embedded-hal traits, at the moment championed by @Dirbaio. This will be developed as a separate crate called embedded-hal-async until it is ready for integration inside a module in embedded-hal. This can be found in the subfolder embedded-hal-async

Roadmap:

Asynchronous execution holds great potential for embedded. Please join us!

adamgreig commented 2 years ago

Would it be worth cutting a 0.1.0-alpha (or just 0.1.0) release with the existing I2C, SPI etc async traits, while Serial is worked out?

Given the higher complexity I also expect CAN might take a while longer to figure out than the others, so at least getting the basic traits onto crates.io would let people start using them without git dependencies.

kalkyl commented 2 years ago

Could we have an alpha 1 release of this? I have a pending PR for embassy that depends on the new SPI transaction API in master. Cheers!

quentinmit commented 2 years ago

I've started trying to port some of my code over to embedded-hal-async, and I'm wondering if you have any guidance for how device driver crates should be ported. It's not a great story if we tell everyone they have to maintain two copies of every function, where the ~only difference is async and .await sprinkled in one of them.

I'm wondering if there might be a procedural macro solution that would allow a single device driver to be compiled in both blocking and async modes. Has anyone explored that? Or is it more trouble than it's worth and we expect crates to quickly move over to the new traits? (Presumably that's blocked on the necessary features hitting stable.)

Dirbaio commented 2 years ago

there's a blocking to "fake async" adapter that you can use to get an async impl out of any HAL with only a blocking impl. It's "fake async" because it will block and never "async yield", but for many use cases that's fine (spi/i2c register reads/writes are fast).

This allows maintaining just one version of the driver (async) but still using it with HALs that don't support async.

You still need an "executor", but since there's no yielding/waking going on a very dumb one like loop { fut.poll() } is enough (roughly same idea as nb::block!()), so it should still be very lightweight.

quentinmit commented 2 years ago

I saw that adapter, but I feel like you wouldn't want to do "async only" until both these traits are stable and they can be compiled with stable Rust, and probably an fut::block!() macro that mirrors nb:block!() exists for people who are not using async.

ryankurte commented 2 years ago

I'm wondering if there might be a procedural macro solution that would allow a single device driver to be compiled in both blocking and async modes. Has anyone explored that?

i've spent way more time than i should have trying to achieve this with traits with no real success (though radio is kinda neat in that our higher-level blocking traits automagically become async)... definitely interested in whether we can do useful things with proc macros without too much magic (tm).

Or is it more trouble than it's worth and we expect crates to quickly move over to the new traits?

i don't think we can expect folks to all move to async (or vice versa), so the story for compatibility and combining the two seems fairly important!

quentinmit commented 2 years ago

I'm wondering if there might be a procedural macro solution that would allow a single device driver to be compiled in both blocking and async modes. Has anyone explored that?

i've spent way more time than i should have trying to achieve this with traits with no real success (though radio is kinda neat in that our higher-level blocking traits automagically become async)... definitely interested in whether we can do useful things with proc macros without too much magic (tm).

I started writing something like this and then I discovered the maybe-async-cfg crate already does pretty much exactly what I was thinking of.

I was able to very easily adapt my async device driver into something that compiles both sync and async versions side-by-side. I don't quite love the user ergonomics (it generates types named FooSync and FooAsync to keep them separate; I think it would be better if they could just be generated in two different modules, so the user would only have to change a use statement to swap between them, but then the problem is that async is a reserved identifier and use device::r#async::Device is not a great UX either.)

@ryankurte Maybe take a look at that crate and see what you think?

kalkyl commented 2 years ago

I was able to very easily adapt my async device driver into something that compiles both sync and async versions side-by-side. I don't quite love the user ergonomics (it generates types named FooSync and FooAsync to keep them separate; I think it would be better if they could just be generated in two different modules, so the user would only have to change a use statement to swap between them, but then the problem is that async is a reserved identifier and use device::r#async::Device is not a great UX either.)

I think the accepted naming convention for async modules is asynch :)

elpiel commented 1 year ago

Hello everyone, I'm joining this conversation to see the progress of the issue. It seems that there are still a few things missing (to my understanding) in order to release a stable version of the async traits, however, there is one thing that can be done additionally:

Digital Pin (input, output, etc.) in the async hal should be added, until the traits get merged inside the embedded-hal, until then we still have to rely on embedded-hal for the async versions when dealing with digital pins.

madsmtm commented 12 months ago

Discussion point from https://github.com/rust-embedded/embedded-hal/issues/177:

Should embedded-hal-async be merged back into embedded-hal, now that async fn in traits are stable? And if so, how do we best do that?

madsmtm commented 12 months ago

Another thing: Is embedded-hal-nb still useful, or should it be deprecated now that we can do async things in stable?

Dirbaio commented 11 months ago

my personal opionion is we should deprecate it (i.e never release embedded-hal-nb 1.0), but it's up to the HAL team as a whole to decide... It was discussed a while back and the conclusion was "don't deprecate, or at least not yet". Perhaps now that stable AFIT is here it's worth a second discussion?

jessebraham commented 11 months ago

For what it's worth, I never encountered a library that actually implements the embedded-hal-nb traits in the wild. Most of the dependent packages are HALs, based on what's published at least.

eldruin commented 10 months ago

embedded-hal-async 1.0.0 has now been released. Thank you so much to everyone that was worked and helped through all this time to make this a reality. 🎉