subghz_cli.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  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, string_t args, void* context) {
  19. UNUSED(context);
  20. uint32_t frequency = 433920000;
  21. if(string_size(args)) {
  22. int ret = sscanf(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>", 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, string_t args, void* context) {
  55. UNUSED(context);
  56. uint32_t frequency = 433920000;
  57. if(string_size(args)) {
  58. int ret = sscanf(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>", 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, string_t 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(string_size(args)) {
  94. int ret = sscanf(string_get_cstr(args), "%lx %lu %lu %lu", &key, &frequency, &te, &repeat);
  95. if(ret != 4) {
  96. printf(
  97. "sscanf returned %d, key: %lx, frequency: %lu, te:%lu, repeat: %lu\r\n",
  98. ret,
  99. key,
  100. frequency,
  101. te,
  102. repeat);
  103. cli_print_usage(
  104. "subghz tx",
  105. "<3 Byte Key: in hex> <Frequency: in Hz> <Te us> <Repeat count>",
  106. string_get_cstr(args));
  107. return;
  108. }
  109. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  110. printf(
  111. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  112. frequency);
  113. return;
  114. }
  115. }
  116. printf(
  117. "Transmitting at %lu, key %lx, te %lu, repeat %lu. Press CTRL+C to stop\r\n",
  118. frequency,
  119. key,
  120. te,
  121. repeat);
  122. string_t flipper_format_string;
  123. string_init_printf(
  124. flipper_format_string,
  125. "Protocol: Princeton\n"
  126. "Bit: 24\n"
  127. "Key: 00 00 00 00 00 %02X %02X %02X\n"
  128. "TE: %d\n"
  129. "Repeat: %d\n",
  130. (uint8_t)((key >> 16) & 0xFF),
  131. (uint8_t)((key >> 8) & 0xFF),
  132. (uint8_t)(key & 0xFF),
  133. te,
  134. repeat);
  135. FlipperFormat* flipper_format = flipper_format_string_alloc();
  136. Stream* stream = flipper_format_get_raw_stream(flipper_format);
  137. stream_clean(stream);
  138. stream_write_cstring(stream, string_get_cstr(flipper_format_string));
  139. SubGhzEnvironment* environment = subghz_environment_alloc();
  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. StreamBufferHandle_t 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. BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  167. LevelDuration level_duration = level_duration_make(level, duration);
  168. if(instance->overrun) {
  169. instance->overrun = false;
  170. level_duration = level_duration_reset();
  171. }
  172. size_t ret = xStreamBufferSendFromISR(
  173. instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken);
  174. if(sizeof(LevelDuration) != ret) instance->overrun = true;
  175. portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  176. }
  177. static void subghz_cli_command_rx_callback(
  178. SubGhzReceiver* receiver,
  179. SubGhzProtocolDecoderBase* decoder_base,
  180. void* context) {
  181. SubGhzCliCommandRx* instance = context;
  182. instance->packet_count++;
  183. string_t text;
  184. string_init(text);
  185. subghz_protocol_decoder_base_get_string(decoder_base, text);
  186. subghz_receiver_reset(receiver);
  187. printf("%s", string_get_cstr(text));
  188. string_clear(text);
  189. }
  190. void subghz_cli_command_rx(Cli* cli, string_t args, void* context) {
  191. UNUSED(context);
  192. uint32_t frequency = 433920000;
  193. if(string_size(args)) {
  194. int ret = sscanf(string_get_cstr(args), "%lu", &frequency);
  195. if(ret != 1) {
  196. printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
  197. cli_print_usage("subghz rx", "<Frequency: in Hz>", string_get_cstr(args));
  198. return;
  199. }
  200. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  201. printf(
  202. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  203. frequency);
  204. return;
  205. }
  206. }
  207. // Allocate context and buffers
  208. SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx));
  209. instance->stream = xStreamBufferCreate(sizeof(LevelDuration) * 1024, sizeof(LevelDuration));
  210. furi_check(instance->stream);
  211. SubGhzEnvironment* environment = subghz_environment_alloc();
  212. subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes"));
  213. subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"));
  214. subghz_environment_set_came_atomo_rainbow_table_file_name(
  215. environment, EXT_PATH("subghz/assets/came_atomo"));
  216. subghz_environment_set_nice_flor_s_rainbow_table_file_name(
  217. environment, EXT_PATH("subghz/assets/nice_flor_s"));
  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 =
  234. xStreamBufferReceive(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. vStreamBufferDelete(instance->stream);
  255. free(instance);
  256. }
  257. void subghz_cli_command_decode_raw(Cli* cli, string_t args, void* context) {
  258. UNUSED(context);
  259. string_t file_name;
  260. string_init(file_name);
  261. string_set_str(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. string_t temp_str;
  265. string_init(temp_str);
  266. uint32_t temp_data32;
  267. bool check_file = false;
  268. do {
  269. if(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>", string_get_cstr(args));
  273. break;
  274. }
  275. }
  276. if(!flipper_format_file_open_existing(fff_data_file, string_get_cstr(file_name))) {
  277. printf(
  278. "subghz decode_raw \033[0;31mError open file\033[0m %s\r\n",
  279. 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(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. string_clear(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. SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
  319. subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
  320. subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance);
  321. SubGhzFileEncoderWorker* file_worker_encoder = subghz_file_encoder_worker_alloc();
  322. if(subghz_file_encoder_worker_start(file_worker_encoder, string_get_cstr(file_name))) {
  323. //the worker needs a file in order to open and read part of the file
  324. furi_delay_ms(100);
  325. }
  326. printf(
  327. "Listening at \033[0;33m%s\033[0m.\r\n\r\nPress CTRL+C to stop\r\n\r\n",
  328. string_get_cstr(file_name));
  329. LevelDuration level_duration;
  330. while(!cli_cmd_interrupt_received(cli)) {
  331. furi_delay_us(500); //you need to have time to read from the file from the SD card
  332. level_duration = subghz_file_encoder_worker_get_level_duration(file_worker_encoder);
  333. if(!level_duration_is_reset(level_duration)) {
  334. bool level = level_duration_get_level(level_duration);
  335. uint32_t duration = level_duration_get_duration(level_duration);
  336. subghz_receiver_decode(receiver, level, duration);
  337. } else {
  338. break;
  339. }
  340. }
  341. printf("\r\nPackets recieved \033[0;32m%u\033[0m\r\n", instance->packet_count);
  342. // Cleanup
  343. subghz_receiver_free(receiver);
  344. subghz_environment_free(environment);
  345. if(subghz_file_encoder_worker_is_running(file_worker_encoder)) {
  346. subghz_file_encoder_worker_stop(file_worker_encoder);
  347. }
  348. subghz_file_encoder_worker_free(file_worker_encoder);
  349. free(instance);
  350. }
  351. string_clear(file_name);
  352. }
  353. static void subghz_cli_command_print_usage() {
  354. printf("Usage:\r\n");
  355. printf("subghz <cmd> <args>\r\n");
  356. printf("Cmd list:\r\n");
  357. printf("\tchat <frequency:in Hz>\t - Chat with other Flippers\r\n");
  358. printf(
  359. "\ttx <3 byte Key: in hex> <frequency: in Hz> <te: us> <repeat: count>\t - Transmitting key\r\n");
  360. printf("\trx <frequency:in Hz>\t - Reception key\r\n");
  361. printf("\tdecode_raw <file_name: path_RAW_file>\t - Testing\r\n");
  362. if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
  363. printf("\r\n");
  364. printf(" debug cmd:\r\n");
  365. printf("\ttx_carrier <frequency:in Hz>\t - Transmit carrier\r\n");
  366. printf("\trx_carrier <frequency:in Hz>\t - Receiv carrier\r\n");
  367. printf(
  368. "\tencrypt_keeloq <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt keeloq manufacture keys\r\n");
  369. printf(
  370. "\tencrypt_raw <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt RAW data\r\n");
  371. }
  372. }
  373. static void subghz_cli_command_encrypt_keeloq(Cli* cli, string_t args) {
  374. UNUSED(cli);
  375. uint8_t iv[16];
  376. string_t source;
  377. string_t destination;
  378. string_init(source);
  379. string_init(destination);
  380. SubGhzKeystore* keystore = subghz_keystore_alloc();
  381. do {
  382. if(!args_read_string_and_trim(args, source)) {
  383. subghz_cli_command_print_usage();
  384. break;
  385. }
  386. if(!args_read_string_and_trim(args, destination)) {
  387. subghz_cli_command_print_usage();
  388. break;
  389. }
  390. if(!args_read_hex_bytes(args, iv, 16)) {
  391. subghz_cli_command_print_usage();
  392. break;
  393. }
  394. if(!subghz_keystore_load(keystore, string_get_cstr(source))) {
  395. printf("Failed to load Keystore");
  396. break;
  397. }
  398. if(!subghz_keystore_save(keystore, string_get_cstr(destination), iv)) {
  399. printf("Failed to save Keystore");
  400. break;
  401. }
  402. } while(false);
  403. subghz_keystore_free(keystore);
  404. string_clear(destination);
  405. string_clear(source);
  406. }
  407. static void subghz_cli_command_encrypt_raw(Cli* cli, string_t args) {
  408. UNUSED(cli);
  409. uint8_t iv[16];
  410. string_t source;
  411. string_t destination;
  412. string_init(source);
  413. string_init(destination);
  414. do {
  415. if(!args_read_string_and_trim(args, source)) {
  416. subghz_cli_command_print_usage();
  417. break;
  418. }
  419. if(!args_read_string_and_trim(args, destination)) {
  420. subghz_cli_command_print_usage();
  421. break;
  422. }
  423. if(!args_read_hex_bytes(args, iv, 16)) {
  424. subghz_cli_command_print_usage();
  425. break;
  426. }
  427. if(!subghz_keystore_raw_encrypted_save(
  428. string_get_cstr(source), string_get_cstr(destination), iv)) {
  429. printf("Failed to save Keystore");
  430. break;
  431. }
  432. } while(false);
  433. string_clear(destination);
  434. string_clear(source);
  435. }
  436. static void subghz_cli_command_chat(Cli* cli, string_t args) {
  437. uint32_t frequency = 433920000;
  438. if(string_size(args)) {
  439. int ret = sscanf(string_get_cstr(args), "%lu", &frequency);
  440. if(ret != 1) {
  441. printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
  442. cli_print_usage("subghz chat", "<Frequency: in Hz>", string_get_cstr(args));
  443. return;
  444. }
  445. if(!furi_hal_subghz_is_frequency_valid(frequency)) {
  446. printf(
  447. "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
  448. frequency);
  449. return;
  450. }
  451. }
  452. if(!furi_hal_region_is_frequency_allowed(frequency)) {
  453. printf(
  454. "In your region, only reception on this frequency (%lu) is allowed,\r\n"
  455. "the actual operation of the application is not possible\r\n ",
  456. frequency);
  457. return;
  458. }
  459. SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli);
  460. if(!subghz_chat_worker_start(subghz_chat, frequency)) {
  461. printf("Startup error SubGhzChatWorker\r\n");
  462. if(subghz_chat_worker_is_running(subghz_chat)) {
  463. subghz_chat_worker_stop(subghz_chat);
  464. subghz_chat_worker_free(subghz_chat);
  465. }
  466. return;
  467. }
  468. printf("Receiving at frequency %lu Hz\r\n", frequency);
  469. printf("Press CTRL+C to stop\r\n");
  470. furi_hal_power_suppress_charge_enter();
  471. size_t message_max_len = 64;
  472. uint8_t message[64] = {0};
  473. string_t input;
  474. string_init(input);
  475. string_t name;
  476. string_init(name);
  477. string_t output;
  478. string_init(output);
  479. string_t sysmsg;
  480. string_init(sysmsg);
  481. bool exit = false;
  482. SubGhzChatEvent chat_event;
  483. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  484. string_printf(name, "\033[0;33m%s\033[0m: ", furi_hal_version_get_name_ptr());
  485. string_set(input, name);
  486. printf("%s", string_get_cstr(input));
  487. fflush(stdout);
  488. while(!exit) {
  489. chat_event = subghz_chat_worker_get_event_chat(subghz_chat);
  490. switch(chat_event.event) {
  491. case SubGhzChatEventInputData:
  492. if(chat_event.c == CliSymbolAsciiETX) {
  493. printf("\r\n");
  494. chat_event.event = SubGhzChatEventUserExit;
  495. subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
  496. break;
  497. } else if(
  498. (chat_event.c == CliSymbolAsciiBackspace) || (chat_event.c == CliSymbolAsciiDel)) {
  499. size_t len = string_length_u(input);
  500. if(len > string_length_u(name)) {
  501. printf("%s", "\e[D\e[1P");
  502. fflush(stdout);
  503. //delete 1 char UTF
  504. const char* str = string_get_cstr(input);
  505. size_t size = 0;
  506. m_str1ng_utf8_state_e s = M_STRING_UTF8_STARTING;
  507. string_unicode_t u = 0;
  508. string_reset(sysmsg);
  509. while(*str) {
  510. m_str1ng_utf8_decode(*str, &s, &u);
  511. if((s == M_STRING_UTF8_ERROR) || s == M_STRING_UTF8_STARTING) {
  512. string_push_u(sysmsg, u);
  513. if(++size >= len - 1) break;
  514. s = M_STRING_UTF8_STARTING;
  515. }
  516. str++;
  517. }
  518. string_set(input, sysmsg);
  519. }
  520. } else if(chat_event.c == CliSymbolAsciiCR) {
  521. printf("\r\n");
  522. string_push_back(input, '\r');
  523. string_push_back(input, '\n');
  524. while(!subghz_chat_worker_write(
  525. subghz_chat,
  526. (uint8_t*)string_get_cstr(input),
  527. strlen(string_get_cstr(input)))) {
  528. furi_delay_ms(10);
  529. }
  530. string_printf(input, "%s", string_get_cstr(name));
  531. printf("%s", string_get_cstr(input));
  532. fflush(stdout);
  533. } else if(chat_event.c == CliSymbolAsciiLF) {
  534. //cut out the symbol \n
  535. } else {
  536. putc(chat_event.c, stdout);
  537. fflush(stdout);
  538. string_push_back(input, chat_event.c);
  539. break;
  540. case SubGhzChatEventRXData:
  541. do {
  542. memset(message, 0x00, message_max_len);
  543. size_t len = subghz_chat_worker_read(subghz_chat, message, message_max_len);
  544. for(size_t i = 0; i < len; i++) {
  545. string_push_back(output, message[i]);
  546. if(message[i] == '\n') {
  547. printf("\r");
  548. for(uint8_t i = 0; i < 80; i++) {
  549. printf(" ");
  550. }
  551. printf("\r %s", string_get_cstr(output));
  552. printf("%s", string_get_cstr(input));
  553. fflush(stdout);
  554. string_reset(output);
  555. }
  556. }
  557. } while(subghz_chat_worker_available(subghz_chat));
  558. break;
  559. case SubGhzChatEventNewMessage:
  560. notification_message(notification, &sequence_single_vibro);
  561. break;
  562. case SubGhzChatEventUserEntrance:
  563. string_printf(
  564. sysmsg,
  565. "\033[0;34m%s joined chat.\033[0m\r\n",
  566. furi_hal_version_get_name_ptr());
  567. subghz_chat_worker_write(
  568. subghz_chat,
  569. (uint8_t*)string_get_cstr(sysmsg),
  570. strlen(string_get_cstr(sysmsg)));
  571. break;
  572. case SubGhzChatEventUserExit:
  573. string_printf(
  574. sysmsg, "\033[0;31m%s left chat.\033[0m\r\n", furi_hal_version_get_name_ptr());
  575. subghz_chat_worker_write(
  576. subghz_chat,
  577. (uint8_t*)string_get_cstr(sysmsg),
  578. strlen(string_get_cstr(sysmsg)));
  579. furi_delay_ms(10);
  580. exit = true;
  581. break;
  582. default:
  583. FURI_LOG_W("SubGhzChat", "Error event");
  584. break;
  585. }
  586. }
  587. if(!cli_is_connected(cli)) {
  588. printf("\r\n");
  589. chat_event.event = SubGhzChatEventUserExit;
  590. subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
  591. }
  592. }
  593. string_clear(input);
  594. string_clear(name);
  595. string_clear(output);
  596. string_clear(sysmsg);
  597. furi_hal_power_suppress_charge_exit();
  598. furi_record_close(RECORD_NOTIFICATION);
  599. if(subghz_chat_worker_is_running(subghz_chat)) {
  600. subghz_chat_worker_stop(subghz_chat);
  601. subghz_chat_worker_free(subghz_chat);
  602. }
  603. printf("\r\nExit chat\r\n");
  604. }
  605. static void subghz_cli_command(Cli* cli, string_t args, void* context) {
  606. string_t cmd;
  607. string_init(cmd);
  608. do {
  609. if(!args_read_string_and_trim(args, cmd)) {
  610. subghz_cli_command_print_usage();
  611. break;
  612. }
  613. if(string_cmp_str(cmd, "chat") == 0) {
  614. subghz_cli_command_chat(cli, args);
  615. break;
  616. }
  617. if(string_cmp_str(cmd, "tx") == 0) {
  618. subghz_cli_command_tx(cli, args, context);
  619. break;
  620. }
  621. if(string_cmp_str(cmd, "rx") == 0) {
  622. subghz_cli_command_rx(cli, args, context);
  623. break;
  624. }
  625. if(string_cmp_str(cmd, "decode_raw") == 0) {
  626. subghz_cli_command_decode_raw(cli, args, context);
  627. break;
  628. }
  629. if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
  630. if(string_cmp_str(cmd, "encrypt_keeloq") == 0) {
  631. subghz_cli_command_encrypt_keeloq(cli, args);
  632. break;
  633. }
  634. if(string_cmp_str(cmd, "encrypt_raw") == 0) {
  635. subghz_cli_command_encrypt_raw(cli, args);
  636. break;
  637. }
  638. if(string_cmp_str(cmd, "tx_carrier") == 0) {
  639. subghz_cli_command_tx_carrier(cli, args, context);
  640. break;
  641. }
  642. if(string_cmp_str(cmd, "rx_carrier") == 0) {
  643. subghz_cli_command_rx_carrier(cli, args, context);
  644. break;
  645. }
  646. }
  647. subghz_cli_command_print_usage();
  648. } while(false);
  649. string_clear(cmd);
  650. }
  651. static bool
  652. subghz_on_system_start_istream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {
  653. File* file = istream->state;
  654. uint16_t ret = storage_file_read(file, buf, count);
  655. return (count == ret);
  656. }
  657. static bool subghz_on_system_start_istream_decode_band(
  658. pb_istream_t* stream,
  659. const pb_field_t* field,
  660. void** arg) {
  661. (void)field;
  662. FuriHalRegion* region = *arg;
  663. PB_Region_Band band = {0};
  664. if(!pb_decode(stream, PB_Region_Band_fields, &band)) {
  665. FURI_LOG_E("SubGhzOnStart", "PB Region band decode error: %s", PB_GET_ERROR(stream));
  666. return false;
  667. }
  668. region->bands_count += 1;
  669. region =
  670. realloc(region, sizeof(FuriHalRegion) + sizeof(FuriHalRegionBand) * region->bands_count);
  671. size_t pos = region->bands_count - 1;
  672. region->bands[pos].start = band.start;
  673. region->bands[pos].end = band.end;
  674. region->bands[pos].power_limit = band.power_limit;
  675. region->bands[pos].duty_cycle = band.duty_cycle;
  676. *arg = region;
  677. FURI_LOG_I(
  678. "SubGhzOnStart",
  679. "Add allowed band: start %dHz, stop %dHz, power_limit %ddBm, duty_cycle %d%%",
  680. band.start,
  681. band.end,
  682. band.power_limit,
  683. band.duty_cycle);
  684. return true;
  685. }
  686. void subghz_on_system_start() {
  687. #ifdef SRV_CLI
  688. Cli* cli = furi_record_open(RECORD_CLI);
  689. cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL);
  690. furi_record_close(RECORD_CLI);
  691. #else
  692. UNUSED(subghz_cli_command);
  693. #endif
  694. #ifdef SRV_STORAGE
  695. Storage* storage = furi_record_open(RECORD_STORAGE);
  696. File* file = storage_file_alloc(storage);
  697. FileInfo fileinfo = {0};
  698. PB_Region pb_region = {0};
  699. pb_region.bands.funcs.decode = subghz_on_system_start_istream_decode_band;
  700. do {
  701. if(storage_common_stat(storage, SUBGHZ_REGION_FILENAME, &fileinfo) != FSE_OK ||
  702. fileinfo.size == 0) {
  703. FURI_LOG_W("SubGhzOnStart", "Region data is missing or empty");
  704. break;
  705. }
  706. if(!storage_file_open(file, SUBGHZ_REGION_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) {
  707. FURI_LOG_E("SubGhzOnStart", "Unable to open region data");
  708. break;
  709. }
  710. pb_istream_t istream = {
  711. .callback = subghz_on_system_start_istream_read,
  712. .state = file,
  713. .errmsg = NULL,
  714. .bytes_left = fileinfo.size,
  715. };
  716. pb_region.bands.arg = malloc(sizeof(FuriHalRegion));
  717. if(!pb_decode(&istream, PB_Region_fields, &pb_region)) {
  718. FURI_LOG_E("SubGhzOnStart", "Invalid region data");
  719. free(pb_region.bands.arg);
  720. break;
  721. }
  722. FuriHalRegion* region = pb_region.bands.arg;
  723. memcpy(
  724. region->country_code,
  725. pb_region.country_code->bytes,
  726. pb_region.country_code->size < 4 ? pb_region.country_code->size : 3);
  727. furi_hal_region_set(region);
  728. } while(0);
  729. pb_release(PB_Region_fields, &pb_region);
  730. storage_file_free(file);
  731. furi_record_close(RECORD_STORAGE);
  732. #else
  733. UNUSED(subghz_cli_command);
  734. UNUSED(subghz_on_system_start_istream_decode_band);
  735. UNUSED(subghz_on_system_start_istream_read);
  736. #endif
  737. }