Merge remote-tracking branch 'ul/dev' into mntm-dev --nobuild

This commit is contained in:
WillyJL
2025-07-19 16:58:37 +01:00
21 changed files with 1214 additions and 100 deletions

View File

@@ -1,11 +1,21 @@
### Added:
- Nothing
- SubGHz:
- UL: Roger (static 28 bit) with add manually support (by @xMasterX & @mishamyte)
- UL: V2 Phoenix full support (button switch, add manually, counter decrypt/encrypt) (by @xMasterX & @RocketGod-git, original code by @Skorpionm)
- UL: Add Keeloq support for - Motorline (with add manually support), Rosh, Pecinin, Rossi, Merlin, Steelmate (by @xMasterX & @RocketGod-git)
- UL: Nero Radio static parse and display more data (by @xMasterX)
- UL: Marantec protocol implement CRC verification display and add manually support (by @xMasterX & @li0ard, original code by @Skorpionm)
- UL: Keeloq Comunello add manually support (by @xMasterX)
### Updated:
- Apps:
- Combo Cracker: Allow press and hold to change values (by @TAxelAnderson)
- FlipDownloader: Added a new option to download GitHub repositories (by @jblanked)
- KeyCopier: Added Weiser WR3 key format (by @lightos)
- Sub-GHz:
- UL: Add 868.46 MHz to default subghz freqs list (by @xMasterX)
- UL: Reduce less popular freqs in default hopper preset, make it faster (by @xMasterX)
- UL: Docs: Update Sub-GHz DoorHan programming instructions (by @li0ard)
### Fixed:
- Bad KB: Fix modifier keys with HOLD/RELEASE commands (by @WillyJL)

View File

