subghz_cli.c 30 KB

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