diff --git a/packages/pros/Cargo.toml b/packages/pros/Cargo.toml index 702142b2..63fcd1fa 100644 --- a/packages/pros/Cargo.toml +++ b/packages/pros/Cargo.toml @@ -29,6 +29,7 @@ no_std_io = { version = "0.6.0", features = ["alloc"] } futures = { version = "0.3.28", default-features = false, features = ["alloc"] } async-task = { version = "4.5.0", default-features = false } waker-fn = "1.1.1" +smart-leds-trait = { version = "0.3.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] dlmalloc = { version = "0.2.4", features = ["global"] } diff --git a/packages/pros/src/devices/adi/addrled.rs b/packages/pros/src/devices/adi/addrled.rs new file mode 100644 index 00000000..b4b27d09 --- /dev/null +++ b/packages/pros/src/devices/adi/addrled.rs @@ -0,0 +1,189 @@ +//! ADI Addressable LEDs +//! +//! This module contains abstractions for interacting with WS2812B addressable smart LED +//! strips over ADI ports. + +use alloc::vec::Vec; + +use pros_sys::{ext_adi_led_t, PROS_ERR}; +use snafu::Snafu; + +use super::{AdiDevice, AdiDeviceType, AdiError, AdiPort}; +use crate::{ + color::IntoRgb, + error::{bail_on, map_errno}, +}; + +/// WS2812B Addressable LED Strip +#[derive(Debug, Eq, PartialEq)] +pub struct AdiAddrLed { + raw: ext_adi_led_t, + buffer: Vec, + port: AdiPort, +} + +/// The max number of LED diodes on one strip that a single ADI port can control. +pub const MAX_LED_LENGTH: usize = 64; + +impl AdiAddrLed { + /// Initialize an LED strip on an ADI port from a buffer of light colors. + pub fn new(port: AdiPort, buf: T) -> Result + where + T: IntoIterator, + I: Into, + { + let raw = bail_on!(PROS_ERR, unsafe { + pros_sys::ext_adi_led_init(port.internal_expander_index(), port.index()) + }); + + let mut device = Self { + port, + raw, + buffer: buf.into_iter().map(|i| i.into()).collect::>(), + }; + + bail_on!(PROS_ERR, unsafe { + pros_sys::ext_adi_led_set( + device.raw, + device.buffer.as_mut_ptr(), + device.buffer.len() as u32, + ) + }); + + Ok(device) + } + + /// Clear the entire led strip of color. + pub fn clear_all(&mut self) -> Result<(), AddrLedError> { + bail_on!(PROS_ERR, unsafe { + pros_sys::ext_adi_led_clear_all( + self.raw, + self.buffer.as_mut_ptr(), + self.buffer.len() as u32, + ) + }); + + Ok(()) + } + + /// Set the entire led strip to one color + pub fn set_all(&mut self, color: impl IntoRgb) -> Result<(), AddrLedError> { + bail_on!(PROS_ERR, unsafe { + pros_sys::ext_adi_led_set_all( + self.raw, + self.buffer.as_mut_ptr(), + self.buffer.len() as u32, + color.into_rgb().into(), + ) + }); + + Ok(()) + } + + /// Set the entire led strip using the colors contained in a new buffer. + pub fn set_buffer(&mut self, buf: T) -> Result<(), AddrLedError> + where + T: IntoIterator, + I: IntoRgb, + { + self.buffer = buf + .into_iter() + .map(|i| i.into_rgb().into()) + .collect::>(); + + bail_on!(PROS_ERR, unsafe { + pros_sys::ext_adi_led_set(self.raw, self.buffer.as_mut_ptr(), self.buffer.len() as u32) + }); + + Ok(()) + } + + /// Set the color of a single LED on the strip. + pub fn set_pixel(&mut self, index: usize, color: impl IntoRgb) -> Result<(), AddrLedError> { + if self.buffer.get(index).is_some() { + self.buffer[index] = color.into_rgb().into(); + + bail_on!(PROS_ERR, unsafe { + pros_sys::ext_adi_led_set( + self.raw, + self.buffer.as_mut_ptr(), + self.buffer.len() as u32, + ) + }); + + Ok(()) + } else { + Err(AddrLedError::InvalidBufferAccess) + } + } + + /// Clear one LED on the strip. + pub fn clear_pixel(&mut self, index: usize) -> Result<(), AddrLedError> { + self.set_pixel(index, 0u32)?; + + Ok(()) + } +} + +impl AdiDevice for AdiAddrLed { + type PortIndexOutput = u8; + + fn port_index(&self) -> Self::PortIndexOutput { + self.port.index() + } + + fn expander_port_index(&self) -> Option { + self.port.expander_index() + } + + fn device_type(&self) -> AdiDeviceType { + AdiDeviceType::DigitalOut + } +} + +#[cfg(feature = "smart-leds-trait")] +impl smart_leds_trait::SmartLedsWrite for AdiAddrLed { + type Error = AddrLedError; + type Color = Rgb; + + fn write(&mut self, iterator: T) -> Result<(), Self::Error> + where + T: IntoIterator, + I: Into, + { + self.buffer = iterator + .into_iter() + .map(|i| i.into().into()) + .collect::>(); + + bail_on!(PROS_ERR, unsafe { + pros_sys::ext_adi_led_set(self.raw, self.buffer.as_mut_ptr(), self.buffer.len() as u32) + }); + + Ok(()) + } +} + +/// Errors that can occur when interacting with an Addrled strip. +#[derive(Debug, Snafu)] +pub enum AddrLedError { + /// Failed to access LED buffer. A given value is not correct, or the buffer is null. + #[snafu(display( + "Failed to access LED buffer. A given value is not correct, or the buffer is null." + ))] + InvalidBufferAccess, + + #[snafu(display("{source}"), context(false))] + /// Generic ADI related error. + Adi { + /// The source of the error + source: AdiError, + }, +} + +map_errno! { + AddrLedError { + EINVAL => Self::InvalidBufferAccess, + } + inherit AdiError; +} diff --git a/packages/pros/src/devices/adi/mod.rs b/packages/pros/src/devices/adi/mod.rs index 5c845b1f..46c12136 100644 --- a/packages/pros/src/devices/adi/mod.rs +++ b/packages/pros/src/devices/adi/mod.rs @@ -9,6 +9,7 @@ use crate::error::{bail_on, map_errno, PortError}; pub mod analog; pub mod digital; +pub mod addrled; pub mod encoder; pub mod gyro; pub mod linetracker; @@ -18,6 +19,7 @@ pub mod solenoid; pub mod switch; pub mod ultrasonic; +pub use addrled::AdiAddrLed; pub use analog::AdiAnalogIn; pub use digital::{AdiDigitalIn, AdiDigitalOut}; pub use encoder::AdiEncoder; diff --git a/packages/pros/src/lib.rs b/packages/pros/src/lib.rs index 6b294123..81032f20 100644 --- a/packages/pros/src/lib.rs +++ b/packages/pros/src/lib.rs @@ -377,6 +377,7 @@ pub mod prelude { color::Rgb, devices::{ adi::{ + addrled::AdiAddrLed, analog::{AdiAnalogIn, AdiAnalogOut}, digital::{AdiDigitalIn, AdiDigitalOut}, encoder::AdiEncoder,