From 67e191b135960726b1ecb194272bc5e0a816263b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 5 Feb 2026 01:06:02 +0300 Subject: [PATCH] Keeloq ultimate, and some fixes and improvements [ci skip] - fix repeat values - fix endless tx missing - add mode 7 aka counter bypass - take some ram - free some ram - fix comments --- .../scenes/subghz_scene_signal_settings.c | 6 +- lib/subghz/protocols/came_twee.c | 2 +- lib/subghz/protocols/elplast.c | 2 +- lib/subghz/protocols/hay21.c | 2 +- lib/subghz/protocols/hollarm.c | 2 +- lib/subghz/protocols/hormann.c | 6 +- lib/subghz/protocols/keeloq.c | 214 +++++++++++------- lib/subghz/protocols/nice_flor_s.c | 2 +- lib/subghz/protocols/roger.c | 2 +- lib/subghz/protocols/treadmill37.c | 2 +- 10 files changed, 144 insertions(+), 96 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_signal_settings.c b/applications/main/subghz/scenes/subghz_scene_signal_settings.c index ae52e7314..90f99e50c 100644 --- a/applications/main/subghz/scenes/subghz_scene_signal_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_signal_settings.c @@ -22,7 +22,7 @@ static uint8_t* btn_byte_ptr = NULL; static uint8_t submenu_called = 0; -#define COUNTER_MODE_COUNT 7 +#define COUNTER_MODE_COUNT 8 static const char* const counter_mode_text[COUNTER_MODE_COUNT] = { "System", "Mode 1", @@ -31,6 +31,7 @@ static const char* const counter_mode_text[COUNTER_MODE_COUNT] = { "Mode 4", "Mode 5", "Mode 6", + "Mode 7", }; static const int32_t counter_mode_value[COUNTER_MODE_COUNT] = { @@ -41,6 +42,7 @@ static const int32_t counter_mode_value[COUNTER_MODE_COUNT] = { 4, 5, 6, + 7, }; typedef struct { @@ -53,7 +55,7 @@ static Protocols protocols[] = { {"Nice FloR-S", 3}, {"CAME Atomo", 4}, {"Alutech AT-4N", 3}, - {"KeeLoq", 7}, + {"KeeLoq", 8}, {"Phoenix_V2", 3}, }; diff --git a/lib/subghz/protocols/came_twee.c b/lib/subghz/protocols/came_twee.c index 20a4c5cc3..d6b656a57 100644 --- a/lib/subghz/protocols/came_twee.c +++ b/lib/subghz/protocols/came_twee.c @@ -110,7 +110,7 @@ void* subghz_protocol_encoder_came_twee_alloc(SubGhzEnvironment* environment) { instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 3; - instance->encoder.size_upload = 1536; //max upload 92*14 = 1288 !!!! + instance->encoder.size_upload = 1536; // 1308 instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; diff --git a/lib/subghz/protocols/elplast.c b/lib/subghz/protocols/elplast.c index 909689830..ee029bfc7 100644 --- a/lib/subghz/protocols/elplast.c +++ b/lib/subghz/protocols/elplast.c @@ -74,7 +74,7 @@ void* subghz_protocol_encoder_elplast_alloc(SubGhzEnvironment* environment) { instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 3; - instance->encoder.size_upload = 256; + instance->encoder.size_upload = 64; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; diff --git a/lib/subghz/protocols/hay21.c b/lib/subghz/protocols/hay21.c index 3495f5253..19995932c 100644 --- a/lib/subghz/protocols/hay21.c +++ b/lib/subghz/protocols/hay21.c @@ -76,7 +76,7 @@ void* subghz_protocol_encoder_hay21_alloc(SubGhzEnvironment* environment) { instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 3; - instance->encoder.size_upload = 256; + instance->encoder.size_upload = 64; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; diff --git a/lib/subghz/protocols/hollarm.c b/lib/subghz/protocols/hollarm.c index dc388b74a..fade91278 100644 --- a/lib/subghz/protocols/hollarm.c +++ b/lib/subghz/protocols/hollarm.c @@ -77,7 +77,7 @@ void* subghz_protocol_encoder_hollarm_alloc(SubGhzEnvironment* environment) { instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 3; - instance->encoder.size_upload = 256; + instance->encoder.size_upload = 128; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; diff --git a/lib/subghz/protocols/hormann.c b/lib/subghz/protocols/hormann.c index 1c463ee89..f8a2a9ed5 100644 --- a/lib/subghz/protocols/hormann.c +++ b/lib/subghz/protocols/hormann.c @@ -80,8 +80,8 @@ void* subghz_protocol_encoder_hormann_alloc(SubGhzEnvironment* environment) { instance->base.protocol = &subghz_protocol_hormann; instance->generic.protocol_name = instance->base.protocol->name; - instance->encoder.repeat = 3; - instance->encoder.size_upload = 2048; + instance->encoder.repeat = 1; + instance->encoder.size_upload = 1850; // 1801 instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; @@ -110,7 +110,6 @@ static bool subghz_protocol_encoder_hormann_get_upload(SubGhzProtocolEncoderHorm } else { instance->encoder.size_upload = size_upload; } - instance->encoder.repeat = 3; //original remote does 10 repeats for(size_t repeat = 0; repeat < 20; repeat++) { //Send start bit @@ -137,6 +136,7 @@ static bool subghz_protocol_encoder_hormann_get_upload(SubGhzProtocolEncoderHorm } instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short * 24); + return true; } diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 972229f68..d2182a396 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -100,6 +100,14 @@ static uint32_t subghz_protocol_keeloq_check_remote_controller( SubGhzKeystore* keystore, const char** manufacture_name); +/** + * Defines the button value for the current btn_id + * Basic set | 0x1 | 0x2 | 0x4 | 0x8 | 0xA or Special Learning Code | + * @param last_btn_code Candidate for the last button + * @return Button code + */ +static uint8_t subghz_protocol_keeloq_get_btn_code(uint8_t last_btn_code); + void* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment) { SubGhzProtocolEncoderKeeloq* instance = malloc(sizeof(SubGhzProtocolEncoderKeeloq)); @@ -107,8 +115,8 @@ void* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment) { instance->generic.protocol_name = instance->base.protocol->name; instance->keystore = subghz_environment_get_keystore(environment); - instance->encoder.repeat = 100; - instance->encoder.size_upload = 256; + instance->encoder.repeat = 3; + instance->encoder.size_upload = 1100; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; @@ -134,7 +142,57 @@ void subghz_protocol_encoder_keeloq_free(void* context) { static bool subghz_protocol_keeloq_gen_data( SubGhzProtocolEncoderKeeloq* instance, uint8_t btn, - bool counter_up) { + bool counter_up, + bool skip_btn_check) { + // No mf name set? -> set to "" + if(instance->manufacture_name == 0x0) { + instance->manufacture_name = ""; + } + // add gendata part + ProgMode prog_mode = subghz_custom_btn_get_prog_mode(); + if(!skip_btn_check && (keeloq_counter_mode != 7)) { + // Save original button + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(btn); + } + + // Prog mode checks and extra fixage of MF Names + if(prog_mode == PROG_MODE_KEELOQ_BFT) { + instance->manufacture_name = "BFT"; + } else if(prog_mode == PROG_MODE_KEELOQ_APRIMATIC) { + instance->manufacture_name = "Aprimatic"; + } else if(prog_mode == PROG_MODE_KEELOQ_DEA_MIO) { + instance->manufacture_name = "Dea_Mio"; + } + // Custom button (programming mode button) for BFT, Aprimatic, Dea_Mio + uint8_t klq_last_custom_btn = 0xA; + if((strcmp(instance->manufacture_name, "BFT") == 0) || + (strcmp(instance->manufacture_name, "Aprimatic") == 0) || + (strcmp(instance->manufacture_name, "Dea_Mio") == 0) || + (strcmp(instance->manufacture_name, "NICE_MHOUSE") == 0)) { + klq_last_custom_btn = 0xF; + } else if( + (strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) || + (strcmp(instance->manufacture_name, "Monarch") == 0) || + (strcmp(instance->manufacture_name, "NICE_Smilo") == 0)) { + klq_last_custom_btn = 0xB; + } else if( + (strcmp(instance->manufacture_name, "Novoferm") == 0) || + (strcmp(instance->manufacture_name, "Stilmatic") == 0)) { + klq_last_custom_btn = 0x9; + } else if( + (strcmp(instance->manufacture_name, "EcoStar") == 0) || + (strcmp(instance->manufacture_name, "Sommer") == 0)) { + klq_last_custom_btn = 0x6; + } else if((strcmp(instance->manufacture_name, "AN-Motors") == 0)) { + klq_last_custom_btn = 0xC; + } else if((strcmp(instance->manufacture_name, "Cardin_S449") == 0)) { + klq_last_custom_btn = 0xD; + } + + btn = subghz_protocol_keeloq_get_btn_code(klq_last_custom_btn); + } + // end gendata part // override button if we change it with signal settings button editor if(subghz_block_generic_global_button_override_get(&btn)) FURI_LOG_D(TAG, "Button sucessfully changed to 0x%X", btn); @@ -150,7 +208,6 @@ static bool subghz_protocol_keeloq_gen_data( } // programming mode on / off conditions - ProgMode prog_mode = subghz_custom_btn_get_prog_mode(); if(strcmp(instance->manufacture_name, "BFT") == 0) { // BFT programming mode on / off conditions if(btn == 0xF) { @@ -259,8 +316,16 @@ static bool subghz_protocol_keeloq_gen_data( } else { instance->generic.cnt = 0xFFFF; } - } else { + } else if(keeloq_counter_mode == 6) { // Mode 6 - Freeze counter + } else { + // Mode 7 - Make 12 signals in row with mode 2 + // + 0x3333 each time + if((instance->generic.cnt + 0x3333) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += 0x3333; + } } } if(prog_mode == PROG_MODE_OFF) { @@ -437,7 +502,7 @@ bool subghz_protocol_keeloq_create_data( instance->generic.cnt = cnt; instance->manufacture_name = manufacture_name; instance->generic.data_count_bit = 64; - if(subghz_protocol_keeloq_gen_data(instance, btn, false)) { + if(subghz_protocol_keeloq_gen_data(instance, btn, false, true)) { return ( subghz_block_generic_serialize(&instance->generic, flipper_format, preset) == SubGhzProtocolStatusOk); @@ -463,7 +528,7 @@ bool subghz_protocol_keeloq_bft_create_data( instance->manufacture_name = manufacture_name; instance->generic.data_count_bit = 64; // hehehehe - if(subghz_protocol_keeloq_gen_data(instance, btn, false)) { + if(subghz_protocol_keeloq_gen_data(instance, btn, false, true)) { return ( subghz_block_generic_serialize(&instance->generic, flipper_format, preset) == SubGhzProtocolStatusOk); @@ -471,88 +536,22 @@ bool subghz_protocol_keeloq_bft_create_data( return false; } -/** - * Defines the button value for the current btn_id - * Basic set | 0x1 | 0x2 | 0x4 | 0x8 | 0xA or Special Learning Code | - * @param last_btn_code Candidate for the last button - * @return Button code - */ -static uint8_t subghz_protocol_keeloq_get_btn_code(uint8_t last_btn_code); - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance - * @return true On success - */ -static bool - subghz_protocol_encoder_keeloq_get_upload(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { +static size_t subghz_protocol_encoder_keeloq_encode_to_timings( + SubGhzProtocolEncoderKeeloq* instance, + uint8_t btn, + bool counter_up, + size_t index) { furi_assert(instance); - - // Save original button - if(subghz_custom_btn_get_original() == 0) { - subghz_custom_btn_set_original(btn); - } - - // No mf name set? -> set to "" - if(instance->manufacture_name == 0x0) { - instance->manufacture_name = ""; - } - // Prog mode checks and extra fixage of MF Names - ProgMode prog_mode = subghz_custom_btn_get_prog_mode(); - if(prog_mode == PROG_MODE_KEELOQ_BFT) { - instance->manufacture_name = "BFT"; - } else if(prog_mode == PROG_MODE_KEELOQ_APRIMATIC) { - instance->manufacture_name = "Aprimatic"; - } else if(prog_mode == PROG_MODE_KEELOQ_DEA_MIO) { - instance->manufacture_name = "Dea_Mio"; - } - // Custom button (programming mode button) for BFT, Aprimatic, Dea_Mio - uint8_t klq_last_custom_btn = 0xA; - if((strcmp(instance->manufacture_name, "BFT") == 0) || - (strcmp(instance->manufacture_name, "Aprimatic") == 0) || - (strcmp(instance->manufacture_name, "Dea_Mio") == 0) || - (strcmp(instance->manufacture_name, "NICE_MHOUSE") == 0)) { - klq_last_custom_btn = 0xF; - } else if( - (strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) || - (strcmp(instance->manufacture_name, "Monarch") == 0) || - (strcmp(instance->manufacture_name, "NICE_Smilo") == 0)) { - klq_last_custom_btn = 0xB; - } else if( - (strcmp(instance->manufacture_name, "Novoferm") == 0) || - (strcmp(instance->manufacture_name, "Stilmatic") == 0)) { - klq_last_custom_btn = 0x9; - } else if( - (strcmp(instance->manufacture_name, "EcoStar") == 0) || - (strcmp(instance->manufacture_name, "Sommer") == 0)) { - klq_last_custom_btn = 0x6; - } else if((strcmp(instance->manufacture_name, "AN-Motors") == 0)) { - klq_last_custom_btn = 0xC; - } else if((strcmp(instance->manufacture_name, "Cardin_S449") == 0)) { - klq_last_custom_btn = 0xD; - } - - uint32_t gap_duration = subghz_protocol_keeloq_const.te_short * 40; - if((strcmp(instance->manufacture_name, "Sommer") == 0)) { - gap_duration = subghz_protocol_keeloq_const.te_short * 29; - } - - btn = subghz_protocol_keeloq_get_btn_code(klq_last_custom_btn); - // Generate new key - if(subghz_protocol_keeloq_gen_data(instance, btn, true)) { + if(subghz_protocol_keeloq_gen_data(instance, btn, counter_up, false)) { // OK } else { return false; } - size_t index = 0; - size_t size_upload = 11 * 2 + 2 + (instance->generic.data_count_bit * 2) + 4; - if(size_upload > instance->encoder.size_upload) { - FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); - return false; - } else { - instance->encoder.size_upload = size_upload; + uint32_t gap_duration = subghz_protocol_keeloq_const.te_short * 40; + if((strcmp(instance->manufacture_name, "Sommer") == 0)) { + gap_duration = subghz_protocol_keeloq_const.te_short * 29; } //Send header @@ -593,6 +592,52 @@ static bool level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); instance->encoder.upload[index++] = level_duration_make(false, gap_duration); + return index; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool + subghz_protocol_encoder_keeloq_get_upload(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { + furi_assert(instance); + + instance->encoder.size_upload = 0; + size_t upindex = 0; + + if(keeloq_counter_mode == 7) { + uint16_t temp_cnt = instance->generic.cnt; + instance->encoder.repeat = 1; + for(uint8_t i = 7; i > 0; i--) { + if(i == 3) { + instance->generic.cnt = 0x0000; + upindex = subghz_protocol_encoder_keeloq_encode_to_timings( + instance, (uint8_t)0x00, false, upindex); + continue; + } else if(i == 2) { + instance->generic.cnt = temp_cnt; + upindex = subghz_protocol_encoder_keeloq_encode_to_timings( + instance, btn, false, upindex); + continue; + } else if(i == 1) { + instance->generic.cnt = temp_cnt + 1; + upindex = subghz_protocol_encoder_keeloq_encode_to_timings( + instance, btn, false, upindex); + continue; + } + upindex = subghz_protocol_encoder_keeloq_encode_to_timings( + instance, (uint8_t)0x00, true, upindex); + } + instance->encoder.size_upload = upindex; + return true; + } else { + instance->encoder.repeat = 3; + instance->encoder.size_upload = + subghz_protocol_encoder_keeloq_encode_to_timings(instance, btn, true, upindex); + } + return true; } @@ -719,7 +764,7 @@ LevelDuration subghz_protocol_encoder_keeloq_yield(void* context) { LevelDuration ret = instance->encoder.upload[instance->encoder.front]; if(++instance->encoder.front == instance->encoder.size_upload) { - instance->encoder.repeat--; + if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--; instance->encoder.front = 0; } @@ -1366,7 +1411,8 @@ static uint8_t subghz_protocol_keeloq_get_btn_code(uint8_t last_btn_code) { // Set custom button // Basic set | 0x1 | 0x2 | 0x4 | 0x8 | 0xA or Special Learning Code | - if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0)) { + if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0) && + (keeloq_counter_mode != 7)) { // Restore original button code btn = original_btn_code; } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_UP) { diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index e1d796ae3..b554deccf 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -105,7 +105,7 @@ void* subghz_protocol_encoder_nice_flor_s_alloc(SubGhzEnvironment* environment) TAG, "Loading rainbow table from %s", instance->nice_flor_s_rainbow_table_file_name); } instance->encoder.repeat = 1; - instance->encoder.size_upload = 2400; //wrong!! upload 186*16 = 2976 - actual size about 1728 + instance->encoder.size_upload = 2400; // 2368 for Nice ONE instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; diff --git a/lib/subghz/protocols/roger.c b/lib/subghz/protocols/roger.c index 8eccee566..35d80788d 100644 --- a/lib/subghz/protocols/roger.c +++ b/lib/subghz/protocols/roger.c @@ -77,7 +77,7 @@ void* subghz_protocol_encoder_roger_alloc(SubGhzEnvironment* environment) { instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 3; - instance->encoder.size_upload = 256; + instance->encoder.size_upload = 128; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; diff --git a/lib/subghz/protocols/treadmill37.c b/lib/subghz/protocols/treadmill37.c index fdd5b3d71..ddb8ba021 100644 --- a/lib/subghz/protocols/treadmill37.c +++ b/lib/subghz/protocols/treadmill37.c @@ -74,7 +74,7 @@ void* subghz_protocol_encoder_treadmill37_alloc(SubGhzEnvironment* environment) instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 3; - instance->encoder.size_upload = 256; + instance->encoder.size_upload = 128; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance;