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

Add GPIO capability #406

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a540a1d
Basic GPIO WIT
Quantaly May 30, 2023
a54cc53
Starting work on GPIO
Quantaly May 31, 2023
bc18e21
GPIO: Raspberry Pi pin construction
Quantaly May 31, 2023
f4ef59b
Remove the RaspberryPiImplementorInner struct
Quantaly Jun 1, 2023
f64c422
Implement Raspberry Pi GPIO methods
Quantaly Jun 1, 2023
5f424d4
Actually call the pin destructor
Quantaly Jun 1, 2023
ab486e3
Applications reference GPIO pins by name
Quantaly Jun 1, 2023
f5b0f31
GPIO configuration
Quantaly Jun 2, 2023
659a9e5
Add pullup/pulldown and initial output state
Quantaly Jun 5, 2023
87c201d
Unit tests for pin configuration parsing
Quantaly Jun 5, 2023
4445bf4
Made initial demo of input(pushdown) and output
joeyvongphasouk Jun 5, 2023
33adb2e
Added cargo dependencies
joeyvongphasouk Jun 5, 2023
ddd56a7
added flicker to gpiodemo
BrendanBurm Jun 5, 2023
400e0c5
Added additional documentation onto gpio demo. Added more test cases …
joeyvongphasouk Jun 7, 2023
f328c0e
Added documentation to tests.rs and lib.rs in gpio crate
BrendanBurm Jun 7, 2023
b77b6e7
Write documentation for GPIO capability stuff
Quantaly Jun 7, 2023
5067c6c
Merge my GPIO documentation with Brandon's
Quantaly Jun 7, 2023
424a3fc
Merge pull request #1 from mines-370-wasm-squad/gpio-dev
BrendanBurm Jun 7, 2023
f0f022e
Merge remote-tracking branch 'upstream/main' into gpio
Quantaly Jun 8, 2023
031a844
Add PWM output pin to GPIO
Quantaly Jun 8, 2023
3ad4258
Update GPIO demo with PWM
Quantaly Jun 8, 2023
76fbde2
Fix bug in GPIO demo
Quantaly Jun 8, 2023
1935c41
Fix other bug in GPIO demo
Quantaly Jun 8, 2023
4f786fe
Specify type with annotation instead of cast
Quantaly Jun 8, 2023
cd18c2b
Revised documentation of gpio demo
joeyvongphasouk Jun 9, 2023
d7287c6
Update GPIO documentation
Quantaly Jun 12, 2023
c588203
Merge documentation updates
Quantaly Jun 12, 2023
cccea05
Use crate doc comment syntax
Quantaly Jun 12, 2023
e53a187
Emit warnings instead of errors for PWM
Quantaly Jun 12, 2023
210aa31
Merge pull request #2 from mines-370-wasm-squad/gpio-pwm
BrendanBurm Jun 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ slight-common = { workspace = true }
slight-sql = { workspace = true, features = ["postgres"], optional = true }
slight-http-server = { workspace = true, optional = true }
slight-http-client = { workspace = true, optional = true }
slight-gpio = { workspace = true, optional = true }
anyhow = { workspace = true }
log = { workspace = true }
tokio = { workspace = true }
Expand All @@ -45,7 +46,7 @@ tempfile = { workspace = true }
rand = { worspace = true }

[features]
default = ["blob-store", "keyvalue", "distributed-locking", "messaging", "runtime-configs", "sql", "http-server", "http-client"]
default = ["blob-store", "keyvalue", "distributed-locking", "messaging", "runtime-configs", "sql", "http-server", "http-client", "gpio"]
blob-store = ["dep:slight-blob-store"]
keyvalue = ["dep:slight-keyvalue"]
distributed-locking = ["dep:slight-distributed-locking"]
Expand All @@ -54,6 +55,7 @@ runtime-configs = ["dep:slight-runtime-configs"]
sql = ["dep:slight-sql"]
http-server = ["dep:slight-http-server"]
http-client = ["dep:slight-http-client"]
gpio = ["dep:slight-gpio"]

