automation_hat/lights.rs
1//! LED control functionality for the Automation HAT.
2//!
3//! This module provides the `LED` struct, which represents a single LED on the Automation HAT.
4//! Each LED has a brightness level that can be controlled from 0.0 to 1.0.
5
6use linux_embedded_hal::I2cdev;
7use sn3218_hal::SN3218;
8use std::collections::HashMap;
9use std::sync::{Arc, Mutex, OnceLock};
10
11// Shared global state to track LED brightness values across the system
12static LED_STATE: OnceLock<Mutex<HashMap<u8, u8>>> = OnceLock::new();
13
14/// Represents a single LED on the Automation HAT.
15///
16/// The `LED` struct provides control over a single LED, allowing it to be turned on/off
17/// or set to a specific brightness level. LEDs are controlled through the SN3218 LED driver
18/// chip which supports 18 channels with 255 brightness levels each.
19pub struct LED {
20 /// Reference to the SN3218 LED driver
21 driver: Arc<Mutex<SN3218<I2cdev>>>,
22 /// Channel number on the SN3218 (0-17)
23 channel: u8,
24 /// Current brightness value (0.0-1.0)
25 pub brightness: f64,
26 /// Maximum hardware brightness value (typically 255)
27 max_brightness: u8,
28}
29
30impl LED {
31 /// Creates a new LED instance for the specified channel.
32 ///
33 /// # Arguments
34 ///
35 /// * `driver` - Shared reference to the SN3218 LED driver
36 /// * `channel` - The channel number (0-17) on the SN3218 chip
37 ///
38 /// # Returns
39 ///
40 /// A new `LED` instance initialized to off (brightness 0.0)
41 pub fn new(driver: Arc<Mutex<SN3218<I2cdev>>>, channel: u8) -> Self {
42 // Initialize global LED state if not already done
43 LED_STATE.get_or_init(|| Mutex::new(HashMap::new()));
44
45 LED {
46 driver,
47 channel,
48 brightness: 0.0,
49 max_brightness: 255,
50 }
51 }
52
53 /// Turns the LED on at full brightness.
54 ///
55 /// # Returns
56 ///
57 /// A `Result` indicating success or containing an error
58 pub fn on(&mut self) -> Result<(), Box<dyn std::error::Error>> {
59 self.set_brightness(1.0)
60 }
61
62 /// Turns the LED off (brightness 0.0).
63 ///
64 /// # Returns
65 ///
66 /// A `Result` indicating success or containing an error
67 pub fn off(&mut self) -> Result<(), Box<dyn std::error::Error>> {
68 self.set_brightness(0.0)
69 }
70
71 /// Toggles the LED between on and off states.
72 ///
73 /// If the LED is currently off (brightness 0.0), it will be turned on.
74 /// Otherwise, it will be turned off.
75 ///
76 /// # Returns
77 ///
78 /// A `Result` indicating success or containing an error
79 pub fn toggle(&mut self) -> Result<(), Box<dyn std::error::Error>> {
80 if self.brightness == 0.0 {
81 self.on()
82 } else {
83 self.off()
84 }
85 }
86
87 /// Sets the LED brightness to a specific value.
88 ///
89 /// # Arguments
90 ///
91 /// * `brightness` - A value between 0.0 (off) and 1.0 (full brightness)
92 ///
93 /// # Returns
94 ///
95 /// A `Result` indicating success or containing an error
96 ///
97 /// # Errors
98 ///
99 /// Returns an error if the brightness value is outside the valid range of 0.0 to 1.0,
100 /// or if communication with the LED driver fails.
101 pub fn set_brightness(&mut self, brightness: f64) -> Result<(), Box<dyn std::error::Error>> {
102 if brightness < 0.0 || brightness > 1.0 {
103 return Err("Brightness must be between 0.0 and 1.0".into());
104 }
105
106 self.brightness = brightness;
107 let value = (brightness * self.max_brightness as f64) as u8;
108
109 let led_state_mutex = LED_STATE.get_or_init(|| Mutex::new(HashMap::new()));
110 let mut led_state = led_state_mutex.lock().unwrap();
111
112 // Update the state for this channel
113 led_state.insert(self.channel, value);
114
115 // Prepare values array with current state of all channels
116 let mut values = [0u8; 18];
117 let mut led_mask = 0u32;
118
119 for (channel, brightness) in led_state.iter() {
120 if *channel < 18 {
121 values[*channel as usize] = *brightness;
122 if *brightness > 0 {
123 led_mask |= 1u32 << channel;
124 }
125 }
126 }
127
128 let mut driver = self.driver.lock().unwrap();
129 driver.enable_leds(led_mask).unwrap();
130 driver.output(&values).unwrap();
131
132 Ok(())
133 }
134
135 /// Alias for `set_brightness` - sets the LED to a specific brightness.
136 ///
137 /// # Arguments
138 ///
139 /// * `brightness` - A value between 0.0 (off) and 1.0 (full brightness)
140 ///
141 /// # Returns
142 ///
143 /// A `Result` indicating success or containing an error
144 pub fn set(&mut self, brightness: f64) -> Result<(), Box<dyn std::error::Error>> {
145 self.set_brightness(brightness)
146 }
147}
148
149/// Implement Clone for LED to allow LED objects to be duplicated.
150/// This is useful when the same LED needs to be shared between multiple components.
151impl Clone for LED {
152 fn clone(&self) -> Self {
153 LED {
154 driver: Arc::clone(&self.driver),
155 channel: self.channel,
156 brightness: self.brightness,
157 max_brightness: self.max_brightness,
158 }
159 }
160}
161
162/// Implement Debug for LED to allow for easier debugging and printing of LED objects.
163impl std::fmt::Debug for LED {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 f.debug_struct("LED")
166 .field("channel", &self.channel)
167 .field("brightness", &self.brightness)
168 .field("max_brightness", &self.max_brightness)
169 .finish()
170 }
171}