66 holding buffers for the duration of a data transfer."
77) ]
88
9- use desk_control_panel:: meeting_instruction:: { self , MeetingSignInstruction } ;
9+ use desk_control_panel:: meeting_instruction:: {
10+ self , MeetingSignInstruction , UART_COMMUNICATION_TIMEOUT ,
11+ } ;
1012use embassy_executor:: Spawner ;
13+ use embassy_futures:: select:: { select, Either } ;
1114use embassy_sync:: blocking_mutex:: raw:: CriticalSectionRawMutex ;
1215use embassy_sync:: mutex:: Mutex ;
13- use embassy_time:: { Duration , Timer } ;
16+ use embassy_sync:: pubsub:: { ImmediatePublisher , PubSubChannel , Subscriber } ;
17+ use embassy_time:: Timer ;
1418use esp_hal:: {
1519 clock:: CpuClock ,
1620 gpio:: { AnyPin , Level , Output , OutputConfig } ,
@@ -34,47 +38,42 @@ esp_bootloader_esp_idf::esp_app_desc!();
3438// Global static for the panic pin
3539static mut PANIC_PIN : Option < Output < ' static > > = None ;
3640
37- type LEDsMutex = Mutex < CriticalSectionRawMutex , LEDs < ' static > > ;
38- static LEDS : StaticCell < LEDsMutex > = StaticCell :: new ( ) ;
39-
40- struct LEDs < ' a > {
41- led_outs : [ Output < ' a > ; 9 ] ,
41+ #[ derive( Clone ) ]
42+ enum MeetingSignState {
43+ NoUart ,
44+ Uart ,
4245}
43- impl < ' a > LEDs < ' a > {
44- pub fn new ( pins : [ AnyPin < ' a > ; 9 ] ) -> Self {
45- // Convert each pin to an Output
46- let led_outs = pins. map ( |pin| Output :: new ( pin, Level :: Low , OutputConfig :: default ( ) ) ) ;
47-
48- Self { led_outs }
49- }
5046
51- pub fn toggle_all ( & mut self ) {
52- for led in & mut self . led_outs {
53- led. toggle ( ) ;
54- }
55- }
56-
57- pub fn set_portion_high ( & mut self , numerator : f32 , denominator : f32 ) {
58- let num_on_leds = if numerator <= 0.0 || denominator <= 0.0 {
59- warn ! (
60- "Invalid portion values: numerator {}, denominator {}" ,
61- numerator, denominator
62- ) ;
63- 0 // This will turn off all LEDs
64- } else {
65- ( NUM_LEDS as f32 * numerator / denominator) . round ( ) as usize
66- } ;
47+ type LEDsMutex = Mutex < CriticalSectionRawMutex , LEDs < ' static > > ;
48+ static LEDS : StaticCell < LEDsMutex > = StaticCell :: new ( ) ;
6749
68- for ( led_idx, led) in self . led_outs . iter_mut ( ) . enumerate ( ) {
69- // if led_idx + 1 <= num_on_leds {
70- if led_idx < num_on_leds {
71- led. set_high ( ) ;
72- } else {
73- led. set_low ( ) ;
74- }
75- }
76- }
77- }
50+ const STATE_PUB_SUB_CAPACITY : usize = 1 ;
51+ const STATE_NUM_PUBLISHERS : usize = 0 ;
52+ const STATE_NUM_SUBSCRIBERS : usize = 3 ;
53+ type MeetingSignStatePubSubChannel = PubSubChannel <
54+ CriticalSectionRawMutex ,
55+ MeetingSignState ,
56+ STATE_PUB_SUB_CAPACITY ,
57+ STATE_NUM_SUBSCRIBERS ,
58+ STATE_NUM_PUBLISHERS ,
59+ > ;
60+ type MeetingSignStatePublisher < ' a > = ImmediatePublisher <
61+ ' a ,
62+ CriticalSectionRawMutex ,
63+ MeetingSignState ,
64+ STATE_PUB_SUB_CAPACITY ,
65+ STATE_NUM_SUBSCRIBERS ,
66+ STATE_NUM_PUBLISHERS ,
67+ > ;
68+ type MeetingSignStateSubscriber < ' a > = Subscriber <
69+ ' a ,
70+ CriticalSectionRawMutex ,
71+ MeetingSignState ,
72+ STATE_PUB_SUB_CAPACITY ,
73+ STATE_NUM_SUBSCRIBERS ,
74+ STATE_NUM_PUBLISHERS ,
75+ > ;
76+ static MEETING_SIGN_STATE : StaticCell < MeetingSignStatePubSubChannel > = StaticCell :: new ( ) ;
7877
7978#[ esp_hal_embassy:: main]
8079async fn main ( spawner : Spawner ) {
@@ -97,7 +96,7 @@ async fn main(spawner: Spawner) {
9796 }
9897
9998 // WARN: These LED pins need to match the emergency hardcoded LED pins
100- let led_pins: [ AnyPin ; 9 ] = [
99+ let led_pins: [ AnyPin ; NUM_LEDS ] = [
101100 peripherals. GPIO5 . into ( ) ,
102101 peripherals. GPIO6 . into ( ) ,
103102 peripherals. GPIO7 . into ( ) ,
@@ -109,7 +108,25 @@ async fn main(spawner: Spawner) {
109108 peripherals. GPIO0 . into ( ) ,
110109 ] ;
111110 let leds = LEDS . init ( Mutex :: new ( LEDs :: new ( led_pins) ) ) ;
112- spawner. spawn ( led_test_task ( leds) ) . ok ( ) ;
111+
112+ let meeting_sign_state = MEETING_SIGN_STATE . init ( MeetingSignStatePubSubChannel :: new ( ) ) ;
113+
114+ // We only care about the latest state, so we use immediate publishers
115+ let state_publisher_1 = meeting_sign_state. immediate_publisher ( ) ;
116+ let state_publisher_2 = meeting_sign_state. immediate_publisher ( ) ;
117+
118+ // Initialize the state to NoUart
119+ state_publisher_1. publish_immediate ( MeetingSignState :: NoUart ) ;
120+
121+ let state_subscriber_1 = meeting_sign_state
122+ . subscriber ( )
123+ . expect ( "Failed to create state subscriber 1" ) ;
124+ let state_subscriber_2 = meeting_sign_state
125+ . subscriber ( )
126+ . expect ( "Failed to create state subscriber 2" ) ;
127+ let mut state_subscriber_3 = meeting_sign_state
128+ . subscriber ( )
129+ . expect ( "Failed to create state subscriber 3" ) ;
113130
114131 let rx_pin = peripherals. GPIO1 ;
115132 let uart_config = Config :: default ( ) . with_rx (
@@ -120,27 +137,107 @@ async fn main(spawner: Spawner) {
120137 . with_rx ( rx_pin)
121138 . into_async ( ) ;
122139
123- spawner. spawn ( uart_reader ( uart) ) . ok ( ) ;
124-
125- Timer :: after ( Duration :: from_secs ( 5 ) ) . await ;
126- panic ! ( "Ahhhh" ) ;
127- }
140+ spawner
141+ . spawn ( uart_reader ( uart, state_publisher_1, state_subscriber_1) )
142+ . ok ( ) ;
143+ spawner
144+ . spawn ( uart_timeout_monitor ( state_publisher_2, state_subscriber_2) )
145+ . ok ( ) ;
146+
147+ // Initialize hardcoded timer if no UART within threshold
148+ match select (
149+ state_subscriber_3. next_message_pure ( ) ,
150+ Timer :: after ( UART_COMMUNICATION_TIMEOUT ) ,
151+ )
152+ . await
153+ {
154+ Either :: First ( state) => {
155+ match state {
156+ MeetingSignState :: NoUart => {
157+ info ! ( "State changed to NoUart." ) ;
158+ // Turn off all LEDs
159+ leds. lock ( ) . await . set_portion_high ( 1.0 , 2.0 ) ;
160+ }
161+ MeetingSignState :: Uart => {
162+ info ! ( "State changed to Uart." ) ;
163+ // Set LEDs to indicate UART communication
164+ leds. lock ( ) . await . set_portion_high ( 1.0 , 1.0 ) ;
165+ }
166+ }
167+ }
168+ Either :: Second ( _) => {
169+ info ! ( "No initial state change detected within {}s, initializing LEDs according to hardcoded timer..." ,
170+ UART_COMMUNICATION_TIMEOUT . as_secs( ) ) ;
171+ }
172+ }
128173
129- #[ embassy_executor:: task]
130- async fn led_test_task ( leds : & ' static LEDsMutex ) {
131- let mut counter = 0u16 ;
132174 loop {
175+ match select (
176+ state_subscriber_3. next_message_pure ( ) ,
177+ Timer :: after_secs ( 60 ) ,
178+ )
179+ . await
133180 {
134- let mut leds = leds. lock ( ) . await ;
135- leds. toggle_all ( ) ;
181+ Either :: First ( state) => {
182+ match state {
183+ MeetingSignState :: NoUart => {
184+ info ! ( "State changed to NoUart." ) ;
185+ // Turn off all LEDs
186+ leds. lock ( ) . await . set_portion_high ( 1.0 , 2.0 ) ;
187+ }
188+ MeetingSignState :: Uart => {
189+ info ! ( "State changed to Uart." ) ;
190+ // Set LEDs to indicate UART communication
191+ leds. lock ( ) . await . set_portion_high ( 1.0 , 1.0 ) ;
192+ }
193+ }
194+ }
195+ Either :: Second ( _) => {
196+ info ! ( "No state change detected within 60 seconds, updating LEDs according to internal timer..." ) ;
197+ }
198+ }
199+ }
200+ }
201+
202+ struct LEDs < ' a > {
203+ led_outs : [ Output < ' a > ; NUM_LEDS ] ,
204+ }
205+ impl < ' a > LEDs < ' a > {
206+ pub fn new ( pins : [ AnyPin < ' a > ; NUM_LEDS ] ) -> Self {
207+ // Convert each pin to an Output
208+ let led_outs = pins. map ( |pin| Output :: new ( pin, Level :: Low , OutputConfig :: default ( ) ) ) ;
209+
210+ Self { led_outs }
211+ }
212+
213+ /// Set the portion of LEDs to high based on the given numerator and denominator.
214+ pub fn set_portion_high ( & mut self , numerator : f32 , denominator : f32 ) {
215+ let num_on_leds = if numerator <= 0.0 || denominator <= 0.0 {
216+ warn ! (
217+ "Invalid portion values: numerator {}, denominator {}" ,
218+ numerator, denominator
219+ ) ;
220+ 0 // This will turn off all LEDs
221+ } else {
222+ ( NUM_LEDS as f32 * numerator / denominator) . round ( ) as usize
223+ } ;
224+
225+ for ( led_idx, led) in self . led_outs . iter_mut ( ) . enumerate ( ) {
226+ if led_idx < num_on_leds {
227+ led. set_high ( ) ;
228+ } else {
229+ led. set_low ( ) ;
230+ }
136231 }
137- counter = counter. wrapping_add ( 1 ) ;
138- Timer :: after ( Duration :: from_millis ( 500 ) ) . await ;
139232 }
140233}
141234
142235#[ embassy_executor:: task]
143- async fn uart_reader ( mut uart : Uart < ' static , Async > ) {
236+ async fn uart_reader (
237+ mut uart : Uart < ' static , Async > ,
238+ state_publisher : MeetingSignStatePublisher < ' static > ,
239+ mut state_subscriber : MeetingSignStateSubscriber < ' static > ,
240+ ) {
144241 debug ! ( "Starting UART reader task" ) ;
145242
146243 // Buffers sized appropriately for the MeetingInstruction payload
@@ -167,6 +264,18 @@ async fn uart_reader(mut uart: Uart<'static, Async>) {
167264 match postcard:: from_bytes :: < MeetingSignInstruction > ( decoded_data) {
168265 Ok ( instruction) => {
169266 debug ! ( "Received: {:?}" , instruction) ;
267+ match state_subscriber. try_next_message_pure ( ) {
268+ None | Some ( MeetingSignState :: NoUart ) => {
269+ // If we were not in UART state, switch to UART
270+ debug ! ( "Switching state to UART" ) ;
271+ state_publisher
272+ . publish_immediate ( MeetingSignState :: Uart ) ;
273+ }
274+ Some ( MeetingSignState :: Uart ) => {
275+ // Already in UART state, just log
276+ debug ! ( "Already in UART state, processing instruction" ) ;
277+ }
278+ }
170279 }
171280 Err ( e) => warn ! ( "Deserialization error: {:?}" , e) ,
172281 }
@@ -189,6 +298,51 @@ async fn uart_reader(mut uart: Uart<'static, Async>) {
189298 }
190299}
191300
301+ #[ embassy_executor:: task]
302+ async fn uart_timeout_monitor (
303+ state_publisher : MeetingSignStatePublisher < ' static > ,
304+ mut state_subscriber : MeetingSignStateSubscriber < ' static > ,
305+ ) {
306+ debug ! ( "Starting UART timeout monitor task" ) ;
307+
308+ loop {
309+ // Wait for UART state
310+ loop {
311+ match state_subscriber. next_message_pure ( ) . await {
312+ MeetingSignState :: Uart => break , // Start monitoring
313+ MeetingSignState :: NoUart => continue , // Keep waiting
314+ }
315+ }
316+
317+ // Now we're in UART state, start the timeout monitoring
318+ let mut timeout_timer = Timer :: after ( UART_COMMUNICATION_TIMEOUT ) ;
319+
320+ loop {
321+ match select ( state_subscriber. next_message_pure ( ) , & mut timeout_timer) . await {
322+ Either :: First ( state) => {
323+ match state {
324+ MeetingSignState :: Uart => {
325+ // New UART message received, reset the timer
326+ debug ! ( "UART activity detected, resetting timeout" ) ;
327+ timeout_timer = Timer :: after ( UART_COMMUNICATION_TIMEOUT ) ;
328+ }
329+ MeetingSignState :: NoUart => {
330+ // Someone else set it to NoUART, stop monitoring
331+ debug ! ( "State changed to NoUART, stopping timeout monitoring" ) ;
332+ break ;
333+ }
334+ }
335+ }
336+ Either :: Second ( _) => {
337+ // Timeout occurred
338+ debug ! ( "UART communication timed out, signaling NoUART" ) ;
339+ state_publisher. publish_immediate ( MeetingSignState :: NoUart ) ;
340+ break ; // Exit inner loop, will wait for next UART state
341+ }
342+ }
343+ }
344+ }
345+ }
192346#[ panic_handler]
193347fn panic_handler ( info : & core:: panic:: PanicInfo ) -> ! {
194348 // Disable interrupts to prevent further issues
@@ -198,8 +352,6 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! {
198352 if let Some ( ref mut pin) = PANIC_PIN {
199353 pin. set_low ( ) ;
200354 } else {
201- // Fallback: directly access GPIO registers if pin wasn't initialized
202- // This is a last resort and uses unsafe register access
203355 emergency_gpio3_low ( ) ;
204356 }
205357
0 commit comments