From 098497cdda76323db762c59c3b4543a1b9461582 Mon Sep 17 00:00:00 2001 From: David Lee Date: Mon, 29 Jan 2024 17:31:12 +0100 Subject: [PATCH] Included library for Subghz signal --- helpers/subghz/subghz.c | 77 ++++ helpers/subghz/subghz.h | 9 + helpers/subghz/subghz_error_type.h | 14 + helpers/subghz/subghz_i.c | 260 ++++++++++++ helpers/subghz/subghz_i.h | 83 ++++ helpers/subghz/subghz_txrx.c | 630 +++++++++++++++++++++++++++++ helpers/subghz/subghz_txrx.h | 336 +++++++++++++++ helpers/subghz/subghz_txrx_i.h | 29 ++ helpers/subghz/subghz_types.h | 77 ++++ helpers/xremote_custom_event.h | 2 + xremote.c | 4 + xremote.h | 6 + 12 files changed, 1527 insertions(+) create mode 100644 helpers/subghz/subghz.c create mode 100644 helpers/subghz/subghz.h create mode 100644 helpers/subghz/subghz_error_type.h create mode 100644 helpers/subghz/subghz_i.c create mode 100644 helpers/subghz/subghz_i.h create mode 100644 helpers/subghz/subghz_txrx.c create mode 100644 helpers/subghz/subghz_txrx.h create mode 100644 helpers/subghz/subghz_txrx_i.h create mode 100644 helpers/subghz/subghz_types.h diff --git a/helpers/subghz/subghz.c b/helpers/subghz/subghz.c new file mode 100644 index 00000000000..adc07c5ac7d --- /dev/null +++ b/helpers/subghz/subghz.c @@ -0,0 +1,77 @@ +/* Reduced variant of the Flipper Zero SubGhz Class */ + +#include "subghz_i.h" +#include "../../helpers/xremote_custom_event.h" +#include "../../helpers/xremote_led.h" +//#include "../meal_pager_storage.h" + +SubGhz* subghz_alloc() { + SubGhz* subghz = malloc(sizeof(SubGhz)); + + subghz->file_path = furi_string_alloc(); + + subghz->txrx = subghz_txrx_alloc(); + + return subghz; +} + +void subghz_free(SubGhz* subghz) { + //TxRx + subghz_txrx_free(subghz->txrx); + + // Furi strings + furi_string_free(subghz->file_path); + + // The rest + free(subghz); +} + +void subghz_scene_transmit_callback_end_tx(void* context) { + furi_assert(context); + //UNUSED(context); + FURI_LOG_D(TAG, "callback end"); + XRemote* app = context; + view_dispatcher_send_custom_event( + app->view_dispatcher, XRemoteCustomEventViewTransmitterSendStop); +} + +void subghz_send(void* context, const char* path) { + //UNUSED(context); + XRemote* app = context; + //SubGhz* subghz = subghz_alloc(); + + FURI_LOG_D(TAG, "loading protocol from file"); + subghz_load_protocol_from_file(app->subghz, path); + + /*Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_file_alloc(storage); + + if(!flipper_format_file_open_existing(ff, MEAL_PAGER_TMP_FILE)) { + //totp_close_config_file(fff_file); + FURI_LOG_E(TAG, "Error reading Temp File %s", MEAL_PAGER_TMP_FILE); + furi_record_close(RECORD_STORAGE); + return; + }*/ + + //subghz_txrx_tx_start(subghz->txrx, ff); + + FURI_LOG_D(TAG, "Starting Transmission"); + subghz_txrx_tx_start( + app->subghz->txrx, + subghz_txrx_get_fff_data(app->subghz->txrx)); //Seems like it must be done this way + + FURI_LOG_D(TAG, "setting sugbhz raw file encoder worker callback"); + subghz_txrx_set_raw_file_encoder_worker_callback_end( + app->subghz->txrx, subghz_scene_transmit_callback_end_tx, app); + app->state_notifications = SubGhzNotificationStateTx; + + /*flipper_format_rewind(ff); + flipper_format_file_close(ff); + flipper_format_free(ff); + + furi_record_close(RECORD_STORAGE);*/ + + //subghz_free(subghz); + FURI_LOG_D(TAG, "Finished Transmitting"); + //meal_pager_blink_stop(app); +} \ No newline at end of file diff --git a/helpers/subghz/subghz.h b/helpers/subghz/subghz.h new file mode 100644 index 00000000000..5fee9798cb7 --- /dev/null +++ b/helpers/subghz/subghz.h @@ -0,0 +1,9 @@ +#pragma once + +#include "subghz_i.h" + +typedef struct SubGhz SubGhz; + +SubGhz* subghz_alloc(); +void subghz_free(SubGhz* subghz); +void subghz_send(void* context, const char* path); \ No newline at end of file diff --git a/helpers/subghz/subghz_error_type.h b/helpers/subghz/subghz_error_type.h new file mode 100644 index 00000000000..0f86d6ea7d1 --- /dev/null +++ b/helpers/subghz/subghz_error_type.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +/** SubGhzErrorType */ +typedef enum { + SubGhzErrorTypeNoError = 0, /** There are no errors */ + SubGhzErrorTypeParseFile = + 1, /** File parsing error, or wrong file structure, or missing required parameters. more accurate data can be obtained through the debug port */ + SubGhzErrorTypeOnlyRX = + 2, /** Transmission on this frequency is blocked by regional settings */ + SubGhzErrorTypeParserOthers = 3, /** Error in protocol parameters description */ +} SubGhzErrorType; diff --git a/helpers/subghz/subghz_i.c b/helpers/subghz/subghz_i.c new file mode 100644 index 00000000000..d38d7a2eb6e --- /dev/null +++ b/helpers/subghz/subghz_i.c @@ -0,0 +1,260 @@ +#include "subghz_i.h" + +#include "subghz/types.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +//#define TAG "SubGhz" +/* +void subghz_set_default_preset(SubGhz* subghz) { + furi_assert(subghz); + subghz_txrx_set_preset( + subghz->txrx, + "AM650", + subghz_setting_get_default_frequency(subghz_txrx_get_setting(subghz->txrx)), + NULL, + 0); +}*/ + +/*bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { + switch(subghz_txrx_tx_start(subghz->txrx, flipper_format)) { + case SubGhzTxRxStartTxStateErrorParserOthers: + dialog_message_show_storage_error( + subghz->dialogs, "Error in protocol\nparameters\ndescription"); + break; + case SubGhzTxRxStartTxStateErrorOnlyRx: + FURI_LOG_D(TAG, 'Cannot send, only RX possible'); + break; + + default: + return true; + break; + } + return false; +}*/ + +bool subghz_key_load(SubGhz* subghz, const char* file_path) { //, bool show_dialog) { + furi_assert(subghz); + furi_assert(file_path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + Stream* fff_data_stream = + flipper_format_get_raw_stream(subghz_txrx_get_fff_data(subghz->txrx)); + + SubGhzLoadKeyState load_key_state = SubGhzLoadKeyStateParseErr; + FuriString* temp_str = furi_string_alloc(); + uint32_t temp_data32; + + do { + stream_clean(fff_data_stream); + if(!flipper_format_file_open_existing(fff_data_file, file_path)) { + FURI_LOG_E(TAG, "Error open file %s", file_path); + break; + } + + if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + + if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) || + (!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) && + temp_data32 == SUBGHZ_KEY_FILE_VERSION) { + } else { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + //Load frequency + if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { + FURI_LOG_E(TAG, "Missing Frequency"); + break; + } + + if(!subghz_txrx_radio_device_is_frequecy_valid(subghz->txrx, temp_data32)) { + FURI_LOG_E(TAG, "Frequency not supported"); + break; + } + + //Load preset + if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { + FURI_LOG_E(TAG, "Missing Preset"); + break; + } + + furi_string_set_str( + temp_str, subghz_txrx_get_preset_name(subghz->txrx, furi_string_get_cstr(temp_str))); + if(!strcmp(furi_string_get_cstr(temp_str), "")) { + break; + } + SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); + + if(!strcmp(furi_string_get_cstr(temp_str), "CUSTOM")) { + //TODO FL-3551: add Custom_preset_module + //delete preset if it already exists + subghz_setting_delete_custom_preset(setting, furi_string_get_cstr(temp_str)); + //load custom preset from file + if(!subghz_setting_load_custom_preset( + setting, furi_string_get_cstr(temp_str), fff_data_file)) { + FURI_LOG_E(TAG, "Missing Custom preset"); + break; + } + } + size_t preset_index = + subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(temp_str)); + subghz_txrx_set_preset( + subghz->txrx, + furi_string_get_cstr(temp_str), + temp_data32, + subghz_setting_get_preset_data(setting, preset_index), + subghz_setting_get_preset_data_size(setting, preset_index)); + + //Load protocol + if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + + FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx); + if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { + //if RAW + subghz->load_type_file = SubGhzLoadTypeFileRaw; + subghz_protocol_raw_gen_fff_data( + fff_data, file_path, subghz_txrx_radio_device_get_name(subghz->txrx)); + } else { + subghz->load_type_file = SubGhzLoadTypeFileKey; + stream_copy_full( + flipper_format_get_raw_stream(fff_data_file), + flipper_format_get_raw_stream(fff_data)); + } + + if(subghz_txrx_load_decoder_by_name_protocol( + subghz->txrx, furi_string_get_cstr(temp_str))) { + SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize( + subghz_txrx_get_decoder(subghz->txrx), fff_data); + if(status != SubGhzProtocolStatusOk) { + load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr; + break; + } + } else { + FURI_LOG_E(TAG, "Protocol not found"); + break; + } + + load_key_state = SubGhzLoadKeyStateOK; + } while(0); + + furi_string_free(temp_str); + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); + + switch(load_key_state) { + case SubGhzLoadKeyStateParseErr: + //if(show_dialog) { + // dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile"); + //} + return false; + case SubGhzLoadKeyStateProtocolDescriptionErr: + //if(show_dialog) { + // dialog_message_show_storage_error( + // subghz->dialogs, "Error in protocol\nparameters\ndescription"); + //} + return false; + + case SubGhzLoadKeyStateOK: + return true; + + default: + furi_crash("SubGhz: Unknown load_key_state."); + return false; + } + return false; +} + +/*SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz) { + furi_assert(subghz); + return subghz->load_type_file; +}*/ + +bool subghz_load_protocol_from_file(SubGhz* subghz, const char* path) { + furi_assert(subghz); + + //FuriString* file_path = furi_string_alloc(); + + bool res = false; + + /*DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, SUBGHZ_APP_FILENAME_EXTENSION, &I_sub1_10px); + browser_options.base_path = SUBGHZ_APP_FOLDER; + + // Input events and views are managed by file_select + bool res = dialog_file_browser_show( + subghz->dialogs, subghz->file_path, subghz->file_path, &browser_options); + + if(res) {*/ + //res = subghz_key_load(subghz, furi_string_get_cstr(subghz->file_path), true); + res = subghz_key_load(subghz, path); //, true); + //} + + //furi_string_free(file_path); + + return res; +} + +/*bool subghz_file_available(SubGhz* subghz) { + furi_assert(subghz); + bool ret = true; + Storage* storage = furi_record_open(RECORD_STORAGE); + + FS_Error fs_result = + storage_common_stat(storage, furi_string_get_cstr(subghz->file_path), NULL); + + if(fs_result != FSE_OK) { + dialog_message_show_storage_error(subghz->dialogs, "File not available\n file/directory"); + ret = false; + } + + furi_record_close(RECORD_STORAGE); + return ret; +}*/ + +/*bool subghz_path_is_file(FuriString* path) { + return furi_string_end_with(path, SUBGHZ_APP_FILENAME_EXTENSION); +}*/ + +/*void subghz_lock(SubGhz* subghz) { + furi_assert(subghz); + subghz->lock = SubGhzLockOn; +}*/ + +/*void subghz_unlock(SubGhz* subghz) { + furi_assert(subghz); + subghz->lock = SubGhzLockOff; +}*/ + +/*bool subghz_is_locked(SubGhz* subghz) { + furi_assert(subghz); + return (subghz->lock == SubGhzLockOn); +}*/ + +/*void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state) { + furi_assert(subghz); + subghz->rx_key_state = state; +}*/ + +/*SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz) { + furi_assert(subghz); + return subghz->rx_key_state; +}*/ diff --git a/helpers/subghz/subghz_i.h b/helpers/subghz/subghz_i.h new file mode 100644 index 00000000000..8b88f16d3d4 --- /dev/null +++ b/helpers/subghz/subghz_i.h @@ -0,0 +1,83 @@ +#pragma once + +#include "subghz_types.h" +#include "subghz_error_type.h" +#include +#include "subghz.h" +#include "../xremote_storage.h" + +/*#include "views/receiver.h" +#include "views/transmitter.h" +#include "views/subghz_frequency_analyzer.h" +#include "views/subghz_read_raw.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "subghz_history.h" + +#include +#include + +#include "rpc/rpc_app.h" + +#include "helpers/subghz_threshold_rssi.h" + + +*/ +#include "subghz_txrx.h" + +#define SUBGHZ_MAX_LEN_NAME 64 + +typedef struct SubGhz SubGhz; + +struct SubGhz { + SubGhzTxRx* txrx; + FuriString* file_path; + //FuriString* file_path_tmp; + //char file_name_tmp[SUBGHZ_MAX_LEN_NAME]; // just left it in to make the object not empty + //SubGhzNotificationState state_notifications; + + /*SubGhzViewReceiver* subghz_receiver; + SubGhzViewTransmitter* subghz_transmitter; + + SubGhzFrequencyAnalyzer* subghz_frequency_analyzer; + SubGhzReadRAW* subghz_read_raw;*/ + + //SubGhzProtocolFlag filter; + //FuriString* error_str; + //SubGhzLock lock; + //SubGhzThresholdRssi* threshold_rssi; + //SubGhzRxKeyState rx_key_state; + //SubGhzHistory* history; + SubGhzLoadTypeFile load_type_file; + //void* rpc_ctx; +}; + +//void subghz_set_default_preset(SubGhz* subghz); +//void subghz_blink_start(SubGhz* subghz); +//void subghz_blink_stop(SubGhz* subghz); + +//bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); +//void subghz_dialog_message_show_only_rx(SubGhz* subghz); + +bool subghz_key_load(SubGhz* subghz, const char* file_path); //, bool show_dialog); +bool subghz_load_protocol_from_file(SubGhz* subghz, const char* path); +//bool subghz_file_available(SubGhz* subghz); +//SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz); + +//void subghz_lock(SubGhz* subghz); +//void subghz_unlock(SubGhz* subghz); +//bool subghz_is_locked(SubGhz* subghz); + +//void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state); +//SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz); diff --git a/helpers/subghz/subghz_txrx.c b/helpers/subghz/subghz_txrx.c new file mode 100644 index 00000000000..018d02b6d4a --- /dev/null +++ b/helpers/subghz/subghz_txrx.c @@ -0,0 +1,630 @@ +#include "subghz_txrx_i.h" + +#include +#include +#include + +#define TAG "SubGhz" + +static void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) { + UNUSED(instance); + uint8_t attempts = 5; + while(--attempts > 0) { + if(furi_hal_power_enable_otg()) break; + } + if(attempts == 0) { + if(furi_hal_power_get_usb_voltage() < 4.5f) { + FURI_LOG_E( + TAG, + "Error power otg enable. BQ2589 check otg fault = %d", + furi_hal_power_check_otg_fault() ? 1 : 0); + } + } +} + +static void subghz_txrx_radio_device_power_off(SubGhzTxRx* instance) { + UNUSED(instance); + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); +} + +SubGhzTxRx* subghz_txrx_alloc() { + SubGhzTxRx* instance = malloc(sizeof(SubGhzTxRx)); + instance->setting = subghz_setting_alloc(); + subghz_setting_load(instance->setting, EXT_PATH("subghz/assets/setting_user")); + + instance->preset = malloc(sizeof(SubGhzRadioPreset)); + instance->preset->name = furi_string_alloc(); + subghz_txrx_set_preset( + instance, "AM650", subghz_setting_get_default_frequency(instance->setting), NULL, 0); + + instance->txrx_state = SubGhzTxRxStateSleep; + + subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF); + subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable); + + instance->worker = subghz_worker_alloc(); + instance->fff_data = flipper_format_string_alloc(); + + instance->environment = subghz_environment_alloc(); + instance->is_database_loaded = + subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_NAME); + subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_USER_NAME); + subghz_environment_set_came_atomo_rainbow_table_file_name( + instance->environment, SUBGHZ_CAME_ATOMO_DIR_NAME); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + instance->environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); + subghz_environment_set_nice_flor_s_rainbow_table_file_name( + instance->environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); + subghz_environment_set_protocol_registry( + instance->environment, (void*)&subghz_protocol_registry); + instance->receiver = subghz_receiver_alloc_init(instance->environment); + + subghz_worker_set_overrun_callback( + instance->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); + subghz_worker_set_pair_callback( + instance->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); + subghz_worker_set_context(instance->worker, instance->receiver); + + //set default device External + subghz_devices_init(); + instance->radio_device_type = SubGhzRadioDeviceTypeInternal; + instance->radio_device_type = + subghz_txrx_radio_device_set(instance, SubGhzRadioDeviceTypeExternalCC1101); + + FURI_LOG_D(TAG, "completed TXRX alloc"); + + return instance; +} + +void subghz_txrx_free(SubGhzTxRx* instance) { + furi_assert(instance); + FURI_LOG_D(TAG, "freeing TXRX"); + + if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) { + subghz_txrx_radio_device_power_off(instance); + subghz_devices_end(instance->radio_device); + } + + subghz_devices_deinit(); + + subghz_worker_free(instance->worker); + subghz_receiver_free(instance->receiver); + subghz_environment_free(instance->environment); + flipper_format_free(instance->fff_data); + furi_string_free(instance->preset->name); + subghz_setting_free(instance->setting); + + free(instance->preset); + free(instance); +} + +/*bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->is_database_loaded; +}*/ + +void subghz_txrx_set_preset( + SubGhzTxRx* instance, + const char* preset_name, + uint32_t frequency, + uint8_t* preset_data, + size_t preset_data_size) { + furi_assert(instance); + furi_string_set(instance->preset->name, preset_name); + SubGhzRadioPreset* preset = instance->preset; + preset->frequency = frequency; + preset->data = preset_data; + preset->data_size = preset_data_size; +} + +const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset) { + UNUSED(instance); + const char* preset_name = ""; + if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { + preset_name = "AM270"; + } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { + preset_name = "AM650"; + } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) { + preset_name = "FM238"; + } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) { + preset_name = "FM476"; + } else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) { + preset_name = "CUSTOM"; + } else { + FURI_LOG_E(TAG, "Unknown preset"); + } + return preset_name; +} + +/*SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance) { + furi_assert(instance); + return *instance->preset; +}*/ + +/*void subghz_txrx_get_frequency_and_modulation( + SubGhzTxRx* instance, + FuriString* frequency, + FuriString* modulation) { + furi_assert(instance); + SubGhzRadioPreset* preset = instance->preset; + if(frequency != NULL) { + furi_string_printf( + frequency, + "%03ld.%02ld", + preset->frequency / 1000000 % 1000, + preset->frequency / 10000 % 100); + } + if(modulation != NULL) { + furi_string_printf(modulation, "%.2s", furi_string_get_cstr(preset->name)); + } +}*/ + +static void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) { + furi_assert(instance); + subghz_devices_reset(instance->radio_device); + subghz_devices_idle(instance->radio_device); + subghz_devices_load_preset(instance->radio_device, FuriHalSubGhzPresetCustom, preset_data); + instance->txrx_state = SubGhzTxRxStateIDLE; + FURI_LOG_D(TAG, "completed subghz_txrx_begin"); +} + +/*static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) { + furi_assert(instance); + + furi_assert( + instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep); + + subghz_devices_idle(instance->radio_device); + + uint32_t value = subghz_devices_set_frequency(instance->radio_device, frequency); + subghz_devices_flush_rx(instance->radio_device); + subghz_txrx_speaker_on(instance); + + subghz_devices_start_async_rx( + instance->radio_device, subghz_worker_rx_callback, instance->worker); + subghz_worker_start(instance->worker); + instance->txrx_state = SubGhzTxRxStateRx; + return value; +}*/ + +static void subghz_txrx_idle(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->txrx_state != SubGhzTxRxStateSleep) { + subghz_devices_idle(instance->radio_device); + subghz_txrx_speaker_off(instance); + instance->txrx_state = SubGhzTxRxStateIDLE; + } + FURI_LOG_D(TAG, "completed subghz_txrx_idle"); +} + +/*static void subghz_txrx_rx_end(SubGhzTxRx* instance) { + furi_assert(instance); + furi_assert(instance->txrx_state == SubGhzTxRxStateRx); + + if(subghz_worker_is_running(instance->worker)) { + subghz_worker_stop(instance->worker); + subghz_devices_stop_async_rx(instance->radio_device); + } + subghz_devices_idle(instance->radio_device); + subghz_txrx_speaker_off(instance); + instance->txrx_state = SubGhzTxRxStateIDLE; +}*/ + +/*void subghz_txrx_sleep(SubGhzTxRx* instance) { + furi_assert(instance); + subghz_devices_sleep(instance->radio_device); + instance->txrx_state = SubGhzTxRxStateSleep; +}*/ + +static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) { + furi_assert(instance); + furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); + subghz_devices_idle(instance->radio_device); + subghz_devices_set_frequency(instance->radio_device, frequency); + + bool ret = subghz_devices_set_tx(instance->radio_device); + if(ret) { + subghz_txrx_speaker_on(instance); + instance->txrx_state = SubGhzTxRxStateTx; + } + + FURI_LOG_D(TAG, "completed subghz_txrx_tx"); + return ret; +} + +SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format) { + furi_assert(instance); + furi_assert(flipper_format); + + subghz_txrx_stop(instance); + + SubGhzTxRxStartTxState ret = SubGhzTxRxStartTxStateErrorParserOthers; + FuriString* temp_str = furi_string_alloc(); + uint32_t repeat = 200; + + FURI_LOG_D(TAG, "starting loop in subghz_txrx_tx_start"); + do { + FURI_LOG_D(TAG, "looping"); + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) { + FURI_LOG_E(TAG, "Unable Repeat"); + break; + } + //FURI_LOG_D(TAG, "File loaded"); + ret = SubGhzTxRxStartTxStateOk; + + SubGhzRadioPreset* preset = instance->preset; + instance->transmitter = + subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str)); + + if(instance->transmitter) { + FURI_LOG_D(TAG, "transmitter found"); + if(subghz_transmitter_deserialize(instance->transmitter, flipper_format) == + SubGhzProtocolStatusOk) { + //if (false) { + FURI_LOG_D(TAG, "deserialization"); + if(strcmp(furi_string_get_cstr(preset->name), "") != 0) { + FURI_LOG_D(TAG, "got preset name"); + subghz_txrx_begin( + instance, + subghz_setting_get_preset_data_by_name( + instance->setting, furi_string_get_cstr(preset->name))); + FURI_LOG_D(TAG, "loaded preset by name"); + if(preset->frequency) { + if(!subghz_txrx_tx(instance, preset->frequency)) { + FURI_LOG_E(TAG, "Only Rx"); + ret = SubGhzTxRxStartTxStateErrorOnlyRx; + } + FURI_LOG_D(TAG, "got frequency"); + } else { + ret = SubGhzTxRxStartTxStateErrorParserOthers; + } + + } else { + FURI_LOG_E( + TAG, "Unknown name preset \" %s \"", furi_string_get_cstr(preset->name)); + ret = SubGhzTxRxStartTxStateErrorParserOthers; + } + + if(ret == SubGhzTxRxStartTxStateOk) { + //Start TX + FURI_LOG_D(TAG, "starting Async TX"); + subghz_devices_start_async_tx( + instance->radio_device, subghz_transmitter_yield, instance->transmitter); + } + } else { + FURI_LOG_D(TAG, "no deserialization"); + ret = SubGhzTxRxStartTxStateErrorParserOthers; + } + } else { + ret = SubGhzTxRxStartTxStateErrorParserOthers; + } + if(ret != SubGhzTxRxStartTxStateOk) { + FURI_LOG_D(TAG, "state not ok"); + subghz_transmitter_free(instance->transmitter); // Crashes here + if(instance->txrx_state != SubGhzTxRxStateIDLE) { + subghz_txrx_idle(instance); + } + } + } while(false); + furi_string_free(temp_str); + return ret; +} + +/*void subghz_txrx_rx_start(SubGhzTxRx* instance) { + furi_assert(instance); + subghz_txrx_stop(instance); + subghz_txrx_begin( + instance, + subghz_setting_get_preset_data_by_name( + subghz_txrx_get_setting(instance), furi_string_get_cstr(instance->preset->name))); + subghz_txrx_rx(instance, instance->preset->frequency); +}*/ + +/*void subghz_txrx_set_need_save_callback( + SubGhzTxRx* instance, + SubGhzTxRxNeedSaveCallback callback, + void* context) { + furi_assert(instance); + instance->need_save_callback = callback; + instance->need_save_context = context; +}*/ + +static void subghz_txrx_tx_stop(SubGhzTxRx* instance) { + furi_assert(instance); + furi_assert(instance->txrx_state == SubGhzTxRxStateTx); + //Stop TX + subghz_devices_stop_async_tx(instance->radio_device); + subghz_transmitter_stop(instance->transmitter); + subghz_transmitter_free(instance->transmitter); + + //if protocol dynamic then we save the last upload + /*if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) { + if(instance->need_save_callback) { + instance->need_save_callback(instance->need_save_context); + } + }*/ + subghz_txrx_idle(instance); + subghz_txrx_speaker_off(instance); +} + +FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->fff_data; +} + +SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->setting; +} + +void subghz_txrx_stop(SubGhzTxRx* instance) { + furi_assert(instance); + + switch(instance->txrx_state) { + case SubGhzTxRxStateTx: + subghz_txrx_tx_stop(instance); + subghz_txrx_speaker_unmute(instance); + break; + case SubGhzTxRxStateRx: + //subghz_txrx_rx_end(instance); + //subghz_txrx_speaker_mute(instance); + break; + + default: + break; + } +} + +/*void subghz_txrx_hopper_update(SubGhzTxRx* instance) { + furi_assert(instance); + + switch(instance->hopper_state) { + case SubGhzHopperStateOFF: + case SubGhzHopperStatePause: + return; + case SubGhzHopperStateRSSITimeOut: + if(instance->hopper_timeout != 0) { + instance->hopper_timeout--; + return; + } + break; + default: + break; + } + float rssi = -127.0f; + if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) { + // See RSSI Calculation timings in CC1101 17.3 RSSI + rssi = subghz_devices_get_rssi(instance->radio_device); + + // Stay if RSSI is high enough + if(rssi > -90.0f) { + instance->hopper_timeout = 10; + instance->hopper_state = SubGhzHopperStateRSSITimeOut; + return; + } + } else { + instance->hopper_state = SubGhzHopperStateRunnig; + } + // Select next frequency + if(instance->hopper_idx_frequency < + subghz_setting_get_hopper_frequency_count(instance->setting) - 1) { + instance->hopper_idx_frequency++; + } else { + instance->hopper_idx_frequency = 0; + } + + if(instance->txrx_state == SubGhzTxRxStateRx) { + subghz_txrx_rx_end(instance); + }; + if(instance->txrx_state == SubGhzTxRxStateIDLE) { + subghz_receiver_reset(instance->receiver); + instance->preset->frequency = + subghz_setting_get_hopper_frequency(instance->setting, instance->hopper_idx_frequency); + subghz_txrx_rx(instance, instance->preset->frequency); + } +}*/ + +/*SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->hopper_state; +}*/ + +void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state) { + furi_assert(instance); + instance->hopper_state = state; +} + +/*void subghz_txrx_hopper_unpause(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->hopper_state == SubGhzHopperStatePause) { + instance->hopper_state = SubGhzHopperStateRunnig; + } +}*/ + +/*void subghz_txrx_hopper_pause(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->hopper_state == SubGhzHopperStateRunnig) { + instance->hopper_state = SubGhzHopperStatePause; + } +}*/ + +void subghz_txrx_speaker_on(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->speaker_state == SubGhzSpeakerStateEnable) { + if(furi_hal_speaker_acquire(30)) { + subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker); + } else { + instance->speaker_state = SubGhzSpeakerStateDisable; + } + } +} + +void subghz_txrx_speaker_off(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->speaker_state != SubGhzSpeakerStateDisable) { + if(furi_hal_speaker_is_mine()) { + subghz_devices_set_async_mirror_pin(instance->radio_device, NULL); + furi_hal_speaker_release(); + if(instance->speaker_state == SubGhzSpeakerStateShutdown) + instance->speaker_state = SubGhzSpeakerStateDisable; + } + } +} + +/*void subghz_txrx_speaker_mute(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->speaker_state == SubGhzSpeakerStateEnable) { + if(furi_hal_speaker_is_mine()) { + subghz_devices_set_async_mirror_pin(instance->radio_device, NULL); + } + } +}*/ + +void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->speaker_state == SubGhzSpeakerStateEnable) { + if(furi_hal_speaker_is_mine()) { + subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker); + } + } +} + +void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state) { + furi_assert(instance); + instance->speaker_state = state; +} + +/*SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->speaker_state; +}*/ + +bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol) { + furi_assert(instance); + furi_assert(name_protocol); + bool res = false; + instance->decoder_result = + subghz_receiver_search_decoder_base_by_name(instance->receiver, name_protocol); + if(instance->decoder_result) { + res = true; + } + return res; +} + +SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->decoder_result; +} + +/*bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance) { + furi_assert(instance); + return ( + (instance->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == + SubGhzProtocolFlag_Save); +}*/ + +/*bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type) { + furi_assert(instance); + const SubGhzProtocol* protocol = instance->decoder_result->protocol; + if(check_type) { + return ( + ((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) && + protocol->encoder->deserialize && protocol->type == SubGhzProtocolTypeStatic); + } + return ( + ((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) && + protocol->encoder->deserialize); +}*/ + +/*void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter) { + furi_assert(instance); + subghz_receiver_set_filter(instance->receiver, filter); +}*/ + +/*void subghz_txrx_set_rx_calback( + SubGhzTxRx* instance, + SubGhzReceiverCallback callback, + void* context) { + subghz_receiver_set_rx_callback(instance->receiver, callback, context); +}*/ + +void subghz_txrx_set_raw_file_encoder_worker_callback_end( + SubGhzTxRx* instance, + SubGhzProtocolEncoderRAWCallbackEnd callback, + void* context) { + subghz_protocol_raw_file_encoder_worker_set_callback_end( + (SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(instance->transmitter), + callback, + context); +} + +bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name) { + furi_assert(instance); + + bool is_connect = false; + bool is_otg_enabled = furi_hal_power_is_otg_enabled(); + + if(!is_otg_enabled) { + subghz_txrx_radio_device_power_on(instance); + } + + const SubGhzDevice* device = subghz_devices_get_by_name(name); + if(device) { + is_connect = subghz_devices_is_connect(device); + } + + if(!is_otg_enabled) { + subghz_txrx_radio_device_power_off(instance); + } + return is_connect; +} + +SubGhzRadioDeviceType + subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type) { + furi_assert(instance); + + if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 && + subghz_txrx_radio_device_is_external_connected(instance, SUBGHZ_DEVICE_CC1101_EXT_NAME)) { + subghz_txrx_radio_device_power_on(instance); + instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME); + subghz_devices_begin(instance->radio_device); + instance->radio_device_type = SubGhzRadioDeviceTypeExternalCC1101; + } else { + subghz_txrx_radio_device_power_off(instance); + if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) { + subghz_devices_end(instance->radio_device); + } + instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + instance->radio_device_type = SubGhzRadioDeviceTypeInternal; + } + + return instance->radio_device_type; +} + +/*SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->radio_device_type; +}*/ + +/*float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance) { + furi_assert(instance); + return subghz_devices_get_rssi(instance->radio_device); +}*/ + +const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance) { + furi_assert(instance); + return subghz_devices_get_name(instance->radio_device); +} + +bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency) { + furi_assert(instance); + return subghz_devices_is_frequency_valid(instance->radio_device, frequency); +} \ No newline at end of file diff --git a/helpers/subghz/subghz_txrx.h b/helpers/subghz/subghz_txrx.h new file mode 100644 index 00000000000..a74051adea7 --- /dev/null +++ b/helpers/subghz/subghz_txrx.h @@ -0,0 +1,336 @@ +#pragma once + +#include "subghz_types.h" + +#include +#include +#include +#include +#include +#include + +typedef struct SubGhzTxRx SubGhzTxRx; + +typedef void (*SubGhzTxRxNeedSaveCallback)(void* context); + +typedef enum { + SubGhzTxRxStartTxStateOk, + SubGhzTxRxStartTxStateErrorOnlyRx, + SubGhzTxRxStartTxStateErrorParserOthers, +} SubGhzTxRxStartTxState; + +/** + * Allocate SubGhzTxRx + * + * @return SubGhzTxRx* pointer to SubGhzTxRx + */ +SubGhzTxRx* subghz_txrx_alloc(); + +/** + * Free SubGhzTxRx + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_free(SubGhzTxRx* instance); + +/** + * Check if the database is loaded + * + * @param instance Pointer to a SubGhzTxRx + * @return bool True if the database is loaded + */ +//bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance); + +/** + * Set preset + * + * @param instance Pointer to a SubGhzTxRx + * @param preset_name Name of preset + * @param frequency Frequency in Hz + * @param preset_data Data of preset + * @param preset_data_size Size of preset data + */ +void subghz_txrx_set_preset( + SubGhzTxRx* instance, + const char* preset_name, + uint32_t frequency, + uint8_t* preset_data, + size_t preset_data_size); + +/** + * Get name of preset + * + * @param instance Pointer to a SubGhzTxRx + * @param preset String of preset + * @return const char* Name of preset + */ +const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset); + +/** + * Get of preset + * + * @param instance Pointer to a SubGhzTxRx + * @return SubGhzRadioPreset Preset + */ +//SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance); + +/** + * Get string frequency and modulation + * + * @param instance Pointer to a SubGhzTxRx + * @param frequency Pointer to a string frequency + * @param modulation Pointer to a string modulation + */ +/*void subghz_txrx_get_frequency_and_modulation( + SubGhzTxRx* instance, + FuriString* frequency, + FuriString* modulation);*/ + +/** + * Start TX CC1101 + * + * @param instance Pointer to a SubGhzTxRx + * @param flipper_format Pointer to a FlipperFormat + * @return SubGhzTxRxStartTxState + */ +SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format); + +/** + * Start RX CC1101 + * + * @param instance Pointer to a SubGhzTxRx + */ +//void subghz_txrx_rx_start(SubGhzTxRx* instance); + +/** + * Stop TX/RX CC1101 + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_stop(SubGhzTxRx* instance); + +/** + * Set sleep mode CC1101 + * + * @param instance Pointer to a SubGhzTxRx + */ +//void subghz_txrx_sleep(SubGhzTxRx* instance); + +/** + * Update frequency CC1101 in automatic mode (hopper) + * + * @param instance Pointer to a SubGhzTxRx + */ +//void subghz_txrx_hopper_update(SubGhzTxRx* instance); + +/** + * Get state hopper + * + * @param instance Pointer to a SubGhzTxRx + * @return SubGhzHopperState + */ +//SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance); + +/** + * Set state hopper + * + * @param instance Pointer to a SubGhzTxRx + * @param state State hopper + */ +void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state); + +/** + * Unpause hopper + * + * @param instance Pointer to a SubGhzTxRx + */ +//void subghz_txrx_hopper_unpause(SubGhzTxRx* instance); + +/** + * Set pause hopper + * + * @param instance Pointer to a SubGhzTxRx + */ +//void subghz_txrx_hopper_pause(SubGhzTxRx* instance); + +/** + * Speaker on + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_speaker_on(SubGhzTxRx* instance); + +/** + * Speaker off + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_speaker_off(SubGhzTxRx* instance); + +/** + * Speaker mute + * + * @param instance Pointer to a SubGhzTxRx + */ +//void subghz_txrx_speaker_mute(SubGhzTxRx* instance); + +/** + * Speaker unmute + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_speaker_unmute(SubGhzTxRx* instance); + +/** + * Set state speaker + * + * @param instance Pointer to a SubGhzTxRx + * @param state State speaker + */ +void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state); + +/** + * Get state speaker + * + * @param instance Pointer to a SubGhzTxRx + * @return SubGhzSpeakerState + */ +//SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance); + +/** + * load decoder by name protocol + * + * @param instance Pointer to a SubGhzTxRx + * @param name_protocol Name protocol + * @return bool True if the decoder is loaded + */ +bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol); + +/** + * Get decoder + * + * @param instance Pointer to a SubGhzTxRx + * @return SubGhzProtocolDecoderBase* Pointer to a SubGhzProtocolDecoderBase + */ +SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance); + +/** + * Set callback for save data + * + * @param instance Pointer to a SubGhzTxRx + * @param callback Callback for save data + * @param context Context for callback + */ +/*void subghz_txrx_set_need_save_callback( + SubGhzTxRx* instance, + SubGhzTxRxNeedSaveCallback callback, + void* context);*/ + +/** + * Get pointer to a load data key + * + * @param instance Pointer to a SubGhzTxRx + * @return FlipperFormat* + */ +FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance); + +/** + * Get pointer to a SugGhzSetting + * + * @param instance Pointer to a SubGhzTxRx + * @return SubGhzSetting* + */ +SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance); + +/** + * Is it possible to save this protocol + * + * @param instance Pointer to a SubGhzTxRx + * @return bool True if it is possible to save this protocol + */ +//bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance); + +/** + * Is it possible to send this protocol + * + * @param instance Pointer to a SubGhzTxRx + * @return bool True if it is possible to send this protocol + */ +//bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type); + +/** + * Set filter, what types of decoder to use + * + * @param instance Pointer to a SubGhzTxRx + * @param filter Filter + */ +//void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter); + +/** + * Set callback for receive data + * + * @param instance Pointer to a SubGhzTxRx + * @param callback Callback for receive data + * @param context Context for callback + */ +/*void subghz_txrx_set_rx_calback( + SubGhzTxRx* instance, + SubGhzReceiverCallback callback, + void* context);*/ + +/** + * Set callback for Raw decoder, end of data transfer + * + * @param instance Pointer to a SubGhzTxRx + * @param callback Callback for Raw decoder, end of data transfer + * @param context Context for callback + */ +void subghz_txrx_set_raw_file_encoder_worker_callback_end( + SubGhzTxRx* instance, + SubGhzProtocolEncoderRAWCallbackEnd callback, + void* context); + +/* Checking if an external radio device is connected +* +* @param instance Pointer to a SubGhzTxRx +* @param name Name of external radio device +* @return bool True if is connected to the external radio device +*/ +bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name); + +/* Set the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @param radio_device_type Radio device type +* @return SubGhzRadioDeviceType Type of installed radio device +*/ +SubGhzRadioDeviceType + subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type); + +/* Get the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return SubGhzRadioDeviceType Type of installed radio device +*/ +//SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance); + +/* Get RSSI the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return float RSSI +*/ +//float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance); + +/* Get name the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return const char* Name of installed radio device +*/ +const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance); + +/* Get get intelligence whether frequency the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return bool True if the frequency is valid +*/ +bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency); \ No newline at end of file diff --git a/helpers/subghz/subghz_txrx_i.h b/helpers/subghz/subghz_txrx_i.h new file mode 100644 index 00000000000..b7d74fd492f --- /dev/null +++ b/helpers/subghz/subghz_txrx_i.h @@ -0,0 +1,29 @@ +#pragma once + +#include "subghz_txrx.h" + +struct SubGhzTxRx { + SubGhzWorker* worker; + + SubGhzEnvironment* environment; + SubGhzReceiver* receiver; + SubGhzTransmitter* transmitter; + SubGhzProtocolDecoderBase* decoder_result; + FlipperFormat* fff_data; + + SubGhzRadioPreset* preset; + SubGhzSetting* setting; + + uint8_t hopper_timeout; + uint8_t hopper_idx_frequency; + bool is_database_loaded; + SubGhzHopperState hopper_state; + + SubGhzTxRxState txrx_state; + SubGhzSpeakerState speaker_state; + const SubGhzDevice* radio_device; + SubGhzRadioDeviceType radio_device_type; + + SubGhzTxRxNeedSaveCallback need_save_callback; + void* need_save_context; +}; diff --git a/helpers/subghz/subghz_types.h b/helpers/subghz/subghz_types.h new file mode 100644 index 00000000000..1afb6b6c9db --- /dev/null +++ b/helpers/subghz/subghz_types.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +/** SubGhzNotification state */ +typedef enum { + SubGhzNotificationStateStarting, + SubGhzNotificationStateIDLE, + SubGhzNotificationStateTx, + SubGhzNotificationStateRx, + SubGhzNotificationStateRxDone, +} SubGhzNotificationState; + +/** SubGhzTxRx state */ +typedef enum { + SubGhzTxRxStateIDLE, + SubGhzTxRxStateRx, + SubGhzTxRxStateTx, + SubGhzTxRxStateSleep, +} SubGhzTxRxState; + +/** SubGhzHopperState state */ +typedef enum { + SubGhzHopperStateOFF, + SubGhzHopperStateRunnig, + SubGhzHopperStatePause, + SubGhzHopperStateRSSITimeOut, +} SubGhzHopperState; + +/** SubGhzSpeakerState state */ +typedef enum { + SubGhzSpeakerStateDisable, + SubGhzSpeakerStateShutdown, + SubGhzSpeakerStateEnable, +} SubGhzSpeakerState; + +/** SubGhzRadioDeviceType */ +typedef enum { + SubGhzRadioDeviceTypeAuto, + SubGhzRadioDeviceTypeInternal, + SubGhzRadioDeviceTypeExternalCC1101, +} SubGhzRadioDeviceType; + +/** SubGhzRxKeyState state */ +typedef enum { + SubGhzRxKeyStateIDLE, + SubGhzRxKeyStateNoSave, + SubGhzRxKeyStateNeedSave, + SubGhzRxKeyStateBack, + SubGhzRxKeyStateStart, + SubGhzRxKeyStateAddKey, + SubGhzRxKeyStateExit, + SubGhzRxKeyStateRAWLoad, + SubGhzRxKeyStateRAWSave, +} SubGhzRxKeyState; + +/** SubGhzLoadKeyState state */ +typedef enum { + SubGhzLoadKeyStateUnknown, + SubGhzLoadKeyStateOK, + SubGhzLoadKeyStateParseErr, + SubGhzLoadKeyStateProtocolDescriptionErr, +} SubGhzLoadKeyState; + +/** SubGhzLock */ +typedef enum { + SubGhzLockOff, + SubGhzLockOn, +} SubGhzLock; + +/** SubGhz load type file */ +typedef enum { + SubGhzLoadTypeFileNoLoad, + SubGhzLoadTypeFileKey, + SubGhzLoadTypeFileRaw, +} SubGhzLoadTypeFile; diff --git a/helpers/xremote_custom_event.h b/helpers/xremote_custom_event.h index 07099e44180..e5c38c87969 100644 --- a/helpers/xremote_custom_event.h +++ b/helpers/xremote_custom_event.h @@ -50,6 +50,8 @@ typedef enum { XRemoteCustomEventPauseSetUp, XRemoteCustomEventPauseSetDown, XRemoteCustomEventPauseSetOk, + + XRemoteCustomEventViewTransmitterSendStop, } XRemoteCustomEvent; static inline uint32_t xremote_custom_menu_event_pack(uint16_t type, int16_t value) { diff --git a/xremote.c b/xremote.c index 33f6b5f68a4..3331da49e3c 100644 --- a/xremote.c +++ b/xremote.c @@ -64,6 +64,8 @@ XRemote* xremote_app_alloc() { app->loading = loading_alloc(); + app->subghz = subghz_alloc(); + app->text_input = text_input_alloc(); view_dispatcher_add_view( app->view_dispatcher, XRemoteViewIdTextInput, text_input_get_view(app->text_input)); @@ -125,6 +127,8 @@ void xremote_app_free(XRemote* app) { xremote_cross_remote_free(app->cross_remote); + subghz_free(app->subghz); + // View Dispatcher view_dispatcher_remove_view(app->view_dispatcher, XRemoteViewIdMenu); view_dispatcher_remove_view(app->view_dispatcher, XRemoteViewIdCreate); diff --git a/xremote.h b/xremote.h index 99ff6de69b7..578094819e1 100644 --- a/xremote.h +++ b/xremote.h @@ -7,13 +7,18 @@ #include "helpers/xremote_storage.h" #include "models/infrared/xremote_ir_remote.h" #include "models/cross/xremote_cross_remote.h" +#include "helpers/subghz/subghz_types.h" +#include "helpers/subghz/subghz.h" #include "xremote_i.h" +typedef struct SubGhz SubGhz; + typedef struct { Gui* gui; DialogsApp* dialogs; FuriString* file_path; NotificationApp* notification; + SubGhzNotificationState state_notifications; ViewDispatcher* view_dispatcher; Submenu* submenu; Submenu* editmenu; @@ -42,6 +47,7 @@ typedef struct { char* ir_timing_char; bool transmitting; char text_store[XREMOTE_TEXT_STORE_NUM][XREMOTE_TEXT_STORE_SIZE + 1]; + SubGhz* subghz; } XRemote; typedef enum {