Compare commits

...
Author SHA1 Message Date
d4rks1d33 63d951dc38 Update ProtoPirate
Build Dev Firmware / build (push) Has been cancelled
2026-07-13 22:15:44 -03:00
d4rks1d33 f1422cb701 Full dpad emulation on receiver scene not need to save the signal to use all the buttons 2026-07-13 22:14:08 -03:00
David f7b04f1382 Fix saved signal editor command and counter updates
Build Dev Firmware / build (push) Has been cancelled
2026-07-12 23:55:11 +02:00
David 7286b5c529 Avoid in-place FastFAP objcopy 2026-07-12 21:29:36 +02:00
David 6a49fd89a1 Fix saved signal editor availability 2026-07-12 21:21:28 +02:00
David c4378c2e20 Approve SubGhz custom button API symbol 2026-07-12 19:36:51 +02:00
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) Has been cancelled
2026-07-12 11:54:06 +02:00
David 069a42d697 Repeat car emulate TX while held
Build Dev Firmware / build (push) Has been cancelled
2026-07-12 11:24:21 +02:00
42 changed files with 1076 additions and 236 deletions
@@ -15,6 +15,7 @@ typedef enum {
SubGhzCustomEventSceneReceiverInfoTxStart, SubGhzCustomEventSceneReceiverInfoTxStart,
SubGhzCustomEventSceneReceiverInfoTxStop, SubGhzCustomEventSceneReceiverInfoTxStop,
SubGhzCustomEventSceneReceiverInfoSave, SubGhzCustomEventSceneReceiverInfoSave,
SubGhzCustomEventSceneReceiverInfoTxFullDpad,
SubGhzCustomEventSceneSaveName, SubGhzCustomEventSceneSaveName,
SubGhzCustomEventSceneSignalSettings, SubGhzCustomEventSceneSignalSettings,
SubGhzCustomEventSceneSaveSuccess, SubGhzCustomEventSceneSaveSuccess,
@@ -361,6 +361,49 @@ SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat*
return ret; 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) { void subghz_txrx_rx_start(SubGhzTxRx* instance) {
furi_assert(instance); furi_assert(instance);
subghz_txrx_stop(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); 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 * Start RX CC1101
* *
@@ -34,7 +34,7 @@ typedef struct {
bool stop_pending; /* stop requested before MIN_TX_TICKS elapsed */ bool stop_pending; /* stop requested before MIN_TX_TICKS elapsed */
uint32_t tx_start_tick; uint32_t tx_start_tick;
/* Pending button key (InputKey) decoded from the packed custom event */ /* Resolved custom button repeated while the physical key is held */
uint8_t pending_button; uint8_t pending_button;
} CarEmulateState; } CarEmulateState;
@@ -252,12 +252,34 @@ static bool car_emulate_start_tx(SubGhz* subghz, uint8_t custom_btn_id) {
/** Stop an active transmission. */ /** Stop an active transmission. */
static void car_emulate_stop_tx(SubGhz* subghz) { static void car_emulate_stop_tx(SubGhz* subghz) {
subghz_block_generic_global.endless_tx = false;
subghz_txrx_stop(subghz->txrx); subghz_txrx_stop(subghz->txrx);
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
notification_message(subghz->notifications, &sequence_blink_stop); notification_message(subghz->notifications, &sequence_blink_stop);
FURI_LOG_I(TAG, "TX stopped"); FURI_LOG_I(TAG, "TX stopped");
} }
static bool car_emulate_restart_tx(SubGhz* subghz) {
furi_assert(s_state);
car_emulate_update_fff(subghz, s_state->current_counter);
subghz_block_generic_global.endless_tx = true;
if(car_emulate_start_tx(subghz, s_state->pending_button)) {
s_state->is_transmitting = true;
subghz->state_notifications = SubGhzNotificationStateTx;
notification_message(subghz->notifications, &sequence_blink_magenta_10);
FURI_LOG_D(TAG, "TX restarted while button is held");
return true;
}
subghz_block_generic_global.endless_tx = false;
s_state->is_transmitting = false;
s_state->stop_pending = false;
notification_message(subghz->notifications, &sequence_error);
return false;
}
/* ═══════════════════════════════════════════════════════════════════════════ /* ═══════════════════════════════════════════════════════════════════════════
* View callback (fired from the View's input handler) * View callback (fired from the View's input handler)
* ═════════════════════════════════════════════════════════════════════════*/ * ═════════════════════════════════════════════════════════════════════════*/
@@ -293,6 +315,7 @@ void subghz_scene_car_emulate_on_enter(void* context) {
s_state = malloc(sizeof(CarEmulateState)); s_state = malloc(sizeof(CarEmulateState));
furi_check(s_state); furi_check(s_state);
memset(s_state, 0, sizeof(CarEmulateState)); memset(s_state, 0, sizeof(CarEmulateState));
subghz_block_generic_global.endless_tx = false;
/* ── Read metadata from the loaded fff_data ── */ /* ── Read metadata from the loaded fff_data ── */
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx); FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
@@ -401,8 +424,11 @@ bool subghz_scene_car_emulate_on_event(void* context, SceneManagerEvent event) {
s_state->tx_start_tick = (uint32_t)furi_get_tick(); s_state->tx_start_tick = (uint32_t)furi_get_tick();
uint8_t cur_btn = subghz_custom_btn_get(); uint8_t cur_btn = subghz_custom_btn_get();
s_state->pending_button = cur_btn;
subghz_block_generic_global.endless_tx = true;
if(!car_emulate_start_tx(subghz, cur_btn)) { if(!car_emulate_start_tx(subghz, cur_btn)) {
s_state->is_transmitting = false; s_state->is_transmitting = false;
subghz_block_generic_global.endless_tx = false;
notification_message(subghz->notifications, &sequence_error); notification_message(subghz->notifications, &sequence_error);
} }
@@ -411,11 +437,13 @@ bool subghz_scene_car_emulate_on_event(void* context, SceneManagerEvent event) {
/* ── Stop ── */ /* ── Stop ── */
} else if(event.event == SubGhzCustomEventCarEmulateStop) { } else if(event.event == SubGhzCustomEventCarEmulateStop) {
if(s_state->is_transmitting && if(s_state->is_transmitting) {
subghz->state_notifications == SubGhzNotificationStateTx) { subghz_block_generic_global.endless_tx = false;
uint32_t elapsed = (uint32_t)furi_get_tick() - s_state->tx_start_tick; uint32_t elapsed = (uint32_t)furi_get_tick() - s_state->tx_start_tick;
if(elapsed >= MIN_TX_TICKS) { if(
elapsed >= MIN_TX_TICKS &&
subghz->state_notifications == SubGhzNotificationStateTx) {
car_emulate_stop_tx(subghz); car_emulate_stop_tx(subghz);
s_state->is_transmitting = false; s_state->is_transmitting = false;
s_state->stop_pending = false; s_state->stop_pending = false;
@@ -431,6 +459,7 @@ bool subghz_scene_car_emulate_on_event(void* context, SceneManagerEvent event) {
if(subghz->state_notifications == SubGhzNotificationStateTx) { if(subghz->state_notifications == SubGhzNotificationStateTx) {
car_emulate_stop_tx(subghz); car_emulate_stop_tx(subghz);
} }
subghz_block_generic_global.endless_tx = false;
scene_manager_search_and_switch_to_previous_scene( scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneSavedMenu); subghz->scene_manager, SubGhzSceneSavedMenu);
consumed = true; consumed = true;
@@ -450,6 +479,8 @@ bool subghz_scene_car_emulate_on_event(void* context, SceneManagerEvent event) {
s_state->is_transmitting = false; s_state->is_transmitting = false;
s_state->stop_pending = false; s_state->stop_pending = false;
notification_message(subghz->notifications, &sequence_blink_stop); notification_message(subghz->notifications, &sequence_blink_stop);
} else {
car_emulate_restart_tx(subghz);
} }
} else { } else {
/* Still transmitting blink LED */ /* Still transmitting blink LED */
@@ -485,6 +516,7 @@ void subghz_scene_car_emulate_on_exit(void* context) {
car_emulate_stop_tx(subghz); car_emulate_stop_tx(subghz);
} }
subghz_block_generic_global.endless_tx = false;
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
notification_message(subghz->notifications, &sequence_blink_stop); notification_message(subghz->notifications, &sequence_blink_stop);
@@ -1,6 +1,7 @@
#include "../subghz_i.h" #include "../subghz_i.h"
#include <lib/subghz/blocks/custom_btn.h> #include <lib/subghz/blocks/custom_btn.h>
#include <flipper_format/flipper_format_i.h>
#include "applications/main/subghz/helpers/subghz_txrx_i.h" #include "applications/main/subghz/helpers/subghz_txrx_i.h"
#include <lib/subghz/blocks/generic.h> #include <lib/subghz/blocks/generic.h>
@@ -20,6 +21,9 @@ void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, v
} else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { } else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoSave); subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoSave);
} else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
view_dispatcher_send_custom_event(
subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoTxFullDpad);
} }
} }
@@ -29,7 +33,6 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
if(subghz_txrx_load_decoder_by_name_protocol( if(subghz_txrx_load_decoder_by_name_protocol(
subghz->txrx, subghz->txrx,
subghz_history_get_protocol_name(subghz->history, subghz->idx_menu_chosen))) { subghz_history_get_protocol_name(subghz->history, subghz->idx_menu_chosen))) {
// we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal
subghz_protocol_decoder_base_deserialize( subghz_protocol_decoder_base_deserialize(
subghz_txrx_get_decoder(subghz->txrx), subghz_txrx_get_decoder(subghz->txrx),
subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen)); subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen));
@@ -37,7 +40,6 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
SubGhzRadioPreset* preset = SubGhzRadioPreset* preset =
subghz_history_get_radio_preset(subghz->history, subghz->idx_menu_chosen); subghz_history_get_radio_preset(subghz->history, subghz->idx_menu_chosen);
//Edit TX power, if necessary.
subghz_txrx_set_tx_power(preset->data, preset->data_size, subghz->tx_power); subghz_txrx_set_tx_power(preset->data, preset->data_size, subghz->tx_power);
subghz_txrx_set_preset( subghz_txrx_set_preset(
@@ -93,7 +95,7 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) {
subghz_scene_receiver_info_callback, subghz_scene_receiver_info_callback,
subghz); subghz);
} }
// Removed static check
if(subghz_txrx_protocol_is_transmittable(subghz->txrx, false)) { if(subghz_txrx_protocol_is_transmittable(subghz->txrx, false)) {
widget_add_button_element( widget_add_button_element(
subghz->widget, subghz->widget,
@@ -101,9 +103,14 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) {
"Send", "Send",
subghz_scene_receiver_info_callback, subghz_scene_receiver_info_callback,
subghz); subghz);
widget_add_button_element(
subghz->widget,
GuiButtonTypeLeft,
"Full",
subghz_scene_receiver_info_callback,
subghz);
} }
} else { } else {
// [NO_DOLPHIN] widget_add_icon_element(subghz->widget, 83, 22, &I_WarningDolphinFlip_45x42);
widget_add_string_element( widget_add_string_element(
subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse."); subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse.");
} }
@@ -131,12 +138,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
if(!subghz_scene_receiver_info_update_parser(subghz)) { if(!subghz_scene_receiver_info_update_parser(subghz)) {
return false; return false;
} }
//CC1101 Stop RX -> Start TX
subghz_txrx_hopper_pause(subghz->txrx); subghz_txrx_hopper_pause(subghz->txrx);
// key concept: we start endless TX until user release OK button, and after this we send last
// protocols repeats - this guarantee that one press OK will
// be guarantee send the required minimum protocol data packets
// for all of this we use subghz_block_generic_global.endless_tx in protocols _yield function.
subghz->state_notifications = SubGhzNotificationStateTx; subghz->state_notifications = SubGhzNotificationStateTx;
subghz_block_generic_global.endless_tx = true; subghz_block_generic_global.endless_tx = true;
if(!subghz_tx_start( if(!subghz_tx_start(
@@ -146,37 +148,51 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
subghz_txrx_hopper_unpause(subghz->txrx); subghz_txrx_hopper_unpause(subghz->txrx);
subghz->state_notifications = SubGhzNotificationStateRx; subghz->state_notifications = SubGhzNotificationStateRx;
subghz_block_generic_global.endless_tx = false; subghz_block_generic_global.endless_tx = false;
return true;
} }
return true;
} else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) { } else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) {
//CC1101 Stop Tx -> next tick event Start RX
// user release OK
// we switch off endless_tx - that mean protocols yield finish endless transmission,
// send upload "repeat=xx" times, and after will be stoped by the tick event down in this code
subghz->state_notifications = SubGhzNotificationStateTxWait; subghz->state_notifications = SubGhzNotificationStateTxWait;
subghz_block_generic_global.endless_tx = false; subghz_block_generic_global.endless_tx = false;
return true; return true;
} else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) {
//CC1101 Stop RX -> Save
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
subghz_txrx_stop(subghz->txrx); subghz_txrx_stop(subghz->txrx);
if(!subghz_scene_receiver_info_update_parser(subghz)) { if(!subghz_scene_receiver_info_update_parser(subghz)) {
return false; return false;
} }
if(subghz_txrx_protocol_is_serializable(subghz->txrx)) { if(subghz_txrx_protocol_is_serializable(subghz->txrx)) {
subghz_file_name_clear(subghz); subghz_file_name_clear(subghz);
subghz->save_datetime = subghz->save_datetime =
subghz_history_get_datetime(subghz->history, subghz->idx_menu_chosen); subghz_history_get_datetime(subghz->history, subghz->idx_menu_chosen);
subghz->save_datetime_set = true; subghz->save_datetime_set = true;
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
} }
return true; return true;
} else if(event.event == SubGhzCustomEventSceneReceiverInfoTxFullDpad) {
if(!subghz_scene_receiver_info_update_parser(subghz)) {
return false;
}
FlipperFormat* fff_history =
subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen);
FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx);
Stream* src = flipper_format_get_raw_stream(fff_history);
Stream* dst = flipper_format_get_raw_stream(fff_data);
stream_seek(src, 0, StreamOffsetFromStart);
stream_clean(dst);
stream_copy_full(src, dst);
stream_seek(dst, 0, StreamOffsetFromStart);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);
return true;
} }
} else if(event.type == SceneManagerEventTypeTick) { } else if(event.type == SceneManagerEventTypeTick) {
if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) {
subghz_txrx_hopper_update(subghz->txrx, subghz->last_settings->hopping_threshold); subghz_txrx_hopper_update(subghz->txrx, subghz->last_settings->hopping_threshold);
@@ -193,18 +209,15 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
subghz->state_notifications = SubGhzNotificationStateRx; subghz->state_notifications = SubGhzNotificationStateRx;
break; break;
case SubGhzNotificationStateTxWait: case SubGhzNotificationStateTxWait:
// we wait until hardware TX finished and after stop TX and start RX, else just blink led
if(!subghz_devices_is_async_complete_tx(subghz->txrx->radio_device)) { if(!subghz_devices_is_async_complete_tx(subghz->txrx->radio_device)) {
notification_message(subghz->notifications, &sequence_blink_magenta_10); notification_message(subghz->notifications, &sequence_blink_magenta_10);
} else { } else {
subghz_txrx_stop(subghz->txrx); subghz_txrx_stop(subghz->txrx);
// update screen
widget_reset(subghz->widget); widget_reset(subghz->widget);
subghz_scene_receiver_info_draw_widget(subghz); subghz_scene_receiver_info_draw_widget(subghz);
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(!scene_manager_has_previous_scene(
if(!scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneDecodeRAW)) { subghz->scene_manager, SubGhzSceneDecodeRAW)) {
subghz_txrx_rx_start(subghz->txrx); subghz_txrx_rx_start(subghz->txrx);
subghz_txrx_hopper_unpause(subghz->txrx); subghz_txrx_hopper_unpause(subghz->txrx);
if(!subghz_history_get_text_space_left(subghz->history, NULL)) { if(!subghz_history_get_text_space_left(subghz->history, NULL)) {
@@ -222,7 +235,6 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
void subghz_scene_receiver_info_on_exit(void* context) { void subghz_scene_receiver_info_on_exit(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
widget_reset(subghz->widget); widget_reset(subghz->widget);
subghz_txrx_reset_dynamic_and_custom_btns(subghz->txrx); subghz_txrx_reset_dynamic_and_custom_btns(subghz->txrx);
} }
@@ -110,6 +110,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
} else if(event.type == SceneManagerEventTypeCustom) { } else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneSaveName) { if(event.event == SubGhzCustomEventSceneSaveName) {
if(strcmp(subghz->file_name_tmp, "") != 0) { if(strcmp(subghz->file_name_tmp, "") != 0) {
furi_string_reset(subghz->error_str);
furi_string_cat_printf( furi_string_cat_printf(
subghz->file_path, subghz->file_path,
"/%s%s", "/%s%s",
@@ -12,8 +12,14 @@ void subghz_scene_save_success_on_enter(void* context) {
// Setup view // Setup view
Popup* popup = subghz->popup; Popup* popup = subghz->popup;
// [NO_DOLPHIN] popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); // [NO_DOLPHIN] popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); if(furi_string_size(subghz->error_str)) {
popup_set_timeout(popup, 1500); 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_context(popup, subghz);
popup_set_callback(popup, subghz_scene_save_success_popup_callback); popup_set_callback(popup, subghz_scene_save_success_popup_callback);
popup_enable_timeout(popup); popup_enable_timeout(popup);
@@ -72,4 +78,5 @@ void subghz_scene_save_success_on_exit(void* context) {
Popup* popup = subghz->popup; Popup* popup = subghz->popup;
popup_reset(popup); popup_reset(popup);
furi_string_reset(subghz->error_str);
} }
@@ -2,10 +2,10 @@
enum SubmenuIndex { enum SubmenuIndex {
SubmenuIndexEmulate, SubmenuIndexEmulate,
SubmenuIndexSignalSettings,
SubmenuIndexPsaDecrypt, SubmenuIndexPsaDecrypt,
SubmenuIndexEdit, SubmenuIndexEdit,
SubmenuIndexDelete, SubmenuIndexDelete,
SubmenuIndexSignalSettings,
SubmenuIndexCounterBf, /* <-- comma was missing here */ SubmenuIndexCounterBf, /* <-- comma was missing here */
SubmenuIndexCarEmulateSettings, SubmenuIndexCarEmulateSettings,
}; };
@@ -20,17 +20,20 @@ void subghz_scene_saved_menu_on_enter(void* context) {
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx); FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
bool is_psa_encrypted = false; bool is_psa_encrypted = false;
bool has_signal_editor = false;
bool has_counter = false; bool has_counter = false;
if(fff) { if(fff) {
FuriString* proto = furi_string_alloc(); FuriString* proto = furi_string_alloc();
flipper_format_rewind(fff); flipper_format_rewind(fff);
if(flipper_format_read_string(fff, "Protocol", proto)) { if(flipper_format_read_string(fff, "Protocol", proto)) {
has_signal_editor = !furi_string_equal_str(proto, "RAW");
if(furi_string_equal_str(proto, "PSA GROUP")) { if(furi_string_equal_str(proto, "PSA GROUP")) {
FuriString* type_str = furi_string_alloc(); FuriString* type_str = furi_string_alloc();
flipper_format_rewind(fff); flipper_format_rewind(fff);
if(!flipper_format_read_string(fff, "Type", type_str) || if(!flipper_format_read_string(fff, "Type", type_str) ||
furi_string_equal_str(type_str, "00")) { furi_string_equal_str(type_str, "00")) {
is_psa_encrypted = true; is_psa_encrypted = true;
has_signal_editor = false;
} }
furi_string_free(type_str); furi_string_free(type_str);
} }
@@ -53,6 +56,15 @@ void subghz_scene_saved_menu_on_enter(void* context) {
SubmenuIndexEmulate, SubmenuIndexEmulate,
subghz_scene_saved_menu_submenu_callback, subghz_scene_saved_menu_submenu_callback,
subghz); subghz);
if(has_signal_editor) {
submenu_add_item(
subghz->submenu,
"Signal Editor",
SubmenuIndexSignalSettings,
subghz_scene_saved_menu_submenu_callback,
subghz);
}
} }
if(is_psa_encrypted) { if(is_psa_encrypted) {
@@ -85,15 +97,6 @@ void subghz_scene_saved_menu_on_enter(void* context) {
subghz_scene_saved_menu_submenu_callback, subghz_scene_saved_menu_submenu_callback,
subghz); 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) { if(has_counter) {
submenu_add_item( submenu_add_item(
subghz->submenu, subghz->submenu,
@@ -5,6 +5,8 @@
#include <machine/endian.h> #include <machine/endian.h>
#include <toolbox/strint.h> #include <toolbox/strint.h>
#include <lib/subghz/blocks/generic.h> #include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/custom_btn_i.h>
#include <string.h>
#define TAG "SubGhzSceneSignalSettings" #define TAG "SubGhzSceneSignalSettings"
@@ -21,6 +23,14 @@ static uint8_t btn_byte_count = 1;
static uint8_t* btn_byte_ptr = NULL; static uint8_t* btn_byte_ptr = NULL;
static uint8_t submenu_called = 0; 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 #define COUNTER_MODE_COUNT 8
static const char* const counter_mode_text[COUNTER_MODE_COUNT] = { static const char* const counter_mode_text[COUNTER_MODE_COUNT] = {
@@ -45,6 +55,65 @@ static const int32_t counter_mode_value[COUNTER_MODE_COUNT] = {
7, 7,
}; };
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,
5,
6,
7,
};
#define BUTTON_VALUE_COUNT (sizeof(button_value) / sizeof(button_value[0]))
static const char* const button_default_labels[BUTTON_VALUE_COUNT] = {
"Original",
"Up",
"Down",
"Left",
"Right",
"Button 5",
"Button 6",
"Button 7",
};
static const char* button_labels[BUTTON_VALUE_COUNT] = {
"Original",
"Up",
"Down",
"Left",
"Right",
"Button 5",
"Button 6",
"Button 7",
};
typedef struct {
const char* protocol;
const char* labels[BUTTON_VALUE_COUNT];
} ProtocolButtonLabels;
static const ProtocolButtonLabels protocol_button_labels[] = {
{"VAG GROUP", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
{"Porsche AG", {"Original", "Lock", "Unlock", "Trunk", "Open"}},
{"FORD V0", {"Original", "Lock", "Unlock", "Trunk"}},
{"Ford V2", {"Unlock", "Lock", "Trunk", "Panic", "Remote Start"}},
{"PSA GROUP", {"Original", "Lock", "Unlock", "Trunk", "Trunk"}},
{"KIA/HYU V0", {"Original", "Lock", "Unlock", "Trunk", "Horn"}},
{"KIA/HYU V1", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
{"KIA/HYU V2", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
{"KIA/HYU V3/V4", {"Original", "Lock", "Unlock", "Trunk", "Panic", "Horn"}},
{"KIA/HYU V5", {"Original", "Unlock", "Lock", "Trunk", "Horn"}},
{"KIA/HYU V6", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
{"SUBARU", {"Original", "Lock", "Unlock", "Trunk", "Panic", "Extra"}},
{"SUZUKI", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
{"Star Line", {"Original", "Lock", "Unlock", "Trunk", "Start"}},
{"Scher-Khan", {"Original", "Lock", "Unlock", "Trunk", "Start"}},
{"Sheriff CFM", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
};
typedef struct { typedef struct {
char* name; char* name;
uint8_t mode_count; uint8_t mode_count;
@@ -59,7 +128,171 @@ static Protocols protocols[] = {
{"Phoenix_V2", 3}, {"Phoenix_V2", 3},
}; };
#define PROTOCOLS_COUNT (sizeof(protocols) / sizeof(Protocols)); #define PROTOCOLS_COUNT (sizeof(protocols) / sizeof(Protocols))
static void subghz_scene_signal_settings_reset_button_labels(void) {
for(uint8_t i = 0; i < BUTTON_VALUE_COUNT; i++) {
button_labels[i] = button_default_labels[i];
}
}
static void subghz_scene_signal_settings_apply_button_labels(const char* protocol) {
for(uint8_t i = 0; i < COUNT_OF(protocol_button_labels); i++) {
if(strcmp(protocol, protocol_button_labels[i].protocol) == 0) {
for(uint8_t btn = 0; btn < BUTTON_VALUE_COUNT; btn++) {
if(protocol_button_labels[i].labels[btn]) {
button_labels[btn] = protocol_button_labels[i].labels[btn];
}
}
break;
}
}
}
static const char* subghz_scene_signal_settings_get_button_label(uint8_t custom_btn_id) {
if(custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) {
uint8_t original = subghz_custom_btn_get_original();
if((original != SUBGHZ_CUSTOM_BTN_OK) && (original < BUTTON_VALUE_COUNT)) {
return button_labels[original];
}
}
if(custom_btn_id < BUTTON_VALUE_COUNT) {
return button_labels[custom_btn_id];
}
return "Button";
}
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_hex_digit(char c, uint8_t* value) {
if(c >= '0' && c <= '9') {
*value = c - '0';
return true;
} else if(c >= 'a' && c <= 'f') {
*value = c - 'a' + 10;
return true;
} else if(c >= 'A' && c <= 'F') {
*value = c - 'A' + 10;
return true;
}
return false;
}
static bool subghz_scene_signal_settings_parse_hex_field(
const char* text,
const char* marker,
uint32_t* value,
uint8_t* length_bit) {
const char* field = strstr(text, marker);
if(!field) return false;
field += strlen(marker);
while(*field == ' ' || *field == '\t') {
field++;
}
uint32_t parsed = 0;
uint8_t digits = 0;
uint8_t digit_value = 0;
while(digits < 8 && subghz_scene_signal_settings_hex_digit(*field, &digit_value)) {
parsed = (parsed << 4) | digit_value;
digits++;
field++;
}
if(digits == 0) return false;
*value = parsed;
*length_bit = digits * 4;
return true;
}
static void subghz_scene_signal_settings_apply_text_fallback(FuriString* decoded_text) {
uint32_t value = 0;
uint8_t length_bit = 0;
const char* text = furi_string_get_cstr(decoded_text);
if(!subghz_block_generic_global.cnt_is_available &&
subghz_scene_signal_settings_parse_hex_field(text, "Cnt:", &value, &length_bit)) {
subghz_block_generic_global.cnt_is_available = true;
subghz_block_generic_global.current_cnt = value;
subghz_block_generic_global.cnt_length_bit = length_bit;
}
if(!subghz_block_generic_global.btn_is_available &&
subghz_scene_signal_settings_parse_hex_field(text, "Btn:", &value, &length_bit)) {
subghz_block_generic_global.btn_is_available = true;
subghz_block_generic_global.current_btn = value & 0xFF;
subghz_block_generic_global.btn_length_bit = length_bit > 8 ? 8 : length_bit;
}
}
static void subghz_scene_signal_settings_apply_file_fallback(FlipperFormat* fff) {
uint32_t value = 0;
if(!subghz_block_generic_global.cnt_is_available) {
flipper_format_rewind(fff);
if(flipper_format_read_uint32(fff, "Cnt", &value, 1)) {
subghz_block_generic_global.cnt_is_available = true;
subghz_block_generic_global.current_cnt = value;
subghz_block_generic_global.cnt_length_bit = 32;
}
}
if(!subghz_block_generic_global.btn_is_available) {
flipper_format_rewind(fff);
if(flipper_format_read_uint32(fff, "Btn", &value, 1)) {
subghz_block_generic_global.btn_is_available = true;
subghz_block_generic_global.current_btn = value & 0xFF;
subghz_block_generic_global.btn_length_bit = 8;
}
}
}
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);
}
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) { void subghz_scene_signal_settings_counter_mode_changed(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
@@ -99,6 +332,22 @@ 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;
button_custom_id = button_value[index];
variable_item_set_current_value_text(
item, subghz_scene_signal_settings_get_button_label(button_custom_id));
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) { void subghz_scene_signal_settings_byte_input_callback(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone);
@@ -108,8 +357,10 @@ void subghz_scene_signal_settings_variable_item_list_enter_callback(void* contex
SubGhz* subghz = context; SubGhz* subghz = context;
// when we click OK on "Edit counter" item // 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; 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_printf(byte_input_text, "%i", subghz_block_generic_global.cnt_length_bit);
furi_string_cat_str(byte_input_text, "-bits counter in HEX"); furi_string_cat_str(byte_input_text, "-bits counter in HEX");
@@ -127,8 +378,10 @@ void subghz_scene_signal_settings_variable_item_list_enter_callback(void* contex
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput);
} }
// when we click OK on "Edit button" item // 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; 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_printf(byte_input_text, "%i", subghz_block_generic_global.btn_length_bit);
furi_string_cat_str(byte_input_text, "-bits button in HEX"); furi_string_cat_str(byte_input_text, "-bits button in HEX");
@@ -150,6 +403,20 @@ void subghz_scene_signal_settings_variable_item_list_enter_callback(void* contex
void subghz_scene_signal_settings_on_enter(void* context) { void subghz_scene_signal_settings_on_enter(void* context) {
SubGhz* subghz = 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();
subghz_scene_signal_settings_reset_button_labels();
// ### Counter mode section ### // ### Counter mode section ###
// When we open saved file we do some check and fill up subghz->file_path. // When we open saved file we do some check and fill up subghz->file_path.
@@ -162,6 +429,7 @@ void subghz_scene_signal_settings_on_enter(void* context) {
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
FuriString* tmp_text = furi_string_alloc_set_str(""); FuriString* tmp_text = furi_string_alloc_set_str("");
FuriString* protocol_name = furi_string_alloc();
uint32_t tmp_counter_mode = 0; uint32_t tmp_counter_mode = 0;
counter_mode = 0xff; counter_mode = 0xff;
@@ -174,8 +442,9 @@ void subghz_scene_signal_settings_on_enter(void* context) {
FURI_LOG_E(TAG, "Error open file %s", file_path); FURI_LOG_E(TAG, "Error open file %s", file_path);
} else { } else {
flipper_format_read_string(fff_data_file, "Protocol", tmp_text); flipper_format_read_string(fff_data_file, "Protocol", tmp_text);
furi_string_set(protocol_name, tmp_text);
// compare available protocols names, load CounterMode value from file and setup variable_item_list values_count // compare available protocols names, load CounterMode value from file and setup variable_item_list values_count
for(uint8_t i = 0; i < PROTOCOLS_COUNT i++) { for(uint8_t i = 0; i < PROTOCOLS_COUNT; i++) {
if(!strcmp(furi_string_get_cstr(tmp_text), protocols[i].name)) { if(!strcmp(furi_string_get_cstr(tmp_text), protocols[i].name)) {
mode_count = protocols[i].mode_count; mode_count = protocols[i].mode_count;
if(flipper_format_read_uint32(fff_data_file, "CounterMode", &tmp_counter_mode, 1)) { if(flipper_format_read_uint32(fff_data_file, "CounterMode", &tmp_counter_mode, 1)) {
@@ -223,7 +492,10 @@ void subghz_scene_signal_settings_on_enter(void* context) {
// deserialaze and decode loaded sugbhz file and push data to subghz_block_generic_global variable // deserialaze and decode loaded sugbhz file and push data to subghz_block_generic_global variable
if(subghz_protocol_decoder_base_deserialize(decoder, subghz_txrx_get_fff_data(subghz->txrx)) == if(subghz_protocol_decoder_base_deserialize(decoder, subghz_txrx_get_fff_data(subghz->txrx)) ==
SubGhzProtocolStatusOk) { SubGhzProtocolStatusOk) {
subghz_scene_signal_settings_apply_button_labels(furi_string_get_cstr(protocol_name));
subghz_protocol_decoder_base_get_string(decoder, tmp_text); subghz_protocol_decoder_base_get_string(decoder, tmp_text);
subghz_scene_signal_settings_apply_text_fallback(tmp_text);
subghz_scene_signal_settings_apply_file_fallback(subghz_txrx_get_fff_data(subghz->txrx));
} else { } else {
FURI_LOG_E(TAG, "Cant deserialize this subghz file"); FURI_LOG_E(TAG, "Cant deserialize this subghz file");
} }
@@ -232,6 +504,7 @@ void subghz_scene_signal_settings_on_enter(void* context) {
if(!subghz_block_generic_global.cnt_is_available) { if(!subghz_block_generic_global.cnt_is_available) {
counter_mode = 0xff; counter_mode = 0xff;
furi_string_set_str(tmp_text, "-");
FURI_LOG_D(TAG, "Counter mode and edit not available for this protocol"); FURI_LOG_D(TAG, "Counter mode and edit not available for this protocol");
} else { } else {
counter_not_available = false; counter_not_available = false;
@@ -261,26 +534,45 @@ void subghz_scene_signal_settings_on_enter(void* context) {
// ### Button edit section ### // ### Button edit section ###
if(!subghz_block_generic_global.btn_is_available) { if(subghz_custom_btn_is_allowed()) {
uint8_t max_custom_btn = subghz_custom_btn_get_max();
uint8_t custom_button_count = max_custom_btn + 1;
if(custom_button_count > BUTTON_VALUE_COUNT) custom_button_count = BUTTON_VALUE_COUNT;
button_uses_custom_btn = custom_button_count > 1;
}
if(button_uses_custom_btn) {
button_not_available = false;
furi_string_set_str(
tmp_text, subghz_scene_signal_settings_get_button_label(SUBGHZ_CUSTOM_BTN_OK));
} else 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"); FURI_LOG_D(TAG, "Button edit not available for this protocol");
} else { } else {
button_not_available = false; button_not_available = false;
button = subghz_block_generic_global.current_btn; button = subghz_block_generic_global.current_btn;
furi_string_printf(tmp_text, "%X", button);
btn_byte_ptr = (uint8_t*)&button; btn_byte_ptr = (uint8_t*)&button;
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_index(item, 0);
variable_item_set_current_value_text(item, furi_string_get_cstr(tmp_text)); 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 !"); 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); furi_string_free(tmp_text);
furi_string_free(protocol_name);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
} }
@@ -290,23 +582,25 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventByteInputDone) { if(event.event == SubGhzCustomEventByteInputDone) {
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
switch(submenu_called) { switch(submenu_called) {
// edit counter // edit counter
case 1: case 1:
switch(cnt_byte_count) { switch(cnt_byte_count) {
case 2: case 2:
// set new cnt value and override_flag to global variable and call transmit to generate and save subghz signal
counter16 = __bswap16(counter16); counter16 = __bswap16(counter16);
subghz_scene_signal_settings_update_uint32_field(fff, "Cnt", counter16);
subghz_block_generic_global_counter_override_set(counter16); subghz_block_generic_global_counter_override_set(counter16);
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx)); subghz_scene_signal_settings_rebuild_save_reload(
subghz_txrx_stop(subghz->txrx); subghz, false, SUBGHZ_CUSTOM_BTN_OK);
break; break;
case 4: case 4:
// the same for 32 bit Counter
counter32 = __bswap32(counter32); counter32 = __bswap32(counter32);
subghz_scene_signal_settings_update_uint32_field(fff, "Cnt", counter32);
subghz_block_generic_global_counter_override_set(counter32); subghz_block_generic_global_counter_override_set(counter32);
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx)); subghz_scene_signal_settings_rebuild_save_reload(
subghz_txrx_stop(subghz->txrx); subghz, false, SUBGHZ_CUSTOM_BTN_OK);
break; break;
default: default:
break; break;
@@ -314,14 +608,10 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
break; break;
// edit button // edit button
case 2: case 2:
subghz_scene_signal_settings_update_uint32_field(fff, "Btn", button);
subghz_block_generic_global_button_override_set(button); subghz_block_generic_global_button_override_set(button);
// save counter mult to rewrite subghz singnal without changing counter subghz_scene_signal_settings_rebuild_save_reload(
int32_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult(); subghz, false, SUBGHZ_CUSTOM_BTN_OK);
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);
break; break;
default: default:
@@ -330,13 +620,10 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
scene_manager_previous_scene(subghz->scene_manager); scene_manager_previous_scene(subghz->scene_manager);
return true; 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; return false;
} }
@@ -352,4 +639,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_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0);
byte_input_set_header_text(subghz->byte_input, ""); byte_input_set_header_text(subghz->byte_input, "");
furi_string_free(byte_input_text); furi_string_free(byte_input_text);
subghz_custom_btns_reset();
} }
+25 -12
View File
@@ -13,12 +13,15 @@ def ProtoPirateDefineEnabled(name, app_manifest_path=app_manifest_path):
_ENABLE_TIMING_TUNER = ProtoPirateDefineEnabled("ENABLE_TIMING_TUNER_SCENE") _ENABLE_TIMING_TUNER = ProtoPirateDefineEnabled("ENABLE_TIMING_TUNER_SCENE")
_ENABLE_EMULATE = ProtoPirateDefineEnabled("ENABLE_EMULATE_FEATURE")
_MAIN_APP_SOURCES = [ _MAIN_APP_SOURCES = [
"*.c*", "*.c*",
"!protocols/plugins", "!protocols/plugins",
"!scenes/plugins", "!scenes/plugins",
"!protocols", "!protocols",
"!protopirate_emulate_plugin.c",
"!protopirate_psa_bf_plugin.c",
"protocols/protocol_items.c", "protocols/protocol_items.c",
"protocols/protocols_common.c", "protocols/protocols_common.c",
"!raw_file_reader.c", "!raw_file_reader.c",
@@ -30,6 +33,11 @@ if not _ENABLE_TIMING_TUNER:
"!protocol_timings.c", "!protocol_timings.c",
] ]
if not _ENABLE_EMULATE:
_MAIN_APP_SOURCES += [
"!protopirate_scene_emulate.c",
]
App( App(
appid="proto_pirate", appid="proto_pirate",
name="ProtoPirate", name="ProtoPirate",
@@ -39,7 +47,7 @@ App(
requires=["gui"], requires=["gui"],
stack_size=8 * 1024, stack_size=8 * 1024,
fap_description="Decode car key fob signals from Sub-GHz", fap_description="Decode car key fob signals from Sub-GHz",
fap_version="3.0", fap_version="3.2",
fap_icon="images/protopirate_10px.png", fap_icon="images/protopirate_10px.png",
fap_category="Sub-GHz", fap_category="Sub-GHz",
fap_icon_assets="images", fap_icon_assets="images",
@@ -165,7 +173,11 @@ def ProtoPirateTxProtocolPlugin(
App=App, App=App,
FlipperAppType=FlipperAppType, FlipperAppType=FlipperAppType,
tx_plugin_sources=_TX_PLUGIN_SOURCES, tx_plugin_sources=_TX_PLUGIN_SOURCES,
enable_emulate=_ENABLE_EMULATE,
): ):
if not enable_emulate:
return
App( App(
appid=f"protopirate_tx_{key}_plugin", appid=f"protopirate_tx_{key}_plugin",
apptype=FlipperAppType.PLUGIN, apptype=FlipperAppType.PLUGIN,
@@ -370,17 +382,18 @@ if _ENABLE_TIMING_TUNER:
fal_embedded=True, fal_embedded=True,
) )
App( if _ENABLE_EMULATE:
appid="protopirate_emulate_plugin", App(
apptype=FlipperAppType.PLUGIN, appid="protopirate_emulate_plugin",
entry_point="protopirate_emulate_plugin_ep", apptype=FlipperAppType.PLUGIN,
requires=["proto_pirate"], entry_point="protopirate_emulate_plugin_ep",
sources=[ requires=["proto_pirate"],
"scenes/plugins/protopirate_emulate_plugin.c", sources=[
"protocols/protocol_items.c", "scenes/plugins/protopirate_emulate_plugin.c",
], "protocols/protocol_items.c",
fal_embedded=True, ],
) fal_embedded=True,
)
App( App(
appid="protopirate_psa_bf_plugin", appid="protopirate_psa_bf_plugin",
@@ -7,7 +7,9 @@
#include <string.h> #include <string.h>
#define TAG "ProtoPirateProtocolPlugin" #define TAG "ProtoPirateProtocolPlugin"
#ifdef ENABLE_EMULATE_FEATURE
#define PROTOPIRATE_TX_PLUGIN_PATH_MAX 160U #define PROTOPIRATE_TX_PLUGIN_PATH_MAX 160U
#endif
static const char* protopirate_get_registry_plugin_path(ProtoPirateProtocolRegistryRoute route) { static const char* protopirate_get_registry_plugin_path(ProtoPirateProtocolRegistryRoute route) {
switch(route) { switch(route) {
@@ -25,6 +27,7 @@ static const char* protopirate_get_registry_plugin_path(ProtoPirateProtocolRegis
} }
} }
#ifdef ENABLE_EMULATE_FEATURE
static bool protopirate_build_tx_protocol_plugin_path( static bool protopirate_build_tx_protocol_plugin_path(
const char* tx_key, const char* tx_key,
char* plugin_path, char* plugin_path,
@@ -40,6 +43,7 @@ static bool protopirate_build_tx_protocol_plugin_path(
tx_key); tx_key);
return (written > 0) && ((size_t)written < plugin_path_size); return (written > 0) && ((size_t)written < plugin_path_size);
} }
#endif
static const SubGhzProtocolRegistry protopirate_empty_protocol_registry = { static const SubGhzProtocolRegistry protopirate_empty_protocol_registry = {
.items = NULL, .items = NULL,
@@ -165,6 +169,7 @@ static bool protopirate_ensure_protocol_registry_plugin(
return true; return true;
} }
#ifdef ENABLE_EMULATE_FEATURE
static bool protopirate_ensure_tx_protocol_plugin( static bool protopirate_ensure_tx_protocol_plugin(
ProtoPirateApp* app, ProtoPirateApp* app,
const char* protocol_name, const char* protocol_name,
@@ -264,6 +269,7 @@ static bool protopirate_ensure_tx_protocol_plugin(
*registry = plugin->registry; *registry = plugin->registry;
return true; return true;
} }
#endif
bool protopirate_refresh_protocol_registry(ProtoPirateApp* app, bool ensure_receiver_ready) { bool protopirate_refresh_protocol_registry(ProtoPirateApp* app, bool ensure_receiver_ready) {
furi_check(app); furi_check(app);
@@ -378,6 +384,7 @@ bool protopirate_apply_protocol_registry_for_context(
} }
if(protocol_name && protocol_name[0] != '\0') { if(protocol_name && protocol_name[0] != '\0') {
#ifdef ENABLE_EMULATE_FEATURE
const char* registry_name = protopirate_protocol_catalog_canonical_name(protocol_name); const char* registry_name = protopirate_protocol_catalog_canonical_name(protocol_name);
if(!registry_name || !protopirate_protocol_catalog_can_tx(protocol_name)) { if(!registry_name || !protopirate_protocol_catalog_can_tx(protocol_name)) {
FURI_LOG_E(TAG, "No TX protocol plugin for %s", protocol_name); FURI_LOG_E(TAG, "No TX protocol plugin for %s", protocol_name);
@@ -410,6 +417,9 @@ bool protopirate_apply_protocol_registry_for_context(
subghz_environment_set_protocol_registry(app->txrx->environment, tx_registry); subghz_environment_set_protocol_registry(app->txrx->environment, tx_registry);
app->txrx->protocol_registry = tx_registry; app->txrx->protocol_registry = tx_registry;
return true; return true;
#else
return false;
#endif
} }
ProtoPirateProtocolRegistryRoute route = protopirate_get_protocol_registry_route( ProtoPirateProtocolRegistryRoute route = protopirate_get_protocol_registry_route(
@@ -103,12 +103,14 @@ void protopirate_settings_load(ProtoPirateSettings* settings) {
} }
settings->hopping_enabled = (hopping_temp == 1); settings->hopping_enabled = (hopping_temp == 1);
#ifdef ENABLE_EMULATE_FEATURE
uint32_t emulate_temp = 0; uint32_t emulate_temp = 0;
if(!flipper_format_read_uint32(ff, "EmulateFeature", &emulate_temp, 1)) { if(!flipper_format_read_uint32(ff, "EmulateFeature", &emulate_temp, 1)) {
FURI_LOG_I(TAG, "EmulateFeature key missing, defaulting to disabled"); FURI_LOG_I(TAG, "EmulateFeature key missing, defaulting to disabled");
emulate_temp = 0; emulate_temp = 0;
} }
settings->emulate_feature_enabled = (emulate_temp == 1); settings->emulate_feature_enabled = (emulate_temp == 1);
#endif
uint32_t check_saved_temp = 0; uint32_t check_saved_temp = 0;
if(!flipper_format_read_uint32(ff, "CheckSaved", &check_saved_temp, 1)) { if(!flipper_format_read_uint32(ff, "CheckSaved", &check_saved_temp, 1)) {
@@ -185,11 +187,13 @@ void protopirate_settings_save(ProtoPirateSettings* settings) {
break; break;
} }
#ifdef ENABLE_EMULATE_FEATURE
uint32_t emulate_temp = settings->emulate_feature_enabled ? 1 : 0; uint32_t emulate_temp = settings->emulate_feature_enabled ? 1 : 0;
if(!flipper_format_write_uint32(ff, "EmulateFeature", &emulate_temp, 1)) { if(!flipper_format_write_uint32(ff, "EmulateFeature", &emulate_temp, 1)) {
FURI_LOG_E(TAG, "Failed to write emulate feature flag"); FURI_LOG_E(TAG, "Failed to write emulate feature flag");
break; break;
} }
#endif
uint32_t check_saved_temp = settings->check_saved ? 1 : 0; uint32_t check_saved_temp = settings->check_saved ? 1 : 0;
if(!flipper_format_write_uint32(ff, "CheckSaved", &check_saved_temp, 1)) { if(!flipper_format_write_uint32(ff, "CheckSaved", &check_saved_temp, 1)) {
@@ -513,6 +513,20 @@ static bool protopirate_storage_copy_key_2(
protopirate_storage_copy_u32_optional(save_file, flipper_format, "Key_2"); protopirate_storage_copy_u32_optional(save_file, flipper_format, "Key_2");
} }
static bool protopirate_storage_copy_key2(
FlipperFormat* save_file,
FlipperFormat* flipper_format) {
bool copied = false;
if(!protopirate_storage_copy_hex_fixed(save_file, flipper_format, "Key2", 8, &copied)) {
return false;
}
if(copied) {
return true;
}
return protopirate_storage_copy_hex_or_u32(save_file, flipper_format, "Key2", 4);
}
static bool protopirate_storage_write_capture_data( static bool protopirate_storage_write_capture_data(
FlipperFormat* save_file, FlipperFormat* save_file,
FlipperFormat* flipper_format) { FlipperFormat* flipper_format) {
@@ -548,7 +562,7 @@ static bool protopirate_storage_write_capture_data(
protopirate_storage_base_u32_fields, protopirate_storage_base_u32_fields,
COUNT_OF(protopirate_storage_base_u32_fields))) COUNT_OF(protopirate_storage_base_u32_fields)))
break; break;
if(!protopirate_storage_copy_hex_or_u32(save_file, flipper_format, "Key2", 4)) break; if(!protopirate_storage_copy_key2(save_file, flipper_format)) break;
if(!protopirate_storage_copy_u32_optional(save_file, flipper_format, "KeyIdx")) break; if(!protopirate_storage_copy_u32_optional(save_file, flipper_format, "KeyIdx")) break;
if(!protopirate_storage_copy_u32_optional(save_file, flipper_format, "Seed")) break; if(!protopirate_storage_copy_u32_optional(save_file, flipper_format, "Seed")) break;
if(!protopirate_storage_copy_hex_or_u32(save_file, flipper_format, "ValidationField", 2)) if(!protopirate_storage_copy_hex_or_u32(save_file, flipper_format, "ValidationField", 2))
@@ -3,6 +3,8 @@
#include <furi.h> #include <furi.h>
#include <string.h> #include <string.h>
#include "../defines.h"
#define TAG "ProtoPirateCatalog" #define TAG "ProtoPirateCatalog"
#define PROTOPIRATE_CC1101_REG_MDMCFG2 0x12U #define PROTOPIRATE_CC1101_REG_MDMCFG2 0x12U
@@ -17,6 +19,12 @@
#define PROTOPIRATE_COUNT_OF(array) (sizeof(array) / sizeof((array)[0])) #define PROTOPIRATE_COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
#ifdef ENABLE_EMULATE_FEATURE
#define PROTOPIRATE_TX_KEY(key) key
#else
#define PROTOPIRATE_TX_KEY(key) NULL
#endif
typedef enum { typedef enum {
ProtoPirateProtocolCatalogModulationAM = 0, ProtoPirateProtocolCatalogModulationAM = 0,
ProtoPirateProtocolCatalogModulationFM, ProtoPirateProtocolCatalogModulationFM,
@@ -28,33 +36,33 @@ typedef struct {
} ProtoPirateProtocolCatalogAlias; } ProtoPirateProtocolCatalogAlias;
static const ProtoPirateProtocolCatalogEntry protopirate_protocol_catalog[] = { static const ProtoPirateProtocolCatalogEntry protopirate_protocol_catalog[] = {
{"Chrysler V0", ProtoPirateProtocolCatalogRouteAMDefault, "chrysler_v0"}, {"Chrysler V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("chrysler_v0")},
{"Fiat V0", ProtoPirateProtocolCatalogRouteAMDefault, "fiat_v0"}, {"Fiat V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("fiat_v0")},
{"Fiat V1", ProtoPirateProtocolCatalogRouteAMDefault, "fiat_v1"}, {"Fiat V1", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("fiat_v1")},
{"Fiat V2", ProtoPirateProtocolCatalogRouteAMDefault, NULL}, {"Fiat V2", ProtoPirateProtocolCatalogRouteAMDefault, NULL},
{"Ford V0", ProtoPirateProtocolCatalogRouteAMDefault, "ford_v0"}, {"Ford V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("ford_v0")},
{"Ford V1", ProtoPirateProtocolCatalogRouteFMF4, "ford_v1"}, {"Ford V1", ProtoPirateProtocolCatalogRouteFMF4, PROTOPIRATE_TX_KEY("ford_v1")},
{"Ford V2", ProtoPirateProtocolCatalogRouteFMF4, "ford_v2"}, {"Ford V2", ProtoPirateProtocolCatalogRouteFMF4, PROTOPIRATE_TX_KEY("ford_v2")},
{"Ford V3", ProtoPirateProtocolCatalogRouteFMF4, NULL}, {"Ford V3", ProtoPirateProtocolCatalogRouteFMF4, NULL},
{"Honda Static", ProtoPirateProtocolCatalogRouteFMHonda1, "honda_static"}, {"Honda Static", ProtoPirateProtocolCatalogRouteFMHonda1, PROTOPIRATE_TX_KEY("honda_static")},
{"Honda V1", ProtoPirateProtocolCatalogRouteAMDefault, "honda_v1"}, {"Honda V1", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("honda_v1")},
{"Kia V0", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v0"}, {"Kia V0", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v0")},
{"Kia V1", ProtoPirateProtocolCatalogRouteAMDefault, "kia_v1"}, {"Kia V1", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("kia_v1")},
{"Kia V2", ProtoPirateProtocolCatalogRouteAMDefault, "kia_v2"}, {"Kia V2", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("kia_v2")},
{"Kia V3/V4", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v3_v4"}, {"Kia V3/V4", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v3_v4")},
{"Kia V5", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v5"}, {"Kia V5", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v5")},
{"Kia V6", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v6"}, {"Kia V6", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v6")},
{"Kia V7", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v7"}, {"Kia V7", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v7")},
{"Honda V2", ProtoPirateProtocolCatalogRouteFMF4, "honda_v2"}, {"Honda V2", ProtoPirateProtocolCatalogRouteFMF4, PROTOPIRATE_TX_KEY("honda_v2")},
{"Mazda V0", ProtoPirateProtocolCatalogRouteByModulation, "mazda_v0"}, {"Mazda V0", ProtoPirateProtocolCatalogRouteByModulation, PROTOPIRATE_TX_KEY("mazda_v0")},
{"Mitsubishi V0", ProtoPirateProtocolCatalogRouteFMDefault, NULL}, {"Mitsubishi V0", ProtoPirateProtocolCatalogRouteFMDefault, NULL},
{"Porsche Touareg", ProtoPirateProtocolCatalogRouteAMDefault, NULL}, {"Porsche Touareg", ProtoPirateProtocolCatalogRouteAMDefault, NULL},
{"PSA", ProtoPirateProtocolCatalogRouteByModulation, "psa"}, {"PSA", ProtoPirateProtocolCatalogRouteByModulation, PROTOPIRATE_TX_KEY("psa")},
{"Renault V0", ProtoPirateProtocolCatalogRouteAMDefault, "renault_v0"}, {"Renault V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("renault_v0")},
{"Scher-Khan", ProtoPirateProtocolCatalogRouteFMDefault, NULL}, {"Scher-Khan", ProtoPirateProtocolCatalogRouteFMDefault, NULL},
{"Star Line", ProtoPirateProtocolCatalogRouteAMDefault, "star_line"}, {"Star Line", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("star_line")},
{"Subaru", ProtoPirateProtocolCatalogRouteAMDefault, "subaru"}, {"Subaru", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("subaru")},
{"VAG", ProtoPirateProtocolCatalogRouteAMVag, "vag"}, {"VAG", ProtoPirateProtocolCatalogRouteAMVag, PROTOPIRATE_TX_KEY("vag")},
}; };
static const ProtoPirateProtocolCatalogAlias protopirate_protocol_catalog_aliases[] = { static const ProtoPirateProtocolCatalogAlias protopirate_protocol_catalog_aliases[] = {
@@ -90,7 +90,11 @@ ProtoPirateApp* protopirate_app_alloc() {
app->auto_save = settings.auto_save; app->auto_save = settings.auto_save;
app->check_saved = settings.check_saved; app->check_saved = settings.check_saved;
app->tx_power = settings.tx_power; app->tx_power = settings.tx_power;
#ifdef ENABLE_EMULATE_FEATURE
app->emulate_feature_enabled = settings.emulate_feature_enabled; app->emulate_feature_enabled = settings.emulate_feature_enabled;
#else
app->emulate_feature_enabled = false;
#endif
// Init setting - KEEP THIS, it's small // Init setting - KEEP THIS, it's small
app->setting = subghz_setting_alloc(); app->setting = subghz_setting_alloc();
@@ -175,7 +179,11 @@ void protopirate_app_free(ProtoPirateApp* app) {
settings.check_saved = app->check_saved; settings.check_saved = app->check_saved;
settings.tx_power = app->tx_power; settings.tx_power = app->tx_power;
settings.hopping_enabled = (app->txrx->hopper_state != ProtoPirateHopperStateOFF); settings.hopping_enabled = (app->txrx->hopper_state != ProtoPirateHopperStateOFF);
#ifdef ENABLE_EMULATE_FEATURE
settings.emulate_feature_enabled = app->emulate_feature_enabled; settings.emulate_feature_enabled = app->emulate_feature_enabled;
#else
settings.emulate_feature_enabled = false;
#endif
// Find current preset index // Find current preset index
settings.preset_index = 0; settings.preset_index = 0;
@@ -273,14 +281,18 @@ int32_t protopirate_app(char* p) {
//We now jump straight to emulate scene from Browser. If the user wanted the key to look at, just click back. //We now jump straight to emulate scene from Browser. If the user wanted the key to look at, just click back.
if(load_saved) { if(load_saved) {
#ifdef ENABLE_EMULATE_FEATURE
if(protopirate_app->emulate_feature_enabled) { if(protopirate_app->emulate_feature_enabled) {
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
protopirate_app->view_dispatcher, ProtoPirateCustomEventSavedInfoEmulate); protopirate_app->view_dispatcher, ProtoPirateCustomEventSavedInfoEmulate);
notification_message(protopirate_app->notifications, &sequence_success); notification_message(protopirate_app->notifications, &sequence_success);
} else { } else {
#endif
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
protopirate_app->view_dispatcher, ProtoPirateCustomEventReceiverInfoSave); protopirate_app->view_dispatcher, ProtoPirateCustomEventReceiverInfoSave);
#ifdef ENABLE_EMULATE_FEATURE
} }
#endif
} }
view_dispatcher_run(protopirate_app->view_dispatcher); view_dispatcher_run(protopirate_app->view_dispatcher);
@@ -12,6 +12,7 @@
#define CREDIT_LINE_HEIGHT 10 #define CREDIT_LINE_HEIGHT 10
#define SCROLL_SPEED 1 #define SCROLL_SPEED 1
#ifdef ENABLE_EMULATE_FEATURE
static const InputKey EMULATE_TOGGLE_COMBO[] = { static const InputKey EMULATE_TOGGLE_COMBO[] = {
InputKeyUp, InputKeyUp,
InputKeyUp, InputKeyUp,
@@ -23,6 +24,7 @@ static const InputKey EMULATE_TOGGLE_COMBO[] = {
InputKeyRight, InputKeyRight,
}; };
#define EMULATE_TOGGLE_COMBO_LEN (sizeof(EMULATE_TOGGLE_COMBO) / sizeof(EMULATE_TOGGLE_COMBO[0])) #define EMULATE_TOGGLE_COMBO_LEN (sizeof(EMULATE_TOGGLE_COMBO) / sizeof(EMULATE_TOGGLE_COMBO[0]))
#endif
static const char* credits[] = { static const char* credits[] = {
"", "",
@@ -62,7 +64,9 @@ typedef struct {
uint8_t frame; uint8_t frame;
uint8_t seed; uint8_t seed;
int16_t scroll_offset; int16_t scroll_offset;
#ifdef ENABLE_EMULATE_FEATURE
uint8_t combo_progress; uint8_t combo_progress;
#endif
} GlitchState; } GlitchState;
static GlitchState g_state = {0}; static GlitchState g_state = {0};
@@ -159,6 +163,7 @@ static void about_draw_callback(Canvas* canvas, void* context) {
static bool about_input_callback(InputEvent* event, void* context) { static bool about_input_callback(InputEvent* event, void* context) {
furi_check(context); furi_check(context);
#ifdef ENABLE_EMULATE_FEATURE
ProtoPirateApp* app = context; ProtoPirateApp* app = context;
if(event->type != InputTypePress) { if(event->type != InputTypePress) {
@@ -184,8 +189,13 @@ static bool about_input_callback(InputEvent* event, void* context) {
} }
return true; return true;
#else
UNUSED(event);
return false;
#endif
} }
#ifdef ENABLE_EMULATE_FEATURE
static void about_show_emulate_toggle_popup(ProtoPirateApp* app) { static void about_show_emulate_toggle_popup(ProtoPirateApp* app) {
const bool now_enabled = app->emulate_feature_enabled; const bool now_enabled = app->emulate_feature_enabled;
@@ -204,6 +214,7 @@ static void about_show_emulate_toggle_popup(ProtoPirateApp* app) {
dialog_message_show(app->dialogs, message); dialog_message_show(app->dialogs, message);
dialog_message_free(message); dialog_message_free(message);
} }
#endif
void protopirate_scene_about_on_enter(void* context) { void protopirate_scene_about_on_enter(void* context) {
furi_check(context); furi_check(context);
@@ -218,7 +229,9 @@ void protopirate_scene_about_on_enter(void* context) {
g_state.frame = 0; g_state.frame = 0;
g_state.seed = furi_get_tick() & 0xFF; g_state.seed = furi_get_tick() & 0xFF;
g_state.scroll_offset = 0; g_state.scroll_offset = 0;
#ifdef ENABLE_EMULATE_FEATURE
g_state.combo_progress = 0; g_state.combo_progress = 0;
#endif
view_set_draw_callback(app->view_about, about_draw_callback); view_set_draw_callback(app->view_about, about_draw_callback);
view_set_input_callback(app->view_about, about_input_callback); view_set_input_callback(app->view_about, about_input_callback);
@@ -245,7 +258,9 @@ bool protopirate_scene_about_on_event(void* context, SceneManagerEvent event) {
view_commit_model(app->view_about, true); view_commit_model(app->view_about, true);
consumed = true; consumed = true;
} else if(event.type == SceneManagerEventTypeCustom) { }
#ifdef ENABLE_EMULATE_FEATURE
else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == ProtoPirateCustomEventAboutToggleEmulate) { if(event.event == ProtoPirateCustomEventAboutToggleEmulate) {
app->emulate_feature_enabled = !app->emulate_feature_enabled; app->emulate_feature_enabled = !app->emulate_feature_enabled;
@@ -262,6 +277,7 @@ bool protopirate_scene_about_on_event(void* context, SceneManagerEvent event) {
consumed = true; consumed = true;
} }
} }
#endif
return consumed; return consumed;
} }
@@ -1205,7 +1205,9 @@ bool protopirate_scene_sub_decode_on_event(void* context, SceneManagerEvent even
app); app);
ctx->signal_info_left_is_emulate = false; ctx->signal_info_left_is_emulate = false;
#ifdef ENABLE_EMULATE_FEATURE
bool left_button_used = false; bool left_button_used = false;
#endif
app->emulate_disabled_for_loaded = true; app->emulate_disabled_for_loaded = true;
// Store reference to history item's flipper format for saving // Store reference to history item's flipper format for saving
@@ -1239,7 +1241,9 @@ bool protopirate_scene_sub_decode_on_event(void* context, SceneManagerEvent even
"Brute force", "Brute force",
protopirate_scene_sub_decode_widget_callback, protopirate_scene_sub_decode_widget_callback,
app); app);
#ifdef ENABLE_EMULATE_FEATURE
left_button_used = true; left_button_used = true;
#endif
} }
} }
} }
+4
View File
@@ -24,6 +24,10 @@ uint8_t subghz_custom_btn_get(void) {
return custom_btn_id; 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) { void subghz_custom_btn_set_original(uint8_t btn_code) {
custom_btn_original = btn_code; custom_btn_original = btn_code;
} }
+2
View File
@@ -13,6 +13,8 @@ void subghz_custom_btn_set_original(uint8_t btn_code);
void subghz_custom_btn_set_max(uint8_t b); void subghz_custom_btn_set_max(uint8_t b);
uint8_t subghz_custom_btn_get_max(void);
void subghz_custom_btn_set_prog_mode(ProgMode prog_mode); void subghz_custom_btn_set_prog_mode(ProgMode prog_mode);
ProgMode subghz_custom_btn_get_prog_mode(void); ProgMode subghz_custom_btn_get_prog_mode(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]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
return ret; return ret;
+10 -2
View File
@@ -364,7 +364,7 @@ LevelDuration subghz_protocol_encoder_ford_v0_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
@@ -438,12 +438,18 @@ SubGhzProtocolStatus subghz_protocol_encoder_ford_v0_deserialize(void* context,
} }
subghz_custom_btn_set_max(3); subghz_custom_btn_set_max(3);
instance->count = (instance->count + furi_hal_subghz_get_rolling_counter_mult()) & 0xFFFFF; uint32_t override_cnt = 0;
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->count = override_cnt & 0xFFFFF;
} else {
instance->count = (instance->count + furi_hal_subghz_get_rolling_counter_mult()) & 0xFFFFF;
}
uint8_t btn = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ? uint8_t btn = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ?
subghz_custom_btn_get_original() : subghz_custom_btn_get_original() :
subghz_custom_btn_get(); subghz_custom_btn_get();
instance->button = ford_v0_get_button_code(btn); instance->button = ford_v0_get_button_code(btn);
subghz_block_generic_global_button_override_get(&instance->button);
uint8_t new_chk = 0; uint8_t new_chk = 0;
encode_ford_v0(instance->generic.data, instance->serial, instance->button, instance->count, &new_chk, &instance->key1); encode_ford_v0(instance->generic.data, instance->serial, instance->button, instance->count, &new_chk, &instance->key1);
@@ -459,6 +465,8 @@ SubGhzProtocolStatus subghz_protocol_encoder_ford_v0_deserialize(void* context,
flipper_format_update_hex(flipper_format, "Key", key_data, 8); flipper_format_update_hex(flipper_format, "Key", key_data, 8);
flipper_format_update_uint32(flipper_format, "CheckSum", (uint32_t[]){new_chk}, 1); flipper_format_update_uint32(flipper_format, "CheckSum", (uint32_t[]){new_chk}, 1);
flipper_format_update_uint32(flipper_format, "CRC", (uint32_t[]){new_crc}, 1); flipper_format_update_uint32(flipper_format, "CRC", (uint32_t[]){new_crc}, 1);
flipper_format_insert_or_update_uint32(flipper_format, "Btn", (uint32_t[]){instance->button}, 1);
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->count, 1);
instance->encoder.is_running = true; instance->encoder.is_running = true;
return SubGhzProtocolStatusOk; return SubGhzProtocolStatusOk;
+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", "Encoder yield: finished one full %lu-word frame (all %u bursts); repeats_left=%u",
(unsigned long)instance->encoder.size_upload, (unsigned long)instance->encoder.size_upload,
(unsigned)FORD_V1_ENC_BURST_COUNT, (unsigned)FORD_V1_ENC_BURST_COUNT,
(unsigned)instance->encoder.repeat - 1U); subghz_block_generic_global.endless_tx ? (unsigned)instance->encoder.repeat :
instance->encoder.repeat--; (unsigned)instance->encoder.repeat - 1U);
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
instance->encoder.front = 0; instance->encoder.front = 0;
} else if(instance->encoder.front <= 4U) { } else if(instance->encoder.front <= 4U) {
uint32_t raw_word; uint32_t raw_word;
+72 -22
View File
@@ -399,6 +399,62 @@ static void ford_v2_encoder_refresh_data_from_raw(SubGhzProtocolEncoderFordV2* i
} }
} }
static void ford_v2_encoder_set_button_code(
SubGhzProtocolEncoderFordV2* instance,
uint8_t new_code) {
if(!ford_v2_button_is_valid(new_code)) return;
instance->raw_bytes[6] = new_code;
instance->raw_bytes[7] =
(instance->raw_bytes[7] & 0x7FU) | (uint8_t)(ford_v2_uint8_parity(new_code) << 7);
ford_v2_encoder_refresh_data_from_raw(instance);
instance->generic.btn = new_code;
}
static void ford_v2_encoder_set_counter(
SubGhzProtocolEncoderFordV2* instance,
uint16_t cnt) {
cnt &= 0x7FFFU;
instance->raw_bytes[7] =
(instance->raw_bytes[7] & 0x80U) | (uint8_t)((cnt >> 9) & 0x7FU);
instance->raw_bytes[8] = (uint8_t)((cnt >> 1) & 0xFFU);
instance->raw_bytes[9] =
(instance->raw_bytes[9] & 0x7FU) | (uint8_t)((cnt & 1U) << 7);
ford_v2_encoder_refresh_data_from_raw(instance);
instance->generic.cnt = cnt;
}
static void ford_v2_encoder_update_flipper_format(
SubGhzProtocolEncoderFordV2* instance,
FlipperFormat* flipper_format) {
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_hex(
flipper_format, "Key", instance->raw_bytes, FORD_V2_KEY_BYTE_COUNT);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(
flipper_format, "Serial", &instance->generic.serial, 1);
uint32_t btn = instance->generic.btn;
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &btn, 1);
uint32_t cnt = instance->generic.cnt;
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &cnt, 1);
uint32_t tail31 = (((uint32_t)(instance->raw_bytes[9] & 0x7FU)) << 24) |
((uint32_t)instance->raw_bytes[10] << 16) |
((uint32_t)instance->raw_bytes[11] << 8) |
(uint32_t)instance->raw_bytes[12];
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Tail31", &tail31, 1);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_hex(
flipper_format, "TailRaw", &instance->raw_bytes[8], FORD_V2_TAIL_RAW_BYTE_COUNT);
}
static inline void ford_v2_encoder_emit_manchester_bit( static inline void ford_v2_encoder_emit_manchester_bit(
SubGhzProtocolEncoderFordV2* instance, SubGhzProtocolEncoderFordV2* instance,
bool bit) { bool bit) {
@@ -490,17 +546,14 @@ static SubGhzProtocolStatus ford_v2_encoder_deserialize_validate_and_pack(
(((uint16_t)instance->raw_bytes[8]) << 1) | (((uint16_t)instance->raw_bytes[8]) << 1) |
((uint16_t)(instance->raw_bytes[9] >> 7))); ((uint16_t)(instance->raw_bytes[9] >> 7)));
cnt = (cnt + 1U) & 0x7FFFU; uint32_t override_cnt = 0;
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
cnt = override_cnt & 0x7FFFU;
} else {
cnt = (cnt + furi_hal_subghz_get_rolling_counter_mult()) & 0x7FFFU;
}
// raw_bytes[7] bits [6:0] = cnt[14:8], bit[7] = parity(btn) ford_v2_encoder_set_counter(instance, cnt);
instance->raw_bytes[7] = (instance->raw_bytes[7] & 0x80U) |
(uint8_t)((cnt >> 9) & 0x7FU);
instance->raw_bytes[8] = (uint8_t)((cnt >> 1) & 0xFFU);
// raw_bytes[9] bit[7] = cnt[0], bits[6:0] = tail
instance->raw_bytes[9] = (instance->raw_bytes[9] & 0x7FU) |
(uint8_t)((cnt & 1U) << 7);
ford_v2_encoder_refresh_data_from_raw(instance);
instance->generic.btn = instance->raw_bytes[6]; instance->generic.btn = instance->raw_bytes[6];
instance->generic.serial = instance->generic.serial =
@@ -566,17 +619,13 @@ SubGhzProtocolStatus
if(ret == SubGhzProtocolStatusOk) { if(ret == SubGhzProtocolStatusOk) {
ford_v2_custom_btn_init(instance->raw_bytes[6]); ford_v2_custom_btn_init(instance->raw_bytes[6]);
uint8_t btn_sel = subghz_custom_btn_get(); uint8_t new_code = 0;
if(btn_sel != SUBGHZ_CUSTOM_BTN_OK) { if(subghz_block_generic_global_button_override_get(&new_code)) {
uint8_t new_code = ford_v2_custom_btn_to_code(btn_sel); ford_v2_encoder_set_button_code(instance, new_code);
if(ford_v2_button_is_valid(new_code)) { } else {
instance->raw_bytes[6] = new_code; uint8_t btn_sel = subghz_custom_btn_get();
const uint8_t k7_msb = if(btn_sel != SUBGHZ_CUSTOM_BTN_OK) {
(uint8_t)(ford_v2_uint8_parity(new_code) << 7); ford_v2_encoder_set_button_code(instance, ford_v2_custom_btn_to_code(btn_sel));
instance->raw_bytes[7] =
(instance->raw_bytes[7] & 0x7FU) | k7_msb;
ford_v2_encoder_refresh_data_from_raw(instance);
instance->generic.btn = new_code;
} }
} }
@@ -586,6 +635,7 @@ SubGhzProtocolStatus
(instance->extra_data << 8) | (uint64_t)instance->raw_bytes[8U + i]; (instance->extra_data << 8) | (uint64_t)instance->raw_bytes[8U + i];
} }
ford_v2_encoder_update_flipper_format(instance, flipper_format);
ford_v2_encoder_deserialize_apply_repeat(instance, flipper_format); ford_v2_encoder_deserialize_apply_repeat(instance, flipper_format);
ford_v2_encoder_build_upload(instance); ford_v2_encoder_build_upload(instance);
instance->encoder.is_running = true; instance->encoder.is_running = true;
@@ -615,7 +665,7 @@ LevelDuration subghz_protocol_encoder_ford_v2_yield(void* context) {
if(++instance->encoder.front == instance->encoder.size_upload) { if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.front = 0U; instance->encoder.front = 0U;
instance->encoder.repeat--; if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
} }
return ret; return ret;
+13 -4
View File
@@ -154,7 +154,7 @@ LevelDuration subghz_protocol_encoder_kia_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
@@ -193,8 +193,10 @@ static bool subghz_protocol_encoder_kia_get_upload(SubGhzProtocolEncoderKIA* ins
instance->encoder.size_upload = size_upload; instance->encoder.size_upload = size_upload;
} }
// Counter increment logic uint32_t override_cnt = 0;
if(instance->generic.cnt < 0xFFFF) { if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->generic.cnt = override_cnt & 0xFFFF;
} else if(instance->generic.cnt < 0xFFFF) {
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
instance->generic.cnt = 0; instance->generic.cnt = 0;
} else { } else {
@@ -209,6 +211,7 @@ static bool subghz_protocol_encoder_kia_get_upload(SubGhzProtocolEncoderKIA* ins
uint8_t btn = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ? uint8_t btn = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ?
subghz_custom_btn_get_original() : subghz_custom_btn_get_original() :
subghz_custom_btn_get(); subghz_custom_btn_get();
subghz_block_generic_global_button_override_get(&btn);
// Update the generic button value for potential button changes // Update the generic button value for potential button changes
instance->generic.btn = btn; instance->generic.btn = btn;
@@ -325,6 +328,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_deserialize(void* context, Flip
break; break;
} }
uint32_t temp_btn = instance->generic.btn;
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp_btn, 1);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
instance->encoder.is_running = true; instance->encoder.is_running = true;
} while(false); } while(false);
@@ -594,4 +604,3 @@ void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output) {
received_crc, received_crc,
crc_valid ? "(OK)" : "(FAIL)"); crc_valid ? "(OK)" : "(FAIL)");
} }
+15 -4
View File
@@ -174,7 +174,7 @@ LevelDuration subghz_protocol_encoder_kia_v1_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
@@ -289,8 +289,10 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v1_deserialize(void* context, F
} }
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
// Incrementa counter uint32_t override_cnt = 0;
if(instance->generic.cnt < 0xFFF) { if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->generic.cnt = override_cnt & 0xFFF;
} else if(instance->generic.cnt < 0xFFF) {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
if(instance->generic.cnt > 0xFFF) { if(instance->generic.cnt > 0xFFF) {
instance->generic.cnt = 0; instance->generic.cnt = 0;
@@ -301,7 +303,9 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v1_deserialize(void* context, F
// Gestione bottoni custom // Gestione bottoni custom
uint8_t btn = subghz_custom_btn_get(); uint8_t btn = subghz_custom_btn_get();
if(btn != SUBGHZ_CUSTOM_BTN_OK) { if(subghz_block_generic_global_button_override_get(&btn)) {
instance->generic.btn = btn;
} else if(btn != SUBGHZ_CUSTOM_BTN_OK) {
instance->generic.btn = btn; instance->generic.btn = btn;
} }
@@ -324,6 +328,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v1_deserialize(void* context, F
ret = SubGhzProtocolStatusErrorParserKey; ret = SubGhzProtocolStatusErrorParserKey;
break; break;
} }
uint32_t temp_btn = instance->generic.btn;
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp_btn, 1);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
instance->encoder.is_running = true; instance->encoder.is_running = true;
ret = SubGhzProtocolStatusOk; ret = SubGhzProtocolStatusOk;
+15 -3
View File
@@ -183,7 +183,7 @@ LevelDuration subghz_protocol_encoder_kia_v2_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
@@ -214,7 +214,10 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v2_deserialize(void* context, F
} }
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
if(instance->generic.cnt < 0xFFF) { uint32_t override_cnt = 0;
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->generic.cnt = override_cnt & 0xFFF;
} else if(instance->generic.cnt < 0xFFF) {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
if(instance->generic.cnt > 0xFFF) { if(instance->generic.cnt > 0xFFF) {
instance->generic.cnt = 0; instance->generic.cnt = 0;
@@ -224,7 +227,9 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v2_deserialize(void* context, F
} }
uint8_t btn = subghz_custom_btn_get(); uint8_t btn = subghz_custom_btn_get();
if(btn != SUBGHZ_CUSTOM_BTN_OK) { if(subghz_block_generic_global_button_override_get(&btn)) {
instance->generic.btn = btn;
} else if(btn != SUBGHZ_CUSTOM_BTN_OK) {
instance->generic.btn = btn; instance->generic.btn = btn;
} }
@@ -257,6 +262,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v2_deserialize(void* context, F
ret = SubGhzProtocolStatusErrorParserKey; ret = SubGhzProtocolStatusErrorParserKey;
break; break;
} }
uint32_t temp_btn = instance->generic.btn;
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp_btn, 1);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
instance->encoder.is_running = true; instance->encoder.is_running = true;
ret = SubGhzProtocolStatusOk; ret = SubGhzProtocolStatusOk;
+34 -7
View File
@@ -124,6 +124,8 @@ static uint8_t kia_v3_v4_calculate_crc(uint8_t* bytes) {
return crc & 0x0F; return crc & 0x0F;
} }
static uint8_t kia_v3_v4_btn_to_custom(uint8_t btn);
static bool kia_v3_v4_process_buffer(SubGhzProtocolDecoderKiaV3V4* instance) { static bool kia_v3_v4_process_buffer(SubGhzProtocolDecoderKiaV3V4* instance) {
if(instance->raw_bit_count < 68) { if(instance->raw_bit_count < 68) {
return false; return false;
@@ -175,7 +177,7 @@ static bool kia_v3_v4_process_buffer(SubGhzProtocolDecoderKiaV3V4* instance) {
instance->decoder.decode_count_bit = 68; instance->decoder.decode_count_bit = 68;
if(subghz_custom_btn_get_original() == 0) { if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(instance->generic.btn); subghz_custom_btn_set_original(kia_v3_v4_btn_to_custom(instance->generic.btn));
} }
subghz_custom_btn_set_max(5); subghz_custom_btn_set_max(5);
@@ -222,6 +224,17 @@ static const char* subghz_protocol_kia_v3_v4_get_name_button(uint8_t btn) {
} }
} }
static uint8_t kia_v3_v4_btn_to_custom(uint8_t btn) {
switch(btn) {
case 0x1: return 1;
case 0x2: return 2;
case 0x3: return 3;
case 0x4: return 4;
case 0x8: return 5;
default: return 1;
}
}
void* subghz_protocol_encoder_kia_v3_v4_alloc(SubGhzEnvironment* environment) { void* subghz_protocol_encoder_kia_v3_v4_alloc(SubGhzEnvironment* environment) {
UNUSED(environment); UNUSED(environment);
SubGhzProtocolEncoderKiaV3V4* instance = malloc(sizeof(SubGhzProtocolEncoderKiaV3V4)); SubGhzProtocolEncoderKiaV3V4* instance = malloc(sizeof(SubGhzProtocolEncoderKiaV3V4));
@@ -520,7 +533,7 @@ SubGhzProtocolStatus
} }
if(subghz_custom_btn_get_original() == 0) { if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(instance->btn); subghz_custom_btn_set_original(kia_v3_v4_btn_to_custom(instance->btn));
} }
subghz_custom_btn_set_max(5); subghz_custom_btn_set_max(5);
@@ -531,14 +544,21 @@ SubGhzProtocolStatus
selected_btn = subghz_custom_btn_get(); selected_btn = subghz_custom_btn_get();
} }
if(selected_btn == 5) { if(subghz_block_generic_global_button_override_get(&selected_btn)) {
instance->btn = selected_btn;
} else if(selected_btn == 5) {
instance->btn = 0x8; instance->btn = 0x8;
} else if(selected_btn >= 1 && selected_btn <= 4) { } else if(selected_btn >= 1 && selected_btn <= 4) {
instance->btn = selected_btn; instance->btn = selected_btn;
} }
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult(); uint32_t override_cnt = 0;
instance->cnt = (instance->cnt + mult) & 0xFFFF; if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->cnt = override_cnt & 0xFFFF;
} else {
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
instance->cnt = (instance->cnt + mult) & 0xFFFF;
}
instance->generic.btn = instance->btn; instance->generic.btn = instance->btn;
instance->generic.cnt = instance->cnt; instance->generic.cnt = instance->cnt;
@@ -583,6 +603,13 @@ SubGhzProtocolStatus
uint32_t crc_to_write = instance->crc; uint32_t crc_to_write = instance->crc;
flipper_format_update_uint32(flipper_format, "CRC", &crc_to_write, 1); flipper_format_update_uint32(flipper_format, "CRC", &crc_to_write, 1);
uint32_t btn_to_write = instance->generic.btn;
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &btn_to_write, 1);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
instance->encoder.is_running = true; instance->encoder.is_running = true;
instance->encoder.front = 0; instance->encoder.front = 0;
@@ -648,7 +675,7 @@ LevelDuration subghz_protocol_encoder_kia_v3_v4_yield(void* context) {
instance->crc_iter = (uint8_t)((instance->crc_iter + 1U) & 0x0FU); instance->crc_iter = (uint8_t)((instance->crc_iter + 1U) & 0x0FU);
subghz_protocol_encoder_kia_v3_v4_patch_crc(instance); subghz_protocol_encoder_kia_v3_v4_patch_crc(instance);
instance->encoder.front = 0; instance->encoder.front = 0;
instance->encoder.repeat--; if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
if(instance->bursts_sent < 16U) { if(instance->bursts_sent < 16U) {
instance->bursts_sent++; instance->bursts_sent++;
} }
@@ -931,7 +958,7 @@ SubGhzProtocolStatus
} }
if(subghz_custom_btn_get_original() == 0) { if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(instance->generic.btn); subghz_custom_btn_set_original(kia_v3_v4_btn_to_custom(instance->generic.btn));
} }
subghz_custom_btn_set_max(5); subghz_custom_btn_set_max(5);
+47 -13
View File
@@ -166,6 +166,26 @@ static uint64_t kia_v5_encode_data(uint32_t serial, uint16_t counter, uint8_t bu
return bit_reverse_64(yek); return bit_reverse_64(yek);
} }
static uint8_t kia_v5_custom_to_btn(uint8_t custom_btn) {
switch(custom_btn) {
case 1: return 0x01; // Unlock
case 2: return 0x02; // Lock
case 3: return 0x04; // Trunk
case 4: return 0x08; // Horn
default: return 0x01;
}
}
static uint8_t kia_v5_btn_to_custom(uint8_t btn) {
switch(btn) {
case 0x01: return 1; // Unlock
case 0x02: return 2; // Lock
case 0x04: return 3; // Trunk
case 0x08: return 4; // Horn
default: return 1;
}
}
struct SubGhzProtocolDecoderKiaV5 { struct SubGhzProtocolDecoderKiaV5 {
SubGhzProtocolDecoderBase base; SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder; SubGhzBlockDecoder decoder;
@@ -327,15 +347,29 @@ SubGhzProtocolStatus
uint32_t encrypted = (uint32_t)(yek & 0xFFFFFFFF); uint32_t encrypted = (uint32_t)(yek & 0xFFFFFFFF);
instance->generic.cnt = mixer_decode(encrypted); 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) { if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(instance->generic.btn); subghz_custom_btn_set_original(kia_v5_btn_to_custom(instance->generic.btn));
} }
subghz_custom_btn_set_max(4); 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 = kia_v5_custom_to_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.data = kia_v5_encode_data(
instance->generic.serial, instance->generic.cnt, instance->generic.btn); instance->generic.serial, instance->generic.cnt, instance->generic.btn);
instance->replay_data = instance->generic.data; instance->replay_data = instance->generic.data;
@@ -361,12 +395,12 @@ SubGhzProtocolStatus
} }
uint32_t temp_btn = instance->generic.btn; uint32_t temp_btn = instance->generic.btn;
if(!flipper_format_update_uint32(flipper_format, "Btn", &temp_btn, 1)) { if(!flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp_btn, 1)) {
ret = SubGhzProtocolStatusErrorParserOthers; ret = SubGhzProtocolStatusErrorParserOthers;
break; break;
} }
if(!flipper_format_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1)) { if(!flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1)) {
ret = SubGhzProtocolStatusErrorParserOthers; ret = SubGhzProtocolStatusErrorParserOthers;
break; break;
} }
@@ -374,18 +408,18 @@ SubGhzProtocolStatus
yek = bit_reverse_64(instance->generic.data); yek = bit_reverse_64(instance->generic.data);
yek_high = (uint32_t)(yek >> 32); yek_high = (uint32_t)(yek >> 32);
yek_low = (uint32_t)(yek & 0xFFFFFFFF); yek_low = (uint32_t)(yek & 0xFFFFFFFF);
if(!flipper_format_update_uint32(flipper_format, "YekHi", &yek_high, 1)) { if(!flipper_format_insert_or_update_uint32(flipper_format, "YekHi", &yek_high, 1)) {
ret = SubGhzProtocolStatusErrorParserOthers; ret = SubGhzProtocolStatusErrorParserOthers;
break; break;
} }
if(!flipper_format_update_uint32(flipper_format, "YekLo", &yek_low, 1)) { if(!flipper_format_insert_or_update_uint32(flipper_format, "YekLo", &yek_low, 1)) {
ret = SubGhzProtocolStatusErrorParserOthers; ret = SubGhzProtocolStatusErrorParserOthers;
break; break;
} }
uint8_t crc = kia_v5_calculate_crc(yek); uint8_t crc = kia_v5_calculate_crc(yek);
uint32_t crc_temp = crc; uint32_t crc_temp = crc;
if(!flipper_format_update_uint32(flipper_format, "CRC", &crc_temp, 1)) { if(!flipper_format_insert_or_update_uint32(flipper_format, "CRC", &crc_temp, 1)) {
ret = SubGhzProtocolStatusErrorParserOthers; ret = SubGhzProtocolStatusErrorParserOthers;
break; break;
} }
@@ -456,7 +490,7 @@ LevelDuration subghz_protocol_encoder_kia_v5_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
@@ -596,7 +630,7 @@ void subghz_protocol_decoder_kia_v5_feed(void* context, bool level, uint32_t dur
instance->decoder.decode_count_bit = instance->generic.data_count_bit; instance->decoder.decode_count_bit = instance->generic.data_count_bit;
if(subghz_custom_btn_get_original() == 0) { if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(instance->generic.btn); subghz_custom_btn_set_original(kia_v5_btn_to_custom(instance->generic.btn));
} }
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
@@ -730,7 +764,7 @@ SubGhzProtocolStatus
instance->generic.cnt = mixer_decode(encrypted); instance->generic.cnt = mixer_decode(encrypted);
if(subghz_custom_btn_get_original() == 0) { if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(instance->generic.btn); subghz_custom_btn_set_original(kia_v5_btn_to_custom(instance->generic.btn));
} }
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
} }
+21 -8
View File
@@ -717,8 +717,10 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v6_deserialize(void* context, F
} }
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
// Incrementa cnt uint32_t override_cnt = 0;
if(instance->generic.cnt < 0xFFFFFFFF) { if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->generic.cnt = override_cnt;
} else if(instance->generic.cnt < 0xFFFFFFFF) {
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFFFFFF) { if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFFFFFF) {
instance->generic.cnt = 0; instance->generic.cnt = 0;
} else { } else {
@@ -728,11 +730,15 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v6_deserialize(void* context, F
instance->generic.cnt = 0; instance->generic.cnt = 0;
} }
// Risolvi btn dal tasto premuto (identico a Suzuki) uint8_t override_btn = 0;
instance->generic.btn = kia_v6_custom_to_btn( if(subghz_block_generic_global_button_override_get(&override_btn)) {
subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK instance->generic.btn = override_btn & 0x0F;
? subghz_custom_btn_get_original() } else {
: subghz_custom_btn_get()); instance->generic.btn = kia_v6_custom_to_btn(
subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK
? subghz_custom_btn_get_original()
: subghz_custom_btn_get());
}
kia_v6_encoder_build_upload(instance); kia_v6_encoder_build_upload(instance);
@@ -766,6 +772,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v6_deserialize(void* context, F
uint32_t file_key3 = file_part3; uint32_t file_key3 = file_part3;
flipper_format_update_uint32(flipper_format, "Key_4", &file_key3, 1); flipper_format_update_uint32(flipper_format, "Key_4", &file_key3, 1);
uint32_t btn_to_write = instance->generic.btn;
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &btn_to_write, 1);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
instance->encoder.is_running = true; instance->encoder.is_running = true;
ret = SubGhzProtocolStatusOk; ret = SubGhzProtocolStatusOk;
} while(false); } while(false);
@@ -791,7 +804,7 @@ LevelDuration subghz_protocol_encoder_kia_v6_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
instance->encoder.front++; instance->encoder.front++;
if(instance->encoder.front >= instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
return ret; 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]; LevelDuration duration = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; 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) { if(instance->encoder.front >= instance->encoder.size_upload) {
/* One full repetition done; count it down */ /* 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.repeat--;
} }
instance->encoder.front = 0; 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]; LevelDuration out = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; 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(); if(!instance->encoder.is_running || instance->encoder.repeat == 0) return level_duration_reset();
LevelDuration ret = instance->encoder.upload[instance->encoder.front]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
return ret; return ret;
+49 -12
View File
@@ -422,12 +422,14 @@ SubGhzProtocolStatus subghz_protocol_decoder_porsche_cayenne_deserialize(
return ret; return ret;
} }
static uint8_t porsche_cayenne_btn_to_custom(uint8_t btn);
void subghz_protocol_decoder_porsche_cayenne_get_string(void* context, FuriString* output) { void subghz_protocol_decoder_porsche_cayenne_get_string(void* context, FuriString* output) {
furi_check(context); furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context; SubGhzProtocolDecoderPorscheCayenne* instance = context;
if(subghz_custom_btn_get_original() == 0) { if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(instance->generic.btn); subghz_custom_btn_set_original(porsche_cayenne_btn_to_custom(instance->generic.btn));
} }
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
@@ -456,16 +458,37 @@ void subghz_protocol_decoder_porsche_cayenne_get_string(void* context, FuriStrin
// ENCODER // ENCODER
// ============================================================================= // =============================================================================
static uint8_t porsche_cayenne_custom_to_btn(uint8_t custom_btn, uint8_t fallback_btn) {
switch(custom_btn) {
case SUBGHZ_CUSTOM_BTN_UP: return 0x01; // Lock
case SUBGHZ_CUSTOM_BTN_DOWN: return 0x02; // Unlock
case SUBGHZ_CUSTOM_BTN_LEFT: return 0x04; // Trunk
case SUBGHZ_CUSTOM_BTN_RIGHT: return 0x08; // Open
default: return fallback_btn;
}
}
static uint8_t porsche_cayenne_btn_to_custom(uint8_t btn) {
switch(btn) {
case 0x01: return SUBGHZ_CUSTOM_BTN_UP;
case 0x02: return SUBGHZ_CUSTOM_BTN_DOWN;
case 0x04: return SUBGHZ_CUSTOM_BTN_LEFT;
case 0x08: return SUBGHZ_CUSTOM_BTN_RIGHT;
default: return SUBGHZ_CUSTOM_BTN_OK;
}
}
static uint8_t porsche_cayenne_get_btn_code(void) { static uint8_t porsche_cayenne_get_btn_code(void) {
uint8_t override_btn = 0;
if(subghz_block_generic_global_button_override_get(&override_btn)) {
return override_btn;
}
uint8_t custom_btn = subghz_custom_btn_get(); uint8_t custom_btn = subghz_custom_btn_get();
uint8_t original_btn = subghz_custom_btn_get_original(); uint8_t original_btn = porsche_cayenne_custom_to_btn(
subghz_custom_btn_get_original(), 0x01);
if(custom_btn == SUBGHZ_CUSTOM_BTN_OK) return original_btn; if(custom_btn == SUBGHZ_CUSTOM_BTN_OK) return original_btn;
// Map d-pad buttons to common VAG key codes return porsche_cayenne_custom_to_btn(custom_btn, original_btn);
if(custom_btn == SUBGHZ_CUSTOM_BTN_UP) return 0x01; // Lock
if(custom_btn == SUBGHZ_CUSTOM_BTN_DOWN) return 0x02; // Unlock
if(custom_btn == SUBGHZ_CUSTOM_BTN_LEFT) return 0x04; // Trunk
if(custom_btn == SUBGHZ_CUSTOM_BTN_RIGHT) return 0x08; // Open
return original_btn;
} }
static void porsche_cayenne_build_upload(SubGhzProtocolEncoderPorscheCayenne* instance) { static void porsche_cayenne_build_upload(SubGhzProtocolEncoderPorscheCayenne* instance) {
@@ -483,6 +506,7 @@ static void porsche_cayenne_build_upload(SubGhzProtocolEncoderPorscheCayenne* in
uint32_t serial = instance->generic.serial & 0xFFFFFF; uint32_t serial = instance->generic.serial & 0xFFFFFF;
uint8_t btn = porsche_cayenne_get_btn_code(); uint8_t btn = porsche_cayenne_get_btn_code();
uint16_t cnt = (uint16_t)instance->generic.cnt; uint16_t cnt = (uint16_t)instance->generic.cnt;
instance->generic.btn = btn;
size_t idx = 0; size_t idx = 0;
LevelDuration* up = instance->encoder.upload; LevelDuration* up = instance->encoder.upload;
@@ -522,8 +546,9 @@ static void porsche_cayenne_build_upload(SubGhzProtocolEncoderPorscheCayenne* in
instance->encoder.size_upload = idx; instance->encoder.size_upload = idx;
instance->encoder.front = 0; instance->encoder.front = 0;
// Advance stored counter by 4 (one per frame) if(furi_hal_subghz_get_rolling_counter_mult() != 0) {
instance->generic.cnt = (uint16_t)(cnt + 4); instance->generic.cnt = (uint16_t)(cnt + 4);
}
} }
void* subghz_protocol_encoder_porsche_cayenne_alloc(SubGhzEnvironment* environment) { void* subghz_protocol_encoder_porsche_cayenne_alloc(SubGhzEnvironment* environment) {
@@ -582,9 +607,14 @@ SubGhzProtocolStatus subghz_protocol_encoder_porsche_cayenne_deserialize(
break; break;
} }
uint32_t override_cnt = 0;
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->generic.cnt = override_cnt & 0xFFFF;
}
// Apply custom button if set // Apply custom button if set
if(subghz_custom_btn_get_original() == 0) { if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(instance->generic.btn); subghz_custom_btn_set_original(porsche_cayenne_btn_to_custom(instance->generic.btn));
} }
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
@@ -596,6 +626,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_porsche_cayenne_deserialize(
uint32_t new_cnt = instance->generic.cnt; uint32_t new_cnt = instance->generic.cnt;
flipper_format_insert_or_update_uint32(flipper_format, "Counter", &new_cnt, 1); flipper_format_insert_or_update_uint32(flipper_format, "Counter", &new_cnt, 1);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &new_cnt, 1);
flipper_format_rewind(flipper_format);
uint32_t new_btn = instance->generic.btn;
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &new_btn, 1);
instance->encoder.is_running = true; instance->encoder.is_running = true;
ret = SubGhzProtocolStatusOk; ret = SubGhzProtocolStatusOk;
} while(false); } while(false);
@@ -621,7 +658,7 @@ LevelDuration subghz_protocol_encoder_porsche_cayenne_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
+62 -19
View File
@@ -70,17 +70,38 @@ static const char* psa_button_name(uint8_t btn) {
} }
} }
static uint8_t psa_btn_to_custom(uint8_t btn) {
switch(btn) {
case 0x0:
return SUBGHZ_CUSTOM_BTN_UP;
case 0x1:
return SUBGHZ_CUSTOM_BTN_DOWN;
case 0x2:
return SUBGHZ_CUSTOM_BTN_LEFT;
default:
return SUBGHZ_CUSTOM_BTN_OK;
}
}
static uint8_t psa_custom_to_btn(uint8_t custom_btn, uint8_t fallback_btn) {
switch(custom_btn) {
case SUBGHZ_CUSTOM_BTN_UP:
return 0x0; // Lock
case SUBGHZ_CUSTOM_BTN_DOWN:
return 0x1; // Unlock
case SUBGHZ_CUSTOM_BTN_LEFT:
case SUBGHZ_CUSTOM_BTN_RIGHT:
return 0x2; // Trunk
default:
return fallback_btn;
}
}
static uint8_t psa_get_btn_code(void) { static uint8_t psa_get_btn_code(void) {
uint8_t custom_btn = subghz_custom_btn_get(); uint8_t custom_btn = subghz_custom_btn_get();
uint8_t original_raw = subghz_custom_btn_get_original(); uint8_t original_btn = psa_custom_to_btn(subghz_custom_btn_get_original(), 0x0);
// 0xFF is sentinel for PSA btn 0x0 (Lock) if(custom_btn == SUBGHZ_CUSTOM_BTN_OK) return original_btn;
uint8_t original_btn = (original_raw == 0xFF) ? 0x0 : original_raw; return psa_custom_to_btn(custom_btn, original_btn);
if(custom_btn == SUBGHZ_CUSTOM_BTN_OK) return original_btn;
if(custom_btn == SUBGHZ_CUSTOM_BTN_UP) return 0x0; // Lock
if(custom_btn == SUBGHZ_CUSTOM_BTN_DOWN) return 0x1; // Unlock
if(custom_btn == SUBGHZ_CUSTOM_BTN_LEFT) return 0x2; // Trunk
if(custom_btn == SUBGHZ_CUSTOM_BTN_RIGHT) return 0x2; // Trunk
return original_btn;
} }
typedef enum { typedef enum {
@@ -1390,7 +1411,7 @@ void subghz_protocol_decoder_psa_get_string(void* context, FuriString* output) {
if(instance->decrypted == 0x50 && instance->decrypted_type != 0) { if(instance->decrypted == 0x50 && instance->decrypted_type != 0) {
// Always update original button when loading a new file // Always update original button when loading a new file
subghz_custom_btn_set_original(instance->generic.btn == 0 ? 0xFF : instance->generic.btn); subghz_custom_btn_set_original(psa_btn_to_custom(instance->generic.btn));
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
uint8_t display_btn = psa_get_btn_code(); uint8_t display_btn = psa_get_btn_code();
if(instance->decrypted_type == 0x23) { if(instance->decrypted_type == 0x23) {
@@ -1762,7 +1783,10 @@ SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, Flip
counter = (counter << 4) | nibble; counter = (counter << 4) | nibble;
} }
} else { } else {
has_decrypted_data = false; flipper_format_rewind(flipper_format);
if(!flipper_format_read_uint32(flipper_format, "Cnt", &counter, 1)) {
has_decrypted_data = false;
}
} }
flipper_format_rewind(flipper_format); flipper_format_rewind(flipper_format);
@@ -1779,7 +1803,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, Flip
} }
button = (uint8_t)(btn_val & 0xFF); button = (uint8_t)(btn_val & 0xFF);
} else { } else {
has_decrypted_data = false; uint32_t btn_val = 0;
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, "Btn", &btn_val, 1)) {
button = (uint8_t)(btn_val & 0xFF);
} else {
has_decrypted_data = false;
}
} }
flipper_format_rewind(flipper_format); flipper_format_rewind(flipper_format);
@@ -1849,14 +1879,20 @@ SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, Flip
// Setup custom button system // Setup custom button system
// Save original button from FILE before any d-pad remapping // Save original button from FILE before any d-pad remapping
uint8_t file_btn = button; // 'button' was read directly from file above uint8_t file_btn = button; // 'button' was read directly from file above
subghz_custom_btn_set_original(file_btn == 0 ? 0xFF : file_btn); subghz_custom_btn_set_original(psa_btn_to_custom(file_btn));
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
instance->button = psa_get_btn_code(); if(!subghz_block_generic_global_button_override_get(&instance->button)) {
instance->button = psa_get_btn_code();
}
FURI_LOG_I("PSA_ENC", "file_btn=%02X custom=%02X result=%02X orig=%02X", file_btn, subghz_custom_btn_get(), instance->button, subghz_custom_btn_get_original()); FURI_LOG_I("PSA_ENC", "file_btn=%02X custom=%02X result=%02X orig=%02X", file_btn, subghz_custom_btn_get(), instance->button, subghz_custom_btn_get_original());
// Increment counter uint32_t override_cnt = 0;
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult(); if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->counter = (instance->counter + mult) & 0xFFFFFFFF; instance->counter = override_cnt;
} else {
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
instance->counter = (instance->counter + mult) & 0xFFFFFFFF;
}
psa_encoder_build_upload(instance); psa_encoder_build_upload(instance);
@@ -1879,7 +1915,12 @@ SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, Flip
(unsigned int)((instance->counter >> 8) & 0xFF), (unsigned int)((instance->counter >> 8) & 0xFF),
(unsigned int)(instance->counter & 0xFF)); (unsigned int)(instance->counter & 0xFF));
} }
flipper_format_update_string_cstr(flipper_format, "Cnt", cnt_str); flipper_format_insert_or_update_string_cstr(flipper_format, "Cnt", cnt_str);
char btn_str[8];
snprintf(btn_str, sizeof(btn_str), "%02X", (unsigned int)instance->button);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_string_cstr(flipper_format, "Btn", btn_str);
char key_str[32]; char key_str[32];
uint64_t key1 = ((uint64_t)instance->key1_high << 32) | instance->key1_low; uint64_t key1 = ((uint64_t)instance->key1_high << 32) | instance->key1_low;
@@ -1892,6 +1933,7 @@ SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, Flip
(unsigned int)((key1 >> 16) & 0xFF), (unsigned int)((key1 >> 16) & 0xFF),
(unsigned int)((key1 >> 8) & 0xFF), (unsigned int)((key1 >> 8) & 0xFF),
(unsigned int)(key1 & 0xFF)); (unsigned int)(key1 & 0xFF));
flipper_format_rewind(flipper_format);
flipper_format_update_string_cstr(flipper_format, "Key", key_str); flipper_format_update_string_cstr(flipper_format, "Key", key_str);
char key2_str[32]; char key2_str[32];
@@ -1899,6 +1941,7 @@ SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, Flip
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
(unsigned int)((instance->key2_low >> 8) & 0xFF), (unsigned int)((instance->key2_low >> 8) & 0xFF),
(unsigned int)(instance->key2_low & 0xFF)); (unsigned int)(instance->key2_low & 0xFF));
flipper_format_rewind(flipper_format);
flipper_format_update_string_cstr(flipper_format, "Key_2", key2_str); flipper_format_update_string_cstr(flipper_format, "Key_2", key2_str);
@@ -1932,7 +1975,7 @@ LevelDuration subghz_protocol_encoder_psa_yield(void* context) {
instance->encoder.front++; instance->encoder.front++;
if(instance->encoder.front >= instance->encoder.size_upload) { if(instance->encoder.front >= instance->encoder.size_upload) {
instance->encoder.front = 0; 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; if(instance->encoder.repeat <= 0) instance->is_running = false;
} }
return ret; return ret;
+20 -7
View File
@@ -307,10 +307,16 @@ static bool subghz_protocol_encoder_scher_khan_get_upload(
// For 51-bit dynamic: rebuild data with new button and incremented counter // For 51-bit dynamic: rebuild data with new button and incremented counter
if(instance->generic.data_count_bit == 51) { if(instance->generic.data_count_bit == 51) {
if((instance->generic.cnt + 1) > 0xFFFF) { uint32_t override_cnt = 0;
instance->generic.cnt = 0; if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->generic.cnt = override_cnt & 0xFFFF;
} else { } else {
instance->generic.cnt += 1; uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
if((instance->generic.cnt + mult) > 0xFFFF) {
instance->generic.cnt = 0;
} else {
instance->generic.cnt += mult;
}
} }
uint64_t upper = instance->generic.data & 0x7FFFFFFF0000ULL; uint64_t upper = instance->generic.data & 0x7FFFFFFF0000ULL;
@@ -320,10 +326,16 @@ static bool subghz_protocol_encoder_scher_khan_get_upload(
} }
if(instance->generic.data_count_bit == 57) { if(instance->generic.data_count_bit == 57) {
if((instance->generic.cnt + 1) > 0xFFFF) { uint32_t override_cnt = 0;
instance->generic.cnt = 0; if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->generic.cnt = override_cnt & 0xFFFF;
} else { } else {
instance->generic.cnt += 1; uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
if((instance->generic.cnt + mult) > 0xFFFF) {
instance->generic.cnt = 0;
} else {
instance->generic.cnt += mult;
}
} }
uint8_t kb[7]; uint8_t kb[7];
@@ -581,6 +593,7 @@ SubGhzProtocolStatus
instance->protocol_name = pname; instance->protocol_name = pname;
uint8_t selected_btn = scher_khan_get_btn_code(instance->generic.btn); uint8_t selected_btn = scher_khan_get_btn_code(instance->generic.btn);
subghz_block_generic_global_button_override_get(&selected_btn);
FURI_LOG_I(TAG, FURI_LOG_I(TAG,
"Building upload: original_btn=0x%02X, selected_btn=0x%02X, bits=%d", "Building upload: original_btn=0x%02X, selected_btn=0x%02X, bits=%d",
@@ -622,7 +635,7 @@ LevelDuration subghz_protocol_encoder_scher_khan_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
+29 -4
View File
@@ -282,10 +282,16 @@ static bool subghz_protocol_encoder_sheriff_cfm_get_upload(
uint8_t btn) { uint8_t btn) {
furi_check(instance); furi_check(instance);
if((instance->generic.cnt + 1) > 0xFFFF) { uint32_t override_cnt = 0;
instance->generic.cnt = 0; if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->generic.cnt = override_cnt & 0xFFFF;
} else { } else {
instance->generic.cnt += 1; uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
if((instance->generic.cnt + mult) > 0xFFFF) {
instance->generic.cnt = 0;
} else {
instance->generic.cnt += mult;
}
} }
uint32_t fix = (uint32_t)(instance->generic.data >> 32); uint32_t fix = (uint32_t)(instance->generic.data >> 32);
@@ -388,11 +394,30 @@ SubGhzProtocolStatus
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
uint8_t selected_btn = cfm_get_btn_code(instance->generic.btn); uint8_t selected_btn = cfm_get_btn_code(instance->generic.btn);
subghz_block_generic_global_button_override_get(&selected_btn);
if(!subghz_protocol_encoder_sheriff_cfm_get_upload(instance, selected_btn)) { if(!subghz_protocol_encoder_sheriff_cfm_get_upload(instance, selected_btn)) {
break; break;
} }
uint8_t key_data[sizeof(uint64_t)] = {0};
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF;
}
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t));
uint32_t model = (uint32_t)instance->model;
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Model", &model, 1);
uint32_t btn = instance->generic.btn;
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &btn, 1);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
instance->encoder.is_running = true; instance->encoder.is_running = true;
instance->encoder.front = 0; instance->encoder.front = 0;
ret = SubGhzProtocolStatusOk; ret = SubGhzProtocolStatusOk;
@@ -418,7 +443,7 @@ LevelDuration subghz_protocol_encoder_sheriff_cfm_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
+12 -4
View File
@@ -206,11 +206,18 @@ void subghz_protocol_encoder_star_line_free(void* context) {
static bool static bool
subghz_protocol_star_line_gen_data(SubGhzProtocolEncoderStarLine* instance, uint8_t btn) { subghz_protocol_star_line_gen_data(SubGhzProtocolEncoderStarLine* instance, uint8_t btn) {
if((instance->generic.cnt + 1) > 0xFFFF) { uint32_t override_cnt = 0;
instance->generic.cnt = 0; if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->generic.cnt = override_cnt & 0xFFFF;
} else { } else {
instance->generic.cnt += 1; uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
if((instance->generic.cnt + mult) > 0xFFFF) {
instance->generic.cnt = 0;
} else {
instance->generic.cnt += mult;
}
} }
instance->generic.btn = btn;
uint32_t fix = btn << 24 | instance->generic.serial; uint32_t fix = btn << 24 | instance->generic.serial;
uint32_t decrypt = btn << 24 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; uint32_t decrypt = btn << 24 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt;
@@ -520,6 +527,7 @@ SubGhzProtocolStatus
&instance->generic, instance->keystore, &instance->manufacture_name); &instance->generic, instance->keystore, &instance->manufacture_name);
uint8_t selected_btn = star_line_get_btn_code(instance->generic.btn); uint8_t selected_btn = star_line_get_btn_code(instance->generic.btn);
subghz_block_generic_global_button_override_get(&selected_btn);
subghz_protocol_encoder_star_line_get_upload(instance, selected_btn); subghz_protocol_encoder_star_line_get_upload(instance, selected_btn);
subghz_protocol_encoder_star_line_serialize(instance, flipper_format); subghz_protocol_encoder_star_line_serialize(instance, flipper_format);
@@ -556,7 +564,7 @@ LevelDuration subghz_protocol_encoder_star_line_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
+16 -3
View File
@@ -527,7 +527,7 @@ LevelDuration subghz_protocol_encoder_subaru_yield(void* context) {
LevelDuration ret = instance->encoder.upload[instance->encoder.front]; LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) { 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; instance->encoder.front = 0;
} }
@@ -609,10 +609,16 @@ SubGhzProtocolStatus subghz_protocol_encoder_subaru_deserialize(void* context, F
} }
uint8_t new_button = subaru_get_button_code(selected_custom_btn); uint8_t new_button = subaru_get_button_code(selected_custom_btn);
subghz_block_generic_global_button_override_get(&new_button);
instance->button = new_button; instance->button = new_button;
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult(); uint32_t override_cnt = 0;
instance->count = (instance->count + mult) & 0xFFFF; if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->count = override_cnt & 0xFFFF;
} else {
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
instance->count = (instance->count + mult) & 0xFFFF;
}
b[0] = (b[0] & 0xF0) | (instance->button & 0x0F); b[0] = (b[0] & 0xF0) | (instance->button & 0x0F);
@@ -643,6 +649,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_subaru_deserialize(void* context, F
ret = SubGhzProtocolStatusErrorParserKey; ret = SubGhzProtocolStatusErrorParserKey;
break; break;
} }
uint32_t temp_btn = instance->generic.btn;
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp_btn, 1);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
instance->encoder.is_running = true; instance->encoder.is_running = true;
ret = SubGhzProtocolStatusOk; ret = SubGhzProtocolStatusOk;
+16 -3
View File
@@ -400,13 +400,19 @@ SubGhzProtocolStatus subghz_protocol_encoder_suzuki_deserialize(void *context, F
} }
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult(); uint32_t override_cnt = 0;
instance->generic.cnt = (instance->generic.cnt + mult) & 0xFFFFF; if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->generic.cnt = override_cnt & 0xFFFFF;
} else {
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
instance->generic.cnt = (instance->generic.cnt + mult) & 0xFFFFF;
}
uint8_t selected = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ? uint8_t selected = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ?
subghz_custom_btn_get_original() : subghz_custom_btn_get_original() :
subghz_custom_btn_get(); subghz_custom_btn_get();
uint8_t btn = suzuki_custom_to_btn(selected); uint8_t btn = suzuki_custom_to_btn(selected);
subghz_block_generic_global_button_override_get(&btn);
instance->generic.btn = btn; instance->generic.btn = btn;
uint64_t new_data = 0; uint64_t new_data = 0;
@@ -434,6 +440,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_suzuki_deserialize(void *context, F
break; break;
} }
uint32_t temp_btn = instance->generic.btn;
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp_btn, 1);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
size_t preamble_count = SUZUKI_ENCODER_PREAMBLE_COUNT; size_t preamble_count = SUZUKI_ENCODER_PREAMBLE_COUNT;
size_t bit_count = 64; size_t bit_count = 64;
instance->encoder.size_upload = (preamble_count * 2) + (bit_count * 2) + 1; instance->encoder.size_upload = (preamble_count * 2) + (bit_count * 2) + 1;
@@ -494,7 +507,7 @@ LevelDuration subghz_protocol_encoder_suzuki_yield(void *context)
if (++instance->encoder.front == instance->encoder.size_upload) 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; instance->encoder.front = 0;
} }
+15 -3
View File
@@ -1358,7 +1358,7 @@ LevelDuration subghz_protocol_encoder_vag_yield(void* context) {
if(instance->front >= instance->size_upload) { if(instance->front >= instance->size_upload) {
instance->front = 0; instance->front = 0;
instance->repeat--; if(!subghz_block_generic_global.endless_tx) instance->repeat--;
} }
return ret; return ret;
@@ -1443,12 +1443,18 @@ SubGhzProtocolStatus
} }
uint8_t new_btn = vag_custom_to_btn(selected_custom, instance->btn); uint8_t new_btn = vag_custom_to_btn(selected_custom, instance->btn);
subghz_block_generic_global_button_override_get(&new_btn);
instance->btn = new_btn; instance->btn = new_btn;
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult(); uint32_t override_cnt = 0;
instance->cnt = (instance->cnt + mult) & 0xFFFFFF; if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
instance->cnt = override_cnt & 0xFFFFFF;
} else {
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
instance->cnt = (instance->cnt + mult) & 0xFFFFFF;
}
uint8_t type_byte = (uint8_t)(instance->key1_high >> 24); uint8_t type_byte = (uint8_t)(instance->key1_high >> 24);
if(instance->vag_type == 1 && type_byte == 0x00) { if(instance->vag_type == 1 && type_byte == 0x00) {
@@ -1518,6 +1524,12 @@ SubGhzProtocolStatus
flipper_format_insert_or_update_uint32(flipper_format, "Type", &type32, 1); flipper_format_insert_or_update_uint32(flipper_format, "Type", &type32, 1);
} }
uint32_t btn32 = instance->btn;
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &btn32, 1);
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->cnt, 1);
instance->repeat = 1; instance->repeat = 1;
+32 -4
View File
@@ -3,7 +3,9 @@ import hashlib
import os import os
import struct import struct
import subprocess import subprocess
import shutil
import tempfile import tempfile
import time
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass from dataclasses import dataclass
@@ -16,6 +18,21 @@ from flipper.app import App
VERSION = 1 VERSION = 1
def replace_file_with_retry(source: str, target: str, attempts: int = 50) -> None:
for attempt in range(attempts):
try:
os.replace(source, target)
return
except PermissionError:
if attempt == attempts - 1:
raise
time.sleep(0.1)
except OSError:
shutil.copy2(source, target)
os.remove(source)
return
@dataclass @dataclass
class RelData: class RelData:
section: int section: int
@@ -135,11 +152,16 @@ class Main(App):
RelSection(section_name, section.name, unique_relocations) RelSection(section_name, section.name, unique_relocations)
) )
with tempfile.TemporaryDirectory() as temp_dir: with tempfile.TemporaryDirectory(dir=os.path.dirname(os.path.abspath(fap_path))) as temp_dir:
for section in sections: current_fap_path = fap_path
for section_index, section in enumerate(sections):
data = serialize_relsection_data(section.data) data = serialize_relsection_data(section.data)
hash_name = hashlib.md5(section.name.encode()).hexdigest() hash_name = hashlib.md5(section.name.encode()).hexdigest()
filename = f"{temp_dir}/{hash_name}.bin" filename = os.path.join(temp_dir, f"{section_index}_{hash_name}.bin")
patched_fap_path = os.path.join(
temp_dir, f"{section_index}_{hash_name}.fap"
)
if os.path.isfile(filename): if os.path.isfile(filename):
self.logger.error(f"File {filename} already exists") self.logger.error(f"File {filename} already exists")
@@ -153,7 +175,8 @@ class Main(App):
objcopy_path, objcopy_path,
"--add-section", "--add-section",
f"{section.name}={filename}", f"{section.name}={filename}",
fap_path, current_fap_path,
patched_fap_path,
], ],
check=True, check=True,
) )
@@ -162,6 +185,11 @@ class Main(App):
self.logger.error("objcopy failed") self.logger.error("objcopy failed")
return 1 return 1
current_fap_path = patched_fap_path
if current_fap_path != fap_path:
replace_file_with_retry(current_fap_path, fap_path)
return 0 return 0