1
+ #include <furi_hal.h>
2
+ #include <furi_hal_pwm.h>
3
+ #include <gui/gui.h>
4
+ #include <notification/notification.h>
5
+ #include <notification/notification_messages.h>
6
+
7
+ #include "dcf77.h"
8
+
9
+ #define SCREEN_SIZE_X 128
10
+ #define SCREEN_SIZE_Y 64
11
+ #define DCF77_FREQ 77500
12
+ #define DCF77_OFFSET 60
13
+ #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
21
+
22
+ char * WEEKDAYS [] = {"Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" , "Sun" };
23
+
24
+ typedef struct {
25
+ DateTime dt ;
26
+ DateTime dcf_dt ;
27
+ bool is_dst ;
28
+ } AppData ;
29
+
30
+ static void app_draw_callback (Canvas * canvas , void * context ) {
31
+ AppData * app_data = (AppData * )context ;
32
+
33
+ char buffer [64 ];
34
+
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 );
42
+
43
+ canvas_set_font (canvas , FontBigNumbers );
44
+ 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 );
58
+
59
+ canvas_set_font (canvas , FontSecondary );
60
+ canvas_draw_str_aligned (canvas , SCREEN_SIZE_X / 2 , 0 , AlignCenter , AlignTop , buffer );
61
+
62
+ if (app_data -> dt .second < 59 ) {
63
+ char * data = get_dcf77_data (app_data -> dt .second );
64
+ canvas_draw_str_aligned (
65
+ canvas , SCREEN_SIZE_X , SCREEN_SIZE_Y , AlignRight , AlignBottom , data );
66
+ }
67
+ }
68
+
69
+ static void app_input_callback (InputEvent * input_event , void * ctx ) {
70
+ furi_assert (ctx );
71
+ FuriMessageQueue * event_queue = ctx ;
72
+ furi_message_queue_put (event_queue , input_event , FuriWaitForever );
73
+ }
74
+
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 ;
100
+ }
101
+
102
+ int dcf77_clock_sync_app_main (void * p ) {
103
+ UNUSED (p );
104
+
105
+ AppData app_data ;
106
+ InputEvent event ;
107
+
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 );
112
+
113
+ ViewPort * view_port = view_port_alloc ();
114
+ FuriMessageQueue * event_queue = furi_messa ge_queue_alloc (8 , sizeof (InputEvent ));
115
+
116
+ view_port_draw_callback_set (view_port , app_draw_callback , & app_data );
117
+ view_port_input_callback_set (view_port , app_input_callback , event_queue );
118
+
119
+ Gui * gui = furi_record_open (RECORD_GUI );
120
+ gui_add_view_port (gui , view_port , GuiLayerFullscreen );
121
+
122
+ NotificationApp * notification = furi_record_open (RECORD_NOTIFICATION );
123
+ notification_message_block (notification , & sequence_display_backlight_enforce_on );
124
+
125
+ bool running = false;
126
+ bool exit = false;
127
+ int sec = app_data .dt .second ;
128
+ while (!exit ) {
129
+ int silence_ms = 0 ;
130
+ // wait next second
131
+ while (app_data .dt .second == sec ) furi_hal_rtc_get_datetime (& app_data .dt );
132
+
133
+ if (app_data .dt .second < 59 ) {
134
+ furi_hal_light_set (LightRed | LightGreen | LightBlue , 0 );
135
+ if (running ) {
136
+ furi_hal_rfid_tim_read_stop ();
137
+ furi_hal_pwm_stop (FuriHalPwmOutputIdLptim2PA4 );
138
+ furi_hal_gpio_init (
139
+ & gpio_ext_pa4 , GpioModeOutputPushPull , GpioPullNo , GpioSpeedVeryHigh );
140
+ }
141
+ silence_ms = get_dcf77_bit (app_data .dt .second ) ? 200 : 100 ;
142
+ furi_delay_ms (silence_ms );
143
+ furi_hal_rfid_tim_read_start (DCF77_FREQ , 0.5 );
144
+ furi_hal_pwm_start (FuriHalPwmOutputIdLptim2PA4 , DCF77_FREQ , 50 );
145
+ running = true;
146
+ 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
+ }
167
+ }
168
+ }
169
+ view_port_update (view_port );
170
+ if (exit ) break ;
171
+ }
172
+ }
173
+
174
+ if (running ) {
175
+ furi_hal_rfid_tim_read_stop ();
176
+ furi_hal_pwm_stop (FuriHalPwmOutputIdLptim2PA4 );
177
+ furi_hal_light_set (LightRed | LightGreen | LightBlue , 0 );
178
+ }
179
+
180
+ notification_message_block (notification , & sequence_display_backlight_enforce_auto );
181
+
182
+ view_port_enabled_set (view_port , false);
183
+ gui_remove_view_port (gui , view_port );
184
+ furi_record_close (RECORD_NOTIFICATION );
185
+ furi_record_close (RECORD_GUI );
186
+ furi_message_queue_free (event_queue );
187
+ view_port_free (view_port );
188
+
189
+ return 0 ;
190
+ }
0 commit comments