From 84f580d1684ad6ad1b830b5fb310bebd504badb1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jan 2026 03:17:41 +0300 Subject: [PATCH 1/7] subghz: fix stilmatic keeloq support, display decr. hop feature --- CHANGELOG.md | 3 +- documentation/SubGHzSupportedSystems.md | 2 +- lib/subghz/protocols/keeloq.c | 63 ++++++++++++++----------- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f87d730fd..4f37e21aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,14 @@ * SubGHz: **Jarolift protocol full support** (72bit dynamic) (with Add manually, and all button codes) (by @xMasterX & d82k & Steffen (bastelbudenbuben de)) * SubGHz: **New modulation FSK with 12KHz deviation** * SubGHz: **KingGates Stylo 4k - Add manually and button switch support** + refactoring of encoder -* SubGHz: **Stilmatic - button 9 support** (two buttons hold simulation) (mapped on arrow keys) +* SubGHz: **Stilmatic (R-Tech) - 12bit discr. fix & button 9 support** (two buttons hold simulation) (mapped on arrow keys) * SubGHz: **Counter editor refactoring** (PR #939 | by @Dmitry422) * SubGHz: **Alutech AT-4N & Nice Flor S turbo speedup** (PR #942 | by @Dmitry422) * SubGHz: **Sommer fm2 in Add manually now uses FM12K modulation** (Sommer without fm2 tag uses FM476) (try this if regular option doesn't work for you) * SubGHz: **Sommer - last button code 0x6 support** (mapped on arrow keys) * SubGHz: Add 390MHz, 430.5MHz to default hopper list (6 elements like in OFW) (works well with Hopper RSSI level set for your enviroment) * SubGHz: Fixed button mapping for **FAAC RC/XT** +* SubGHz: KeeLoq display decrypted hop in `Hop` instead of showing encrypted as is (encrypted non byte reversed hop is still displayed in `Key` field) * NFC: Handle PPS request in ISO14443-4 layer (by @WillyJL) * NFC: Fixes to `READ_MULTI` and `GET_BLOCK_SECURITY` commands in ISO 15693-3 emulation (by @WillyJL & @aaronjamt) * Archive: Allow folders to be pinned (by @WillyJL) diff --git a/documentation/SubGHzSupportedSystems.md b/documentation/SubGHzSupportedSystems.md index 0cfcb8881..33e2d1cb2 100644 --- a/documentation/SubGHzSupportedSystems.md +++ b/documentation/SubGHzSupportedSystems.md @@ -124,7 +124,7 @@ The following manufacturers have KeeLoq support in Unleashed firmware: - Novoferm - `433.92MHz` `AM650` (KeeLoq, 64 bits) - Sommer `434.42MHz, 868.80MHz` `FSK12K (or FSK476)` (KeeLoq, 64 bits) (normal learning) (TX03-868-4, Pearl, and maybe other models are supported (SOMloq)) - Steelmate - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - normal learning) -- Stilmatic - `433.92MHz` `AM650` (KeeLoq, 64 bits) (normal learning) +- Stilmatic (R-Tech) - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - normal learning) ### Alarms, unknown origin, etc. - APS-1100/APS-2550 (KeeLoq, 64 bits) diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index cd657f23f..837ae038e 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -95,7 +95,7 @@ const SubGhzProtocol subghz_protocol_keeloq = { * @param keystore Pointer to a SubGhzKeystore* instance * @param manufacture_name */ -static void subghz_protocol_keeloq_check_remote_controller( +static uint32_t subghz_protocol_keeloq_check_remote_controller( SubGhzBlockGeneric* instance, SubGhzKeystore* keystore, const char** manufacture_name); @@ -305,12 +305,14 @@ static bool subghz_protocol_keeloq_gen_data( (strcmp(instance->manufacture_name, "Rossi") == 0) || (strcmp(instance->manufacture_name, "Pecinin") == 0) || (strcmp(instance->manufacture_name, "Steelmate") == 0) || - (strcmp(instance->manufacture_name, "Cardin_S449") == 0)) { + (strcmp(instance->manufacture_name, "Cardin_S449") == 0) || + (strcmp(instance->manufacture_name, "Stilmatic") == 0)) { // DTM Neo, Came_Space uses 12bit serial -> simple learning // FAAC_RC,XT , Mutanco_Mutancode, Genius_Bravo, GSN 12bit serial -> normal learning // Rosh, Rossi, Pecinin -> 12bit serial - simple learning // Steelmate -> 12bit serial - normal learning // Cardin_S449 -> 12bit serial - normal learning + // Stilmatic (r-tech) -> 12bit serial - normal learning decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 | instance->generic.cnt; } else if( @@ -899,9 +901,9 @@ static inline bool subghz_protocol_keeloq_check_decrypt_centurion( * @param hop Hop encrypted part of the parcel * @param keystore Pointer to a SubGhzKeystore* instance * @param manufacture_name - * @return true on successful search - */ -static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( + * @return uint32_t decrypt data */ + +static uint32_t subghz_protocol_keeloq_check_remote_controller_selector( SubGhzBlockGeneric* instance, uint32_t fix, uint32_t hop, @@ -924,7 +926,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( const char* mfname = keystore->mfname; if(strcmp(mfname, "Unknown") == 0) { - return 1; + return 0; } else if(strcmp(mfname, "") == 0) { mf_not_set = true; } @@ -938,7 +940,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; - return 1; + return decrypt; } break; case KEELOQ_LEARNING_NORMAL: @@ -951,14 +953,14 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( if(subghz_protocol_keeloq_check_decrypt_centurion(instance, decrypt, btn)) { *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; - return 1; + return decrypt; } } else { if(subghz_protocol_keeloq_check_decrypt( instance, decrypt, btn, end_serial)) { *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; - return 1; + return decrypt; } } break; @@ -976,7 +978,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; - return 1; + return decrypt; } else { if(reset_seed_back) instance->seed = 0; } @@ -988,7 +990,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; - return 1; + return decrypt; } break; case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: @@ -998,7 +1000,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; - return 1; + return decrypt; } break; case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2: @@ -1008,7 +1010,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; - return 1; + return decrypt; } break; case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3: @@ -1018,7 +1020,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; - return 1; + return decrypt; } break; case KEELOQ_LEARNING_UNKNOWN: @@ -1028,7 +1030,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; keystore->kl_type = 1; - return 1; + return decrypt; } // Check for mirrored man @@ -1044,7 +1046,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; keystore->kl_type = 1; - return 1; + return decrypt; } //########################### @@ -1057,7 +1059,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; keystore->kl_type = 2; - return 1; + return decrypt; } // Check for mirrored man @@ -1067,7 +1069,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; keystore->kl_type = 2; - return 1; + return decrypt; } // Secure Learning @@ -1078,7 +1080,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; keystore->kl_type = 3; - return 1; + return decrypt; } // Check for mirrored man @@ -1089,7 +1091,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; keystore->kl_type = 3; - return 1; + return decrypt; } // Magic xor type1 learning @@ -1100,7 +1102,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; keystore->kl_type = 4; - return 1; + return decrypt; } // Check for mirrored man @@ -1110,7 +1112,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( *manufacture_name = furi_string_get_cstr(manufacture_code->name); keystore->mfname = *manufacture_name; keystore->kl_type = 4; - return 1; + return decrypt; } break; @@ -1126,7 +1128,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( return 0; } -static void subghz_protocol_keeloq_check_remote_controller( +static uint32_t subghz_protocol_keeloq_check_remote_controller( SubGhzBlockGeneric* instance, SubGhzKeystore* keystore, const char** manufacture_name) { @@ -1135,6 +1137,7 @@ static void subghz_protocol_keeloq_check_remote_controller( uint32_t key_fix = key >> 32; uint32_t key_hop = key & 0x00000000ffffffff; static uint16_t temp_counter = 0; // Be careful with prog_mode + uint32_t resdecrypt = 0; // If we are in BFT / Aprimatic / Dea_Mio programming mode we will set previous remembered counter and skip mf keys check ProgMode prog_mode = subghz_custom_btn_get_prog_mode(); @@ -1159,7 +1162,7 @@ static void subghz_protocol_keeloq_check_remote_controller( keystore->mfname = *manufacture_name; instance->cnt = key_hop >> 16; } else { - subghz_protocol_keeloq_check_remote_controller_selector( + resdecrypt = subghz_protocol_keeloq_check_remote_controller_selector( instance, key_fix, key_hop, keystore, manufacture_name); } } else { @@ -1176,7 +1179,7 @@ static void subghz_protocol_keeloq_check_remote_controller( instance->cnt = key_hop >> 16; } else { // Else we have mfname that is not AN-Motors or HCS101 we should check it via default selector - subghz_protocol_keeloq_check_remote_controller_selector( + resdecrypt = subghz_protocol_keeloq_check_remote_controller_selector( instance, key_fix, key_hop, keystore, manufacture_name); } } @@ -1213,6 +1216,8 @@ static void subghz_protocol_keeloq_check_remote_controller( } // Set max custom buttons subghz_custom_btn_set_max(4); + + return resdecrypt; } uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) { @@ -1483,7 +1488,9 @@ void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output furi_assert(context); SubGhzProtocolDecoderKeeloq* instance = context; - subghz_protocol_keeloq_check_remote_controller( + uint32_t hopdecrypt = 0; + + hopdecrypt = subghz_protocol_keeloq_check_remote_controller( &instance->generic, instance->keystore, &instance->manufacture_name); uint32_t code_found_hi = instance->generic.data >> 32; @@ -1511,7 +1518,7 @@ void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output code_found_lo, code_found_reverse_hi, instance->generic.cnt, - code_found_reverse_lo, + hopdecrypt, instance->generic.btn, instance->manufacture_name, instance->generic.seed); @@ -1549,7 +1556,7 @@ void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output code_found_lo, code_found_reverse_hi, instance->generic.cnt, - code_found_reverse_lo, + hopdecrypt, instance->generic.btn, instance->manufacture_name); } From c05ef82b11b93b35ff150b111859276782dafa2e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jan 2026 04:52:02 +0300 Subject: [PATCH 2/7] try to decode bft with zero seed ?! --- lib/subghz/protocols/keeloq.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 837ae038e..500ea6459 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -981,6 +981,16 @@ static uint32_t subghz_protocol_keeloq_check_remote_controller_selector( return decrypt; } else { if(reset_seed_back) instance->seed = 0; + // Try with zero seed (some strange remotes have been reported to use 0 seed) + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + keystore->mfname = *manufacture_name; + return decrypt; + } } break; case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: From 523a3723cf9ec475c2904603b422c55802120519 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jan 2026 06:10:45 +0300 Subject: [PATCH 3/7] upd changelog and faq --- CHANGELOG.md | 3 ++- documentation/FAQ.md | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f37e21aa..f8564a535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ * SubGHz: **Sommer - last button code 0x6 support** (mapped on arrow keys) * SubGHz: Add 390MHz, 430.5MHz to default hopper list (6 elements like in OFW) (works well with Hopper RSSI level set for your enviroment) * SubGHz: Fixed button mapping for **FAAC RC/XT** -* SubGHz: KeeLoq display decrypted hop in `Hop` instead of showing encrypted as is (encrypted non byte reversed hop is still displayed in `Key` field) +* SubGHz: KeeLoq **display decrypted hop** in `Hop` instead of showing encrypted as is (encrypted non byte reversed hop is still displayed in `Key` field) +* SubGHz: **BFT KeeLoq** try decoding with **zero seed** too * NFC: Handle PPS request in ISO14443-4 layer (by @WillyJL) * NFC: Fixes to `READ_MULTI` and `GET_BLOCK_SECURITY` commands in ISO 15693-3 emulation (by @WillyJL & @aaronjamt) * Archive: Allow folders to be pinned (by @WillyJL) diff --git a/documentation/FAQ.md b/documentation/FAQ.md index cd38ad7a8..25f19e30f 100644 --- a/documentation/FAQ.md +++ b/documentation/FAQ.md @@ -44,21 +44,21 @@ Here - [link](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/docume ## I want to request or make new SubGHz protocol, my remote (is not car keyfob) and is not supported, how to record RAW signal properly? -1. Open SubGHz app, (if you know the frequency skip that step and go to Read) select Frequency analyzer, press and hold button on your remote and place it near IR window on flipper -You will find a approx. frequency that remote uses, release button on the remote and wait until frequency will be placed in history list -Hold OK on flipper to jump into Read mode, now try pressing your remote couple times holding it for at least 2 seconds -Try different modulations, AM650/FM238/FM476/FM12K - nothing works? Lets make RAW recording for analysis -2. Knowing the frequency open Read RAW and set it here in config page -Make sure RSSI Threshold is set to (----) -You need to make 1 RAW for each modulation AM650/FM238/FM476/FM12K -Press REC and on your remote press 1 button 5 times holding it for 1-2 seconds - then 5 times holding it for 5 seconds each time -If your remote has more than 1 button - record each button in similar way +1. Open SubGHz app, (if you know the frequency skip that step and go to Read) select Frequency analyzer, press and hold button on your remote and place it near IR window on flipper
+You will find a approx. frequency that remote uses, release button on the remote and wait until frequency will be placed in history list
+Hold OK on flipper to jump into Read mode, now try pressing your remote couple times holding it for at least 2 seconds
+Try different modulations, AM650/FM238/FM476/FM12K - nothing works? Lets make RAW recording for analysis
+2. Knowing the frequency open Read RAW and set it here in config page
+Make sure RSSI Threshold is set to (----)
+You need to make 1 RAW for each modulation AM650/FM238/FM476/FM12K
+Press REC and on your remote press 1 button 5 times holding it for 1-2 seconds - then 5 times holding it for 5 seconds each time
+If your remote has more than 1 button - record each button in similar way
Label each raw - what button you recorded -3. Copy all that RAW files to PC and create issue in firmware repo, attach raw's in archive -Provide high quality photos of the remote, if possible - photos of disassembled remote too -Its model, manufacturer, any known information -If you have access to receiver board, add a photo too -Done! If your remote appears not to be encrypted and very unique, it might be added soon +3. Copy all that RAW files to PC and create issue in firmware repo, attach raw's in archive
+Provide high quality photos of the remote, if possible - photos of disassembled remote too
+Its model, manufacturer, any known information
+If you have access to receiver board, add a photo too
+Done! If your remote appears not to be encrypted and very unique, it might be added soon
In case if you want to help us or analyze that signals youself there's a great online tool - https://lab.flipper.net/pulse-plotter ## How to build (compile) the firmware? From 2469206cc8b549f35d4c7fcbcd70d4986966ad60 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jan 2026 06:18:41 +0300 Subject: [PATCH 4/7] subghz fix and show prog mode bft --- CHANGELOG.md | 1 + lib/subghz/protocols/keeloq.c | 55 ++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8564a535..da9bbee95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * SubGHz: Fixed button mapping for **FAAC RC/XT** * SubGHz: KeeLoq **display decrypted hop** in `Hop` instead of showing encrypted as is (encrypted non byte reversed hop is still displayed in `Key` field) * SubGHz: **BFT KeeLoq** try decoding with **zero seed** too +* SubGHz: KeeLoq **display BFT programming mode TX** (when arrow button is held) * NFC: Handle PPS request in ISO14443-4 layer (by @WillyJL) * NFC: Fixes to `READ_MULTI` and `GET_BLOCK_SECURITY` commands in ISO 15693-3 emulation (by @WillyJL & @aaronjamt) * Archive: Allow folders to be pinned (by @WillyJL) diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 500ea6459..ca0db9f7b 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -1515,23 +1515,44 @@ void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output subghz_block_generic_global.cnt_is_available = true; subghz_block_generic_global.cnt_length_bit = 16; subghz_block_generic_global.current_cnt = instance->generic.cnt; - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%08lX%08lX\r\n" - "Fix:0x%08lX Cnt:%04lX\r\n" - "Hop:0x%08lX Btn:%01X\r\n" - "MF:%s Sd:%08lX", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - code_found_reverse_hi, - instance->generic.cnt, - hopdecrypt, - instance->generic.btn, - instance->manufacture_name, - instance->generic.seed); + ProgMode prog_mode = subghz_custom_btn_get_prog_mode(); + if(prog_mode == PROG_MODE_KEELOQ_BFT) { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%08lX%08lX\r\n" + "Fix:0x%08lX Cnt:%04lX\r\n" + "Hop:0x%08lX Btn:%01X\r\n" + "MF:%s PRG Sd:%08lX", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + instance->generic.cnt, + code_found_reverse_lo, + instance->generic.btn, + instance->manufacture_name, + instance->generic.seed); + } else { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%08lX%08lX\r\n" + "Fix:0x%08lX Cnt:%04lX\r\n" + "Hop:0x%08lX Btn:%01X\r\n" + "MF:%s Sd:%08lX", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + instance->generic.cnt, + hopdecrypt, + instance->generic.btn, + instance->manufacture_name, + instance->generic.seed); + } } else if(strcmp(instance->manufacture_name, "Unknown") == 0) { instance->generic.cnt = 0x0; furi_string_cat_printf( From 1d734948d9592d3aa3339eb0e6bf633718510af3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jan 2026 07:36:42 +0300 Subject: [PATCH 5/7] fix about screen define --- applications/settings/about/about.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index d9fbca87a..efd53f210 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -156,9 +156,9 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* buffer = furi_string_alloc(); const Version* ver = furi_hal_version_get_firmware_version(); const BleGlueC2Info* c2_ver = NULL; -#ifdef SRV_BT + //#ifdef SRV_BT c2_ver = ble_glue_get_c2_info(); -#endif + //#endif if(!ver) { //-V1051 furi_string_cat_printf(buffer, "No info\n"); From 4800039d64b181e112eaf545295d226b00662584 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 31 Jan 2026 17:00:11 +0300 Subject: [PATCH 6/7] add info about stilmatic and cardin receivers --- documentation/SubGHzSupportedSystems.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/SubGHzSupportedSystems.md b/documentation/SubGHzSupportedSystems.md index 33e2d1cb2..faa42e9e1 100644 --- a/documentation/SubGHzSupportedSystems.md +++ b/documentation/SubGHzSupportedSystems.md @@ -98,7 +98,7 @@ The following manufacturers have KeeLoq support in Unleashed firmware: - Aprimatic - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial number art in Hop + 2bit "parity" in front of it replacing first 2 bits of serial) - Beninca - `433.92MHz, 868MHz` `AM650` (KeeLoq, 64 bits) (no serial part in Hop - magic XOR) - CAME Space - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - simple learning) -- Cardin S449 - `433.92MHz` `FSK12K` (KeeLoq, 64 bits) (12bit (original remotes) or 10bit (chinese remotes) serial part in Hop - normal learning) +- Cardin S449 - `433.92MHz` `FSK12K` (KeeLoq, 64 bits) (12bit (original remotes) or 10bit (chinese remotes) serial part in Hop - normal learning) (receiver checks for 10bit only (unverified)) - Centurion - `433.92MHz` `AM650` (KeeLoq, 64 bits) (no serial in Hop, uses fixed value 0x1CE - normal learning) - Comunello - `433.92MHz, 868MHz` `AM650` (KeeLoq, 64 bits) (normal learning) - DEA Mio - `433.92MHz` `AM650` (KeeLoq, 64 bits) (modified serial in Hop, uses last 3 digits modifying first one (example - 419 -> C19) - simple learning) @@ -124,7 +124,7 @@ The following manufacturers have KeeLoq support in Unleashed firmware: - Novoferm - `433.92MHz` `AM650` (KeeLoq, 64 bits) - Sommer `434.42MHz, 868.80MHz` `FSK12K (or FSK476)` (KeeLoq, 64 bits) (normal learning) (TX03-868-4, Pearl, and maybe other models are supported (SOMloq)) - Steelmate - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - normal learning) -- Stilmatic (R-Tech) - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - normal learning) +- Stilmatic (R-Tech) - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - normal learning) (receiver checks for 10bit only (unverified)) ### Alarms, unknown origin, etc. - APS-1100/APS-2550 (KeeLoq, 64 bits) From 95dd537bf81ee445b1fb71aa60431cb62080333d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 1 Feb 2026 05:51:09 +0300 Subject: [PATCH 7/7] add v2 phox counter modes support --- CHANGELOG.md | 1 + .../scenes/subghz_scene_signal_settings.c | 1 + documentation/SubGHzCounterMode.md | 18 +++++ documentation/SubGHzSupportedSystems.md | 4 +- lib/subghz/protocols/phoenix_v2.c | 72 ++++++++++++++++--- 5 files changed, 85 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da9bbee95..fea108a20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * SubGHz: **Alutech AT-4N & Nice Flor S turbo speedup** (PR #942 | by @Dmitry422) * SubGHz: **Sommer fm2 in Add manually now uses FM12K modulation** (Sommer without fm2 tag uses FM476) (try this if regular option doesn't work for you) * SubGHz: **Sommer - last button code 0x6 support** (mapped on arrow keys) +* SubGHz: **V2 Phoenix (Phox) added 2 counter modes support** (docs updated) * SubGHz: Add 390MHz, 430.5MHz to default hopper list (6 elements like in OFW) (works well with Hopper RSSI level set for your enviroment) * SubGHz: Fixed button mapping for **FAAC RC/XT** * SubGHz: KeeLoq **display decrypted hop** in `Hop` instead of showing encrypted as is (encrypted non byte reversed hop is still displayed in `Key` field) diff --git a/applications/main/subghz/scenes/subghz_scene_signal_settings.c b/applications/main/subghz/scenes/subghz_scene_signal_settings.c index e74219a28..13c8234e2 100644 --- a/applications/main/subghz/scenes/subghz_scene_signal_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_signal_settings.c @@ -47,6 +47,7 @@ static Protocols protocols[] = { {"CAME Atomo", 4}, {"Alutech AT-4N", 3}, {"KeeLoq", 7}, + {"Phoenix_V2", 3}, }; #define PROTOCOLS_COUNT (sizeof(protocols) / sizeof(Protocols)); diff --git a/documentation/SubGHzCounterMode.md b/documentation/SubGHzCounterMode.md index 54fd0e24e..76eac58e4 100644 --- a/documentation/SubGHzCounterMode.md +++ b/documentation/SubGHzCounterMode.md @@ -129,6 +129,24 @@ CounterMode: 1 --- +### 5. V2 Phoenix (Phox) + +**Mode 0 (Default):** +- Standard - acts like regular remote +- Uses rolling counter multiplier from global settings (default +1) +- Counter increments based on the multiplier value (default +1) +- Resets to 0 when overflow occurs (> 0xFFFF) + +**Mode 1 (ofex like):** +- Counter sequence: `0x0000 / 0x0001 / 0xFFFE / 0xFFFF` +- Verified as working + +**Mode 2 (0 - 4):** +- Counter sequence: `0x0000 / 0x0001 / 0x0002 / 0x0003 / 0x0004` +- Might work (let us know!) + +--- + ## Notes and Warnings ### Important Considerations: diff --git a/documentation/SubGHzSupportedSystems.md b/documentation/SubGHzSupportedSystems.md index faa42e9e1..eed5ee420 100644 --- a/documentation/SubGHzSupportedSystems.md +++ b/documentation/SubGHzSupportedSystems.md @@ -20,7 +20,7 @@ That list is only for default SubGHz app, apps like *Weather Station* have their - AN-Motors (Alutech) AT4 `433.92MHz` `AM650` (64 bits, Pseudo-Dynamic, KeeLoq based) - Ansonic `433MHz` `FM` (12 bits, Static) - BETT `433.92MHz` `AM650` (18 bits, Static) -- Beninca ARC (TOGO2VA) `433.92MHz` `AM650` (128 bits, Dynamic AES) +- Beninca ARC (TOGO2VA) `433.92MHz` `AM650` (128 bits, Dynamic AES) (button code `0` emulates `hidden button` option on the remote) - BFT Mitto `433.92MHz` `AM650` (64 bits, Dynamic, KeeLoq based with Seed) - CAME Atomo `433.92MHz, 868MHz` `AM650` (62 bits, Dynamic) - CAME TWEE `433.92MHz` `AM650` (54 bits, Static) @@ -46,7 +46,7 @@ That list is only for default SubGHz app, apps like *Weather Station* have their - Nice One `433.92MHz` `AM650` (72 bits, Dynamic) - Revers RB2 (Реверс РБ-2 (М)) `433.92MHz` `AM650` (64 bits, Static) - Roger `433.92MHz` `AM650` (28 bits, Static) -- V2 Phoenix (Phox) `433.92MHz` `AM650` (52 bits, Dynamic) +- V2 Phoenix (Phox) `433.92MHz` `AM650` (52 bits, Dynamic) (receivers have option to enable Static mode, making them ignore rolling part of the key) - Marantec `433.92MHz, 868MHz` `AM650` (49 bits, Static) - Marantec24 `868MHz` `AM650` (24 bits, Static) - Somfy Keytis `433.92MHz, 868MHz` `AM650` (80 bits, Dynamic) diff --git a/lib/subghz/protocols/phoenix_v2.c b/lib/subghz/protocols/phoenix_v2.c index 1f2731f54..e6d5d6710 100644 --- a/lib/subghz/protocols/phoenix_v2.c +++ b/lib/subghz/protocols/phoenix_v2.c @@ -91,6 +91,8 @@ void subghz_protocol_encoder_phoenix_v2_free(void* context) { free(instance); } +static uint8_t v2_phoenix_counter_mode = 0; + // Pre define functions static uint16_t subghz_protocol_phoenix_v2_encrypt_counter(uint64_t full_key, uint16_t counter); static void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneric* instance); @@ -252,18 +254,30 @@ static bool btn = subghz_protocol_phoenix_v2_get_btn_code(); // Reconstruction of the data - // Check for OFEX (overflow experimental) mode - if(furi_hal_subghz_get_rolling_counter_mult() != -0x7FFFFFFF) { - // standart counter mode. PULL data from subghz_block_generic_global variables - if(!subghz_block_generic_global_counter_override_get(&instance->generic.cnt)) { - // if counter_override_get return FALSE then counter was not changed and we increase counter by standart mult value - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + if(v2_phoenix_counter_mode == 0) { + // Check for OFEX (overflow experimental) mode + if(furi_hal_subghz_get_rolling_counter_mult() != -0x7FFFFFFF) { + // standart counter mode. PULL data from subghz_block_generic_global variables + if(!subghz_block_generic_global_counter_override_get(&instance->generic.cnt)) { + // if counter_override_get return FALSE then counter was not changed and we increase counter by standart mult value + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } + } else { + if((instance->generic.cnt + 0x1) > 0xFFFF) { instance->generic.cnt = 0; + } else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) { + instance->generic.cnt = 0xFFFE; } else { - instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + instance->generic.cnt++; } } - } else { + } else if(v2_phoenix_counter_mode == 1) { + // Mode 1 (ofex like) + // 0000 / 0001 / FFFE / FFFF if((instance->generic.cnt + 0x1) > 0xFFFF) { instance->generic.cnt = 0; } else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) { @@ -271,6 +285,14 @@ static bool } else { instance->generic.cnt++; } + } else { + // Mode 2 (0 to 4) + // 0x0000 / 0x0001 / 0x0002 / 0x0003 / 0x0004 + if(instance->generic.cnt >= 0x0004) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt++; + } } uint64_t local_data_rev = subghz_protocol_blocks_reverse_key( @@ -326,6 +348,18 @@ SubGhzProtocolStatus flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + uint32_t tmp_counter_mode; + if(flipper_format_read_uint32(flipper_format, "CounterMode", &tmp_counter_mode, 1)) { + v2_phoenix_counter_mode = (uint8_t)tmp_counter_mode; + } else { + v2_phoenix_counter_mode = 0; + } + subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic); if(!subghz_protocol_encoder_phoenix_v2_get_upload(instance)) { @@ -333,6 +367,11 @@ SubGhzProtocolStatus break; } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + 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; @@ -582,10 +621,25 @@ SubGhzProtocolStatus subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderPhoenix_V2* instance = context; - return subghz_block_generic_deserialize_check_count_bit( + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + + ret = subghz_block_generic_deserialize_check_count_bit( &instance->generic, flipper_format, subghz_protocol_phoenix_v2_const.min_count_bit_for_found); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + return SubGhzProtocolStatusError; + } + + uint32_t tmp_counter_mode; + if(flipper_format_read_uint32(flipper_format, "CounterMode", &tmp_counter_mode, 1)) { + v2_phoenix_counter_mode = (uint8_t)tmp_counter_mode; + } else { + v2_phoenix_counter_mode = 0; + } + return ret; } void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* output) {