@@ -82,16 +82,20 @@ typedef enum {
SetTypeSomfyTelis,
SetTypeANMotorsAT4,
SetTypeAlutechAT4N,
SetTypePhoenix_V2_433,
SetTypeHCS101_433_92,
SetTypeDoorHan_315_00,
SetTypeDoorHan_433_92,
SetTypeBeninca433,
SetTypeBeninca868,
SetTypeComunello433,
SetTypeComunello868,
SetTypeAllmatic433,
SetTypeAllmatic868,
SetTypeCenturion433,
SetTypeMonarch433,
SetTypeJollyMotors433,
SetTypeMotorline433,
SetTypeSommer_FM_434,
SetTypeSommer_FM_868,
SetTypeSommer_FM238_434,
@@ -132,6 +136,9 @@ typedef enum {
SetTypeHollarm_433,
SetTypeReversRB2_433,
SetTypeMarantec24_868,
SetTypeMarantec_433,
SetTypeMarantec_868,
SetTypeRoger_433,
SetTypeLinear_300_00,
// SetTypeNeroSketch, //Deleted in OFW
// SetTypeNeroRadio, //Deleted in OFW

View File

@@ -6,6 +6,7 @@
#include <lib/subghz/protocols/secplus_v1.h>
#include <lib/subghz/protocols/secplus_v2.h>
#include <lib/subghz/protocols/nice_flor_s.h>
#include <lib/subghz/protocols/marantec.h>
#include <flipper_format/flipper_format_i.h>
#include <lib/toolbox/stream/stream.h>
@@ -383,6 +384,34 @@ bool subghz_txrx_gen_secplus_v1_protocol(
return ret;
}
bool subghz_txrx_gen_phoenix_v2_protocol(
void* context,
const char* preset_name,
uint32_t frequency,
uint32_t serial,
uint16_t cnt) {
SubGhzTxRx* txrx = context;
bool res = false;
txrx->transmitter =
subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_PHOENIX_V2_NAME);
subghz_txrx_set_preset(txrx, preset_name, frequency, NAN, NAN, NULL, 0);
if(txrx->transmitter && subghz_protocol_phoenix_v2_create_data(
subghz_transmitter_get_protocol_instance(txrx->transmitter),
txrx->fff_data,
serial,
cnt,
txrx->preset)) {
res = true;
}
subghz_transmitter_free(txrx->transmitter);
return res;
}
void subghz_txrx_gen_serial_gangqi(uint64_t* result_key) {
uint64_t randkey = (uint64_t)rand();
uint16_t serial = (uint16_t)((randkey) & 0xFFFF);
@@ -395,3 +424,27 @@ void subghz_txrx_gen_serial_gangqi(uint64_t* result_key) {
// serial | const_and_button
*result_key = (serial << 18) | (const_and_button << 10) | (bytesum << 2);
}
void subghz_txrx_gen_key_marantec(uint64_t* result_key) {
uint64_t randkey = (uint64_t)rand();
uint32_t serial = (uint32_t)((randkey) & 0xFFFFF);
// 0x130 is the constant
// 0x4 is the button code
// 0x86 is the serial constant
// serial is random value that we pre generate above
// At the end we will put the crc sum
uint64_t full_key_no_crc = (uint64_t)((uint64_t)0x130 << 40 | (uint64_t)serial << 20 |
(uint64_t)0x4 << 16 | (uint64_t)0x86 << 8);
uint8_t tdata[6] = {
full_key_no_crc >> 48,
full_key_no_crc >> 40,
full_key_no_crc >> 32,
full_key_no_crc >> 24,
full_key_no_crc >> 16,
full_key_no_crc >> 8};
uint8_t crc = subghz_protocol_marantec_crc8(tdata, sizeof(tdata));
*result_key = ((full_key_no_crc >> 8) << 8) | crc;
}

View File

@@ -115,6 +115,13 @@ bool subghz_txrx_gen_came_atomo_protocol(
uint32_t serial,
uint16_t cnt);
bool subghz_txrx_gen_phoenix_v2_protocol(
void* context,
const char* preset_name,
uint32_t frequency,
uint32_t serial,
uint16_t cnt);
/**
* Generate data SecPlus v2 protocol
*
@@ -153,3 +160,10 @@ bool subghz_txrx_gen_secplus_v1_protocol(
* @return uint64_t if success
*/
void subghz_txrx_gen_serial_gangqi(uint64_t* result_key);
/**
* Generate key for Marantec protocol
*
* @param result_key Pointer to a uint64_t where the key will be stored
*/
void subghz_txrx_gen_key_marantec(uint64_t* result_key);

View File

@@ -1,63 +1,69 @@
Filetype: Flipper SubGhz Keystore File
Version: 0
Encryption: 1
IV: 4E 6F 20 66 75 72 69 20 63 68 65 63 6B 3F 21 3F
2F0767B5B190608EB032D12BFA937D760A77D08D37F851E940767F1915E97ACF
332F8DCCFDBF0485EC2EEED0C279F277E52A86A93BC5E4E96BE5F7276CC66713
D9A02CC785FC0495063C424B0B1BAE7C120A2C24D4C0EE743F5D216718B16490
4D9DD617090BDB100986B6987CAAC3652D2ADAB1AD9E368C5806D98562FF6B2F
28D21748FF3826FA13C785A6721CC5927C81EDAB0C5CF31C92EAFF12AA91608298485D8A3AB443640237372ADF0DDC49
5058E12C0A41EDCB5C0812554F619DADFB6E895B94421952ECD9255A04EE5E1A
83A3EB8B22D94487A6B0F37856FB6AE9F42272BF25E1AE06DE03AA881A12D15F
D0E207DE64402B43ECD0C341216B6BCDC449508116E81D8ACDE7FA0BFBEA56F7
6C4F723DE3B775D4C07E12ED3C369250B4D2089ADE2207816DED130D4B498CDF
B041911C56555E5F4676BF16819F61BF7A92402EB0427B8C2E7367B0AEA6B53C
1AD460260F20146A763BF6D4CD26DF5139EE29FFF8B53F6C5367EA779E1BEE56D5DFD872EA0268FE27204175925079AA
B1A9331AED36137CD078536A67775E2880D3CD7305373BC44A5649435E466AD2DC9FDE8AC1F572EF094D4B438C9509EA
105819300A9152F16E3478151799ECBBB7CCCE63DADA3F6C6D16D46830E1E035
354186E04BC90D672F76A427FC1CD35C2EFAE8D4D1C36247FFB71ACB862A3601
84B533148282D0D8121E5BBBBD39DE16F398365B015E02417ECC535C88EB9C57
E899C9DC779F82E798EE923D257D5F051E1254DCDA2A6A955882727AA8E98ED8
B8EC34F9B75E61D34E9C075A5316FAFC395E8FBA4F76B215620C5B5C76C53DB7BF316E53582161AD20F64CAB8E3347B2
966C3B0873F48889B51D7D9DACC0674CBC95B06E231E30F235C826285D529A54
370DED014764D7278E342D3AB631AB37D8F8A8FAE08987E03D7FC279DEEEB913
2318A2DA42EEA2A35FFC9BDFBB244DF3FF6933010D74B3465336C0E37FFDC48A200568F8D6003AB215388734B8AC1F20
475B35437FECEE9792F53A671252E78566AA9894DE7A4DEC9AED70834864E804E87478009F424CE1424C00F162BB03C5
01CE6251ED9682BA6366075081167196CD740D346C4DAC4E0012C7951C475AE7
CB225891F937CA491B711AA942B04C61C7CFA6A8E912D91D0906B401737E03B4
F35D279815DEF19C9C5BC9ED55E7C1A0366E37DCD0A93E6E168B5A2602201C7B
3569D8DF2490797D40978F8325B94CC707383FEA1B46240BFDAECFEFB1F8176D
3D7BAF13573BBF3102757C68D52236638CC57126FF3795A604CFFA2D3F1B9C26
B9102C87D7DBCF35463F38B6B80B70408968B6E01A47F6A7E8A3E87A0577B4ED
7673FAC14D94ABF72800A78E2DC4CAF2166FBB24719C22CFC1010492F4C87734
1AF74DA07EA3A418EB86BB7ABAD6192B8E5A53F61B3E74CB898CB3EE4A7E244A
832D18C44062DDE856384E19D1417FA48D809C2CB2107CDEC5281943559791A6
CD482A8FAB2A2CBE25A0B4A4788F274CA7095AA24508C00DBB78DD12BFB11C37
EAC52E802DB76B51058752D7EFA91BCB1212AB96B589F9A88465195C1DE3242E
96CC75952A513AB5FE62A69AB6CDDA93C2156A3EA607C25B3201CE7284B3DAA9
986E71EE87E860192141A1453929E575706E3FE72B7A9FEF5ACA696388649EB6
FFF89FECC1C01FA3F266B95BDEF61A16F514E59599DAA07E908C604E9FE686C0
ACC159D4AE78E26B5A1468F69D961028D0BF962D03E865415E7FE746553FEF15
0FF46B2F9D4E907B9924675081D17C38C09957AA2F4C3C1F5568461DBA850F6301328CDC0FCEE83C7E8BA00CF8FC0F97
7FD793C05E499739C3C4F8CC1D2D205A55928AB5BC60752A81D86DFBE30C50BD
CE444F4A1BEB38C9E93579E1B7FB3E90B4F85D8DA94DFC622272DED093A35192
C7C31D8AB9D717FAF842F850A3E0D6B404EB9A980D9679362ABA90594B1957AB
1D48A6CFFBB6F14DD5BED1F8E74E6CC0292150546EDD76361792677434A8FE5F
F7335B8877DDF17F0737ECF77E2E6286E78534CE28E72077360E8E24D7750DFE
51051D9A8D5941661EBCF437B4640E6DA0B9B27518D761F3EF26BF7EABC691D4
79F279733E18393FEDB50D654A0D0A532A64BED5ACBD13319439EEC007BC359C
646666FDB75D439C0427A9E3EF47F145DBD4FF5FE2E244909D74F075B24FF5A9B47E7AF98271210057D937A0E4B1F46D
DE7E814A2BD4D8823D9F2380EFAFFA1380A90391F87CBF24CE46BD46205EABAB
1335C4C3E819E942F5C212E9BEFAF5D984316C0A2FF6E9886886B565625618A9
65386F906F18FF9C3A20AB57F3241D4975FE312ACDEB7FB1B91F2B816CAA46E7
DF8A8B33782D56667F4C98F8F91B49B71A9E83AF015D8841986D41663233A0DC
27264455248878BB226FA1DED0922BD10313FF65F8A6A0E3CCDFB77890C838BB
43A08F784F36A3E8049BA63A401F3F15B3CA2ED263F8638595B5F22A0B081369
F9F82F89C15AD970320E3D7A83B272EB00CD0ED657E4D230AB33C995859EA77F
70AD020D172E18E1011DF88E5F934F03F34DCE8148F8053B4FFA6F92CAC9FC93
2B845F67BAB432CED64F2D68454A2B4B3BC46FFDC2A16D3340360C7BEA110BBB
B85F16A2370B278FDB3C85E6455B8DA239D6413B727839DEFBCB5628A6C747266291AB9D9F4F5DA1826B219C1A29F956
FFB7B10D96F241FDB994008AF85EC85D147A97AA599D05F5EE1BB2FC27644A26
0BD42CA312CBBCAE556AA0159EC2CC2FA70BBB00D8DF7B63BBEA60A282481AED
9CC73810056A21EA6F311B01BA7F44655A075D1F60947FBC9B6924C3BD0ED819
024FCB96977ECA1C0D4B9C7C461361329D96E5AFF315124FEFC0DF2A400DE312F45D602DB40CD4EB088F144EB0B8DF41
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

View File

@@ -20,13 +20,18 @@ static const char* submenu_names[SetTypeMAX] = {
[SetTypeSomfyTelis] = "Somfy Telis 433MHz",
[SetTypeANMotorsAT4] = "AN-Motors AT4 433MHz",
[SetTypeAlutechAT4N] = "Alutech AT4N 433MHz",
[SetTypeRoger_433] = "Roger 433MHz",
[SetTypePhoenix_V2_433] = "V2 Phoenix 433MHz",
[SetTypeHCS101_433_92] = "KL: HCS101 433MHz",
[SetTypeDoorHan_315_00] = "KL: DoorHan 315MHz",
[SetTypeDoorHan_433_92] = "KL: DoorHan 433MHz",
[SetTypeBeninca433] = "KL: Beninca 433MHz",
[SetTypeBeninca868] = "KL: Beninca 868MHz",
[SetTypeComunello433] = "KL: Comunello 433MHz",
[SetTypeComunello868] = "KL: Comunello 868MHz",
[SetTypeAllmatic433] = "KL: Allmatic 433MHz",
[SetTypeAllmatic868] = "KL: Allmatic 868MHz",
[SetTypeMotorline433] = "KL: Motorline 433MHz",
[SetTypeCenturion433] = "KL: Centurion 433MHz",
[SetTypeMonarch433] = "KL: Monarch 433MHz",
[SetTypeJollyMotors433] = "KL: Jolly Mot. 433MHz",
@@ -69,6 +74,8 @@ static const char* submenu_names[SetTypeMAX] = {
[SetTypeHollarm_433] = "Hollarm 433MHz",
[SetTypeReversRB2_433] = "Revers RB2 433MHz",
[SetTypeMarantec24_868] = "Marantec24 868MHz",
[SetTypeMarantec_433] = "Marantec 433MHz",
[SetTypeMarantec_868] = "Marantec 868MHz",
[SetTypeBETT_433] = "BETT 433MHz",
[SetTypeLinear_300_00] = "Linear 300MHz",
// [SetTypeNeroSketch] = "Nero Sketch", // Deleted in OFW
@@ -108,6 +115,7 @@ typedef enum {
GenNiceFlorS,
GenSecPlus1,
GenSecPlus2,
GenPhoenixV2,
} GenType;
typedef struct {
@@ -166,6 +174,10 @@ typedef struct {
uint8_t btn;
uint32_t cnt;
} sec_plus_2;
struct {
uint32_t serial;
uint16_t cnt;
} phoenix_v2;
};
} GenInfo;
@@ -190,6 +202,9 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
uint64_t gangqi_key;
subghz_txrx_gen_serial_gangqi(&gangqi_key);
uint64_t marantec_key;
subghz_txrx_gen_key_marantec(&marantec_key);
GenInfo gen_info = {0};
switch(event.event) {
case SetTypePricenton433:
@@ -272,6 +287,16 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
.data.bits = 24,
.data.te = 0};
break;
case SetTypeRoger_433:
gen_info = (GenInfo){
.type = GenData,
.mod = "AM650",
.freq = 433920000,
.data.name = SUBGHZ_PROTOCOL_ROGER_NAME,
.data.key = (key & 0xFFFF000) | 0x0000101, // button code 0x1 and (crc?) is 0x01
.data.bits = 28,
.data.te = 0};
break;
case SetTypeLinear_300_00:
gen_info = (GenInfo){
.type = GenData,
@@ -358,6 +383,28 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
.data.bits = 24,
.data.te = 0};
break;
case SetTypeMarantec_433:
gen_info = (GenInfo){
.type = GenData,
.mod = "AM650",
.freq = 433920000,
.data.name =
SUBGHZ_PROTOCOL_MARANTEC_NAME, // Button code is 0x4 and crc sum to the end
.data.key = marantec_key,
.data.bits = 49,
.data.te = 0};
break;
case SetTypeMarantec_868:
gen_info = (GenInfo){
.type = GenData,
.mod = "AM650",
.freq = 868350000,
.data.name =
SUBGHZ_PROTOCOL_MARANTEC_NAME, // Button code is 0x4 and crc sum to the end
.data.key = marantec_key,
.data.bits = 49,
.data.te = 0};
break;
case SetTypeFaacSLH_433:
gen_info = (GenInfo){
.type = GenFaacSLH,
@@ -400,6 +447,26 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
.keeloq.cnt = 0x05,
.keeloq.manuf = "Beninca"};
break;
case SetTypeComunello433:
gen_info = (GenInfo){
.type = GenKeeloq,
.mod = "AM650",
.freq = 433920000,
.keeloq.serial = key & 0x00FFFFFF,
.keeloq.btn = 0x08,
.keeloq.cnt = 0x05,
.keeloq.manuf = "Comunello"};
break;
case SetTypeComunello868:
gen_info = (GenInfo){
.type = GenKeeloq,
.mod = "AM650",
.freq = 868460000,
.keeloq.serial = key & 0x00FFFFFF,
.keeloq.btn = 0x08,
.keeloq.cnt = 0x05,
.keeloq.manuf = "Comunello"};
break;
case SetTypeAllmatic433:
gen_info = (GenInfo){
.type = GenKeeloq,
@@ -585,16 +652,16 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
.type = GenCameAtomo,
.mod = "AM650",
.freq = 433920000,
.keeloq.serial = (key & 0x0FFFFFFF) | 0x10000000,
.keeloq.cnt = 0x03};
.came_atomo.serial = (key & 0x0FFFFFFF) | 0x10000000,
.came_atomo.cnt = 0x03};
break;
case SetTypeCameAtomo868:
gen_info = (GenInfo){
.type = GenCameAtomo,
.mod = "AM650",
.freq = 868350000,
.keeloq.serial = (key & 0x0FFFFFFF) | 0x10000000,
.keeloq.cnt = 0x03};
.came_atomo.serial = (key & 0x0FFFFFFF) | 0x10000000,
.came_atomo.cnt = 0x03};
break;
case SetTypeBFTMitto:
gen_info = (GenInfo){
@@ -625,6 +692,16 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
.somfy_telis.btn = 0x02,
.somfy_telis.cnt = 0x03};
break;
case SetTypeMotorline433:
gen_info = (GenInfo){
.type = GenKeeloq,
.mod = "AM650",
.freq = 433920000,
.keeloq.serial = key & 0x0FFFFFFF,
.keeloq.btn = 0x01,
.keeloq.cnt = 0x03,
.keeloq.manuf = "Motorline"};
break;
case SetTypeDoorHan_433_92:
gen_info = (GenInfo){
.type = GenKeeloq,
@@ -820,6 +897,14 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
.sec_plus_2.btn = 0x68,
.sec_plus_2.cnt = 0xE500000};
break;
case SetTypePhoenix_V2_433:
gen_info = (GenInfo){
.type = GenPhoenixV2,
.mod = "AM650",
.freq = 433920000,
.phoenix_v2.serial = (key & 0x0FFFFFFF) | 0xB0000000,
.phoenix_v2.cnt = 0x025D};
break;
default:
furi_crash("Not implemented");
break;
@@ -927,6 +1012,14 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
gen_info.sec_plus_2.btn,
gen_info.sec_plus_2.cnt);
break;
case GenPhoenixV2:
generated_protocol = subghz_txrx_gen_phoenix_v2_protocol(
subghz->txrx,
gen_info.mod,
gen_info.freq,
gen_info.phoenix_v2.serial,
gen_info.phoenix_v2.cnt);
break;
default:
furi_crash("Not implemented");
break;

