blinkt_cdev/
lib.rs

1use anyhow::{anyhow, Context, Result};
2use gpio_cdev::*;
3
4const DEFAULT_BRIGHTNESS: u8 = 7;
5const RED_INDEX: usize = 0;
6const GREEN_INDEX: usize = 1;
7const BLUE_INDEX: usize = 2;
8const BRIGHTNESS_INDEX: usize = 3;
9const DATA_LINE: u32 = 23;
10const CLOCK_LINE: u32 = 24;
11
12#[derive(Debug, Copy, Clone)]
13struct Pixel {
14    values: [u8; 4],
15}
16
17impl Pixel {
18    pub fn rgb(&self) -> (u8, u8, u8) {
19        (
20            self.values[RED_INDEX],
21            self.values[GREEN_INDEX],
22            self.values[BLUE_INDEX],
23        )
24    }
25
26    pub fn rgbb(&self) -> (u8, u8, u8, f32) {
27        (
28            self.values[RED_INDEX],
29            self.values[GREEN_INDEX],
30            self.values[BLUE_INDEX],
31            f32::from(0b0001_1111 & self.values[BRIGHTNESS_INDEX]) / 31.0,
32        )
33    }
34
35    pub fn set_brightness(&mut self, brightness: f32) {
36        self.values[BRIGHTNESS_INDEX] = 0b1110_0000 | ((31.0 * brightness.max(0.0).min(1.0)) as u8);
37    }
38
39    pub fn set_rgb(&mut self, red: u8, green: u8, blue: u8) {
40        self.values[RED_INDEX] = red;
41        self.values[GREEN_INDEX] = green;
42        self.values[BLUE_INDEX] = blue;
43    }
44
45    pub fn set_rgbb(&mut self, red: u8, green: u8, blue: u8, brightness: f32) {
46        self.set_rgb(red, green, blue);
47        self.set_brightness(brightness);
48    }
49
50    pub(crate) fn data(&self) -> &[u8] {
51        &self.values
52    }
53
54    pub fn clear(&mut self) {
55        self.set_rgb(0, 0, 0);
56    }
57}
58
59impl Default for Pixel {
60    fn default() -> Pixel {
61        Pixel {
62            values: [0, 0, 0, 0b1110_0000 | DEFAULT_BRIGHTNESS],
63        }
64    }
65}
66
67pub struct Blinkt {
68    data_output_handle: LineHandle,
69    clock_output_handle: LineHandle,
70    clear_on_drop: bool,
71    pixels: Vec<Pixel>,
72}
73
74impl Blinkt {
75    pub fn new() -> Result<Blinkt> {
76        let mut chip = Chip::new("/dev/gpiochip0").context("Failed to get gpio chip")?;
77
78        let data_output = chip
79            .get_line(DATA_LINE)
80            .context("Failed to get data line")?;
81        let data_output_handle = data_output
82            .request(LineRequestFlags::OUTPUT, 0, "data-output")
83            .context("Failed to get data output handle")?;
84
85        let clock_output = chip
86            .get_line(CLOCK_LINE)
87            .context("Failed to get clock line")?;
88        let clock_output_handle = clock_output
89            .request(LineRequestFlags::OUTPUT, 0, "clock-output")
90            .context("Failed to get clock output handle")?;
91        Ok(Blinkt {
92            data_output_handle,
93            clock_output_handle,
94            clear_on_drop: true,
95            pixels: vec![Pixel::default(); 8],
96        })
97    }
98
99    fn write_sof(&self) -> Result<()> {
100        self.data_output_handle
101            .set_value(0)
102            .context("Failed to write start of sof")?;
103        for _ in 0..32 {
104            self.clock_output_handle
105                .set_value(1)
106                .context("Failed to write sof")?;
107
108            self.clock_output_handle
109                .set_value(0)
110                .context("Failed to write sof")?;
111        }
112        Ok(())
113    }
114
115    fn write_eof(&self) -> Result<()> {
116        self.data_output_handle
117            .set_value(0)
118            .context("Failed to write start of eof")?;
119        for _ in 0..36 {
120            self.clock_output_handle
121                .set_value(1)
122                .context("Failed to write sof")?;
123
124            self.clock_output_handle
125                .set_value(0)
126                .context("Failed to write sof")?;
127        }
128        Ok(())
129    }
130
131    fn write_byte(&self, mut byte: u8) -> Result<()> {
132        for x in 0..8 {
133            self.data_output_handle
134                .set_value(byte & 0b1000_0000)
135                .with_context(|| format!("Failed to write bit \"{:?}\" of byte", x))?;
136            self.clock_output_handle
137                .set_value(1)
138                .context("Failed to start clock pulse of writing bit")?;
139
140            byte <<= 1;
141            self.clock_output_handle
142                .set_value(0)
143                .context("Failed to end clock pulse of writing bit")?;
144        }
145        Ok(())
146    }
147
148    pub fn show(&self) -> Result<()> {
149        self.write_sof()?;
150        for pixel in &self.pixels {
151            let data = pixel.data();
152            // write brightness
153            self.write_byte(0b1110_0000 | data[3])
154                .context("Failed to write brightness byte")?;
155            //write blue
156            self.write_byte(data[2])
157                .context("Failed to write blue byte")?;
158            // write green
159            self.write_byte(data[1])
160                .context("Failed to write green byte")?;
161            // write red
162            self.write_byte(data[0])
163                .context("Failed to write red byte")?;
164        }
165        self.write_eof()?;
166        Ok(())
167    }
168
169    pub fn get_pixel(&mut self, pixel: usize) -> Result<(u8, u8, u8, f32)> {
170        if let Some(pixel) = self.pixels.get_mut(pixel) {
171            return Ok(pixel.rgbb());
172        }
173        Err(anyhow!("Failed to get data for pixel \"{:?}\"", pixel))
174    }
175
176    pub fn set_pixel(&mut self, pixel: usize, red: u8, green: u8, blue: u8, brightness: f32) {
177        if let Some(pixel) = self.pixels.get_mut(pixel) {
178            pixel.set_rgbb(red, green, blue, brightness);
179        }
180    }
181
182    pub fn set_all_pixels(&mut self, red: u8, green: u8, blue: u8, brightness: f32) {
183        for pixel in 0..8 {
184            self.set_pixel(pixel, red, green, blue, brightness);
185        }
186    }
187
188    pub fn clear(&mut self) {
189        self.set_all_pixels(0, 0, 0, 0.2);
190    }
191}
192
193impl Drop for Blinkt {
194    fn drop(&mut self) {
195        if self.clear_on_drop {
196            self.clear();
197            let _ = self.show();
198        }
199    }
200}