#include "subghz_txrx_i.h" #include "subghz_txrx.h" #include #define TAG "SubGhz" 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); subghz_txrx_set_debug_pin_state(instance, false); 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, EXT_PATH("subghz/assets/keeloq_mfcodes")); subghz_environment_load_keystore( instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); subghz_environment_set_came_atomo_rainbow_table_file_name( instance->environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_alutech_at_4n_rainbow_table_file_name( instance->environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( instance->environment, EXT_PATH("subghz/assets/nice_flor_s")); 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); return instance; } void subghz_txrx_free(SubGhzTxRx* instance) { furi_assert(instance); 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 = NULL; 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, bool long_name) { 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) { if(long_name) { furi_string_printf(modulation, "%s", furi_string_get_cstr(preset->name)); } else { 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); furi_hal_subghz_reset(); furi_hal_subghz_idle(); furi_hal_subghz_load_custom_preset(preset_data); furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); instance->txrx_state = SubGhzTxRxStateIDLE; } static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) { furi_assert(instance); if(!furi_hal_subghz_is_frequency_valid(frequency)) { furi_crash("SubGhz: Incorrect RX frequency."); } furi_assert( instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep); furi_hal_subghz_idle(); uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_flush_rx(); subghz_txrx_speaker_on(instance); furi_hal_subghz_rx(); furi_hal_subghz_start_async_rx(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); furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); furi_hal_subghz_idle(); subghz_txrx_speaker_off(instance); instance->txrx_state = SubGhzTxRxStateIDLE; } 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); furi_hal_subghz_stop_async_rx(); } furi_hal_subghz_idle(); subghz_txrx_speaker_off(instance); instance->txrx_state = SubGhzTxRxStateIDLE; } void subghz_txrx_sleep(SubGhzTxRx* instance) { furi_assert(instance); furi_hal_subghz_sleep(); instance->txrx_state = SubGhzTxRxStateSleep; } static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) { furi_assert(instance); if(!furi_hal_subghz_is_frequency_valid(frequency)) { furi_crash("SubGhz: Incorrect TX frequency."); } furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); furi_hal_subghz_idle(); furi_hal_subghz_set_frequency_and_path(frequency); furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); furi_hal_gpio_init( furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); bool ret = furi_hal_subghz_tx(); if(ret) { subghz_txrx_speaker_on(instance); instance->txrx_state = SubGhzTxRxStateTx; } 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; do { 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; } ret = SubGhzTxRxStartTxStateOk; SubGhzRadioPreset* preset = instance->preset; instance->transmitter = subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str)); if(instance->transmitter) { if(subghz_transmitter_deserialize(instance->transmitter, flipper_format) == SubGhzProtocolStatusOk) { if(strcmp(furi_string_get_cstr(preset->name), "") != 0) { subghz_txrx_begin( instance, subghz_setting_get_preset_data_by_name( instance->setting, furi_string_get_cstr(preset->name))); if(preset->frequency) { if(!subghz_txrx_tx(instance, preset->frequency)) { FURI_LOG_E(TAG, "Only Rx"); ret = SubGhzTxRxStartTxStateErrorOnlyRx; } } 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_hal_subghz_start_async_tx( subghz_transmitter_yield, instance->transmitter); } } else { ret = SubGhzTxRxStartTxStateErrorParserOthers; } } if(ret != SubGhzTxRxStartTxStateOk) { subghz_transmitter_free(instance->transmitter); 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 furi_hal_subghz_stop_async_tx(); 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); //Todo: Show message // notification_message(notifications, &sequence_reset_red); } 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 = furi_hal_subghz_get_rssi(); // Stay if RSSI is high enough if(rssi > -90.0f) { instance->hopper_timeout = 10; instance->hopper_state = SubGhzHopperStateRSSITimeOut; return; } } else { instance->hopper_state = SubGhzHopperStateRunning; } // 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 = SubGhzHopperStateRunning; } } void subghz_txrx_hopper_pause(SubGhzTxRx* instance) { furi_assert(instance); if(instance->hopper_state == SubGhzHopperStateRunning) { instance->hopper_state = SubGhzHopperStatePause; } } void subghz_txrx_speaker_on(SubGhzTxRx* instance) { furi_assert(instance); if(instance->debug_pin_state) { furi_hal_subghz_set_async_mirror_pin(&gpio_ibutton); } if(instance->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_acquire(30)) { if(!instance->debug_pin_state) { furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); } } else { instance->speaker_state = SubGhzSpeakerStateDisable; } } } void subghz_txrx_speaker_off(SubGhzTxRx* instance) { furi_assert(instance); if(instance->debug_pin_state) { furi_hal_subghz_set_async_mirror_pin(NULL); } if(instance->speaker_state != SubGhzSpeakerStateDisable) { if(furi_hal_speaker_is_mine()) { if(!instance->debug_pin_state) { furi_hal_subghz_set_async_mirror_pin(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->debug_pin_state) { furi_hal_subghz_set_async_mirror_pin(NULL); } if(instance->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_is_mine()) { if(!instance->debug_pin_state) { furi_hal_subghz_set_async_mirror_pin(NULL); } } } } void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) { furi_assert(instance); if(instance->debug_pin_state) { furi_hal_subghz_set_async_mirror_pin(&gpio_ibutton); } if(instance->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_is_mine()) { if(!instance->debug_pin_state) { furi_hal_subghz_set_async_mirror_pin(&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); } void subghz_txrx_set_debug_pin_state(SubGhzTxRx* instance, bool state) { furi_assert(instance); instance->debug_pin_state = state; } bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance) { furi_assert(instance); return instance->debug_pin_state; } SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance) { furi_assert(instance); return instance->receiver; }