rolling_flaws.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /*
  2. Wish list:
  3. 1. variable_item_set_current_value_text allows for large text, but
  4. it trucates it to X characters. It would be nice it it scrolled.
  5. */
  6. #include <furi.h>
  7. #include <furi_hal.h>
  8. #include <gui/gui.h>
  9. #include <gui/view.h>
  10. #include <gui/view_dispatcher.h>
  11. #include <gui/modules/submenu.h>
  12. #include <gui/modules/widget.h>
  13. #include <gui/modules/variable_item_list.h>
  14. #include <notification/notification.h>
  15. #include <notification/notification_messages.h>
  16. #include "rolling_flaws_icons.h"
  17. #include <lib/subghz/receiver.h>
  18. #include <lib/subghz/protocols/protocol_items.h>
  19. #include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
  20. #include <lib/subghz/devices/devices.h>
  21. #include "rolling_flaws_subghz_receive.h"
  22. #include "rolling_flaws_settings.h"
  23. #include "rolling_flaws_about.h"
  24. #include "rolling_flaws_keeloq.h"
  25. #include "rolling_flaws_send_keeloq.h"
  26. #ifdef TAG
  27. #undef TAG
  28. #endif
  29. #define TAG "RollingFlawsSubGHzApp"
  30. // Comment this line if you don't want the backlight to be continuously on.
  31. #define BACKLIGHT_ALWAYS_ON yes
  32. typedef enum {
  33. RollingFlawsSubmenuIndexConfigure,
  34. RollingFlawsSubmenuIndexResetCountToZero,
  35. RollingFlawsSubmenuIndexTransmit,
  36. RollingFlawsSubmenuIndexReceive,
  37. RollingFlawsSubmenuIndexSyncRemote,
  38. RollingFlawsSubmenuIndexAbout,
  39. } RollingFlawsSubmenuIndex;
  40. typedef enum {
  41. RollingFlawsViewSubmenu,
  42. RollingFlawsViewConfigure,
  43. RollingFlawsViewReceiveSignals,
  44. RollingFlawsViewReceiveSync,
  45. RollingFlawsViewAbout,
  46. } RollingFlawsView;
  47. typedef enum {
  48. RollingFlawsEventIdReceivedSignal,
  49. RollingFlawsEventIdCycleSignal,
  50. } RollingFlawsEventId;
  51. static bool decode_packet(FuriString* buffer, void* ctx) {
  52. RollingFlaws* context = ctx;
  53. furi_hal_vibro_on(true);
  54. furi_delay_ms(50);
  55. furi_hal_vibro_on(false);
  56. if(furi_string_start_with_str(buffer, "KeeLoq 64bit")) {
  57. if(!furi_string_start_with_str(
  58. buffer, rolling_flaws_setting_protocol_base_name_get(context->model))) {
  59. FURI_LOG_I(TAG, "KeeLoq 64bit protocol is not enabled");
  60. return true;
  61. }
  62. decode_keeloq(context->model, buffer, false);
  63. } else {
  64. FURI_LOG_I(TAG, "Unknown protocol");
  65. }
  66. return true;
  67. }
  68. static bool sync_packet(FuriString* buffer, void* ctx) {
  69. RollingFlaws* context = ctx;
  70. if(furi_string_start_with_str(buffer, "KeeLoq 64bit")) {
  71. if(!furi_string_start_with_str(
  72. buffer, rolling_flaws_setting_protocol_base_name_get(context->model))) {
  73. FURI_LOG_I(TAG, "KeeLoq 64bit protocol is not enabled");
  74. return true;
  75. }
  76. decode_keeloq(context->model, buffer, true);
  77. view_dispatcher_send_custom_event(
  78. context->view_dispatcher, RollingFlawsEventIdReceivedSignal);
  79. return false;
  80. } else {
  81. FURI_LOG_I(TAG, "Unknown protocol");
  82. }
  83. return true;
  84. }
  85. /**
  86. * @brief Callback for navigation events
  87. * @details This function is called when user press back button. We return VIEW_NONE to
  88. * indicate that we want to exit the application.
  89. * @param context The context
  90. * @return next view id
  91. */
  92. uint32_t rolling_flaws_navigation_exit_callback(void* context) {
  93. UNUSED(context);
  94. return VIEW_NONE;
  95. }
  96. /**
  97. * @brief Callback for navigation events
  98. * @details This function is called when user press back button. We return VIEW_NONE to
  99. * indicate that we want to exit the application.
  100. * @param context The context
  101. * @return next view id
  102. */
  103. uint32_t rolling_flaws_navigation_submenu_callback(void* context) {
  104. UNUSED(context);
  105. return RollingFlawsViewSubmenu;
  106. }
  107. /**
  108. * @brief Callback for navigation events
  109. * @details This function is called when user press back button. We return VIEW_NONE to
  110. * indicate that we want to exit the application.
  111. * @param context The context
  112. * @return next view id
  113. */
  114. uint32_t rolling_flaws_navigation_submenu_stop_receiving_callback(void* context) {
  115. RollingFlaws* app = (RollingFlaws*)context;
  116. stop_listening(app->subghz);
  117. return RollingFlawsViewSubmenu;
  118. }
  119. uint32_t rolling_flaws_navigation_submenu_stop_sync_callback(void* context) {
  120. RollingFlaws* app = (RollingFlaws*)context;
  121. stop_listening(app->subghz);
  122. return RollingFlawsViewSubmenu;
  123. }
  124. bool rolling_flaws_view_dispatcher_custom_event_callback(void* context, uint32_t event) {
  125. FURI_LOG_I(TAG, "Custom event received: %ld", event);
  126. if(event == RollingFlawsEventIdReceivedSignal) {
  127. RollingFlaws* app = (RollingFlaws*)context;
  128. stop_listening(app->subghz);
  129. furi_hal_vibro_on(true);
  130. furi_delay_ms(200);
  131. furi_hal_vibro_on(false);
  132. furi_delay_ms(100);
  133. furi_hal_vibro_on(true);
  134. furi_delay_ms(100);
  135. furi_hal_vibro_on(false);
  136. view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewSubmenu);
  137. return true;
  138. }
  139. if(event == RollingFlawsEventIdCycleSignal) {
  140. RollingFlaws* app = (RollingFlaws*)context;
  141. stop_listening(app->subghz);
  142. uint32_t frequency = rolling_flaws_setting_frequency_get(app->model);
  143. app->model->opened = false;
  144. start_listening(app->subghz, frequency, decode_packet, app);
  145. }
  146. return false;
  147. }
  148. void rolling_flaws_submenu_callback(void* context, uint32_t index) {
  149. RollingFlaws* app = (RollingFlaws*)context;
  150. switch(index) {
  151. case RollingFlawsSubmenuIndexConfigure:
  152. view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewConfigure);
  153. break;
  154. case RollingFlawsSubmenuIndexResetCountToZero:
  155. app->model->count = 0x0;
  156. app->model->future_count = 0xFFFFFFFF;
  157. furi_hal_vibro_on(true);
  158. furi_delay_ms(200);
  159. furi_hal_vibro_on(false);
  160. break;
  161. case RollingFlawsSubmenuIndexTransmit:
  162. app->model->count++;
  163. app->model->future_count = 0xFFFFFFFF;
  164. send_keeloq_count(
  165. rolling_flaws_setting_fix_get(app->model),
  166. app->model->count - 2,
  167. rolling_flaws_setting_protocol_mf_name_get(app->model),
  168. rolling_flaws_setting_frequency_get(app->model));
  169. furi_hal_vibro_on(true);
  170. furi_delay_ms(100);
  171. furi_hal_vibro_on(false);
  172. furi_delay_ms(100);
  173. furi_hal_vibro_on(true);
  174. furi_delay_ms(200);
  175. furi_hal_vibro_on(false);
  176. break;
  177. case RollingFlawsSubmenuIndexReceive: {
  178. uint32_t frequency = rolling_flaws_setting_frequency_get(app->model);
  179. app->model->opened = false;
  180. start_listening(app->subghz, frequency, decode_packet, app);
  181. view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewReceiveSignals);
  182. } break;
  183. case RollingFlawsSubmenuIndexSyncRemote: {
  184. uint32_t frequency = rolling_flaws_setting_frequency_get(app->model);
  185. start_listening(app->subghz, frequency, sync_packet, app);
  186. view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewReceiveSync);
  187. } break;
  188. case RollingFlawsSubmenuIndexAbout:
  189. view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewAbout);
  190. break;
  191. default:
  192. break;
  193. }
  194. }
  195. void rolling_flaws_receive_sync_draw_callback(Canvas* canvas, void* model) {
  196. UNUSED(model);
  197. canvas_set_font(canvas, FontPrimary);
  198. canvas_draw_str(canvas, 13, 30, "Syncing rolling code:");
  199. canvas_set_font(canvas, FontSecondary);
  200. canvas_draw_str(canvas, 13, 45, "Press remote button now.");
  201. }
  202. void rolling_flaws_receive_signal_draw_callback(Canvas* canvas, void* model) {
  203. RollingFlawsModel* my_model = ((RollingFlawsRefModel*)model)->model;
  204. FuriString* str = furi_string_alloc();
  205. canvas_set_bitmap_mode(canvas, true);
  206. canvas_set_font(canvas, FontPrimary);
  207. canvas_draw_str(canvas, 13, 8, "Rolling code receiver");
  208. canvas_set_font(canvas, FontSecondary);
  209. furi_string_printf(str, "Count: %04X", (uint16_t)my_model->count);
  210. canvas_draw_str(canvas, 2, 34, furi_string_get_cstr(str));
  211. canvas_set_font(canvas, FontSecondary);
  212. if(my_model->future_count > 0xFFFF) {
  213. canvas_draw_str(canvas, 2, 44, "Future: none");
  214. } else {
  215. furi_string_printf(str, "Future: %04X", (uint16_t)my_model->future_count);
  216. canvas_draw_str(canvas, 2, 44, furi_string_get_cstr(str));
  217. }
  218. canvas_set_font(canvas, FontSecondary);
  219. canvas_draw_str(canvas, 3, 20, rolling_flaws_setting_protocol_display_name_get(my_model));
  220. canvas_set_font(canvas, FontSecondary);
  221. furi_string_printf(str, "Fix: %08lX", rolling_flaws_setting_fix_get(my_model));
  222. canvas_draw_str(canvas, 2, 54, furi_string_get_cstr(str));
  223. canvas_set_font(canvas, FontSecondary);
  224. furi_string_printf(str, "RX: %s", furi_string_get_cstr(my_model->key));
  225. canvas_draw_str(canvas, 2, 64, furi_string_get_cstr(str));
  226. if(my_model->opened) {
  227. canvas_draw_icon(canvas, 100, 15, &I_Unlock_10x8);
  228. canvas_set_font(canvas, FontPrimary);
  229. canvas_draw_str(canvas, 82, 33, "OPENED!");
  230. } else {
  231. canvas_draw_icon(canvas, 100, 15, &I_Lock_10x8);
  232. canvas_set_font(canvas, FontPrimary);
  233. canvas_draw_str(canvas, 85, 33, "CLOSED");
  234. }
  235. canvas_set_font(canvas, FontSecondary);
  236. furi_string_printf(str, "%sMHz", rolling_flaws_setting_frequency_name_get(my_model));
  237. canvas_draw_str(canvas, 75, 43, furi_string_get_cstr(str));
  238. canvas_set_font(canvas, FontSecondary);
  239. canvas_draw_str(canvas, 82, 54, furi_string_get_cstr(my_model->status));
  240. furi_string_free(str);
  241. }
  242. bool rolling_flaws_view_input_ignore_callback(InputEvent* event, void* context) {
  243. UNUSED(event);
  244. UNUSED(context);
  245. return false;
  246. }
  247. bool rolling_flaws_view_input_callback(InputEvent* event, void* context) {
  248. RollingFlaws* app = (RollingFlaws*)context;
  249. RollingFlawsModel* my_model = app->model;
  250. FURI_LOG_I(TAG, "Input event received: %d", event->type);
  251. if(event->type == InputTypeShort) {
  252. FURI_LOG_I(TAG, "Input key: %d", event->key);
  253. if(event->key == InputKeyLeft) {
  254. if(my_model->count == 0) {
  255. my_model->count = 0xFFFF;
  256. } else {
  257. my_model->count--;
  258. }
  259. __gui_redraw();
  260. return true;
  261. }
  262. if(event->key == InputKeyRight) {
  263. if(my_model->count == 0xFFFF) {
  264. my_model->count = 0;
  265. } else {
  266. my_model->count++;
  267. }
  268. __gui_redraw();
  269. return true;
  270. }
  271. if(event->key == InputKeyOk) {
  272. my_model->opened = false;
  273. view_dispatcher_send_custom_event(
  274. app->view_dispatcher, RollingFlawsEventIdCycleSignal);
  275. __gui_redraw();
  276. return true;
  277. }
  278. }
  279. return false;
  280. }
  281. RollingFlaws* rolling_flaws_alloc() {
  282. RollingFlaws* app = (RollingFlaws*)malloc(sizeof(RollingFlaws));
  283. Gui* gui = furi_record_open(RECORD_GUI);
  284. app->subghz = rolling_flaws_subghz_alloc();
  285. app->model = malloc(sizeof(RollingFlawsModel));
  286. app->model->key = furi_string_alloc();
  287. app->model->custom_mf = furi_string_alloc();
  288. app->model->status = furi_string_alloc();
  289. app->model->custom_fix = 0x24321234;
  290. app->model->count = 0x0;
  291. app->model->future_count = 0xFFFFFFFF;
  292. app->model->opened = false;
  293. app->view_dispatcher = view_dispatcher_alloc();
  294. view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
  295. view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
  296. view_dispatcher_set_custom_event_callback(
  297. app->view_dispatcher, rolling_flaws_view_dispatcher_custom_event_callback);
  298. app->submenu = submenu_alloc();
  299. submenu_add_item(
  300. app->submenu,
  301. "Config",
  302. RollingFlawsSubmenuIndexConfigure,
  303. rolling_flaws_submenu_callback,
  304. app);
  305. submenu_add_item(
  306. app->submenu,
  307. "Reset count to 0",
  308. RollingFlawsSubmenuIndexResetCountToZero,
  309. rolling_flaws_submenu_callback,
  310. app);
  311. submenu_add_item(
  312. app->submenu,
  313. "Transmit Signal",
  314. RollingFlawsSubmenuIndexTransmit,
  315. rolling_flaws_submenu_callback,
  316. app);
  317. submenu_add_item(
  318. app->submenu,
  319. "Receive Signals",
  320. RollingFlawsSubmenuIndexReceive,
  321. rolling_flaws_submenu_callback,
  322. app);
  323. submenu_add_item(
  324. app->submenu,
  325. "Sync Remote",
  326. RollingFlawsSubmenuIndexSyncRemote,
  327. rolling_flaws_submenu_callback,
  328. app);
  329. submenu_add_item(
  330. app->submenu, "About", RollingFlawsSubmenuIndexAbout, rolling_flaws_submenu_callback, app);
  331. view_set_previous_callback(
  332. submenu_get_view(app->submenu), rolling_flaws_navigation_exit_callback);
  333. view_dispatcher_add_view(
  334. app->view_dispatcher, RollingFlawsViewSubmenu, submenu_get_view(app->submenu));
  335. view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewSubmenu);
  336. rolling_flaw_populate_variable_item_list(app);
  337. view_set_previous_callback(
  338. variable_item_list_get_view(app->variable_item_list_config),
  339. rolling_flaws_navigation_submenu_callback);
  340. view_dispatcher_add_view(
  341. app->view_dispatcher,
  342. RollingFlawsViewConfigure,
  343. variable_item_list_get_view(app->variable_item_list_config));
  344. app->view_receive_signals = view_alloc();
  345. view_set_context(app->view_receive_signals, app);
  346. view_set_draw_callback(app->view_receive_signals, rolling_flaws_receive_signal_draw_callback);
  347. view_set_input_callback(app->view_receive_signals, rolling_flaws_view_input_callback);
  348. view_set_previous_callback(
  349. app->view_receive_signals, rolling_flaws_navigation_submenu_stop_receiving_callback);
  350. view_allocate_model(
  351. app->view_receive_signals, ViewModelTypeLockFree, sizeof(RollingFlawsRefModel));
  352. RollingFlawsRefModel* refmodel = view_get_model(app->view_receive_signals);
  353. refmodel->model = app->model;
  354. view_dispatcher_add_view(
  355. app->view_dispatcher, RollingFlawsViewReceiveSignals, app->view_receive_signals);
  356. app->view_receive_sync = view_alloc();
  357. view_set_context(app->view_receive_sync, app);
  358. view_set_draw_callback(app->view_receive_sync, rolling_flaws_receive_sync_draw_callback);
  359. view_set_input_callback(app->view_receive_sync, rolling_flaws_view_input_ignore_callback);
  360. view_set_previous_callback(
  361. app->view_receive_sync, rolling_flaws_navigation_submenu_stop_sync_callback);
  362. view_allocate_model(
  363. app->view_receive_sync, ViewModelTypeLockFree, sizeof(RollingFlawsRefModel));
  364. refmodel = view_get_model(app->view_receive_sync);
  365. refmodel->model = app->model;
  366. view_dispatcher_add_view(
  367. app->view_dispatcher, RollingFlawsViewReceiveSync, app->view_receive_sync);
  368. app->widget_about = widget_alloc();
  369. widget_add_text_scroll_element(app->widget_about, 0, 0, 128, 64, ROLLING_FLAWS_ABOUT_TEXT);
  370. view_set_previous_callback(
  371. widget_get_view(app->widget_about), rolling_flaws_navigation_submenu_callback);
  372. view_dispatcher_add_view(
  373. app->view_dispatcher, RollingFlawsViewAbout, widget_get_view(app->widget_about));
  374. app->notifications = furi_record_open(RECORD_NOTIFICATION);
  375. #ifdef BACKLIGHT_ALWAYS_ON
  376. notification_message(app->notifications, &sequence_display_backlight_enforce_on);
  377. #endif
  378. return app;
  379. }
  380. void rolling_flaws_free(RollingFlaws* app) {
  381. #ifdef BACKLIGHT_ALWAYS_ON
  382. notification_message(app->notifications, &sequence_display_backlight_enforce_auto);
  383. #endif
  384. furi_record_close(RECORD_NOTIFICATION);
  385. view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewAbout);
  386. widget_free(app->widget_about);
  387. view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewReceiveSignals);
  388. view_free(app->view_receive_signals);
  389. view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewReceiveSync);
  390. view_free(app->view_receive_sync);
  391. view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewConfigure);
  392. variable_item_list_free(app->variable_item_list_config);
  393. view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewSubmenu);
  394. submenu_free(app->submenu);
  395. view_dispatcher_free(app->view_dispatcher);
  396. furi_record_close(RECORD_GUI);
  397. rolling_flaws_subghz_free(app->subghz);
  398. free(app);
  399. }
  400. int32_t rolling_flaws_app(void* p) {
  401. UNUSED(p);
  402. RollingFlaws* app = rolling_flaws_alloc();
  403. view_dispatcher_run(app->view_dispatcher);
  404. rolling_flaws_free(app);
  405. return 0;
  406. }