View File

@@ -76,13 +76,20 @@ Watch this video to learn more and see how different boards can be programmed (v
## Doorhan
With access to the receiver box:
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> KL: Doorhan 433Mhz or 315Mhz depends on your receiver (find out by reading your existing remote)
2. Open your new remote file
3. Push `P` button for ~2 sec, led will start flashing
4. Press `Send` on your flipper for ~2 seconds
5. Led on the receiver board will flash and turn off
6. Done!
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> KL: Doorhan 433Mhz or 315Mhz depends on your receiver (find out by reading your existing remote or follow guide below)
- Finding frequency
There are 2 frequencies for DoorHan: 315.00 / 433.92. To determine them it is enough to create a DoorHan remote control with one of the frequencies via Sub-GHz -> Add manually, press the button and watch the receiver's reaction. If you have guessed the frequency, the light bulb will turn on when we press the button on the FZ and turn off when we release it.
2. Binding the remote control
Once you have access to the receiver (removed the protective cover), look at the buttons:
- If there are 4 buttons (Radio, Reverse, Auto, ...) then press and hold Radio until the LED lights up, then press the FZ button 2 times and the LED goes out;
- If there are 4 buttons (R, P, +, -) and display, press R, then press 2 times the button on FZ and wait +/- 10 seconds;
- If there are 4 buttons (+, -, F, TR) and display, press TR, then press 2 times the button on FZ and wait +/- 10 seconds;
- In other cases there is a “universal” instruction: Press and hold the button “P” +/- 2 seconds until the LED flashes, then press 2 times the button on the FZ and the LED goes out.
In all cases it is recommended to wait until the receiver returns to normal mode.
With existing remote:
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> KL: Doorhan 433Mhz or 315Mhz depends on your receiver (find out by reading your existing remote)

View File

@@ -24,6 +24,7 @@ if you need your custom one, make sure it doesn't listed here
/* 300 - 348 */
300000000,
302757000,
303000000,
303875000,
303900000,
304250000,
@@ -80,6 +81,7 @@ if you need your custom one, make sure it doesn't listed here
779000000,
868350000,
868400000,
868460000,
868800000,
868950000,
906400000,
@@ -111,10 +113,8 @@ Your frequencies will be added after default ones
### Default hopper list
```
310000000,
315000000,
318000000,
418000000,
433920000,
434420000,
868350000,
```

View File

@@ -239,9 +239,15 @@ static bool subghz_protocol_keeloq_gen_data(
(strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0) ||
(strcmp(instance->manufacture_name, "Came_Space") == 0) ||
(strcmp(instance->manufacture_name, "Genius_Bravo") == 0) ||
(strcmp(instance->manufacture_name, "GSN") == 0)) {
(strcmp(instance->manufacture_name, "GSN") == 0) ||
(strcmp(instance->manufacture_name, "Rosh") == 0) ||
(strcmp(instance->manufacture_name, "Rossi") == 0) ||
(strcmp(instance->manufacture_name, "Pecinin") == 0) ||
(strcmp(instance->manufacture_name, "Steelmate") == 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
decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 |
instance->generic.cnt;
} else if(
@@ -251,9 +257,12 @@ static bool subghz_protocol_keeloq_gen_data(
// Nice Smilo, MHouse, JCM -> 8bit serial - simple learning
decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 |
instance->generic.cnt;
} else if(strcmp(instance->manufacture_name, "Beninca") == 0) {
} else if(
(strcmp(instance->manufacture_name, "Beninca") == 0) ||
(strcmp(instance->manufacture_name, "Merlin") == 0)) {
decrypt = btn << 28 | (0x000) << 16 | instance->generic.cnt;
// Beninca / Allmatic -> no serial - simple XOR
// Merlin -> no serial - simple XOR
} else if(strcmp(instance->manufacture_name, "Centurion") == 0) {
decrypt = btn << 28 | (0x1CE) << 16 | instance->generic.cnt;
// Centurion -> no serial in hop, uses fixed value 0x1CE - normal learning

View File

@@ -167,7 +167,7 @@ static void subghz_protocol_encoder_marantec_get_upload(SubGhzProtocolEncoderMar
}
uint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len) {
uint8_t crc = 0x08;
uint8_t crc = 0x01;
size_t i, j;
for(i = 0; i < len; i++) {
crc ^= data[i];
@@ -186,6 +186,18 @@ uint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len) {
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_marantec_remote_controller(SubGhzBlockGeneric* instance) {
// Key samples
// 1307EDF6486C5 = 000 100110000 01111110110111110110 0100 10000110 11000101
// 1303EFAFD8683 = 000 100110000 00111110111110101111 1101 10000110 10000011
// From unittests
// 1300710DF869F
// const serial button serial crc
// 130 7EDF6 4 86 C5
// 130 3EFAF D 86 83
// 130 0710D F 86 9F
instance->btn = (instance->data >> 16) & 0xF;
instance->serial = ((instance->data >> 12) & 0xFFFFFF00) | ((instance->data >> 8) & 0xFF);
}
@@ -369,16 +381,30 @@ void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* outp
SubGhzProtocolDecoderMarantec* instance = context;
subghz_protocol_marantec_remote_controller(&instance->generic);
uint8_t tdata[6] = {
instance->generic.data >> 48,
instance->generic.data >> 40,
instance->generic.data >> 32,
instance->generic.data >> 24,
instance->generic.data >> 16,
instance->generic.data >> 8};
uint8_t crc = subghz_protocol_marantec_crc8(tdata, sizeof(tdata));
bool crc_ok = (crc == (instance->generic.data & 0xFF));
furi_string_cat_printf(
output,
"%s %db\r\n"
"Key:0x%lX%08lX\r\n"
"Sn:0x%07lX \r\n"
"Btn:%X\r\n",
"Key: 0x%lX%08lX\r\n"
"Sn: 0x%07lX \r\n"
"CRC: 0x%02X - %s\r\n"
"Btn: %X\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data >> 32),
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
instance->generic.serial,
crc,
crc_ok ? "Valid" : "Invalid",
instance->generic.btn);
}

View File

@@ -107,3 +107,11 @@ SubGhzProtocolStatus
* @param output Resulting text
*/
void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output);
/**
* Calculate CRC8 for Marantec protocol.
* @param data Pointer to the data buffer
* @param len Length of the data buffer
* @return CRC8 value
*/
uint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len);

View File

@@ -219,6 +219,11 @@ void subghz_protocol_decoder_marantec24_feed(void* context, bool level, volatile
// Marantec24 Decoder
// 2024 - @xMasterX (MMX)
// 2025 update - The protocol is not real marantec,
// it comes from chinese remote that pretends to be replica of original marantec, actually it was a cloner
// which had some thing written on it, which is uknown, but since its pretentding to be marantec,
// it was decided to keep the name of the protocol as marantec24 (24 bits)
// Key samples
// 101011000000010111001000 = AC05C8
// 101011000000010111000100 = AC05C4
@@ -268,16 +273,12 @@ void subghz_protocol_decoder_marantec24_feed(void* context, bool level, volatile
//Found next GAP and add bit 0 or 1 (only bit 0 was found on the remotes)
if((DURATION_DIFF(
instance->decoder.te_last, subghz_protocol_marantec24_const.te_long) <
subghz_protocol_marantec24_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_long * 9) <
subghz_protocol_marantec24_const.te_delta * 4)) {
subghz_protocol_marantec24_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
}
if((DURATION_DIFF(
instance->decoder.te_last, subghz_protocol_marantec24_const.te_short) <
subghz_protocol_marantec24_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_long * 9) <
subghz_protocol_marantec24_const.te_delta * 4)) {
subghz_protocol_marantec24_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
}
// If got 24 bits key reading is finished

View File

@@ -387,6 +387,36 @@ SubGhzProtocolStatus
}
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_nero_radio_parse_data(SubGhzBlockGeneric* instance) {
// Key samples from unit tests
// 57250501049DD3
// 57250502049D13
//
// Samples from remote
// 36E4E80104A644
// 36E4E80204A684
// 36E4E80304A604
// 36E4E80404A6E4
// possible contents
// serial button serial/const crc??
// 5725050 1 049D D3
// 5725050 2 049D 13
// 36E4E80 1 04A6 44
// 36E4E80 2 04A6 84
// 36E4E80 3 04A6 04
// 36E4E80 4 04A6 E4
// serial is larger than uint32 can't fit into serial field
// using data2 var since its uint64_t
instance->btn = (instance->data >> 24) & 0xF;
instance->data_2 = ((instance->data >> 28) << 16) | ((instance->data >> 8) & 0xFFFF);
}
void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output) {
furi_assert(context);
SubGhzProtocolDecoderNeroRadio* instance = context;
@@ -400,15 +430,23 @@ void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* ou
uint32_t code_found_reverse_hi = code_found_reverse >> 32;
uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;
subghz_protocol_nero_radio_parse_data(&instance->generic);
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Key:0x%lX%08lX\r\n"
"Yek:0x%lX%08lX\r\n",
"Yek:0x%lX%08lX\r\n"
"Sn: 0x%llX \r\n"
"CRC?: 0x%02X\r\n"
"Btn: %X\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
code_found_hi,
code_found_lo,
code_found_reverse_hi,
code_found_reverse_lo);
code_found_reverse_lo,
instance->generic.data_2,
(uint8_t)(instance->generic.data & 0xFF),
instance->generic.btn);
}

