infrared_remote_app.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include <infrared_worker.h>
  4. #include <gui/gui.h>
  5. #include <input/input.h>
  6. #include <dialogs/dialogs.h>
  7. #include <ir_remote_icons.h>
  8. #include <notification/notification.h>
  9. #include <notification/notification_messages.h>
  10. #include "infrared_signal.h"
  11. #include "infrared_remote.h"
  12. #include "infrared_remote_button.h"
  13. #define TAG "ir_remote"
  14. #include <flipper_format/flipper_format.h>
  15. typedef struct {
  16. int status;
  17. ViewPort* view_port;
  18. FuriString* up_button;
  19. FuriString* down_button;
  20. FuriString* left_button;
  21. FuriString* right_button;
  22. FuriString* ok_button;
  23. FuriString* back_button;
  24. FuriString* up_hold_button;
  25. FuriString* down_hold_button;
  26. FuriString* left_hold_button;
  27. FuriString* right_hold_button;
  28. FuriString* ok_hold_button;
  29. InfraredWorker* infrared_worker;
  30. } IRApp;
  31. // Screen is 128x64 px
  32. static void app_draw_callback(Canvas* canvas, void* ctx) {
  33. // Show config is incorrect when cannot read the remote file
  34. // Showing button string in the screen, upper part is short press, lower part is long press
  35. IRApp* app = ctx;
  36. if(app->status) {
  37. canvas_clear(canvas);
  38. view_port_set_orientation(app->view_port, ViewPortOrientationHorizontal);
  39. canvas_set_font(canvas, FontPrimary);
  40. canvas_draw_str_aligned(canvas, 62, 5, AlignCenter, AlignTop, "Config is incorrect.");
  41. canvas_set_font(canvas, FontSecondary);
  42. canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignTop, "Please configure map.");
  43. canvas_draw_str_aligned(canvas, 62, 60, AlignCenter, AlignBottom, "Press Back to Exit.");
  44. } else {
  45. canvas_clear(canvas);
  46. view_port_set_orientation(app->view_port, ViewPortOrientationVertical);
  47. canvas_draw_icon(canvas, 1, 5, &I_ButtonUp_7x4);
  48. canvas_draw_icon(canvas, 1, 15, &I_ButtonDown_7x4);
  49. canvas_draw_icon(canvas, 2, 23, &I_ButtonLeft_4x7);
  50. canvas_draw_icon(canvas, 2, 33, &I_ButtonRight_4x7);
  51. canvas_draw_icon(canvas, 0, 42, &I_Ok_btn_9x9);
  52. canvas_draw_icon(canvas, 0, 53, &I_back_10px);
  53. //Labels
  54. canvas_set_font(canvas, FontSecondary);
  55. canvas_draw_str_aligned(
  56. canvas, 32, 8, AlignCenter, AlignCenter, furi_string_get_cstr(app->up_button));
  57. canvas_draw_str_aligned(
  58. canvas, 32, 18, AlignCenter, AlignCenter, furi_string_get_cstr(app->down_button));
  59. canvas_draw_str_aligned(
  60. canvas, 32, 28, AlignCenter, AlignCenter, furi_string_get_cstr(app->left_button));
  61. canvas_draw_str_aligned(
  62. canvas, 32, 38, AlignCenter, AlignCenter, furi_string_get_cstr(app->right_button));
  63. canvas_draw_str_aligned(
  64. canvas, 32, 48, AlignCenter, AlignCenter, furi_string_get_cstr(app->ok_button));
  65. canvas_draw_str_aligned(
  66. canvas, 32, 58, AlignCenter, AlignCenter, furi_string_get_cstr(app->back_button));
  67. canvas_draw_line(canvas, 0, 65, 64, 65);
  68. canvas_draw_icon(canvas, 1, 70, &I_ButtonUp_7x4);
  69. canvas_draw_icon(canvas, 1, 80, &I_ButtonDown_7x4);
  70. canvas_draw_icon(canvas, 2, 88, &I_ButtonLeft_4x7);
  71. canvas_draw_icon(canvas, 2, 98, &I_ButtonRight_4x7);
  72. canvas_draw_icon(canvas, 0, 107, &I_Ok_btn_9x9);
  73. canvas_draw_icon(canvas, 0, 118, &I_back_10px);
  74. canvas_draw_str_aligned(
  75. canvas, 32, 73, AlignCenter, AlignCenter, furi_string_get_cstr(app->up_hold_button));
  76. canvas_draw_str_aligned(
  77. canvas, 32, 83, AlignCenter, AlignCenter, furi_string_get_cstr(app->down_hold_button));
  78. canvas_draw_str_aligned(
  79. canvas, 32, 93, AlignCenter, AlignCenter, furi_string_get_cstr(app->left_hold_button));
  80. canvas_draw_str_aligned(
  81. canvas,
  82. 32,
  83. 103,
  84. AlignCenter,
  85. AlignCenter,
  86. furi_string_get_cstr(app->right_hold_button));
  87. canvas_draw_str_aligned(
  88. canvas, 32, 113, AlignCenter, AlignCenter, furi_string_get_cstr(app->ok_hold_button));
  89. canvas_draw_str_aligned(canvas, 32, 123, AlignCenter, AlignCenter, "Exit App");
  90. }
  91. }
  92. static void app_input_callback(InputEvent* input_event, void* ctx) {
  93. furi_assert(ctx);
  94. FuriMessageQueue* event_queue = ctx;
  95. furi_message_queue_put(event_queue, input_event, FuriWaitForever);
  96. }
  97. int32_t infrared_remote_app(void* p) {
  98. UNUSED(p);
  99. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
  100. // App button string
  101. IRApp* app = malloc(sizeof(IRApp));
  102. app->up_button = furi_string_alloc();
  103. app->down_button = furi_string_alloc();
  104. app->left_button = furi_string_alloc();
  105. app->right_button = furi_string_alloc();
  106. app->ok_button = furi_string_alloc();
  107. app->back_button = furi_string_alloc();
  108. app->up_hold_button = furi_string_alloc();
  109. app->down_hold_button = furi_string_alloc();
  110. app->left_hold_button = furi_string_alloc();
  111. app->right_hold_button = furi_string_alloc();
  112. app->ok_hold_button = furi_string_alloc();
  113. app->view_port = view_port_alloc();
  114. app->infrared_worker = infrared_worker_alloc();
  115. // Configure view port
  116. view_port_draw_callback_set(app->view_port, app_draw_callback, app);
  117. view_port_input_callback_set(app->view_port, app_input_callback, event_queue);
  118. // Register view port in GUI
  119. Gui* gui = furi_record_open(RECORD_GUI);
  120. gui_add_view_port(gui, app->view_port, GuiLayerFullscreen);
  121. InputEvent event;
  122. Storage* storage = furi_record_open(RECORD_STORAGE);
  123. FlipperFormat* ff = flipper_format_file_alloc(storage);
  124. DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
  125. DialogsFileBrowserOptions browser_options;
  126. dialog_file_browser_set_basic_options(&browser_options, ".txt", &I_sub1_10px);
  127. FuriString* map_file = furi_string_alloc();
  128. furi_string_set(map_file, "/ext/ir_remote");
  129. bool res = dialog_file_browser_show(dialogs, map_file, map_file, &browser_options);
  130. furi_record_close(RECORD_DIALOGS);
  131. // if user didn't choose anything, free everything and exit
  132. if(!res) {
  133. FURI_LOG_I(TAG, "exit");
  134. flipper_format_free(ff);
  135. furi_record_close(RECORD_STORAGE);
  136. furi_string_free(app->up_button);
  137. furi_string_free(app->down_button);
  138. furi_string_free(app->left_button);
  139. furi_string_free(app->right_button);
  140. furi_string_free(app->ok_button);
  141. furi_string_free(app->back_button);
  142. furi_string_free(app->up_hold_button);
  143. furi_string_free(app->down_hold_button);
  144. furi_string_free(app->left_hold_button);
  145. furi_string_free(app->right_hold_button);
  146. furi_string_free(app->ok_hold_button);
  147. view_port_enabled_set(app->view_port, false);
  148. gui_remove_view_port(gui, app->view_port);
  149. view_port_free(app->view_port);
  150. free(app);
  151. furi_message_queue_free(event_queue);
  152. furi_record_close(RECORD_GUI);
  153. return 255;
  154. }
  155. InfraredRemote* remote = infrared_remote_alloc();
  156. FuriString* remote_path = furi_string_alloc();
  157. InfraredSignal* up_signal = infrared_signal_alloc();
  158. InfraredSignal* down_signal = infrared_signal_alloc();
  159. InfraredSignal* left_signal = infrared_signal_alloc();
  160. InfraredSignal* right_signal = infrared_signal_alloc();
  161. InfraredSignal* ok_signal = infrared_signal_alloc();
  162. InfraredSignal* back_signal = infrared_signal_alloc();
  163. InfraredSignal* up_hold_signal = infrared_signal_alloc();
  164. InfraredSignal* down_hold_signal = infrared_signal_alloc();
  165. InfraredSignal* left_hold_signal = infrared_signal_alloc();
  166. InfraredSignal* right_hold_signal = infrared_signal_alloc();
  167. InfraredSignal* ok_hold_signal = infrared_signal_alloc();
  168. InfraredSignal* active_signal = NULL;
  169. bool is_transmitting = false;
  170. bool up_enabled = false;
  171. bool down_enabled = false;
  172. bool left_enabled = false;
  173. bool right_enabled = false;
  174. bool ok_enabled = false;
  175. bool back_enabled = false;
  176. bool up_hold_enabled = false;
  177. bool down_hold_enabled = false;
  178. bool left_hold_enabled = false;
  179. bool right_hold_enabled = false;
  180. bool ok_hold_enabled = false;
  181. if(!flipper_format_file_open_existing(ff, furi_string_get_cstr(map_file))) {
  182. FURI_LOG_E(TAG, "Could not open MAP file %s", furi_string_get_cstr(map_file));
  183. app->status = 1;
  184. } else {
  185. //Filename Assignment/Check Start
  186. if(!flipper_format_read_string(ff, "REMOTE", remote_path)) {
  187. FURI_LOG_E(TAG, "Could not read REMOTE string");
  188. app->status = 1;
  189. } else {
  190. if(!infrared_remote_load(remote, remote_path)) {
  191. FURI_LOG_E(TAG, "Could not load ir file: %s", furi_string_get_cstr(remote_path));
  192. app->status = 1;
  193. } else {
  194. FURI_LOG_I(TAG, "Loaded REMOTE file: %s", furi_string_get_cstr(remote_path));
  195. }
  196. }
  197. //assign variables to values within map file
  198. //set missing filenames to N/A
  199. //assign button signals
  200. size_t index = 0;
  201. if(!flipper_format_read_string(ff, "UP", app->up_button)) {
  202. FURI_LOG_W(TAG, "Could not read UP string");
  203. furi_string_set(app->up_button, "N/A");
  204. } else {
  205. if(!infrared_remote_find_button_by_name(
  206. remote, furi_string_get_cstr(app->up_button), &index)) {
  207. FURI_LOG_W(TAG, "Error");
  208. } else {
  209. up_signal =
  210. infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
  211. up_enabled = true;
  212. }
  213. }
  214. if(!flipper_format_read_string(ff, "DOWN", app->down_button)) {
  215. FURI_LOG_W(TAG, "Could not read DOWN string");
  216. furi_string_set(app->down_button, "N/A");
  217. } else {
  218. if(!infrared_remote_find_button_by_name(
  219. remote, furi_string_get_cstr(app->down_button), &index)) {
  220. FURI_LOG_W(TAG, "Error");
  221. } else {
  222. down_signal =
  223. infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
  224. down_enabled = true;
  225. }
  226. }
  227. if(!flipper_format_read_string(ff, "LEFT", app->left_button)) {
  228. FURI_LOG_W(TAG, "Could not read LEFT string");
  229. furi_string_set(app->left_button, "N/A");
  230. } else {
  231. if(!infrared_remote_find_button_by_name(
  232. remote, furi_string_get_cstr(app->left_button), &index)) {
  233. FURI_LOG_W(TAG, "Error");
  234. } else {
  235. left_signal =
  236. infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
  237. left_enabled = true;
  238. }
  239. }
  240. if(!flipper_format_read_string(ff, "RIGHT", app->right_button)) {
  241. FURI_LOG_W(TAG, "Could not read RIGHT string");
  242. furi_string_set(app->right_button, "N/A");
  243. } else {
  244. if(!infrared_remote_find_button_by_name(
  245. remote, furi_string_get_cstr(app->right_button), &index)) {
  246. FURI_LOG_W(TAG, "Error");
  247. } else {
  248. right_signal =
  249. infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
  250. right_enabled = true;
  251. }
  252. }
  253. if(!flipper_format_read_string(ff, "OK", app->ok_button)) {
  254. FURI_LOG_W(TAG, "Could not read OK string");
  255. furi_string_set(app->ok_button, "N/A");
  256. } else {
  257. if(!infrared_remote_find_button_by_name(
  258. remote, furi_string_get_cstr(app->ok_button), &index)) {
  259. FURI_LOG_W(TAG, "Error");
  260. } else {
  261. ok_signal =
  262. infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
  263. ok_enabled = true;
  264. }
  265. }
  266. if(!flipper_format_read_string(ff, "BACK", app->back_button)) {
  267. FURI_LOG_W(TAG, "Could not read BACK string");
  268. furi_string_set(app->back_button, "N/A");
  269. } else {
  270. if(!infrared_remote_find_button_by_name(
  271. remote, furi_string_get_cstr(app->back_button), &index)) {
  272. FURI_LOG_W(TAG, "Error");
  273. } else {
  274. back_signal =
  275. infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
  276. back_enabled = true;
  277. }
  278. }
  279. if(!flipper_format_read_string(ff, "UPHOLD", app->up_hold_button)) {
  280. FURI_LOG_W(TAG, "Could not read UPHOLD string");
  281. furi_string_set(app->up_hold_button, "N/A");
  282. } else {
  283. if(!infrared_remote_find_button_by_name(
  284. remote, furi_string_get_cstr(app->up_hold_button), &index)) {
  285. FURI_LOG_W(TAG, "Error");
  286. } else {
  287. up_hold_signal =
  288. infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
  289. up_hold_enabled = true;
  290. }
  291. }
  292. if(!flipper_format_read_string(ff, "DOWNHOLD", app->down_hold_button)) {
  293. FURI_LOG_W(TAG, "Could not read DOWNHOLD string");
  294. furi_string_set(app->down_hold_button, "N/A");
  295. } else {
  296. if(!infrared_remote_find_button_by_name(
  297. remote, furi_string_get_cstr(app->down_hold_button), &index)) {
  298. FURI_LOG_W(TAG, "Error");
  299. } else {
  300. down_hold_signal =
  301. infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
  302. down_hold_enabled = true;
  303. }
  304. }
  305. if(!flipper_format_read_string(ff, "LEFTHOLD", app->left_hold_button)) {
  306. FURI_LOG_W(TAG, "Could not read LEFTHOLD string");
  307. furi_string_set(app->left_hold_button, "N/A");
  308. } else {
  309. if(!infrared_remote_find_button_by_name(
  310. remote, furi_string_get_cstr(app->left_hold_button), &index)) {
  311. FURI_LOG_W(TAG, "Error");
  312. } else {
  313. left_hold_signal =
  314. infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
  315. left_hold_enabled = true;
  316. }
  317. }
  318. if(!flipper_format_read_string(ff, "RIGHTHOLD", app->right_hold_button)) {
  319. FURI_LOG_W(TAG, "Could not read RIGHTHOLD string");
  320. furi_string_set(app->right_hold_button, "N/A");
  321. } else {
  322. if(!infrared_remote_find_button_by_name(
  323. remote, furi_string_get_cstr(app->right_hold_button), &index)) {
  324. FURI_LOG_W(TAG, "Error");
  325. } else {
  326. right_hold_signal =
  327. infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
  328. right_hold_enabled = true;
  329. }
  330. }
  331. if(!flipper_format_read_string(ff, "OKHOLD", app->ok_hold_button)) {
  332. FURI_LOG_W(TAG, "Could not read OKHOLD string");
  333. furi_string_set(app->ok_hold_button, "N/A");
  334. } else {
  335. if(!infrared_remote_find_button_by_name(
  336. remote, furi_string_get_cstr(app->ok_hold_button), &index)) {
  337. FURI_LOG_W(TAG, "Error");
  338. } else {
  339. ok_hold_signal =
  340. infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
  341. ok_hold_enabled = true;
  342. }
  343. }
  344. }
  345. furi_string_free(remote_path);
  346. flipper_format_free(ff);
  347. furi_record_close(RECORD_STORAGE);
  348. bool running = true;
  349. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  350. if(app->status) {
  351. view_port_update(app->view_port);
  352. while(running) {
  353. if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
  354. if(event.type == InputTypeShort) {
  355. switch(event.key) {
  356. case InputKeyBack:
  357. running = false;
  358. break;
  359. default:
  360. break;
  361. }
  362. }
  363. }
  364. }
  365. } else {
  366. view_port_update(app->view_port);
  367. while(running) {
  368. if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
  369. // short press signal
  370. if(event.type == InputTypeShort) {
  371. switch(event.key) {
  372. case InputKeyUp:
  373. if(up_enabled) {
  374. active_signal = up_signal;
  375. FURI_LOG_I(TAG, "up");
  376. }
  377. break;
  378. case InputKeyDown:
  379. if(down_enabled) {
  380. active_signal = down_signal;
  381. FURI_LOG_I(TAG, "down");
  382. }
  383. break;
  384. case InputKeyRight:
  385. if(right_enabled) {
  386. active_signal = right_signal;
  387. FURI_LOG_I(TAG, "right");
  388. }
  389. break;
  390. case InputKeyLeft:
  391. if(left_enabled) {
  392. active_signal = left_signal;
  393. FURI_LOG_I(TAG, "left");
  394. }
  395. break;
  396. case InputKeyOk:
  397. if(ok_enabled) {
  398. active_signal = ok_signal;
  399. FURI_LOG_I(TAG, "ok");
  400. }
  401. break;
  402. case InputKeyBack:
  403. if(back_enabled) {
  404. active_signal = back_signal;
  405. FURI_LOG_I(TAG, "back");
  406. }
  407. break;
  408. default:
  409. running = false;
  410. break;
  411. }
  412. // long press signal
  413. } else if(event.type == InputTypeLong) {
  414. switch(event.key) {
  415. case InputKeyUp:
  416. if(up_hold_enabled) {
  417. active_signal = up_hold_signal;
  418. FURI_LOG_I(TAG, "up!");
  419. }
  420. break;
  421. case InputKeyDown:
  422. if(down_hold_enabled) {
  423. active_signal = down_hold_signal;
  424. FURI_LOG_I(TAG, "down!");
  425. }
  426. break;
  427. case InputKeyRight:
  428. if(right_hold_enabled) {
  429. active_signal = right_hold_signal;
  430. FURI_LOG_I(TAG, "right!");
  431. }
  432. break;
  433. case InputKeyLeft:
  434. if(left_hold_enabled) {
  435. active_signal = left_hold_signal;
  436. FURI_LOG_I(TAG, "left!");
  437. }
  438. break;
  439. case InputKeyOk:
  440. if(ok_hold_enabled) {
  441. active_signal = ok_hold_signal;
  442. FURI_LOG_I(TAG, "ok!");
  443. }
  444. break;
  445. default:
  446. running = false;
  447. break;
  448. }
  449. } else if(event.type == InputTypeRelease && is_transmitting) {
  450. notification_message(notification, &sequence_blink_stop);
  451. infrared_worker_tx_stop(app->infrared_worker);
  452. is_transmitting = false;
  453. active_signal = NULL;
  454. }
  455. if(active_signal != NULL &&
  456. (event.type == InputTypeShort || event.type == InputTypeLong)) {
  457. if(is_transmitting) {
  458. infrared_worker_tx_stop(app->infrared_worker);
  459. }
  460. if(infrared_signal_is_raw(active_signal)) {
  461. InfraredRawSignal* raw_signal =
  462. infrared_signal_get_raw_signal(active_signal);
  463. infrared_worker_set_raw_signal(
  464. app->infrared_worker,
  465. raw_signal->timings,
  466. raw_signal->timings_size,
  467. raw_signal->frequency,
  468. raw_signal->duty_cycle);
  469. } else {
  470. InfraredMessage* message = infrared_signal_get_message(active_signal);
  471. infrared_worker_set_decoded_signal(app->infrared_worker, message);
  472. }
  473. infrared_worker_tx_set_get_signal_callback(
  474. app->infrared_worker, infrared_worker_tx_get_signal_steady_callback, app);
  475. infrared_worker_tx_start(app->infrared_worker);
  476. notification_message(notification, &sequence_blink_start_magenta);
  477. is_transmitting = true;
  478. }
  479. }
  480. }
  481. }
  482. // Free all things
  483. furi_string_free(app->up_button);
  484. furi_string_free(app->down_button);
  485. furi_string_free(app->left_button);
  486. furi_string_free(app->right_button);
  487. furi_string_free(app->ok_button);
  488. furi_string_free(app->back_button);
  489. furi_string_free(app->up_hold_button);
  490. furi_string_free(app->down_hold_button);
  491. furi_string_free(app->left_hold_button);
  492. furi_string_free(app->right_hold_button);
  493. furi_string_free(app->ok_hold_button);
  494. if(is_transmitting) {
  495. infrared_worker_tx_stop(app->infrared_worker);
  496. notification_message(notification, &sequence_blink_stop);
  497. }
  498. infrared_worker_free(app->infrared_worker);
  499. infrared_remote_free(remote);
  500. view_port_enabled_set(app->view_port, false);
  501. gui_remove_view_port(gui, app->view_port);
  502. view_port_free(app->view_port);
  503. free(app);
  504. furi_message_queue_free(event_queue);
  505. furi_record_close(RECORD_NOTIFICATION);
  506. furi_record_close(RECORD_GUI);
  507. return 0;
  508. }