Compare commits

..

4 Commits

Author SHA1 Message Date
David 6e5ba9ec22 Rework saved signal editor and duplicate warning 2026-07-12 16:31:22 +02:00
David 502570a18b Show signal editor from saved file menu 2026-07-12 14:22:44 +02:00
David e606c5b24f Add saved signal editor and duplicate detection 2026-07-12 13:58:10 +02:00
David b34a73b08a Keep protocol encoders running while held
Build Dev Firmware / build (push) Waiting to run
2026-07-12 11:54:06 +02:00
30 changed files with 499 additions and 69 deletions
@@ -361,6 +361,49 @@ SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat*
return ret;
}
bool subghz_txrx_rebuild_from_fff(SubGhzTxRx* instance, FlipperFormat* flipper_format) {
furi_assert(instance);
furi_assert(flipper_format);
subghz_txrx_stop(instance);
bool rebuilt = false;
FuriString* temp_str = furi_string_alloc();
SubGhzTransmitter* transmitter = NULL;
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;
}
transmitter =
subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str));
if(!transmitter) {
FURI_LOG_E(TAG, "Protocol not found");
break;
}
rebuilt =
subghz_transmitter_deserialize(transmitter, flipper_format) == SubGhzProtocolStatusOk;
if(!rebuilt) {
FURI_LOG_E(TAG, "Protocol rebuild failed");
break;
}
} while(false);
if(transmitter) {
subghz_transmitter_free(transmitter);
}
furi_string_free(temp_str);
return rebuilt;
}
void subghz_txrx_rx_start(SubGhzTxRx* instance) {
furi_assert(instance);
subghz_txrx_stop(instance);
@@ -105,6 +105,15 @@ void subghz_txrx_get_frequency_and_modulation(
*/
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format);
/**
* Rebuild protocol data without starting TX.
*
* @param instance Pointer to a SubGhzTxRx
* @param flipper_format Pointer to a FlipperFormat
* @return bool True when encoder deserialization updated protocol data successfully
*/
bool subghz_txrx_rebuild_from_fff(SubGhzTxRx* instance, FlipperFormat* flipper_format);
/**
* Start RX CC1101
*
@@ -5,6 +5,209 @@
#include <gui/modules/validators.h>
#include <dolphin/dolphin.h>
#include <toolbox/name_generator.h>
#include <lib/toolbox/dir_walk.h>
#include <toolbox/strint.h>
#include <string.h>
typedef struct {
FuriString* protocol;
FuriString* manufacture;
bool has_manufacture;
uint32_t serial;
bool has_serial;
uint32_t btn;
bool has_btn;
uint8_t key[sizeof(uint64_t)];
bool has_key;
} SubGhzSaveNameSignalSignature;
static void subghz_scene_save_name_signature_init(SubGhzSaveNameSignalSignature* signature) {
signature->protocol = furi_string_alloc();
signature->manufacture = furi_string_alloc();
signature->has_manufacture = false;
signature->serial = 0;
signature->has_serial = false;
signature->btn = 0;
signature->has_btn = false;
memset(signature->key, 0, sizeof(signature->key));
signature->has_key = false;
}
static void subghz_scene_save_name_signature_free(SubGhzSaveNameSignalSignature* signature) {
furi_string_free(signature->protocol);
furi_string_free(signature->manufacture);
}
static bool
subghz_scene_save_name_read_string(FlipperFormat* fff, const char* key, FuriString* value) {
flipper_format_rewind(fff);
bool result = flipper_format_read_string(fff, key, value);
if(result) {
furi_string_trim(value);
}
return result;
}
static bool subghz_scene_save_name_parse_u32(const char* value, uint32_t* out) {
char* end = NULL;
uint32_t parsed = 0;
if(strint_to_uint32(value, &end, &parsed, 10) == StrintParseNoError && end && *end == '\0') {
*out = parsed;
return true;
}
end = NULL;
if(strint_to_uint32(value, &end, &parsed, 16) == StrintParseNoError && end && *end == '\0') {
*out = parsed;
return true;
}
return false;
}
static bool subghz_scene_save_name_read_u32(FlipperFormat* fff, const char* key, uint32_t* value) {
flipper_format_rewind(fff);
if(flipper_format_read_uint32(fff, key, value, 1)) {
return true;
}
FuriString* value_str = furi_string_alloc();
bool result = false;
if(subghz_scene_save_name_read_string(fff, key, value_str)) {
result = subghz_scene_save_name_parse_u32(furi_string_get_cstr(value_str), value);
}
furi_string_free(value_str);
return result;
}
static bool subghz_scene_save_name_signature_read(
FlipperFormat* fff,
SubGhzSaveNameSignalSignature* signature) {
if(!subghz_scene_save_name_read_string(fff, "Protocol", signature->protocol)) {
return false;
}
if(furi_string_equal_str(signature->protocol, "RAW")) {
return false;
}
signature->has_manufacture =
subghz_scene_save_name_read_string(fff, "Manufacture", signature->manufacture);
signature->has_serial = subghz_scene_save_name_read_u32(fff, "Serial", &signature->serial);
signature->has_btn = subghz_scene_save_name_read_u32(fff, "Btn", &signature->btn);
flipper_format_rewind(fff);
signature->has_key = flipper_format_read_hex(fff, "Key", signature->key, sizeof(signature->key));
return signature->has_serial || signature->has_key;
}
static bool subghz_scene_save_name_signatures_match(
const SubGhzSaveNameSignalSignature* saved,
const SubGhzSaveNameSignalSignature* captured) {
if(!furi_string_equal(saved->protocol, captured->protocol)) {
return false;
}
if(saved->has_manufacture && captured->has_manufacture &&
!furi_string_equal(saved->manufacture, captured->manufacture)) {
return false;
}
if(saved->has_serial && captured->has_serial) {
if(saved->serial != captured->serial) {
return false;
}
if(saved->has_btn && captured->has_btn && saved->btn != captured->btn) {
return false;
}
if(saved->serial != 0) {
return true;
}
}
return saved->has_key && captured->has_key &&
memcmp(saved->key, captured->key, sizeof(saved->key)) == 0;
}
static bool subghz_scene_save_name_file_filter(const char* name, FileInfo* fileinfo, void* ctx) {
UNUSED(ctx);
if(file_info_is_dir(fileinfo)) return false;
const char* ext = strrchr(name, '.');
return ext && !strcmp(ext, SUBGHZ_APP_FILENAME_EXTENSION);
}
static bool subghz_scene_save_name_find_duplicate(
FlipperFormat* captured,
const char* target_path,
FuriString* duplicate_name) {
SubGhzSaveNameSignalSignature captured_signature;
subghz_scene_save_name_signature_init(&captured_signature);
bool found = false;
if(!subghz_scene_save_name_signature_read(captured, &captured_signature)) {
subghz_scene_save_name_signature_free(&captured_signature);
return false;
}
Storage* storage = furi_record_open(RECORD_STORAGE);
DirWalk* dir_walk = dir_walk_alloc(storage);
FlipperFormat* saved_fff = flipper_format_file_alloc(storage);
FuriString* path = furi_string_alloc();
FileInfo fileinfo;
dir_walk_set_recursive(dir_walk, true);
dir_walk_set_filter_cb(dir_walk, subghz_scene_save_name_file_filter, NULL);
if(dir_walk_open(dir_walk, SUBGHZ_APP_FOLDER)) {
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
if(file_info_is_dir(&fileinfo)) continue;
if(target_path && !strcmp(furi_string_get_cstr(path), target_path)) continue;
if(flipper_format_file_open_existing(saved_fff, furi_string_get_cstr(path))) {
SubGhzSaveNameSignalSignature saved_signature;
subghz_scene_save_name_signature_init(&saved_signature);
if(subghz_scene_save_name_signature_read(saved_fff, &saved_signature) &&
subghz_scene_save_name_signatures_match(
&saved_signature, &captured_signature)) {
path_extract_filename(path, duplicate_name, true);
found = true;
}
subghz_scene_save_name_signature_free(&saved_signature);
flipper_format_file_close(saved_fff);
if(found) break;
}
}
dir_walk_close(dir_walk);
}
furi_string_free(path);
flipper_format_free(saved_fff);
dir_walk_free(dir_walk);
furi_record_close(RECORD_STORAGE);
subghz_scene_save_name_signature_free(&captured_signature);
return found;
}
static void subghz_scene_save_name_set_duplicate_message(
SubGhz* subghz,
FlipperFormat* signal_data) {
FuriString* duplicate_name = furi_string_alloc();
furi_string_reset(subghz->error_str);
if(signal_data && subghz_scene_save_name_find_duplicate(
signal_data, furi_string_get_cstr(subghz->file_path), duplicate_name)) {
furi_string_printf(
subghz->error_str,
"Same signal\nsaved in file\n%s",
furi_string_get_cstr(duplicate_name));
}
furi_string_free(duplicate_name);
}
void subghz_scene_save_name_text_input_callback(void* context) {
furi_assert(context);
@@ -110,6 +313,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
} else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneSaveName) {
if(strcmp(subghz->file_name_tmp, "") != 0) {
furi_string_reset(subghz->error_str);
furi_string_cat_printf(
subghz->file_path,
"/%s%s",
@@ -122,18 +326,23 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
} else {
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType) !=
SubGhzCustomEventManagerNoSet) {
FlipperFormat* signal_data = subghz_txrx_get_fff_data(subghz->txrx);
subghz_scene_save_name_set_duplicate_message(subghz, signal_data);
subghz_save_protocol_to_file(
subghz,
subghz_txrx_get_fff_data(subghz->txrx),
signal_data,
furi_string_get_cstr(subghz->file_path));
scene_manager_set_scene_state(
subghz->scene_manager,
SubGhzSceneSetType,
SubGhzCustomEventManagerNoSet);
} else {
FlipperFormat* signal_data =
subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen);
subghz_scene_save_name_set_duplicate_message(subghz, signal_data);
subghz_save_protocol_to_file(
subghz,
subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen),
signal_data,
furi_string_get_cstr(subghz->file_path));
}
}
@@ -12,8 +12,14 @@ void subghz_scene_save_success_on_enter(void* context) {
// Setup view
Popup* popup = subghz->popup;
// [NO_DOLPHIN] popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
if(furi_string_size(subghz->error_str)) {
popup_set_header(popup, "Saved", 15, 4, AlignLeft, AlignTop);
popup_set_text(popup, furi_string_get_cstr(subghz->error_str), 4, 18, AlignLeft, AlignTop);
popup_set_timeout(popup, 2500);
} else {
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
}
popup_set_context(popup, subghz);
popup_set_callback(popup, subghz_scene_save_success_popup_callback);
popup_enable_timeout(popup);
@@ -72,4 +78,5 @@ void subghz_scene_save_success_on_exit(void* context) {
Popup* popup = subghz->popup;
popup_reset(popup);
furi_string_reset(subghz->error_str);
}
@@ -2,14 +2,29 @@
enum SubmenuIndex {
SubmenuIndexEmulate,
SubmenuIndexSignalSettings,
SubmenuIndexPsaDecrypt,
SubmenuIndexEdit,
SubmenuIndexDelete,
SubmenuIndexSignalSettings,
SubmenuIndexCounterBf, /* <-- comma was missing here */
SubmenuIndexCarEmulateSettings,
};
static bool subghz_scene_saved_menu_has_field(FlipperFormat* fff, const char* key) {
uint32_t value = 0;
FuriString* value_str = furi_string_alloc();
flipper_format_rewind(fff);
bool has_field = flipper_format_read_uint32(fff, key, &value, 1);
if(!has_field) {
flipper_format_rewind(fff);
has_field = flipper_format_read_string(fff, key, value_str);
}
furi_string_free(value_str);
return has_field;
}
void subghz_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, index);
@@ -20,6 +35,7 @@ void subghz_scene_saved_menu_on_enter(void* context) {
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
bool is_psa_encrypted = false;
bool has_signal_editor = false;
bool has_counter = false;
if(fff) {
FuriString* proto = furi_string_alloc();
@@ -44,6 +60,8 @@ void subghz_scene_saved_menu_on_enter(void* context) {
if(flipper_format_read_uint32(fff, "Cnt", &cnt_tmp, 1)) {
has_counter = true;
}
has_signal_editor = subghz_scene_saved_menu_has_field(fff, "Cnt") ||
subghz_scene_saved_menu_has_field(fff, "Btn");
}
if(!is_psa_encrypted) {
@@ -53,6 +71,15 @@ void subghz_scene_saved_menu_on_enter(void* context) {
SubmenuIndexEmulate,
subghz_scene_saved_menu_submenu_callback,
subghz);
if(has_signal_editor) {
submenu_add_item(
subghz->submenu,
"Signal Editor",
SubmenuIndexSignalSettings,
subghz_scene_saved_menu_submenu_callback,
subghz);
}
}
if(is_psa_encrypted) {
@@ -85,15 +112,6 @@ void subghz_scene_saved_menu_on_enter(void* context) {
subghz_scene_saved_menu_submenu_callback,
subghz);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
submenu_add_item(
subghz->submenu,
"Signal Settings",
SubmenuIndexSignalSettings,
subghz_scene_saved_menu_submenu_callback,
subghz);
}
if(has_counter) {
submenu_add_item(
subghz->submenu,
@@ -5,6 +5,7 @@
#include <machine/endian.h>
#include <toolbox/strint.h>
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/custom_btn.h>
#define TAG "SubGhzSceneSignalSettings"
@@ -21,6 +22,14 @@ static uint8_t btn_byte_count = 1;
static uint8_t* btn_byte_ptr = NULL;
static uint8_t submenu_called = 0;
static bool button_uses_custom_btn = false;
static uint8_t button_custom_id = SUBGHZ_CUSTOM_BTN_OK;
enum {
SignalSettingsIndexCounterMode,
SignalSettingsIndexCounter,
SignalSettingsIndexButton,
};
#define COUNTER_MODE_COUNT 8
static const char* const counter_mode_text[COUNTER_MODE_COUNT] = {
@@ -45,6 +54,24 @@ static const int32_t counter_mode_value[COUNTER_MODE_COUNT] = {
7,
};
static const char* const button_text[] = {
"Original",
"Up",
"Down",
"Left",
"Right",
};
static const uint8_t button_value[] = {
SUBGHZ_CUSTOM_BTN_OK,
SUBGHZ_CUSTOM_BTN_UP,
SUBGHZ_CUSTOM_BTN_DOWN,
SUBGHZ_CUSTOM_BTN_LEFT,
SUBGHZ_CUSTOM_BTN_RIGHT,
};
#define BUTTON_VALUE_COUNT (sizeof(button_value) / sizeof(button_value[0]))
typedef struct {
char* name;
uint8_t mode_count;
@@ -61,6 +88,55 @@ static Protocols protocols[] = {
#define PROTOCOLS_COUNT (sizeof(protocols) / sizeof(Protocols));
static bool subghz_scene_signal_settings_update_uint32_field(
FlipperFormat* fff,
const char* key,
uint32_t value) {
flipper_format_rewind(fff);
return flipper_format_insert_or_update_uint32(fff, key, &value, 1);
}
static bool subghz_scene_signal_settings_rebuild_save_reload(
SubGhz* subghz,
bool use_custom_btn,
uint8_t custom_btn_id) {
const char* file_path = furi_string_get_cstr(subghz->file_path);
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
bool updated = false;
int32_t counter_mult = furi_hal_subghz_get_rolling_counter_mult();
furi_hal_subghz_set_rolling_counter_mult(0);
if(use_custom_btn) {
subghz_custom_btn_set(custom_btn_id);
} else {
subghz_custom_btns_reset();
}
do {
if(!subghz_txrx_rebuild_from_fff(subghz->txrx, fff)) {
FURI_LOG_E(TAG, "Error rebuilding protocol data");
break;
}
if(!subghz_save_protocol_to_file(subghz, fff, file_path)) {
FURI_LOG_E(TAG, "Error saving edited signal");
break;
}
if(!subghz_key_load(subghz, file_path, false)) {
FURI_LOG_E(TAG, "Error reloading edited signal");
break;
}
updated = true;
} while(false);
furi_hal_subghz_set_rolling_counter_mult(counter_mult);
if(!updated) {
dialog_message_show_storage_error(subghz->dialogs, "Cannot save\nsignal");
}
return updated;
}
void subghz_scene_signal_settings_counter_mode_changed(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, counter_mode_text[index]);
@@ -99,6 +175,21 @@ void subghz_scene_signal_settings_counter_mode_changed(VariableItem* item) {
}
}
void subghz_scene_signal_settings_button_changed(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item);
if(index >= BUTTON_VALUE_COUNT) index = 0;
variable_item_set_current_value_text(item, button_text[index]);
button_custom_id = button_value[index];
if(!button_uses_custom_btn) return;
SubGhz* subghz = variable_item_get_context(item);
furi_assert(subghz);
subghz_scene_signal_settings_rebuild_save_reload(subghz, true, button_custom_id);
}
void subghz_scene_signal_settings_byte_input_callback(void* context) {
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone);
@@ -108,8 +199,10 @@ void subghz_scene_signal_settings_variable_item_list_enter_callback(void* contex
SubGhz* subghz = context;
// when we click OK on "Edit counter" item
if(index == 1) {
if(index == SignalSettingsIndexCounter) {
if(!cnt_byte_ptr || cnt_byte_count == 0) return;
submenu_called = 1;
furi_string_set_str(byte_input_text, "Enter ");
furi_string_cat_printf(byte_input_text, "%i", subghz_block_generic_global.cnt_length_bit);
furi_string_cat_str(byte_input_text, "-bits counter in HEX");
@@ -127,8 +220,10 @@ void subghz_scene_signal_settings_variable_item_list_enter_callback(void* contex
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput);
}
// when we click OK on "Edit button" item
if(index == 2) {
if(index == SignalSettingsIndexButton && !button_uses_custom_btn) {
if(!btn_byte_ptr || btn_byte_count == 0) return;
submenu_called = 2;
furi_string_set_str(byte_input_text, "Enter ");
furi_string_cat_printf(byte_input_text, "%i", subghz_block_generic_global.btn_length_bit);
furi_string_cat_str(byte_input_text, "-bits button in HEX");
@@ -150,6 +245,19 @@ void subghz_scene_signal_settings_variable_item_list_enter_callback(void* contex
void subghz_scene_signal_settings_on_enter(void* context) {
SubGhz* subghz = context;
counter32 = 0;
counter16 = 0;
cnt_byte_count = 0;
cnt_byte_ptr = NULL;
button = 0;
btn_byte_count = 1;
btn_byte_ptr = NULL;
submenu_called = 0;
button_uses_custom_btn = false;
button_custom_id = SUBGHZ_CUSTOM_BTN_OK;
subghz_block_generic_global_reset(NULL);
subghz_custom_btns_reset();
// ### Counter mode section ###
// When we open saved file we do some check and fill up subghz->file_path.
@@ -232,6 +340,7 @@ void subghz_scene_signal_settings_on_enter(void* context) {
if(!subghz_block_generic_global.cnt_is_available) {
counter_mode = 0xff;
furi_string_set_str(tmp_text, "-");
FURI_LOG_D(TAG, "Counter mode and edit not available for this protocol");
} else {
counter_not_available = false;
@@ -262,24 +371,43 @@ void subghz_scene_signal_settings_on_enter(void* context) {
// ### Button edit section ###
if(!subghz_block_generic_global.btn_is_available) {
furi_string_set_str(tmp_text, "-");
FURI_LOG_D(TAG, "Button edit not available for this protocol");
} else {
button_not_available = false;
button = subghz_block_generic_global.current_btn;
furi_string_printf(tmp_text, "%X", button);
btn_byte_ptr = (uint8_t*)&button;
if(subghz_custom_btn_is_allowed()) {
uint8_t max_custom_btn = subghz_custom_btn_get_max();
uint8_t button_count = max_custom_btn + 1;
if(button_count > BUTTON_VALUE_COUNT) button_count = BUTTON_VALUE_COUNT;
button_uses_custom_btn = button_count > 1;
}
if(button_uses_custom_btn) {
furi_string_set_str(tmp_text, button_text[0]);
} else {
furi_string_printf(tmp_text, "%X", button);
}
}
item = variable_item_list_add(variable_item_list, "Edit Button", 1, NULL, subghz);
uint8_t button_count = 1;
if(button_uses_custom_btn) {
button_count = subghz_custom_btn_get_max() + 1;
if(button_count > BUTTON_VALUE_COUNT) button_count = BUTTON_VALUE_COUNT;
}
item = variable_item_list_add(
variable_item_list,
button_uses_custom_btn ? "Button" : "Edit Button",
button_count,
button_uses_custom_btn ? subghz_scene_signal_settings_button_changed : NULL,
subghz);
variable_item_set_current_value_index(item, 0);
variable_item_set_current_value_text(item, furi_string_get_cstr(tmp_text));
variable_item_set_locked(item, (button_not_available), "Not available\nfor this\nprotocol !");
//
furi_assert(cnt_byte_ptr);
furi_assert(cnt_byte_count > 0);
furi_assert(btn_byte_ptr);
furi_string_free(tmp_text);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
@@ -290,23 +418,25 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventByteInputDone) {
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
switch(submenu_called) {
// edit counter
case 1:
switch(cnt_byte_count) {
case 2:
// set new cnt value and override_flag to global variable and call transmit to generate and save subghz signal
counter16 = __bswap16(counter16);
subghz_scene_signal_settings_update_uint32_field(fff, "Cnt", counter16);
subghz_block_generic_global_counter_override_set(counter16);
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
subghz_txrx_stop(subghz->txrx);
subghz_scene_signal_settings_rebuild_save_reload(
subghz, false, SUBGHZ_CUSTOM_BTN_OK);
break;
case 4:
// the same for 32 bit Counter
counter32 = __bswap32(counter32);
subghz_scene_signal_settings_update_uint32_field(fff, "Cnt", counter32);
subghz_block_generic_global_counter_override_set(counter32);
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
subghz_txrx_stop(subghz->txrx);
subghz_scene_signal_settings_rebuild_save_reload(
subghz, false, SUBGHZ_CUSTOM_BTN_OK);
break;
default:
break;
@@ -314,14 +444,10 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
break;
// edit button
case 2:
subghz_scene_signal_settings_update_uint32_field(fff, "Btn", button);
subghz_block_generic_global_button_override_set(button);
// save counter mult to rewrite subghz singnal without changing counter
int32_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
furi_hal_subghz_set_rolling_counter_mult(0);
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
subghz_txrx_stop(subghz->txrx);
// restore counter mult
furi_hal_subghz_set_rolling_counter_mult(tmp_counter);
subghz_scene_signal_settings_rebuild_save_reload(
subghz, false, SUBGHZ_CUSTOM_BTN_OK);
break;
default:
@@ -330,13 +456,10 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
scene_manager_previous_scene(subghz->scene_manager);
return true;
} else {
if(event.type == SceneManagerEventTypeBack) {
scene_manager_previous_scene(subghz->scene_manager);
return true;
}
}
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_previous_scene(subghz->scene_manager);
return true;
}
return false;
}
@@ -352,4 +475,5 @@ void subghz_scene_signal_settings_on_exit(void* context) {
byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0);
byte_input_set_header_text(subghz->byte_input, "");
furi_string_free(byte_input_text);
subghz_custom_btns_reset();
}
+4
View File
@@ -24,6 +24,10 @@ uint8_t subghz_custom_btn_get(void) {
return custom_btn_id;
}
uint8_t subghz_custom_btn_get_max(void) {
return custom_btn_max_btns;
}
void subghz_custom_btn_set_original(uint8_t btn_code) {
custom_btn_original = btn_code;
}
+2
View File
@@ -19,6 +19,8 @@ bool subghz_custom_btn_set(uint8_t btn_id);
uint8_t subghz_custom_btn_get(void);
uint8_t subghz_custom_btn_get_max(void);
uint8_t subghz_custom_btn_get_original(void);
void subghz_custom_btns_reset(void);
+1 -1
View File
@@ -407,7 +407,7 @@ LevelDuration subghz_protocol_encoder_fiat_spa_yield(void* context) {
}
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
return ret;
+1 -1
View File
@@ -364,7 +364,7 @@ LevelDuration subghz_protocol_encoder_ford_v0_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
+3 -2
View File
@@ -1278,8 +1278,9 @@ LevelDuration subghz_protocol_encoder_ford_v1_yield(void* context) {
"Encoder yield: finished one full %lu-word frame (all %u bursts); repeats_left=%u",
(unsigned long)instance->encoder.size_upload,
(unsigned)FORD_V1_ENC_BURST_COUNT,
(unsigned)instance->encoder.repeat - 1U);
instance->encoder.repeat--;
subghz_block_generic_global.endless_tx ? (unsigned)instance->encoder.repeat :
(unsigned)instance->encoder.repeat - 1U);
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
} else if(instance->encoder.front <= 4U) {
uint32_t raw_word;
+1 -1
View File
@@ -615,7 +615,7 @@ LevelDuration subghz_protocol_encoder_ford_v2_yield(void* context) {
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.front = 0U;
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
}
return ret;
+1 -2
View File
@@ -154,7 +154,7 @@ LevelDuration subghz_protocol_encoder_kia_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
@@ -594,4 +594,3 @@ void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output) {
received_crc,
crc_valid ? "(OK)" : "(FAIL)");
}
+1 -1
View File
@@ -174,7 +174,7 @@ LevelDuration subghz_protocol_encoder_kia_v1_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
+1 -1
View File
@@ -183,7 +183,7 @@ LevelDuration subghz_protocol_encoder_kia_v2_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
+1 -1
View File
@@ -648,7 +648,7 @@ LevelDuration subghz_protocol_encoder_kia_v3_v4_yield(void* context) {
instance->crc_iter = (uint8_t)((instance->crc_iter + 1U) & 0x0FU);
subghz_protocol_encoder_kia_v3_v4_patch_crc(instance);
instance->encoder.front = 0;
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
if(instance->bursts_sent < 16U) {
instance->bursts_sent++;
}
+19 -5
View File
@@ -327,15 +327,29 @@ SubGhzProtocolStatus
uint32_t encrypted = (uint32_t)(yek & 0xFFFFFFFF);
instance->generic.cnt = mixer_decode(encrypted);
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
instance->generic.cnt = (instance->generic.cnt + mult) & 0xFFFF;
FURI_LOG_I(TAG, "deserialize #%lu, cnt after=%04lX", call_count, (uint32_t)instance->generic.cnt);
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(instance->generic.btn);
}
subghz_custom_btn_set_max(4);
uint32_t override_cnt = 0;
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->generic.cnt = override_cnt & 0xFFFF;
} else {
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
instance->generic.cnt = (instance->generic.cnt + mult) & 0xFFFF;
}
FURI_LOG_I(
TAG, "deserialize #%lu, cnt after=%04lX", call_count, (uint32_t)instance->generic.cnt);
uint8_t btn = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ?
subghz_custom_btn_get_original() :
subghz_custom_btn_get();
if(subghz_block_generic_global_button_override_get(&btn)) {
FURI_LOG_D(TAG, "Button changed to 0x%X", btn);
}
instance->generic.btn = btn & 0x0F;
instance->generic.data = kia_v5_encode_data(
instance->generic.serial, instance->generic.cnt, instance->generic.btn);
instance->replay_data = instance->generic.data;
@@ -456,7 +470,7 @@ LevelDuration subghz_protocol_encoder_kia_v5_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
+1 -1
View File
@@ -791,7 +791,7 @@ LevelDuration subghz_protocol_encoder_kia_v6_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
instance->encoder.front++;
if(instance->encoder.front >= instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
return ret;
+1 -1
View File
@@ -431,7 +431,7 @@ LevelDuration kia_protocol_encoder_v7_yield(void* context) {
LevelDuration duration = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
+1 -1
View File
@@ -969,7 +969,7 @@ LevelDuration subghz_protocol_encoder_land_rover_v0_yield(void* context) {
if(instance->encoder.front >= instance->encoder.size_upload) {
/* One full repetition done; count it down */
if(instance->encoder.repeat > 0) {
if(instance->encoder.repeat > 0 && !subghz_block_generic_global.endless_tx) {
instance->encoder.repeat--;
}
instance->encoder.front = 0;
+1 -1
View File
@@ -499,7 +499,7 @@ LevelDuration subghz_protocol_encoder_mazda_v0_yield(void* context) {
LevelDuration out = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
+1 -1
View File
@@ -193,7 +193,7 @@ LevelDuration subghz_protocol_encoder_mitsubishi_v0_yield(void* context) {
if(!instance->encoder.is_running || instance->encoder.repeat == 0) return level_duration_reset();
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
return ret;
+1 -1
View File
@@ -621,7 +621,7 @@ LevelDuration subghz_protocol_encoder_porsche_cayenne_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
+1 -1
View File
@@ -1932,7 +1932,7 @@ LevelDuration subghz_protocol_encoder_psa_yield(void* context) {
instance->encoder.front++;
if(instance->encoder.front >= instance->encoder.size_upload) {
instance->encoder.front = 0;
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
if(instance->encoder.repeat <= 0) instance->is_running = false;
}
return ret;
+1 -1
View File
@@ -622,7 +622,7 @@ LevelDuration subghz_protocol_encoder_scher_khan_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
+1 -1
View File
@@ -418,7 +418,7 @@ LevelDuration subghz_protocol_encoder_sheriff_cfm_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
+1 -1
View File
@@ -556,7 +556,7 @@ LevelDuration subghz_protocol_encoder_star_line_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
+1 -1
View File
@@ -527,7 +527,7 @@ LevelDuration subghz_protocol_encoder_subaru_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
+1 -1
View File
@@ -494,7 +494,7 @@ LevelDuration subghz_protocol_encoder_suzuki_yield(void *context)
if (++instance->encoder.front == instance->encoder.size_upload)
{
instance->encoder.repeat--;
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0;
}
+1 -1
View File
@@ -1358,7 +1358,7 @@ LevelDuration subghz_protocol_encoder_vag_yield(void* context) {
if(instance->front >= instance->size_upload) {
instance->front = 0;
instance->repeat--;
if(!subghz_block_generic_global.endless_tx) instance->repeat--;
}
return ret;