Skip to content

Commit

Permalink
update dcf77 sync
Browse files Browse the repository at this point in the history
  • Loading branch information
xMasterX committed Mar 20, 2024
1 parent 26fcf9d commit ef52585
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 101 deletions.
10 changes: 5 additions & 5 deletions non_catalog_apps/dcf77_clock_sync/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ App(
order=10,
fap_icon="icons/app_10x10.png",
fap_category="Tools",
fap_author="@mdaskalov",
fap_weburl="https://github.com/mdaskalov/dcf77-clock-sync.git",
fap_version="1.1",
fap_description="Emulate DCF77 time signal on the RFID antena and the A4 GPIO pin",
)
fap_author="mdaskalov",
fap_weburl="https://github.com/mdaskalov/dcf77-clock-sync",
fap_version="1.3",
fap_description="Emulate DCF77 time signal on the RFID antenna and the A4 GPIO pin",
)
2 changes: 1 addition & 1 deletion non_catalog_apps/dcf77_clock_sync/dcf77.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include <furi_hal.h>
#include "dcf77.h"

#define DST_BIT 17
#define MIN_BIT 21
Expand Down
5 changes: 3 additions & 2 deletions non_catalog_apps/dcf77_clock_sync/dcf77.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#pragma once

#include <furi_hal.h>
#include <datetime/datetime.h>
#include <furi.h>

void set_dcf77_time(DateTime* dt, bool is_dst);
int get_dcf77_bit(int sec);
bool get_dcf77_bit(int sec);
char* get_dcf77_data(int sec);
172 changes: 79 additions & 93 deletions non_catalog_apps/dcf77_clock_sync/dcf77_clock_sync.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,63 +4,69 @@
#include <notification/notification.h>
#include <notification/notification_messages.h>

#include <datetime/datetime.h>
#include <locale/locale.h>

#include "dcf77.h"

#define SCREEN_SIZE_X 128
#define SCREEN_SIZE_Y 64
#define DCF77_FREQ 77500
#define DCF77_OFFSET 60
#define SYNC_DELAY 50
#define UPDATES 8

#define SECONDS_PER_MINUTE 60
#define SECONDS_PER_HOUR (SECONDS_PER_MINUTE * 60)
#define SECONDS_PER_DAY (SECONDS_PER_HOUR * 24)
#define MONTHS_COUNT 12
#define EPOCH_START_YEAR 1970

char* WEEKDAYS[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

typedef struct {
DateTime dt;
DateTime dcf_dt;
bool is_dst;
FuriString* str;
LocaleTimeFormat tim_fmt;
LocaleDateFormat dat_fmt;
} AppData;

static void app_draw_callback(Canvas* canvas, void* context) {
AppData* app_data = (AppData*)context;

char buffer[64];
AppData* app = (AppData*)context;
furi_assert(app->str);

uint8_t hour = app->dt.hour;
bool fmt_12h = false;
if(app->tim_fmt == LocaleTimeFormat12h) {
hour = hour == 0 ? 12 : hour % 12;
fmt_12h = true;
}

snprintf(
buffer,
sizeof(buffer),
"%02u:%02u:%02u",
app_data->dt.hour,
app_data->dt.minute,
app_data->dt.second);
furi_string_printf(app->str, "%2u:%02u:%02u", hour, app->dt.minute, app->dt.second);
const char* tim_cstr = furi_string_get_cstr(app->str);

canvas_set_font(canvas, FontBigNumbers);
canvas_draw_str_aligned(
canvas, SCREEN_SIZE_X / 2, SCREEN_SIZE_Y / 2, AlignCenter, AlignCenter, buffer);

const char* dow_str = WEEKDAYS[(app_data->dt.weekday - 1) % 7];
const char* dst_str = app_data->is_dst ? "CEST" : "CET";
snprintf(
buffer,
sizeof(buffer),
"%s %02u-%02u-%04u %s",
dow_str,
app_data->dt.day,
app_data->dt.month,
app_data->dt.year,
dst_str);
canvas, SCREEN_SIZE_X / 2, SCREEN_SIZE_Y / 2, AlignCenter, AlignCenter, tim_cstr);

if(fmt_12h) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas,
0,
(SCREEN_SIZE_Y / 2) - 7,
AlignLeft,
AlignTop,
(app->dt.hour >= 12 ? "PM" : "AM"));
}

FuriString* dat = furi_string_alloc();
locale_format_date(dat, &app->dt, app->dat_fmt, "-");
const char* dow_str = WEEKDAYS[(app->dt.weekday - 1) % 7];
const char* dst_str = app->is_dst ? "CEST" : "CET";
furi_string_printf(app->str, "%s %s %s", dow_str, furi_string_get_cstr(dat), dst_str);
furi_string_free(dat);

canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, SCREEN_SIZE_X / 2, 0, AlignCenter, AlignTop, buffer);
canvas_draw_str_aligned(
canvas, SCREEN_SIZE_X / 2, 0, AlignCenter, AlignTop, furi_string_get_cstr(app->str));

