Skip to content

Commit 1610ce8

Browse files
committed
fix(ws2812): Try opening device multiple times
1 parent 6b11b1f commit 1610ce8

File tree

1 file changed

+76
-13
lines changed

1 file changed

+76
-13
lines changed

src/instance/device/ws2812spi.rs

+76-13
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use crate::models;
77
pub type Ws2812SpiDevice = Rewriter<Ws2812SpiImpl>;
88

99
pub struct Ws2812SpiImpl {
10-
dev: Spidev,
10+
dev: ImplState,
11+
notified_error: bool,
1112
buf: Vec<u8>,
1213
}
1314

@@ -16,30 +17,78 @@ const SPI_BYTES_PER_COLOUR: usize = 4;
1617
const SPI_FRAME_END_LATCH_BYTES: usize = 116;
1718
const BITPAIR_TO_BYTE: [u8; 4] = [0b10001000, 0b10001100, 0b11001000, 0b11001100];
1819

20+
enum ImplState {
21+
Pending(models::Ws2812Spi),
22+
Ready(Spidev),
23+
}
24+
25+
impl 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+
// Initialize SPI device
37+
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+
56+
impl From<&models::Ws2812Spi> for ImplState {
57+
fn from(value: &models::Ws2812Spi) -> Self {
58+
Self::Pending(value.clone())
59+
}
60+
}
61+
62+
impl From<Spidev> for ImplState {
63+
fn from(value: Spidev) -> Self {
64+
Self::Ready(value)
65+
}
66+
}
67+
1968
#[async_trait]
2069
impl WritingDevice for Ws2812SpiImpl {
2170
type Config = models::Ws2812Spi;
2271

2372
fn new(config: &models::Ws2812Spi) -> Result<Self, DeviceError> {
24-
// Initialize SPI device
25-
let mut dev = Spidev::open(&config.output)?;
26-
let options = SpidevOptions::new()
27-
.bits_per_word(8)
28-
.max_speed_hz(config.rate as _)
29-
.mode(SpiModeFlags::SPI_MODE_0)
30-
.build();
31-
dev.configure(&options)?;
32-
3373
// Buffer for SPI tranfers
3474
let buf = vec![
3575
0;
3676
config.hardware_led_count as usize * SPI_BYTES_PER_LED
3777
+ SPI_FRAME_END_LATCH_BYTES
3878
];
3979

40-
info!(path = %config.output, "initialized SPI device");
80+
let mut dev = ImplState::from(config);
81+
82+
// Try to open the device early
83+
if let Err(error) = dev.try_init() {
84+
warn!(%error, path = %config.output, "failed to initialize SPI device, will try again later");
85+
}
4186

42-
Ok(Self { dev, buf })
87+
Ok(Self {
88+
dev,
89+
notified_error: false,
90+
buf,
91+
})
4392
}
4493

4594
async fn set_let_data(
@@ -77,7 +126,21 @@ impl WritingDevice for Ws2812SpiImpl {
77126
async fn write(&mut self) -> Result<(), DeviceError> {
78127
// Perform SPI transfer
79128
let mut transfer = SpidevTransfer::write(&self.buf);
80-
self.dev.transfer(&mut transfer)?;
129+
130+
// Try writing to the device
131+
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+
81144
Ok(())
82145
}
83146
}

0 commit comments

Comments
 (0)