subghz_cli.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. #include "subghz_cli.h"
  2. #include <furi.h>
  3. #include <furi_hal.h>
  4. #include <lib/toolbox/args.h>
  5. #include <lib/subghz/subghz_keystore.h>
  6. #include <lib/subghz/receiver.h>
  7. #include <lib/subghz/transmitter.h>
  8. #include <lib/subghz/subghz_file_encoder_worker.h>
  9. #include "helpers/subghz_chat.h"
  10. #include <notification/notification_messages.h>
  11. #include <flipper_format/flipper_format_i.h>
  12. #include <flipper.pb.h>
  13. #include <pb_decode.h>
  14. #define SUBGHZ_FREQUENCY_RANGE_STR \
  15. "299999755...348000000 or 386999938...464000000 or 778999847...928000000"
  16. #define SUBGHZ_REGION_FILENAME "/int/.region_data"
  17. void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) {
  18. UNUSED(context);
  19. uint32_t frequency = 433920000;
  20. if(furi_string_size(args)) {
  21. int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency);
  22. if(ret != 1) {
  23. printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
  24. cli_print_usage("subghz tx_carrier", "<Frequency: in Hz>", furi_string_get_cstr(args));
  25. return;
  26. }
  27. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  28. printf(
  29. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  30. frequency);
  31. return;
  32. }
  33. }
  34. furi_hal_subghz_reset();
  35. furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
  36. frequency = furi_hal_subghz_set_frequency_and_path(frequency);
  37. furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
  38. furi_hal_gpio_write(&gpio_cc1101_g0, true);
  39. furi_hal_power_suppress_charge_enter();
  40. if(furi_hal_subghz_tx()) {
  41. printf("Transmitting at frequency %lu Hz\r\n", frequency);
  42. printf("Press CTRL+C to stop\r\n");
  43. while(!cli_cmd_interrupt_received(cli)) {
  44. furi_delay_ms(250);
  45. }
  46. } else {
  47. printf("This frequency can only be used for RX in your region\r\n");
  48. }
  49. furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
  50. furi_hal_subghz_sleep();
  51. furi_hal_power_suppress_charge_exit();
  52. }
  53. void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) {
  54. UNUSED(context);
  55. uint32_t frequency = 433920000;
  56. if(furi_string_size(args)) {
  57. int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency);
  58. if(ret != 1) {
  59. printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
  60. cli_print_usage("subghz rx_carrier", "<Frequency: in Hz>", furi_string_get_cstr(args));
  61. return;
  62. }
  63. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  64. printf(
  65. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  66. frequency);
  67. return;
  68. }
  69. }
  70. furi_hal_subghz_reset();
  71. furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
  72. frequency = furi_hal_subghz_set_frequency_and_path(frequency);
  73. printf("Receiving at frequency %lu Hz\r\n", frequency);
  74. printf("Press CTRL+C to stop\r\n");
  75. furi_hal_power_suppress_charge_enter();
  76. furi_hal_subghz_rx();
  77. while(!cli_cmd_interrupt_received(cli)) {
  78. furi_delay_ms(250);
  79. printf("RSSI: %03.1fdbm\r", (double)furi_hal_subghz_get_rssi());
  80. fflush(stdout);
  81. }
  82. furi_hal_power_suppress_charge_exit();
  83. furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
  84. furi_hal_subghz_sleep();
  85. }
  86. void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) {
  87. UNUSED(context);
  88. uint32_t frequency = 433920000;
  89. uint32_t key = 0x0074BADE;
  90. uint32_t repeat = 10;
  91. uint32_t te = 403;
  92. if(furi_string_size(args)) {
  93. int ret =
  94. sscanf(furi_string_get_cstr(args), "%lx %lu %lu %lu", &key, &frequency, &te, &repeat);
  95. if(ret != 4) {
  96. printf(
  97. "sscanf returned %d, key: %lx, frequency: %lu, te:%lu, repeat: %lu\r\n",
  98. ret,
  99. key,
  100. frequency,
  101. te,
  102. repeat);
  103. cli_print_usage(
  104. "subghz tx",
  105. "<3 Byte Key: in hex> <Frequency: in Hz> <Te us> <Repeat count>",
  106. furi_string_get_cstr(args));
  107. return;
  108. }
  109. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  110. printf(
  111. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  112. frequency);
  113. return;
  114. }
  115. }
  116. printf(
  117. "Transmitting at %lu, key %lx, te %lu, repeat %lu. Press CTRL+C to stop\r\n",
  118. frequency,
  119. key,
  120. te,
  121. repeat);
  122. FuriString* flipper_format_string = furi_string_alloc_printf(
  123. "Protocol: Princeton\n"
  124. "Bit: 24\n"
  125. "Key: 00 00 00 00 00 %02X %02X %02X\n"
  126. "TE: %ld\n"
  127. "Repeat: %ld\n",
  128. (uint8_t)((key >> 16) & 0xFF),
  129. (uint8_t)((key >> 8) & 0xFF),
  130. (uint8_t)(key & 0xFF),
  131. te,
  132. repeat);
  133. FlipperFormat* flipper_format = flipper_format_string_alloc();
  134. Stream* stream = flipper_format_get_raw_stream(flipper_format);
  135. stream_clean(stream);
  136. stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string));
  137. SubGhzEnvironment* environment = subghz_environment_alloc();
  138. SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton");
  139. subghz_transmitter_deserialize(transmitter, flipper_format);
  140. furi_hal_subghz_reset();
  141. furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
  142. frequency = furi_hal_subghz_set_frequency_and_path(frequency);
  143. furi_hal_power_suppress_charge_enter();
  144. furi_hal_subghz_start_async_tx(subghz_transmitter_yield, transmitter);
  145. while(!(furi_hal_subghz_is_async_tx_complete() || cli_cmd_interrupt_received(cli))) {
  146. printf(".");
  147. fflush(stdout);
  148. furi_delay_ms(333);
  149. }
  150. furi_hal_subghz_stop_async_tx();
  151. furi_hal_subghz_sleep();
  152. furi_hal_power_suppress_charge_exit();
  153. flipper_format_free(flipper_format);
  154. subghz_transmitter_free(transmitter);
  155. subghz_environment_free(environment);
  156. }
  157. typedef struct {
  158. volatile bool overrun;
  159. FuriStreamBuffer* stream;
  160. size_t packet_count;
  161. } SubGhzCliCommandRx;
  162. static void subghz_cli_command_rx_capture_callback(bool level, uint32_t duration, void* context) {
  163. SubGhzCliCommandRx* instance = context;
  164. LevelDuration level_duration = level_duration_make(level, duration);
  165. if(instance->overrun) {
  166. instance->overrun = false;
  167. level_duration = level_duration_reset();
  168. }
  169. size_t ret =
  170. furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0);
  171. if(sizeof(LevelDuration) != ret) instance->overrun = true;
  172. }
  173. static void subghz_cli_command_rx_callback(
  174. SubGhzReceiver* receiver,
  175. SubGhzProtocolDecoderBase* decoder_base,
  176. void* context) {
  177. SubGhzCliCommandRx* instance = context;
  178. instance->packet_count++;
  179. FuriString* text;
  180. text = furi_string_alloc();
  181. subghz_protocol_decoder_base_get_string(decoder_base, text);
  182. subghz_receiver_reset(receiver);
  183. printf("%s", furi_string_get_cstr(text));
  184. furi_string_free(text);
  185. }
  186. void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
  187. UNUSED(context);
  188. uint32_t frequency = 433920000;
  189. if(furi_string_size(args)) {
  190. int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency);
  191. if(ret != 1) {
  192. printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
  193. cli_print_usage("subghz rx", "<Frequency: in Hz>", furi_string_get_cstr(args));
  194. return;
  195. }
  196. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  197. printf(
  198. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  199. frequency);
  200. return;
  201. }
  202. }
  203. // Allocate context and buffers
  204. SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx));
  205. instance->stream =
  206. furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration));
  207. furi_check(instance->stream);
  208. SubGhzEnvironment* environment = subghz_environment_alloc();
  209. subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes"));
  210. subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"));
  211. subghz_environment_set_came_atomo_rainbow_table_file_name(
  212. environment, EXT_PATH("subghz/assets/came_atomo"));
  213. subghz_environment_set_nice_flor_s_rainbow_table_file_name(
  214. environment, EXT_PATH("subghz/assets/nice_flor_s"));
  215. SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
  216. subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
  217. subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance);
  218. // Configure radio
  219. furi_hal_subghz_reset();
  220. furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
  221. frequency = furi_hal_subghz_set_frequency_and_path(frequency);
  222. furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
  223. furi_hal_power_suppress_charge_enter();
  224. // Prepare and start RX
  225. furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance);
  226. // Wait for packets to arrive
  227. printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency);
  228. LevelDuration level_duration;
  229. while(!cli_cmd_interrupt_received(cli)) {
  230. int ret = furi_stream_buffer_receive(
  231. instance->stream, &level_duration, sizeof(LevelDuration), 10);
  232. if(ret == sizeof(LevelDuration)) {
  233. if(level_duration_is_reset(level_duration)) {
  234. printf(".");
  235. subghz_receiver_reset(receiver);
  236. } else {
  237. bool level = level_duration_get_level(level_duration);
  238. uint32_t duration = level_duration_get_duration(level_duration);
  239. subghz_receiver_decode(receiver, level, duration);
  240. }
  241. }
  242. }
  243. // Shutdown radio
  244. furi_hal_subghz_stop_async_rx();
  245. furi_hal_subghz_sleep();
  246. furi_hal_power_suppress_charge_exit();
  247. printf("\r\nPackets recieved %u\r\n", instance->packet_count);
  248. // Cleanup
  249. subghz_receiver_free(receiver);
  250. subghz_environment_free(environment);
  251. furi_stream_buffer_free(instance->stream);
  252. free(instance);
  253. }
  254. void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
  255. UNUSED(context);
  256. FuriString* file_name;
  257. file_name = furi_string_alloc();
  258. furi_string_set(file_name, ANY_PATH("subghz/test.sub"));
  259. Storage* storage = furi_record_open(RECORD_STORAGE);
  260. FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
  261. FuriString* temp_str;
  262. temp_str = furi_string_alloc();
  263. uint32_t temp_data32;
  264. bool check_file = false;
  265. do {
  266. if(furi_string_size(args)) {
  267. if(!args_read_string_and_trim(args, file_name)) {
  268. cli_print_usage(
  269. "subghz decode_raw", "<file_name: path_RAW_file>", furi_string_get_cstr(args));
  270. break;
  271. }
  272. }
  273. if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) {
  274. printf(
  275. "subghz decode_raw \033[0;31mError open file\033[0m %s\r\n",
  276. furi_string_get_cstr(file_name));
  277. break;
  278. }
  279. if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
  280. printf("subghz decode_raw \033[0;31mMissing or incorrect header\033[0m\r\n");
  281. break;
  282. }
  283. if(!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE) &&
  284. temp_data32 == SUBGHZ_KEY_FILE_VERSION) {
  285. } else {
  286. printf("subghz decode_raw \033[0;31mType or version mismatch\033[0m\r\n");
  287. break;
  288. }
  289. check_file = true;
  290. } while(false);
  291. furi_string_free(temp_str);
  292. flipper_format_free(fff_data_file);
  293. furi_record_close(RECORD_STORAGE);
  294. if(check_file) {
  295. // Allocate context
  296. SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx));
  297. SubGhzEnvironment* environment = subghz_environment_alloc();
  298. if(subghz_environment_load_keystore(
  299. environment, EXT_PATH("subghz/assets/keeloq_mfcodes"))) {
  300. printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n");
  301. } else {
  302. printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n");
  303. }
  304. if(subghz_environment_load_keystore(
  305. environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"))) {
  306. printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n");
  307. } else {
  308. printf(
  309. "SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n");
  310. }
  311. subghz_environment_set_came_atomo_rainbow_table_file_name(
  312. environment, EXT_PATH("subghz/assets/came_atomo"));
  313. subghz_environment_set_nice_flor_s_rainbow_table_file_name(
  314. environment, EXT_PATH("subghz/assets/nice_flor_s"));
  315. SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
  316. subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
  317. subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance);
  318. SubGhzFileEncoderWorker* file_worker_encoder = subghz_file_encoder_worker_alloc();
  319. if(subghz_file_encoder_worker_start(file_worker_encoder, furi_string_get_cstr(file_name))) {
  320. //the worker needs a file in order to open and read part of the file
  321. furi_delay_ms(100);
  322. }
  323. printf(
  324. "Listening at \033[0;33m%s\033[0m.\r\n\r\nPress CTRL+C to stop\r\n\r\n",
  325. furi_string_get_cstr(file_name));
  326. LevelDuration level_duration;
  327. while(!cli_cmd_interrupt_received(cli)) {
  328. furi_delay_us(500); //you need to have time to read from the file from the SD card
  329. level_duration = subghz_file_encoder_worker_get_level_duration(file_worker_encoder);
  330. if(!level_duration_is_reset(level_duration)) {
  331. bool level = level_duration_get_level(level_duration);
  332. uint32_t duration = level_duration_get_duration(level_duration);
  333. subghz_receiver_decode(receiver, level, duration);
  334. } else {
  335. break;
  336. }
  337. }
  338. printf("\r\nPackets recieved \033[0;32m%u\033[0m\r\n", instance->packet_count);
  339. // Cleanup
  340. subghz_receiver_free(receiver);
  341. subghz_environment_free(environment);
  342. if(subghz_file_encoder_worker_is_running(file_worker_encoder)) {
  343. subghz_file_encoder_worker_stop(file_worker_encoder);
  344. }
  345. subghz_file_encoder_worker_free(file_worker_encoder);
  346. free(instance);
  347. }
  348. furi_string_free(file_name);
  349. }
  350. static void subghz_cli_command_print_usage() {
  351. printf("Usage:\r\n");
  352. printf("subghz <cmd> <args>\r\n");
  353. printf("Cmd list:\r\n");
  354. printf("\tchat <frequency:in Hz>\t - Chat with other Flippers\r\n");
  355. printf(
  356. "\ttx <3 byte Key: in hex> <frequency: in Hz> <te: us> <repeat: count>\t - Transmitting key\r\n");
  357. printf("\trx <frequency:in Hz>\t - Reception key\r\n");
  358. printf("\tdecode_raw <file_name: path_RAW_file>\t - Testing\r\n");
  359. if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
  360. printf("\r\n");
  361. printf(" debug cmd:\r\n");
  362. printf("\ttx_carrier <frequency:in Hz>\t - Transmit carrier\r\n");
  363. printf("\trx_carrier <frequency:in Hz>\t - Receiv carrier\r\n");
  364. printf(
  365. "\tencrypt_keeloq <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt keeloq manufacture keys\r\n");
  366. printf(
  367. "\tencrypt_raw <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt RAW data\r\n");
  368. }
  369. }
  370. static void subghz_cli_command_encrypt_keeloq(Cli* cli, FuriString* args) {
  371. UNUSED(cli);
  372. uint8_t iv[16];
  373. FuriString* source;
  374. FuriString* destination;
  375. source = furi_string_alloc();
  376. destination = furi_string_alloc();
  377. SubGhzKeystore* keystore = subghz_keystore_alloc();
  378. do {
  379. if(!args_read_string_and_trim(args, source)) {
  380. subghz_cli_command_print_usage();
  381. break;
  382. }
  383. if(!args_read_string_and_trim(args, destination)) {
  384. subghz_cli_command_print_usage();
  385. break;
  386. }
  387. if(!args_read_hex_bytes(args, iv, 16)) {
  388. subghz_cli_command_print_usage();
  389. break;
  390. }
  391. if(!subghz_keystore_load(keystore, furi_string_get_cstr(source))) {
  392. printf("Failed to load Keystore");
  393. break;
  394. }
  395. if(!subghz_keystore_save(keystore, furi_string_get_cstr(destination), iv)) {
  396. printf("Failed to save Keystore");
  397. break;
  398. }
  399. } while(false);
  400. subghz_keystore_free(keystore);
  401. furi_string_free(destination);
  402. furi_string_free(source);
  403. }
  404. static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) {
  405. UNUSED(cli);
  406. uint8_t iv[16];
  407. FuriString* source;
  408. FuriString* destination;
  409. source = furi_string_alloc();
  410. destination = furi_string_alloc();
  411. do {
  412. if(!args_read_string_and_trim(args, source)) {
  413. subghz_cli_command_print_usage();
  414. break;
  415. }
  416. if(!args_read_string_and_trim(args, destination)) {
  417. subghz_cli_command_print_usage();
  418. break;
  419. }
  420. if(!args_read_hex_bytes(args, iv, 16)) {
  421. subghz_cli_command_print_usage();
  422. break;
  423. }
  424. if(!subghz_keystore_raw_encrypted_save(
  425. furi_string_get_cstr(source), furi_string_get_cstr(destination), iv)) {
  426. printf("Failed to save Keystore");
  427. break;
  428. }
  429. } while(false);
  430. furi_string_free(destination);
  431. furi_string_free(source);
  432. }
  433. static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
  434. uint32_t frequency = 433920000;
  435. if(furi_string_size(args)) {
  436. int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency);
  437. if(ret != 1) {
  438. printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
  439. cli_print_usage("subghz chat", "<Frequency: in Hz>", furi_string_get_cstr(args));
  440. return;
  441. }
  442. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  443. printf(
  444. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  445. frequency);
  446. return;
  447. }
  448. }
  449. if(!furi_hal_region_is_frequency_allowed(frequency)) {
  450. printf(
  451. "In your region, only reception on this frequency (%lu) is allowed,\r\n"
  452. "the actual operation of the application is not possible\r\n ",
  453. frequency);
  454. return;
  455. }
  456. SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli);
  457. if(!subghz_chat_worker_start(subghz_chat, frequency)) {
  458. printf("Startup error SubGhzChatWorker\r\n");
  459. if(subghz_chat_worker_is_running(subghz_chat)) {
  460. subghz_chat_worker_stop(subghz_chat);
  461. subghz_chat_worker_free(subghz_chat);
  462. }
  463. return;
  464. }
  465. printf("Receiving at frequency %lu Hz\r\n", frequency);
  466. printf("Press CTRL+C to stop\r\n");
  467. furi_hal_power_suppress_charge_enter();
  468. size_t message_max_len = 64;
  469. uint8_t message[64] = {0};
  470. FuriString* input;
  471. input = furi_string_alloc();
  472. FuriString* name;
  473. name = furi_string_alloc();
  474. FuriString* output;
  475. output = furi_string_alloc();
  476. FuriString* sysmsg;
  477. sysmsg = furi_string_alloc();
  478. bool exit = false;
  479. SubGhzChatEvent chat_event;
  480. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  481. furi_string_printf(name, "\033[0;33m%s\033[0m: ", furi_hal_version_get_name_ptr());
  482. furi_string_set(input, name);
  483. printf("%s", furi_string_get_cstr(input));
  484. fflush(stdout);
  485. while(!exit) {
  486. chat_event = subghz_chat_worker_get_event_chat(subghz_chat);
  487. switch(chat_event.event) {
  488. case SubGhzChatEventInputData:
  489. if(chat_event.c == CliSymbolAsciiETX) {
  490. printf("\r\n");
  491. chat_event.event = SubGhzChatEventUserExit;
  492. subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
  493. break;
  494. } else if(
  495. (chat_event.c == CliSymbolAsciiBackspace) || (chat_event.c == CliSymbolAsciiDel)) {
  496. size_t len = furi_string_utf8_length(input);
  497. if(len > furi_string_utf8_length(name)) {
  498. printf("%s", "\e[D\e[1P");
  499. fflush(stdout);
  500. //delete 1 char UTF
  501. const char* str = furi_string_get_cstr(input);
  502. size_t size = 0;
  503. FuriStringUTF8State s = FuriStringUTF8StateStarting;
  504. FuriStringUnicodeValue u = 0;
  505. furi_string_reset(sysmsg);
  506. while(*str) {
  507. furi_string_utf8_decode(*str, &s, &u);
  508. if((s == FuriStringUTF8StateError) || s == FuriStringUTF8StateStarting) {
  509. furi_string_utf8_push(sysmsg, u);
  510. if(++size >= len - 1) break;
  511. s = FuriStringUTF8StateStarting;
  512. }
  513. str++;
  514. }
  515. furi_string_set(input, sysmsg);
  516. }
  517. } else if(chat_event.c == CliSymbolAsciiCR) {
  518. printf("\r\n");
  519. furi_string_push_back(input, '\r');
  520. furi_string_push_back(input, '\n');
  521. while(!subghz_chat_worker_write(
  522. subghz_chat,
  523. (uint8_t*)furi_string_get_cstr(input),
  524. strlen(furi_string_get_cstr(input)))) {
  525. furi_delay_ms(10);
  526. }
  527. furi_string_printf(input, "%s", furi_string_get_cstr(name));
  528. printf("%s", furi_string_get_cstr(input));
  529. fflush(stdout);
  530. } else if(chat_event.c == CliSymbolAsciiLF) {
  531. //cut out the symbol \n
  532. } else {
  533. putc(chat_event.c, stdout);
  534. fflush(stdout);
  535. furi_string_push_back(input, chat_event.c);
  536. break;
  537. case SubGhzChatEventRXData:
  538. do {
  539. memset(message, 0x00, message_max_len);
  540. size_t len = subghz_chat_worker_read(subghz_chat, message, message_max_len);
  541. for(size_t i = 0; i < len; i++) {
  542. furi_string_push_back(output, message[i]);
  543. if(message[i] == '\n') {
  544. printf("\r");
  545. for(uint8_t i = 0; i < 80; i++) {
  546. printf(" ");
  547. }
  548. printf("\r %s", furi_string_get_cstr(output));
  549. printf("%s", furi_string_get_cstr(input));
  550. fflush(stdout);
  551. furi_string_reset(output);
  552. }
  553. }
  554. } while(subghz_chat_worker_available(subghz_chat));
  555. break;
  556. case SubGhzChatEventNewMessage:
  557. notification_message(notification, &sequence_single_vibro);
  558. break;
  559. case SubGhzChatEventUserEntrance:
  560. furi_string_printf(
  561. sysmsg,
  562. "\033[0;34m%s joined chat.\033[0m\r\n",
  563. furi_hal_version_get_name_ptr());
  564. subghz_chat_worker_write(
  565. subghz_chat,
  566. (uint8_t*)furi_string_get_cstr(sysmsg),
  567. strlen(furi_string_get_cstr(sysmsg)));
  568. break;
  569. case SubGhzChatEventUserExit:
  570. furi_string_printf(
  571. sysmsg, "\033[0;31m%s left chat.\033[0m\r\n", furi_hal_version_get_name_ptr());
  572. subghz_chat_worker_write(
  573. subghz_chat,
  574. (uint8_t*)furi_string_get_cstr(sysmsg),
  575. strlen(furi_string_get_cstr(sysmsg)));
  576. furi_delay_ms(10);
  577. exit = true;
  578. break;
  579. default:
  580. FURI_LOG_W("SubGhzChat", "Error event");
  581. break;
  582. }
  583. }
  584. if(!cli_is_connected(cli)) {
  585. printf("\r\n");
  586. chat_event.event = SubGhzChatEventUserExit;
  587. subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
  588. }
  589. }
  590. furi_string_free(input);
  591. furi_string_free(name);
  592. furi_string_free(output);
  593. furi_string_free(sysmsg);
  594. furi_hal_power_suppress_charge_exit();
  595. furi_record_close(RECORD_NOTIFICATION);
  596. if(subghz_chat_worker_is_running(subghz_chat)) {
  597. subghz_chat_worker_stop(subghz_chat);
  598. subghz_chat_worker_free(subghz_chat);
  599. }
  600. printf("\r\nExit chat\r\n");
  601. }
  602. static void subghz_cli_command(Cli* cli, FuriString* args, void* context) {
  603. FuriString* cmd;
  604. cmd = furi_string_alloc();
  605. do {
  606. if(!args_read_string_and_trim(args, cmd)) {
  607. subghz_cli_command_print_usage();
  608. break;
  609. }
  610. if(furi_string_cmp_str(cmd, "chat") == 0) {
  611. subghz_cli_command_chat(cli, args);
  612. break;
  613. }
  614. if(furi_string_cmp_str(cmd, "tx") == 0) {
  615. subghz_cli_command_tx(cli, args, context);
  616. break;
  617. }
  618. if(furi_string_cmp_str(cmd, "rx") == 0) {
  619. subghz_cli_command_rx(cli, args, context);
  620. break;
  621. }
  622. if(furi_string_cmp_str(cmd, "decode_raw") == 0) {
  623. subghz_cli_command_decode_raw(cli, args, context);
  624. break;
  625. }
  626. if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
  627. if(furi_string_cmp_str(cmd, "encrypt_keeloq") == 0) {
  628. subghz_cli_command_encrypt_keeloq(cli, args);
  629. break;
  630. }
  631. if(furi_string_cmp_str(cmd, "encrypt_raw") == 0) {
  632. subghz_cli_command_encrypt_raw(cli, args);
  633. break;
  634. }
  635. if(furi_string_cmp_str(cmd, "tx_carrier") == 0) {
  636. subghz_cli_command_tx_carrier(cli, args, context);
  637. break;
  638. }
  639. if(furi_string_cmp_str(cmd, "rx_carrier") == 0) {
  640. subghz_cli_command_rx_carrier(cli, args, context);
  641. break;
  642. }
  643. }
  644. subghz_cli_command_print_usage();
  645. } while(false);
  646. furi_string_free(cmd);
  647. }
  648. static bool
  649. subghz_on_system_start_istream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {
  650. File* file = istream->state;
  651. uint16_t ret = storage_file_read(file, buf, count);
  652. return (count == ret);
  653. }
  654. static bool subghz_on_system_start_istream_decode_band(
  655. pb_istream_t* stream,
  656. const pb_field_t* field,
  657. void** arg) {
  658. (void)field;
  659. FuriHalRegion* region = *arg;
  660. PB_Region_Band band = {0};
  661. if(!pb_decode(stream, PB_Region_Band_fields, &band)) {
  662. FURI_LOG_E("SubGhzOnStart", "PB Region band decode error: %s", PB_GET_ERROR(stream));
  663. return false;
  664. }
  665. region->bands_count += 1;
  666. region =
  667. realloc(region, sizeof(FuriHalRegion) + sizeof(FuriHalRegionBand) * region->bands_count);
  668. size_t pos = region->bands_count - 1;
  669. region->bands[pos].start = band.start;
  670. region->bands[pos].end = band.end;
  671. region->bands[pos].power_limit = band.power_limit;
  672. region->bands[pos].duty_cycle = band.duty_cycle;
  673. *arg = region;
  674. FURI_LOG_I(
  675. "SubGhzOnStart",
  676. "Add allowed band: start %ldHz, stop %ldHz, power_limit %ddBm, duty_cycle %d%%",
  677. band.start,
  678. band.end,
  679. band.power_limit,
  680. band.duty_cycle);
  681. return true;
  682. }
  683. void subghz_on_system_start() {
  684. #ifdef SRV_CLI
  685. Cli* cli = furi_record_open(RECORD_CLI);
  686. cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL);
  687. furi_record_close(RECORD_CLI);
  688. #else
  689. UNUSED(subghz_cli_command);
  690. #endif
  691. #ifdef SRV_STORAGE
  692. Storage* storage = furi_record_open(RECORD_STORAGE);
  693. File* file = storage_file_alloc(storage);
  694. FileInfo fileinfo = {0};
  695. PB_Region pb_region = {0};
  696. pb_region.bands.funcs.decode = subghz_on_system_start_istream_decode_band;
  697. do {
  698. if(storage_common_stat(storage, SUBGHZ_REGION_FILENAME, &fileinfo) != FSE_OK ||
  699. fileinfo.size == 0) {
  700. FURI_LOG_W("SubGhzOnStart", "Region data is missing or empty");
  701. break;
  702. }
  703. if(!storage_file_open(file, SUBGHZ_REGION_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) {
  704. FURI_LOG_E("SubGhzOnStart", "Unable to open region data");
  705. break;
  706. }
  707. pb_istream_t istream = {
  708. .callback = subghz_on_system_start_istream_read,
  709. .state = file,
  710. .errmsg = NULL,
  711. .bytes_left = fileinfo.size,
  712. };
  713. pb_region.bands.arg = malloc(sizeof(FuriHalRegion));
  714. if(!pb_decode(&istream, PB_Region_fields, &pb_region)) {
  715. FURI_LOG_E("SubGhzOnStart", "Invalid region data");
  716. free(pb_region.bands.arg);
  717. break;
  718. }
  719. FuriHalRegion* region = pb_region.bands.arg;
  720. memcpy(
  721. region->country_code,
  722. pb_region.country_code->bytes,
  723. pb_region.country_code->size < 4 ? pb_region.country_code->size : 3);
  724. furi_hal_region_set(region);
  725. } while(0);
  726. pb_release(PB_Region_fields, &pb_region);
  727. storage_file_free(file);
  728. furi_record_close(RECORD_STORAGE);
  729. #else
  730. UNUSED(subghz_cli_command);
  731. UNUSED(subghz_on_system_start_istream_decode_band);
  732. UNUSED(subghz_on_system_start_istream_read);
  733. #endif
  734. }