View File

@@ -6,9 +6,9 @@
#include "../blocks/generic.h"
#include "../blocks/math.h"
#define TAG "SubGhzProtocolPhoenixV2"
#include "../blocks/custom_btn_i.h"
//transmission only static mode
#define TAG "SubGhzProtocolPhoenixV2"
static const SubGhzBlockConst subghz_protocol_phoenix_v2_const = {
.te_short = 427,
@@ -64,7 +64,7 @@ const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder = {
const SubGhzProtocol subghz_protocol_phoenix_v2 = {
.name = SUBGHZ_PROTOCOL_PHOENIX_V2_NAME,
.type = SubGhzProtocolTypeStatic,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
@@ -93,6 +93,138 @@ void subghz_protocol_encoder_phoenix_v2_free(void* context) {
free(instance);
}
// 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);
bool subghz_protocol_phoenix_v2_create_data(
void* context,
FlipperFormat* flipper_format,
uint32_t serial,
uint16_t cnt,
SubGhzRadioPreset* preset) {
furi_assert(context);
SubGhzProtocolEncoderPhoenix_V2* instance = context;
instance->generic.btn = 0x1;
instance->generic.serial = serial;
instance->generic.cnt = cnt;
instance->generic.data_count_bit = 52;
uint64_t local_data_rev =
(uint64_t)(((uint64_t)instance->generic.cnt << 40) |
((uint64_t)instance->generic.btn << 32) | (uint64_t)instance->generic.serial);
uint16_t encrypted_counter = (uint16_t)subghz_protocol_phoenix_v2_encrypt_counter(
local_data_rev, instance->generic.cnt);
instance->generic.data = subghz_protocol_blocks_reverse_key(
(uint64_t)(((uint64_t)encrypted_counter << 40) | ((uint64_t)instance->generic.btn << 32) |
(uint64_t)instance->generic.serial),
instance->generic.data_count_bit + 4);
return SubGhzProtocolStatusOk ==
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
}
// Get custom button code
static uint8_t subghz_protocol_phoenix_v2_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 0x1:
btn = 0x2;
break;
case 0x2:
btn = 0x1;
break;
case 0x4:
btn = 0x1;
break;
case 0x8:
btn = 0x1;
break;
case 0x3:
btn = 0x1;
break;
default:
break;
}
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) {
switch(original_btn_code) {
case 0x1:
btn = 0x4;
break;
case 0x2:
btn = 0x4;
break;
case 0x4:
btn = 0x2;
break;
case 0x8:
btn = 0x4;
break;
case 0x3:
btn = 0x4;
break;
default:
break;
}
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) {
switch(original_btn_code) {
case 0x1:
btn = 0x8;
break;
case 0x2:
btn = 0x8;
break;
case 0x4:
btn = 0x8;
break;
case 0x8:
btn = 0x2;
break;
case 0x3:
btn = 0x8;
break;
default:
break;
}
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_RIGHT) {
switch(original_btn_code) {
case 0x1:
btn = 0x3;
break;
case 0x2:
btn = 0x3;
break;
case 0x4:
btn = 0x3;
break;
case 0x8:
btn = 0x3;
break;
case 0x3:
btn = 0x2;
break;
default:
break;
}
}
return btn;
}
/**
* Generating an upload from data.
* @param instance Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
@@ -109,6 +241,40 @@ static bool
} else {
instance->encoder.size_upload = size_upload;
}
uint8_t btn = instance->generic.btn;
// Save original button for later use
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(btn);
}
// Get custom button code
// This will override the btn variable if a custom button is set
btn = subghz_protocol_phoenix_v2_get_btn_code();
// Reconstruction of the data
if(instance->generic.cnt < 0xFFFF) {
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 >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
instance->generic.cnt = 0;
}
uint64_t local_data_rev = subghz_protocol_blocks_reverse_key(
instance->generic.data, instance->generic.data_count_bit + 4);
uint16_t encrypted_counter = (uint16_t)subghz_protocol_phoenix_v2_encrypt_counter(
local_data_rev, instance->generic.cnt);
instance->generic.data = subghz_protocol_blocks_reverse_key(
(uint64_t)(((uint64_t)encrypted_counter << 40) | ((uint64_t)btn << 32) |
(uint64_t)instance->generic.serial),
instance->generic.data_count_bit + 4);
//Send header
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 60);
@@ -151,10 +317,22 @@ SubGhzProtocolStatus
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic);
if(!subghz_protocol_encoder_phoenix_v2_get_upload(instance)) {
ret = SubGhzProtocolStatusErrorEncoderGetUpload;
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;
}
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Unable to add Key");
break;
}
instance->encoder.is_running = true;
} while(false);
@@ -276,16 +454,103 @@ void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t
}
}
static uint16_t subghz_protocol_phoenix_v2_encrypt_counter(uint64_t full_key, uint16_t counter) {
uint8_t xor_key1 = (uint8_t)(full_key >> 24); // First byte of serial
uint8_t xor_key2 = (uint8_t)((full_key >> 16) & 0xFF); // Second byte of serial
uint8_t byte2 = (uint8_t)(counter >> 8); // First counter byte
uint8_t byte1 = (uint8_t)(counter & 0xFF); // Second counter byte
// See decrypt function before reading these comments
for(int i = 0; i < 16; i++) {
// The key to reversing the process is that the MSB of the *current* byte2
// tells us what the MSB of the *previous* byte1 was. This allows us to
// determine if the conditional XOR was applied before?.
uint8_t msb_of_prev_byte1 = byte2 & 0x80;
if(msb_of_prev_byte1 == 0) {
// reverse the XOR.
byte2 ^= xor_key2;
byte1 ^= xor_key1;
}
// Perform the bit shuffle in reverse
// Store the least significant bit (LSB) of the current byte1.
uint8_t lsb_of_current_byte1 = byte1 & 1;
byte2 = (byte2 << 1) | lsb_of_current_byte1;
byte1 = (byte1 >> 1) | msb_of_prev_byte1;
}
return (uint16_t)byte1 << 8 | byte2;
}
static uint16_t subghz_protocol_phoenix_v2_decrypt_counter(uint64_t full_key) {
uint16_t encrypted_value = (uint16_t)((full_key >> 40) & 0xFFFF);
uint8_t byte1 = (uint8_t)(encrypted_value >> 8); // First encrypted counter byte
uint8_t byte2 = (uint8_t)(encrypted_value & 0xFF); // Second encrypted counter byte
uint8_t xor_key1 = (uint8_t)(full_key >> 24); // First byte of serial
uint8_t xor_key2 = (uint8_t)((full_key >> 16) & 0xFF); // Second byte of serial
for(int i = 0; i < 16; i++) {
// Store the most significant bit (MSB) of byte1.
// The check `(msb_of_byte1 == 0)` will determine if we apply the XOR keys.
uint8_t msb_of_byte1 = byte1 & 0x80;
// Store the least significant bit (LSB) of byte2.
uint8_t lsb_of_byte2 = byte2 & 1;
// Perform a bit shuffle between the two bytes
byte2 = (byte2 >> 1) | msb_of_byte1;
byte1 = (byte1 << 1) | lsb_of_byte2;
// Conditionally apply the XOR keys based on the original MSB of byte1.
if(msb_of_byte1 == 0) {
byte1 ^= xor_key1;
// The mask `& 0x7F` clears the MSB of byte2 after the XOR.
byte2 = (byte2 ^ xor_key2) & 0x7F;
}
}
return (uint16_t)byte2 << 8 | byte1;
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneric* instance) {
// 2022.08 - @Skorpionm
// 2025.07 - @xMasterX & @RocketGod-git
// Fully supported now, with button switch and add manually
//
// Key samples
// Full key example: 0xC63E01B9615720 - after subghz_protocol_blocks_reverse_key was applied
// Serial - B9615720
// Button - 01
// Encrypted -> Decrypted counters
// C63E - 025C
// BCC1 - 025D
// 3341 - 025E
// 49BE - 025F
// 99D3 - 0260
// E32C - 0261
uint64_t data_rev =
subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit + 4);
instance->serial = data_rev & 0xFFFFFFFF;
instance->cnt = (data_rev >> 40) & 0xFFFF;
instance->cnt = subghz_protocol_phoenix_v2_decrypt_counter(data_rev);
instance->btn = (data_rev >> 32) & 0xF;
// encrypted cnt is (data_rev >> 40) & 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(4);
}
uint32_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) {
@@ -320,15 +585,15 @@ void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* ou
subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic);
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"V2 Phoenix %dbit\r\n"
"Key:%05lX%08lX\r\n"
"Sn:0x%07lX \r\n"
"Btn:%X Cnt: 0x%04lX\r\n",
instance->generic.protocol_name,
"Cnt: 0x%04lX\r\n"
"Btn: %X\r\n",
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF,
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
instance->generic.serial,
instance->generic.btn,
instance->generic.cnt);
instance->generic.cnt,
instance->generic.btn);
}

View File

@@ -82,6 +82,7 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = {
&subghz_protocol_hay21,
&subghz_protocol_revers_rb2,
&subghz_protocol_feron,
&subghz_protocol_roger,
};
const SubGhzProtocolRegistry subghz_protocol_registry = {

View File

@@ -83,3 +83,4 @@
#include "hay21.h"
#include "revers_rb2.h"
#include "feron.h"
#include "roger.h"

View File

@@ -123,6 +123,22 @@ bool subghz_protocol_came_atomo_create_data(
uint16_t cnt,
SubGhzRadioPreset* preset);
/**
* Key generation from simple data.
* @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param serial Serial number
* @param cnt Counter value, 16 bit
* @param preset Modulation, SubGhzRadioPreset
* @return true On success
*/
bool subghz_protocol_phoenix_v2_create_data(
void* context,
FlipperFormat* flipper_format,
uint32_t serial,
uint16_t cnt,
SubGhzRadioPreset* preset);
/**
* New remote generation.
* @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance

View File

@@ -0,0 +1,449 @@
#include "roger.h"
#include "../blocks/const.h"
#include "../blocks/decoder.h"
#include "../blocks/encoder.h"
#include "../blocks/generic.h"
#include "../blocks/math.h"
#include "../blocks/custom_btn_i.h"
#define TAG "SubGhzProtocolRoger"
static const SubGhzBlockConst subghz_protocol_roger_const = {
.te_short = 500,
.te_long = 1000,
.te_delta = 270,
.min_count_bit_for_found = 28,
};
struct SubGhzProtocolDecoderRoger {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
};
struct SubGhzProtocolEncoderRoger {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
};
typedef enum {
RogerDecoderStepReset = 0,
RogerDecoderStepSaveDuration,
RogerDecoderStepCheckDuration,
} RogerDecoderStep;
const SubGhzProtocolDecoder subghz_protocol_roger_decoder = {
.alloc = subghz_protocol_decoder_roger_alloc,
.free = subghz_protocol_decoder_roger_free,
.feed = subghz_protocol_decoder_roger_feed,
.reset = subghz_protocol_decoder_roger_reset,
.get_hash_data = subghz_protocol_decoder_roger_get_hash_data,
.serialize = subghz_protocol_decoder_roger_serialize,
.deserialize = subghz_protocol_decoder_roger_deserialize,
.get_string = subghz_protocol_decoder_roger_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_roger_encoder = {
.alloc = subghz_protocol_encoder_roger_alloc,
.free = subghz_protocol_encoder_roger_free,
.deserialize = subghz_protocol_encoder_roger_deserialize,
.stop = subghz_protocol_encoder_roger_stop,
.yield = subghz_protocol_encoder_roger_yield,
};
const SubGhzProtocol subghz_protocol_roger = {
.name = SUBGHZ_PROTOCOL_ROGER_NAME,
.type = SubGhzProtocolTypeStatic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_roger_decoder,
.encoder = &subghz_protocol_roger_encoder,
};
void* subghz_protocol_encoder_roger_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderRoger* instance = malloc(sizeof(SubGhzProtocolEncoderRoger));
instance->base.protocol = &subghz_protocol_roger;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 256;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
return instance;
}
void subghz_protocol_encoder_roger_free(void* context) {
furi_assert(context);
SubGhzProtocolEncoderRoger* instance = context;
free(instance->encoder.upload);
free(instance);
}
// Get custom button code
static uint8_t subghz_protocol_roger_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 0x1:
btn = 0x2;
break;
case 0x2:
btn = 0x1;
break;
case 0x4:
btn = 0x1;
break;
case 0x8:
btn = 0x1;
break;
default:
break;
}
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) {
switch(original_btn_code) {
case 0x1:
btn = 0x4;
break;
case 0x2:
btn = 0x4;
break;
case 0x4:
btn = 0x2;
break;
case 0x8:
btn = 0x4;
break;
default:
break;
}
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) {
switch(original_btn_code) {
case 0x1:
btn = 0x8;
break;
case 0x2:
btn = 0x8;
break;
case 0x4:
btn = 0x8;
break;
case 0x8:
btn = 0x2;
break;
default:
break;
}
}
return btn;
}
/**
* Generating an upload from data.
* @param instance Pointer to a SubGhzProtocolEncoderRoger instance
*/
static void subghz_protocol_encoder_roger_get_upload(SubGhzProtocolEncoderRoger* instance) {
furi_assert(instance);
size_t index = 0;
uint8_t btn = instance->generic.btn;
// Save original button for later use
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(btn);
}
// Get custom button code
// This will override the btn variable if a custom button is set
btn = subghz_protocol_roger_get_btn_code();
// If End is not == button - transmit as is, no custom button allowed
// For "End" values 23 and 20 - transmit correct ending used for their buttons
if((instance->generic.data & 0xFF) == instance->generic.btn) {
instance->generic.data = (uint64_t)instance->generic.serial << 12 | ((uint64_t)btn << 8) |
btn;
} else if(((instance->generic.data & 0xFF) == 0x23) && btn == 0x1) {
instance->generic.data = (uint64_t)instance->generic.serial << 12 | ((uint64_t)btn << 8) |
0x20;
} else if(((instance->generic.data & 0xFF) == 0x20) && btn == 0x2) {
instance->generic.data = (uint64_t)instance->generic.serial << 12 | ((uint64_t)btn << 8) |
0x23;
}
// Send key and GAP
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
if(bit_read(instance->generic.data, i - 1)) {
// Send bit 1
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_roger_const.te_long);
if(i == 1) {
//Send gap if bit was last
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_roger_const.te_short * 19);
} else {
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_roger_const.te_short);
}
} else {
// Send bit 0
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_roger_const.te_short);
if(i == 1) {
//Send gap if bit was last
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_roger_const.te_short * 19);
} else {
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_roger_const.te_long);
}
}
}
instance->encoder.size_upload = index;
return;
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_roger_check_remote_controller(SubGhzBlockGeneric* instance) {
// Roger Decoder
// 2025.07 - @xMasterX (MMX)
// Key samples
// 0010001111111001 0001 00100000 // S/N: 0x23F9 Btn: 0x1 End: 0x20
// 0010001111111001 0010 00100011 // S/N: 0x23F9 Btn: 0x2 End: 0x23
// 0101011001010110 0001 00000001 // S/N: 0x5656 Btn: 0x1 End: 0x01
// 0101011001010110 0010 00000010 // S/N: 0x5656 Btn: 0x2 End: 0x02
// 0000110111111110 0001 00000001 // S/N: 0x0DFE Btn: 0x1 End: 0x01
// 0000110111111110 0100 00000100 // S/N: 0x0DFE Btn: 0x4 End: 0x04
// 0000110111111110 0010 00000010 // S/N: 0x0DFE Btn: 0x2 End: 0x02
// 0000110111111110 1000 00001000 // S/N: 0x0DFE Btn: 0x8 End: 0x08
instance->serial = instance->data >> 12;
instance->btn = (instance->data >> 8) & 0xF;
// 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);
}
SubGhzProtocolStatus
subghz_protocol_encoder_roger_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolEncoderRoger* instance = context;
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
do {
ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
subghz_protocol_roger_const.min_count_bit_for_found);
if(ret != SubGhzProtocolStatusOk) {
break;
}
//optional parameter parameter
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_roger_check_remote_controller(&instance->generic);
subghz_protocol_encoder_roger_get_upload(instance);
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;
}
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Unable to add Key");
break;
}
instance->encoder.is_running = true;
} while(false);
return ret;
}
void subghz_protocol_encoder_roger_stop(void* context) {
SubGhzProtocolEncoderRoger* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_roger_yield(void* context) {
SubGhzProtocolEncoderRoger* instance = context;
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
instance->encoder.is_running = false;
return level_duration_reset();
}
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
instance->encoder.front = 0;
}
return ret;
}
void* subghz_protocol_decoder_roger_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderRoger* instance = malloc(sizeof(SubGhzProtocolDecoderRoger));
instance->base.protocol = &subghz_protocol_roger;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_roger_free(void* context) {
furi_assert(context);
SubGhzProtocolDecoderRoger* instance = context;
free(instance);
}
void subghz_protocol_decoder_roger_reset(void* context) {
furi_assert(context);
SubGhzProtocolDecoderRoger* instance = context;
instance->decoder.parser_step = RogerDecoderStepReset;
}
void subghz_protocol_decoder_roger_feed(void* context, bool level, volatile uint32_t duration) {
furi_assert(context);
SubGhzProtocolDecoderRoger* instance = context;
switch(instance->decoder.parser_step) {
case RogerDecoderStepReset:
if((!level) && (DURATION_DIFF(duration, subghz_protocol_roger_const.te_short * 19) <
subghz_protocol_roger_const.te_delta * 5)) {
//Found GAP
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->decoder.parser_step = RogerDecoderStepSaveDuration;
}
break;
case RogerDecoderStepSaveDuration:
if(level) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = RogerDecoderStepCheckDuration;
} else {
instance->decoder.parser_step = RogerDecoderStepReset;
}
break;
case RogerDecoderStepCheckDuration:
if(!level) {
// Bit 1 is long and short timing = 1000us HIGH (te_last) and 500us LOW
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_long) <
subghz_protocol_roger_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_roger_const.te_short) <
subghz_protocol_roger_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = RogerDecoderStepSaveDuration;
// Bit 0 is short and long timing = 500us HIGH (te_last) and 1000us LOW
} else if(
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_short) <
subghz_protocol_roger_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_roger_const.te_long) <
subghz_protocol_roger_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = RogerDecoderStepSaveDuration;
} else if(
// End of the key
DURATION_DIFF(duration, subghz_protocol_roger_const.te_short * 19) <
subghz_protocol_roger_const.te_delta * 5) {
//Found next GAP and add bit 1 or 0
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_long) <
subghz_protocol_roger_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
}
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_short) <
subghz_protocol_roger_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
}
// If got full 28 bits key reading is finished
if(instance->decoder.decode_count_bit ==
subghz_protocol_roger_const.min_count_bit_for_found) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->decoder.parser_step = RogerDecoderStepReset;
} else {
instance->decoder.parser_step = RogerDecoderStepReset;
}
} else {
instance->decoder.parser_step = RogerDecoderStepReset;
}
break;
}
}
uint8_t subghz_protocol_decoder_roger_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderRoger* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus subghz_protocol_decoder_roger_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
SubGhzProtocolDecoderRoger* instance = context;
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
}
SubGhzProtocolStatus
subghz_protocol_decoder_roger_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolDecoderRoger* instance = context;
return subghz_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, subghz_protocol_roger_const.min_count_bit_for_found);
}
void subghz_protocol_decoder_roger_get_string(void* context, FuriString* output) {
furi_assert(context);
SubGhzProtocolDecoderRoger* instance = context;
subghz_protocol_roger_check_remote_controller(&instance->generic);
furi_string_cat_printf(
output,
"%s %db\r\n"
"Key: 0x%07lX\r\n"
"Serial: 0x%04lX\r\n"
"End: 0x%02lX\r\n"
"Btn: %01X",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data & 0xFFFFFFF),
instance->generic.serial,
(uint32_t)(instance->generic.data & 0xFF),
instance->generic.btn);
}

