1use crate::device::PoKeysDevice;
4use crate::error::{PoKeysError, Result};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct MatrixLed {
10 pub display_enabled: u8,
11 pub rows: u8,
12 pub columns: u8,
13 pub refresh_flag: u8,
14 pub data: [u8; 8],
15}
16
17impl MatrixLed {
18 pub fn new() -> Self {
19 Self {
20 display_enabled: 0,
21 rows: 0,
22 columns: 0,
23 refresh_flag: 0,
24 data: [0; 8],
25 }
26 }
27
28 pub fn is_enabled(&self) -> bool {
29 self.display_enabled != 0
30 }
31
32 pub fn set_led(&mut self, row: usize, col: usize, state: bool) -> Result<()> {
33 if row >= self.rows as usize || col >= 8 {
34 return Err(PoKeysError::Parameter("Invalid LED position".to_string()));
35 }
36
37 if state {
38 self.data[row] |= 1 << col;
39 } else {
40 self.data[row] &= !(1 << col);
41 }
42
43 self.refresh_flag = 1;
44 Ok(())
45 }
46
47 pub fn get_led(&self, row: usize, col: usize) -> bool {
48 if row >= self.rows as usize || col >= 8 {
49 return false;
50 }
51 (self.data[row] & (1 << col)) != 0
52 }
53
54 pub fn clear_all(&mut self) {
55 self.data.fill(0);
56 self.refresh_flag = 1;
57 }
58
59 pub fn set_all(&mut self) {
60 self.data.fill(0xFF);
61 self.refresh_flag = 1;
62 }
63}
64
65impl Default for MatrixLed {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct LedMatrixConfig {
74 pub name: String,
75 pub description: Option<String>,
76 pub matrix_id: u8, pub enabled: bool,
78 pub characters: u8, }
80
81pub struct SevenSegmentDisplay {
83 pub matrix_id: u8,
84 pub character_count: u8, pub decimal_points: Vec<bool>,
86}
87
88pub struct MatrixLedProtocolConfig {
90 pub display1_enabled: bool,
91 pub display2_enabled: bool,
92 pub display1_characters: u8, pub display2_characters: u8, }
95
96pub const SEVEN_SEGMENT_COLUMNS: u8 = 8; pub const LED_MATRIX_1_PINS: [u8; 3] = [9, 10, 11]; pub const LED_MATRIX_2_PINS: [u8; 3] = [23, 24, 25]; pub const SEVEN_SEGMENT_DIGITS: [u8; 10] = [
106 0b11111100, 0b01100000, 0b11011010, 0b11110010, 0b01100110, 0b10110110, 0b10111110, 0b11100000, 0b11111110, 0b11110110, ];
117
118pub const SEVEN_SEGMENT_LETTERS: [(char, u8); 19] = [
121 ('a', 0b11101110), ('b', 0b00111110), ('c', 0b10011100), ('d', 0b01111010), ('e', 0b10011110), ('f', 0b10001110), ('h', 0b01101110), ('i', 0b01100000), ('j', 0b01110000), ('l', 0b00011100), ('n', 0b00101010), ('o', 0b11111100), ('p', 0b11001110), ('q', 0b11100110), ('r', 0b00001010), ('s', 0b10110110), ('t', 0b00011110), ('u', 0b01111000), ('y', 0b01110110), ];
141
142#[derive(Debug, Clone, Copy)]
144pub enum MatrixAction {
145 UpdateWhole,
146 SetPixel,
147 ClearPixel,
148}
149
150pub fn get_seven_segment_pattern(ch: char) -> Option<u8> {
152 match ch {
153 '0'..='9' => {
154 let digit = (ch as u8) - b'0';
155 Some(SEVEN_SEGMENT_DIGITS[digit as usize])
156 }
157 'N' => Some(0b11101100), 'a'..='z' | 'A'..='Z' => {
160 let lower_ch = ch.to_ascii_lowercase();
161 SEVEN_SEGMENT_LETTERS
162 .iter()
163 .find(|(c, _)| *c == lower_ch)
164 .map(|(_, pattern)| *pattern)
165 }
166 '-' => Some(0b00000010), '_' => Some(0b00010000), ']' => Some(0b11110000), '[' => Some(0b10011100), ' ' => Some(0b00000000), _ => None,
173 }
174}
175
176impl SevenSegmentDisplay {
177 pub fn new(matrix_id: u8, character_count: u8) -> Self {
178 Self {
179 matrix_id,
180 character_count,
181 decimal_points: vec![false; character_count as usize],
182 }
183 }
184
185 pub fn display_number(&self, device: &mut PoKeysDevice, number: u32) -> Result<()> {
186 let digits = self.number_to_digits(number);
187 let mut row_data = [0u8; 8];
188
189 for (row, digit) in digits.iter().enumerate() {
190 if row < self.character_count as usize {
191 row_data[row] = SEVEN_SEGMENT_DIGITS[*digit as usize];
192 if self.decimal_points[row] {
193 row_data[row] |= 0b00000001; }
195 }
196 }
197
198 device.update_led_matrix(self.matrix_id, MatrixAction::UpdateWhole, 0, 0, &row_data)
199 }
200
201 pub fn display_text(&self, device: &mut PoKeysDevice, text: &str) -> Result<()> {
202 let mut row_data = [0u8; 8];
203 let chars: Vec<char> = text.chars().collect();
204
205 for (row, &ch) in chars.iter().enumerate() {
206 if row < self.character_count as usize {
207 if let Some(pattern) = get_seven_segment_pattern(ch) {
208 row_data[row] = pattern;
209 if self.decimal_points[row] {
210 row_data[row] |= 0b00000001; }
212 } else {
213 row_data[row] = 0;
215 }
216 }
217 }
218
219 device.update_led_matrix(self.matrix_id, MatrixAction::UpdateWhole, 0, 0, &row_data)
220 }
221
222 pub fn display_mixed(&self, device: &mut PoKeysDevice, text: &str) -> Result<()> {
223 self.display_text(device, text)
225 }
226
227 pub fn set_decimal_point(&mut self, character: u8, enabled: bool) {
228 if character < self.character_count {
229 self.decimal_points[character as usize] = enabled;
230 }
231 }
232
233 fn number_to_digits(&self, number: u32) -> Vec<u8> {
234 let mut digits = Vec::new();
235 let mut n = number;
236
237 if n == 0 {
238 digits.push(0);
239 } else {
240 while n > 0 {
241 digits.push((n % 10) as u8);
242 n /= 10;
243 }
244 digits.reverse();
245 }
246
247 while digits.len() < self.character_count as usize {
249 digits.insert(0, 0);
250 }
251
252 digits.truncate(self.character_count as usize);
253 digits
254 }
255}
256
257impl PoKeysDevice {
258 pub fn configure_matrix_led(&mut self, led_index: usize, rows: u8, columns: u8) -> Result<()> {
260 if led_index >= self.matrix_led.len() {
261 return Err(PoKeysError::Parameter(
262 "Invalid LED matrix index".to_string(),
263 ));
264 }
265
266 if rows > 8 || columns > 8 {
267 return Err(PoKeysError::Parameter(
268 "Matrix LED size too large".to_string(),
269 ));
270 }
271
272 self.matrix_led[led_index].display_enabled = 1;
273 self.matrix_led[led_index].rows = rows;
274 self.matrix_led[led_index].columns = columns;
275
276 self.send_request(0x62, led_index as u8, rows, columns, 1)?;
278 Ok(())
279 }
280
281 pub fn update_matrix_led(&mut self, led_index: usize) -> Result<()> {
283 if led_index >= self.matrix_led.len() {
284 return Err(PoKeysError::Parameter(
285 "Invalid LED matrix index".to_string(),
286 ));
287 }
288
289 if !self.matrix_led[led_index].is_enabled() {
290 return Err(PoKeysError::NotSupported);
291 }
292
293 let data = self.matrix_led[led_index].data;
295 let rows = self.matrix_led[led_index].rows;
296
297 self.send_request(0x63, led_index as u8, data[0], data[1], data[2])?;
299
300 if rows > 3 {
302 self.send_request(0x64, led_index as u8, data[3], data[4], data[5])?;
303 }
304
305 if rows > 6 {
306 self.send_request(0x65, led_index as u8, data[6], data[7], 0)?;
307 }
308
309 self.matrix_led[led_index].refresh_flag = 0;
310 Ok(())
311 }
312
313 pub fn set_matrix_led(
315 &mut self,
316 led_index: usize,
317 row: usize,
318 col: usize,
319 state: bool,
320 ) -> Result<()> {
321 if led_index >= self.matrix_led.len() {
322 return Err(PoKeysError::Parameter(
323 "Invalid LED matrix index".to_string(),
324 ));
325 }
326
327 self.matrix_led[led_index].set_led(row, col, state)?;
328 self.update_matrix_led(led_index)?;
329 Ok(())
330 }
331
332 pub fn get_matrix_led(&self, led_index: usize, row: usize, col: usize) -> Result<bool> {
334 if led_index >= self.matrix_led.len() {
335 return Err(PoKeysError::Parameter(
336 "Invalid LED matrix index".to_string(),
337 ));
338 }
339
340 Ok(self.matrix_led[led_index].get_led(row, col))
341 }
342
343 pub fn clear_matrix_led(&mut self, led_index: usize) -> Result<()> {
345 if led_index >= self.matrix_led.len() {
346 return Err(PoKeysError::Parameter(
347 "Invalid LED matrix index".to_string(),
348 ));
349 }
350
351 self.matrix_led[led_index].clear_all();
352 self.update_matrix_led(led_index)?;
353 Ok(())
354 }
355
356 pub fn set_all_matrix_led(&mut self, led_index: usize) -> Result<()> {
358 if led_index >= self.matrix_led.len() {
359 return Err(PoKeysError::Parameter(
360 "Invalid LED matrix index".to_string(),
361 ));
362 }
363
364 self.matrix_led[led_index].set_all();
365 self.update_matrix_led(led_index)?;
366 Ok(())
367 }
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373
374 #[test]
375 fn test_matrix_led_creation() {
376 let mut led = MatrixLed::new();
377 assert!(!led.is_enabled());
378
379 led.display_enabled = 1;
380 led.rows = 8;
381 led.columns = 8;
382
383 assert!(led.is_enabled());
384 assert!(!led.get_led(0, 0));
385
386 assert!(led.set_led(0, 0, true).is_ok());
387 assert!(led.get_led(0, 0));
388
389 assert!(led.set_led(0, 0, false).is_ok());
390 assert!(!led.get_led(0, 0));
391 }
392
393 #[test]
394 fn test_matrix_led_bounds_checking() {
395 let mut led = MatrixLed::new();
396 led.rows = 4;
397 led.columns = 8;
398
399 assert!(led.set_led(0, 0, true).is_ok());
401 assert!(led.set_led(3, 7, true).is_ok());
402
403 assert!(led.set_led(4, 0, true).is_err()); assert!(led.set_led(0, 8, true).is_err()); }
407}