-
Notifications
You must be signed in to change notification settings - Fork 30
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
base: main
Are you sure you want to change the base?
Add GPIO capability #406
Changes from all commits
a540a1d
a54cc53
bc18e21
f4ef59b
f64c422
5f424d4
ab486e3
f5b0f31
659a9e5
87c201d
4445bf4
33adb2e
ddd56a7
400e0c5
f328c0e
b77b6e7
5067c6c
424a3fc
f0f022e
031a844
3ad4258
76fbde2
1935c41
4f786fe
cd18c2b
d7287c6
c588203
cccea05
e53a187
210aa31
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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 } | ||
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"] |
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/). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 (: |
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() | ||
} | ||
} |
There was a problem hiding this comment.
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