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