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 self.write_byte(0b1110_0000 | data[3])
154 .context("Failed to write brightness byte")?;
155 self.write_byte(data[2])
157 .context("Failed to write blue byte")?;
158 self.write_byte(data[1])
160 .context("Failed to write green byte")?;
161 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}