[workspace.package]
version = "0.5.1"
Expand All @@ -76,6 +78,7 @@ slight-sql = { path = "./crates/sql" }
slight-http-server = { path = "./crates/http-server" }
slight-http-client = { path = "./crates/http-client" }
slight-http-api = { path = "./crates/http-api" }
slight-gpio = { path = "./crates/gpio" }
wit-bindgen-wasmtime = { git = "https://github.com/fermyon/wit-bindgen-backport", features = ["async"] }
wit-error-rs = { git = "https://github.com/danbugs/wit-error-rs", rev = "05362f1a4a3a9dc6a1de39195e06d2d5d6491a5e" }
wasmtime = "8.0.1"
Expand Down
22 changes: 22 additions & 0 deletions crates/gpio/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "slight-gpio"
version = "0.1.0"
edition = { workspace = true }
authors = { workspace = true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add your names to the authors of this crate

license = { workspace = true }
repository = { workspace = true }

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = { workspace = true }
rppal = { version = "0.14.1", optional = true }
slight-common = { workspace = true }
slight-file = { workspace = true }
tracing = { workspace = true }
wit-bindgen-wasmtime = { workspace = true }
wit-error-rs = { workspace = true }

[features]
default = ["raspberry_pi"]
raspberry_pi = ["dep:rppal"]
17 changes: 17 additions & 0 deletions crates/gpio/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# GPIO support

This crate provides the ability for SpiderLighting applications to interact with GPIO pins.

Pins are named in the slightfile and passed a configuration string with the following slash-separated parameters:

- Pin number
- Pin mode
- `input`
- `output`
- `pwm_output`
- Optional mode-specific configuration
- For input: `pullup` or `pulldown`
- For output: initially set to `low` or `high`
- For PWM output: the PWM period in microseconds, if supported by the implementation

See the [example application](../../examples/gpio-demo/).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's definitely not required to close this PR, but it might be helpful to add some comments to aid someone getting started w/ this capability – I have a Raspberry Pi at home like the one in your video, and would love to try using this (:

154 changes: 154 additions & 0 deletions crates/gpio/src/implementors/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use std::fmt::{self, Debug, Formatter};
use std::str::FromStr;
use std::sync::Arc;

use crate::{gpio, Pin, Pull};

#[cfg(feature = "raspberry_pi")]
pub mod raspberry_pi;

/// A GPIO implementation.
///
/// This trait is not referred to directly by the linked capability, but is used to construct pin resources implementing the other traits in this module.
pub(crate) trait GpioImplementor {
/// Constructs an input pin resource.
fn new_input_pin(
&mut self,
pin: u8,
pull: Option<Pull>,
) -> Result<Arc<dyn InputPinImplementor>, gpio::GpioError>;
/// Constructs an output pin resource.
fn new_output_pin(
&mut self,
pin: u8,
init_level: Option<gpio::LogicLevel>,
) -> Result<Arc<dyn OutputPinImplementor>, gpio::GpioError>;
/// Constructs a PWM output pin resource.
fn new_pwm_output_pin(
&mut self,
pin: u8,
period_microseconds: Option<u32>,
) -> Result<Arc<dyn PwmOutputPinImplementor>, gpio::GpioError>;
}

impl dyn GpioImplementor {
/// Parse the provided configuration string and construct the appropriate pin resource.
pub(crate) fn parse_pin_config(&mut self, config: &str) -> Result<Pin, gpio::GpioError> {
let mut config_iter = config.split('/');

let pin_number = config_iter
.next()
.ok_or_else(|| gpio::GpioError::ConfigurationError(String::from("no pin number")))?;
let pin_number = u8::from_str(pin_number).map_err(|e| {
gpio::GpioError::ConfigurationError(format!("invalid pin number '{pin_number}': {e}"))
})?;

match config_iter.next() {
Some("input") => {
let pull = if let Some(pull) = config_iter.next() {
Some(match pull {
"pullup" => Pull::Up,
"pulldown" => Pull::Down,
_ => Err(gpio::GpioError::ConfigurationError(format!(
"unknown pull setting '{pull}'"
)))?,
})
} else {
None
};
if config_iter.next().is_some() {
return Err(gpio::GpioError::ConfigurationError(String::from(
"too many fields for input pin",
)));
}
self.new_input_pin(pin_number, pull).map(Pin::Input)
}
Some("output") => {
let init_level = if let Some(init_level) = config_iter.next() {
Some(match init_level {
"low" => gpio::LogicLevel::Low,
"high" => gpio::LogicLevel::High,
_ => Err(gpio::GpioError::ConfigurationError(format!(
"unknown initial level '{init_level}'"
)))?,
})
} else {
None
};
if config_iter.next().is_some() {
return Err(gpio::GpioError::ConfigurationError(String::from(
"too many fields for output pin",
)));
}
self.new_output_pin(pin_number, init_level).map(Pin::Output)
}
Some("pwm_output") => {
let period_microseconds = if let Some(period_microseconds) = config_iter.next() {
u32::from_str(period_microseconds).map(Some).map_err(|e| {
gpio::GpioError::ConfigurationError(format!(
"invalid period length '{period_microseconds}': {e}"
))
})?
} else {
None
};
if config_iter.next().is_some() {
return Err(gpio::GpioError::ConfigurationError(String::from(
"too many fields for PWM output pin",
)));
}
self.new_pwm_output_pin(pin_number, period_microseconds)
.map(Pin::PwmOutput)
}
Some(unknown_type) => Err(gpio::GpioError::ConfigurationError(format!(
"unknown pin type '{unknown_type}'"
))),
None => Err(gpio::GpioError::ConfigurationError(String::from(
"no pin type",
))),
}
}
}

/// An implementation of an input pin resource.
pub trait InputPinImplementor: Send + Sync {
/// Read the current logic level to the pin.
fn read(&self) -> gpio::LogicLevel;
}

impl Debug for dyn InputPinImplementor {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("InputPinImplementor")
.finish_non_exhaustive()
}
}

/// An implementation of an output pin resource.
pub trait OutputPinImplementor: Send + Sync {
/// Write the given logic level to the pin.
fn write(&self, level: gpio::LogicLevel);
}

impl Debug for dyn OutputPinImplementor {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("OutputPinImplementor")
.finish_non_exhaustive()
}
}

/// An implementation of a PWM output pin resource.
pub trait PwmOutputPinImplementor: Send + Sync {
/// Configure the pin with the given parameters. This does not enable it if it is disabled.
fn set_duty_cycle(&self, duty_cycle: f32);
/// Enable the pin's output.
fn enable(&self);
/// Disable the pin's output.
fn disable(&self);
}

impl Debug for dyn PwmOutputPinImplementor {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("PwmOutputPinImplementor")
.finish_non_exhaustive()
}
}
Loading