diff --git a/Cargo.toml b/Cargo.toml index ced06bc..0fa3eb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,4 @@ edition = "2018" [dependencies] rppal = "0.12.0" - +palette = "0.7.3" diff --git a/examples/blink_rgb.rs b/examples/blink_rgb.rs new file mode 100644 index 0000000..b1527de --- /dev/null +++ b/examples/blink_rgb.rs @@ -0,0 +1,38 @@ +//! Blinks an LED : on_time: 2 seconds and off_time: 3 seconds + +use palette::rgb::Rgb; +use rust_gpiozero::*; +use std::{thread, time}; + +fn main() { + // Create a new LED attached to Pin 17 + let mut led = RGBLED::new(12, 19, 13, true); + + // Display some colours for half a second each + led.set_color(Rgb::new(1.0, 1.0, 0.0)); + thread::sleep(time::Duration::from_millis(1500)); + led.set_color(Rgb::new(0.0, 1.0, 0.0)); + thread::sleep(time::Duration::from_millis(1500)); + led.set_color(Rgb::new(0.0, 1.0, 1.0)); + thread::sleep(time::Duration::from_millis(1500)); + + // Blink five times + led.set_blink_count(50); + led.blink(2.5, 1.2, Rgb::new(1.0, 1.0, 0.0), Rgb::new(0.0, 0.0, 1.0)); + led.wait(); + led.off(); + + // Stay off for a second + thread::sleep(time::Duration::from_millis(1000)); + + // Blink ten times + led.set_blink_count(10); + led.blink(0.2, 0.5, Rgb::new(1.0, 1.0, 1.0), Rgb::new(1.0, 0.0, 0.0)); + led.wait(); + + // Show white for two seconds + led.on(); + led.set_color(Rgb::new(1.0, 1.0, 1.0)); + thread::sleep(time::Duration::from_millis(2000)); + led.off(); +} diff --git a/src/devices.rs b/src/devices.rs index a368d50..bcd0f3b 100644 --- a/src/devices.rs +++ b/src/devices.rs @@ -31,6 +31,7 @@ macro_rules! impl_device { pub struct GpioDevice { pin: Pin, active_state: bool, + #[allow(dead_code)] inactive_state: bool, } diff --git a/src/output_devices.rs b/src/output_devices.rs index d3d1270..9ac7fba 100644 --- a/src/output_devices.rs +++ b/src/output_devices.rs @@ -1,4 +1,5 @@ //! Output device component interfaces for devices such as `LED`, `PWMLED`, etc +use palette::rgb::Rgb; use rppal::gpio::{Gpio, IoPin, Level, Mode}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -256,6 +257,225 @@ impl DigitalOutputDevice { } } +pub struct RGBLED { + red: Arc>, + green: Arc>, + blue: Arc>, + blinking: Arc, + handle: Option>, + blink_count: Option, +} + +impl RGBLED { + pub fn new(pin_red: u8, pin_green: u8, pin_blue: u8, active_high: bool) -> RGBLED { + let red = Arc::new(Mutex::new(OutputDevice::new(pin_red))); + let green = Arc::new(Mutex::new(OutputDevice::new(pin_green))); + let blue = Arc::new(Mutex::new(OutputDevice::new(pin_blue))); + red.lock().unwrap().set_active_high(active_high); + green.lock().unwrap().set_active_high(active_high); + blue.lock().unwrap().set_active_high(active_high); + Self { + red, + green, + blue, + blinking: Arc::new(AtomicBool::new(false)), + handle: None, + blink_count: None, + } + } + + pub fn set_color(&mut self, color: Rgb) { + Self::write_color(&self.red, &self.green, &self.blue, color); + } + + fn write_color( + red: &Arc>, + green: &Arc>, + blue: &Arc>, + color: Rgb, + ) { + Self::write_state(&red, color.red > 0.5); + Self::write_state(&green, color.green > 0.5); + Self::write_state(&blue, color.blue > 0.5); + } + + fn write_state(device: &Arc>, value: bool) { + if device.lock().unwrap().value_to_state(value) { + device.lock().unwrap().pin.set_high() + } else { + device.lock().unwrap().pin.set_low() + } + } + + fn blinker( + &mut self, + on_time: f32, + off_time: f32, + on_color: Rgb, + off_color: Rgb, + n: Option, + ) { + self.stop(); + + let red = Arc::clone(&self.red); + let green = Arc::clone(&self.green); + let blue = Arc::clone(&self.blue); + + let blinking = Arc::clone(&self.blinking); + + self.handle = Some(thread::spawn(move || { + blinking.store(true, Ordering::SeqCst); + match n { + Some(end) => { + for _ in 0..end { + if !blinking.load(Ordering::SeqCst) { + red.lock().unwrap().off(); + green.lock().unwrap().off(); + blue.lock().unwrap().off(); + break; + } + Self::write_color(&red, &green, &blue, on_color); + thread::sleep(Duration::from_millis((on_time * 1000.0) as u64)); + Self::write_color(&red, &green, &blue, off_color); + thread::sleep(Duration::from_millis((off_time * 1000.0) as u64)); + } + } + None => loop { + if !blinking.load(Ordering::SeqCst) { + red.lock().unwrap().off(); + green.lock().unwrap().off(); + blue.lock().unwrap().off(); + break; + } + Self::write_color(&red, &green, &blue, on_color); + thread::sleep(Duration::from_millis((on_time * 1000.0) as u64)); + Self::write_color(&red, &green, &blue, off_color); + thread::sleep(Duration::from_millis((off_time * 1000.0) as u64)); + }, + } + })); + } + /// Returns ``True`` if the device is currently active and ``False`` otherwise. + pub fn is_active(&self) -> bool { + self.red.lock().unwrap().is_active() + || self.green.lock().unwrap().is_active() + || self.blue.lock().unwrap().is_active() + } + /// Turns the device on. + pub fn on(&self) { + self.stop(); + self.red.lock().unwrap().on(); + self.green.lock().unwrap().on(); + self.blue.lock().unwrap().on(); + } + /// Turns the device off. + pub fn off(&self) { + self.stop(); + self.red.lock().unwrap().off(); + self.green.lock().unwrap().off(); + self.blue.lock().unwrap().off(); + } + /// Reverse the state of the device. If it's on, turn it off; if it's off, turn it on. + pub fn toggle(&mut self) { + if self.is_active() { + self.on() + } else { + self.off() + } + } + + /// Returns ``True`` if the device is currently active and ``False`` otherwise. + pub fn value_red(&self) -> bool { + self.red.lock().unwrap().value() + } + + /// Returns ``True`` if the device is currently active and ``False`` otherwise. + pub fn value_green(&self) -> bool { + self.green.lock().unwrap().value() + } + + /// Returns ``True`` if the device is currently active and ``False`` otherwise. + pub fn value_blue(&self) -> bool { + self.blue.lock().unwrap().value() + } + + fn stop(&self) { + self.blinking.clone().store(false, Ordering::SeqCst); + self.red.lock().unwrap().pin.set_low(); + self.green.lock().unwrap().pin.set_low(); + self.blue.lock().unwrap().pin.set_low(); + } + + /// When ``True``, the `value` property is ``True`` when the device's + /// `pin` is high. When ``False`` the `value` property is + /// ``True`` when the device's pin is low (i.e. the value is inverted). + /// Be warned that changing it will invert `value` (i.e. changing this property doesn't change + /// the device's pin state - it just changes how that state is interpreted). + pub fn active_high(&self) -> bool { + self.red.lock().unwrap().active_high() + || self.green.lock().unwrap().active_high() + || self.blue.lock().unwrap().active_high() + } + + /// Set the state for active_high + pub fn set_active_high(&mut self, value: bool) { + self.red.lock().unwrap().set_active_high(value); + self.green.lock().unwrap().set_active_high(value); + self.blue.lock().unwrap().set_active_high(value); + } + + /// The `Pin` that the device is connected to. + pub fn pin_red(&self) -> u8 { + self.red.lock().unwrap().pin.pin() + } + + /// The `Pin` that the device is connected to. + pub fn pin_green(&self) -> u8 { + self.green.lock().unwrap().pin.pin() + } + + /// The `Pin` that the device is connected to. + pub fn pin_blue(&self) -> u8 { + self.blue.lock().unwrap().pin.pin() + } + + /// Shut down the device and release all associated resources. + pub fn close(self) { + drop(self) + } + + /// Block until background process is done + pub fn wait(&mut self) { + self.handle + .take() + .expect("Called stop on non-running thread") + .join() + .expect("Could not join spawned thread"); + } + + /// Make the device turn on and off repeatedly in the background. + /// Use `set_blink_count` to set the number of times to blink the device + /// * `on_time` - Number of seconds on + /// * `off_time` - Number of seconds off + /// + pub fn blink(&mut self, on_time: f32, off_time: f32, on_color: Rgb, off_color: Rgb) { + match self.blink_count { + None => self.blinker(on_time, off_time, on_color, off_color, None), + Some(n) => self.blinker(on_time, off_time, on_color, off_color, Some(n)), + } + } + + /// Set the number of times to blink the device + /// * `n` - Number of times to blink + pub fn set_blink_count(&mut self, n: i32) { + self.blink_count = Some(n) + } + + pub fn is_lit(&self) -> bool { + self.is_active() + } +} + /// Represents a light emitting diode (LED) /// /// # Example @@ -740,7 +960,8 @@ impl Servo { if value >= -1.0 && value <= 1.0 { // Map value form [-1, 1] to [min_pulse_width, max_pulse_width] linearly let range: f64 = (self.max_pulse_width - self.min_pulse_width) as f64; - let pulse_width: u64 = self.min_pulse_width + (((value + 1.0)/2.0) * range).round() as u64; + let pulse_width: u64 = + self.min_pulse_width + (((value + 1.0) / 2.0) * range).round() as u64; if self .pin .set_pwm( @@ -751,12 +972,11 @@ impl Servo { { println!("Failed to set servo to a new position"); } - } - else { + } else { println!("set_position value must be between -1 and 1"); } } - + /// Set the servo's minimum pulse width pub fn set_min_pulse_width(&mut self, value: u64) { if value >= self.max_pulse_width { @@ -796,12 +1016,8 @@ impl Servo { } pub fn detach(&mut self) { - if self - .pin - .clear_pwm() - .is_err() - { + if self.pin.clear_pwm().is_err() { println!("Failed to detach servo") } - } + } }