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

request for advice on "dynamic" device handling #442

Open
zevv opened this issue Jun 26, 2024 · 3 comments
Open

request for advice on "dynamic" device handling #442

zevv opened this issue Jun 26, 2024 · 3 comments

Comments

@zevv
Copy link

zevv commented Jun 26, 2024

(note: I'm pretty much a Rust newbie, so the questions below might be silly and full of nonsense. Feel free to point out any obvious stupidities)

I'm at the process of converting an exiting embedded C++ code base to Rust, but I'm running into some hard problems when trying to work with the device API offered by the esp-idf-hal.

The code is portable over multiple (embedded) platforms and has a simple internal model for representing devices with &dyn trait objects; every platform simply implements drivers for the available hardware to allow the application to run on Linux, ESP32, STM32, NXP, etc. The application runs on a number of different (but similar) devices; it detects the exact hardware configuration at early boot and dynamically instantiates device drivers and assigns these to particular functions. This is not unlike the "device tree" model in the Linux kernel, and very common in embedded applications that exceed your average blinkenlights project.

My problem is that every individual instance of a hardware device (for example gpio, UART) are represented by their own particular type; this makes it very hard (impossible?) for me to dynamically work with device drivers and assignment of their functionality in my code base. At this time I see no way to dynamically represent "any particular UART" or "any particular GPIO" without being forced to use generics to be able to handle the specific device instances.

Also, I just don't seem to properly grok the offered API's here: I found the downgrade() method for gpios with do provide me with some kind of dynamic object type, but I can not seem to find a way to actually use this downgraded type anymore to actually drive the hardware. For the UART driver it seems that the generic per-device-type interface is the only available API.

It's very likely that I am missing something very obvious, any advice much appreciated.

@DaneSlattery
Copy link
Contributor

DaneSlattery commented Aug 10, 2024

Hi Zevv

Have you looked at the embedded-hal and embedded-svc packages? These top level traits define a lot of shared behaviour that is implemented for several devices, you can see a list of implementations here.

The general idea is to target your code for implementers of the trait.

@zevv
Copy link
Author

zevv commented Aug 10, 2024

Sure, and this is where my confusion came from. As I understand it, the embedded HAL specification states the interface that should be offered by any library implementations implementing the HAL. The Embedded Rust book has a section about GPIO which states:

"Pins should provide type erasure methods that move their properties from compile time to runtime, and allow more flexibility in applications."

which is basically exactly the thing I need, but which the esp idf hal GPIO implementation does not seem to offer.

So my current understanding is that the esp-idf-hal GPIO implementation does not conform to the GPIO embedded hal standard. Of course it's also very likely that I do not properly understand how the different interfaces relate to each other and that there is something I need to implement myself to make this work - I'd be happy for any pointers to examples or documentation that I might have missed!

@Vollbrecht
Copy link
Collaborator

In the specific case of gpio pins, we implement different Any*Pin variants that you can use. They basically type transform into something such that you can hold them together in any collection or array.

In general in the rust HAL's you will encounter not many usage of dyn trait's, since the size of the object is not know at compile-time and such, they require to be boxed, which would require for example an allocator - something not available directly in no_std.

We in esp-idf-hal are in the fortunate position to be not affected by that, but that is in the other 90% HAL's the default case ( besides running the linux hal variant).

So what you will more often see is a form of impl traits in function arguments or similar patterns used to be a bit more generalized.

In the earlier days of all the hal's we also often used a lot of generics in public facing API's, though in general they are to cumbersome to use for most day to day usage. So we try to reduce there usage in public API's.

All our peripheral are implementing there respective public trait's, that makes them in most cases relative simple to pass them down the line into something that can consume or borrow them out.

For most of the peripherals we provide, we also provide Drivers. If you spawn a driver you keep it around though often you don't store it in a struct, you create it where you want it and usually doesn't move it much around.

The Drivers we provide are all written with "impl trait in function arguments" style device handling. So they are dynamic in the way to allow different kinds of peripherals as long as they are sized.

If you plan on writing your own Drivers or some abstraction over what we provide you are free to adapt such a pattern, but you are absolutely free to do what you want ontop of what we do here.

Embedded-hal's goal is to bring a reasonable common denominator between vastly different platforms. From running an ATtiny with just 2kb of ram to running on a embedded Linux machine with GB's of ram. All hal's try to adhere to it so people can write more application drivers, but it has also its clear limits.

Its try's to archive this goal by bridging between application driver authors and different hal's by providing a trait interface. The end user hopefully than only needs to plump the driver and the hal together. And that's it, the rest is up to individual hal's and drivers to work with that.

I hope i make some sense, and please excuse my subpar usage of the english language.

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

No branches or pull requests

3 participants