View File

@@ -0,0 +1,109 @@
#pragma once
#include "base.h"
#define SUBGHZ_PROTOCOL_ROGER_NAME "Roger"
typedef struct SubGhzProtocolDecoderRoger SubGhzProtocolDecoderRoger;
typedef struct SubGhzProtocolEncoderRoger SubGhzProtocolEncoderRoger;
extern const SubGhzProtocolDecoder subghz_protocol_roger_decoder;
extern const SubGhzProtocolEncoder subghz_protocol_roger_encoder;
extern const SubGhzProtocol subghz_protocol_roger;
/**
* Allocate SubGhzProtocolEncoderRoger.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolEncoderRoger* pointer to a SubGhzProtocolEncoderRoger instance
*/
void* subghz_protocol_encoder_roger_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolEncoderRoger.
* @param context Pointer to a SubGhzProtocolEncoderRoger instance
*/
void subghz_protocol_encoder_roger_free(void* context);
/**
* Deserialize and generating an upload to send.
* @param context Pointer to a SubGhzProtocolEncoderRoger instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return status
*/
SubGhzProtocolStatus
subghz_protocol_encoder_roger_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Forced transmission stop.
* @param context Pointer to a SubGhzProtocolEncoderRoger instance
*/
void subghz_protocol_encoder_roger_stop(void* context);
/**
* Getting the level and duration of the upload to be loaded into DMA.
* @param context Pointer to a SubGhzProtocolEncoderRoger instance
* @return LevelDuration
*/
LevelDuration subghz_protocol_encoder_roger_yield(void* context);
/**
* Allocate SubGhzProtocolDecoderRoger.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolDecoderRoger* pointer to a SubGhzProtocolDecoderRoger instance
*/
void* subghz_protocol_decoder_roger_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolDecoderRoger.
* @param context Pointer to a SubGhzProtocolDecoderRoger instance
*/
void subghz_protocol_decoder_roger_free(void* context);
/**
* Reset decoder SubGhzProtocolDecoderRoger.
* @param context Pointer to a SubGhzProtocolDecoderRoger instance
*/
void subghz_protocol_decoder_roger_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a SubGhzProtocolDecoderRoger instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void subghz_protocol_decoder_roger_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a SubGhzProtocolDecoderRoger instance
* @return hash Hash sum
*/
uint8_t subghz_protocol_decoder_roger_get_hash_data(void* context);
/**
* Serialize data SubGhzProtocolDecoderRoger.
* @param context Pointer to a SubGhzProtocolDecoderRoger instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return status
*/
SubGhzProtocolStatus subghz_protocol_decoder_roger_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data SubGhzProtocolDecoderRoger.
* @param context Pointer to a SubGhzProtocolDecoderRoger instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return status
*/
SubGhzProtocolStatus
subghz_protocol_decoder_roger_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a SubGhzProtocolDecoderRoger instance
* @param output Resulting text
*/
void subghz_protocol_decoder_roger_get_string(void* context, FuriString* output);

