subghz_cli.c 29 KB

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