subghz_cli.c 33 KB

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