hyperion/instance/device/
ws2812spi.rs1use async_trait::async_trait;
2use spidev::{SpiModeFlags, Spidev, SpidevOptions, SpidevTransfer};
3
4use super::{common::*, DeviceError};
5use crate::models;
6
7pub type Ws2812SpiDevice = Rewriter<Ws2812SpiImpl>;
8
9pub struct Ws2812SpiImpl {
10 dev: ImplState,
11 notified_error: bool,
12 buf: Vec<u8>,
13}
14
15const SPI_BYTES_PER_LED: usize = 3 * SPI_BYTES_PER_COLOUR;
16const SPI_BYTES_PER_COLOUR: usize = 4;
17const SPI_FRAME_END_LATCH_BYTES: usize = 116;
18const BITPAIR_TO_BYTE: [u8; 4] = [0b10001000, 0b10001100, 0b11001000, 0b11001100];
19
20enum ImplState {
21 Pending(models::Ws2812Spi),
22 Ready(Spidev),
23}
24
25impl ImplState {
26 fn as_dev(&self) -> Option<&Spidev> {
27 match self {
28 ImplState::Ready(dev) => Some(dev),
29 _ => None,
30 }
31 }
32
33 fn try_init(&mut self) -> Result<&Spidev, DeviceError> {
34 match self {
35 ImplState::Pending(config) => {
36 let mut dev = Spidev::open(&config.output)?;
38 let options = SpidevOptions::new()
39 .bits_per_word(8)
40 .max_speed_hz(config.rate as _)
41 .mode(SpiModeFlags::SPI_MODE_0)
42 .build();
43 dev.configure(&options)?;
44
45 info!(path = %config.output, "initialized SPI device");
46
47 *self = Self::from(dev);
48 Ok(self.as_dev().unwrap())
49 }
50
51 ImplState::Ready(dev) => Ok(dev),
52 }
53 }
54}
55
56impl From<&models::Ws2812Spi> for ImplState {
57 fn from(value: &models::Ws2812Spi) -> Self {
58 Self::Pending(value.clone())
59 }
60}
61
62impl From<Spidev> for ImplState {
63 fn from(value: Spidev) -> Self {
64 Self::Ready(value)
65 }
66}
67
68#[async_trait]
69impl WritingDevice for Ws2812SpiImpl {
70 type Config = models::Ws2812Spi;
71
72 fn new(config: &models::Ws2812Spi) -> Result<Self, DeviceError> {
73 let buf = vec![
75 0;
76 config.hardware_led_count as usize * SPI_BYTES_PER_LED
77 + SPI_FRAME_END_LATCH_BYTES
78 ];
79
80 let mut dev = ImplState::from(config);
81
82 if let Err(error) = dev.try_init() {
84 warn!(%error, path = %config.output, "failed to initialize SPI device, will try again later");
85 }
86
87 Ok(Self {
88 dev,
89 notified_error: false,
90 buf,
91 })
92 }
93
94 async fn set_let_data(
95 &mut self,
96 config: &Self::Config,
97 led_data: &[models::Color],
98 ) -> Result<(), DeviceError> {
99 let mut ptr = 0;
101 for led in led_data {
102 let (r, g, b) = config.color_order.reorder_from_rgb(*led).into_components();
103 let mut color_bits = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
104
105 for j in (0..SPI_BYTES_PER_LED).rev() {
106 self.buf[ptr + j] = BITPAIR_TO_BYTE[(color_bits & 0x3) as usize];
107 color_bits >>= 2;
108 }
109
110 ptr += SPI_BYTES_PER_LED;
111 }
112
113 for dst in self.buf.iter_mut().skip(ptr) {
114 *dst = 0;
115 }
116
117 if config.invert {
118 for byte in &mut self.buf {
119 *byte = !*byte;
120 }
121 }
122
123 Ok(())
124 }
125
126 async fn write(&mut self) -> Result<(), DeviceError> {
127 let mut transfer = SpidevTransfer::write(&self.buf);
129
130 match self.dev.try_init() {
132 Ok(dev) => {
133 self.notified_error = false;
134 dev.transfer(&mut transfer)?;
135 }
136 Err(err) => {
137 if !self.notified_error {
138 self.notified_error = true;
139 error!(error = %err, "failed to initialize SPI device");
140 }
141 }
142 }
143
144 Ok(())
145 }
146}