66
77#define TAG "Clock"
88
9- bool timerStarted = false;
10- int timerSecs = 0 ;
11-
129typedef enum {
1310 EventTypeTick ,
1411 EventTypeKey ,
@@ -20,7 +17,11 @@ typedef struct {
2017} PluginEvent ;
2118
2219typedef struct {
23- FuriHalRtcDateTime datetime ;
20+ FuriMutex * mutex ;
21+ FuriMessageQueue * event_queue ;
22+ uint32_t timer_start_timestamp ;
23+ uint32_t timer_stopped_seconds ;
24+ bool timer_running ;
2425 bool militaryTime ; // 24 hour
2526} ClockState ;
2627
@@ -31,61 +32,60 @@ static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* even
3132}
3233
3334static void clock_render_callback (Canvas * const canvas , void * ctx ) {
34- canvas_clear ( canvas ) ;
35- canvas_set_color ( canvas , ColorBlack );
36- ClockState * state = ( ClockState * ) acquire_mutex (( ValueMutex * ) ctx , 25 );
37- if ( state == NULL ) {
38- FURI_LOG_E ( TAG , "state was NULL\r\n" );
35+ ClockState * state = ctx ;
36+ if ( furi_mutex_acquire ( state -> mutex , 200 ) != FuriStatusOk ) {
37+ // Can't obtain mutex, requeue render
38+ PluginEvent event = {. type = EventTypeTick };
39+ furi_message_queue_put ( state -> event_queue , & event , 0 );
3940 return ;
4041 }
42+
43+ FuriHalRtcDateTime curr_dt ;
44+ furi_hal_rtc_get_datetime (& curr_dt );
45+ uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp (& curr_dt );
46+
4147 char strings [3 ][20 ];
42- int curMin = (timerSecs / 60 );
43- int curSec = timerSecs - (curMin * 60 );
44- snprintf (
45- strings [0 ],
46- 20 ,
47- "%.4d-%.2d-%.2d" ,
48- state -> datetime .year ,
49- state -> datetime .month ,
50- state -> datetime .day );
51-
52- uint8_t hour = state -> datetime .hour ;
48+ snprintf (strings [0 ], 20 , "%.4d-%.2d-%.2d" , curr_dt .year , curr_dt .month , curr_dt .day );
49+
50+ uint8_t hour = curr_dt .hour ;
5351 // bool pm = false;
54- if (!state -> militaryTime && hour > 12 ) {
52+ if (!state -> militaryTime && hour > 12 ) {
5553 hour -= 12 ;
5654 // pm = true;
5755 }
58- snprintf (
59- strings [1 ],
60- 20 ,
61- "%.2d:%.2d:%.2d" ,
62- hour ,
63- state -> datetime .minute ,
64- state -> datetime .second );
65- snprintf (strings [2 ], 20 , "%.2d:%.2d" , curMin , curSec );
66- release_mutex ((ValueMutex * )ctx , state );
56+ snprintf (strings [1 ], 20 , "%.2d:%.2d:%.2d" , hour , curr_dt .minute , curr_dt .second );
57+
58+ bool timer_running = state -> timer_running ;
59+ uint32_t timer_start_timestamp = state -> timer_start_timestamp ;
60+ uint32_t timer_stopped_seconds = state -> timer_stopped_seconds ;
61+
62+ furi_mutex_release (state -> mutex );
63+
6764 canvas_set_font (canvas , FontBigNumbers );
68- if (timerStarted || timerSecs != 0 ) {
65+ if (timer_start_timestamp != 0 ) {
66+ int32_t elapsed_secs = timer_running ? (curr_ts - timer_start_timestamp ) :
67+ timer_stopped_seconds ;
68+ snprintf (strings [2 ], 20 , "%.2ld:%.2ld" , elapsed_secs / 60 , elapsed_secs % 60 );
6969 canvas_draw_str_aligned (canvas , 64 , 8 , AlignCenter , AlignCenter , strings [1 ]); // DRAW TIME
7070 canvas_draw_str_aligned (canvas , 64 , 32 , AlignCenter , AlignTop , strings [2 ]); // DRAW TIMER
7171 canvas_set_font (canvas , FontSecondary );
7272 canvas_draw_str_aligned (canvas , 64 , 20 , AlignCenter , AlignTop , strings [0 ]); // DRAW DATE
73+ elements_button_left (canvas , "Reset" );
7374 } else {
7475 canvas_draw_str_aligned (canvas , 64 , 26 , AlignCenter , AlignCenter , strings [1 ]); // DRAW TIME
7576 canvas_set_font (canvas , FontSecondary );
7677 canvas_draw_str_aligned (canvas , 64 , 38 , AlignCenter , AlignTop , strings [0 ]); // DRAW DATE
7778 elements_button_left (canvas , state -> militaryTime ? "12h" : "24h" );
7879 }
79- if (timerStarted ) {
80+ if (timer_running ) {
8081 elements_button_center (canvas , "Stop" );
8182 } else {
8283 elements_button_center (canvas , "Start" );
8384 }
84- if (timerSecs != 0 ) elements_button_left (canvas , "Reset" );
8585}
8686
8787static void clock_state_init (ClockState * const state ) {
88- furi_hal_rtc_get_datetime ( & state -> datetime );
88+ memset ( state , 0 , sizeof ( ClockState ) );
8989 state -> militaryTime = true;
9090}
9191
@@ -94,82 +94,113 @@ static void clock_tick(void* ctx) {
9494 furi_assert (ctx );
9595 FuriMessageQueue * event_queue = ctx ;
9696 PluginEvent event = {.type = EventTypeTick };
97- if (timerStarted ) {
98- timerSecs = timerSecs + 1 ;
99- }
100- // It's OK to loose this event if system overloaded
97+ // It's OK to lose this event if system overloaded
10198 furi_message_queue_put (event_queue , & event , 0 );
10299}
103100
104101int32_t clock_app (void * p ) {
105102 UNUSED (p );
106- timerStarted = false;
107- timerSecs = 0 ;
108- FuriMessageQueue * event_queue = furi_message_queue_alloc (8 , sizeof (PluginEvent ));
109103 ClockState * plugin_state = malloc (sizeof (ClockState ));
110104 clock_state_init (plugin_state );
111- ValueMutex state_mutex ;
112- if (!init_mutex (& state_mutex , plugin_state , sizeof (ClockState ))) {
113- FURI_LOG_E (TAG , "cannot create mutex\r\n" );
114- furi_message_queue_free (event_queue );
105+ plugin_state -> event_queue = furi_message_queue_alloc (8 , sizeof (PluginEvent ));
106+ if (plugin_state -> event_queue == NULL ) {
107+ FURI_LOG_E (TAG , "cannot create event queue\n" );
108+ free (plugin_state );
109+ return 255 ;
110+ }
111+
112+ plugin_state -> mutex = furi_mutex_alloc (FuriMutexTypeNormal );
113+ if (plugin_state -> mutex == NULL ) {
114+ FURI_LOG_E (TAG , "cannot create mutex\n" );
115+ furi_message_queue_free (plugin_state -> event_queue );
116+ free (plugin_state );
117+ return 255 ;
118+ }
119+
120+ FuriTimer * timer =
121+ furi_timer_alloc (clock_tick , FuriTimerTypePeriodic , plugin_state -> event_queue );
122+ if (timer == NULL ) {
123+ FURI_LOG_E (TAG , "cannot create timer\n" );
124+ furi_mutex_free (plugin_state -> mutex );
125+ furi_message_queue_free (plugin_state -> event_queue );
115126 free (plugin_state );
116127 return 255 ;
117128 }
129+
118130 // Set system callbacks
119131 ViewPort * view_port = view_port_alloc ();
120- view_port_draw_callback_set (view_port , clock_render_callback , & state_mutex );
121- view_port_input_callback_set (view_port , clock_input_callback , event_queue );
122- FuriTimer * timer = furi_timer_alloc (clock_tick , FuriTimerTypePeriodic , event_queue );
123- furi_timer_start (timer , furi_kernel_get_tick_frequency ());
132+ view_port_draw_callback_set (view_port , clock_render_callback , plugin_state );
133+ view_port_input_callback_set (view_port , clock_input_callback , plugin_state -> event_queue );
134+
124135 // Open GUI and register view_port
125136 Gui * gui = furi_record_open (RECORD_GUI );
126137 gui_add_view_port (gui , view_port , GuiLayerFullscreen );
138+ furi_timer_start (timer , furi_kernel_get_tick_frequency ());
139+
127140 // Main loop
128141 PluginEvent event ;
129142 for (bool processing = true; processing ;) {
130- FuriStatus event_status = furi_message_queue_get (event_queue , & event , 100 );
131- ClockState * plugin_state = (ClockState * )acquire_mutex_block (& state_mutex );
143+ FuriStatus event_status = furi_message_queue_get (plugin_state -> event_queue , & event , 100 );
132144 if (event_status == FuriStatusOk ) {
145+ if (furi_mutex_acquire (plugin_state -> mutex , FuriWaitForever ) != FuriStatusOk ) continue ;
133146 // press events
134147 if (event .type == EventTypeKey ) {
135148 if (event .input .type == InputTypeShort || event .input .type == InputTypeRepeat ) {
136- if (event .input .key == InputKeyOk ) {
137- // START/STOP TIMER
138- if (timerStarted ) {
139- timerStarted = false;
149+ if (event .input .key == InputKeyOk ) {
150+ // START/STOP TIMER
151+ FuriHalRtcDateTime curr_dt ;
152+ furi_hal_rtc_get_datetime (& curr_dt );
153+ uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp (& curr_dt );
154+
155+ if (plugin_state -> timer_running ) {
156+ // Update stopped seconds
157+ plugin_state -> timer_stopped_seconds =
158+ curr_ts - plugin_state -> timer_start_timestamp ;
140159 } else {
141- timerStarted = true;
160+ if (plugin_state -> timer_start_timestamp == 0 ) {
161+ // Set starting timestamp if this is first time
162+ plugin_state -> timer_start_timestamp = curr_ts ;
163+ } else {
164+ // Timer was already running, need to slightly readjust so we don't
165+ // count the intervening time
166+ plugin_state -> timer_start_timestamp =
167+ curr_ts - plugin_state -> timer_stopped_seconds ;
168+ }
142169 }
143- } else if (event .input .key == InputKeyLeft ) {
144- if (timerStarted || timerSecs != 0 ) {
145- timerSecs = 0 ;
170+ plugin_state -> timer_running = !plugin_state -> timer_running ;
171+ } else if (event .input .key == InputKeyLeft ) {
172+ if (plugin_state -> timer_start_timestamp != 0 ) {
173+ // Reset seconds
174+ plugin_state -> timer_running = false;
175+ plugin_state -> timer_start_timestamp = 0 ;
176+ plugin_state -> timer_stopped_seconds = 0 ;
146177 } else {
178+ // Toggle 12/24 hours
147179 plugin_state -> militaryTime = !plugin_state -> militaryTime ;
148180 }
149- } else if (event .input .key == InputKeyBack ) {
181+ } else if (event .input .key == InputKeyBack ) {
150182 // Exit the plugin
151183 processing = false;
152- } else {
153- // KEY NOT PROGRAMMED
154- }
184+ }
155185 }
156186 } else if (event .type == EventTypeTick ) {
157- furi_hal_rtc_get_datetime ( & plugin_state -> datetime );
187+ // Do nothing, just need to update viewport
158188 }
189+ view_port_update (view_port );
190+ furi_mutex_release (plugin_state -> mutex );
159191 } else {
160- FURI_LOG_D (TAG , "osMessageQueue: event timeout" );
161192 // event timeout
193+ // FURI_LOG_D(TAG, "osMessageQueue: event timeout");
162194 }
163- view_port_update (view_port );
164- release_mutex (& state_mutex , plugin_state );
165195 }
196+ // Cleanup
166197 furi_timer_free (timer );
167198 view_port_enabled_set (view_port , false);
168199 gui_remove_view_port (gui , view_port );
169200 furi_record_close (RECORD_GUI );
170201 view_port_free (view_port );
171- delete_mutex (& state_mutex );
202+ furi_message_queue_free (plugin_state -> event_queue );
203+ furi_mutex_free (plugin_state -> mutex );
172204 free (plugin_state );
173- furi_message_queue_free (event_queue );
174205 return 0 ;
175206}
0 commit comments