-
Notifications
You must be signed in to change notification settings - Fork 101
Building a high level library for embedded-hal compatible drivers #62
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
Comments
Hey @rahul-thakoor The only problem can be that it won't be easy to define that which features should REALLY be part of a trait like that. I absolutely think it's possible to define this kind of trait but needs to be discussed very well. I am really interested in others opinion. |
I think you can look to how Linux has abstracted some if it’s drivers. The AXP209 I’m working on gets exposed via something like four or five drivers: one for the GPIO, one for temperature, one for voltage, another for the power button, etc. I’m not sure what their abstraction is for humidity, but I think one more level of abstraction is a great idea as long as the price is paid at compile time. |
Hey @DomonkosSuranyi ! I think we need to decide on an API for such devices. |
@DomonkosSuranyi I absolutely agree. There should be traits which unify the API for working with sensor data so when using the API one can rely on getting the same information with the same unit (and easily convert to another one if necessary). |
I've been thinking about something like this for the past week or so since I've started working with embedded rust. @therealprof pointed me to this thread. My thoughts on implementing a high level API like this would be to just declare generic traits for driver writers to implement on their drivers. Initialization for each sensor has its own complications so I think it makes sense to leave that up to the driver writer to define whatever they need in that regard. The high level abstraction would just give driver writers standard traits to implement that provide users an interface to get, for example, standard SI units. @therealprof suggested that there was another conversation somewhere where it was determined that it should maybe be broken up even further:
I figure I would revive this discussion and possibly take a stab at making a basic implementation if a general direction can be agreed on. P.S. - Another project that aimed to solve this in another language is Adafruit's Unified Sensor Driver: https://learn.adafruit.com/using-the-adafruit-unified-sensor-driver |
@DomonkosSuranyi btw, https://github.com/copterust/mpu9250 is released and support mpu9250, MPU9255, and MPU6500. Adding support for mpu6050 might be as easy as adding WHO_AM_I value to the list of supported devices. |
Sometimes (and only sometimes), the perspective of a noob (like myself) is useful, because soon my experience and familiarity with the hal will grow, and make all my bad thoughts disappear! Here is the code from an stm32f1 blink program to setup a pin for output let dp = pac::Peripherals::take().unwrap();
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.freeze(&mut flash.acr);
let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
led.set_high().unwrap(); To an embedded Rust newcomer, perhaps someone coming from Arduino, this is an insane level of complexity to simply set up a pin. let dp = stm32::Peripherals::take().unwrap();
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.freeze();
let gpioc = dp.GPIOC.split();
let mut led = gpioc.pc13.into_push_pull_output();
led.set_high().unwrap(); I've spent many an hour reading through both chips' Reference Manuals... so I can see why they are different. I also see why the code is complex. It is less of an abstraction and more about taking what is in the reference manual (or more accurately the svd) and converting it into safe Rust. The problem with this, is that until the new user has a firm grasp of the hal, they constantly need to refer to both the Reference Manual and read the embedded-hal source code (I always have a clone of stm32-rs-nightlies open at all times for reference). It does often feel that it would be quicker for me to just read the reference manual and use the PAC directly. I am sticking with the HAL though, I'm sure it will become easier with time. All this has not been said to criticize, it is just to remind everyone of the pain a new user experiences, and that yes, there is a need for something with a higher level of abstraction. I also think that talking about sensors is probably too high a level. Given that sensors often run on well know protocols like spi and i2c, I feel that it is the setup of these peripherals which needs a higher level of abstraction, before sensor libraries are added on top, Compare the above code to Arduino code, which although being C++ is simple to comprehend. pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH); Now that is more like it! But then it doesn't have the benefits of Rust. The levels of abstraction I'd like to see are
I am sure my expectations are naive, and I'm happy to be told what an idiot I am, but I can't help feeling that the pool of embedded Rust developers needs to grow, and it needs a higher level library to get more people on board. |
@GregWoods I really like this idea. I'm going to think a bit about how we could make a higher level abstraction that is willing to pay some overhead costs for the sake of consistency and ease of use. Thanks for the feedback! |
I think one of the reasons the Arduino code is able to be so simple is because (in our terminology) it's an automatically selected BSP for your specific board, which is always higher level than our HALs or PACs. It knows the precise pinout and resources of your board. That said, even our BSPs are fairly complicated to use in comparison: if let (Some(p), Some(cp)) = (stm32::Peripherals::take(), Peripherals::take()) {
let gpiod = p.GPIOD.split();
// Initialize on-board LEDs
let mut leds = Leds::new(gpiod);
// Constrain clock registers
let rcc = p.RCC.constrain();
// Configure clock to 168 MHz (i.e. the maximum) and freeze it
let clocks = rcc.cfgr.sysclk(168.mhz()).freeze();
// Get delay provider
let mut delay = Delay::new(cp.SYST, clocks);
loop {
// Turn LEDs on one after the other with 500ms delay between them
leds[LedColor::Orange].on();
delay.delay_ms(500_u16);
.... I could imagine a somewhat simpler BSP would be possible, though not having unsynchronised global access to everything at all times will always make things a bit more complicated. It would probably work well with the Arduino-style rt discussed in https://github.com/rust-embedded/cortex-m-rt/issues/250. I think on the whole a higher level abstraction focused on making it as simple as possible to get started with a popular board (e.g. Teensy, Arduinos with Cortex-M, ST discovery boards, etc) should look like a BSP, i.e. we don't need another different layer. |
Not just that but it also makes some important decisions about things like clock speeds and setup and leaves the user completely in the dark about the made choices. It's also very easy to accidentally end up with incompatible peripheral configurations and if you want to do something advanced which is not supported by Arduino out of the box you have to reside to either weird vendor specific functions (like the STM32 Arduino port with the HAL/LL interface) or bang random bits into random adresses and hunt a full day or two for bugs. I think some simplification of our Rust crates may be possible and desirable but I don't think it will ever be as dumbed down as Arduino and I do consider that a good thing. |
I agree; I think the Arduino approach quickly breaks down in larger or more complicated projects and it doesn't offer good support when you need more than its library can provide (as in, it's of course possible to change random settings registers, but going outside its framework quickly leads to trouble). It's important that the WG's work continues to support larger projects requiring very low-level control. However I think there could still be room for a crate that did provide the very easy to use Arduino approach, which knew exactly what platform you were using (i.e., which Arduino board), and made opinionated choices about clocks and peripherals that are likely to be correct, to make it easier to get started. We (or someone) could provide such a crate without hindering efforts to work on powerful alternatives which require more setup. |
I'm closing this old issue as part of today's triage. I think the basic idea of making some high-level traits that represent useful devices like accelerometers is nice, but there's no motivation today to develop then inside the WG. This thread itself has become more of a discussion of alternative or simplified runtimes, which would be better discussed in a separate thread if someone is still interested in working on that. |
I released a crate- rust_gpiozero which is a Rust implementation of the python gpiozero library.
The idea of having such a library is to get more people started with the Rust programming language through physical computing. Also, having a similar API to the original gpiozero library would be appealing to those who are already familiar with it.
Currently, the rust_gpiozero uses the std library. It has components such as LED, Buzzer, and Motor. Having a high-level component interface would make it easier to control hardware with Rust. For e.g creating a LED component and interfacing with it is as easy as :
There are also implementations of methods related a component such as
blink()
for LED orforward
/backward
for Motor.I am planning on making the rust_gpiozero crate compatible with the linux-embedded-hal crate. This will allow integrating
embedded-hal
compatible drivers into the library which would allow interfacing with common components easily irrespective of their controllers/drivers. As more and more drivers are being implemented through the weekly Driver Initiative(#39), having high-level component interfaces seems more intuitive. For e.g, there are several accelerometer controllers( MPU6050, ADXL345, ADXL335, MMA7361, LSM303C) and one could interface with an accelerometer specifying which controller to use.e.g:
A good example of this is the Johhny-Five(http://johnny-five.io/) Framework for javascript robotics.
Any thoughts? Are there similar works being done?
The text was updated successfully, but these errors were encountered: