dcf77_app.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. #include <furi.h>
  2. #include <gui/gui.h>
  3. #include <input/input.h>
  4. #include <stdlib.h>
  5. #include <dolphin/dolphin.h>
  6. #include <notification/notification.h>
  7. #include <notification/notification_messages.h>
  8. #include "dcf77_app.h"
  9. uint8_t get_dcf_message_bit(uint8_t* message, uint8_t bit) {
  10. if(bit == 59 || bit == 0) {
  11. return 0;
  12. }
  13. uint8_t byte_ = bit / 8;
  14. uint8_t bit_ = bit % 8;
  15. uint8_t bit_value = *(message + byte_) & (1 << (7 - bit_));
  16. if(!!bit_value) {
  17. return 1;
  18. }
  19. return 0;
  20. }
  21. // should it still be const?
  22. static void update_dcf77_message_from_rtc(AppFSM* app_fsm) {
  23. FuriHalRtcDateTime dt;
  24. furi_hal_rtc_get_datetime(&dt);
  25. app_fsm->bit_number = dt.second;
  26. app_fsm->next_message = malloc(8);
  27. /*
  28. set_dcf_message(app_fsm->next_message, dt.minute, dt.hour,
  29. dt.day, dt.month, (uint8_t)(dt.year % 100), dt.weekday,
  30. false, false, false, false, 0x0000);
  31. */
  32. //set_dcf_message(app_fsm->next_message, 41, 17, 27, 12, 22, 2, false, false, false, false, 0x0000);
  33. app_fsm->tx_hour = 17;
  34. app_fsm->tx_minute = 41;
  35. app_fsm->tx_day = 27;
  36. app_fsm->tx_month = 12;
  37. app_fsm->tx_year = 22;
  38. app_fsm->tx_dow = 2;
  39. app_fsm->tx_hour = dt.hour;
  40. app_fsm->tx_minute = dt.minute;
  41. app_fsm->tx_day = dt.day;
  42. app_fsm->tx_month = dt.month;
  43. app_fsm->tx_year = dt.year % 100;
  44. app_fsm->tx_dow = dt.weekday;
  45. set_dcf_message(
  46. app_fsm->next_message,
  47. app_fsm->tx_minute,
  48. app_fsm->tx_hour,
  49. app_fsm->tx_day,
  50. app_fsm->tx_month,
  51. app_fsm->tx_year,
  52. app_fsm->tx_dow,
  53. false,
  54. false,
  55. false,
  56. false,
  57. 0x0000);
  58. app_fsm->buffer_swap_pending = true;
  59. }
  60. static void set_outputs(bool status, AppFSM* app_fsm) {
  61. UNUSED(app_fsm);
  62. static bool last;
  63. if(last != status) {
  64. if(status) {
  65. furi_hal_light_set(LightRed, 0xFF);
  66. furi_hal_light_set(LightGreen, 0x1F);
  67. // furi_hal_speaker_start(50, 1);
  68. } else {
  69. furi_hal_light_set(LightRed | LightGreen | LightBlue, 0);
  70. // furi_hal_speaker_stop();
  71. }
  72. }
  73. last = status;
  74. }
  75. static void comparator_trigger_callback(bool level, void* comp_ctx) {
  76. UNUSED(comp_ctx);
  77. furi_hal_gpio_write(&gpio_ext_pa7, !level);
  78. }
  79. void gpio_init() {
  80. furi_hal_gpio_write(OUTPUT_PIN, false);
  81. furi_hal_gpio_init(OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  82. }
  83. void gpio_mark() {
  84. furi_hal_gpio_write(OUTPUT_PIN, true);
  85. }
  86. void gpio_space() {
  87. furi_hal_gpio_write(OUTPUT_PIN, false);
  88. }
  89. void gpio_deinit() {
  90. furi_hal_gpio_init(OUTPUT_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedVeryHigh);
  91. }
  92. void dcf77_lf_init(int freq, AppFSM* app_fsm) {
  93. /* // this ends up doing
  94. // LL_TIM_SetAutoReload(FURI_HAL_RFID_READ_TIMER, period);
  95. // LL_TIM_OC_SetCompareCH1(FURI_HAL_RFID_READ_TIMER, period*duty_cycle);
  96. */
  97. furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull);
  98. furi_hal_rfid_comp_set_callback(comparator_trigger_callback, app_fsm);
  99. furi_hal_rfid_tim_read_start(freq, 0.5);
  100. furi_hal_rfid_pin_pull_release();
  101. }
  102. void dcf77_mark(int freq) {
  103. UNUSED(freq);
  104. furi_hal_rfid_comp_start();
  105. furi_hal_rfid_tim_read_continue();
  106. //furi_hal_rfid_tim_read_start(freq, 0.5);
  107. /* --- */
  108. furi_hal_rfid_comp_stop();
  109. furi_hal_rfid_tim_read_pause();
  110. }
  111. void dcf77_space() {
  112. furi_hal_rfid_comp_stop();
  113. furi_hal_rfid_tim_read_pause();
  114. }
  115. void dcf77_deinit() {
  116. dcf77_space();
  117. furi_hal_rfid_comp_stop();
  118. furi_hal_rfid_comp_set_callback(NULL, NULL);
  119. furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog);
  120. furi_hal_rfid_tim_read_stop();
  121. furi_hal_rfid_pins_reset();
  122. }
  123. static void render_callback(Canvas* const canvas, void* ctx) {
  124. furi_assert(ctx);
  125. AppFSM* app_fsm = ctx;
  126. furi_mutex_acquire(app_fsm->mutex, FuriWaitForever);
  127. char buffer[64];
  128. uint8_t yoffset = 9;
  129. uint8_t bit_number = app_fsm->bit_number;
  130. uint8_t bit_value = app_fsm->bit_value;
  131. uint8_t underline_x = (bit_number / 8) * 12 + 16;
  132. uint8_t byte_ = app_fsm->dcf77_message[bit_number / 8];
  133. char display_bits[9] = "00000000";
  134. if(app_fsm->buffer_swap_pending) {
  135. memcpy(app_fsm->dcf77_message, app_fsm->next_message, 8);
  136. canvas_draw_str_aligned(canvas, 112, 10, AlignCenter, AlignBottom, "*");
  137. app_fsm->buffer_swap_pending = false;
  138. }
  139. canvas_draw_frame(canvas, 0, 0, 128, 64);
  140. canvas_set_font(canvas, FontPrimary);
  141. snprintf(buffer, 64, "%1x.%1x=%01x", bit_number / 8, (bit_number % 8), bit_value);
  142. FuriHalRtcDateTime dt;
  143. furi_hal_rtc_get_datetime(&dt);
  144. //canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignBottom, "DCF77 emulator");
  145. snprintf(
  146. buffer,
  147. 64,
  148. "%02d:%02d %02d.%02d.%02d",
  149. app_fsm->tx_hour,
  150. app_fsm->tx_minute,
  151. app_fsm->tx_day,
  152. app_fsm->tx_month,
  153. app_fsm->tx_year);
  154. canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignBottom, buffer);
  155. if(app_fsm->debug_flag) {
  156. canvas_draw_str_aligned(canvas, 8, 10, AlignCenter, AlignBottom, "D");
  157. }
  158. snprintf(buffer, 64, "%1x.%1x=%01x", bit_number / 8, (bit_number % 8), bit_value);
  159. canvas_set_font(canvas, FontSecondary);
  160. canvas_draw_str_aligned(canvas, 64, 24 + (9 - yoffset), AlignCenter, AlignBottom, buffer);
  161. snprintf(buffer, 64, "%s (bit %d)", dcf77_bitnames[bit_number], bit_value);
  162. canvas_set_font(canvas, FontSecondary);
  163. canvas_draw_str_aligned(canvas, 64, 34 + (9 - yoffset), AlignCenter, AlignBottom, buffer);
  164. snprintf(
  165. buffer,
  166. 64,
  167. "%02x%02x%02x%02x%02x%02x%02x%02x",
  168. app_fsm->dcf77_message[0],
  169. app_fsm->dcf77_message[1],
  170. app_fsm->dcf77_message[2],
  171. app_fsm->dcf77_message[3],
  172. app_fsm->dcf77_message[4],
  173. app_fsm->dcf77_message[5],
  174. app_fsm->dcf77_message[6],
  175. app_fsm->dcf77_message[7]);
  176. canvas_set_font(canvas, FontKeyboard);
  177. canvas_draw_str_aligned(canvas, 64, 44 + (9 - yoffset), AlignCenter, AlignBottom, buffer);
  178. canvas_draw_line(canvas, underline_x, 45, underline_x + 10, 45);
  179. for(int i = 0; i < 8; i++) {
  180. if((byte_ & (1 << (7 - i))) != 0) {
  181. display_bits[i] = '1';
  182. }
  183. }
  184. canvas_set_font(canvas, FontKeyboard);
  185. snprintf(buffer, 64, "%02x=%s", byte_, display_bits);
  186. canvas_draw_str_aligned(
  187. canvas, 64, 54 + (9 - yoffset), AlignCenter, AlignBottom, buffer); // whole message
  188. underline_x = (bit_number % 8) * 6 + 49;
  189. canvas_draw_line(canvas, underline_x, 55, underline_x + 4, 55); // current byte
  190. furi_mutex_release(app_fsm->mutex);
  191. }
  192. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  193. furi_assert(event_queue);
  194. AppEvent event = {.type = EventKeyPress, .input = *input_event};
  195. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  196. }
  197. static void timer_tick_callback(FuriMessageQueue* event_queue) {
  198. furi_assert(event_queue);
  199. AppEvent event = {.type = EventTimerTick};
  200. furi_message_queue_put(event_queue, &event, 0);
  201. }
  202. static void app_init(AppFSM* const app_fsm, FuriMessageQueue* event_queue) {
  203. app_fsm->counter = 0;
  204. app_fsm->dcf77_message = &dcf77_message_buffer[0];
  205. app_fsm->next_message = &dcf77_next_buffer[0];
  206. dcf77_lf_init(LF_FREQ, app_fsm);
  207. gpio_init();
  208. app_fsm->dcf77_message = malloc(8); // message had 60 bits since 1959 and is unlikely to change
  209. // app_fsm->next_message = malloc(8);
  210. update_dcf77_message_from_rtc(app_fsm);
  211. app_fsm->baseband_counter = 0;
  212. app_fsm->_event_queue = event_queue;
  213. FuriTimer* timer =
  214. furi_timer_alloc(timer_tick_callback, FuriTimerTypePeriodic, app_fsm->_event_queue);
  215. furi_timer_start(timer, furi_kernel_get_tick_frequency() / TIMER_HZ);
  216. app_fsm->_timer = timer;
  217. }
  218. static void app_deinit(AppFSM* const app_fsm) {
  219. dcf77_deinit();
  220. gpio_deinit();
  221. app_fsm->buffer_swap_pending = false;
  222. free(app_fsm->dcf77_message);
  223. furi_timer_free(app_fsm->_timer);
  224. }
  225. static void on_timer_tick(AppFSM* app_fsm) {
  226. static uint8_t last_second = 61;
  227. static bool last_output = false;
  228. bool output = true;
  229. FuriHalRtcDateTime dt;
  230. furi_hal_rtc_get_datetime(&dt);
  231. app_fsm->bit_number = dt.second;
  232. app_fsm->bit_value = get_dcf_message_bit(app_fsm->dcf77_message, app_fsm->bit_number);
  233. if(dt.second != last_second) {
  234. app_fsm->baseband_counter = 0;
  235. output = true;
  236. } else {
  237. app_fsm->baseband_counter++;
  238. }
  239. /*
  240. if (app_fsm->baseband_counter < 8)
  241. {
  242. app_fsm->debug_flag = true;
  243. }
  244. else
  245. {
  246. app_fsm->debug_flag = false;
  247. }*/
  248. if(dt.second == 0 && app_fsm->baseband_counter == 3) {
  249. update_dcf77_message_from_rtc(app_fsm);
  250. }
  251. if(dt.second == 59) {
  252. output = true;
  253. } else {
  254. if(app_fsm->baseband_counter > TIME_ZERO && app_fsm->bit_value == 0) {
  255. output = false;
  256. } else if(app_fsm->baseband_counter > TIME_ONE && app_fsm->bit_value == 1) {
  257. output = false;
  258. }
  259. }
  260. if(last_output != output) {
  261. set_outputs(output, app_fsm);
  262. if(!output) {
  263. dcf77_space();
  264. gpio_space();
  265. } else {
  266. dcf77_mark(LF_FREQ);
  267. gpio_mark();
  268. }
  269. }
  270. last_second = dt.second;
  271. last_output = output;
  272. }
  273. int32_t dcf77_app_main(void* p) {
  274. UNUSED(p);
  275. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
  276. AppFSM* app_fsm = malloc(sizeof(AppFSM));
  277. app_init(app_fsm, event_queue);
  278. app_fsm->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  279. if(!app_fsm->mutex) {
  280. FURI_LOG_E(TAG, "cannot create mutex\r\n");
  281. free(app_fsm);
  282. return 255;
  283. }
  284. ViewPort* view_port = view_port_alloc();
  285. view_port_draw_callback_set(view_port, render_callback, app_fsm);
  286. view_port_input_callback_set(view_port, input_callback, event_queue);
  287. // Open GUI and register view_port
  288. Gui* gui = furi_record_open(RECORD_GUI);
  289. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  290. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  291. notification_message_block(notification, &sequence_display_backlight_enforce_on);
  292. if(furi_hal_speaker_acquire(500)) {
  293. ;
  294. }
  295. dolphin_deed(DolphinDeedPluginGameStart);
  296. AppEvent event;
  297. for(bool processing = true; processing;) {
  298. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  299. furi_mutex_acquire(app_fsm->mutex, FuriWaitForever);
  300. if(event_status == FuriStatusOk) {
  301. // kepress events
  302. if(event.type == EventKeyPress) {
  303. if(event.input.type == InputTypeShort) {
  304. switch(event.input.key) {
  305. case InputKeyUp:
  306. app_fsm->last_key = KeyUp;
  307. break;
  308. case InputKeyDown:
  309. app_fsm->last_key = KeyDown;
  310. break;
  311. case InputKeyRight:
  312. app_fsm->last_key = KeyRight;
  313. break;
  314. case InputKeyLeft:
  315. app_fsm->last_key = KeyLeft;
  316. break;
  317. case InputKeyOk:
  318. app_fsm->last_key = KeyOK;
  319. app_fsm->counter = -5;
  320. break;
  321. case InputKeyBack:
  322. processing = false;
  323. break;
  324. default:
  325. break;
  326. }
  327. }
  328. // user events
  329. } else if(event.type == EventTimerTick) {
  330. FURI_CRITICAL_ENTER();
  331. on_timer_tick(app_fsm);
  332. FURI_CRITICAL_EXIT();
  333. }
  334. }
  335. furi_mutex_release(app_fsm->mutex);
  336. view_port_update(view_port);
  337. }
  338. furi_hal_speaker_release();
  339. notification_message_block(notification, &seq_c_minor);
  340. // Wait for all notifications to be played and return backlight to normal state
  341. app_deinit(app_fsm);
  342. notification_message_block(notification, &sequence_display_backlight_enforce_auto);
  343. view_port_enabled_set(view_port, false);
  344. gui_remove_view_port(gui, view_port);
  345. furi_record_close(RECORD_GUI);
  346. furi_record_close(RECORD_NOTIFICATION);
  347. view_port_free(view_port);
  348. furi_message_queue_free(event_queue);
  349. furi_mutex_free(app_fsm->mutex);
  350. free(app_fsm);
  351. return 0;
  352. }