dcf77_app.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  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. DateTime 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. DateTime 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, void* ctx) {
  193. furi_assert(ctx);
  194. FuriMessageQueue* event_queue = ctx;
  195. AppEvent event = {.type = EventKeyPress, .input = *input_event};
  196. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  197. }
  198. static void timer_tick_callback(void* ctx) {
  199. furi_assert(ctx);
  200. FuriMessageQueue* event_queue = ctx;
  201. AppEvent event = {.type = EventTimerTick};
  202. furi_message_queue_put(event_queue, &event, 0);
  203. }
  204. static void app_init(AppFSM* const app_fsm, FuriMessageQueue* event_queue) {
  205. app_fsm->counter = 0;
  206. app_fsm->dcf77_message = &dcf77_message_buffer[0];
  207. app_fsm->next_message = &dcf77_next_buffer[0];
  208. dcf77_lf_init(LF_FREQ, app_fsm);
  209. gpio_init();
  210. app_fsm->dcf77_message = malloc(8); // message had 60 bits since 1959 and is unlikely to change
  211. // app_fsm->next_message = malloc(8);
  212. update_dcf77_message_from_rtc(app_fsm);
  213. app_fsm->baseband_counter = 0;
  214. app_fsm->_event_queue = event_queue;
  215. FuriTimer* timer =
  216. furi_timer_alloc(timer_tick_callback, FuriTimerTypePeriodic, app_fsm->_event_queue);
  217. furi_timer_start(timer, furi_kernel_get_tick_frequency() / TIMER_HZ);
  218. app_fsm->_timer = timer;
  219. }
  220. static void app_deinit(AppFSM* const app_fsm) {
  221. dcf77_deinit();
  222. gpio_deinit();
  223. app_fsm->buffer_swap_pending = false;
  224. free(app_fsm->dcf77_message);
  225. furi_timer_free(app_fsm->_timer);
  226. }
  227. static void on_timer_tick(AppFSM* app_fsm) {
  228. static uint8_t last_second = 61;
  229. static bool last_output = false;
  230. bool output = true;
  231. DateTime dt;
  232. furi_hal_rtc_get_datetime(&dt);
  233. app_fsm->bit_number = dt.second;
  234. app_fsm->bit_value = get_dcf_message_bit(app_fsm->dcf77_message, app_fsm->bit_number);
  235. if(dt.second != last_second) {
  236. app_fsm->baseband_counter = 0;
  237. output = true;
  238. } else {
  239. app_fsm->baseband_counter++;
  240. }
  241. /*
  242. if (app_fsm->baseband_counter < 8)
  243. {
  244. app_fsm->debug_flag = true;
  245. }
  246. else
  247. {
  248. app_fsm->debug_flag = false;
  249. }*/
  250. if(dt.second == 0 && app_fsm->baseband_counter == 3) {
  251. update_dcf77_message_from_rtc(app_fsm);
  252. }
  253. if(dt.second == 59) {
  254. output = true;
  255. } else {
  256. if(app_fsm->baseband_counter > TIME_ZERO && app_fsm->bit_value == 0) {
  257. output = false;
  258. } else if(app_fsm->baseband_counter > TIME_ONE && app_fsm->bit_value == 1) {
  259. output = false;
  260. }
  261. }
  262. if(last_output != output) {
  263. set_outputs(output, app_fsm);
  264. if(!output) {
  265. dcf77_space();
  266. gpio_space();
  267. } else {
  268. dcf77_mark(LF_FREQ);
  269. gpio_mark();
  270. }
  271. }
  272. last_second = dt.second;
  273. last_output = output;
  274. }
  275. int32_t dcf77_app_main(void* p) {
  276. UNUSED(p);
  277. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
  278. AppFSM* app_fsm = malloc(sizeof(AppFSM));
  279. app_init(app_fsm, event_queue);
  280. app_fsm->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  281. if(!app_fsm->mutex) {
  282. FURI_LOG_E(TAG, "cannot create mutex\r\n");
  283. free(app_fsm);
  284. return 255;
  285. }
  286. ViewPort* view_port = view_port_alloc();
  287. view_port_draw_callback_set(view_port, render_callback, app_fsm);
  288. view_port_input_callback_set(view_port, input_callback, event_queue);
  289. // Open GUI and register view_port
  290. Gui* gui = furi_record_open(RECORD_GUI);
  291. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  292. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  293. notification_message_block(notification, &sequence_display_backlight_enforce_on);
  294. if(furi_hal_speaker_acquire(500)) {
  295. ;
  296. }
  297. dolphin_deed(DolphinDeedPluginGameStart);
  298. AppEvent event;
  299. for(bool processing = true; processing;) {
  300. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  301. furi_mutex_acquire(app_fsm->mutex, FuriWaitForever);
  302. if(event_status == FuriStatusOk) {
  303. // kepress events
  304. if(event.type == EventKeyPress) {
  305. if(event.input.type == InputTypeShort) {
  306. switch(event.input.key) {
  307. case InputKeyUp:
  308. app_fsm->last_key = KeyUp;
  309. break;
  310. case InputKeyDown:
  311. app_fsm->last_key = KeyDown;
  312. break;
  313. case InputKeyRight:
  314. app_fsm->last_key = KeyRight;
  315. break;
  316. case InputKeyLeft:
  317. app_fsm->last_key = KeyLeft;
  318. break;
  319. case InputKeyOk:
  320. app_fsm->last_key = KeyOK;
  321. app_fsm->counter = -5;
  322. break;
  323. case InputKeyBack:
  324. processing = false;
  325. break;
  326. default:
  327. break;
  328. }
  329. }
  330. // user events
  331. } else if(event.type == EventTimerTick) {
  332. FURI_CRITICAL_ENTER();
  333. on_timer_tick(app_fsm);
  334. FURI_CRITICAL_EXIT();
  335. }
  336. }
  337. furi_mutex_release(app_fsm->mutex);
  338. view_port_update(view_port);
  339. }
  340. furi_hal_speaker_release();
  341. notification_message_block(notification, &seq_c_minor);
  342. // Wait for all notifications to be played and return backlight to normal state
  343. app_deinit(app_fsm);
  344. notification_message_block(notification, &sequence_display_backlight_enforce_auto);
  345. view_port_enabled_set(view_port, false);
  346. gui_remove_view_port(gui, view_port);
  347. furi_record_close(RECORD_GUI);
  348. furi_record_close(RECORD_NOTIFICATION);
  349. view_port_free(view_port);
  350. furi_message_queue_free(event_queue);
  351. furi_mutex_free(app_fsm->mutex);
  352. free(app_fsm);
  353. return 0;
  354. }