View File

@@ -14,6 +14,7 @@ static const uint32_t subghz_frequency_list[] = {
/* 300 - 348 */
300000000,
302757000,
303000000,
303875000,
303900000,
304250000,
@@ -70,6 +71,7 @@ static const uint32_t subghz_frequency_list[] = {
779000000,
868350000,
868400000,
868460000,
868800000,
868950000,
906400000,
@@ -80,11 +82,9 @@ static const uint32_t subghz_frequency_list[] = {
};
static const uint32_t subghz_hopper_frequency_list[] = {
310000000,
315000000,
318000000,
418000000,
433920000,
434420000,
868350000,
0,
};

View File

@@ -3674,6 +3674,7 @@ Function,+,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, ui
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_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*"
Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*, const char*"
Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW*
1 entry status name type params
3674 Function + subghz_protocol_keeloq_bft_create_data _Bool void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*
3675 Function + subghz_protocol_keeloq_create_data _Bool void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*
3676 Function + subghz_protocol_nice_flor_s_create_data _Bool void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*, _Bool
3677 Function + subghz_protocol_phoenix_v2_create_data _Bool void*, FlipperFormat*, uint32_t, uint16_t, SubGhzRadioPreset*
3678 Function + subghz_protocol_raw_file_encoder_worker_set_callback_end void SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*
3679 Function + subghz_protocol_raw_gen_fff_data void FlipperFormat*, const char*, const char*
3680 Function + subghz_protocol_raw_get_sample_write size_t SubGhzProtocolDecoderRAW*