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

GPIO API design #2

Open
zsup opened this issue Jan 27, 2016 · 5 comments
Open

GPIO API design #2

zsup opened this issue Jan 27, 2016 · 5 comments

Comments

@zsup
Copy link
Owner

zsup commented Jan 27, 2016

Quote @m-mcgowan from this post on the community:

  • The user-facing API will be idiomatic rust, making effective use of traits. Traits will allow the capabilities of the system resources to be described and checked at compile time. For example, not all pins support PWM - traits will capture this information so attempts to use PWM on a pin that doesn't support it would result in a compiler error.
  • we'll consider using parts of zinc.rs that allow bare metal interaction with the MCU, to provide an equivalent of the CMSIS API that C++ developers have access to on our platform.
  • for discussion: will the user API be object-oriented (OO), functional, procedural? Wiring++2 and original Wiring1 are examples of OO and procedural API paradigms.
@zsup
Copy link
Owner Author

zsup commented Jan 27, 2016

My own comment from the same community post:

I'm not a Rust expert but I've played with it a bit, and am reading up on Rust idioms while I type some thoughts here.

  • I think we should keep the fn main() {} standard basic Rust application format rather than replicating the whole setup() and loop() thing from Arduino. I see that Rust has a loop keyword so perhaps a template Rust app looks like this:
fn main() {
  // setup-y things
  loop {
    // loop-y things
  }
}
  • I'm really excited to use Cargo and have a real package management system. We should lean on Cargo heavily, which will hopefully give us some nice modularity. That also suggests that an empty template app should begin with importing the Particle crate, and then maybe the board definitions are modules? Something like this:
extern crate particle;

use particle::photon as board;

fn main() {
  board::pin_mode(D7, OUTPUT);

  particle::publish("Photon is initialized!");

  loop {
    board::digital_write(D7, HIGH);
    delay(1000);
    board::digital_write(D7, HIGH);
    delay(1000);
  }
}
  • Regarding OO vs. functional, my gut says functional, but at the same time I'm struggling to imagine what the syntax for each would look like, and in particular what an easy-to-use functional programming API would look like, since functional programming can often be scary for n00bs.

@dbrgn
Copy link

dbrgn commented Jan 27, 2016

I think we should keep the fn main() {} standard basic Rust application format rather than replicating the whole setup() and loop() thing from Arduino. I see that Rust has a loop keyword so perhaps a template Rust app looks like this:

Interesting idea. I think that should work out.

An alternative would be to provide setup!() and loop!() macros.

That also suggests that an empty template app should begin with importing the Particle crate, and then maybe the board definitions are modules? Something like this

I like that :)

Regarding OO vs. functional, my gut says functional, but at the same time I'm struggling to imagine what the syntax for each would look like

Some approaches that come to mind:

  • Each GPIO pin is a separate type that implements/derives the supported traits
  • Each GPIO pin is an enum entry. The "receiving" function would have to decide whether the pin supports the functionality or whether it doesn't. This is much less nice than compile-time checking using traits.

Are there more possibilities? I think the first (OO with traits) approach is superior in this case.

Potential usage example:

extern crate particle;

// Board implementation
use particle::photon as board;

// Traits (probably don't have to be imported explicitly)
use particle::photon::DigitalWrite;

// API
use particle::cloud;

fn main() {
  let led_pin = board::D7::new();

  cloud::publish("Photon is initialized!");

  loop {
    led_pin.high();
    delay(1000);
    led_pin.low();
    delay(1000);
  }
}

We just need to make sure that the pin structs are stateless, as there could be multiple instances of the same pin initialized. That wouldn't really make sense.

I'm not sure where functions like delay should live.

Regarding Cargo, I opened a separate issue: #10.

@zsup
Copy link
Owner Author

zsup commented Jan 28, 2016

Loving the suggested syntax above (OO with traits). 👍

@tjpeden
Copy link

tjpeden commented Jan 28, 2016

I agree, I'm down with that syntax

@juleskers
Copy link

Loving it too, I arrived at a similar idea on the forums before I started reading here:
https://community.particle.io/t/rust-on-particle-development-discussion/19142/25?u=juleskers

One difference I see between our proposals:
You suggest stateless pin structs to enable multiple instances (closer to the current api, but may need thread safety stuff with the new system thread feature?)
I proposed a mutex-like guard object around the pin, which wrapper decides the mode.
With my version there would be only one access point and ownership/borrowck would prevent accidentally claiming a pin in two conflicting modes at the same time.
I see merit in both versions, so I'm curious what you guys/girls think.

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

4 participants