nrf24scan.c 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114
  1. //
  2. // Written by vad7, 20.11.2022.
  3. //
  4. #include "nrf24scan.h"
  5. #include <furi.h>
  6. #include <furi_hal.h>
  7. #include <gui/gui.h>
  8. #include <dialogs/dialogs.h>
  9. #include <input/input.h>
  10. #include <stdlib.h>
  11. #include <dolphin/dolphin.h>
  12. #include <nrf24.h>
  13. #include <u8g2.h>
  14. #define TAG "nrf24scan"
  15. #define VERSION "1.5"
  16. #define MAX_CHANNEL 125
  17. #define MAX_ADDR 6
  18. #define SCAN_APP_PATH_FOLDER "/ext/nrf24scan"
  19. #define SETTINGS_FILENAME "settings.txt" // File format (1 parameter per line):
  20. // Rate: 0/1/2 - rate in Mbps (=0.25/1/2)
  21. // Ch: 0..125 - default channel
  22. // ESB: 0/1 (1 - Enhanced ShockBurst)
  23. // DPL: 0/1 (1 - Dynamic Payload Length)
  24. // CRC: 0/1/2 (CRC length)
  25. // Payload: 1..32 (bytes)
  26. // P0: address P0 in hex (5 byte, LSB last)
  27. // P1: address P1 in hex (5 byte, LSB last)
  28. // P2: address P2, LSB in hex (1 byte)
  29. // P3: address P3, LSB in hex (1 byte)
  30. // P4: address P4, LSB in hex (1 byte)
  31. // P5: address P5, LSB in hex (1 byte)
  32. // captured data in raw format, first byte = address # 0..5, Payload len if DPL
  33. // ... up to MAX_LOG_RECORDS-1
  34. #define LOG_FILENAME "log"
  35. #define LOG_FILEEXT ".txt"
  36. #define MAX_LOG_RECORDS 100
  37. #define LOG_REC_SIZE 33 // max packet size
  38. #define VIEW_LOG_MAX_X 22
  39. #define VIEW_LOG_WIDTH_B 10 // bytes
  40. const char SettingsFld_Rate[] = "Rate:";
  41. const char SettingsFld_Ch[] = "Ch:";
  42. const char SettingsFld_ESB[] = "ESB:";
  43. const char SettingsFld_DPL[] = "DPL:";
  44. const char SettingsFld_CRC[] = "CRC:";
  45. const char SettingsFld_Payload[] = "Payload:";
  46. char SettingsFld_Addr = 'P';
  47. Nrf24Scan* APP;
  48. uint8_t what_doing = 0; // 0 - setup, 1 - view log, 2 - view addresses
  49. uint8_t what_to_do = 1; // 0 - view, 1 - view & scan
  50. uint32_t key_press_seq_ok = 0;
  51. uint8_t save_settings = 0;
  52. char screen_buf[64];
  53. char addr_file_name[32];
  54. uint8_t NRF_rate = 1; // 0 - 250Kbps, 1 - 1Mbps, 2 - 2Mbps
  55. uint8_t NRF_channel = 0; // 0..125
  56. uint8_t NRF_ESB = 0; // 0 - ShockBurst, 1 - Enhanced ShockBurst
  57. uint8_t NRF_DPL = 0; // 1 - Dynamic Payload Length
  58. uint8_t NRF_CRC = 0; // 1 - No, 1 - CRC 1byte, 2 - CRC 2byte
  59. uint8_t NRF_Payload = 32; // len in bytes, max 32
  60. uint8_t NRF_AA_OFF = 0; // Disable Auto Acknowledgement
  61. bool NRF_ERROR = 0;
  62. struct {
  63. uint8_t addr_P0[5]; // MSB first
  64. uint8_t addr_P1[5]; // MSB first
  65. uint8_t addr_P2; // LSB only, MSB bytes equal addr_P1
  66. uint8_t addr_P3; // LSB only, MSB bytes equal addr_P1
  67. uint8_t addr_P4; // LSB only, MSB bytes equal addr_P1
  68. uint8_t addr_P5; // LSB only, MSB bytes equal addr_P1
  69. uint8_t addr_len; // 2..5
  70. uint8_t addr_count;
  71. } addrs;
  72. int8_t log_to_file = 0; // 0 - no, 1 - yes(new), 2 - append, -1 - only clear
  73. uint16_t log_arr_idx;
  74. uint16_t view_log_arr_idx = 0;
  75. uint16_t view_log_arr_x = 0;
  76. bool save_to_new_log = true;
  77. uint16_t last_packet_send = -1;
  78. uint8_t last_packet_send_st = 0;
  79. int16_t find_channel_period = 0; // sec
  80. uint8_t menu_selected = 0;
  81. uint32_t start_time;
  82. uint8_t view_log_decode_PCF =
  83. 0; // view log: 1 - decode packet control field (9b) when ESB off. After pipe # (hex): <Payload len 6b><PID_2b+NO_ACK_1b>
  84. uint8_t view_log_decode_CRC = 0; // CRC bytes - 1/2, 0 - none
  85. #define menu_selected_max 5
  86. enum {
  87. Menu_open_file = 0,
  88. Menu_enter_channel,
  89. Menu_enter_rate,
  90. Menu_enter_scan_period,
  91. Menu_log,
  92. Menu_ok
  93. };
  94. //#define MIN(a, b) ((a<b)?a:b)
  95. static uint8_t GetHexVal(char hex) {
  96. return (uint8_t)hex - ((uint8_t)hex < 58 ? 48 : ((uint8_t)hex < 97 ? 55 : 87));
  97. }
  98. // Return num bytes in array
  99. static uint8_t ConvertHexToArray(char* hex, uint8_t* array, uint8_t maxlen) {
  100. uint8_t len = 0;
  101. while(maxlen) {
  102. uint8_t ch = *hex++;
  103. if(ch == 0) break;
  104. if(ch < '0') continue;
  105. *array++ = (GetHexVal(ch) << 4) + GetHexVal(*hex++);
  106. len++;
  107. maxlen--;
  108. }
  109. return len;
  110. }
  111. static void add_to_str_hex_bytes(char* out, char* arr, int bytes) {
  112. if(bytes <= 0) return;
  113. out += strlen(out);
  114. do {
  115. snprintf(out, 3, "%02X", *arr++);
  116. out += 2;
  117. } while(--bytes);
  118. }
  119. static void add_to_furi_str_hex_bytes(FuriString* str, char* arr, int bytes) {
  120. if(!bytes) return;
  121. do {
  122. furi_string_cat_printf(str, "%02X", *arr++);
  123. } while(--bytes);
  124. }
  125. static void add_to_str_hex_bytes_shift_9b(char* out, char* arr, int bytes) {
  126. if(bytes <= 0) return;
  127. out += strlen(out);
  128. arr++; // +8b
  129. do {
  130. snprintf(out, 4, "%02X", ((uint8_t)(*arr << 1)) | (*(arr + 1) >> 7));
  131. arr++;
  132. out += 2;
  133. } while(--bytes);
  134. }
  135. void clear_log() {
  136. log_arr_idx = 0;
  137. view_log_arr_idx = 0;
  138. last_packet_send = -1;
  139. }
  140. void allocate_log_array() {
  141. APP->log_arr = malloc(LOG_REC_SIZE * MAX_LOG_RECORDS);
  142. if(APP->log_arr == NULL) {
  143. FURI_LOG_E(TAG, "Not enouch memory: %d", LOG_REC_SIZE * MAX_LOG_RECORDS);
  144. strcpy(addr_file_name, "MEMORY LOW!");
  145. }
  146. clear_log();
  147. }
  148. void write_to_log_file(Storage* storage, bool f_settings) {
  149. if(log_arr_idx == 0 && !f_settings) return;
  150. Stream* file_stream = file_stream_alloc(storage);
  151. FuriString* str = furi_string_alloc();
  152. furi_string_set(str, SCAN_APP_PATH_FOLDER);
  153. furi_string_cat(str, "/");
  154. bool fl;
  155. if(f_settings) {
  156. furi_string_cat(str, SETTINGS_FILENAME);
  157. fl = file_stream_open(
  158. file_stream, furi_string_get_cstr(str), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS);
  159. if(!fl) file_stream_close(file_stream);
  160. } else {
  161. furi_string_cat(str, LOG_FILENAME);
  162. furi_string_cat(str, LOG_FILEEXT);
  163. if(save_to_new_log) {
  164. int cnt = 1;
  165. do {
  166. fl = file_stream_open(
  167. file_stream, furi_string_get_cstr(str), FSAM_READ_WRITE, FSOM_CREATE_NEW);
  168. if(fl) break;
  169. file_stream_close(file_stream);
  170. furi_string_set(str, SCAN_APP_PATH_FOLDER);
  171. furi_string_cat(str, "/");
  172. furi_string_cat(str, LOG_FILENAME);
  173. furi_string_cat_printf(str, "-%02d", cnt);
  174. furi_string_cat(str, LOG_FILEEXT);
  175. } while(++cnt < 100);
  176. if(!fl) {
  177. FURI_LOG_E(TAG, "Failed to create new log file");
  178. notification_message(APP->notification, &sequence_blink_red_100);
  179. }
  180. } else {
  181. fl = file_stream_open(
  182. file_stream, furi_string_get_cstr(str), FSAM_READ_WRITE, FSOM_OPEN_APPEND);
  183. if(fl) {
  184. if(stream_size(file_stream) == 0) save_to_new_log = true;
  185. } else
  186. file_stream_close(file_stream);
  187. }
  188. }
  189. if(fl) {
  190. FURI_LOG_D(TAG, "Save to %s", furi_string_get_cstr(str));
  191. if(save_to_new_log || f_settings) {
  192. furi_string_printf(
  193. str,
  194. "%s %d\n%s %d\n%s %d\n",
  195. SettingsFld_Rate,
  196. NRF_rate,
  197. SettingsFld_Ch,
  198. NRF_channel,
  199. SettingsFld_ESB,
  200. NRF_ESB);
  201. furi_string_cat_printf(
  202. str,
  203. "%s %d\n%s %d\n%s %d\n",
  204. SettingsFld_DPL,
  205. NRF_DPL,
  206. SettingsFld_CRC,
  207. NRF_CRC,
  208. SettingsFld_Payload,
  209. NRF_Payload);
  210. furi_string_cat_printf(str, "P0: ");
  211. add_to_furi_str_hex_bytes(str, (char*)addrs.addr_P0, addrs.addr_len);
  212. furi_string_cat(str, "\n");
  213. if(addrs.addr_count > 1) {
  214. furi_string_cat_printf(str, "P1: ");
  215. add_to_furi_str_hex_bytes(str, (char*)addrs.addr_P1, addrs.addr_len);
  216. furi_string_cat(str, "\n");
  217. }
  218. if(addrs.addr_count > 2) {
  219. furi_string_cat_printf(str, "P2: ");
  220. furi_string_cat_printf(str, "%02X\n", addrs.addr_P2);
  221. }
  222. if(addrs.addr_count > 3) {
  223. furi_string_cat_printf(str, "P3: ");
  224. furi_string_cat_printf(str, "%02X\n", addrs.addr_P3);
  225. }
  226. if(addrs.addr_count > 4) {
  227. furi_string_cat_printf(str, "P4: ");
  228. furi_string_cat_printf(str, "%02X\n", addrs.addr_P4);
  229. }
  230. if(addrs.addr_count > 5) {
  231. furi_string_cat_printf(str, "P5: ");
  232. furi_string_cat_printf(str, "%02X\n", addrs.addr_P5);
  233. }
  234. if(!(fl = stream_write_string(file_stream, str) == furi_string_size(str))) {
  235. FURI_LOG_E(TAG, "Failed to write header to file!");
  236. notification_message(APP->notification, &sequence_blink_red_100);
  237. }
  238. }
  239. if(fl) {
  240. if(f_settings) {
  241. save_settings = 0;
  242. if(strcmp(addr_file_name, "NONE") == 0) strcpy(addr_file_name, SETTINGS_FILENAME);
  243. } else {
  244. int i = 0;
  245. for(; i < log_arr_idx; i++) {
  246. furi_string_reset(str);
  247. add_to_furi_str_hex_bytes(
  248. str,
  249. (char*)APP->log_arr + i * LOG_REC_SIZE,
  250. MIN(NRF_Payload + 1, LOG_REC_SIZE));
  251. furi_string_cat(str, "\n");
  252. if(stream_write_string(file_stream, str) != furi_string_size(str)) {
  253. FURI_LOG_E(TAG, "Failed to write to file!");
  254. break;
  255. }
  256. }
  257. if(i == log_arr_idx) {
  258. notification_message(APP->notification, &sequence_blink_yellow_100);
  259. FURI_LOG_D(TAG, "File saved");
  260. }
  261. save_to_new_log = false;
  262. }
  263. }
  264. file_stream_close(file_stream);
  265. } else {
  266. FURI_LOG_E(TAG, "Failed to open file %s", furi_string_get_cstr(str));
  267. notification_message(APP->notification, &sequence_blink_red_100);
  268. }
  269. stream_free(file_stream);
  270. furi_string_free(str);
  271. }
  272. static bool select_settings_file(Stream* stream) {
  273. DialogsApp* dialogs = furi_record_open("dialogs");
  274. bool result = false;
  275. FuriString* path;
  276. path = furi_string_alloc();
  277. furi_string_set(path, SCAN_APP_PATH_FOLDER);
  278. DialogsFileBrowserOptions browser_options;
  279. dialog_file_browser_set_basic_options(&browser_options, ".txt", NULL);
  280. browser_options.hide_ext = false;
  281. bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options);
  282. furi_record_close("dialogs");
  283. if(ret) {
  284. if(!file_stream_open(stream, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
  285. FURI_LOG_D(TAG, "Cannot open file \"%s\"", furi_string_get_cstr(path));
  286. file_stream_close(stream);
  287. } else {
  288. FURI_LOG_D(TAG, "Open file \"%s\"", furi_string_get_cstr(path));
  289. strncpy(
  290. addr_file_name,
  291. furi_string_get_cstr(path) + sizeof(SCAN_APP_PATH_FOLDER),
  292. sizeof(addr_file_name));
  293. result = true;
  294. }
  295. }
  296. furi_string_free(path);
  297. return result;
  298. }
  299. // 0 - success, otherwise an error
  300. static uint8_t load_settings_file(Stream* file_stream) {
  301. size_t file_size = 0;
  302. char* file_buf;
  303. uint8_t err = 5;
  304. file_size = stream_size(file_stream);
  305. if(file_size == (size_t)0) {
  306. FURI_LOG_D(TAG, "load failed. file_size: %d", file_size);
  307. return 1;
  308. }
  309. file_size = MIN(file_size, (size_t)LOG_REC_SIZE * MAX_LOG_RECORDS * 2 + 100);
  310. file_buf = malloc(file_size + 1);
  311. if(file_buf == NULL) {
  312. FURI_LOG_D(TAG, "Memory low, need: %d", file_size);
  313. return 2;
  314. }
  315. memset(file_buf, 0, file_size + 1);
  316. if(stream_read(file_stream, (uint8_t*)file_buf, file_size) == file_size) {
  317. FURI_LOG_D(TAG, "Loading settings file");
  318. char* line_ptr = file_buf;
  319. int16_t line_num = 0;
  320. memset((uint8_t*)&addrs, 0, sizeof(addrs));
  321. bool log_loaded = false;
  322. while(line_ptr && (size_t)(line_ptr - file_buf) < file_size) {
  323. char* end_ptr = strstr((char*)line_ptr, "\n");
  324. if(end_ptr == NULL)
  325. end_ptr = file_buf + file_size;
  326. else
  327. *end_ptr = '\0';
  328. int line_len = end_ptr - line_ptr;
  329. if(*line_ptr == '\r' || line_len == 0) {
  330. line_ptr = end_ptr + 1;
  331. continue;
  332. }
  333. if(*(end_ptr - 1) < '0') {
  334. *(end_ptr - 1) = '\0';
  335. line_len--;
  336. }
  337. //FURI_LOG_D(TAG, " L#%d: [%d]%s", line_num, line_len, line_ptr);
  338. if(strncmp(line_ptr, SettingsFld_Rate, sizeof(SettingsFld_Rate) - 1) == 0) {
  339. NRF_rate = atoi(line_ptr + sizeof(SettingsFld_Rate));
  340. } else if(strncmp(line_ptr, SettingsFld_Ch, sizeof(SettingsFld_Ch) - 1) == 0) {
  341. NRF_channel = atoi(line_ptr + sizeof(SettingsFld_Ch));
  342. } else if(strncmp(line_ptr, SettingsFld_ESB, sizeof(SettingsFld_ESB) - 1) == 0) {
  343. NRF_ESB = atoi(line_ptr + sizeof(SettingsFld_ESB));
  344. } else if(strncmp(line_ptr, SettingsFld_DPL, sizeof(SettingsFld_DPL) - 1) == 0) {
  345. NRF_DPL = atoi(line_ptr + sizeof(SettingsFld_DPL));
  346. } else if(strncmp(line_ptr, SettingsFld_CRC, sizeof(SettingsFld_CRC) - 1) == 0) {
  347. NRF_CRC = atoi(line_ptr + sizeof(SettingsFld_CRC));
  348. } else if(strncmp(line_ptr, SettingsFld_Payload, sizeof(SettingsFld_Payload) - 1) == 0) {
  349. NRF_Payload = atoi(line_ptr + sizeof(SettingsFld_Payload));
  350. if(NRF_Payload == 0 || NRF_Payload > 32) NRF_Payload = 32;
  351. } else if(*line_ptr == SettingsFld_Addr) {
  352. char a = *(++line_ptr);
  353. line_ptr += 3;
  354. switch(a) {
  355. case '0':
  356. addrs.addr_len = ConvertHexToArray(line_ptr, addrs.addr_P0, 5);
  357. //FURI_LOG_D(TAG, " +Addr(%d): %02X%02X%02X...", addrs.addr_len, addrs.addr_P0[0], addrs.addr_P0[1], addrs.addr_P0[2]);
  358. if(addrs.addr_len >= 2) err = 0;
  359. break;
  360. case '1':
  361. ConvertHexToArray(line_ptr, addrs.addr_P1, 5);
  362. //FURI_LOG_D(TAG, " +Addr: %02X%02X%02X...", addrs.addr_P0[1], addrs.addr_P1[1], addrs.addr_P1[2]);
  363. break;
  364. case '2':
  365. ConvertHexToArray(line_ptr, &addrs.addr_P2, 1);
  366. break;
  367. case '3':
  368. ConvertHexToArray(line_ptr, &addrs.addr_P3, 1);
  369. break;
  370. case '4':
  371. ConvertHexToArray(line_ptr, &addrs.addr_P4, 1);
  372. break;
  373. case '5':
  374. ConvertHexToArray(line_ptr, &addrs.addr_P5, 1);
  375. break;
  376. default:
  377. a = 0;
  378. break;
  379. }
  380. if(err == 0 && a) addrs.addr_count = a - '0' + 1;
  381. } else if(line_len >= (NRF_Payload + 1) * 2) { // data
  382. if(!log_loaded) {
  383. clear_log();
  384. what_to_do = 0;
  385. log_loaded = true;
  386. }
  387. if(log_arr_idx < MAX_LOG_RECORDS - 1) {
  388. ConvertHexToArray(
  389. line_ptr,
  390. APP->log_arr + log_arr_idx * LOG_REC_SIZE,
  391. MIN(NRF_Payload + 1, LOG_REC_SIZE));
  392. log_arr_idx++;
  393. }
  394. }
  395. line_ptr = end_ptr + 1;
  396. line_num++;
  397. }
  398. } else {
  399. FURI_LOG_D(TAG, "load failed. file size: %d", file_size);
  400. err = 4;
  401. }
  402. free(file_buf);
  403. return err;
  404. }
  405. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  406. furi_assert(event_queue);
  407. PluginEvent event = {.type = EventTypeKey, .input = *input_event};
  408. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  409. }
  410. static void prepare_nrf24() {
  411. uint8_t addr[5];
  412. uint8_t erx_addr = (1 << 0); // Enable RX_P0
  413. if(addrs.addr_count == 0) return;
  414. nrf24_write_reg(
  415. nrf24_HANDLE,
  416. REG_CONFIG,
  417. 0x70 | ((NRF_CRC == 1 ? 0b1000 :
  418. NRF_CRC == 2 ? 0b1100 :
  419. 0))); // Mask all interrupts
  420. nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, NRF_ESB ? 0x11 : 0); // Automatic Retransmission
  421. nrf24_write_reg(
  422. nrf24_HANDLE, REG_EN_AA, NRF_AA_OFF || !NRF_ESB ? 0 : 0x3F); // Auto acknowledgement
  423. nrf24_write_reg(
  424. nrf24_HANDLE,
  425. REG_FEATURE,
  426. NRF_DPL ?
  427. 4 + 1 :
  428. 1); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload
  429. nrf24_set_maclen(nrf24_HANDLE, addrs.addr_len);
  430. for(int i = 0; i < addrs.addr_len; i++) addr[i] = addrs.addr_P0[addrs.addr_len - i - 1];
  431. nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P0, addr, addrs.addr_len);
  432. uint8_t tmp[5];
  433. nrf24_read_reg(nrf24_HANDLE, REG_RX_ADDR_P0, tmp, addrs.addr_len);
  434. NRF_ERROR = memcmp(addr, tmp, addrs.addr_len) != 0;
  435. nrf24_write_reg(nrf24_HANDLE, RX_PW_P0, NRF_Payload);
  436. if(addrs.addr_count > 0)
  437. nrf24_write_reg(
  438. nrf24_HANDLE, REG_DYNPD, NRF_DPL ? (1 << 0) : 0); // Enable dynamic payload reg
  439. if(addrs.addr_count > 1) {
  440. for(int i = 0; i < addrs.addr_len; i++) addr[i] = addrs.addr_P1[addrs.addr_len - i - 1];
  441. nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P1, addr, addrs.addr_len);
  442. nrf24_write_reg(nrf24_HANDLE, RX_PW_P1, NRF_Payload);
  443. nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, NRF_DPL ? (1 << 1) : 0);
  444. erx_addr |= (1 << 1); // Enable RX_P1
  445. } else
  446. nrf24_write_reg(nrf24_HANDLE, RX_PW_P1, 0);
  447. if(addrs.addr_count > 2) {
  448. nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P2, &addrs.addr_P2, 1);
  449. nrf24_write_reg(nrf24_HANDLE, RX_PW_P2, NRF_Payload);
  450. nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, NRF_DPL ? (1 << 2) : 0);
  451. erx_addr |= (1 << 2); // Enable RX_P2
  452. } else
  453. nrf24_write_reg(nrf24_HANDLE, RX_PW_P2, 0);
  454. if(addrs.addr_count > 3) {
  455. nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P3, &addrs.addr_P3, 1);
  456. nrf24_write_reg(nrf24_HANDLE, RX_PW_P3, NRF_Payload);
  457. nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, NRF_DPL ? (1 << 3) : 0);
  458. erx_addr |= (1 << 3); // Enable RX_P3
  459. } else
  460. nrf24_write_reg(nrf24_HANDLE, RX_PW_P3, 0);
  461. if(addrs.addr_count > 4) {
  462. nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P4, &addrs.addr_P4, 1);
  463. nrf24_write_reg(nrf24_HANDLE, RX_PW_P4, NRF_Payload);
  464. nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, NRF_DPL ? (1 << 4) : 0);
  465. erx_addr |= (1 << 4); // Enable RX_P4
  466. } else
  467. nrf24_write_reg(nrf24_HANDLE, RX_PW_P4, 0);
  468. if(addrs.addr_count > 5) {
  469. nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P5, &addrs.addr_P5, 1);
  470. nrf24_write_reg(nrf24_HANDLE, RX_PW_P5, NRF_Payload);
  471. nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, NRF_DPL ? (1 << 5) : 0);
  472. erx_addr |= (1 << 5); // Enable RX_P5
  473. } else
  474. nrf24_write_reg(nrf24_HANDLE, RX_PW_P5, 0);
  475. nrf24_write_reg(nrf24_HANDLE, REG_STATUS, 0x70); // clear interrupts
  476. nrf24_write_reg(nrf24_HANDLE, REG_EN_RXADDR, erx_addr);
  477. nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, NRF_channel);
  478. nrf24_write_reg(nrf24_HANDLE, REG_RF_SETUP, NRF_rate);
  479. nrf24_flush_rx(nrf24_HANDLE);
  480. nrf24_flush_tx(nrf24_HANDLE);
  481. nrf24_set_idle(nrf24_HANDLE);
  482. }
  483. static void start_scanning() {
  484. prepare_nrf24();
  485. if(NRF_ERROR) {
  486. FURI_LOG_E(TAG, "NRF R/W ERROR!");
  487. return;
  488. }
  489. nrf24_set_rx_mode(nrf24_HANDLE);
  490. start_time = furi_get_tick();
  491. FURI_LOG_D(TAG, "Start scan: Ch=%d Rate=%d", NRF_channel, NRF_rate);
  492. }
  493. bool nrf24_read_newpacket() {
  494. if(APP->log_arr == NULL) return false;
  495. bool found = false;
  496. uint8_t packetsize;
  497. uint8_t* ptr = APP->log_arr + log_arr_idx * LOG_REC_SIZE;
  498. uint8_t status = nrf24_rxpacket(nrf24_HANDLE, ptr + 1, &packetsize, !NRF_DPL);
  499. if(status & RX_DR) {
  500. *ptr = ((packetsize & 0x1F) << 3) | ((status >> 1) & 7); // payload size + pipe #
  501. if(packetsize < 32) memset(ptr + packetsize + 1, 0, 32 - packetsize);
  502. if(log_arr_idx < MAX_LOG_RECORDS - 1) {
  503. log_arr_idx++;
  504. } else {
  505. if(log_to_file == 1 || log_to_file == 2) {
  506. write_to_log_file(APP->storage, false);
  507. clear_log();
  508. } else {
  509. memmove(APP->log_arr, APP->log_arr + LOG_REC_SIZE, log_arr_idx * LOG_REC_SIZE);
  510. }
  511. }
  512. FURI_LOG_D(TAG, "Found packet #%d pipe %d", log_arr_idx, (status >> 1) & 7);
  513. notification_message(APP->notification, &sequence_blink_white_100);
  514. found = true;
  515. }
  516. return found;
  517. }
  518. bool nrf24_send_packet() {
  519. if(log_arr_idx == 0) return false;
  520. if(!what_to_do) prepare_nrf24();
  521. last_packet_send_st = nrf24_txpacket(
  522. nrf24_HANDLE, APP->log_arr + view_log_arr_idx * LOG_REC_SIZE + 1, 32, false);
  523. last_packet_send = view_log_arr_idx;
  524. notification_message(
  525. APP->notification,
  526. last_packet_send_st ? &sequence_blink_blue_100 : &sequence_blink_red_100);
  527. if(what_to_do) start_scanning();
  528. return last_packet_send_st;
  529. }
  530. uint16_t calc_crc(uint32_t crc, uint8_t* ptr, uint8_t bitnum, uint16_t bits) {
  531. //uint8_t bitnum = 7;
  532. uint32_t crc_high, polynom;
  533. if(view_log_decode_CRC == 2) {
  534. crc_high = (1 << 16);
  535. polynom = 0x11021; // X^16+X^12+X^5+1
  536. } else {
  537. crc_high = (1 << 8);
  538. polynom = 0x107; // x^8+x^2+x^1+1
  539. }
  540. while(bits--) {
  541. crc <<= 1;
  542. if(((crc & crc_high) != 0) ^ ((*ptr >> bitnum) & 1)) crc ^= polynom;
  543. if(bitnum == 0) {
  544. ptr++;
  545. bitnum = 7;
  546. } else
  547. bitnum--;
  548. }
  549. return crc & (view_log_decode_CRC == 2 ? 0xFFFF : 0xFF);
  550. }
  551. // shifted 1 bit right
  552. uint16_t get_shifted_crc(uint8_t* pcrc) {
  553. uint16_t crc = ((uint8_t)(*pcrc << 1)) | (*(pcrc + 1) >> 7);
  554. if(view_log_decode_CRC == 2) {
  555. crc = (crc << 8) | (((uint8_t)(*(pcrc + 1) << 1))) | (*(pcrc + 2) >> 7);
  556. }
  557. return crc;
  558. }
  559. static void render_callback(Canvas* const canvas, void* ctx) {
  560. const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
  561. if(plugin_state == NULL) return;
  562. //canvas_draw_frame(canvas, 0, 0, 128, 64); // border around the edge of the screen
  563. if(what_doing == 0) {
  564. canvas_set_font(canvas, FontSecondary); // 8x10 font
  565. if(save_settings)
  566. snprintf(
  567. screen_buf, sizeof(screen_buf), "Save: %s", SETTINGS_FILENAME); // menu_selected = 0
  568. else
  569. snprintf(screen_buf, sizeof(screen_buf), "Load: %s", addr_file_name);
  570. canvas_draw_str(canvas, 10, 10, screen_buf);
  571. snprintf(screen_buf, sizeof(screen_buf), "Ch: %d", NRF_channel); // menu_selected = 1
  572. canvas_draw_str(canvas, 10, 20, screen_buf);
  573. if(NRF_ESB) {
  574. strcpy(screen_buf, "ESB");
  575. if(NRF_DPL) strcat(screen_buf, " DPL");
  576. canvas_draw_str(canvas, 80, 20, screen_buf);
  577. }
  578. if(NRF_AA_OFF) {
  579. canvas_draw_str(canvas, 61, 20, "AA");
  580. canvas_draw_line(canvas, 60, 21, 72, 12);
  581. }
  582. snprintf(
  583. screen_buf,
  584. sizeof(screen_buf),
  585. "Rate: %sbps",
  586. NRF_rate == 2 ? "2M" :
  587. NRF_rate == 1 ? "1M" :
  588. "250K"); // menu_selected = 2
  589. canvas_draw_str(canvas, 10, 30, screen_buf);
  590. snprintf(screen_buf, sizeof(screen_buf), "Payload: %d", NRF_Payload);
  591. canvas_draw_str(canvas, 80, 30, screen_buf);
  592. strcpy(screen_buf, "Next Ch time: "); // menu_selected = 3
  593. if(find_channel_period == 0)
  594. strcat(screen_buf, "off");
  595. else
  596. snprintf(
  597. screen_buf + strlen(screen_buf), sizeof(screen_buf), "%d s", find_channel_period);
  598. canvas_draw_str(canvas, 10, 40, screen_buf);
  599. if(NRF_CRC == 1)
  600. canvas_draw_str(canvas, 99, 40, "CRC1");
  601. else if(NRF_CRC == 2)
  602. canvas_draw_str(canvas, 99, 40, "CRC2");
  603. snprintf(
  604. screen_buf,
  605. sizeof(screen_buf),
  606. "Log: %s",
  607. log_to_file == 0 ? "No" :
  608. log_to_file == 1 ? "Yes" :
  609. log_to_file == 2 ? "Append" :
  610. "Clear"); // menu_selected = 4
  611. canvas_draw_str(canvas, 10, 50, screen_buf);
  612. if(what_to_do) {
  613. if(NRF_ERROR)
  614. snprintf(screen_buf, sizeof(screen_buf), "nRF24L01+ R/W ERROR!");
  615. else
  616. snprintf(
  617. screen_buf,
  618. sizeof(screen_buf),
  619. "Start scan (pipes: %d)",
  620. addrs.addr_count); // menu_selected = 5
  621. } else
  622. snprintf(screen_buf, sizeof(screen_buf), "View log (pipes: %d)", addrs.addr_count);
  623. canvas_draw_str(canvas, 10, 60, screen_buf);
  624. canvas_draw_str(canvas, 0, menu_selected * 10 + 10, ">");
  625. } else if(what_doing == 1) {
  626. canvas_set_font(canvas, FontBatteryPercent); // 5x7 font, 9 lines
  627. bool ch2 = false;
  628. screen_buf[0] = '\0';
  629. if(view_log_arr_x == 0) {
  630. strcat(screen_buf, " ");
  631. ch2 = true;
  632. } else {
  633. snprintf(screen_buf, sizeof(screen_buf), "<%d", view_log_arr_x);
  634. if(view_log_arr_x < VIEW_LOG_MAX_X) ch2 = true;
  635. }
  636. snprintf(
  637. screen_buf + strlen(screen_buf),
  638. sizeof(screen_buf),
  639. " %s ch: %d - %d.",
  640. what_to_do ? "Read" : "View",
  641. NRF_channel,
  642. log_arr_idx);
  643. canvas_draw_str(canvas, 0, 7, screen_buf);
  644. if(ch2) canvas_draw_str(canvas, 121, 7, ">");
  645. if(log_arr_idx) {
  646. if(view_log_arr_idx >= log_arr_idx) view_log_arr_idx = log_arr_idx - 1;
  647. uint16_t page = view_log_arr_idx & ~7;
  648. for(uint8_t i = 0; i < 8 && page + i < log_arr_idx; i++) {
  649. screen_buf[0] = (view_log_arr_idx & 7) != i ? ' ' :
  650. last_packet_send != view_log_arr_idx ? '>' :
  651. last_packet_send_st ? '*' :
  652. '!';
  653. screen_buf[1] = '\0';
  654. char* ptr = (char*)APP->log_arr + (page + i) * LOG_REC_SIZE;
  655. uint8_t dpl = *ptr++;
  656. uint8_t pipe = dpl & 7;
  657. dpl >>= 3;
  658. if(dpl == 0) dpl = 32;
  659. int count = dpl - view_log_arr_x;
  660. if(view_log_decode_PCF) count--;
  661. uint8_t max_width = VIEW_LOG_WIDTH_B;
  662. if(view_log_arr_x == 0) {
  663. if(addrs.addr_count > 1) max_width--;
  664. if(view_log_decode_PCF) max_width -= 2;
  665. }
  666. if(count > max_width) count = max_width;
  667. ptr += view_log_arr_x;
  668. uint8_t* crcptr = NULL;
  669. uint8_t pre = 0;
  670. if(count > 0) {
  671. if(view_log_decode_CRC) {
  672. static uint16_t prev_addr_CRC;
  673. static int8_t prev_pipe = -1;
  674. uint8_t* pcrc = APP->log_arr + (page + i) * LOG_REC_SIZE + 1;
  675. uint16_t crc;
  676. if(prev_pipe == pipe) {
  677. crc = prev_addr_CRC;
  678. } else {
  679. crc = view_log_decode_CRC == 2 ? 0xFFFF : 0xFF;
  680. if(pipe <= 1) {
  681. crc = calc_crc(
  682. crc,
  683. pipe == 0 ? addrs.addr_P0 : addrs.addr_P1,
  684. 7,
  685. addrs.addr_len * 8);
  686. } else {
  687. crc = calc_crc(crc, addrs.addr_P1, 7, (addrs.addr_len - 1) * 8);
  688. crc = calc_crc(
  689. crc,
  690. pipe == 2 ? &addrs.addr_P2 :
  691. pipe == 3 ? &addrs.addr_P3 :
  692. pipe == 4 ? &addrs.addr_P4 :
  693. &addrs.addr_P5,
  694. 7,
  695. 8);
  696. }
  697. prev_addr_CRC = crc;
  698. prev_pipe = pipe;
  699. }
  700. if(view_log_decode_PCF) {
  701. crc = calc_crc(crc, pcrc++, 7, 9);
  702. if(crc == get_shifted_crc(pcrc)) crcptr = pcrc;
  703. if(crcptr == NULL) {
  704. for(int8_t j = 0; j < (int8_t)dpl - view_log_decode_CRC - 1; j++) {
  705. crc = calc_crc(crc, pcrc++, 6, 8);
  706. if(crc == get_shifted_crc(pcrc)) {
  707. crcptr = pcrc;
  708. break;
  709. }
  710. }
  711. }
  712. } else {
  713. for(int8_t j = 0; j < (int8_t)dpl - view_log_decode_CRC; j++) {
  714. crc = calc_crc(crc, pcrc++, 7, 8);
  715. if((view_log_decode_CRC == 1 && crc == *pcrc) ||
  716. (view_log_decode_CRC == 2 &&
  717. crc == ((*pcrc << 8) | *(pcrc + 1)))) {
  718. crcptr = pcrc;
  719. break;
  720. }
  721. }
  722. }
  723. }
  724. if(max_width < VIEW_LOG_WIDTH_B) {
  725. pre += snprintf(screen_buf + 1, 10, "%X-", pipe);
  726. if(view_log_decode_PCF) {
  727. pre += snprintf(
  728. screen_buf + strlen(screen_buf),
  729. 10,
  730. "%02X%01X-",
  731. *ptr >> 2,
  732. ((*ptr & 3) << 1) | (*(ptr + 1) >> 7));
  733. }
  734. }
  735. if(view_log_decode_PCF)
  736. add_to_str_hex_bytes_shift_9b(screen_buf, ptr, count);
  737. else
  738. add_to_str_hex_bytes(screen_buf, ptr, count);
  739. }
  740. uint16_t y = 14 + i * 7;
  741. canvas_draw_str(canvas, 3 * 5, y, screen_buf);
  742. uint16_t x = snprintf(screen_buf, sizeof(screen_buf), "%d", page + i + 1);
  743. canvas_draw_str(canvas, 0, y, screen_buf);
  744. if(crcptr) { // 5x7 font, 9 lines
  745. canvas_draw_str(canvas, x * 5, y, "=");
  746. int n = crcptr - (uint8_t*)ptr - 1;
  747. if(n > -view_log_decode_CRC && n < count) {
  748. int len;
  749. x = (4 + pre) * 5;
  750. if(n < 0) {
  751. len = view_log_decode_CRC + n;
  752. n = 0;
  753. } else {
  754. len = MIN(view_log_decode_CRC, count - n);
  755. x += n * 2 * 5;
  756. canvas_draw_line(canvas, x - 1, y, x - 1, y - 1);
  757. }
  758. canvas_draw_line(canvas, x - 1, y, n = x + len * 2 * 5 - 1, y);
  759. canvas_draw_line(canvas, n, y, n, y - 1);
  760. }
  761. } else
  762. canvas_draw_str(canvas, x * 5, y, ":");
  763. }
  764. }
  765. } else {
  766. canvas_set_font(canvas, FontBatteryPercent); // 5x7 font
  767. if(addrs.addr_count > 0) {
  768. snprintf(screen_buf, sizeof(screen_buf), "P0: ");
  769. add_to_str_hex_bytes(screen_buf, (char*)addrs.addr_P0, addrs.addr_len);
  770. canvas_draw_str(canvas, 0, 1 * 7, screen_buf);
  771. }
  772. if(addrs.addr_count > 1) {
  773. snprintf(screen_buf, sizeof(screen_buf), "P1: ");
  774. add_to_str_hex_bytes(screen_buf, (char*)addrs.addr_P1, addrs.addr_len);
  775. canvas_draw_str(canvas, 0, 2 * 7, screen_buf);
  776. }
  777. if(addrs.addr_count > 2) {
  778. canvas_draw_str(canvas, 0, 3 * 7, "P2: ");
  779. snprintf(screen_buf, sizeof(screen_buf), "%02X", addrs.addr_P2);
  780. canvas_draw_str(canvas, (4 + (addrs.addr_len - 1) * 2) * 5, 3 * 7, screen_buf);
  781. }
  782. if(addrs.addr_count > 3) {
  783. canvas_draw_str(canvas, 0, 4 * 7, "P3: ");
  784. snprintf(screen_buf, sizeof(screen_buf), "%02X", addrs.addr_P3);
  785. canvas_draw_str(canvas, (4 + (addrs.addr_len - 1) * 2) * 5, 4 * 7, screen_buf);
  786. }
  787. if(addrs.addr_count > 4) {
  788. canvas_draw_str(canvas, 0, 5 * 7, "P4: ");
  789. snprintf(screen_buf, sizeof(screen_buf), "%02X", addrs.addr_P4);
  790. canvas_draw_str(canvas, (4 + (addrs.addr_len - 1) * 2) * 5, 5 * 7, screen_buf);
  791. }
  792. if(addrs.addr_count > 5) {
  793. canvas_draw_str(canvas, 0, 6 * 7, "P5: ");
  794. snprintf(screen_buf, sizeof(screen_buf), "%02X", addrs.addr_P5);
  795. canvas_draw_str(canvas, (4 + (addrs.addr_len - 1) * 2) * 5, 6 * 7, screen_buf);
  796. }
  797. screen_buf[0] = 'v';
  798. strcpy(screen_buf + 1, VERSION);
  799. canvas_draw_str(canvas, 108, 7, screen_buf);
  800. if(view_log_decode_PCF || view_log_decode_CRC) {
  801. strcpy(screen_buf, "Decode: ");
  802. if(view_log_decode_PCF) strcat(screen_buf, "PCF ");
  803. if(view_log_decode_CRC == 1)
  804. strcat(screen_buf, "CRC1");
  805. else if(view_log_decode_CRC == 2)
  806. strcat(screen_buf, "CRC2");
  807. canvas_draw_str(canvas, 0, 64, screen_buf);
  808. }
  809. }
  810. release_mutex((ValueMutex*)ctx, plugin_state);
  811. }
  812. int32_t nrf24scan_app(void* p) {
  813. UNUSED(p);
  814. APP = malloc(sizeof(Nrf24Scan));
  815. APP->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
  816. APP->plugin_state = malloc(sizeof(PluginState));
  817. ValueMutex state_mutex;
  818. if(!init_mutex(&state_mutex, APP->plugin_state, sizeof(PluginState))) {
  819. furi_message_queue_free(APP->event_queue);
  820. FURI_LOG_E(TAG, "cannot create mutex");
  821. free(APP->plugin_state);
  822. return 255;
  823. }
  824. memset((uint8_t*)&addrs, 0, sizeof(addrs));
  825. nrf24_init();
  826. // Set system callbacks
  827. APP->view_port = view_port_alloc();
  828. view_port_draw_callback_set(APP->view_port, render_callback, &state_mutex);
  829. view_port_input_callback_set(APP->view_port, input_callback, APP->event_queue);
  830. // Open GUI and register view_port
  831. APP->gui = furi_record_open(RECORD_GUI);
  832. gui_add_view_port(APP->gui, APP->view_port, GuiLayerFullscreen);
  833. APP->notification = furi_record_open(RECORD_NOTIFICATION);
  834. APP->storage = furi_record_open(RECORD_STORAGE);
  835. storage_common_mkdir(APP->storage, SCAN_APP_PATH_FOLDER);
  836. Stream* file_stream = file_stream_alloc(APP->storage);
  837. FuriString* path = furi_string_alloc();
  838. furi_string_set(path, SCAN_APP_PATH_FOLDER);
  839. furi_string_cat(path, "/");
  840. furi_string_cat(path, SETTINGS_FILENAME);
  841. if(file_stream_open(file_stream, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
  842. uint8_t err = load_settings_file(file_stream);
  843. if(!err)
  844. strcpy(addr_file_name, SETTINGS_FILENAME);
  845. else
  846. snprintf(addr_file_name, sizeof(addr_file_name), "LOAD ERROR#%d", err);
  847. } else {
  848. strcpy(addr_file_name, "NONE");
  849. }
  850. file_stream_close(file_stream);
  851. stream_free(file_stream);
  852. furi_string_free(path);
  853. allocate_log_array();
  854. PluginEvent event;
  855. for(bool processing = true; processing;) {
  856. FuriStatus event_status = furi_message_queue_get(APP->event_queue, &event, 100);
  857. PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
  858. if(event_status == FuriStatusOk) {
  859. // press events
  860. if(event.type == EventTypeKey) {
  861. //FURI_LOG_D(TAG, "Key: %d Type: %d Sec: %u", event.input.key, event.input.type, event.input.sequence);
  862. switch(event.input.key) {
  863. case InputKeyUp:
  864. if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) {
  865. if(what_doing == 0) {
  866. if(menu_selected > 0)
  867. menu_selected--;
  868. else
  869. menu_selected = menu_selected_max;
  870. } else if(what_doing == 1) {
  871. view_log_arr_idx -= event.input.type == InputTypeRepeat ? 10 : 1;
  872. if(view_log_arr_idx >= log_arr_idx) view_log_arr_idx = 0;
  873. }
  874. }
  875. break;
  876. case InputKeyDown:
  877. if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) {
  878. if(what_doing == 0) {
  879. if(menu_selected < menu_selected_max)
  880. menu_selected++;
  881. else
  882. menu_selected = 0;
  883. } else if(what_doing == 1) {
  884. view_log_arr_idx += event.input.type == InputTypeRepeat ? 10 : 1;
  885. if(view_log_arr_idx >= log_arr_idx) view_log_arr_idx = log_arr_idx - 1;
  886. }
  887. }
  888. break;
  889. case InputKeyLeft:
  890. if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) {
  891. if(what_doing == 0) {
  892. switch(menu_selected) {
  893. case Menu_enter_channel:
  894. NRF_channel -= event.input.type == InputTypeRepeat ? 10 : 1;
  895. if(NRF_channel > MAX_CHANNEL) NRF_channel = MAX_CHANNEL;
  896. break;
  897. case Menu_enter_rate:
  898. NRF_Payload -= event.input.type == InputTypeRepeat ? 10 : 1;
  899. if(NRF_Payload == 0 || NRF_Payload > 32) NRF_Payload = 1;
  900. break;
  901. case Menu_enter_scan_period:
  902. find_channel_period -= event.input.type == InputTypeRepeat ? 10 :
  903. 1;
  904. if(find_channel_period < 0) find_channel_period = 0;
  905. break;
  906. case Menu_log:
  907. if(--log_to_file < -1) log_to_file = 2;
  908. break;
  909. case Menu_ok:
  910. what_to_do = !what_to_do;
  911. break;
  912. }
  913. } else if(what_doing == 1) {
  914. if(view_log_arr_x > 0) view_log_arr_x--;
  915. } else if(what_doing == 2) {
  916. //if(NRF_ESB == 0)
  917. view_log_decode_PCF ^= 1;
  918. }
  919. }
  920. break;
  921. case InputKeyRight:
  922. if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) {
  923. if(what_doing == 0) {
  924. switch(menu_selected) {
  925. case Menu_open_file:
  926. save_settings ^= 1;
  927. break;
  928. case Menu_enter_channel:
  929. NRF_channel += event.input.type == InputTypeRepeat ? 10 : 1;
  930. if(NRF_channel > MAX_CHANNEL) NRF_channel = 0;
  931. break;
  932. case Menu_enter_rate:
  933. NRF_Payload += event.input.type == InputTypeRepeat ? 10 : 1;
  934. if(NRF_Payload == 0 || NRF_Payload > 32) NRF_Payload = 32;
  935. break;
  936. case Menu_enter_scan_period:
  937. find_channel_period += event.input.type == InputTypeRepeat ? 10 :
  938. 1;
  939. break;
  940. case Menu_log:
  941. if(++log_to_file > 2) log_to_file = -1;
  942. break;
  943. case Menu_ok:
  944. what_to_do = !what_to_do;
  945. break;
  946. }
  947. } else if(what_doing == 1) {
  948. if(view_log_arr_x < VIEW_LOG_MAX_X) view_log_arr_x++;
  949. } else if(what_doing == 2) {
  950. if(++view_log_decode_CRC > 2) view_log_decode_CRC = 0;
  951. }
  952. }
  953. break;
  954. case InputKeyOk:
  955. if(event.input.type == InputTypePress) {
  956. if(what_doing == 0) {
  957. switch(menu_selected) {
  958. case Menu_open_file:
  959. if(save_settings) {
  960. write_to_log_file(APP->storage, true);
  961. } else {
  962. file_stream = file_stream_alloc(APP->storage);
  963. if(select_settings_file(file_stream)) {
  964. uint8_t err = load_settings_file(file_stream);
  965. if(!err)
  966. save_to_new_log = true;
  967. else
  968. snprintf(
  969. addr_file_name,
  970. sizeof(addr_file_name),
  971. "LOAD ERROR#%d",
  972. err);
  973. file_stream_close(file_stream);
  974. }
  975. stream_free(file_stream);
  976. }
  977. break;
  978. case Menu_enter_rate:
  979. NRF_rate++;
  980. if(NRF_rate > 2) NRF_rate = 0;
  981. break;
  982. case Menu_enter_scan_period:
  983. if(++NRF_CRC > 2) NRF_CRC = 0;
  984. break;
  985. case Menu_ok:
  986. if(what_to_do) {
  987. if(addrs.addr_count) {
  988. what_doing = 1;
  989. if(log_to_file == -1) {
  990. clear_log();
  991. save_to_new_log = true;
  992. } else if(log_to_file == 1)
  993. save_to_new_log = true;
  994. start_scanning();
  995. }
  996. } else
  997. what_doing = 1;
  998. key_press_seq_ok = event.input.sequence;
  999. break;
  1000. }
  1001. }
  1002. } else if(event.input.type == InputTypeLong) {
  1003. if(what_doing == 0) {
  1004. if(menu_selected == Menu_enter_channel) {
  1005. NRF_AA_OFF ^= 1;
  1006. key_press_seq_ok = event.input.sequence;
  1007. } else if(menu_selected == Menu_log) { // Log
  1008. if(log_arr_idx && (log_to_file == 1 || log_to_file == 2)) {
  1009. write_to_log_file(APP->storage, false);
  1010. clear_log();
  1011. }
  1012. }
  1013. } else if(what_doing == 1) {
  1014. what_doing = 2;
  1015. }
  1016. } else if(event.input.type == InputTypeRelease) {
  1017. if(key_press_seq_ok != event.input.sequence) {
  1018. if(what_doing == 0) {
  1019. if(menu_selected == Menu_enter_channel) {
  1020. if(NRF_ESB) {
  1021. if(NRF_DPL)
  1022. NRF_DPL = NRF_ESB = 0;
  1023. else
  1024. NRF_DPL = 1;
  1025. } else
  1026. NRF_ESB = 1;
  1027. //if(NRF_ESB) view_log_decode_PCF = 0;
  1028. }
  1029. } else if(what_doing == 1) { // Send
  1030. nrf24_send_packet();
  1031. }
  1032. }
  1033. key_press_seq_ok--;
  1034. }
  1035. break;
  1036. case InputKeyBack:
  1037. if(event.input.type == InputTypeLong)
  1038. processing = false;
  1039. else if(event.input.type == InputTypeRelease) {
  1040. if(what_doing) what_doing--;
  1041. if(what_doing == 0) nrf24_set_idle(nrf24_HANDLE);
  1042. ;
  1043. }
  1044. break;
  1045. default:
  1046. break;
  1047. }
  1048. }
  1049. }
  1050. if(what_doing && what_to_do) {
  1051. nrf24_read_newpacket();
  1052. if(find_channel_period &&
  1053. furi_get_tick() - start_time >= (uint32_t)find_channel_period * 1000UL) {
  1054. if(++NRF_channel > MAX_CHANNEL) NRF_channel = 0;
  1055. start_scanning();
  1056. }
  1057. }
  1058. view_port_update(APP->view_port);
  1059. release_mutex(&state_mutex, plugin_state);
  1060. }
  1061. nrf24_set_idle(nrf24_HANDLE);
  1062. if(log_arr_idx && (log_to_file == 1 || log_to_file == 2)) {
  1063. write_to_log_file(APP->storage, false);
  1064. }
  1065. nrf24_deinit();
  1066. view_port_enabled_set(APP->view_port, false);
  1067. gui_remove_view_port(APP->gui, APP->view_port);
  1068. furi_record_close(RECORD_GUI);
  1069. furi_record_close(RECORD_NOTIFICATION);
  1070. furi_record_close(RECORD_STORAGE);
  1071. view_port_free(APP->view_port);
  1072. furi_message_queue_free(APP->event_queue);
  1073. free(APP->plugin_state);
  1074. if(APP->log_arr) free(APP->log_arr);
  1075. free(APP);
  1076. return 0;
  1077. }