1pub use device_envoy_core::led4::{ANIMATION_MAX_FRAMES, AnimationFrame, BlinkState, Led4};
12pub type Animation = device_envoy_core::led4::Animation;
14
15#[must_use]
17pub fn circular_outline_animation(clockwise: bool) -> Animation {
18 device_envoy_core::led4::circular_outline_animation(clockwise)
19}
20
21#[cfg(target_os = "none")]
22const CELL_COUNT: usize = device_envoy_core::led4::CELL_COUNT;
23#[cfg(target_os = "none")]
24const SEGMENT_COUNT: usize = device_envoy_core::led4::SEGMENT_COUNT;
25
26#[cfg(target_os = "none")]
27use core::convert::Infallible;
28
29#[cfg(target_os = "none")]
30use device_envoy_core::led4::{
31 BitMatrixLed4, Led4OutputAdapter, Led4SimpleLoopError, run_command_loop, run_simple_loop,
32 signal_animation, signal_text,
33};
34#[cfg(target_os = "none")]
35use embassy_executor::Spawner;
36#[cfg(target_os = "none")]
37use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
38
39#[cfg(target_os = "none")]
40use crate::{Error, Result};
41
42#[cfg(target_os = "none")]
43mod output_array;
44#[cfg(target_os = "none")]
45pub use output_array::OutputArray;
46
47#[cfg(target_os = "none")]
48struct Led4SimpleStatic(Signal<CriticalSectionRawMutex, BitMatrixLed4>);
49
50#[cfg(target_os = "none")]
51impl Led4SimpleStatic {
52 const fn new() -> Self {
53 Self(Signal::new())
54 }
55
56 fn signal(&self, bit_matrix_led4: BitMatrixLed4) {
57 self.0.signal(bit_matrix_led4);
58 }
59}
60
61#[cfg(target_os = "none")]
62struct Led4Simple<'a>(&'a Led4SimpleStatic);
63
64#[cfg(target_os = "none")]
65impl Led4Simple<'_> {
66 const fn new_static() -> Led4SimpleStatic {
67 Led4SimpleStatic::new()
68 }
69
70 #[must_use = "Must be used to manage the spawned task"]
71 fn new(
72 led4_simple_static: &'static Led4SimpleStatic,
73 cell_pins: OutputArray<'static, CELL_COUNT>,
74 segment_pins: OutputArray<'static, SEGMENT_COUNT>,
75 spawner: Spawner,
76 ) -> Result<Self> {
77 let token = led4_simple_device_loop(cell_pins, segment_pins, led4_simple_static);
78 spawner.spawn(token.map_err(Error::TaskSpawn)?);
79 Ok(Self(led4_simple_static))
80 }
81
82 fn write_text(&self, text: [char; CELL_COUNT]) {
83 self.0.signal(BitMatrixLed4::from_text(&text));
84 }
85}
86
87#[embassy_executor::task(pool_size = 2)]
88#[cfg(target_os = "none")]
89async fn led4_simple_device_loop(
90 cell_pins: OutputArray<'static, CELL_COUNT>,
91 segment_pins: OutputArray<'static, SEGMENT_COUNT>,
92 led4_simple_static: &'static Led4SimpleStatic,
93) -> ! {
94 let error = inner_led4_simple_device_loop(cell_pins, segment_pins, led4_simple_static)
95 .await
96 .unwrap_err();
97 panic!("{error:?}");
98}
99
100#[cfg(target_os = "none")]
101async fn inner_led4_simple_device_loop(
102 cell_pins: OutputArray<'static, CELL_COUNT>,
103 segment_pins: OutputArray<'static, SEGMENT_COUNT>,
104 led4_simple_static: &'static Led4SimpleStatic,
105) -> Result<Infallible> {
106 let mut esp_led4_output = EspLed4Output {
107 cell_pins,
108 segment_pins,
109 };
110 run_simple_loop(&mut esp_led4_output, &led4_simple_static.0)
111 .await
112 .map_err(Error::from)
113}
114
115#[cfg(target_os = "none")]
116struct EspLed4Output {
117 cell_pins: OutputArray<'static, CELL_COUNT>,
118 segment_pins: OutputArray<'static, SEGMENT_COUNT>,
119}
120
121#[cfg(target_os = "none")]
122impl Led4OutputAdapter for EspLed4Output {
123 type Error = Error;
124
125 fn set_segments_from_nonzero_bits(&mut self, bits: core::num::NonZeroU8) {
126 self.segment_pins.set_from_nonzero_bits(bits);
127 }
128
129 fn set_cells_active(&mut self, indexes: &[u8], active: bool) -> Result<(), Self::Error> {
130 let level = if active {
131 esp_hal::gpio::Level::Low
132 } else {
133 esp_hal::gpio::Level::High
134 };
135 self.cell_pins.set_levels_at_indexes(indexes, level)
136 }
137}
138
139#[cfg(target_os = "none")]
140impl From<Led4SimpleLoopError<Error>> for Error {
141 fn from(error: Led4SimpleLoopError<Error>) -> Self {
142 match error {
143 Led4SimpleLoopError::BitsToIndexes(error) => Self::from(error),
144 Led4SimpleLoopError::Output(error) => error,
145 }
146 }
147}
148
149#[cfg(target_os = "none")]
219pub struct Led4Esp<'a>(&'a Led4EspOuterStatic);
220
221#[cfg(target_os = "none")]
222type Led4EspOuterStatic = device_envoy_core::led4::Led4CommandSignal;
223
224#[cfg(target_os = "none")]
226pub struct Led4EspStatic {
227 outer: Led4EspOuterStatic,
228 display: Led4SimpleStatic,
229}
230
231#[cfg(target_os = "none")]
232impl Led4EspStatic {
233 const fn new() -> Self {
234 Self {
235 outer: device_envoy_core::led4::Led4CommandSignal::new(),
236 display: Led4Simple::new_static(),
237 }
238 }
239
240 fn split(&self) -> (&Led4EspOuterStatic, &Led4SimpleStatic) {
241 (&self.outer, &self.display)
242 }
243}
244
245#[cfg(target_os = "none")]
246impl Led4Esp<'_> {
247 #[must_use = "Must be used to manage the spawned task"]
249 pub fn new(
250 led4_static: &'static Led4EspStatic,
251 cell_pins: OutputArray<'static, CELL_COUNT>,
252 segment_pins: OutputArray<'static, SEGMENT_COUNT>,
253 spawner: Spawner,
254 ) -> Result<Self> {
255 let (outer_static, display_static) = led4_static.split();
256 let display = Led4Simple::new(display_static, cell_pins, segment_pins, spawner)?;
257 let token = led4_device_loop(outer_static, display);
258 spawner.spawn(token.map_err(Error::TaskSpawn)?);
259 Ok(Self(outer_static))
260 }
261
262 #[must_use]
264 pub const fn new_static() -> Led4EspStatic {
265 Led4EspStatic::new()
266 }
267}
268
269#[cfg(target_os = "none")]
270impl device_envoy_core::led4::Led4 for Led4Esp<'_> {
271 fn write_text(&self, text: [char; CELL_COUNT], blink_state: BlinkState) {
272 signal_text(self.0, text, blink_state);
273 }
274
275 fn animate_text<I>(&self, animation: I)
276 where
277 I: IntoIterator,
278 I::Item: core::borrow::Borrow<AnimationFrame>,
279 {
280 signal_animation(self.0, animation);
281 }
282}
283
284#[embassy_executor::task(pool_size = 2)]
285#[cfg(target_os = "none")]
286async fn led4_device_loop(
287 outer_static: &'static Led4EspOuterStatic,
288 display: Led4Simple<'static>,
289) -> ! {
290 run_command_loop(outer_static, |text| display.write_text(text)).await
291}