From bf812630abab571653b716fc1739632b6c302f86 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Thu, 1 Jan 2026 22:45:21 +0300
Subject: [PATCH 1/9] Handle PPS request in ISO14443-4 layer
by WillyJL
---
lib/nfc/helpers/iso14443_4_layer.c | 39 ++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/lib/nfc/helpers/iso14443_4_layer.c b/lib/nfc/helpers/iso14443_4_layer.c
index 1d76f4bf3..4a92956a9 100644
--- a/lib/nfc/helpers/iso14443_4_layer.c
+++ b/lib/nfc/helpers/iso14443_4_layer.c
@@ -35,6 +35,14 @@
#define ISO14443_4_BLOCK_PCB_S_CID_MASK (1U << ISO14443_4_BLOCK_PCB_R_CID_OFFSET)
#define ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_MASK (3U << ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_OFFSET)
+#define ISO14443_4_BLOCK_PPS_START (0xD0)
+#define ISO14443_4_BLOCK_PPS_START_MASK (0xF0)
+
+#define ISO14443_4_BLOCK_PPS_0_HAS_PPS1 (1U << 4)
+
+#define ISO14443_4_BLOCK_PPS_1_DSI_MASK (3U << 2)
+#define ISO14443_4_BLOCK_PPS_1_DRI_MASK (3U << 0)
+
#define ISO14443_4_BLOCK_CID_MASK (0x0F)
#define ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, mask) (((pcb) & (mask)) == (mask))
@@ -58,6 +66,9 @@
#define ISO14443_4_LAYER_NAD_NOT_SUPPORTED ((uint8_t) - 1)
#define ISO14443_4_LAYER_NAD_NOT_SET ((uint8_t) - 2)
+#define ISO14443_4_BLOCK_PPS_IS_START(pps) \
+ ((pps & ISO14443_4_BLOCK_PPS_START_MASK) == ISO14443_4_BLOCK_PPS_START)
+
struct Iso14443_4Layer {
uint8_t pcb;
uint8_t pcb_prev;
@@ -65,6 +76,7 @@ struct Iso14443_4Layer {
// Listener specific
uint8_t cid;
uint8_t nad;
+ bool can_pps;
};
static inline void iso14443_4_layer_update_pcb(Iso14443_4Layer* instance, bool toggle_num) {
@@ -93,6 +105,7 @@ void iso14443_4_layer_reset(Iso14443_4Layer* instance) {
instance->cid = ISO14443_4_LAYER_CID_NOT_SUPPORTED;
instance->nad = ISO14443_4_LAYER_NAD_NOT_SUPPORTED;
+ instance->can_pps = true;
}
void iso14443_4_layer_set_i_block(Iso14443_4Layer* instance, bool chaining, bool CID_present) {
@@ -234,6 +247,32 @@ Iso14443_4LayerResult iso14443_4_layer_decode_command(
BitBuffer* block_data) {
furi_assert(instance);
+ uint8_t ppss = bit_buffer_get_byte(input_data, 0);
+ if(ISO14443_4_BLOCK_PPS_IS_START(ppss)) {
+ if(instance->can_pps) {
+ const uint8_t cid = ppss & ISO14443_4_BLOCK_CID_MASK;
+ if(instance->cid != ISO14443_4_LAYER_CID_NOT_SUPPORTED && cid != instance->cid) {
+ return Iso14443_4LayerResultSkip;
+ }
+ instance->can_pps = false;
+ uint8_t pps0 = bit_buffer_get_byte(input_data, 1);
+ if(pps0 & ISO14443_4_BLOCK_PPS_0_HAS_PPS1) {
+ uint8_t pps1 = bit_buffer_get_byte(input_data, 2);
+ uint8_t dsi = pps1 & ISO14443_4_BLOCK_PPS_1_DSI_MASK;
+ uint8_t dri = pps1 & ISO14443_4_BLOCK_PPS_1_DRI_MASK;
+ // TODO: do we need to change bit timings somehow? DRI and DSI mean different bit timing divisors
+ UNUSED(dsi);
+ UNUSED(dri);
+ }
+ bit_buffer_reset(block_data);
+ bit_buffer_append_byte(block_data, ppss);
+ return Iso14443_4LayerResultSend;
+ } else {
+ return Iso14443_4LayerResultSkip;
+ }
+ }
+ instance->can_pps = false;
+
uint8_t prologue_len = 0;
instance->pcb = bit_buffer_get_byte(input_data, prologue_len++);
From cf51ce866f8ed858f0d707b1792450466481259b Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Thu, 1 Jan 2026 22:45:59 +0300
Subject: [PATCH 2/9] upd changelog
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a8e401c0..94cfe2c43 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
## Main changes
- Current API: 87.1
+* NFC: Handle PPS request in ISO14443-4 layer (by @WillyJL)
* Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
## Other changes
* Nothing yet.
From 0490c316986276ca67bb846b384f3aa53ec0c405 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Thu, 1 Jan 2026 22:50:02 +0300
Subject: [PATCH 3/9] clangd config update
by WillyJL
---
.clangd | 4 ++++
CHANGELOG.md | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/.clangd b/.clangd
index 12e13751d..70a1bf4bb 100644
--- a/.clangd
+++ b/.clangd
@@ -4,6 +4,10 @@ CompileFlags:
- -Wno-format
Remove:
- -mword-relocations
+ CompilationDatabase: "./build/latest"
+
+Completion:
+ HeaderInsertion: Never
Diagnostics:
ClangTidy:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 94cfe2c43..25c1e5036 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,7 @@
* NFC: Handle PPS request in ISO14443-4 layer (by @WillyJL)
* Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
## Other changes
-* Nothing yet.
+* Clangd: Add clangd parameters in IDE agnostic config file (by @WillyJL)
#### Known NFC post-refactor regressions list:
- Mifare Mini clones reading is broken (original mini working fine) (OFW)
From ab86d3325e8361154b90aa2a1dde7162205bd6ba Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Mon, 5 Jan 2026 21:13:43 +0300
Subject: [PATCH 4/9] subghz: add fsk12k deviation, cardin s449 full support
+ thanks @zero-mega :))
---
.../cc1101_ext/cc1101_ext_interconnect.c | 4 +
.../main/subghz/helpers/subghz_custom_event.h | 1 +
.../main/subghz/helpers/subghz_gen_info.c | 10 ++
.../main/subghz/helpers/subghz_txrx.c | 2 +
.../resources/subghz/assets/keeloq_mfcodes | 133 +++++++++---------
.../subghz/scenes/subghz_scene_set_type.c | 1 +
applications/main/subghz/subghz_cli.c | 2 +
.../main/subghz/subghz_last_settings.c | 2 +-
.../main/subghz/subghz_last_settings.h | 2 +-
.../js_app/modules/js_subghz/js_subghz.c | 2 +
.../file_formats/SubGhzFileFormats.md | 1 +
lib/subghz/blocks/generic.c | 2 +
lib/subghz/devices/cc1101_configs.c | 71 ++++++++++
lib/subghz/devices/cc1101_configs.h | 1 +
.../cc1101_int/cc1101_int_interconnect.c | 3 +
lib/subghz/devices/preset.h | 1 +
lib/subghz/protocols/keeloq.c | 6 +-
lib/subghz/subghz_setting.c | 2 +
targets/f7/api_symbols.csv | 3 +-
19 files changed, 179 insertions(+), 70 deletions(-)
diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c
index eef92dbe8..6cf9f90b1 100644
--- a/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c
+++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c
@@ -43,6 +43,10 @@ static void subghz_device_cc1101_ext_interconnect_load_preset(
subghz_device_cc1101_ext_load_custom_preset(
subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs);
break;
+ case FuriHalSubGhzPreset2FSKDev12KAsync:
+ subghz_device_cc1101_ext_load_custom_preset(
+ subghz_device_cc1101_preset_2fsk_dev12khz_async_regs);
+ break;
case FuriHalSubGhzPreset2FSKDev476Async:
subghz_device_cc1101_ext_load_custom_preset(
subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs);
diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h
index 41e704ea5..33a70fd57 100644
--- a/applications/main/subghz/helpers/subghz_custom_event.h
+++ b/applications/main/subghz/helpers/subghz_custom_event.h
@@ -103,6 +103,7 @@ typedef enum {
SetTypeJCM_433_92,
SetTypeNovoferm_433_92,
SetTypeHormannEcoStar_433_92,
+ SetTypeCardinS449_433FM,
SetTypeFAACRCXT_433_92,
SetTypeFAACRCXT_868,
SetTypeGeniusBravo433,
diff --git a/applications/main/subghz/helpers/subghz_gen_info.c b/applications/main/subghz/helpers/subghz_gen_info.c
index fe5cd0e8a..5f1021ddd 100644
--- a/applications/main/subghz/helpers/subghz_gen_info.c
+++ b/applications/main/subghz/helpers/subghz_gen_info.c
@@ -643,6 +643,16 @@ void subghz_scene_set_type_fill_generation_infos(GenInfo* infos_dest, SetType ty
.keeloq.cnt = 0x03,
.keeloq.manuf = "EcoStar"};
break;
+ case SetTypeCardinS449_433FM:
+ gen_info = (GenInfo){
+ .type = GenKeeloq,
+ .mod = "FM12K",
+ .freq = 433920000,
+ .keeloq.serial = (key & 0x000FFFFF),
+ .keeloq.btn = 0x02,
+ .keeloq.cnt = 0x03,
+ .keeloq.manuf = "Cardin_S449"};
+ break;
case SetTypeFAACRCXT_433_92:
gen_info = (GenInfo){
.type = GenKeeloq,
diff --git a/applications/main/subghz/helpers/subghz_txrx.c b/applications/main/subghz/helpers/subghz_txrx.c
index 8e00bd6af..8abf373f4 100644
--- a/applications/main/subghz/helpers/subghz_txrx.c
+++ b/applications/main/subghz/helpers/subghz_txrx.c
@@ -116,6 +116,8 @@ const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset
preset_name = "AM650";
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) {
preset_name = "FM238";
+ } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev12KAsync")) {
+ preset_name = "FM12K";
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) {
preset_name = "FM476";
} else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) {
diff --git a/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes b/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes
index 62135c0b9..b146d0ded 100644
--- a/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes
+++ b/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes
@@ -1,69 +1,70 @@
Filetype: Flipper SubGhz Keystore File
Version: 0
Encryption: 1
-IV: 46 75 72 72 79 20 52 6F 63 6B 65 74 21 21 21 30
-05176EEFAC177FE261FE3EB5C8E103BE7CF9F2FEB32BDD6BB63D22EE9C17B9D2
-B645E3CAC0D5E26891249D326BCEB09850E4FB8F8E86A466E97E83437A9E0041
-AA4255FFA1ADE8FB840F80A93F8F1A2D1E39051131D24DE7258D66A8CF2066CF
-13ACA390FD5254B024084D5D1F41B8DDF5304FF00C3C85A9C26CD13A7A268654
-4CFBF498D5E2C85496985E83D91B0F4229A925E16A90C6712750032C3699EE0AA5D04123E579B6121573FC61766E89AD
-93DADC2AE4235470E171E0E85D24D04A84C37187284C38D1CBB48666FDA8CD6C
-DB13D8CCC0CB07685F29F33AE07DA2FD14C2AE4F4D001DB88465D5CFE8CFDAA9
-E51CD1B5074B63D26E274218A0AB3B2E435454EE094DCA5679F35477658A72F9
-10AFD5FD9C296E67EDD9504A60BA9EF84556F40213DEC4DE44F99B088BCC6A57
-EF7AA55F6A473DE093D648240D5FCEB05F8B3295DC37B3E83239A4AF320CD688
-A22892E71B9D0D7FAF92B27C724E76C4A6824DBE5F083F1006D11E42D153C4AC98D0A11C6A8D62F5921A24ECC7437485
-7A25416E390D81DA68A59C3BA30D4B7FC8269B5E0DAF77CA3A857B6F478A050585918485AEE72D375F02D177CB296E31
-94004BA0BB1E47965E60025949EF4CC2738C463F57C97FD2A89C76CCCDEA5397
-111CB1C19863A0165521D974F838CE718DA07948A8D9A8A7490E75032A62ECA2
-17B6E27C69FA002F6CF23D719DFE595140BEFA5083D12E774CF89E2CED53D68D
-73311E0FF8ABB3E9461AD14A4F52791647A50E2102D3B74188A73C35BC14EB55
-54E15840A6A6DCA85275E38E4218EE2B539E9E468E24C49428DA363C955C5FC81ACEE79EEB941B83EE4147A0817043BD
-7D0FBB417B99B3C6AB18C7B2DC82582D2DCD1E10515028874E73254188F7FEE9
-3F6E89BBCC133B85945234A8201539ECD8796909CC81FE67673F8DE1ECA63045
-39554C0DC1C3694FAAFF65537FF710D9593B7B461E011FC39D014F253F0432533A40276D8259AFD8C957A378237D574F
-E60F6CD7063B85F0F20ACB7E7A42B03DE4A9F6CCA54CB7F036AFA23A27D3E9E006BD523E5356260AA78206D9276E6E57
-9EB252EDA9352B966EC4F053D5B013772361D2AD4B217EF33F46A5CEC97A00F3
-AA6773E79BC6D76314BB523FDF203358E01ECB2BBCF3B5DD1EBD043663C74B05
-29B29A50F3F27F4D8C7B0FADA98CC004A7871078DAD1CBAC4846862C3DF82E02
-6E3A479D4334FF05606899B0383116125056A316621B279F904A02B842918C59
-3991732015F4A213E9912E34AC92515D88010C07DA0B118AD6F64A05DC38D2C5
-550B1866F7493C75812DF85DDADC38AF21D9B58189E4EE99A021328523881A9D
-77960CA031D28362586100F17DF94FF4E7D6EFAFAF23952887F9DF0507825A99
-01E6FC89E97B7729BF4D1ED8041F69005181BF3639F939C5833B009E96B9F2F7
-D1CC7C536706ECFC5826C8933135D2B110996F1CB13388A702B8453DA40E40AD
-B64D2F1E1A80E6DAB92283A512B40DB7FFC519F394AA94CC86C8532F69949723
-6399409A0AC0298DEDA76037C83042FC0870132CFF7F82E54AD0966BE16AC882
-D310536FA78F95BB0B408676990AA937117717BADE9D3B975C0ECE10FB586A1B
-A8149C0581DCC291D037E96EF321DB6214BD7CB25F1696226A9FE750AA23B334
-BA3BEBD564D8F571202CD6FE89BC33F89C8E01C03AE0814F2BEF37C33CE874B4
-88CD81AC7605A7F6EFF85FD62C65E0C9945335CFC085B92B27B69648C6E5BF6B
-8057C7CB5071DFFFAE4804FD9EC1EC1D3F54D06514906A34B17F6B6CB45A9D473992DF6BC8A9F9E146E39D6163209CC6
-9ABC8814C8FD1AB254374150177616F5C7B43049473C84329BEC855578B96002
-8BCA39A498B00245C71D94E3160CEE8ACA5BEB18AE0AD64A385AFCC018E99744
-5AD75C51CA5AE5FA9BBC6A41576C745F265CC28FC4DA2AD230B6692CF151FD61
-E86092E04CD72D874A92DE838035E811E75E411049C0A7BD0FE2AA9C802BE5AB
-CE70ADB22E85747FDC064F0B5974385CD57D41D376CE1C7490C1BEC8A3FC5A7A
-8F096E0A11682DB315825213D3DB5D725555C1CDF444169EB919E47E0F0FA6F7
-AD9C9A694D807BA77E5A54B248A88B55000757203D931506255BF8F4215C00D3
-F0E804B6C6B6E91916CB73EB44FB2D1992400BC90ED8B22DF5D038317588341207D74E08C00E529DF2CF2A64F2C7C0FF
-72212FCEED35E9C3A176B67DCDB84B284F4DFDCD0ECE8D3F6089C58C2B8A616C
-000F9F746BFB47FC10B23E3F08C2A84BCB3870D0C5AE974472849699884BC929
-7B8F9AB04E5F86D6DDCF6164A25EA927788A03F57977FC5C55E1D565279B09C4
-0E9CDCD07D1D4F1429E59F81B524960A75F19A464798C7E822E52728AC83784A
-F2DE2B108A1476BB6F85DD3CCB0F0527627B45179092BA7A56D5971490E3875C
-7F307358D988FEA12648739F58DD249EBDF0B1C44B73BA547C50EB576C071DAE
-2DFBA988592CEF3B62A76183DBA727E734359B89F53AFF3160441EF8709FC633
-57F7DC38DDC87C19CE956BC44C638DEF34D814A7BAB0AC8AD61855143FD984FD
-A8AADB687251FA6AC2BBC8EF1E3FA621893293DFBD8C1D07971BF82F22A00DC3
-65AEA1EE34E316C769E551AC2309D07FC2ED92EA044674E3A99CD7B543C730EB
-968ECC790E5590E7EB22AFD3546C28F4EB87EA4CEE35F72DDFE7153F74611EAA
-0F937930D4E1BDF0B729277CF94A47064BCB959938C70CDB3AC3C65DA68DA1FB
-A8AB66375D59E112104CD81B819D618BE43D6A6F159BAD35583653EF3547D25D
-A81D5DE2102F05D50750DC37C26E9C9502FA89EF98A2EB1EA546EE48C628E9C4
-EAFDE0A8936AF8EF718027937BC17CEF691E570996B403CF4762240D267EB305
-C48686348F0A94B07BC60AB825C1A0791C20DBBDD7DAE0ED47E8A7FBD9334EACF8E33DCEC36963E87929260DF769520B
-493D53BD7BB2B3E081AE793A3BADB3AB0F33C95B83677715D6DE2922F2BEC892
-63FFD3D8CAB980E45D49253A69C99A6813CBE6013992EFBC862173BAD0E26373
-2EF88F43C5A76EC87E02B780585B10957F4EA386F96710FAB98BC2C1E214DBFA
-A021CFA0E72AADFD75BC67FBE9345082B0A8B31782E933E81196F84B1797D83E8B2F81E1CF5C3F026D11B9DFC95222E2
+IV: 41 20 46 75 72 72 79 20 48 41 4C 21 4F 77 4F 21
+2CAD19E0C7E482D138AD8EC452C5D9175534F5FD5B8DAB0FF2082A75A9410C60
+87341133DBC22CC39E1952466C1FA6F7DA4215FB3B9D6975CDA16CD0211AEC7E
+8D60AA06878EA9EE24D3F2CDC0166373E3CD131CAD016976758B4F5F0DCC4A36
+E455A4EF9016D5682815F4714A678833CD09D6CED6D9D3D796CC700B0FFB79CC
+FD5A46DF885486EE89F867BF629B63940AE8A1D0695BE93FCE4385AF523F8809145A3381F2F3F578A38FD40B2546A423
+84F8D5D63DC863490A0B36BC6102FFAA4D0852DE5E92B567B39F0C6F90454029
+8D1A3A7BE24A6F96B3401944435FF5441628B3E60C1CA29F54650BF5912C7E00
+4F2A1A51AC845F91CC8F0AC693CC8D95C4FCF2524317F2576BF1B426332257F2
+6482B6BC4C59F14FC414C4A1E8DEE687E3263FC575646287CF07CE21D5356CB9
+B98C8FE2030F448F80F075EC844311F503B8B4A1048F3EF761AFC695BED8767D
+426044078972749851904C762094DC45D8E6DEEEB36232A1489BFBEDC15F6EA0308756673C7494241BCA9BF6216B0C63
+7446FBF891E79C130550E5AEC07BCD6605AB16A16123C7F58B34BA8243567CD29EFBFF44730F36E86A17B97B4CA86BCD
+3F76A9C0273C7A344244703F24F625091B1F599A2DDFBD698F33CF7BF7765BD4
+AC491FF039EE8D1548F127AA11863B8CD6B67C37F5DE55D810C04AA1A0207D7D
+E14027E544A3B867402F1FD496AD8E173CB6FC55053C3846142882D746C8A3BA
+182599DE46C923C4A2A8B1CB07165A8FD5F571BE95A32DA4D517B99378AF618C
+5AE341DCF8BFDBA55301555D3300946352CA174F5B0DF6EB239EE997C0E093D1930F6A75996B149199DFFE78B25F5BD5
+A495E957DBE11937C967CEBF99BCFE85CE2E2FFBBD6952352EC7E59FD9ABDC2D
+F1F924BD569DD0E6AB7EEC7C880EDE0CE53757A2A183EED3674C10FDE4ADFF17
+B6E32951F4C886371968E5BED5711C05648E87F36FC397E2455B8DD5AFF3865D3BADADF7626D071F792712F6B0FD115B
+BC88789A8C037EBCA6F9F6A0FC616D3512474AED1C96270845171CF76F7CDA6D4D2CD42CB8463CE706398B0A9DBEA87A
+4A8686B20E1339C81AAE8BBF1C9EABA5BEB61D0A394F314958546FA14A67DEE7
+CA71F413EC992ACFEB03EE1760BE683050FF4D1E1B65ACF9E3A4964F2B3C2B01
+0E05B3BD4854604B6FB82900A13051C0377E85C49A293A39C2A8C75B904D8102
+C9EDE274DD3C5B6DF276565FEE81ED060F0C3BD0CB72EA3E49E9595FAFE77F5C
+A33A4B9AD451ACDB5EC4830EFE421AE8929E024108C34343D3D55F098CDE5B0B
+9C5C17D4FB32DDD18D88A2B358786E9A941931BE9D645B379412F2E187020C14
+2626DD2FCE983EA3DEAB2FE2A5F378250F1644BE24BDCC2C5FAFDBA68D4F1872
+9F1E27964102BFCEFBF6F5FE7DA8C3B102E97ED0CEE074625175FB02674D5EF2
+7E1745495E213781ABCA814552046470EA41F0EBE8BDD64592DDFA7319AFCD69
+EB6C3B1F4265C71A0DD74847588A89CCF57539CF20711233A221C9BE1F9EB6C9
+4C1A2768E44F9DD2E4AEE9C18DE5449B92CD4DFE5BDA7F65F5A654C86544B2E1
+4F1835CDBDA4128324F4B92E709201502852892699EC0C84B1E4A4B841542C3C
+E0EBE4B259B61F05475FFE07EAC8E2A9D013D4471DC7CF3481E1334006F7FB02
+20342B2D84CD3936DEA5058CE07F3F3DB6178C6BF0AC72CF8F807503B3AC8F88
+DC775129215670CD8B81A5335190F6544687D38A70E758348578B0B3A1389300
+208F0D588404D9BDD6E310F16B7460BDB2A8B284CA6E59E24F5FACFD55D48AACC5D86C886A9EF5F06DC2AADE76EFF35F
+5CD70EFEAB4B5345531102A0DA1453F857AC93F3456E81C305111626CE084627
+341E61A3AB6BA3F71F5215905149E2D3E84260CC77C068C92D71C6DD9067F633
+0342C01D966663D7542F2D5D7635758EC65CDDE5E7FD5884846D675D06C59363
+2E9F77AD044F9C8C23CA239E71303D6682A2A86C3B4D44831DB4B1BC377043CD
+3A11CBA6703808A36911F036E9042198950C2C82269D892F5C87DFD32C101BCA
+6E75A69923DFC22957705A624368CFB657B77C11D7F831704BCC76A87377AB26
+21F70436CF950862D306829B542E307EF327F23870D4FBF42869A3BF0891F9DB
+4A00DFB119C12D86B953BEC9F41DFB3D4AE3A9BE984CD8C3606CC1ED4B9020ED82922BCED5AE8A2A88DD2995A0D28EFE
+12E2B40E49B0FC157203286B39ED852EDC2B444527472A8C07E78A70722DB825
+91A106AF60685D56D19D2E58C9F902F2B3B80A1CFCBC1B84316FBAF69DB47730
+58B5AF1BB0EF318CC3026041A3403B025980FB4DE06C3F2B804800B8578295B7
+FA7C4461C2F0BECADB414694B04E7A89BC99433A635E2905DC3A40A947EC9846
+DF356AFF3212088F223EC32E5FE9D5D73701F25B2075BC50E0F0B45195F28365
+2D099AE3FB0AE7B92976E02E2F01A9D6821D7C15CC04DCA881E05D50BC46EB11
+1FE9F8D29A77CD399B068A8AD890ECB85A957438855F85223B16E3B83A655213
+07DFA64C22CF6DB1E36D16F763BDD822AF3991BDE07C30B4F4084BD13CDEF012
+3BA563F522243D807151BC2EF1B524C174058888D9DE852AA0E9C15F1EA22950
+475979BC2EA05EE2443F65F2372E36D780A34EC8AC1D70BB2480FF519C6C84C1
+7275E88766DCA0EB3725641E950EEC094129361D280AE963D89A9B5403136B06
+65202C209229A36A2B80ED69DEEA285A7273BAA007365AC2E7F438C7AE428BBA
+C83ABA33828918FB80D92D8217BB8AE6B5AA4017D0CD7F37C39925F7F96B9BD2
+AAEF1B708581570F87D1FB016FE23FF2804CB6942E576C40165D9F81A4E7AB04
+F7434FBA076E677D71A584341A77D54BFEF4DEBDCADA67D23DB50AA5902DA4EE
+5480CB40AB7CA50ABA5EED094281282D5302C38E5EF0B8AF7AE51B5C702574F2
+1C90B716E8AF97DEADB0199C4BBDE7E3669FB4D6794A27F716D523DDFC74905ECEB64509C073405B38A271B7FBFEA3DD
+36036999661471644E50D56532DA3D82F2713035BDBDA399E6B121B15E2FDCD5
+DCD61BB556CD42849B99C7A1916005305E57723555CD24F6F1D67D30224A275F
+4E3662D52FB2396DCC0D76E7ECFFECF5E581492FA520D8427F772C61435FF925
+7F33462CB81E81DDF8284A6EA121401EEA6301277ABDF2D5C39EDB818AE973F0D51527BA6146C3178624F61E38F220D2
diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c
index 8ca5b95ab..731da6f3f 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_type.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_type.c
@@ -50,6 +50,7 @@ static const char* submenu_names[SetTypeMAX] = {
[SetTypeJCM_433_92] = "KL: JCM Tech 433MHz",
[SetTypeNovoferm_433_92] = "KL: Novoferm 433MHz",
[SetTypeHormannEcoStar_433_92] = "KL: Hor. EcoStar 433MHz",
+ [SetTypeCardinS449_433FM] = "KL: Cardin S449 433MHz",
[SetTypeFAACRCXT_433_92] = "KL: FAAC RC,XT 433MHz",
[SetTypeFAACRCXT_868] = "KL: FAAC RC,XT 868MHz",
[SetTypeGeniusBravo433] = "KL: Genius Bravo 433MHz",
diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c
index 88b5f7f0d..2c6c6b908 100644
--- a/applications/main/subghz/subghz_cli.c
+++ b/applications/main/subghz/subghz_cli.c
@@ -574,6 +574,8 @@ static FuriHalSubGhzPreset subghz_cli_get_preset_name(const char* preset_name) {
preset = FuriHalSubGhzPresetOok650Async;
} else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev238Async")) {
preset = FuriHalSubGhzPreset2FSKDev238Async;
+ } else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev12KAsync")) {
+ preset = FuriHalSubGhzPreset2FSKDev12KAsync;
} else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev476Async")) {
preset = FuriHalSubGhzPreset2FSKDev476Async;
} else if(!strcmp(preset_name, "FuriHalSubGhzPresetCustom")) {
diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c
index 6ffdc858e..0a4783a23 100644
--- a/applications/main/subghz/subghz_last_settings.c
+++ b/applications/main/subghz/subghz_last_settings.c
@@ -149,7 +149,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
instance->frequency = SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY;
}
- if(instance->preset_index > 3) {
+ if(instance->preset_index > 4) {
instance->preset_index = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
}
}
diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h
index c0559e4ec..78de5e51c 100644
--- a/applications/main/subghz/subghz_last_settings.h
+++ b/applications/main/subghz/subghz_last_settings.h
@@ -8,7 +8,7 @@
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER (-93.0f)
// 1 = "AM650"
-// "AM270", "AM650", "FM238", "FM476",
+// "AM270", "AM650", "FM238", "FM12K", "FM476",
#define SUBGHZ_LAST_SETTING_DEFAULT_PRESET 1
#define SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY 433920000
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2
diff --git a/applications/system/js_app/modules/js_subghz/js_subghz.c b/applications/system/js_app/modules/js_subghz/js_subghz.c
index f7065d38c..ae3e1f60d 100644
--- a/applications/system/js_app/modules/js_subghz/js_subghz.c
+++ b/applications/system/js_app/modules/js_subghz/js_subghz.c
@@ -30,6 +30,8 @@ static FuriHalSubGhzPreset js_subghz_get_preset_name(const char* preset_name) {
preset = FuriHalSubGhzPresetOok650Async;
} else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev238Async")) {
preset = FuriHalSubGhzPreset2FSKDev238Async;
+ } else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev12KAsync")) {
+ preset = FuriHalSubGhzPreset2FSKDev12KAsync;
} else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev476Async")) {
preset = FuriHalSubGhzPreset2FSKDev476Async;
} else if(!strcmp(preset_name, "FuriHalSubGhzPresetCustom")) {
diff --git a/documentation/file_formats/SubGhzFileFormats.md b/documentation/file_formats/SubGhzFileFormats.md
index 80047faf7..1ff1f830d 100644
--- a/documentation/file_formats/SubGhzFileFormats.md
+++ b/documentation/file_formats/SubGhzFileFormats.md
@@ -39,6 +39,7 @@ Built-in presets:
- `FuriHalSubGhzPresetOok270Async` — On/Off Keying, 270kHz bandwidth, async(IO throw GP0)
- `FuriHalSubGhzPresetOok650Async` — On/Off Keying, 650kHz bandwidth, async(IO throw GP0)
- `FuriHalSubGhzPreset2FSKDev238Async` — 2 Frequency Shift Keying, deviation 2kHz, 270kHz bandwidth, async(IO throw GP0)
+- `FuriHalSubGhzPreset2FSKDev12KAsync` — 2 Frequency Shift Keying, deviation 12kHz, 270kHz bandwidth, async(IO throw GP0)
- `FuriHalSubGhzPreset2FSKDev476Async` — 2 Frequency Shift Keying, deviation 47kHz, 270kHz bandwidth, async(IO throw GP0)
### Transceiver Configuration Data {#transceiver-configuration-data}
diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c
index b659e7947..57fbb580f 100644
--- a/lib/subghz/blocks/generic.c
+++ b/lib/subghz/blocks/generic.c
@@ -12,6 +12,8 @@ void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* p
preset_name_temp = "FuriHalSubGhzPresetOok650Async";
} else if(!strcmp(preset_name, "FM238")) {
preset_name_temp = "FuriHalSubGhzPreset2FSKDev238Async";
+ } else if(!strcmp(preset_name, "FM12K")) {
+ preset_name_temp = "FuriHalSubGhzPreset2FSKDev12KAsync";
} else if(!strcmp(preset_name, "FM476")) {
preset_name_temp = "FuriHalSubGhzPreset2FSKDev476Async";
} else {
diff --git a/lib/subghz/devices/cc1101_configs.c b/lib/subghz/devices/cc1101_configs.c
index a75842589..971c3cb28 100644
--- a/lib/subghz/devices/cc1101_configs.c
+++ b/lib/subghz/devices/cc1101_configs.c
@@ -220,6 +220,77 @@ const uint8_t subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs[] = {
0x00,
};
+const uint8_t subghz_device_cc1101_preset_2fsk_dev12khz_async_regs[] = {
+
+ /* GPIO GD0 */
+ CC1101_IOCFG0,
+ 0x0D, // GD0 as async serial data output/input
+
+ /* Frequency Synthesizer Control */
+ CC1101_FSCTRL1,
+ 0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz
+
+ /* Packet engine */
+ CC1101_PKTCTRL0,
+ 0x32, // Async, continious, no whitening
+ CC1101_PKTCTRL1,
+ 0x04,
+
+ // // Modem Configuration
+ CC1101_MDMCFG0,
+ 0x00,
+ CC1101_MDMCFG1,
+ 0x02,
+ CC1101_MDMCFG2,
+ 0x04, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized)
+ CC1101_MDMCFG3,
+ 0x83, // Data rate is 4.79794 kBaud
+ CC1101_MDMCFG4,
+ 0x67, //Rx BW filter is 270.833333 kHz
+ CC1101_DEVIATN,
+ 0x30, //Deviation ~12 kHz
+
+ /* Main Radio Control State Machine */
+ CC1101_MCSM0,
+ 0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us)
+
+ /* Frequency Offset Compensation Configuration */
+ CC1101_FOCCFG,
+ 0x16, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off
+
+ /* Automatic Gain Control */
+ CC1101_AGCCTRL0,
+ 0x91, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary
+ CC1101_AGCCTRL1,
+ 0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET
+ CC1101_AGCCTRL2,
+ 0x07, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB
+
+ /* Wake on radio and timeouts control */
+ CC1101_WORCTRL,
+ 0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours
+
+ /* Frontend configuration */
+ CC1101_FREND0,
+ 0x10, // Adjusts current TX LO buffer
+ CC1101_FREND1,
+ 0x56,
+
+ /* End load reg */
+ 0,
+ 0,
+
+ // 2fsk_async_patable[8]
+ 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+};
+
const uint8_t subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs[] = {
/* GPIO GD0 */
diff --git a/lib/subghz/devices/cc1101_configs.h b/lib/subghz/devices/cc1101_configs.h
index 5b96b4a1a..86321edab 100644
--- a/lib/subghz/devices/cc1101_configs.h
+++ b/lib/subghz/devices/cc1101_configs.h
@@ -8,6 +8,7 @@ extern "C" {
extern const uint8_t subghz_device_cc1101_preset_ook_270khz_async_regs[];
extern const uint8_t subghz_device_cc1101_preset_ook_650khz_async_regs[];
extern const uint8_t subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs[];
+extern const uint8_t subghz_device_cc1101_preset_2fsk_dev12khz_async_regs[];
extern const uint8_t subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs[];
extern const uint8_t subghz_device_cc1101_preset_msk_99_97kb_async_regs[];
extern const uint8_t subghz_device_cc1101_preset_gfsk_9_99kb_async_regs[];
diff --git a/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c
index 284c717fd..be33b8778 100644
--- a/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c
+++ b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c
@@ -38,6 +38,9 @@ static void subghz_device_cc1101_int_interconnect_load_preset(
case FuriHalSubGhzPreset2FSKDev238Async:
furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs);
break;
+ case FuriHalSubGhzPreset2FSKDev12KAsync:
+ furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_2fsk_dev12khz_async_regs);
+ break;
case FuriHalSubGhzPreset2FSKDev476Async:
furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs);
break;
diff --git a/lib/subghz/devices/preset.h b/lib/subghz/devices/preset.h
index 8716f2e23..6f67594af 100644
--- a/lib/subghz/devices/preset.h
+++ b/lib/subghz/devices/preset.h
@@ -6,6 +6,7 @@ typedef enum {
FuriHalSubGhzPresetOok270Async, /**< OOK, bandwidth 270kHz, asynchronous */
FuriHalSubGhzPresetOok650Async, /**< OOK, bandwidth 650kHz, asynchronous */
FuriHalSubGhzPreset2FSKDev238Async, /**< FM, deviation 2.380371 kHz, asynchronous */
+ FuriHalSubGhzPreset2FSKDev12KAsync, /**< FM, deviation ~12 kHz, asynchronous */
FuriHalSubGhzPreset2FSKDev476Async, /**< FM, deviation 47.60742 kHz, asynchronous */
FuriHalSubGhzPresetMSK99_97KbAsync, /**< MSK, deviation 47.60742 kHz, 99.97Kb/s, asynchronous */
FuriHalSubGhzPresetGFSK9_99KbAsync, /**< GFSK, deviation 19.042969 kHz, 9.996Kb/s, asynchronous */
diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c
index 70de346ad..6d40a11ea 100644
--- a/lib/subghz/protocols/keeloq.c
+++ b/lib/subghz/protocols/keeloq.c
@@ -299,11 +299,13 @@ static bool subghz_protocol_keeloq_gen_data(
(strcmp(instance->manufacture_name, "Rosh") == 0) ||
(strcmp(instance->manufacture_name, "Rossi") == 0) ||
(strcmp(instance->manufacture_name, "Pecinin") == 0) ||
- (strcmp(instance->manufacture_name, "Steelmate") == 0)) {
+ (strcmp(instance->manufacture_name, "Steelmate") == 0) ||
+ (strcmp(instance->manufacture_name, "Cardin_S449") == 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
decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 |
instance->generic.cnt;
} else if(
@@ -511,6 +513,8 @@ static bool
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);
diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c
index fcb426c8c..037bd6df8 100644
--- a/lib/subghz/subghz_setting.c
+++ b/lib/subghz/subghz_setting.c
@@ -201,6 +201,8 @@ static void subghz_setting_load_default_region(
instance, "FM238", subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs);
subghz_setting_load_default_preset(
instance, "FM476", subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs);
+ subghz_setting_load_default_preset(
+ instance, "FM12K", subghz_device_cc1101_preset_2fsk_dev12khz_async_regs);
}
// Region check removed
diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv
index 1213d09e7..74c45e91a 100755
--- a/targets/f7/api_symbols.csv
+++ b/targets/f7/api_symbols.csv
@@ -1,5 +1,5 @@
entry,status,name,type,params
-Version,+,87.1,,
+Version,+,87.2,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
@@ -4274,6 +4274,7 @@ Variable,+,sequence_solid_yellow,const NotificationSequence,
Variable,+,sequence_success,const NotificationSequence,
Variable,+,simple_array_config_uint8_t,const SimpleArrayConfig,
Variable,-,subghz_device_cc1101_int,const SubGhzDevice,
+Variable,+,subghz_device_cc1101_preset_2fsk_dev12khz_async_regs,const uint8_t[],
Variable,+,subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs,const uint8_t[],
Variable,+,subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs,const uint8_t[],
Variable,+,subghz_device_cc1101_preset_gfsk_9_99kb_async_regs,const uint8_t[],
From 8d23b84ed1b33f33f728f33dbf94a4ec513fe300 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Mon, 5 Jan 2026 21:14:33 +0300
Subject: [PATCH 5/9] fix typos in signal settings
---
.../scenes/subghz_scene_signal_settings.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/applications/main/subghz/scenes/subghz_scene_signal_settings.c b/applications/main/subghz/scenes/subghz_scene_signal_settings.c
index f5dbfea58..d7d393d52 100644
--- a/applications/main/subghz/scenes/subghz_scene_signal_settings.c
+++ b/applications/main/subghz/scenes/subghz_scene_signal_settings.c
@@ -197,14 +197,13 @@ void subghz_scene_signal_settings_on_enter(void* context) {
}
// In protocols output we allways have HEX format for "Cnt:" output (text formating like ...Cnt:%05lX\r\n")
- // we take 8 simbols starting from "Cnt:........"
- // at first we search "Cnt:????" that mean for this protocol counter cannot be decoded
+ // we take 8 symbols starting from "Cnt:........"
+ // at first we search "Cnt:????" that means for this protocol counter cannot be decoded
int8_t place = furi_string_search_str(tmp_text, "Cnt:??", 0);
if(place > 0) {
counter_mode = 0xff;
- FURI_LOG_D(
- TAG, "Founded Cnt:???? - Counter mode and edit not available for this protocol");
+ FURI_LOG_D(TAG, "Found Cnt:???? - Counter mode and edit not available for this protocol");
} else {
place = furi_string_search_str(tmp_text, "Cnt:", 0);
if(place > 0) {
@@ -219,7 +218,7 @@ void subghz_scene_signal_settings_on_enter(void* context) {
furi_string_trim(textCnt);
FURI_LOG_D(
TAG,
- "Taked 8 bytes hex value starting after 'Cnt:' - %s",
+ "Took 8 bytes hex value starting after 'Cnt:' - %s",
furi_string_get_cstr(textCnt));
// trim and convert 8 simbols string to uint32 by base 16 (hex);
@@ -230,13 +229,13 @@ void subghz_scene_signal_settings_on_enter(void* context) {
StrintParseNoError) {
counter_not_available = false;
- // calculate and roundup number of hex bytes do display counter in byte_input (every 2 hex simbols = 1 byte for view)
+ // calculate and roundup number of hex bytes to display counter in byte_input (every 2 hex symbols = 1 byte for view)
// later must be used in byte_input to restrict number of available byte to edit
// cnt_byte_count = (hex_char_lenght + 1) / 2;
FURI_LOG_D(
TAG,
- "Result of conversion from String to uint_32 DEC %li, HEX %lX, HEX lenght %i symbols",
+ "Result of conversion from String to uint_32 DEC %li, HEX %lX, HEX length %i symbols",
loaded_counter32,
loaded_counter32,
hex_char_lenght);
@@ -268,7 +267,7 @@ void subghz_scene_signal_settings_on_enter(void* context) {
furi_assert(byte_ptr);
furi_assert(byte_count > 0);
- //Create and Enable/Disable variable_item_list depent from current values
+ //Create and Enable/Disable variable_item_list depending on current values
VariableItemList* variable_item_list = subghz->variable_item_list;
int32_t value_index;
VariableItem* item;
@@ -315,7 +314,7 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
// when signal has Cnt:00 we can step only to 0000+FFFF = FFFF, but we need 0000 for next step
// for this case we must use +1 additional step to increace Cnt from FFFF to 0000.
- // save current user definded counter increase value (mult)
+ // save current user defined counter increase value (mult)
tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
// increase signal counter to max value - at result it must be 0000 in most cases
@@ -411,7 +410,7 @@ void subghz_scene_signal_settings_on_exit(void* context) {
flipper_format_free(fff_data_file);
furi_record_close(RECORD_STORAGE);
- // we need reload file after editing when we exit from Signal Settings menu.
+ // we need to reload file after editing when we exit from Signal Settings menu.
if(subghz_key_load(subghz, file_path, false)) {
FURI_LOG_D(TAG, "Subghz file was successfully reloaded");
} else {
From 529d72f7ddab8cad06acccc13c21407e9d58613f Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Mon, 5 Jan 2026 21:10:31 +0300
Subject: [PATCH 6/9] kinggates stylo 4k add manually and button switch
---
.../main/subghz/helpers/subghz_custom_event.h | 1 +
.../main/subghz/helpers/subghz_gen_info.c | 9 +
.../main/subghz/helpers/subghz_gen_info.h | 6 +
.../helpers/subghz_txrx_create_protocol_key.c | 30 +++
.../helpers/subghz_txrx_create_protocol_key.h | 8 +
.../subghz/scenes/subghz_scene_set_button.c | 5 +
.../subghz/scenes/subghz_scene_set_counter.c | 17 ++
.../subghz/scenes/subghz_scene_set_seed.c | 2 +
.../subghz/scenes/subghz_scene_set_serial.c | 9 +
.../subghz/scenes/subghz_scene_set_type.c | 11 +
lib/subghz/protocols/kinggates_stylo_4k.c | 190 +++++++++++++++---
lib/subghz/protocols/public_api.h | 18 ++
targets/f7/api_symbols.csv | 1 +
13 files changed, 282 insertions(+), 25 deletions(-)
diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h
index 33a70fd57..e21ba128c 100644
--- a/applications/main/subghz/helpers/subghz_custom_event.h
+++ b/applications/main/subghz/helpers/subghz_custom_event.h
@@ -70,6 +70,7 @@ typedef enum {
SetTypeFaacSLH_433,
SetTypeBFTMitto,
SetTypeSomfyTelis,
+ SetTypeKingGatesStylo4k,
SetTypeANMotorsAT4,
SetTypeAlutechAT4N,
SetTypePhoenix_V2_433,
diff --git a/applications/main/subghz/helpers/subghz_gen_info.c b/applications/main/subghz/helpers/subghz_gen_info.c
index 5f1021ddd..82c114268 100644
--- a/applications/main/subghz/helpers/subghz_gen_info.c
+++ b/applications/main/subghz/helpers/subghz_gen_info.c
@@ -523,6 +523,15 @@ void subghz_scene_set_type_fill_generation_infos(GenInfo* infos_dest, SetType ty
.somfy_telis.btn = 0x02,
.somfy_telis.cnt = 0x03};
break;
+ case SetTypeKingGatesStylo4k:
+ gen_info = (GenInfo){
+ .type = GenKingGatesStylo4k,
+ .mod = "AM650",
+ .freq = 433920000,
+ .kinggates_stylo_4k.serial = key & 0xFFFFFFFF,
+ .kinggates_stylo_4k.btn = 0x0E,
+ .kinggates_stylo_4k.cnt = 0x03};
+ break;
case SetTypeMotorline433:
gen_info = (GenInfo){
.type = GenKeeloq,
diff --git a/applications/main/subghz/helpers/subghz_gen_info.h b/applications/main/subghz/helpers/subghz_gen_info.h
index 88ccdec75..f54992738 100644
--- a/applications/main/subghz/helpers/subghz_gen_info.h
+++ b/applications/main/subghz/helpers/subghz_gen_info.h
@@ -10,6 +10,7 @@ typedef enum {
GenKeeloqBFT,
GenAlutechAt4n,
GenSomfyTelis,
+ GenKingGatesStylo4k,
GenNiceFlorS,
GenSecPlus1,
GenSecPlus2,
@@ -61,6 +62,11 @@ typedef struct {
uint8_t btn;
uint16_t cnt;
} somfy_telis;
+ struct {
+ uint32_t serial;
+ uint8_t btn;
+ uint16_t cnt;
+ } kinggates_stylo_4k;
struct {
uint32_t serial;
uint8_t btn;
diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c
index 783273e6b..1c0add497 100644
--- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c
+++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c
@@ -335,6 +335,36 @@ bool subghz_txrx_gen_somfy_telis_protocol(
return res;
}
+bool subghz_txrx_gen_kinggates_stylo_4k_protocol(
+ void* context,
+ const char* preset_name,
+ uint32_t frequency,
+ uint32_t serial,
+ uint8_t btn,
+ uint16_t cnt) {
+ SubGhzTxRx* txrx = context;
+
+ bool res = false;
+
+ txrx->transmitter =
+ subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME);
+ subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0);
+
+ if(txrx->transmitter && subghz_protocol_kinggates_stylo_4k_create_data(
+ subghz_transmitter_get_protocol_instance(txrx->transmitter),
+ txrx->fff_data,
+ serial,
+ btn,
+ cnt,
+ txrx->preset)) {
+ res = true;
+ }
+
+ subghz_transmitter_free(txrx->transmitter);
+
+ return res;
+}
+
bool subghz_txrx_gen_secplus_v2_protocol(
SubGhzTxRx* instance,
const char* name_preset,
diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h
index 7daa61b31..590f9fa5b 100644
--- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h
+++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h
@@ -108,6 +108,14 @@ bool subghz_txrx_gen_somfy_telis_protocol(
uint8_t btn,
uint16_t cnt);
+bool subghz_txrx_gen_kinggates_stylo_4k_protocol(
+ void* context,
+ const char* preset_name,
+ uint32_t frequency,
+ uint32_t serial,
+ uint8_t btn,
+ uint16_t cnt);
+
bool subghz_txrx_gen_came_atomo_protocol(
void* context,
const char* preset_name,
diff --git a/applications/main/subghz/scenes/subghz_scene_set_button.c b/applications/main/subghz/scenes/subghz_scene_set_button.c
index c07b793e4..baab8f095 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_button.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_button.c
@@ -36,6 +36,10 @@ void subghz_scene_set_button_on_enter(void* context) {
byte_ptr = &subghz->gen_info->somfy_telis.btn;
byte_count = sizeof(subghz->gen_info->somfy_telis.btn);
break;
+ case GenKingGatesStylo4k:
+ byte_ptr = &subghz->gen_info->kinggates_stylo_4k.btn;
+ byte_count = sizeof(subghz->gen_info->kinggates_stylo_4k.btn);
+ break;
case GenNiceFlorS:
byte_ptr = &subghz->gen_info->nice_flor_s.btn;
byte_count = sizeof(subghz->gen_info->nice_flor_s.btn);
@@ -82,6 +86,7 @@ bool subghz_scene_set_button_on_event(void* context, SceneManagerEvent event) {
case GenKeeloqBFT:
case GenAlutechAt4n:
case GenSomfyTelis:
+ case GenKingGatesStylo4k:
case GenNiceFlorS:
case GenSecPlus2:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetCounter);
diff --git a/applications/main/subghz/scenes/subghz_scene_set_counter.c b/applications/main/subghz/scenes/subghz_scene_set_counter.c
index 9afa3408c..42437fcbf 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_counter.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_counter.c
@@ -42,6 +42,10 @@ void subghz_scene_set_counter_on_enter(void* context) {
byte_ptr = (uint8_t*)&subghz->gen_info->somfy_telis.cnt;
byte_count = sizeof(subghz->gen_info->somfy_telis.cnt);
break;
+ case GenKingGatesStylo4k:
+ byte_ptr = (uint8_t*)&subghz->gen_info->kinggates_stylo_4k.cnt;
+ byte_count = sizeof(subghz->gen_info->kinggates_stylo_4k.cnt);
+ break;
case GenNiceFlorS:
byte_ptr = (uint8_t*)&subghz->gen_info->nice_flor_s.cnt;
byte_count = sizeof(subghz->gen_info->nice_flor_s.cnt);
@@ -113,6 +117,10 @@ bool subghz_scene_set_counter_on_event(void* context, SceneManagerEvent event) {
case GenSomfyTelis:
subghz->gen_info->somfy_telis.cnt = __bswap16(subghz->gen_info->somfy_telis.cnt);
break;
+ case GenKingGatesStylo4k:
+ subghz->gen_info->kinggates_stylo_4k.cnt =
+ __bswap16(subghz->gen_info->kinggates_stylo_4k.cnt);
+ break;
case GenNiceFlorS:
subghz->gen_info->nice_flor_s.cnt = __bswap16(subghz->gen_info->nice_flor_s.cnt);
break;
@@ -171,6 +179,15 @@ bool subghz_scene_set_counter_on_event(void* context, SceneManagerEvent event) {
subghz->gen_info->somfy_telis.btn,
subghz->gen_info->somfy_telis.cnt);
break;
+ case GenKingGatesStylo4k:
+ generated_protocol = subghz_txrx_gen_kinggates_stylo_4k_protocol(
+ subghz->txrx,
+ subghz->gen_info->mod,
+ subghz->gen_info->freq,
+ subghz->gen_info->kinggates_stylo_4k.serial,
+ subghz->gen_info->kinggates_stylo_4k.btn,
+ subghz->gen_info->kinggates_stylo_4k.cnt);
+ break;
case GenNiceFlorS:
generated_protocol = subghz_txrx_gen_nice_flor_s_protocol(
subghz->txrx,
diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed.c b/applications/main/subghz/scenes/subghz_scene_set_seed.c
index c858b2a17..b93203a17 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_seed.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_seed.c
@@ -30,6 +30,7 @@ void subghz_scene_set_seed_on_enter(void* context) {
case GenKeeloq:
case GenAlutechAt4n:
case GenSomfyTelis:
+ case GenKingGatesStylo4k:
case GenNiceFlorS:
case GenSecPlus2:
case GenPhoenixV2:
@@ -89,6 +90,7 @@ bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) {
case GenKeeloq:
case GenAlutechAt4n:
case GenSomfyTelis:
+ case GenKingGatesStylo4k:
case GenNiceFlorS:
case GenSecPlus2:
case GenPhoenixV2:
diff --git a/applications/main/subghz/scenes/subghz_scene_set_serial.c b/applications/main/subghz/scenes/subghz_scene_set_serial.c
index 5c3b76733..b11559b44 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_serial.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_serial.c
@@ -42,6 +42,10 @@ void subghz_scene_set_serial_on_enter(void* context) {
byte_ptr = (uint8_t*)&subghz->gen_info->somfy_telis.serial;
byte_count = sizeof(subghz->gen_info->somfy_telis.serial);
break;
+ case GenKingGatesStylo4k:
+ byte_ptr = (uint8_t*)&subghz->gen_info->kinggates_stylo_4k.serial;
+ byte_count = sizeof(subghz->gen_info->kinggates_stylo_4k.serial);
+ break;
case GenNiceFlorS:
byte_ptr = (uint8_t*)&subghz->gen_info->nice_flor_s.serial;
byte_count = sizeof(subghz->gen_info->nice_flor_s.serial);
@@ -110,6 +114,10 @@ bool subghz_scene_set_serial_on_event(void* context, SceneManagerEvent event) {
subghz->gen_info->somfy_telis.serial =
__bswap32(subghz->gen_info->somfy_telis.serial);
break;
+ case GenKingGatesStylo4k:
+ subghz->gen_info->kinggates_stylo_4k.serial =
+ __bswap32(subghz->gen_info->kinggates_stylo_4k.serial);
+ break;
case GenNiceFlorS:
subghz->gen_info->nice_flor_s.serial =
__bswap32(subghz->gen_info->nice_flor_s.serial);
@@ -136,6 +144,7 @@ bool subghz_scene_set_serial_on_event(void* context, SceneManagerEvent event) {
case GenKeeloqBFT:
case GenAlutechAt4n:
case GenSomfyTelis:
+ case GenKingGatesStylo4k:
case GenNiceFlorS:
case GenSecPlus2:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetButton);
diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c
index 731da6f3f..846dda5d0 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_type.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_type.c
@@ -20,6 +20,7 @@ static const char* submenu_names[SetTypeMAX] = {
[SetTypeAlutechAT4N] = "Alutech AT4N 433MHz",
[SetTypeRoger_433] = "Roger 433MHz",
[SetTypePhoenix_V2_433] = "V2 Phoenix 433MHz",
+ [SetTypeKingGatesStylo4k] = "KingGates Stylo4 433MHz",
[SetTypeHCS101_433_92] = "KL: HCS101 433MHz",
[SetTypeDoorHan_315_00] = "KL: DoorHan 315MHz",
[SetTypeDoorHan_433_92] = "KL: DoorHan 433MHz",
@@ -187,6 +188,15 @@ bool subghz_scene_set_type_generate_protocol_from_infos(SubGhz* subghz) {
gen_info.somfy_telis.btn,
gen_info.somfy_telis.cnt);
break;
+ case GenKingGatesStylo4k:
+ generated_protocol = subghz_txrx_gen_kinggates_stylo_4k_protocol(
+ subghz->txrx,
+ gen_info.mod,
+ gen_info.freq,
+ gen_info.kinggates_stylo_4k.serial,
+ gen_info.kinggates_stylo_4k.btn,
+ gen_info.kinggates_stylo_4k.cnt);
+ break;
case GenNiceFlorS:
generated_protocol = subghz_txrx_gen_nice_flor_s_protocol(
subghz->txrx,
@@ -266,6 +276,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
case GenKeeloqBFT: // Serial (u32), Button (u8), Counter (u16), Seed (u32)
case GenAlutechAt4n: // Serial (u32), Button (u8), Counter (u16)
case GenSomfyTelis: // Serial (u32), Button (u8), Counter (u16)
+ case GenKingGatesStylo4k: // Serial (u32), Button (u8), Counter (u16)
case GenNiceFlorS: // Serial (u32), Button (u8), Counter (u16)
case GenSecPlus2: // Serial (u32), Button (u8), Counter (u32)
case GenPhoenixV2: // Serial (u32), Counter (u16)
diff --git a/lib/subghz/protocols/kinggates_stylo_4k.c b/lib/subghz/protocols/kinggates_stylo_4k.c
index 2a34cc522..e41dc19e2 100644
--- a/lib/subghz/protocols/kinggates_stylo_4k.c
+++ b/lib/subghz/protocols/kinggates_stylo_4k.c
@@ -8,6 +8,8 @@
#include "../blocks/generic.h"
#include "../blocks/math.h"
+#include "../blocks/custom_btn_i.h"
+
#define TAG "SubGhzProtocoKingGatesStylo4k"
static const SubGhzBlockConst subghz_protocol_kinggates_stylo_4k_const = {
@@ -84,6 +86,13 @@ static void subghz_protocol_kinggates_stylo_4k_remote_controller(
SubGhzBlockGeneric* instance,
SubGhzKeystore* keystore);
+/**
+ * Defines the button value for the current btn_id
+ * Basic set | 0xE | 0xD | 0xB | 0x7 |
+ * @return Button code
+ */
+static uint8_t subghz_protocol_kinggates_stylo_4k_get_btn_code(void);
+
void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) {
SubGhzProtocolEncoderKingGates_stylo_4k* instance =
malloc(sizeof(SubGhzProtocolEncoderKingGates_stylo_4k));
@@ -138,22 +147,12 @@ LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context) {
static bool subghz_protocol_kinggates_stylo_4k_gen_data(
SubGhzProtocolEncoderKingGates_stylo_4k* instance,
uint8_t btn) {
- UNUSED(btn);
- uint32_t hop = subghz_protocol_blocks_reverse_key(instance->generic.data_2 >> 4, 32);
- uint64_t fix = subghz_protocol_blocks_reverse_key(instance->generic.data, 53);
- int res = 0;
- uint32_t decrypt = 0;
+ // Save original button for later use
+ if(subghz_custom_btn_get_original() == 0) {
+ subghz_custom_btn_set_original(btn);
+ }
- for
- M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) {
- res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k");
- if(res == 0) {
- //Simple Learning
- decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key);
- break;
- }
- }
- instance->generic.cnt = decrypt & 0xFFFF;
+ btn = subghz_protocol_kinggates_stylo_4k_get_btn_code();
// Check for OFEX (overflow experimental) mode
if(furi_hal_subghz_get_rolling_counter_mult() != -0x7FFFFFFF) {
@@ -172,18 +171,21 @@ static bool subghz_protocol_kinggates_stylo_4k_gen_data(
}
}
- instance->generic.btn = (fix >> 17) & 0x0F;
- instance->generic.serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF);
+ // hop is encrypted part
+ uint32_t hop = (uint64_t)btn << 28 | (((uint32_t)0x0C) << 24) |
+ ((instance->generic.serial & 0xFF) << 16) | (instance->generic.cnt & 0xFFFF);
- uint32_t data = (decrypt & 0xFFFF0000) | instance->generic.cnt;
+ uint64_t fix = ((uint64_t)((instance->generic.serial >> 16) & 0xFFFF) << 21) |
+ (uint64_t)btn << 17 | 0b1 << 16 | (instance->generic.serial & 0xFFFF);
+
+ instance->generic.data = subghz_protocol_blocks_reverse_key(fix, 53);
uint64_t encrypt = 0;
for
M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) {
- res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k");
- if(res == 0) {
- //Simple Learning
- encrypt = subghz_protocol_keeloq_common_encrypt(data, manufacture_code->key);
+ if(strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k") == 0) {
+ // Simple Learning
+ encrypt = subghz_protocol_keeloq_common_encrypt(hop, manufacture_code->key);
encrypt = subghz_protocol_blocks_reverse_key(encrypt, 32);
instance->generic.data_2 = encrypt << 4;
return true;
@@ -193,6 +195,63 @@ static bool subghz_protocol_kinggates_stylo_4k_gen_data(
return false;
}
+bool subghz_protocol_kinggates_stylo_4k_create_data(
+ void* context,
+ FlipperFormat* flipper_format,
+ uint32_t serial,
+ uint8_t btn,
+ uint16_t cnt,
+ SubGhzRadioPreset* preset) {
+ furi_assert(context);
+ SubGhzProtocolEncoderKingGates_stylo_4k* instance = context;
+ instance->generic.serial = serial;
+ instance->generic.cnt = cnt;
+ instance->generic.btn = btn;
+ instance->generic.data_count_bit = 89;
+
+ uint32_t decrypt = instance->generic.btn << 28 | (((uint32_t)0x0C) << 24) |
+ ((instance->generic.serial & 0xFF) << 16) |
+ (instance->generic.cnt & 0xFFFF);
+
+ uint64_t encrypt = 0;
+ for
+ M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) {
+ if(strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k") == 0) {
+ // Simple Learning
+ encrypt = subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key);
+ encrypt = subghz_protocol_blocks_reverse_key(encrypt, 32);
+ instance->generic.data_2 = encrypt << 4;
+ break;
+ }
+ }
+
+ uint64_t fix = ((uint64_t)((instance->generic.serial >> 16) & 0xFFFF) << 21) |
+ instance->generic.btn << 17 | 0b1 << 16 | (instance->generic.serial & 0xFFFF);
+
+ instance->generic.data = subghz_protocol_blocks_reverse_key(fix, 53);
+
+ SubGhzProtocolStatus res =
+ subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
+
+ 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_2 >> (i * 8)) & 0xFF;
+ }
+
+ if(!flipper_format_rewind(flipper_format)) {
+ FURI_LOG_E(TAG, "Rewind error");
+ res = SubGhzProtocolStatusErrorParserOthers;
+ }
+
+ if((res == SubGhzProtocolStatusOk) &&
+ !flipper_format_insert_or_update_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
+ FURI_LOG_E(TAG, "Unable to add Data2");
+ res = SubGhzProtocolStatusErrorParserOthers;
+ }
+
+ return res == SubGhzProtocolStatusOk;
+}
+
/**
* Generating an upload from data.
* @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance
@@ -281,9 +340,6 @@ SubGhzProtocolStatus subghz_protocol_encoder_kinggates_stylo_4k_deserialize(
break;
}
- subghz_protocol_kinggates_stylo_4k_remote_controller(
- &instance->generic, instance->keystore);
-
//optional parameter parameter
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
@@ -303,6 +359,9 @@ SubGhzProtocolStatus subghz_protocol_encoder_kinggates_stylo_4k_deserialize(
instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i];
}
+ subghz_protocol_kinggates_stylo_4k_remote_controller(
+ &instance->generic, instance->keystore);
+
subghz_protocol_encoder_kinggates_stylo_4k_get_upload(instance, instance->generic.btn);
if(!flipper_format_rewind(flipper_format)) {
@@ -310,6 +369,14 @@ SubGhzProtocolStatus subghz_protocol_encoder_kinggates_stylo_4k_deserialize(
break;
}
+ for(size_t i = 0; i < sizeof(uint64_t); i++) {
+ key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF;
+ }
+ if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
+ FURI_LOG_E(TAG, "Unable to update Key");
+ break;
+ }
+
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> i * 8) & 0xFF;
}
@@ -501,6 +568,11 @@ static void subghz_protocol_kinggates_stylo_4k_remote_controller(
}
if(ret) {
instance->cnt = decrypt & 0xFFFF;
+ // Save original button for later use
+ if(subghz_custom_btn_get_original() == 0) {
+ subghz_custom_btn_set_original(instance->btn);
+ }
+ subghz_custom_btn_set_max(3);
} else {
instance->btn = 0;
instance->serial = 0;
@@ -575,6 +647,74 @@ SubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_deserialize(
return ret;
}
+static uint8_t subghz_protocol_kinggates_stylo_4k_get_btn_code(void) {
+ uint8_t custom_btn_id = subghz_custom_btn_get();
+ uint8_t original_btn_code = subghz_custom_btn_get_original();
+ uint8_t btn = original_btn_code;
+
+ // Set custom button
+ if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0)) {
+ // Restore original button code
+ btn = original_btn_code;
+ } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_UP) {
+ switch(original_btn_code) {
+ case 0xE:
+ btn = 0xD;
+ break;
+ case 0xD:
+ btn = 0xE;
+ break;
+ case 0xB:
+ btn = 0xE;
+ break;
+ case 0x7:
+ btn = 0xE;
+ break;
+
+ default:
+ break;
+ }
+ } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) {
+ switch(original_btn_code) {
+ case 0xE:
+ btn = 0xB;
+ break;
+ case 0xD:
+ btn = 0xB;
+ break;
+ case 0xB:
+ btn = 0xD;
+ break;
+ case 0x7:
+ btn = 0xD;
+ break;
+
+ default:
+ break;
+ }
+ } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) {
+ switch(original_btn_code) {
+ case 0xE:
+ btn = 0x7;
+ break;
+ case 0xD:
+ btn = 0x7;
+ break;
+ case 0xB:
+ btn = 0x7;
+ break;
+ case 0x7:
+ btn = 0xB;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return btn;
+}
+
void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) {
furi_assert(context);
SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;
diff --git a/lib/subghz/protocols/public_api.h b/lib/subghz/protocols/public_api.h
index 39c08e6aa..76cdb2525 100644
--- a/lib/subghz/protocols/public_api.h
+++ b/lib/subghz/protocols/public_api.h
@@ -215,6 +215,24 @@ bool subghz_protocol_somfy_keytis_create_data(
uint16_t cnt,
SubGhzRadioPreset* preset);
+/**
+ * Key generation from simple data.
+ * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @param serial Serial number, 24 bit
+ * @param btn Button number, 8 bit
+ * @param cnt Counter value, 16 bit
+ * @param preset Modulation, SubGhzRadioPreset
+ * @return true On success
+ */
+bool subghz_protocol_kinggates_stylo_4k_create_data(
+ void* context,
+ FlipperFormat* flipper_format,
+ uint32_t serial,
+ uint8_t btn,
+ uint16_t cnt,
+ SubGhzRadioPreset* preset);
+
typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW;
void subghz_protocol_decoder_bin_raw_data_input_rssi(
diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv
index 74c45e91a..3889ce342 100755
--- a/targets/f7/api_symbols.csv
+++ b/targets/f7/api_symbols.csv
@@ -3674,6 +3674,7 @@ Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void*
Function,+,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzRadioPreset*"
Function,+,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*"
Function,+,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*"
+Function,+,subghz_protocol_kinggates_stylo_4k_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*"
Function,+,subghz_protocol_nice_flor_s_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*, _Bool"
Function,+,subghz_protocol_phoenix_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint16_t, SubGhzRadioPreset*"
Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*"
From b078213604fe5c020c1e1c067e8c59eab8927b85 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Mon, 5 Jan 2026 21:15:41 +0300
Subject: [PATCH 7/9] bump subremote
---
applications/main/subghz_remote | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/applications/main/subghz_remote b/applications/main/subghz_remote
index 885187e22..a8abfcd60 160000
--- a/applications/main/subghz_remote
+++ b/applications/main/subghz_remote
@@ -1 +1 @@
-Subproject commit 885187e22ccd934093719aac0309b5e1b829f83a
+Subproject commit a8abfcd6026190b7492bc42827c6c465691ccb9a
From bd87186f413bff51c1336b3122a7793dd1de3465 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Mon, 5 Jan 2026 21:21:27 +0300
Subject: [PATCH 8/9] Stilmatic button 9 support
---
lib/subghz/protocols/keeloq.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c
index 6d40a11ea..ba2df2cde 100644
--- a/lib/subghz/protocols/keeloq.c
+++ b/lib/subghz/protocols/keeloq.c
@@ -507,7 +507,9 @@ static bool
(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)) {
+ } 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)) {
klq_last_custom_btn = 0x6;
From ef9cd22dfe73a9b3ce9f2b89d657467ce58d11e0 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Mon, 5 Jan 2026 21:40:10 +0300
Subject: [PATCH 9/9] NFC: Fix some typos in Type4Tag protocol [ci skip]
by WillyJL
---
.../nfc/helpers/protocol_support/type_4_tag/type_4_tag.c | 2 +-
lib/nfc/protocols/type_4_tag/type_4_tag_poller.c | 6 +++---
lib/nfc/protocols/type_4_tag/type_4_tag_poller.h | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c
index 3b1fc91f6..3fa6b2593 100644
--- a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c
+++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c
@@ -184,7 +184,7 @@ static NfcCommand
nfc_device_get_data(instance->nfc_device, NfcProtocolType4Tag);
furi_string_reset(instance->text_box_store);
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
- } else if(type_4_tag_event->type == Type4TagPollerEventTypeWriteFail) {
+ } else if(type_4_tag_event->type == Type4TagPollerEventTypeWriteFailed) {
const char* error_str = type_4_tag_event->data->error == Type4TagErrorCardLocked ?
"Card does not\nallow writing\nnew data" :
"Failed to\nwrite new data";
diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c
index 0291d2df9..639f3f1db 100644
--- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c
+++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c
@@ -4,7 +4,7 @@
#define TAG "Type4TagPoller"
-typedef NfcCommand (*Type4TagPollerReadHandler)(Type4TagPoller* instance);
+typedef NfcCommand (*Type4TagPollerStateHandler)(Type4TagPoller* instance);
static const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance) {
furi_assert(instance);
@@ -191,7 +191,7 @@ static NfcCommand type_4_tag_poller_handler_failed(Type4TagPoller* instance) {
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->type_4_tag_event.type = instance->mode == Type4TagPollerModeRead ?
Type4TagPollerEventTypeReadFailed :
- Type4TagPollerEventTypeWriteFail;
+ Type4TagPollerEventTypeWriteFailed;
instance->type_4_tag_event.data->error = instance->error;
NfcCommand command = instance->callback(instance->general_event, instance->context);
instance->state = Type4TagPollerStateIdle;
@@ -208,7 +208,7 @@ static NfcCommand type_4_tag_poller_handler_success(Type4TagPoller* instance) {
return command;
}
-static const Type4TagPollerReadHandler type_4_tag_poller_read_handler[Type4TagPollerStateNum] = {
+static const Type4TagPollerStateHandler type_4_tag_poller_read_handler[Type4TagPollerStateNum] = {
[Type4TagPollerStateIdle] = type_4_tag_poller_handler_idle,
[Type4TagPollerStateRequestMode] = type_4_tag_poller_handler_request_mode,
[Type4TagPollerStateDetectPlatform] = type_4_tag_poller_handler_detect_platform,
diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h
index 5ac4fd0f2..8263e3790 100644
--- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h
+++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.h
@@ -19,7 +19,7 @@ typedef enum {
Type4TagPollerEventTypeReadSuccess, /**< Card was read successfully. */
Type4TagPollerEventTypeReadFailed, /**< Poller failed to read card. */
Type4TagPollerEventTypeWriteSuccess, /**< Poller wrote card successfully. */
- Type4TagPollerEventTypeWriteFail, /**< Poller failed to write card. */
+ Type4TagPollerEventTypeWriteFailed, /**< Poller failed to write card. */
} Type4TagPollerEventType;
/**