SubGhz: sending / receiving messages via subghz (#851)
* SubGhz: add worker subghz_txrx * SubGhz: added support for transferring Russian characters and support for backspace in CLI subghz_txrx * SubGhz: refactoring subghz_txrx_worker, added a callback for accepting data in an empty one RX buffer * SubGhz: fix conflict * SubGhz: fix syntax errors * Cli: document string_move usage and its behavior * FuriHal: update subghz api and documentation. Subghz: move chat to subghz cli subcommand. * Subghz: update text in chat cli Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									54c41e4189
								
							
						
					
					
						commit
						b912cc7991
					
				| @ -113,7 +113,9 @@ void cli_prompt(Cli* cli) { | ||||
| } | ||||
| 
 | ||||
| void cli_reset(Cli* cli) { | ||||
|     // cli->last_line is cleared and cli->line's buffer moved to cli->last_line
 | ||||
|     string_move(cli->last_line, cli->line); | ||||
|     // Reiniting cli->line
 | ||||
|     string_init(cli->line); | ||||
|     cli->cursor_position = 0; | ||||
| } | ||||
| @ -129,7 +131,11 @@ static void cli_handle_backspace(Cli* cli) { | ||||
|         string_reserve(temp, string_size(cli->line) - 1); | ||||
|         string_set_strn(temp, string_get_cstr(cli->line), cli->cursor_position - 1); | ||||
|         string_cat_str(temp, string_get_cstr(cli->line) + cli->cursor_position); | ||||
| 
 | ||||
|         // cli->line is cleared and temp's buffer moved to cli->line
 | ||||
|         string_move(cli->line, temp); | ||||
|         // NO MEMORY LEAK, STOP REPORTING IT
 | ||||
| 
 | ||||
|         cli->cursor_position--; | ||||
|     } else { | ||||
|         cli_putc(CliSymbolAsciiBell); | ||||
| @ -332,7 +338,11 @@ void cli_process_input(Cli* cli) { | ||||
|             string_set_strn(temp, string_get_cstr(cli->line), cli->cursor_position); | ||||
|             string_push_back(temp, c); | ||||
|             string_cat_str(temp, string_get_cstr(cli->line) + cli->cursor_position); | ||||
| 
 | ||||
|             // cli->line is cleared and temp's buffer moved to cli->line
 | ||||
|             string_move(cli->line, temp); | ||||
|             // NO MEMORY LEAK, STOP REPORTING IT
 | ||||
| 
 | ||||
|             // Print character in replace mode
 | ||||
|             printf("\e[4h%c\e[4l", c); | ||||
|             fflush(stdout); | ||||
|  | ||||
| @ -9,11 +9,12 @@ | ||||
| #include <lib/subghz/subghz_keystore.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol_common.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol_princeton.h> | ||||
| #include <lib/subghz/subghz_tx_rx_worker.h> | ||||
| 
 | ||||
| #define SUBGHZ_FREQUENCY_RANGE_STR \ | ||||
|     "299999755...348000000 or 386999938...464000000 or 778999847...928000000" | ||||
| 
 | ||||
| void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { | ||||
| static void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { | ||||
|     uint32_t frequency = 433920000; | ||||
| 
 | ||||
|     if(string_size(args)) { | ||||
| @ -56,7 +57,7 @@ void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { | ||||
|     furi_hal_power_suppress_charge_exit(); | ||||
| } | ||||
| 
 | ||||
| void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) { | ||||
| static void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) { | ||||
|     uint32_t frequency = 433920000; | ||||
| 
 | ||||
|     if(string_size(args)) { | ||||
| @ -96,7 +97,7 @@ void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) { | ||||
|     furi_hal_subghz_sleep(); | ||||
| } | ||||
| 
 | ||||
| void subghz_cli_command_tx(Cli* cli, string_t args, void* context) { | ||||
| static void subghz_cli_command_tx(Cli* cli, string_t args, void* context) { | ||||
|     uint32_t frequency = 433920000; | ||||
|     uint32_t key = 0x0074BADE; | ||||
|     size_t repeat = 10; | ||||
| @ -187,7 +188,7 @@ static void subghz_cli_command_rx_text_callback(string_t text, void* context) { | ||||
|     printf("%s", string_get_cstr(text)); | ||||
| } | ||||
| 
 | ||||
| void subghz_cli_command_rx(Cli* cli, string_t args, void* context) { | ||||
| static void subghz_cli_command_rx(Cli* cli, string_t args, void* context) { | ||||
|     uint32_t frequency = 433920000; | ||||
| 
 | ||||
|     if(string_size(args)) { | ||||
| @ -260,7 +261,7 @@ void subghz_cli_command_rx(Cli* cli, string_t args, void* context) { | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| void subghz_cli_command_print_usage() { | ||||
| static void subghz_cli_command_print_usage() { | ||||
|     printf("Usage:\r\n"); | ||||
|     printf("subghz <cmd> <args>\r\n"); | ||||
|     printf("Cmd list:\r\n"); | ||||
| @ -268,9 +269,10 @@ void subghz_cli_command_print_usage() { | ||||
|         "\tencrypt_keeloq <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt keeloq manufacture keys\r\n"); | ||||
|     printf( | ||||
|         "\tencrypt_raw <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt RAW data\r\n"); | ||||
|     printf("\tchat <frequency:in Herz>\t - Chat with other Flippers\r\n"); | ||||
| } | ||||
| 
 | ||||
| void subghz_cli_command_encrypt_keeloq(Cli* cli, string_t args) { | ||||
| static void subghz_cli_command_encrypt_keeloq(Cli* cli, string_t args) { | ||||
|     uint8_t iv[16]; | ||||
| 
 | ||||
|     string_t source; | ||||
| @ -312,7 +314,7 @@ void subghz_cli_command_encrypt_keeloq(Cli* cli, string_t args) { | ||||
|     string_clear(source); | ||||
| } | ||||
| 
 | ||||
| void subghz_cli_command_encrypt_raw(Cli* cli, string_t args) { | ||||
| static void subghz_cli_command_encrypt_raw(Cli* cli, string_t args) { | ||||
|     uint8_t iv[16]; | ||||
| 
 | ||||
|     string_t source; | ||||
| @ -348,7 +350,110 @@ void subghz_cli_command_encrypt_raw(Cli* cli, string_t args) { | ||||
|     string_clear(source); | ||||
| } | ||||
| 
 | ||||
| void subghz_cli_command(Cli* cli, string_t args, void* context) { | ||||
| static void subghz_cli_command_chat(Cli* cli, string_t args) { | ||||
|     uint32_t frequency = 433920000; | ||||
| 
 | ||||
|     if(string_size(args)) { | ||||
|         int ret = sscanf(string_get_cstr(args), "%lu", &frequency); | ||||
|         if(ret != 1) { | ||||
|             printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); | ||||
|             cli_print_usage("subghz_txrx", "<Frequency in HZ>", string_get_cstr(args)); | ||||
|             return; | ||||
|         } | ||||
|         if(!furi_hal_subghz_is_frequency_valid(frequency)) { | ||||
|             printf( | ||||
|                 "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", | ||||
|                 frequency); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     if(!furi_hal_subghz_is_tx_allowed(frequency)) { | ||||
|         printf( | ||||
|             "In your region, only reception on this frequency (%lu) is allowed,\r\n" | ||||
|             "the actual operation of the application is not possible\r\n ", | ||||
|             frequency); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     SubGhzTxRxWorker* subghz_txrx = subghz_tx_rx_worker_alloc(); | ||||
|     subghz_tx_rx_worker_start(subghz_txrx, frequency); | ||||
| 
 | ||||
|     printf("Receiving at frequency %lu Hz\r\n", frequency); | ||||
|     printf("Press CTRL+C to stop\r\n"); | ||||
| 
 | ||||
|     furi_hal_power_suppress_charge_enter(); | ||||
|     size_t message_max_len = 64; | ||||
|     uint8_t message[64] = {0}; | ||||
|     string_t input; | ||||
|     string_init(input); | ||||
|     string_t name; | ||||
|     string_init(name); | ||||
|     char c; | ||||
|     bool exit = false; | ||||
| 
 | ||||
|     string_printf(name, "\033[0;33m%s\033[0m: ", furi_hal_version_get_name_ptr()); | ||||
|     string_set(input, name); | ||||
|     printf("%s", string_get_cstr(input)); | ||||
|     fflush(stdout); | ||||
| 
 | ||||
|     while(!exit) { | ||||
|         if(furi_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 0) == 1) { | ||||
|             if(c == CliSymbolAsciiETX) { | ||||
|                 printf("\r\n"); | ||||
|                 exit = true; | ||||
|                 break; | ||||
|             } else if((c >= 0x20 && c < 0x7F) || (c >= 0x80 && c < 0xF0)) { | ||||
|                 putc(c, stdout); | ||||
|                 fflush(stdout); | ||||
|                 string_push_back(input, c); | ||||
|             } else if(c == CliSymbolAsciiBackspace) { | ||||
|                 size_t len = string_size(input); | ||||
|                 if(len > string_size(name)) { | ||||
|                     string_set_strn(input, string_get_cstr(input), len - 1); | ||||
|                     printf("\r"); | ||||
|                     for(uint8_t i = 0; i < len; i++) { | ||||
|                         printf(" "); | ||||
|                     } | ||||
|                     printf("\r%s", string_get_cstr(input)); | ||||
|                     fflush(stdout); | ||||
|                 } | ||||
|             } else if(c == CliSymbolAsciiCR) { | ||||
|                 printf("\r\n"); | ||||
|                 subghz_tx_rx_worker_write( | ||||
|                     subghz_txrx, (uint8_t*)string_get_cstr(input), strlen(string_get_cstr(input))); | ||||
|                 string_printf(input, "%s", string_get_cstr(name)); | ||||
|                 printf("%s", string_get_cstr(input)); | ||||
|                 fflush(stdout); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(subghz_tx_rx_worker_available(subghz_txrx)) { | ||||
|             memset(message, 0x00, message_max_len); | ||||
|             subghz_tx_rx_worker_read(subghz_txrx, message, message_max_len); | ||||
|             printf("\r"); | ||||
|             for(uint8_t i = 0; i < 80; i++) { | ||||
|                 printf(" "); | ||||
|             } | ||||
| 
 | ||||
|             printf("\r %s\r\n", message); | ||||
| 
 | ||||
|             printf("%s", string_get_cstr(input)); | ||||
|             fflush(stdout); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     printf("\r\nExit chat\r\n"); | ||||
|     string_clear(input); | ||||
|     string_clear(name); | ||||
|     furi_hal_power_suppress_charge_exit(); | ||||
| 
 | ||||
|     if(subghz_tx_rx_worker_is_running(subghz_txrx)) { | ||||
|         subghz_tx_rx_worker_stop(subghz_txrx); | ||||
|         subghz_tx_rx_worker_free(subghz_txrx); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void subghz_cli_command(Cli* cli, string_t args, void* context) { | ||||
|     string_t cmd; | ||||
|     string_init(cmd); | ||||
| 
 | ||||
| @ -368,6 +473,11 @@ void subghz_cli_command(Cli* cli, string_t args, void* context) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         if(string_cmp_str(cmd, "chat") == 0) { | ||||
|             subghz_cli_command_chat(cli, args); | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         subghz_cli_command_print_usage(); | ||||
|     } while(false); | ||||
| 
 | ||||
|  | ||||
| @ -244,6 +244,54 @@ static const uint8_t furi_hal_subghz_preset_2fsk_dev4_76khz_async_regs[][2] = { | ||||
|     /* End  */ | ||||
|     {0, 0}, | ||||
| }; | ||||
| static const uint8_t furi_hal_subghz_preset_msk_99_97kb_async_regs[][2] = { | ||||
|     /* GPIO GD0 */ | ||||
|     {CC1101_IOCFG0, 0x06}, | ||||
| 
 | ||||
|     {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION
 | ||||
|     {CC1101_SYNC1, 0x46}, | ||||
|     {CC1101_SYNC0, 0x4C}, | ||||
|     {CC1101_ADDR, 0x00}, | ||||
|     {CC1101_PKTLEN, 0x00}, | ||||
|     {CC1101_CHANNR, 0x00}, | ||||
| 
 | ||||
|     {CC1101_PKTCTRL0, 0x05}, | ||||
| 
 | ||||
|     {CC1101_FSCTRL0, 0x23}, | ||||
|     {CC1101_FSCTRL1, 0x06}, | ||||
| 
 | ||||
|     {CC1101_MDMCFG0, 0xF8}, | ||||
|     {CC1101_MDMCFG1, 0x22}, | ||||
|     {CC1101_MDMCFG2, 0x72}, | ||||
|     {CC1101_MDMCFG3, 0xF8}, | ||||
|     {CC1101_MDMCFG4, 0x5B}, | ||||
|     {CC1101_DEVIATN, 0x47}, | ||||
| 
 | ||||
|     {CC1101_MCSM0, 0x18}, | ||||
|     {CC1101_FOCCFG, 0x16}, | ||||
| 
 | ||||
|     {CC1101_AGCCTRL0, 0xB2}, | ||||
|     {CC1101_AGCCTRL1, 0x00}, | ||||
|     {CC1101_AGCCTRL2, 0xC7}, | ||||
| 
 | ||||
|     {CC1101_FREND0, 0x10}, | ||||
|     {CC1101_FREND1, 0x56}, | ||||
| 
 | ||||
|     {CC1101_FSCAL3, 0xE9}, | ||||
|     {CC1101_FSCAL2, 0x2A}, | ||||
|     {CC1101_FSCAL1, 0x00}, | ||||
|     {CC1101_FSCAL0, 0x1F}, | ||||
| 
 | ||||
|     {CC1101_BSCFG, 0x1C}, | ||||
|     {CC1101_FSTEST, 0x59}, | ||||
| 
 | ||||
|     {CC1101_TEST2, 0x81}, | ||||
|     {CC1101_TEST1, 0x35}, | ||||
|     {CC1101_TEST0, 0x09}, | ||||
|     /* End  */ | ||||
|     {0, 0}, | ||||
| }; | ||||
| 
 | ||||
| static const uint8_t furi_hal_subghz_preset_ook_async_patable[8] = { | ||||
|     0x00, | ||||
|     0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12
 | ||||
| @ -261,9 +309,16 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_patable[8] = { | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00 | ||||
| 
 | ||||
| }; | ||||
|     0x00}; | ||||
| static const uint8_t furi_hal_subghz_preset_msk_async_patable[8] = { | ||||
|     0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12
 | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00}; | ||||
| 
 | ||||
| void furi_hal_subghz_init() { | ||||
|     furi_assert(furi_hal_subghz_state == SubGhzStateInit); | ||||
| @ -344,6 +399,9 @@ void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { | ||||
|     } else if(preset == FuriHalSubGhzPreset2FSKDev476Async) { | ||||
|         furi_hal_subghz_load_registers(furi_hal_subghz_preset_2fsk_dev4_76khz_async_regs); | ||||
|         furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); | ||||
|     } else if(preset == FuriHalSubGhzPresetMSK99_97KbAsync) { | ||||
|         furi_hal_subghz_load_registers(furi_hal_subghz_preset_msk_99_97kb_async_regs); | ||||
|         furi_hal_subghz_load_patable(furi_hal_subghz_preset_msk_async_patable); | ||||
|     } else { | ||||
|         furi_crash(NULL); | ||||
|     } | ||||
| @ -369,6 +427,7 @@ void furi_hal_subghz_load_patable(const uint8_t data[8]) { | ||||
| void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size) { | ||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); | ||||
|     cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz); | ||||
|     cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_FIFO, size); | ||||
|     cc1101_write_fifo(&furi_hal_spi_bus_handle_subghz, data, size); | ||||
|     furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); | ||||
| } | ||||
| @ -379,6 +438,31 @@ void furi_hal_subghz_flush_rx() { | ||||
|     furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_subghz_rx_pipe_not_empty() { | ||||
|     CC1101RxBytes status[1]; | ||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); | ||||
|     cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status); | ||||
|     furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); | ||||
|     // TODO: you can add a buffer overflow flag if needed
 | ||||
|     if(status->NUM_RXBYTES > 0) { | ||||
|         return true; | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_subghz_is_rx_data_crc_valid() { | ||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); | ||||
|     uint8_t data[1]; | ||||
|     cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, CC1101_STATUS_LQI | CC1101_BURST, data); | ||||
|     furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); | ||||
|     if(((data[0] >> 7) & 0x01)) { | ||||
|         return true; | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void furi_hal_subghz_read_packet(uint8_t* data, uint8_t* size) { | ||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); | ||||
|     cc1101_read_fifo(&furi_hal_spi_bus_handle_subghz, data, size); | ||||
| @ -460,18 +544,16 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { | ||||
|     return value; | ||||
| } | ||||
| 
 | ||||
| uint32_t furi_hal_subghz_set_frequency(uint32_t value) { | ||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); | ||||
| 
 | ||||
| bool furi_hal_subghz_is_tx_allowed(uint32_t value) { | ||||
|     //checking regional settings
 | ||||
|     bool txrx = false; | ||||
|     bool is_allowed = false; | ||||
|     switch(furi_hal_version_get_hw_region()) { | ||||
|     case FuriHalVersionRegionEuRu: | ||||
|         //433,05..434,79; 868,15..868,55
 | ||||
|         if(!(value >= 433050000 && value <= 434790000) && | ||||
|            !(value >= 868150000 && value <= 868550000)) { | ||||
|         } else { | ||||
|             txrx = true; | ||||
|             is_allowed = true; | ||||
|         } | ||||
|         break; | ||||
|     case FuriHalVersionRegionUsCaAu: | ||||
| @ -480,7 +562,7 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) { | ||||
|            !(value >= 433050000 && value <= 434790000) && | ||||
|            !(value >= 915000000 && value <= 928000000)) { | ||||
|         } else { | ||||
|             txrx = true; | ||||
|             is_allowed = true; | ||||
|         } | ||||
|         break; | ||||
|     case FuriHalVersionRegionJp: | ||||
| @ -488,16 +570,21 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) { | ||||
|         if(!(value >= 312000000 && value <= 315250000) && | ||||
|            !(value >= 920500000 && value <= 923500000)) { | ||||
|         } else { | ||||
|             txrx = true; | ||||
|             is_allowed = true; | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         txrx = true; | ||||
|         is_allowed = true; | ||||
|         break; | ||||
|     } | ||||
|     return is_allowed; | ||||
| } | ||||
| 
 | ||||
|     if(txrx) { | ||||
| uint32_t furi_hal_subghz_set_frequency(uint32_t value) { | ||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); | ||||
| 
 | ||||
|     if(furi_hal_subghz_is_tx_allowed(value)) { | ||||
|         furi_hal_subghz_regulation = SubGhzRegulationTxRx; | ||||
|     } else { | ||||
|         furi_hal_subghz_regulation = SubGhzRegulationOnlyRx; | ||||
|  | ||||
| @ -244,6 +244,54 @@ static const uint8_t furi_hal_subghz_preset_2fsk_dev4_76khz_async_regs[][2] = { | ||||
|     /* End  */ | ||||
|     {0, 0}, | ||||
| }; | ||||
| static const uint8_t furi_hal_subghz_preset_msk_99_97kb_async_regs[][2] = { | ||||
|     /* GPIO GD0 */ | ||||
|     {CC1101_IOCFG0, 0x06}, | ||||
| 
 | ||||
|     {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION
 | ||||
|     {CC1101_SYNC1, 0x46}, | ||||
|     {CC1101_SYNC0, 0x4C}, | ||||
|     {CC1101_ADDR, 0x00}, | ||||
|     {CC1101_PKTLEN, 0x00}, | ||||
|     {CC1101_CHANNR, 0x00}, | ||||
| 
 | ||||
|     {CC1101_PKTCTRL0, 0x05}, | ||||
| 
 | ||||
|     {CC1101_FSCTRL0, 0x23}, | ||||
|     {CC1101_FSCTRL1, 0x06}, | ||||
| 
 | ||||
|     {CC1101_MDMCFG0, 0xF8}, | ||||
|     {CC1101_MDMCFG1, 0x22}, | ||||
|     {CC1101_MDMCFG2, 0x72}, | ||||
|     {CC1101_MDMCFG3, 0xF8}, | ||||
|     {CC1101_MDMCFG4, 0x5B}, | ||||
|     {CC1101_DEVIATN, 0x47}, | ||||
| 
 | ||||
|     {CC1101_MCSM0, 0x18}, | ||||
|     {CC1101_FOCCFG, 0x16}, | ||||
| 
 | ||||
|     {CC1101_AGCCTRL0, 0xB2}, | ||||
|     {CC1101_AGCCTRL1, 0x00}, | ||||
|     {CC1101_AGCCTRL2, 0xC7}, | ||||
| 
 | ||||
|     {CC1101_FREND0, 0x10}, | ||||
|     {CC1101_FREND1, 0x56}, | ||||
| 
 | ||||
|     {CC1101_FSCAL3, 0xE9}, | ||||
|     {CC1101_FSCAL2, 0x2A}, | ||||
|     {CC1101_FSCAL1, 0x00}, | ||||
|     {CC1101_FSCAL0, 0x1F}, | ||||
| 
 | ||||
|     {CC1101_BSCFG, 0x1C}, | ||||
|     {CC1101_FSTEST, 0x59}, | ||||
| 
 | ||||
|     {CC1101_TEST2, 0x81}, | ||||
|     {CC1101_TEST1, 0x35}, | ||||
|     {CC1101_TEST0, 0x09}, | ||||
|     /* End  */ | ||||
|     {0, 0}, | ||||
| }; | ||||
| 
 | ||||
| static const uint8_t furi_hal_subghz_preset_ook_async_patable[8] = { | ||||
|     0x00, | ||||
|     0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12
 | ||||
| @ -261,9 +309,16 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_patable[8] = { | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00 | ||||
| 
 | ||||
| }; | ||||
|     0x00}; | ||||
| static const uint8_t furi_hal_subghz_preset_msk_async_patable[8] = { | ||||
|     0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12
 | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00, | ||||
|     0x00}; | ||||
| 
 | ||||
| void furi_hal_subghz_init() { | ||||
|     furi_assert(furi_hal_subghz_state == SubGhzStateInit); | ||||
| @ -344,6 +399,9 @@ void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { | ||||
|     } else if(preset == FuriHalSubGhzPreset2FSKDev476Async) { | ||||
|         furi_hal_subghz_load_registers(furi_hal_subghz_preset_2fsk_dev4_76khz_async_regs); | ||||
|         furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); | ||||
|     } else if(preset == FuriHalSubGhzPresetMSK99_97KbAsync) { | ||||
|         furi_hal_subghz_load_registers(furi_hal_subghz_preset_msk_99_97kb_async_regs); | ||||
|         furi_hal_subghz_load_patable(furi_hal_subghz_preset_msk_async_patable); | ||||
|     } else { | ||||
|         furi_crash(NULL); | ||||
|     } | ||||
| @ -369,6 +427,7 @@ void furi_hal_subghz_load_patable(const uint8_t data[8]) { | ||||
| void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size) { | ||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); | ||||
|     cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz); | ||||
|     cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_FIFO, size); | ||||
|     cc1101_write_fifo(&furi_hal_spi_bus_handle_subghz, data, size); | ||||
|     furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); | ||||
| } | ||||
| @ -379,6 +438,31 @@ void furi_hal_subghz_flush_rx() { | ||||
|     furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_subghz_rx_pipe_not_empty() { | ||||
|     CC1101RxBytes status[1]; | ||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); | ||||
|     cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status); | ||||
|     furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); | ||||
|     // TODO: you can add a buffer overflow flag if needed
 | ||||
|     if(status->NUM_RXBYTES > 0) { | ||||
|         return true; | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_subghz_is_rx_data_crc_valid() { | ||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); | ||||
|     uint8_t data[1]; | ||||
|     cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, CC1101_STATUS_LQI | CC1101_BURST, data); | ||||
|     furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); | ||||
|     if(((data[0] >> 7) & 0x01)) { | ||||
|         return true; | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void furi_hal_subghz_read_packet(uint8_t* data, uint8_t* size) { | ||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); | ||||
|     cc1101_read_fifo(&furi_hal_spi_bus_handle_subghz, data, size); | ||||
| @ -460,18 +544,16 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { | ||||
|     return value; | ||||
| } | ||||
| 
 | ||||
| uint32_t furi_hal_subghz_set_frequency(uint32_t value) { | ||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); | ||||
| 
 | ||||
| bool furi_hal_subghz_is_tx_allowed(uint32_t value) { | ||||
|     //checking regional settings
 | ||||
|     bool txrx = false; | ||||
|     bool is_allowed = false; | ||||
|     switch(furi_hal_version_get_hw_region()) { | ||||
|     case FuriHalVersionRegionEuRu: | ||||
|         //433,05..434,79; 868,15..868,55
 | ||||
|         if(!(value >= 433050000 && value <= 434790000) && | ||||
|            !(value >= 868150000 && value <= 868550000)) { | ||||
|         } else { | ||||
|             txrx = true; | ||||
|             is_allowed = true; | ||||
|         } | ||||
|         break; | ||||
|     case FuriHalVersionRegionUsCaAu: | ||||
| @ -480,7 +562,7 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) { | ||||
|            !(value >= 433050000 && value <= 434790000) && | ||||
|            !(value >= 915000000 && value <= 928000000)) { | ||||
|         } else { | ||||
|             txrx = true; | ||||
|             is_allowed = true; | ||||
|         } | ||||
|         break; | ||||
|     case FuriHalVersionRegionJp: | ||||
| @ -488,16 +570,21 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) { | ||||
|         if(!(value >= 312000000 && value <= 315250000) && | ||||
|            !(value >= 920500000 && value <= 923500000)) { | ||||
|         } else { | ||||
|             txrx = true; | ||||
|             is_allowed = true; | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         txrx = true; | ||||
|         is_allowed = true; | ||||
|         break; | ||||
|     } | ||||
|     return is_allowed; | ||||
| } | ||||
| 
 | ||||
|     if(txrx) { | ||||
| uint32_t furi_hal_subghz_set_frequency(uint32_t value) { | ||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); | ||||
| 
 | ||||
|     if(furi_hal_subghz_is_tx_allowed(value)) { | ||||
|         furi_hal_subghz_regulation = SubGhzRegulationTxRx; | ||||
|     } else { | ||||
|         furi_hal_subghz_regulation = SubGhzRegulationOnlyRx; | ||||
|  | ||||
| @ -20,6 +20,7 @@ typedef enum { | ||||
|     FuriHalSubGhzPresetOok650Async,     /**< OOK, bandwidth 650kHz, asynchronous */ | ||||
|     FuriHalSubGhzPreset2FSKDev238Async, /**< FM, deviation 2.380371 kHz, asynchronous */ | ||||
|     FuriHalSubGhzPreset2FSKDev476Async, /**< FM, deviation 4.760742 kHz, asynchronous */ | ||||
|     FuriHalSubGhzPresetMSK99_97KbAsync, /**< MSK, deviation 47.60742 kHz, 99.97Kb/s, asynchronous */ | ||||
| } FuriHalSubGhzPreset; | ||||
| 
 | ||||
| /** Switchable Radio Paths */ | ||||
| @ -90,6 +91,18 @@ void furi_hal_subghz_load_patable(const uint8_t data[8]); | ||||
|  */ | ||||
| void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size); | ||||
| 
 | ||||
| /** Check if recieve pipe is not empty
 | ||||
|  * | ||||
|  * @return     true if not empty | ||||
|  */ | ||||
| bool furi_hal_subghz_rx_pipe_not_empty(); | ||||
| 
 | ||||
| /** Check if recieved data crc is valid
 | ||||
|  * | ||||
|  * @return     true if valid | ||||
|  */ | ||||
| bool furi_hal_subghz_is_rx_data_crc_valid(); | ||||
| 
 | ||||
| /** Read packet from FIFO
 | ||||
|  * | ||||
|  * @param      data  pointer | ||||
| @ -148,6 +161,14 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value); | ||||
|  */ | ||||
| uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); | ||||
| 
 | ||||
| /** Сheck if transmission is allowed on this frequency for your flipper region
 | ||||
|  * | ||||
|  * @param      value  frequency in Hz | ||||
|  * | ||||
|  * @return     true if allowed | ||||
|  */ | ||||
| bool furi_hal_subghz_is_tx_allowed(uint32_t value); | ||||
| 
 | ||||
| /** Set frequency
 | ||||
|  * | ||||
|  * @param      value  frequency in Hz | ||||
|  | ||||
| @ -22,14 +22,14 @@ CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t | ||||
|     while(hal_gpio_read(handle->miso)); | ||||
|     furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT); | ||||
| 
 | ||||
|     assert((rx[0].CHIP_RDYn|rx[1].CHIP_RDYn) == 0); | ||||
|     assert((rx[0].CHIP_RDYn | rx[1].CHIP_RDYn) == 0); | ||||
|     return rx[1]; | ||||
| } | ||||
| 
 | ||||
| CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data) { | ||||
|     assert(sizeof(CC1101Status) == 1); | ||||
|     uint8_t tx[2] = { reg|CC1101_READ, 0}; | ||||
|     CC1101Status rx[2] = { 0 }; | ||||
|     uint8_t tx[2] = {reg | CC1101_READ, 0}; | ||||
|     CC1101Status rx[2] = {0}; | ||||
| 
 | ||||
|     while(hal_gpio_read(handle->miso)); | ||||
|     furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT); | ||||
| @ -58,8 +58,6 @@ uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle) { | ||||
| } | ||||
| 
 | ||||
| void cc1101_reset(FuriHalSpiBusHandle* handle) { | ||||
|     delay_us(1000); | ||||
|     delay_us(1000); | ||||
|     cc1101_strobe(handle, CC1101_STROBE_SRES); | ||||
| } | ||||
| 
 | ||||
| @ -130,7 +128,7 @@ void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]) { | ||||
|     while(hal_gpio_read(handle->miso)); | ||||
|     furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, sizeof(rx), CC1101_TIMEOUT); | ||||
| 
 | ||||
|     assert((rx[0].CHIP_RDYn|rx[8].CHIP_RDYn) == 0); | ||||
|     assert((rx[0].CHIP_RDYn | rx[8].CHIP_RDYn) == 0); | ||||
| } | ||||
| 
 | ||||
| uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size) { | ||||
| @ -159,9 +157,14 @@ uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* si | ||||
| 
 | ||||
|     // First byte - packet length
 | ||||
|     furi_hal_spi_bus_trx(handle, buff_tx, buff_rx, 2, CC1101_TIMEOUT); | ||||
|     *size = buff_rx[1]; | ||||
| 
 | ||||
|     // Check that the packet is placed in the receive buffer
 | ||||
|     if(buff_rx[1] > 64) { | ||||
|         *size = 64; | ||||
|     } else { | ||||
|         *size = buff_rx[1]; | ||||
|     } | ||||
|     furi_hal_spi_bus_trx(handle, &buff_tx[1], data, *size, CC1101_TIMEOUT); | ||||
|     cc1101_flush_rx(handle); | ||||
| 
 | ||||
|     return *size; | ||||
| } | ||||
|  | ||||
| @ -89,22 +89,21 @@ extern "C" { | ||||
| #define CC1101_STATUS_PARTNUM           0x30    /** Chip ID Part Number */ | ||||
| #define CC1101_STATUS_VERSION           0x31    /** Chip ID Version */ | ||||
| #define CC1101_STATUS_FREQEST           0x32    /** Frequency Offset Estimate from Demodulator */ | ||||
| #define CC1101_STATUS_LQI               0x33    /** Demodulator Estimate for Link Quality */ | ||||
| #define CC1101_STATUS_LQI               0x33    /** Demodulator Estimate for Link Quality, 7bit-CRC, 6..0-LQI*/ | ||||
| #define CC1101_STATUS_RSSI              0x34    /** Received Signal Strength Indication */ | ||||
| #define CC1101_STATUS_MARCSTATE         0x35    /** Main Radio Control State Machine State */ | ||||
| #define CC1101_STATUS_WORTIME1          0x36    /** High Byte of WOR Time */ | ||||
| #define CC1101_STATUS_WORTIME0          0x37    /** Low Byte of WOR Time */ | ||||
| #define CC1101_STATUS_PKTSTATUS         0x38    /** Current GDOx Status and Packet Status */ | ||||
| #define CC1101_STATUS_VCO_VC_DAC        0x39    /** Current Setting from PLL Calibration Module */ | ||||
| #define CC1101_STATUS_TXBYTES           0x3A    /** Underflow and Number of Bytes */ | ||||
| #define CC1101_STATUS_RXBYTES           0x3B    /** Overflow and Number of Bytes */ | ||||
| #define CC1101_STATUS_TXBYTES           0x3A    /** Underflow and Number of Bytes, 7bit-Underflow, 6..0-Number of Bytes*/ | ||||
| #define CC1101_STATUS_RXBYTES           0x3B    /** Overflow and Number of Bytes, 7bit-Overflow*, 6..0-Number of Bytes*/ | ||||
| #define CC1101_STATUS_RCCTRL1_STATUS    0x3C    /** Last RC Oscillator Calibration Result */ | ||||
| #define CC1101_STATUS_RCCTRL0_STATUS    0x3D    /** Last RC Oscillator Calibration Result */ | ||||
| 
 | ||||
| /* Some special registers, use CC1101_BURST to read/write data */ | ||||
| #define CC1101_PATABLE                  0x3E    /** PATABLE register number, an 8-byte table that defines the PA control settings */ | ||||
| #define CC1101_FIFO                     0x3F    /** FIFO register nunmber, can be combined with CC1101_WRITE and/or CC1101_BURST */ | ||||
| 
 | ||||
| #define CC1101_IOCFG_INV                (1<<6)  /** IOCFG inversion */ | ||||
| 
 | ||||
| typedef enum { | ||||
|  | ||||
							
								
								
									
										273
									
								
								lib/subghz/subghz_tx_rx_worker.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								lib/subghz/subghz_tx_rx_worker.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,273 @@ | ||||
| #include "subghz_tx_rx_worker.h" | ||||
| 
 | ||||
| #include <stream_buffer.h> | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define TAG "SubGhzTxRxWorker" | ||||
| 
 | ||||
| #define GUBGHZ_TXRX_WORKER_BUF_SIZE 2048 | ||||
| //you can not set more than 62 because it will not fit into the FIFO CC1101
 | ||||
| #define GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE 60 | ||||
| 
 | ||||
| #define GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF 40 | ||||
| 
 | ||||
| struct SubGhzTxRxWorker { | ||||
|     FuriThread* thread; | ||||
|     StreamBufferHandle_t stream_tx; | ||||
|     StreamBufferHandle_t stream_rx; | ||||
| 
 | ||||
|     volatile bool worker_running; | ||||
|     volatile bool worker_stoping; | ||||
| 
 | ||||
|     SubGhzTxRxWorkerStatus satus; | ||||
| 
 | ||||
|     uint32_t frequency; | ||||
| 
 | ||||
|     SubGhzTxRxWorkerCallbackHaveRead callback_have_read; | ||||
|     void* context_have_read; | ||||
| }; | ||||
| 
 | ||||
| bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { | ||||
|     furi_assert(instance); | ||||
|     bool ret = false; | ||||
|     size_t stream_tx_free_byte = xStreamBufferSpacesAvailable(instance->stream_tx); | ||||
|     if(size && (stream_tx_free_byte >= size)) { | ||||
|         if(xStreamBufferSend( | ||||
|                instance->stream_tx, data, size, GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF) == | ||||
|            size) { | ||||
|             ret = true; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance) { | ||||
|     furi_assert(instance); | ||||
|     return xStreamBufferBytesAvailable(instance->stream_rx); | ||||
| } | ||||
| 
 | ||||
| size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { | ||||
|     furi_assert(instance); | ||||
|     size_t len = 0; | ||||
|     size_t stream_rx_byte = xStreamBufferBytesAvailable(instance->stream_rx); | ||||
| 
 | ||||
|     if(stream_rx_byte > 0) { | ||||
|         if(stream_rx_byte <= size) { | ||||
|             len = xStreamBufferReceive( | ||||
|                 instance->stream_rx, | ||||
|                 data, | ||||
|                 stream_rx_byte, | ||||
|                 GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); | ||||
|         } else { | ||||
|             len = xStreamBufferReceive( | ||||
|                 instance->stream_rx, data, size, GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); | ||||
|         } | ||||
|     } | ||||
|     return len; | ||||
| } | ||||
| 
 | ||||
| void subghz_tx_rx_worker_set_callback_have_read( | ||||
|     SubGhzTxRxWorker* instance, | ||||
|     SubGhzTxRxWorkerCallbackHaveRead callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(callback); | ||||
|     furi_assert(context); | ||||
|     instance->callback_have_read = callback; | ||||
|     instance->context_have_read = context; | ||||
| } | ||||
| 
 | ||||
| bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* size) { | ||||
|     uint8_t timeout = 20; | ||||
|     bool ret = false; | ||||
|     if(instance->satus != SubGhzTxRxWorkerStatusRx) { | ||||
|         furi_hal_subghz_rx(); | ||||
|         instance->satus = SubGhzTxRxWorkerStatusRx; | ||||
|         osDelay(1); | ||||
|     } | ||||
|     //waiting for reception to complete
 | ||||
|     while(hal_gpio_read(&gpio_cc1101_g0)) { | ||||
|         osDelay(1); | ||||
|         if(!--timeout) { | ||||
|             FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); | ||||
|             furi_hal_subghz_flush_rx(); | ||||
|             furi_hal_subghz_rx(); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(furi_hal_subghz_rx_pipe_not_empty()) { | ||||
|         if(furi_hal_subghz_is_rx_data_crc_valid()) { | ||||
|             furi_hal_subghz_read_packet(data, size); | ||||
|             ret = true; | ||||
|         } | ||||
|         furi_hal_subghz_flush_rx(); | ||||
|         furi_hal_subghz_rx(); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { | ||||
|     uint8_t timeout = 40; | ||||
|     if(instance->satus != SubGhzTxRxWorkerStatusIDLE) { | ||||
|         furi_hal_subghz_idle(); | ||||
|     } | ||||
|     furi_hal_subghz_write_packet(data, size); | ||||
|     instance->satus = SubGhzTxRxWorkerStatusTx; | ||||
| 
 | ||||
|     furi_hal_subghz_tx(); //start send
 | ||||
| 
 | ||||
|     while(!hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted
 | ||||
|         osDelay(1); | ||||
|         if(!--timeout) { | ||||
|             FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     while(hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet
 | ||||
|         osDelay(1); | ||||
|         if(!--timeout) { | ||||
|             FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     furi_hal_subghz_idle(); | ||||
|     instance->satus = SubGhzTxRxWorkerStatusIDLE; | ||||
| } | ||||
| /** Worker thread
 | ||||
|  *  | ||||
|  * @param context  | ||||
|  * @return exit code  | ||||
|  */ | ||||
| static int32_t subghz_tx_rx_worker_thread(void* context) { | ||||
|     SubGhzTxRxWorker* instance = context; | ||||
|     FURI_LOG_I(TAG, "Worker start"); | ||||
| 
 | ||||
|     furi_hal_subghz_reset(); | ||||
|     furi_hal_subghz_idle(); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); | ||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||
| 
 | ||||
|     furi_hal_subghz_set_frequency_and_path(instance->frequency); | ||||
|     furi_hal_subghz_flush_rx(); | ||||
| 
 | ||||
|     uint8_t data[GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE] = {0}; | ||||
|     size_t size_tx = 0; | ||||
|     uint8_t size_rx[1] = {0}; | ||||
|     uint8_t timeout_tx = 0; | ||||
|     bool callback_rx = false; | ||||
| 
 | ||||
|     while(instance->worker_running) { | ||||
|         //transmit
 | ||||
|         size_tx = xStreamBufferBytesAvailable(instance->stream_tx); | ||||
|         if(size_tx > 0 && !timeout_tx) { | ||||
|             timeout_tx = 20; //20ms
 | ||||
|             if(size_tx > GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE) { | ||||
|                 xStreamBufferReceive( | ||||
|                     instance->stream_tx, | ||||
|                     &data, | ||||
|                     GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE, | ||||
|                     GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); | ||||
|                 subghz_tx_rx_worker_tx(instance, data, GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE); | ||||
|             } else { | ||||
|                 //todo checking that he managed to write all the data to the TX buffer
 | ||||
|                 xStreamBufferReceive( | ||||
|                     instance->stream_tx, &data, size_tx, GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); | ||||
|                 subghz_tx_rx_worker_tx(instance, data, size_tx); | ||||
|             } | ||||
|         } else { | ||||
|             //recive
 | ||||
|             if(subghz_tx_rx_worker_rx(instance, data, size_rx)) { | ||||
|                 if(xStreamBufferSpacesAvailable(instance->stream_rx) >= size_rx[0]) { | ||||
|                     if(instance->callback_have_read && | ||||
|                        xStreamBufferBytesAvailable(instance->stream_rx) == 0) { | ||||
|                         callback_rx = true; | ||||
|                     } | ||||
|                     //todo checking that he managed to write all the data to the RX buffer
 | ||||
|                     xStreamBufferSend( | ||||
|                         instance->stream_rx, | ||||
|                         &data, | ||||
|                         size_rx[0], | ||||
|                         GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); | ||||
|                     if(callback_rx) { | ||||
|                         instance->callback_have_read(instance->context_have_read); | ||||
|                         callback_rx = false; | ||||
|                     } | ||||
|                 } else { | ||||
|                     //todo RX buffer overflow
 | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(timeout_tx) timeout_tx--; | ||||
|         osDelay(1); | ||||
|     } | ||||
| 
 | ||||
|     furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); | ||||
|     furi_hal_subghz_sleep(); | ||||
| 
 | ||||
|     FURI_LOG_I(TAG, "Worker stop"); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| SubGhzTxRxWorker* subghz_tx_rx_worker_alloc() { | ||||
|     SubGhzTxRxWorker* instance = furi_alloc(sizeof(SubGhzTxRxWorker)); | ||||
| 
 | ||||
|     instance->thread = furi_thread_alloc(); | ||||
|     furi_thread_set_name(instance->thread, "SubghzTxRxWorker"); | ||||
|     furi_thread_set_stack_size(instance->thread, 2048); | ||||
|     furi_thread_set_context(instance->thread, instance); | ||||
|     furi_thread_set_callback(instance->thread, subghz_tx_rx_worker_thread); | ||||
|     instance->stream_tx = | ||||
|         xStreamBufferCreate(sizeof(uint8_t) * GUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); | ||||
|     instance->stream_rx = | ||||
|         xStreamBufferCreate(sizeof(uint8_t) * GUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); | ||||
| 
 | ||||
|     instance->satus = SubGhzTxRxWorkerStatusIDLE; | ||||
|     instance->worker_stoping = true; | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     vStreamBufferDelete(instance->stream_tx); | ||||
|     vStreamBufferDelete(instance->stream_rx); | ||||
|     furi_thread_free(instance->thread); | ||||
| 
 | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(!instance->worker_running); | ||||
|     bool res = false; | ||||
|     xStreamBufferReset(instance->stream_tx); | ||||
|     xStreamBufferReset(instance->stream_rx); | ||||
| 
 | ||||
|     instance->worker_running = true; | ||||
| 
 | ||||
|     furi_thread_start(instance->thread); | ||||
| 
 | ||||
|     if(furi_hal_subghz_is_tx_allowed(frequency)) { | ||||
|         instance->frequency = frequency; | ||||
|         res = true; | ||||
|     } | ||||
| 
 | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(instance->worker_running); | ||||
| 
 | ||||
|     instance->worker_running = false; | ||||
| 
 | ||||
|     furi_thread_join(instance->thread); | ||||
| } | ||||
| 
 | ||||
| bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance) { | ||||
|     furi_assert(instance); | ||||
|     return instance->worker_running; | ||||
| } | ||||
							
								
								
									
										81
									
								
								lib/subghz/subghz_tx_rx_worker.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								lib/subghz/subghz_tx_rx_worker.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi-hal.h> | ||||
| 
 | ||||
| typedef void (*SubGhzTxRxWorkerCallbackHaveRead)(void* context); | ||||
| 
 | ||||
| typedef struct SubGhzTxRxWorker SubGhzTxRxWorker; | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubGhzTxRxWorkerStatusIDLE, | ||||
|     SubGhzTxRxWorkerStatusTx, | ||||
|     SubGhzTxRxWorkerStatusRx, | ||||
| } SubGhzTxRxWorkerStatus; | ||||
| 
 | ||||
| /** SubGhzTxRxWorker, add data to transfer
 | ||||
|  *  | ||||
|  * @param instance  SubGhzTxRxWorker instance | ||||
|  * @param data      *data | ||||
|  * @param size      data size | ||||
|  * @return bool     true if ok | ||||
|  */ | ||||
| bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size); | ||||
| 
 | ||||
| /** SubGhzTxRxWorker, get available data
 | ||||
|  *  | ||||
|  * @param instance   SubGhzTxRxWorker instance | ||||
|  * @return size_t    data size | ||||
|  */ | ||||
| size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance); | ||||
| 
 | ||||
| /** SubGhzTxRxWorker, read data
 | ||||
|  *  | ||||
|  * @param instance   SubGhzTxRxWorker instance | ||||
|  * @param data       *data | ||||
|  * @param size       max data size, which can be read | ||||
|  * @return size_t    data size, how much is actually read | ||||
|  */ | ||||
| size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size); | ||||
| 
 | ||||
| /** Сallback SubGhzTxRxWorker when there is data to read in an empty buffer
 | ||||
|  *  | ||||
|  * @param instance SubGhzTxRxWorker instance | ||||
|  * @param callback SubGhzTxRxWorkerCallbackHaveRead callback | ||||
|  * @param context | ||||
|  */ | ||||
| void subghz_tx_rx_worker_set_callback_have_read( | ||||
|     SubGhzTxRxWorker* instance, | ||||
|     SubGhzTxRxWorkerCallbackHaveRead callback, | ||||
|     void* context); | ||||
| 
 | ||||
| /** Allocate SubGhzTxRxWorker
 | ||||
|  *  | ||||
|  * @return SubGhzTxRxWorker*  | ||||
|  */ | ||||
| SubGhzTxRxWorker* subghz_tx_rx_worker_alloc(); | ||||
| 
 | ||||
| /** Free SubGhzTxRxWorker
 | ||||
|  *  | ||||
|  * @param instance SubGhzTxRxWorker instance | ||||
|  */ | ||||
| void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance); | ||||
| 
 | ||||
| /** Start SubGhzTxRxWorker
 | ||||
|  *  | ||||
|  * @param instance SubGhzTxRxWorker instance | ||||
|  * @return bool - true if ok | ||||
|  */ | ||||
| bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency); | ||||
| 
 | ||||
| /** Stop SubGhzTxRxWorker
 | ||||
|  *  | ||||
|  * @param instance SubGhzTxRxWorker instance | ||||
|  */ | ||||
| void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance); | ||||
| 
 | ||||
| /** Check if worker is running
 | ||||
|  *  | ||||
|  * @param instance SubGhzTxRxWorker instance | ||||
|  * @return bool - true if running | ||||
|  */ | ||||
| bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance); | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Skorpionm
						Skorpionm