rolling_flaws.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  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. } RollingFlawsEventId;
  50. static bool decode_packet(FuriString* buffer, void* ctx) {
  51. RollingFlaws* context = ctx;
  52. if(furi_string_start_with_str(buffer, "KeeLoq 64bit")) {
  53. if(!furi_string_start_with_str(
  54. buffer, rolling_flaws_setting_protocol_base_name_get(context->model))) {
  55. FURI_LOG_I(TAG, "KeeLoq 64bit protocol is not enabled");
  56. return true;
  57. }
  58. decode_keeloq(context->model, buffer, false);
  59. } else {
  60. FURI_LOG_I(TAG, "Unknown protocol");
  61. }
  62. return true;
  63. }
  64. static bool sync_packet(FuriString* buffer, void* ctx) {
  65. RollingFlaws* context = ctx;
  66. if(furi_string_start_with_str(buffer, "KeeLoq 64bit")) {
  67. if(!furi_string_start_with_str(
  68. buffer, rolling_flaws_setting_protocol_base_name_get(context->model))) {
  69. FURI_LOG_I(TAG, "KeeLoq 64bit protocol is not enabled");
  70. return true;
  71. }
  72. decode_keeloq(context->model, buffer, true);
  73. view_dispatcher_send_custom_event(
  74. context->view_dispatcher, RollingFlawsEventIdReceivedSignal);
  75. return false;
  76. } else {
  77. FURI_LOG_I(TAG, "Unknown protocol");
  78. }
  79. return true;
  80. }
  81. /**
  82. * @brief Callback for navigation events
  83. * @details This function is called when user press back button. We return VIEW_NONE to
  84. * indicate that we want to exit the application.
  85. * @param context The context
  86. * @return next view id
  87. */
  88. uint32_t rolling_flaws_navigation_exit_callback(void* context) {
  89. UNUSED(context);
  90. return VIEW_NONE;
  91. }
  92. /**
  93. * @brief Callback for navigation events
  94. * @details This function is called when user press back button. We return VIEW_NONE to
  95. * indicate that we want to exit the application.
  96. * @param context The context
  97. * @return next view id
  98. */
  99. uint32_t rolling_flaws_navigation_submenu_callback(void* context) {
  100. UNUSED(context);
  101. return RollingFlawsViewSubmenu;
  102. }
  103. /**
  104. * @brief Callback for navigation events
  105. * @details This function is called when user press back button. We return VIEW_NONE to
  106. * indicate that we want to exit the application.
  107. * @param context The context
  108. * @return next view id
  109. */
  110. uint32_t rolling_flaws_navigation_submenu_stop_receiving_callback(void* context) {
  111. RollingFlaws* app = (RollingFlaws*)context;
  112. stop_listening(app->subghz);
  113. return RollingFlawsViewSubmenu;
  114. }
  115. uint32_t rolling_flaws_navigation_submenu_stop_sync_callback(void* context) {
  116. RollingFlaws* app = (RollingFlaws*)context;
  117. stop_listening(app->subghz);
  118. return RollingFlawsViewSubmenu;
  119. }
  120. bool rolling_flaws_view_dispatcher_custom_event_callback(void* context, uint32_t event) {
  121. FURI_LOG_I(TAG, "Custom event received: %ld", event);
  122. if(event == RollingFlawsEventIdReceivedSignal) {
  123. RollingFlaws* app = (RollingFlaws*)context;
  124. stop_listening(app->subghz);
  125. furi_hal_vibro_on(true);
  126. furi_delay_ms(200);
  127. furi_hal_vibro_on(false);
  128. furi_delay_ms(100);
  129. furi_hal_vibro_on(true);
  130. furi_delay_ms(100);
  131. furi_hal_vibro_on(false);
  132. view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewSubmenu);
  133. return true;
  134. }
  135. return false;
  136. }
  137. void rolling_flaws_submenu_callback(void* context, uint32_t index) {
  138. RollingFlaws* app = (RollingFlaws*)context;
  139. switch(index) {
  140. case RollingFlawsSubmenuIndexConfigure:
  141. view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewConfigure);
  142. break;
  143. case RollingFlawsSubmenuIndexResetCountToZero:
  144. app->model->count = 0x0;
  145. app->model->future_count = 0xFFFFFFFF;
  146. furi_hal_vibro_on(true);
  147. furi_delay_ms(200);
  148. furi_hal_vibro_on(false);
  149. break;
  150. case RollingFlawsSubmenuIndexTransmit:
  151. app->model->count++;
  152. app->model->future_count = 0xFFFFFFFF;
  153. send_keeloq_count(
  154. rolling_flaws_setting_fix_get(app->model),
  155. app->model->count - 2,
  156. rolling_flaws_setting_protocol_mf_name_get(app->model),
  157. rolling_flaws_setting_frequency_get(app->model));
  158. furi_hal_vibro_on(true);
  159. furi_delay_ms(100);
  160. furi_hal_vibro_on(false);
  161. furi_delay_ms(100);
  162. furi_hal_vibro_on(true);
  163. furi_delay_ms(200);
  164. furi_hal_vibro_on(false);
  165. break;
  166. case RollingFlawsSubmenuIndexReceive: {
  167. uint32_t frequency = rolling_flaws_setting_frequency_get(app->model);
  168. app->model->opened = false;
  169. start_listening(app->subghz, frequency, decode_packet, app);
  170. view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewReceiveSignals);
  171. } break;
  172. case RollingFlawsSubmenuIndexSyncRemote: {
  173. uint32_t frequency = rolling_flaws_setting_frequency_get(app->model);
  174. start_listening(app->subghz, frequency, sync_packet, app);
  175. view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewReceiveSync);
  176. } break;
  177. case RollingFlawsSubmenuIndexAbout:
  178. view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewAbout);
  179. break;
  180. default:
  181. break;
  182. }
  183. }
  184. void rolling_flaws_receive_sync_draw_callback(Canvas* canvas, void* model) {
  185. UNUSED(model);
  186. canvas_set_font(canvas, FontPrimary);
  187. canvas_draw_str(canvas, 13, 30, "Syncing rolling code:");
  188. canvas_set_font(canvas, FontSecondary);
  189. canvas_draw_str(canvas, 13, 45, "Press remote button now.");
  190. }
  191. void rolling_flaws_receive_signal_draw_callback(Canvas* canvas, void* model) {
  192. RollingFlawsModel* my_model = ((RollingFlawsRefModel*)model)->model;
  193. FuriString* str = furi_string_alloc(32);
  194. canvas_set_bitmap_mode(canvas, 1);
  195. canvas_set_font(canvas, FontPrimary);
  196. canvas_draw_str(canvas, 13, 8, "Rolling code receiver");
  197. canvas_set_font(canvas, FontSecondary);
  198. furi_string_printf(str, "Count: %04X", (uint16_t)my_model->count);
  199. canvas_draw_str(canvas, 2, 34, furi_string_get_cstr(str));
  200. canvas_set_font(canvas, FontSecondary);
  201. if(my_model->future_count > 0xFFFF) {
  202. canvas_draw_str(canvas, 2, 44, "Future: none");
  203. } else {
  204. furi_string_printf(str, "Future: %04X", (uint16_t)my_model->future_count);
  205. canvas_draw_str(canvas, 2, 44, furi_string_get_cstr(str));
  206. }
  207. canvas_set_font(canvas, FontSecondary);
  208. canvas_draw_str(canvas, 3, 20, rolling_flaws_setting_protocol_display_name_get(my_model));
  209. canvas_set_font(canvas, FontSecondary);
  210. furi_string_printf(str, "Fix: %08lX", rolling_flaws_setting_fix_get(my_model));
  211. canvas_draw_str(canvas, 2, 54, furi_string_get_cstr(str));
  212. canvas_set_font(canvas, FontSecondary);
  213. furi_string_printf(str, "RX: %s", furi_string_get_cstr(my_model->key));
  214. canvas_draw_str(canvas, 2, 64, furi_string_get_cstr(str));
  215. if(my_model->opened) {
  216. canvas_draw_icon(canvas, 100, 15, &I_Unlock_10x8);
  217. canvas_set_font(canvas, FontPrimary);
  218. canvas_draw_str(canvas, 82, 33, "OPENED!");
  219. } else {
  220. canvas_draw_icon(canvas, 100, 15, &I_Lock_10x8);
  221. canvas_set_font(canvas, FontPrimary);
  222. canvas_draw_str(canvas, 85, 33, "CLOSED");
  223. }
  224. canvas_set_font(canvas, FontSecondary);
  225. furi_string_printf(str, "%sMHz", rolling_flaws_setting_frequency_name_get(my_model));
  226. canvas_draw_str(canvas, 75, 43, furi_string_get_cstr(str));
  227. canvas_set_font(canvas, FontSecondary);
  228. canvas_draw_str(canvas, 82, 54, furi_string_get_cstr(my_model->status));
  229. furi_string_free(str);
  230. }
  231. bool rolling_flaws_view_input_callback(InputEvent* event, void* context) {
  232. UNUSED(context);
  233. UNUSED(event);
  234. return false;
  235. }
  236. RollingFlaws* rolling_flaws_alloc() {
  237. RollingFlaws* app = (RollingFlaws*)malloc(sizeof(RollingFlaws));
  238. Gui* gui = furi_record_open(RECORD_GUI);
  239. app->subghz = rolling_flaws_subghz_alloc();
  240. app->model = malloc(sizeof(RollingFlawsModel));
  241. app->model->key = furi_string_alloc();
  242. app->model->custom_mf = furi_string_alloc();
  243. app->model->status = furi_string_alloc();
  244. app->model->custom_fix = 0x24321234;
  245. app->model->count = 0x0;
  246. app->model->future_count = 0xFFFFFFFF;
  247. app->model->opened = false;
  248. app->view_dispatcher = view_dispatcher_alloc();
  249. view_dispatcher_enable_queue(app->view_dispatcher);
  250. view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
  251. view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
  252. view_dispatcher_set_custom_event_callback(
  253. app->view_dispatcher, rolling_flaws_view_dispatcher_custom_event_callback);
  254. app->submenu = submenu_alloc();
  255. submenu_add_item(
  256. app->submenu,
  257. "Config",
  258. RollingFlawsSubmenuIndexConfigure,
  259. rolling_flaws_submenu_callback,
  260. app);
  261. submenu_add_item(
  262. app->submenu,
  263. "Reset count to 0",
  264. RollingFlawsSubmenuIndexResetCountToZero,
  265. rolling_flaws_submenu_callback,
  266. app);
  267. submenu_add_item(
  268. app->submenu,
  269. "Transmit Signal",
  270. RollingFlawsSubmenuIndexTransmit,
  271. rolling_flaws_submenu_callback,
  272. app);
  273. submenu_add_item(
  274. app->submenu,
  275. "Receive Signals",
  276. RollingFlawsSubmenuIndexReceive,
  277. rolling_flaws_submenu_callback,
  278. app);
  279. submenu_add_item(
  280. app->submenu,
  281. "Sync Remote",
  282. RollingFlawsSubmenuIndexSyncRemote,
  283. rolling_flaws_submenu_callback,
  284. app);
  285. submenu_add_item(
  286. app->submenu, "About", RollingFlawsSubmenuIndexAbout, rolling_flaws_submenu_callback, app);
  287. view_set_previous_callback(
  288. submenu_get_view(app->submenu), rolling_flaws_navigation_exit_callback);
  289. view_dispatcher_add_view(
  290. app->view_dispatcher, RollingFlawsViewSubmenu, submenu_get_view(app->submenu));
  291. view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewSubmenu);
  292. rolling_flaw_populate_variable_item_list(app);
  293. view_set_previous_callback(
  294. variable_item_list_get_view(app->variable_item_list_config),
  295. rolling_flaws_navigation_submenu_callback);
  296. view_dispatcher_add_view(
  297. app->view_dispatcher,
  298. RollingFlawsViewConfigure,
  299. variable_item_list_get_view(app->variable_item_list_config));
  300. app->view_receive_signals = view_alloc();
  301. view_set_context(app->view_receive_signals, app);
  302. view_set_draw_callback(app->view_receive_signals, rolling_flaws_receive_signal_draw_callback);
  303. view_set_input_callback(app->view_receive_signals, rolling_flaws_view_input_callback);
  304. view_set_previous_callback(
  305. app->view_receive_signals, rolling_flaws_navigation_submenu_stop_receiving_callback);
  306. view_allocate_model(
  307. app->view_receive_signals, ViewModelTypeLockFree, sizeof(RollingFlawsRefModel));
  308. RollingFlawsRefModel* refmodel = view_get_model(app->view_receive_signals);
  309. refmodel->model = app->model;
  310. view_dispatcher_add_view(
  311. app->view_dispatcher, RollingFlawsViewReceiveSignals, app->view_receive_signals);
  312. app->view_receive_sync = view_alloc();
  313. view_set_context(app->view_receive_sync, app);
  314. view_set_draw_callback(app->view_receive_sync, rolling_flaws_receive_sync_draw_callback);
  315. view_set_input_callback(app->view_receive_sync, rolling_flaws_view_input_callback);
  316. view_set_previous_callback(
  317. app->view_receive_sync, rolling_flaws_navigation_submenu_stop_sync_callback);
  318. view_allocate_model(
  319. app->view_receive_sync, ViewModelTypeLockFree, sizeof(RollingFlawsRefModel));
  320. refmodel = view_get_model(app->view_receive_sync);
  321. refmodel->model = app->model;
  322. view_dispatcher_add_view(
  323. app->view_dispatcher, RollingFlawsViewReceiveSync, app->view_receive_sync);
  324. app->widget_about = widget_alloc();
  325. widget_add_text_scroll_element(app->widget_about, 0, 0, 128, 64, ROLLING_FLAWS_ABOUT_TEXT);
  326. view_set_previous_callback(
  327. widget_get_view(app->widget_about), rolling_flaws_navigation_submenu_callback);
  328. view_dispatcher_add_view(
  329. app->view_dispatcher, RollingFlawsViewAbout, widget_get_view(app->widget_about));
  330. app->notifications = furi_record_open(RECORD_NOTIFICATION);
  331. #ifdef BACKLIGHT_ALWAYS_ON
  332. notification_message(app->notifications, &sequence_display_backlight_enforce_on);
  333. #endif
  334. return app;
  335. }
  336. void rolling_flaws_free(RollingFlaws* app) {
  337. #ifdef BACKLIGHT_ALWAYS_ON
  338. notification_message(app->notifications, &sequence_display_backlight_enforce_auto);
  339. #endif
  340. furi_record_close(RECORD_NOTIFICATION);
  341. view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewAbout);
  342. widget_free(app->widget_about);
  343. view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewReceiveSignals);
  344. view_free(app->view_receive_signals);
  345. view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewReceiveSync);
  346. view_free(app->view_receive_sync);
  347. view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewConfigure);
  348. variable_item_list_free(app->variable_item_list_config);
  349. view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewSubmenu);
  350. submenu_free(app->submenu);
  351. view_dispatcher_free(app->view_dispatcher);
  352. furi_record_close(RECORD_GUI);
  353. rolling_flaws_subghz_free(app->subghz);
  354. free(app);
  355. }
  356. int32_t rolling_flaws_app(void* p) {
  357. UNUSED(p);
  358. RollingFlaws* app = rolling_flaws_alloc();
  359. view_dispatcher_run(app->view_dispatcher);
  360. rolling_flaws_free(app);
  361. return 0;
  362. }