44#include <notification/notification.h>
55#include <notification/notification_messages.h>
66
7+ #include <datetime/datetime.h>
8+ #include <locale/locale.h>
9+
710#include "dcf77.h"
811
912#define SCREEN_SIZE_X 128
1013#define SCREEN_SIZE_Y 64
1114#define DCF77_FREQ 77500
1215#define DCF77_OFFSET 60
1316#define SYNC_DELAY 50
14- #define UPDATES 8
15-
16- #define SECONDS_PER_MINUTE 60
17- #define SECONDS_PER_HOUR (SECONDS_PER_MINUTE * 60)
18- #define SECONDS_PER_DAY (SECONDS_PER_HOUR * 24)
19- #define MONTHS_COUNT 12
20- #define EPOCH_START_YEAR 1970
2117
2218char * WEEKDAYS [] = {"Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" , "Sun" };
2319
2420typedef struct {
2521 DateTime dt ;
26- DateTime dcf_dt ;
2722 bool is_dst ;
23+ FuriString * str ;
24+ LocaleTimeFormat tim_fmt ;
25+ LocaleDateFormat dat_fmt ;
2826} AppData ;
2927
3028static void app_draw_callback (Canvas * canvas , void * context ) {
31- AppData * app_data = (AppData * )context ;
32-
33- char buffer [64 ];
29+ AppData * app = (AppData * )context ;
30+ furi_assert (app -> str );
31+
32+ uint8_t hour = app -> dt .hour ;
33+ bool fmt_12h = false;
34+ if (app -> tim_fmt == LocaleTimeFormat12h ) {
35+ hour = hour == 0 ? 12 : hour % 12 ;
36+ fmt_12h = true;
37+ }
3438
35- snprintf (
36- buffer ,
37- sizeof (buffer ),
38- "%02u:%02u:%02u" ,
39- app_data -> dt .hour ,
40- app_data -> dt .minute ,
41- app_data -> dt .second );
39+ furi_string_printf (app -> str , "%2u:%02u:%02u" , hour , app -> dt .minute , app -> dt .second );
40+ const char * tim_cstr = furi_string_get_cstr (app -> str );
4241
4342 canvas_set_font (canvas , FontBigNumbers );
4443 canvas_draw_str_aligned (
45- canvas , SCREEN_SIZE_X / 2 , SCREEN_SIZE_Y / 2 , AlignCenter , AlignCenter , buffer );
46-
47- const char * dow_str = WEEKDAYS [(app_data -> dt .weekday - 1 ) % 7 ];
48- const char * dst_str = app_data -> is_dst ? "CEST" : "CET" ;
49- snprintf (
50- buffer ,
51- sizeof (buffer ),
52- "%s %02u-%02u-%04u %s" ,
53- dow_str ,
54- app_data -> dt .day ,
55- app_data -> dt .month ,
56- app_data -> dt .year ,
57- dst_str );
44+ canvas , SCREEN_SIZE_X / 2 , SCREEN_SIZE_Y / 2 , AlignCenter , AlignCenter , tim_cstr );
45+
46+ if (fmt_12h ) {
47+ canvas_set_font (canvas , FontSecondary );
48+ canvas_draw_str_aligned (
49+ canvas ,
50+ 0 ,
51+ (SCREEN_SIZE_Y / 2 ) - 7 ,
52+ AlignLeft ,
53+ AlignTop ,
54+ (app -> dt .hour >= 12 ? "PM" : "AM" ));
55+ }
56+
57+ FuriString * dat = furi_string_alloc ();
58+ locale_format_date (dat , & app -> dt , app -> dat_fmt , "-" );
59+ const char * dow_str = WEEKDAYS [(app -> dt .weekday - 1 ) % 7 ];
60+ const char * dst_str = app -> is_dst ? "CEST" : "CET" ;
61+ furi_string_printf (app -> str , "%s %s %s" , dow_str , furi_string_get_cstr (dat ), dst_str );
62+ furi_string_free (dat );
5863
5964 canvas_set_font (canvas , FontSecondary );
60- canvas_draw_str_aligned (canvas , SCREEN_SIZE_X / 2 , 0 , AlignCenter , AlignTop , buffer );
65+ canvas_draw_str_aligned (
66+ canvas , SCREEN_SIZE_X / 2 , 0 , AlignCenter , AlignTop , furi_string_get_cstr (app -> str ));
6167
62- if (app_data -> dt .second < 59 ) {
63- char * data = get_dcf77_data (app_data -> dt .second );
68+ if (app -> dt .second < 59 ) {
69+ char * data = get_dcf77_data (app -> dt .second );
6470 canvas_draw_str_aligned (
6571 canvas , SCREEN_SIZE_X , SCREEN_SIZE_Y , AlignRight , AlignBottom , data );
6672 }
@@ -72,48 +78,29 @@ static void app_input_callback(InputEvent* input_event, void* ctx) {
7278 furi_message_queue_put (event_queue , input_event , FuriWaitForever );
7379}
7480
75- void time_add (DateTime * from , DateTime * to , int add ) {
76- uint32_t timestamp = datetime_datetime_to_timestamp (from ) + add ;
77-
78- uint32_t days = timestamp / SECONDS_PER_DAY ;
79- uint32_t seconds_in_day = timestamp % SECONDS_PER_DAY ;
80-
81- to -> year = EPOCH_START_YEAR ;
82-
83- while (days >= datetime_get_days_per_year (to -> year )) {
84- days -= datetime_get_days_per_year (to -> year );
85- (to -> year )++ ;
86- }
87-
88- to -> month = 1 ;
89- while (days >= datetime_get_days_per_month (datetime_is_leap_year (to -> year ), to -> month )) {
90- days -= datetime_get_days_per_month (datetime_is_leap_year (to -> year ), to -> month );
91- (to -> month )++ ;
92- }
93-
94- to -> weekday = ((days + 4 ) % 7 ) + 1 ;
95-
96- to -> day = days + 1 ;
97- to -> hour = seconds_in_day / SECONDS_PER_HOUR ;
98- to -> minute = (seconds_in_day % SECONDS_PER_HOUR ) / SECONDS_PER_MINUTE ;
99- to -> second = seconds_in_day % SECONDS_PER_MINUTE ;
81+ void set_time (AppData * app , int offset ) {
82+ DateTime dcf_dt ;
83+ uint32_t timestamp = datetime_datetime_to_timestamp (& app -> dt ) + offset ;
84+ datetime_timestamp_to_datetime (timestamp , & dcf_dt );
85+ set_dcf77_time (& dcf_dt , app -> is_dst );
10086}
10187
10288int dcf77_clock_sync_app_main (void * p ) {
10389 UNUSED (p );
10490
105- AppData app_data ;
106- InputEvent event ;
91+ AppData * app = malloc (sizeof (AppData ));
92+ furi_hal_rtc_get_datetime (& app -> dt );
93+ app -> is_dst = false;
94+ app -> str = furi_string_alloc ();
95+ app -> tim_fmt = locale_get_time_format ();
96+ app -> dat_fmt = locale_get_date_format ();
10797
108- app_data .is_dst = false;
109- furi_hal_rtc_get_datetime (& app_data .dt );
110- time_add (& app_data .dt , & app_data .dcf_dt , DCF77_OFFSET );
111- set_dcf77_time (& app_data .dcf_dt , app_data .is_dst );
98+ set_time (app , DCF77_OFFSET );
11299
113100 ViewPort * view_port = view_port_alloc ();
114101 FuriMessageQueue * event_queue = furi_message_queue_alloc (8 , sizeof (InputEvent ));
115102
116- view_port_draw_callback_set (view_port , app_draw_callback , & app_data );
103+ view_port_draw_callback_set (view_port , app_draw_callback , app );
117104 view_port_input_callback_set (view_port , app_input_callback , event_queue );
118105
119106 Gui * gui = furi_record_open (RECORD_GUI );
@@ -122,52 +109,48 @@ int dcf77_clock_sync_app_main(void* p) {
122109 NotificationApp * notification = furi_record_open (RECORD_NOTIFICATION );
123110 notification_message_block (notification , & sequence_display_backlight_enforce_on );
124111
112+ InputEvent event ;
125113 bool running = false;
126114 bool exit = false;
127- int sec = app_data . dt .second ;
115+ int sec = app -> dt .second ;
128116 while (!exit ) {
129117 int silence_ms = 0 ;
130118 // wait next second
131- while (app_data . dt .second == sec ) furi_hal_rtc_get_datetime (& app_data . dt );
119+ while (app -> dt .second == sec ) furi_hal_rtc_get_datetime (& app -> dt );
132120
133- if (app_data .dt .second < 59 ) {
134- furi_hal_light_set (LightRed | LightGreen | LightBlue , 0 );
121+ if (app -> dt .second < 59 ) {
135122 if (running ) {
123+ furi_hal_light_set (LightRed | LightGreen | LightBlue , 0 );
136124 furi_hal_rfid_tim_read_stop ();
137125 furi_hal_pwm_stop (FuriHalPwmOutputIdLptim2PA4 );
138126 furi_hal_gpio_init (
139127 & gpio_ext_pa4 , GpioModeOutputPushPull , GpioPullNo , GpioSpeedVeryHigh );
140128 }
141- silence_ms = get_dcf77_bit (app_data . dt .second ) ? 200 : 100 ;
129+ silence_ms = get_dcf77_bit (app -> dt .second ) ? 200 : 100 ;
142130 furi_delay_ms (silence_ms );
143131 furi_hal_rfid_tim_read_start (DCF77_FREQ , 0.5 );
144132 furi_hal_pwm_start (FuriHalPwmOutputIdLptim2PA4 , DCF77_FREQ , 50 );
145- running = true;
146133 furi_hal_light_set (LightBlue , 0xFF );
147- } else {
148- time_add (& app_data .dt , & app_data .dcf_dt , DCF77_OFFSET + 1 );
149- set_dcf77_time (& app_data .dcf_dt , app_data .is_dst );
150- }
151-
152- sec = app_data .dt .second ;
153- int wait_ms = (1000 - silence_ms - SYNC_DELAY ) / UPDATES ;
154- for (int i = 0 ; i < UPDATES ; i ++ ) {
155- if (furi_message_queue_get (event_queue , & event , wait_ms ) == FuriStatusOk ) {
156- if ((event .type == InputTypePress ) || (event .type == InputTypeRepeat )) {
157- switch (event .key ) {
158- case InputKeyOk :
159- app_data .is_dst = !app_data .is_dst ;
160- break ;
161- case InputKeyBack :
162- exit = true;
163- break ;
164- default :
165- break ;
166- }
134+ running = true;
135+ } else
136+ set_time (app , DCF77_OFFSET + 1 );
137+
138+ sec = app -> dt .second ;
139+ int wait_ms = 1000 - silence_ms - SYNC_DELAY ;
140+ uint32_t tick_start = furi_get_tick ();
141+ while (wait_ms > 0 ) {
142+ FuriStatus status = furi_message_queue_get (event_queue , & event , wait_ms );
143+ if ((status == FuriStatusOk ) && (event .type == InputTypePress )) {
144+ if (event .key == InputKeyOk )
145+ app -> is_dst = !app -> is_dst ;
146+ else if (event .key == InputKeyBack ) {
147+ exit = true;
148+ break ;
167149 }
168150 }
169151 view_port_update (view_port );
170- if (exit ) break ;
152+ if (status == FuriStatusErrorTimeout ) break ;
153+ wait_ms -= furi_get_tick () - tick_start ;
171154 }
172155 }
173156
@@ -186,5 +169,8 @@ int dcf77_clock_sync_app_main(void* p) {
186169 furi_message_queue_free (event_queue );
187170 view_port_free (view_port );
188171
172+ furi_string_free (app -> str );
173+ free (app );
174+
189175 return 0 ;
190176}
0 commit comments