scene_dcf77.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. #include "scene_dcf77.h"
  2. #include "app_state.h"
  3. #include "furi_hal_rtc.h"
  4. #include "longwave_clock_app.h"
  5. #include "module_date.h"
  6. #include "module_lights.h"
  7. #include "module_rollbits.h"
  8. #include "module_time.h"
  9. #include "scenes.h"
  10. #define TOP_BAR 0
  11. #define TOP_TIME 30
  12. #define TOP_DATE 53
  13. #define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y)))
  14. /** main menu events */
  15. static bool lwc_dcf77_scene_input(InputEvent* input_event, void* context) {
  16. UNUSED(input_event);
  17. UNUSED(context);
  18. return false;
  19. }
  20. static void lwc_dcf77_scene_draw(Canvas* canvas, void* context) {
  21. LWCViewModel* model = context;
  22. canvas_clear(canvas);
  23. draw_decoded_bits(
  24. canvas,
  25. DCF77,
  26. TOP_BAR,
  27. model->buffer,
  28. BUFFER,
  29. model->received_count,
  30. model->last_received,
  31. model->received_interrupt < MIN_INTERRUPT,
  32. model->decoding == DecodingUnknown);
  33. draw_decoded_time(
  34. canvas,
  35. TOP_TIME,
  36. model->decoding_time,
  37. model->hours_10s,
  38. model->hours_1s,
  39. model->minutes_10s,
  40. model->minutes_1s,
  41. model->seconds,
  42. model->timezone,
  43. model->seconds > 23);
  44. draw_decoded_date(
  45. canvas,
  46. TOP_DATE,
  47. model->decoding_date,
  48. 20,
  49. model->year_10s,
  50. model->year_1s,
  51. model->month_10s,
  52. model->month_1s,
  53. model->day_of_month_10s,
  54. model->day_of_month_1s,
  55. model->day_of_week);
  56. }
  57. static void dcf77_add_gui_bit(LWCViewModel* model, Bit bit) {
  58. if(model->last_received == BUFFER - 1) {
  59. model->last_received = 0;
  60. } else {
  61. model->last_received++;
  62. }
  63. if(model->received_count < BUFFER) {
  64. model->received_count++;
  65. }
  66. model->buffer[model->last_received] = bit;
  67. }
  68. static void dcf77_process_time_bit(LWCViewModel* model, DecodingPhase current_phase) {
  69. DecodingTimePhase new_decoding_time;
  70. if(current_phase == DecodingTime) {
  71. new_decoding_time = dcf77_get_decoding_time_phase(model->minute_data);
  72. } else {
  73. new_decoding_time = DecodingNoTime;
  74. }
  75. if(model->decoding_time != new_decoding_time) {
  76. switch(new_decoding_time) {
  77. case DecodingTimeTimezone:
  78. dcf77_add_gui_bit(model, BitStartTimezone);
  79. break;
  80. case DecodingTimeMinutes1s:
  81. dcf77_add_gui_bit(model, BitStartMinute);
  82. break;
  83. case DecodingTimeMinutes10s:
  84. dcf77_add_gui_bit(model, BitStartEmpty);
  85. break;
  86. case DecodingTimeMinutes:
  87. if(dcf77_get_minutes_checksum(model->minute_data) != 0) {
  88. model->minutes_1s = -1;
  89. model->minutes_10s = -1;
  90. dcf77_add_gui_bit(model, BitChecksumError);
  91. } else {
  92. model->minutes_10s = dcf77_decode_minutes_10s(model->minute_data);
  93. dcf77_add_gui_bit(model, BitChecksum);
  94. }
  95. break;
  96. case DecodingTimeHours1s:
  97. dcf77_add_gui_bit(model, BitStartHour);
  98. break;
  99. case DecodingTimeHours10s:
  100. dcf77_add_gui_bit(model, BitStartEmpty);
  101. break;
  102. case DecodingTimeHours:
  103. if(dcf77_get_hours_checksum(model->minute_data) != 0) {
  104. model->hours_1s = -1;
  105. model->hours_10s = -1;
  106. dcf77_add_gui_bit(model, BitChecksumError);
  107. } else {
  108. model->hours_10s = dcf77_decode_hours_10s(model->minute_data);
  109. dcf77_add_gui_bit(model, BitChecksum);
  110. }
  111. break;
  112. case DecodingTimeConstant:
  113. dcf77_add_gui_bit(model, BitConstant);
  114. break;
  115. case DecodingTimeSeconds:
  116. dcf77_add_gui_bit(model, BitConstant);
  117. break;
  118. default:
  119. }
  120. switch(model->decoding_time) {
  121. case DecodingTimeTimezone:
  122. model->timezone = dcf77_decode_timezone(model->minute_data);
  123. break;
  124. case DecodingTimeMinutes1s:
  125. model->minutes_1s = dcf77_decode_minutes_1s(model->minute_data);
  126. break;
  127. case DecodingTimeHours1s:
  128. model->hours_1s = dcf77_decode_hours_1s(model->minute_data);
  129. break;
  130. default:
  131. }
  132. model->decoding_time = new_decoding_time;
  133. }
  134. }
  135. static void dcf77_process_date_bit(LWCViewModel* model, DecodingPhase current_phase) {
  136. DecodingDatePhase new_decoding_date;
  137. if(current_phase == DecodingDate) {
  138. new_decoding_date = dcf77_get_decoding_date_phase(model->minute_data);
  139. } else {
  140. new_decoding_date = DecodingNoDate;
  141. }
  142. if(model->decoding_date != new_decoding_date) {
  143. switch(new_decoding_date) {
  144. case DecodingDateDayOfMonth1s:
  145. dcf77_add_gui_bit(model, BitStartDayOfMonth);
  146. break;
  147. case DecodingDateDayOfMonth10s:
  148. dcf77_add_gui_bit(model, BitStartEmpty);
  149. break;
  150. case DecodingDateDayOfWeek:
  151. dcf77_add_gui_bit(model, BitStartDayOfWeek);
  152. break;
  153. case DecodingDateMonth1s:
  154. dcf77_add_gui_bit(model, BitStartMonth);
  155. break;
  156. case DecodingDateMonth10s:
  157. dcf77_add_gui_bit(model, BitStartEmpty);
  158. break;
  159. case DecodingDateYear1s:
  160. dcf77_add_gui_bit(model, BitStartYear);
  161. break;
  162. case DecodingDateYear10s:
  163. dcf77_add_gui_bit(model, BitStartEmpty);
  164. break;
  165. case DecodingDateDate:
  166. if(dcf77_get_date_checksum(model->minute_data) != 0) {
  167. model->year_10s = -1;
  168. model->year_1s = -1;
  169. model->month_10s = -1;
  170. model->month_1s = -1;
  171. model->day_of_month_1s = -1;
  172. model->day_of_month_10s = -1;
  173. model->day_of_week = -1;
  174. dcf77_add_gui_bit(model, BitChecksumError);
  175. } else {
  176. model->year_10s = dcf77_decode_year_10s(model->minute_data);
  177. dcf77_add_gui_bit(model, BitChecksum);
  178. }
  179. break;
  180. default:
  181. }
  182. switch(model->decoding_date) {
  183. case DecodingDateDayOfMonth1s:
  184. model->day_of_month_1s = dcf77_decode_day_of_month_1s(model->minute_data);
  185. break;
  186. case DecodingDateDayOfMonth10s:
  187. model->day_of_month_10s = dcf77_decode_day_of_month_10s(model->minute_data);
  188. break;
  189. case DecodingDateDayOfWeek:
  190. model->day_of_week = dcf77_decode_day_of_week(model->minute_data);
  191. break;
  192. case DecodingDateMonth1s:
  193. model->month_1s = dcf77_decode_month_1s(model->minute_data);
  194. break;
  195. case DecodingDateMonth10s:
  196. model->month_10s = dcf77_decode_month_10s(model->minute_data);
  197. break;
  198. case DecodingDateYear1s:
  199. model->year_1s = dcf77_decode_year_1s(model->minute_data);
  200. break;
  201. default:
  202. }
  203. model->decoding_date = new_decoding_date;
  204. }
  205. }
  206. static void dcf77_receive_bit(void* context, int8_t received_bit) {
  207. View* view = context;
  208. FURI_LOG_D(TAG, "Received a %d", received_bit);
  209. with_view_model(
  210. view,
  211. LWCViewModel * model,
  212. {
  213. model->received_interrupt = MIN_INTERRUPT;
  214. minute_data_add_bit(model->minute_data, received_bit);
  215. int8_t seconds = minute_data_get_length(model->minute_data);
  216. if(seconds > 0) {
  217. model->seconds = seconds - 1;
  218. }
  219. DecodingPhase old_phase = model->decoding;
  220. model->decoding = dcf77_get_decoding_phase(model->minute_data);
  221. bool change_phase = model->decoding != old_phase;
  222. switch(model->decoding) {
  223. case DecodingUnknown:
  224. if(change_phase) {
  225. model->decoding_time = DecodingNoTime;
  226. model->decoding_date = DecodingNoDate;
  227. }
  228. break;
  229. case DecodingMeta:
  230. if(change_phase) {
  231. model->decoding_time = DecodingNoTime;
  232. model->decoding_date = DecodingNoDate;
  233. }
  234. dcf77_add_gui_bit(model, BitConstant);
  235. break;
  236. case DecodingWeather:
  237. if(change_phase) {
  238. model->decoding_time = DecodingNoTime;
  239. model->decoding_date = DecodingNoDate;
  240. dcf77_add_gui_bit(model, BitStartWeather);
  241. }
  242. break;
  243. case DecodingTime:
  244. dcf77_process_time_bit(model, model->decoding);
  245. break;
  246. case DecodingDate:
  247. if(change_phase) {
  248. model->decoding_time = DecodingNoTime;
  249. }
  250. dcf77_process_date_bit(model, model->decoding);
  251. break;
  252. default:
  253. break;
  254. }
  255. dcf77_add_gui_bit(model, (Bit)received_bit);
  256. },
  257. true);
  258. }
  259. static void dcf77_receive_end_of_minute(void* context) {
  260. View* view = context;
  261. FURI_LOG_I(TAG, "Found the DCF77 synchronization, starting a new minute!");
  262. with_view_model(
  263. view,
  264. LWCViewModel * model,
  265. {
  266. dcf77_add_gui_bit(model, BitEndMinute);
  267. minute_data_start_minute(model->minute_data);
  268. model->decoding = DecodingTime;
  269. model->decoding_time = DecodingTimeSeconds;
  270. },
  271. true);
  272. }
  273. static void dcf77_receive_desync(void* context) {
  274. View* view = context;
  275. FURI_LOG_I(TAG, "Got a DESYNC error, resetting all!");
  276. with_view_model(
  277. view,
  278. LWCViewModel * model,
  279. {
  280. model->decoding = DecodingUnknown;
  281. model->decoding_time = DecodingNoTime;
  282. model->decoding_date = DecodingNoDate;
  283. dcf77_add_gui_bit(model, BitEndSync);
  284. },
  285. true);
  286. }
  287. static void dcf77_receive_unknown(void* context) {
  288. View* view = context;
  289. FURI_LOG_I(TAG, "Not received any bit, assuming a missing receipt!");
  290. with_view_model(
  291. view,
  292. LWCViewModel * model,
  293. {
  294. minute_data_add_bit(model->minute_data, -1);
  295. dcf77_add_gui_bit(model, BitUnknown);
  296. },
  297. true);
  298. }
  299. static void dcf77_receive_interrupt(void* context) {
  300. View* view = context;
  301. with_view_model(view, LWCViewModel * model, { model->received_interrupt++; }, true);
  302. }
  303. static void dcf77_receive_gpio(GPIOEvent event, void* context) {
  304. App* app = context;
  305. if(event.shift_up) {
  306. if(DURATION_DIFF(event.time_passed_down, 1850) < 100) {
  307. scene_manager_handle_custom_event(app->scene_manager, LCWDCF77EventReceiveSync);
  308. } else {
  309. scene_manager_handle_custom_event(app->scene_manager, LCWDCF77EventReceiveInterrupt);
  310. }
  311. } else if(event.time_passed_down > 400) {
  312. if(DURATION_DIFF(event.time_passed_up, 200) < 50) {
  313. scene_manager_handle_custom_event(app->scene_manager, LCWDCF77EventReceive1);
  314. } else if(DURATION_DIFF(event.time_passed_up, 100) < 50) {
  315. scene_manager_handle_custom_event(app->scene_manager, LCWDCF77EventReceive0);
  316. }
  317. } else {
  318. scene_manager_handle_custom_event(app->scene_manager, LCWDCF77EventReceiveInterrupt);
  319. }
  320. }
  321. bool lwc_dcf77_scene_on_event(void* context, SceneManagerEvent event) {
  322. App* app = context;
  323. bool consumed = false;
  324. switch(event.type) {
  325. case SceneManagerEventTypeCustom:
  326. LWCDCF77Event dcf77_event = event.event;
  327. switch(dcf77_event) {
  328. case LCWDCF77EventReceive0:
  329. lwc_app_led_on_receive_clear(app);
  330. dcf77_receive_bit(app->dcf77_view, 0);
  331. consumed = true;
  332. break;
  333. case LCWDCF77EventReceive1:
  334. lwc_app_led_on_receive_clear(app);
  335. dcf77_receive_bit(app->dcf77_view, 1);
  336. consumed = true;
  337. break;
  338. case LCWDCF77EventReceiveSync:
  339. lwc_app_led_on_sync(app);
  340. dcf77_receive_end_of_minute(app->dcf77_view);
  341. consumed = true;
  342. break;
  343. case LCWDCF77EventReceiveDesync:
  344. lwc_app_led_on_desync(app);
  345. dcf77_receive_desync(app->dcf77_view);
  346. consumed = true;
  347. break;
  348. case LCWDCF77EventReceiveUnknown:
  349. dcf77_receive_unknown(app->dcf77_view);
  350. lwc_app_led_on_receive_unknown(app);
  351. consumed = true;
  352. break;
  353. case LCWDCF77EventReceiveInterrupt:
  354. dcf77_receive_interrupt(app->dcf77_view);
  355. consumed = true;
  356. break;
  357. default:
  358. }
  359. break;
  360. case SceneManagerEventTypeTick:
  361. if(app->state->gpio != NULL) {
  362. for(int i = 10;
  363. i > 0 && gpio_callback_with_event(app->state->gpio, dcf77_receive_gpio);
  364. i--) {
  365. }
  366. }
  367. consumed = true;
  368. break;
  369. default:
  370. consumed = false;
  371. break;
  372. }
  373. return consumed;
  374. }
  375. static void dcf77_refresh_simulated_data(MinuteData* simulation_data, DateTime datetime) {
  376. FURI_LOG_I(TAG, "Setting simulated data for minute %d", datetime.minute);
  377. dcf77_set_simulated_minute_data(
  378. simulation_data,
  379. datetime.second,
  380. datetime.minute,
  381. datetime.hour,
  382. datetime.weekday,
  383. datetime.day,
  384. datetime.month,
  385. datetime.year % 100);
  386. }
  387. static void dcf77_500ms_callback(void* context) {
  388. App* app = context;
  389. if(lwc_get_protocol_config(app->state)->run_mode == Demo) {
  390. MinuteData* simulation = app->state->simulation_data;
  391. DateTime now;
  392. furi_hal_rtc_get_datetime(&now);
  393. if(now.second < MINUTE - 1 && simulation->index != now.second) {
  394. simulation->index++;
  395. if(now.second == 0) {
  396. dcf77_refresh_simulated_data(app->state->simulation_data, now);
  397. scene_manager_handle_custom_event(app->scene_manager, LCWDCF77EventReceiveSync);
  398. }
  399. if(minute_data_get_bit(app->state->simulation_data, now.second) == 0) {
  400. scene_manager_handle_custom_event(app->scene_manager, LCWDCF77EventReceive0);
  401. } else {
  402. scene_manager_handle_custom_event(app->scene_manager, LCWDCF77EventReceive1);
  403. }
  404. }
  405. }
  406. MinuteDataError result;
  407. with_view_model(
  408. app->dcf77_view,
  409. LWCViewModel * model,
  410. { result = minute_data_500ms_passed(model->minute_data); },
  411. false);
  412. switch(result) {
  413. case MinuteDataErrorNone:
  414. break;
  415. case MinuteDataErrorUnknownBit:
  416. scene_manager_handle_custom_event(app->scene_manager, LCWDCF77EventReceiveUnknown);
  417. break;
  418. case MinuteDataErrorDesync:
  419. scene_manager_handle_custom_event(app->scene_manager, LCWDCF77EventReceiveDesync);
  420. break;
  421. }
  422. }
  423. void lwc_dcf77_scene_on_enter(void* context) {
  424. App* app = context;
  425. ProtoConfig* config = lwc_get_protocol_config(app->state);
  426. with_view_model(
  427. app->dcf77_view,
  428. LWCViewModel * model,
  429. {
  430. model->decoding = DecodingUnknown;
  431. model->decoding_time = DecodingNoTime;
  432. model->decoding_date = DecodingNoDate;
  433. model->year_10s = -1;
  434. model->year_1s = -1;
  435. model->month_10s = -1;
  436. model->month_1s = -1;
  437. model->day_of_month_1s = -1;
  438. model->day_of_month_10s = -1;
  439. model->day_of_week = -1;
  440. model->hours_10s = -1;
  441. model->hours_1s = -1;
  442. model->minutes_10s = -1;
  443. model->minutes_1s = -1;
  444. model->seconds = -1;
  445. model->timezone = UnknownTimezone;
  446. model->last_received = BUFFER - 1;
  447. model->received_interrupt = config->run_mode == Demo ? MIN_INTERRUPT : 0;
  448. model->received_count = 0;
  449. minute_data_reset(model->minute_data);
  450. },
  451. true);
  452. switch(config->run_mode) {
  453. case Demo:
  454. app->state->simulation_data = minute_data_alloc(60);
  455. DateTime now;
  456. furi_hal_rtc_get_datetime(&now);
  457. dcf77_refresh_simulated_data(app->state->simulation_data, now);
  458. break;
  459. case GPIO:
  460. app->state->gpio =
  461. gpio_start_listening(config->data_pin, config->data_mode == Inverted, app);
  462. break;
  463. default:
  464. break;
  465. }
  466. app->state->seconds_timer = furi_timer_alloc(dcf77_500ms_callback, FuriTimerTypePeriodic, app);
  467. furi_timer_start(app->state->seconds_timer, furi_kernel_get_tick_frequency() / 2);
  468. view_dispatcher_switch_to_view(app->view_dispatcher, LWCDCF77View);
  469. }
  470. void lwc_dcf77_scene_on_exit(void* context) {
  471. App* app = context;
  472. notification_message_block(app->notifications, &sequence_display_backlight_enforce_auto);
  473. furi_timer_stop(app->state->seconds_timer);
  474. furi_timer_free(app->state->seconds_timer);
  475. switch(lwc_get_protocol_config(app->state)->run_mode) {
  476. case Demo:
  477. minute_data_free(app->state->simulation_data);
  478. break;
  479. case GPIO:
  480. gpio_stop_listening(app->state->gpio);
  481. break;
  482. default:
  483. break;
  484. }
  485. }
  486. View* lwc_dcf77_scene_alloc() {
  487. View* view = view_alloc();
  488. view_allocate_model(view, ViewModelTypeLocking, sizeof(LWCViewModel));
  489. with_view_model(
  490. view, LWCViewModel * model, { model->minute_data = minute_data_alloc(60); }, true);
  491. view_set_context(view, view);
  492. view_set_input_callback(view, lwc_dcf77_scene_input);
  493. view_set_draw_callback(view, lwc_dcf77_scene_draw);
  494. return view;
  495. }
  496. void lwc_dcf77_scene_free(View* view) {
  497. furi_assert(view);
  498. with_view_model(view, LWCViewModel * model, { minute_data_free(model->minute_data); }, false);
  499. view_free(view);
  500. }