if(app_data->dt.second < 59) {
char* data = get_dcf77_data(app_data->dt.second);
if(app->dt.second < 59) {
char* data = get_dcf77_data(app->dt.second);
canvas_draw_str_aligned(
canvas, SCREEN_SIZE_X, SCREEN_SIZE_Y, AlignRight, AlignBottom, data);
}
Expand All @@ -72,48 +78,29 @@ static void app_input_callback(InputEvent* input_event, void* ctx) {
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
}

void time_add(DateTime* from, DateTime* to, int add) {
uint32_t timestamp = datetime_datetime_to_timestamp(from) + add;

uint32_t days = timestamp / SECONDS_PER_DAY;
uint32_t seconds_in_day = timestamp % SECONDS_PER_DAY;

to->year = EPOCH_START_YEAR;

while(days >= datetime_get_days_per_year(to->year)) {
days -= datetime_get_days_per_year(to->year);
(to->year)++;
}

to->month = 1;
while(days >= datetime_get_days_per_month(datetime_is_leap_year(to->year), to->month)) {
days -= datetime_get_days_per_month(datetime_is_leap_year(to->year), to->month);
(to->month)++;
}

to->weekday = ((days + 4) % 7) + 1;

to->day = days + 1;
to->hour = seconds_in_day / SECONDS_PER_HOUR;
to->minute = (seconds_in_day % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE;
to->second = seconds_in_day % SECONDS_PER_MINUTE;
void set_time(AppData* app, int offset) {
DateTime dcf_dt;
uint32_t timestamp = datetime_datetime_to_timestamp(&app->dt) + offset;
datetime_timestamp_to_datetime(timestamp, &dcf_dt);
set_dcf77_time(&dcf_dt, app->is_dst);
}

int dcf77_clock_sync_app_main(void* p) {
UNUSED(p);

AppData app_data;
InputEvent event;
AppData* app = malloc(sizeof(AppData));
furi_hal_rtc_get_datetime(&app->dt);
app->is_dst = false;
app->str = furi_string_alloc();
app->tim_fmt = locale_get_time_format();
app->dat_fmt = locale_get_date_format();

app_data.is_dst = false;
furi_hal_rtc_get_datetime(&app_data.dt);
time_add(&app_data.dt, &app_data.dcf_dt, DCF77_OFFSET);
set_dcf77_time(&app_data.dcf_dt, app_data.is_dst);
set_time(app, DCF77_OFFSET);

ViewPort* view_port = view_port_alloc();
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));

view_port_draw_callback_set(view_port, app_draw_callback, &app_data);
view_port_draw_callback_set(view_port, app_draw_callback, app);
view_port_input_callback_set(view_port, app_input_callback, event_queue);

Gui* gui = furi_record_open(RECORD_GUI);
Expand All @@ -122,52 +109,48 @@ int dcf77_clock_sync_app_main(void* p) {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
notification_message_block(notification, &sequence_display_backlight_enforce_on);

InputEvent event;
bool running = false;
bool exit = false;
int sec = app_data.dt.second;
int sec = app->dt.second;
while(!exit) {
int silence_ms = 0;
// wait next second
while(app_data.dt.second == sec) furi_hal_rtc_get_datetime(&app_data.dt);
while(app->dt.second == sec) furi_hal_rtc_get_datetime(&app->dt);

if(app_data.dt.second < 59) {
furi_hal_light_set(LightRed | LightGreen | LightBlue, 0);
if(app->dt.second < 59) {
if(running) {
furi_hal_light_set(LightRed | LightGreen | LightBlue, 0);
furi_hal_rfid_tim_read_stop();
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
furi_hal_gpio_init(
&gpio_ext_pa4, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
}
silence_ms = get_dcf77_bit(app_data.dt.second) ? 200 : 100;
silence_ms = get_dcf77_bit(app->dt.second) ? 200 : 100;
furi_delay_ms(silence_ms);
furi_hal_rfid_tim_read_start(DCF77_FREQ, 0.5);
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, DCF77_FREQ, 50);
running = true;
furi_hal_light_set(LightBlue, 0xFF);
} else {
time_add(&app_data.dt, &app_data.dcf_dt, DCF77_OFFSET + 1);
set_dcf77_time(&app_data.dcf_dt, app_data.is_dst);
}

sec = app_data.dt.second;
int wait_ms = (1000 - silence_ms - SYNC_DELAY) / UPDATES;
for(int i = 0; i < UPDATES; i++) {
if(furi_message_queue_get(event_queue, &event, wait_ms) == FuriStatusOk) {
if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) {
switch(event.key) {
case InputKeyOk:
app_data.is_dst = !app_data.is_dst;
break;
case InputKeyBack:
exit = true;
break;
default:
break;
}
running = true;
} else
set_time(app, DCF77_OFFSET + 1);

sec = app->dt.second;
int wait_ms = 1000 - silence_ms - SYNC_DELAY;
uint32_t tick_start = furi_get_tick();
while(wait_ms > 0) {
FuriStatus status = furi_message_queue_get(event_queue, &event, wait_ms);
if((status == FuriStatusOk) && (event.type == InputTypePress)) {
if(event.key == InputKeyOk)
app->is_dst = !app->is_dst;
else if(event.key == InputKeyBack) {
exit = true;
break;
}
}
view_port_update(view_port);
if(exit) break;
if(status == FuriStatusErrorTimeout) break;
wait_ms -= furi_get_tick() - tick_start;
}
}

Expand All @@ -186,5 +169,8 @@ int dcf77_clock_sync_app_main(void* p) {
furi_message_queue_free(event_queue);
view_port_free(view_port);

furi_string_free(app->str);
free(app);

return 0;
}

0 comments on commit ef52585

Please sign in to comment.