|
1 | 1 | //! Output device component interfaces for devices such as `LED`, `PWMLED`, etc
|
| 2 | +use palette::rgb::Rgb; |
2 | 3 | use rppal::gpio::{Gpio, IoPin, Level, Mode};
|
3 | 4 | use std::sync::atomic::{AtomicBool, Ordering};
|
4 | 5 | use std::sync::Arc;
|
@@ -257,19 +258,212 @@ impl DigitalOutputDevice {
|
257 | 258 | }
|
258 | 259 |
|
259 | 260 | pub struct RGBLED {
|
260 |
| - pub red: LED, |
261 |
| - pub green: LED, |
262 |
| - pub blue: LED, |
| 261 | + devices: [Arc<Mutex<OutputDevice>>; 3], |
| 262 | + blinking: Arc<AtomicBool>, |
| 263 | + handle: Option<JoinHandle<()>>, |
| 264 | + blink_count: Option<i32>, |
263 | 265 | }
|
264 | 266 |
|
265 | 267 | impl RGBLED {
|
266 | 268 | pub fn new(pin_red: u8, pin_green: u8, pin_blue: u8) -> RGBLED {
|
267 | 269 | RGBLED {
|
268 |
| - red: LED::new(pin_red), |
269 |
| - green: LED::new(pin_green), |
270 |
| - blue: LED::new(pin_blue), |
| 270 | + devices: [ |
| 271 | + Arc::new(Mutex::new(OutputDevice::new(pin_red))), |
| 272 | + Arc::new(Mutex::new(OutputDevice::new(pin_green))), |
| 273 | + Arc::new(Mutex::new(OutputDevice::new(pin_blue))), |
| 274 | + ], |
| 275 | + blinking: Arc::new(AtomicBool::new(false)), |
| 276 | + handle: None, |
| 277 | + blink_count: None, |
| 278 | + } |
| 279 | + } |
| 280 | + |
| 281 | + pub fn set_color(&mut self, color: Rgb) { |
| 282 | + Self::write_color(&self.devices, color); |
| 283 | + } |
| 284 | + |
| 285 | + fn write_color(devices: &[Arc<Mutex<OutputDevice>>; 3], color: Rgb) { |
| 286 | + Self::write_state(&devices[0], color.red > 0.5); |
| 287 | + Self::write_state(&devices[1], color.green > 0.5); |
| 288 | + Self::write_state(&devices[2], color.blue > 0.5); |
| 289 | + } |
| 290 | + |
| 291 | + fn write_state(device: &Arc<Mutex<OutputDevice>>, value: bool) { |
| 292 | + if device.lock().unwrap().value_to_state(value) { |
| 293 | + device.lock().unwrap().pin.set_high() |
| 294 | + } else { |
| 295 | + device.lock().unwrap().pin.set_low() |
| 296 | + } |
| 297 | + } |
| 298 | + |
| 299 | + fn blinker( |
| 300 | + &mut self, |
| 301 | + on_time: f32, |
| 302 | + off_time: f32, |
| 303 | + on_color: Rgb, |
| 304 | + off_color: Rgb, |
| 305 | + n: Option<i32>, |
| 306 | + ) { |
| 307 | + self.stop(); |
| 308 | + |
| 309 | + let devices = [ |
| 310 | + Arc::clone(&self.devices[0]), |
| 311 | + Arc::clone(&self.devices[1]), |
| 312 | + Arc::clone(&self.devices[2]), |
| 313 | + ]; |
| 314 | + let blinking = Arc::clone(&self.blinking); |
| 315 | + |
| 316 | + self.handle = Some(thread::spawn(move || { |
| 317 | + blinking.store(true, Ordering::SeqCst); |
| 318 | + match n { |
| 319 | + Some(end) => { |
| 320 | + for _ in 0..end { |
| 321 | + if !blinking.load(Ordering::SeqCst) { |
| 322 | + devices[0].lock().unwrap().off(); |
| 323 | + devices[1].lock().unwrap().off(); |
| 324 | + devices[2].lock().unwrap().off(); |
| 325 | + break; |
| 326 | + } |
| 327 | + Self::write_color(&devices, on_color); |
| 328 | + thread::sleep(Duration::from_millis((on_time * 1000.0) as u64)); |
| 329 | + Self::write_color(&devices, off_color); |
| 330 | + thread::sleep(Duration::from_millis((off_time * 1000.0) as u64)); |
| 331 | + } |
| 332 | + } |
| 333 | + None => loop { |
| 334 | + if !blinking.load(Ordering::SeqCst) { |
| 335 | + devices[0].lock().unwrap().off(); |
| 336 | + devices[1].lock().unwrap().off(); |
| 337 | + devices[2].lock().unwrap().off(); |
| 338 | + break; |
| 339 | + } |
| 340 | + Self::write_color(&devices, on_color); |
| 341 | + thread::sleep(Duration::from_millis((on_time * 1000.0) as u64)); |
| 342 | + Self::write_color(&devices, off_color); |
| 343 | + thread::sleep(Duration::from_millis((off_time * 1000.0) as u64)); |
| 344 | + }, |
| 345 | + } |
| 346 | + })); |
| 347 | + } |
| 348 | + /// Returns ``True`` if the device is currently active and ``False`` otherwise. |
| 349 | + pub fn is_active(&self) -> bool { |
| 350 | + self.devices[0].lock().unwrap().is_active() |
| 351 | + || self.devices[1].lock().unwrap().is_active() |
| 352 | + || self.devices[2].lock().unwrap().is_active() |
| 353 | + } |
| 354 | + /// Turns the device on. |
| 355 | + pub fn on(&self) { |
| 356 | + self.stop(); |
| 357 | + self.devices[0].lock().unwrap().on(); |
| 358 | + self.devices[1].lock().unwrap().on(); |
| 359 | + self.devices[2].lock().unwrap().on(); |
| 360 | + } |
| 361 | + /// Turns the device off. |
| 362 | + pub fn off(&self) { |
| 363 | + self.stop(); |
| 364 | + self.devices[0].lock().unwrap().off(); |
| 365 | + self.devices[1].lock().unwrap().off(); |
| 366 | + self.devices[2].lock().unwrap().off(); |
| 367 | + } |
| 368 | + /// Reverse the state of the device. If it's on, turn it off; if it's off, turn it on. |
| 369 | + pub fn toggle(&mut self) { |
| 370 | + if self.is_active() { |
| 371 | + self.on() |
| 372 | + } else { |
| 373 | + self.off() |
271 | 374 | }
|
272 | 375 | }
|
| 376 | + |
| 377 | + /// Returns ``True`` if the device is currently active and ``False`` otherwise. |
| 378 | + pub fn value_red(&self) -> bool { |
| 379 | + self.devices[0].lock().unwrap().value() |
| 380 | + } |
| 381 | + |
| 382 | + /// Returns ``True`` if the device is currently active and ``False`` otherwise. |
| 383 | + pub fn value_green(&self) -> bool { |
| 384 | + self.devices[1].lock().unwrap().value() |
| 385 | + } |
| 386 | + |
| 387 | + /// Returns ``True`` if the device is currently active and ``False`` otherwise. |
| 388 | + pub fn value_blue(&self) -> bool { |
| 389 | + self.devices[2].lock().unwrap().value() |
| 390 | + } |
| 391 | + |
| 392 | + fn stop(&self) { |
| 393 | + self.blinking.clone().store(false, Ordering::SeqCst); |
| 394 | + self.devices[0].lock().unwrap().pin.set_low(); |
| 395 | + self.devices[1].lock().unwrap().pin.set_low(); |
| 396 | + self.devices[2].lock().unwrap().pin.set_low(); |
| 397 | + } |
| 398 | + |
| 399 | + /// When ``True``, the `value` property is ``True`` when the device's |
| 400 | + /// `pin` is high. When ``False`` the `value` property is |
| 401 | + /// ``True`` when the device's pin is low (i.e. the value is inverted). |
| 402 | + /// Be warned that changing it will invert `value` (i.e. changing this property doesn't change |
| 403 | + /// the device's pin state - it just changes how that state is interpreted). |
| 404 | + pub fn active_high(&self) -> bool { |
| 405 | + self.devices[0].lock().unwrap().active_high() |
| 406 | + || self.devices[1].lock().unwrap().active_high() |
| 407 | + || self.devices[2].lock().unwrap().active_high() |
| 408 | + } |
| 409 | + |
| 410 | + /// Set the state for active_high |
| 411 | + pub fn set_active_high(&mut self, value: bool) { |
| 412 | + self.devices[0].lock().unwrap().set_active_high(value); |
| 413 | + self.devices[1].lock().unwrap().set_active_high(value); |
| 414 | + self.devices[2].lock().unwrap().set_active_high(value); |
| 415 | + } |
| 416 | + |
| 417 | + /// The `Pin` that the device is connected to. |
| 418 | + pub fn pin_red(&self) -> u8 { |
| 419 | + self.devices[0].lock().unwrap().pin.pin() |
| 420 | + } |
| 421 | + |
| 422 | + /// The `Pin` that the device is connected to. |
| 423 | + pub fn pin_green(&self) -> u8 { |
| 424 | + self.devices[1].lock().unwrap().pin.pin() |
| 425 | + } |
| 426 | + |
| 427 | + /// The `Pin` that the device is connected to. |
| 428 | + pub fn pin_blue(&self) -> u8 { |
| 429 | + self.devices[2].lock().unwrap().pin.pin() |
| 430 | + } |
| 431 | + |
| 432 | + /// Shut down the device and release all associated resources. |
| 433 | + pub fn close(self) { |
| 434 | + drop(self) |
| 435 | + } |
| 436 | + |
| 437 | + /// Block until background process is done |
| 438 | + pub fn wait(&mut self) { |
| 439 | + self.handle |
| 440 | + .take() |
| 441 | + .expect("Called stop on non-running thread") |
| 442 | + .join() |
| 443 | + .expect("Could not join spawned thread"); |
| 444 | + } |
| 445 | + |
| 446 | + /// Make the device turn on and off repeatedly in the background. |
| 447 | + /// Use `set_blink_count` to set the number of times to blink the device |
| 448 | + /// * `on_time` - Number of seconds on |
| 449 | + /// * `off_time` - Number of seconds off |
| 450 | + /// |
| 451 | + pub fn blink(&mut self, on_time: f32, off_time: f32, on_color: Rgb, off_color: Rgb) { |
| 452 | + match self.blink_count { |
| 453 | + None => self.blinker(on_time, off_time, on_color, off_color, None), |
| 454 | + Some(n) => self.blinker(on_time, off_time, on_color, off_color, Some(n)), |
| 455 | + } |
| 456 | + } |
| 457 | + |
| 458 | + /// Set the number of times to blink the device |
| 459 | + /// * `n` - Number of times to blink |
| 460 | + pub fn set_blink_count(&mut self, n: i32) { |
| 461 | + self.blink_count = Some(n) |
| 462 | + } |
| 463 | + |
| 464 | + pub fn is_lit(&self) -> bool { |
| 465 | + self.is_active() |
| 466 | + } |
273 | 467 | }
|
274 | 468 |
|
275 | 469 | /// Represents a light emitting diode (LED)
|
|
0 commit comments