Skip to content

Commit d209ec9

Browse files
committed
Merge branch 'pr/256' into Clock
2 parents 3c31412 + 009df8f commit d209ec9

File tree

1 file changed

+100
-69
lines changed

1 file changed

+100
-69
lines changed

applications/clock_app/clock_app.c

+100-69
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66

77
#define TAG "Clock"
88

9-
bool timerStarted = false;
10-
int timerSecs = 0;
11-
129
typedef enum {
1310
EventTypeTick,
1411
EventTypeKey,
@@ -20,7 +17,11 @@ typedef struct {
2017
} PluginEvent;
2118

2219
typedef 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

3334
static 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

8787
static 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

104101
int32_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

Comments
 (0)