mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Merge remote-tracking branch 'ul/dev' into mntm-dev --nobuild
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -83,3 +83,4 @@
|
||||
#include "hay21.h"
|
||||
#include "revers_rb2.h"
|
||||
#include "feron.h"
|
||||
#include "roger.h"
|
||||
|
||||
@@ -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
|
||||
|
||||
449
lib/subghz/protocols/roger.c
Normal file
449
lib/subghz/protocols/roger.c
Normal 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);
|
||||
}
|
||||
109
lib/subghz/protocols/roger.h
Normal file
109
lib/subghz/protocols/roger.h
Normal 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);
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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*
|
||||
|
||||
|
Reference in New Issue
Block a user