subghz_cli.c 30 KB

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