From b912cc79914b74f256cd641b610001cdb8072a2f Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 1 Dec 2021 19:44:39 +0400 Subject: [PATCH] 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 --- applications/cli/cli.c | 10 + applications/subghz/subghz_cli.c | 126 +++++++- .../targets/f6/furi-hal/furi-hal-subghz.c | 111 ++++++- .../targets/f7/furi-hal/furi-hal-subghz.c | 111 ++++++- .../furi-hal-include/furi-hal-subghz.h | 21 ++ lib/drivers/cc1101.c | 19 +- lib/drivers/cc1101_regs.h | 7 +- lib/subghz/subghz_tx_rx_worker.c | 273 ++++++++++++++++++ lib/subghz/subghz_tx_rx_worker.h | 81 ++++++ 9 files changed, 715 insertions(+), 44 deletions(-) create mode 100644 lib/subghz/subghz_tx_rx_worker.c create mode 100644 lib/subghz/subghz_tx_rx_worker.h diff --git a/applications/cli/cli.c b/applications/cli/cli.c index 7d60f7d1..fe449906 100644 --- a/applications/cli/cli.c +++ b/applications/cli/cli.c @@ -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); diff --git a/applications/subghz/subghz_cli.c b/applications/subghz/subghz_cli.c index 62d070ef..9b67614b 100644 --- a/applications/subghz/subghz_cli.c +++ b/applications/subghz/subghz_cli.c @@ -9,11 +9,12 @@ #include #include #include +#include #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 \r\n"); printf("Cmd list:\r\n"); @@ -268,9 +269,10 @@ void subghz_cli_command_print_usage() { "\tencrypt_keeloq \t - Encrypt keeloq manufacture keys\r\n"); printf( "\tencrypt_raw \t - Encrypt RAW data\r\n"); + printf("\tchat \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", "", 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); diff --git a/firmware/targets/f6/furi-hal/furi-hal-subghz.c b/firmware/targets/f6/furi-hal/furi-hal-subghz.c index 86540818..20e9c1f9 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-subghz.c +++ b/firmware/targets/f6/furi-hal/furi-hal-subghz.c @@ -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; diff --git a/firmware/targets/f7/furi-hal/furi-hal-subghz.c b/firmware/targets/f7/furi-hal/furi-hal-subghz.c index 86540818..20e9c1f9 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-subghz.c +++ b/firmware/targets/f7/furi-hal/furi-hal-subghz.c @@ -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; diff --git a/firmware/targets/furi-hal-include/furi-hal-subghz.h b/firmware/targets/furi-hal-include/furi-hal-subghz.h index 97b57e27..cde71705 100644 --- a/firmware/targets/furi-hal-include/furi-hal-subghz.h +++ b/firmware/targets/furi-hal-include/furi-hal-subghz.h @@ -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 diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index af37dea9..94232da4 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -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; } diff --git a/lib/drivers/cc1101_regs.h b/lib/drivers/cc1101_regs.h index 6e71cc37..b3e95b72 100644 --- a/lib/drivers/cc1101_regs.h +++ b/lib/drivers/cc1101_regs.h @@ -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 { diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c new file mode 100644 index 00000000..d151babe --- /dev/null +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -0,0 +1,273 @@ +#include "subghz_tx_rx_worker.h" + +#include +#include + +#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; +} diff --git a/lib/subghz/subghz_tx_rx_worker.h b/lib/subghz/subghz_tx_rx_worker.h new file mode 100644 index 00000000..3c62136e --- /dev/null +++ b/lib/subghz/subghz_tx_rx_worker.h @@ -0,0 +1,81 @@ +#pragma once + +#include + +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);