use crate::device::PoKeysDevice;
use crate::error::{PoKeysError, Result};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MatrixLed {
pub display_enabled: u8,
pub rows: u8,
pub columns: u8,
pub refresh_flag: u8,
pub data: [u8; 8],
}
impl MatrixLed {
pub fn new() -> Self {
Self {
display_enabled: 0,
rows: 0,
columns: 0,
refresh_flag: 0,
data: [0; 8],
}
}
pub fn is_enabled(&self) -> bool {
self.display_enabled != 0
}
pub fn set_led(&mut self, row: usize, col: usize, state: bool) -> Result<()> {
if row >= self.rows as usize || col >= 8 {
return Err(PoKeysError::Parameter("Invalid LED position".to_string()));
}
if state {
self.data[row] |= 1 << col;
} else {
self.data[row] &= !(1 << col);
}
self.refresh_flag = 1;
Ok(())
}
pub fn get_led(&self, row: usize, col: usize) -> bool {
if row >= self.rows as usize || col >= 8 {
return false;
}
(self.data[row] & (1 << col)) != 0
}
pub fn clear_all(&mut self) {
self.data.fill(0);
self.refresh_flag = 1;
}
pub fn set_all(&mut self) {
self.data.fill(0xFF);
self.refresh_flag = 1;
}
}
impl Default for MatrixLed {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LedMatrixConfig {
pub name: String,
pub description: Option<String>,
pub matrix_id: u8, pub enabled: bool,
pub characters: u8, }
pub struct SevenSegmentDisplay {
pub matrix_id: u8,
pub character_count: u8, pub decimal_points: Vec<bool>,
}
pub struct MatrixLedProtocolConfig {
pub display1_enabled: bool,
pub display2_enabled: bool,
pub display1_characters: u8, pub display2_characters: u8, }
pub 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] = [
0b11111100, 0b01100000, 0b11011010, 0b11110010, 0b01100110, 0b10110110, 0b10111110, 0b11100000, 0b11111110, 0b11110110, ];
pub const SEVEN_SEGMENT_LETTERS: [(char, u8); 19] = [
('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), ];
#[derive(Debug, Clone, Copy)]
pub enum MatrixAction {
UpdateWhole,
SetPixel,
ClearPixel,
}
pub fn get_seven_segment_pattern(ch: char) -> Option<u8> {
match ch {
'0'..='9' => {
let digit = (ch as u8) - b'0';
Some(SEVEN_SEGMENT_DIGITS[digit as usize])
}
'N' => Some(0b11101100), 'a'..='z' | 'A'..='Z' => {
let lower_ch = ch.to_ascii_lowercase();
SEVEN_SEGMENT_LETTERS
.iter()
.find(|(c, _)| *c == lower_ch)
.map(|(_, pattern)| *pattern)
}
'-' => Some(0b00000010), '_' => Some(0b00010000), ']' => Some(0b11110000), '[' => Some(0b10011100), ' ' => Some(0b00000000), _ => None,
}
}
impl SevenSegmentDisplay {
pub fn new(matrix_id: u8, character_count: u8) -> Self {
Self {
matrix_id,
character_count,
decimal_points: vec![false; character_count as usize],
}
}
pub fn display_number(&self, device: &mut PoKeysDevice, number: u32) -> Result<()> {
let digits = self.number_to_digits(number);
let mut row_data = [0u8; 8];
for (row, digit) in digits.iter().enumerate() {
if row < self.character_count as usize {
row_data[row] = SEVEN_SEGMENT_DIGITS[*digit as usize];
if self.decimal_points[row] {
row_data[row] |= 0b00000001; }
}
}
device.update_led_matrix(self.matrix_id, MatrixAction::UpdateWhole, 0, 0, &row_data)
}
pub fn display_text(&self, device: &mut PoKeysDevice, text: &str) -> Result<()> {
let mut row_data = [0u8; 8];
let chars: Vec<char> = text.chars().collect();
for (row, &ch) in chars.iter().enumerate() {
if row < self.character_count as usize {
if let Some(pattern) = get_seven_segment_pattern(ch) {
row_data[row] = pattern;
if self.decimal_points[row] {
row_data[row] |= 0b00000001; }
} else {
row_data[row] = 0;
}
}
}
device.update_led_matrix(self.matrix_id, MatrixAction::UpdateWhole, 0, 0, &row_data)
}
pub fn display_mixed(&self, device: &mut PoKeysDevice, text: &str) -> Result<()> {
self.display_text(device, text)
}
pub fn set_decimal_point(&mut self, character: u8, enabled: bool) {
if character < self.character_count {
self.decimal_points[character as usize] = enabled;
}
}
fn number_to_digits(&self, number: u32) -> Vec<u8> {
let mut digits = Vec::new();
let mut n = number;
if n == 0 {
digits.push(0);
} else {
while n > 0 {
digits.push((n % 10) as u8);
n /= 10;
}
digits.reverse();
}
while digits.len() < self.character_count as usize {
digits.insert(0, 0);
}
digits.truncate(self.character_count as usize);
digits
}
}
impl PoKeysDevice {
pub fn configure_matrix_led(&mut self, led_index: usize, rows: u8, columns: u8) -> Result<()> {
if led_index >= self.matrix_led.len() {
return Err(PoKeysError::Parameter(
"Invalid LED matrix index".to_string(),
));
}
if rows > 8 || columns > 8 {
return Err(PoKeysError::Parameter(
"Matrix LED size too large".to_string(),
));
}
self.matrix_led[led_index].display_enabled = 1;
self.matrix_led[led_index].rows = rows;
self.matrix_led[led_index].columns = columns;
self.send_request(0x62, led_index as u8, rows, columns, 1)?;
Ok(())
}
pub fn update_matrix_led(&mut self, led_index: usize) -> Result<()> {
if led_index >= self.matrix_led.len() {
return Err(PoKeysError::Parameter(
"Invalid LED matrix index".to_string(),
));
}
if !self.matrix_led[led_index].is_enabled() {
return Err(PoKeysError::NotSupported);
}
let data = self.matrix_led[led_index].data;
let rows = self.matrix_led[led_index].rows;
self.send_request(0x63, led_index as u8, data[0], data[1], data[2])?;
if rows > 3 {
self.send_request(0x64, led_index as u8, data[3], data[4], data[5])?;
}
if rows > 6 {
self.send_request(0x65, led_index as u8, data[6], data[7], 0)?;
}
self.matrix_led[led_index].refresh_flag = 0;
Ok(())
}
pub fn set_matrix_led(
&mut self,
led_index: usize,
row: usize,
col: usize,
state: bool,
) -> Result<()> {
if led_index >= self.matrix_led.len() {
return Err(PoKeysError::Parameter(
"Invalid LED matrix index".to_string(),
));
}
self.matrix_led[led_index].set_led(row, col, state)?;
self.update_matrix_led(led_index)?;
Ok(())
}
pub fn get_matrix_led(&self, led_index: usize, row: usize, col: usize) -> Result<bool> {
if led_index >= self.matrix_led.len() {
return Err(PoKeysError::Parameter(
"Invalid LED matrix index".to_string(),
));
}
Ok(self.matrix_led[led_index].get_led(row, col))
}
pub fn clear_matrix_led(&mut self, led_index: usize) -> Result<()> {
if led_index >= self.matrix_led.len() {
return Err(PoKeysError::Parameter(
"Invalid LED matrix index".to_string(),
));
}
self.matrix_led[led_index].clear_all();
self.update_matrix_led(led_index)?;
Ok(())
}
pub fn set_all_matrix_led(&mut self, led_index: usize) -> Result<()> {
if led_index >= self.matrix_led.len() {
return Err(PoKeysError::Parameter(
"Invalid LED matrix index".to_string(),
));
}
self.matrix_led[led_index].set_all();
self.update_matrix_led(led_index)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_matrix_led_creation() {
let mut led = MatrixLed::new();
assert!(!led.is_enabled());
led.display_enabled = 1;
led.rows = 8;
led.columns = 8;
assert!(led.is_enabled());
assert!(!led.get_led(0, 0));
assert!(led.set_led(0, 0, true).is_ok());
assert!(led.get_led(0, 0));
assert!(led.set_led(0, 0, false).is_ok());
assert!(!led.get_led(0, 0));
}
#[test]
fn test_matrix_led_bounds_checking() {
let mut led = MatrixLed::new();
led.rows = 4;
led.columns = 8;
assert!(led.set_led(0, 0, true).is_ok());
assert!(led.set_led(3, 7, true).is_ok());
assert!(led.set_led(4, 0, true).is_err()); assert!(led.set_led(0, 8, true).is_err()); }
}