This commit is contained in:
Dmitry422
2025-05-19 00:40:04 +07:00
33 changed files with 514 additions and 288 deletions

View File

@@ -1,93 +1,23 @@
## Main changes
- Current API: 86.0
**WARNING! After install of this version your Desktop (fav apps) and LCD & Notifications settings will be reset to default values, please configure them again after this update!** (this is required due to big updates on that parts and config struct changes)
* SubGHz: Add **Feron** protocol (static 32 bit) **full support** (by @xMasterX)
* SubGHz: Add **Revers RB2 / RB2M Protocol** (static 64 bit) **full support** with add manually (by @xMasterX)
* SubGHz: **Fix Hollarm protocol with more verification**
* SubGHz: **Fix GangQi protocol** (by @DoberBit and @mishamyte (who spent 2 weeks on this))
* SubGHz: **Came Atomo button hold simulation with full cycle** simulation (to allow proper pairing with receiver)
* SubGHz: Add **Prastel (42bit static code)** support (OFW PR 4178 by @pmazzini)
* Desktop: **Add support for Favorite App - Ok Long** (Warning! Old favourites apps list will be reset!) (PR #886 | by @DrEverr)
* Display: **LCD Color Inversion** (Settings - LCD and Notifications - LCD Inversion) (PR #887 #893 | by @Dmitry422)
* Display: **Night Shift Feature** (dimming backlight in selected time interval) (PR #885 | by @Dmitry422)
* Display: **Сombining RGB Backlight mod** (by @quen0n) and original backlight support **in one firmware** (+ Rainbow/Wave effect (based on @Willy-JL idea)) (PR #877 #881 #890 | by @Dmitry422) - (**To enable RGB Backlight support go into Notifications settings**)
* NFC: Use default UL/UL-C pwd/key as default value for key input (PR #891 | by @mishamyte)
* OFW: LFRFID - **EM4305 support**
* OFW: **Universal IR signal selection**
* OFW: **BadUSB: Mouse control**
* OFW: **Pinning of settings options**
* OFW: NFC app now can launch MFKey32
* OFW: BadUSB arbitrary key combinations
* OFW PR 4136: BadUSB: Full USB/BLE parameter customization, UI improvements, and more (by @Willy-JL)
* OFW: NFC - Added naming for DESFire cards + fix MF3ICD40 cards unable to be read
* Apps: Add **FindMyFlipper to system apps and allow autostart** on system boot [app by @MatthewKuKanich](https://github.com/MatthewKuKanich/FindMyFlipper) and autoloader by @Willy-JL - to use app please check how to add keys in [app repo](https://github.com/MatthewKuKanich/FindMyFlipper)
* README Update: Enhanced Visuals & Navigation (PR #871 #872 | by @m-xim)
* Docs: Update FAQ.md (PR #865 | by @mi-lrn)
* Input: **Vibro on Button press option** (PR #867 | by @Dmitry422)
* Power: **Option to limit battery charging** (suppress charging on selected charge level) (PR #867 | by @Dmitry422) (idea and example by @oltenxyz)
* iButton: TM01x Dallas write support (PR #899 | by @Leptopt1los)
* SubGHz: Add keeloq ironlogic (aka il100) smart clone cloners support (thanks to Vitaly for RAWs)
* SubGHz: Fix CAME 24bit decoder
* SubGHz: Add 462.750 MHz to default subghz freqs list
* SubGHz: Tune holtek ht12x to decode holtek only and not conflict with came 12bit
* SubGHz: Fix Rename scene bug, that was replacing file name with random name when Rename is opened then closed then opened again
* NFC: Ultralight C - Attempt of authentication with default key (PR #898 | by @mishamyte)
* OFW PR 4210: Infrared: Add text scroll to remote buttons (by @956MB)
* OFW PR 4205: fix sample durations when using external CC1101 (by @Aerosnail)
* OFW PR 4206: Stop JS PWM on exit (by @portasynthinca3)
* OFW PR 4212: Remove stupid "!" that broke subghz chat cli (by @GameLord2011)
* Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
## Other changes
* SubGHz: Move hardcoded extra modulations to user config - uncomment them in setting_user.example and remove .example from filename
* SubGHz: Various bugfixes and experimental options (rolling counter overflow) (by @xMasterX)
* Anims: Disable winter anims
* NFC: mfclassic poller fix early key reuse in dictionary attack state machine (by @noproto)
* OFW: RC fixes
* OFW: Desktop: Fix freeze on boot if PIN set
* OFW PR 4189: USB-UART bridge fix (by @portasynthinca3)
* OFW: FBT: Fix for Python 3.13
* OFW: sdk: bump API to force re-upload for the catalog
* OFW: SDK: Fix missing RECORD_CLI define
* OFW: Fix NULL dereference in CLI completions
* OFW PR 4181: vcp, cli: Handle Tx/Rx events before Connect/Disconnect + extra fixes (by @portasynthinca3)
* OFW: BLE: Slightly increase mfg_data size
* OFW: fbt: Deterministic STARTUP order & additional checks
* OFW: JS: Update and fix docs, fix Number.toString() with decimals
* OFW: New JS value destructuring
* OFW: Docs: Fix doxygen references from PR 4168
* OFW: BLE advertising improvements
* OFW: **New CLI architecture**
* OFW: **CLI autocomplete and other sugar**
* OFW: CLI commands in fals and threads
* OFW: cli: fixed `free_blocks` command
* OFW: docs: badusb arbitrary modkey chains
* OFW: Separate cli_shell into toolbox
* OFW: Move JS modules to new arg parser
* OFW: Application chaining
* OFW: Fix DWARF dead code elimination and linking
* OFW: NFC: Fix crash on ISO15693-3 save when memory is empty or cannot be read
* OFW: Reduced ieee754 parser size
* OFW: Added Doom animation (by @doomwastaken)
* OFW PR 4133: add nfc apdu cli command back (by @leommxj)
* OFW: NFC: Support DESFire Transaction MAC file type (by @Willy-JL)
* OFW: NFC: Fix NDEF parser for MIFARE Classic (by @Willy-JL)
* OFW: GUI: Fix widget text scroll with 256+ lines (by @Willy-JL)
* OFW: Infrared: Fix universals sending (by @Willy-JL)
* OFW: HID Ble: increased stack and improvements (by @doomwastaken)
* OFW: Stricter constness for const data (by @hedger)
* OFW PR 4017: Alarm improvements: Snooze, timeouts, and dismissing from the locked state (by @Astrrra)
* OFW: fix: flipper detected before it was rebooted
* OFW: NFC: FeliCa Protocol Expose Read Block API and Allow Specifying Service
* OFW: LFRFID: Fix Detection Conflict Between Securakey and Noralsy Format (by @zinongli)
* OFW: Stdio API improvements
* OFW: GUI: Widget view extra options for JS
* OFW: Update heap implementation
* OFW: Updated Button Panel
* OFW: UART framing mode selection
* OFW: gpio: clear irq status before calling user handler
* OFW: Fix 5V on GPIO
* OFW: Fixed repeat in subghz tx_from_file command
* OFW: LFRFID: Noralsy Format/Brand
* OFW: Faster di card reading
* OFW: vscode: disabled auto-update for clangd since correct version is in the toolchain
* OFW: Furi, USB, BLE, Debug: various bug fixes and improvements
* OFW: EventLoop unsubscribe fix
* OFW: nfc: Enable MFUL sync poller to be provided with passwords
* OFW: ST25TB poller mode check
* OFW: JS features & bugfixes (SDK 0.2) **Existing Widget JS module was removed and replaced with new ofw gui/widget module, old apps using widget may be incompatible now!**
* OFW: Infrared: increase max carrier limit
* OFW: Ensure that `furi_record_create` is passed a non-NULL data pointer
* OFW: Update mbedtls & expose AES
* OFW: Add the Showtime animation
* Desktop: DEBUG - fix desktop anim switch override by favourite apps
* CLI: Various fixes (by @WillyJL)
* BadUSB: Fix key combos main keys being case sensitive (by @WillyJL)
* System: log level none after update
* Docs: Some updates on subghz remotes programming
<br><br>
#### Known NFC post-refactor regressions list:
- Mifare Mini clones reading is broken (original mini working fine) (OFW)

View File

@@ -155,7 +155,7 @@ Thanks to Official team (to their SubGHz Developer, Skorp) for implementing supp
> | Came_Space | FAAC_RC,XT | Kingates_Stylo4k | Pantera | Tomahawk_TZ-9030 |
> | Cenmax | FAAC_SLH | KGB/Subaru | Pantera_CLK | Tomahawk_Z,X_3-5 |
> | Cenmax_St-5 | Faraon | Leopard | Pantera_XS/Jaguar | ZX-730-750-1055 |
> | Cenmax_St-7 | Genius_Bravo | Magic_1 | Partisan_RX | |
> | Cenmax_St-7 | Genius_Bravo | Magic_1 | Partisan_RX | IL-100(Smart) |
> | Centurion | Gibidi | Magic_2 | Reff | |
> | Monarch | Jolly Motors | Magic_3 | Sheriff | |
> </details>
@@ -173,7 +173,7 @@ Thanks to Official team (to their SubGHz Developer, Skorp) for implementing supp
- Hay21 (dynamic 21 bit) with button parsing
- Nero Radio 57bit (+ 56bit support)
- CAME 12bit/24bit encoder fixes (Fixes are now merged in OFW)
- Keeloq: Dea Mio, Genius Bravo, GSN, HCS101, AN-Motors, JCM Tech, MHouse, Nice Smilo, DTM Neo, FAAC RC,XT, Mutancode, Normstahl, Beninca + Allmatic, Stilmatic, CAME Space, Aprimatic (model TR and similar), Centurion Nova (thanks Carlos !), Hormann EcoStar, Novoferm, Sommer, Monarch (thanks @ashphx !), Jolly Motors (thanks @pkooiman !)
- Keeloq: Dea Mio, Genius Bravo, GSN, HCS101, AN-Motors, JCM Tech, MHouse, Nice Smilo, DTM Neo, FAAC RC,XT, Mutancode, Normstahl, Beninca + Allmatic, Stilmatic, CAME Space, Aprimatic (model TR and similar), Centurion Nova (thanks Carlos !), Hormann EcoStar, Novoferm, Sommer, Monarch (thanks @ashphx !), Jolly Motors (thanks @pkooiman !), IL-100(Smart) (thx Vitaly for RAWs)
</details>
<details>

View File

@@ -612,11 +612,13 @@ void subghz_device_cc1101_ext_start_async_rx(
furi_hal_bus_enable(FuriHalBusTIM17);
// Configure TIM
LL_TIM_InitTypeDef TIM_InitStruct = {0};
//Set the timer resolution to 2 us
LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1);
LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetAutoReload(TIM17, 0xFFFF);
LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1);
TIM_InitStruct.Prescaler = (64 << 1) - 1;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 0xFFFF;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
LL_TIM_Init(TIM17, &TIM_InitStruct);
// Timer: advanced
LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL);

View File

@@ -213,9 +213,7 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
// Main key
char next_char = *line_cstr;
uint16_t main_key = ducky_get_keycode_by_name(line_cstr);
if(!main_key && next_char) main_key = BADUSB_ASCII_TO_KEY(bad_usb, next_char);
key = modifiers | main_key;
key = modifiers | ducky_get_keycode(bad_usb, line_cstr, true);
if(key == 0 && next_char) ducky_error(bad_usb, "No keycode defined for %s", line_cstr);

View File

@@ -7,7 +7,6 @@
#include <lib/nfc/nfc_poller.h>
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
#include <lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
#include <lib/nfc/protocols/iso15693_3/iso15693_3_poller.h>
#include <toolbox/pipe.h>
#include <furi_hal_nfc.h>
@@ -15,13 +14,12 @@
#define FLAG_EVENT (1 << 10)
#define NFC_MAX_BUFFER_SIZE (256)
#define NFC_BASE_PROTOCOL_MAX (3)
#define NFC_BASE_PROTOCOL_MAX (2)
#define POLLER_DONE (1 << 0)
#define POLLER_ERR (1 << 1)
static NfcProtocol BASE_PROTOCOL[NFC_BASE_PROTOCOL_MAX] = {
NfcProtocolIso14443_4a,
NfcProtocolIso14443_4b,
NfcProtocolIso15693_3};
NfcProtocolIso14443_4b};
typedef struct ApduContext {
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
@@ -88,19 +86,6 @@ static NfcCommand trx_callback(NfcGenericEvent event, void* context) {
furi_thread_flags_set(apdu_context->thread_id, POLLER_ERR);
return NfcCommandStop;
}
} else if(NfcProtocolIso15693_3 == event.protocol) {
Iso15693_3Error err = iso15693_3_poller_send_frame(
event.instance,
apdu_context->tx_buffer,
apdu_context->rx_buffer,
ISO15693_3_FDT_POLL_FC);
if(Iso15693_3ErrorNone == err) {
furi_thread_flags_set(apdu_context->thread_id, POLLER_DONE);
return NfcCommandContinue;
} else {
furi_thread_flags_set(apdu_context->thread_id, POLLER_ERR);
return NfcCommandStop;
}
} else {
// should never reach here
furi_crash("Unknown protocol");

View File

@@ -118,7 +118,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) {
uint32_t current_frequency = subghz_setting_get_frequency(instance->setting, i);
if(furi_hal_subghz_is_frequency_valid(current_frequency) &&
(((current_frequency != 467750000) && (current_frequency != 464000000)) &&
(((current_frequency != 462750000) && (current_frequency != 467750000) &&
(current_frequency != 464000000)) &&
(current_frequency <= 920000000))) {
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);

View File

@@ -1,62 +1,63 @@
Filetype: Flipper SubGhz Keystore File
Version: 0
Encryption: 1
IV: 46 75 72 69 20 63 68 65 63 6B 20 4F 77 4F 21 3F
796353C129CC2B688FE158D36E82001F7450D58DD763BCF4D6FA1CE6C3D598EA
1E7DDEB3B54A42B8993C32AF209CBCC9A1137CE334449F016B993D673EB15C69
F9CB2DDC4E3D45694292C7DE45F1DC0BD74235B36F624AF39E8C3D211A713408
538F46EF4250801434FEA469EE07E8C8BF71C6179442718A05455BB501D797C4
F2BF384DBF7F828E025F020E47D1D637B497FB470444F0C6F9DC67C6830EDDE8A26A6EE89A321D3924D9099895AB2EA1
2CA8A8A4866D5C2B715B520F641B41355A81BA73170842233806D8AA3E3F3D62
80CF5DEF931CF902EE602319F7CD506D42E5FBC06ABBF9F05D474C8E8A2AAC4E
15CB465F7A646226C987BB4928E92A61F350ADCDDE355B717730ADDD1B738950
86B7597ED3EDF6826124ABD7AE419197DC4A93FA064179ACAD853EC670F93995
28263A286F1ADA0E851E8A27AFD7706CF3513D8A24D41CF7E24A925FE3D86305
AF29A08AB877F12681706D71B82B8E2447DA18EBF9731EF3CE91FA5ECAC4E98F8FDC817E0F67C3D7348DD2AF128F1E62
A675C5BB6AC41336B5F5A27FC062FBF30DC39B1E5C498F0FD823261C3177FA58416C5402554742370CB0DAFD2895F6F8
0073790C26BD0224F5560161A4D4C4F8E05498E995FD2BF12EA9926A5127E9A5
235ADFE253B51260DCF5C66F9CB9602B91A0C57BE14D1BE1E11EB309643B29DB
F9C2E3A926AAA108F474075F0B4EDFF3135492B1529EAB150CF78E748BF36E17
50F442CC9D90B9FF8C786245EC442B911A28A04D829D0E45D7D2D4C5A3F5B864
9EA00CD21C0BE1BB15A0CAD31097A44CAF42868F259E8ED25F361986C26161514FA85D78F3889B4185D2A1484C1DA88C
52DFE7D9052F29071922876723C30CF6FE6247D6439590C5C7C0493120ACA096
D5AF176D59779A92F6EFB34F271D76D60780C3EA83306326E0119CD3B0417687
C4CC4E589CCCEC609B4DE39F926F83CCFFCC966871B9A998514910A3F59E86FB56C476DFFB4181E3D22E86A760C4C137
50A64FEDA64B61A5C123906426EF726D98AC70C15F38245A931B5649F0944930679513D9862091275F10A804EBB610DE
E7938C4F32E571582D73F855ACC40FF0153EFBAB6D184F2DBE8EAC63C4276D92
7667993F35F1363C0A3AEB5222F07F91904B3F6FA375BF062B269B09706764CB
9DF764EE4A70D331BA8870F4D27C3E90E811A5E306BC72701A99A0377AC7B189
324D0FBC096A489983C45E82B2745A83A3725A87D2D2CA676A521075065D5047
8EA1A30AE08CEBD03A52EB2512C7C69CB824E0B9BE900E25F15FAB17ADBB1188
CE379182103FC4E0442745F6F202AB6ED8EBC051B5F5537916938D9DB8FCD6BE
752BD22AA37D030C60E48CB57309AF631682AC4A0D67C3A7D1130EB056717FB6
9ECFE7D24BF25BD543E1EAE8116D95C110BD4514EE279BCA71234865EB9166BD
14B3FA8704BA3B284C65F1A6FD114E53B883E12EAFB24B574F84585BD20157DA
D026E1E2CA70E33291482EEFCE11123540BBF591D400C92CEBB8CAF99A9DE882
88E618EF5E76D1F5A60926D48D3D58FF67B4A92DE6EECA271EDAFC2419AE787B
74E14AD8ADE2BC575048325D1B3990669CDB35D840169B913043DAC938862AE4
BEB5388B6A7D5C9EF65BDD7577D1DD654B7FAECE5A4CF0937BB7D0C0C5494CD0
761356D494E3C947CBDCA887A30071A675FE1BE6E77FC9016DB9B5659B7FF9E7
7503725DFC7212F5F719AFC9DF29F07321511BE4896E12D1D10C68BB07C6483A2CE5FFDFD074CFC279E42E6DD860C496
BD03D78399DE44449AEE9F00EBCABBF419EDEA1701B9979A97D57AECB5139D1E
E80EB84DF9DA2E34B78F50D6488A30F8EFD11E7C6DBEB7CFEDE83BE5CA86B6DF
9332A130138F2AC11A12030CF43EFF77E7CABE761EDE14748112E13267496CE9
E657F3C95EEF0AF92A5C49F66BD9C053A82493C7D6267F1E7C038473AE488116
6F37491FFE130F90B77D7E5EA4AA75A1DB0CA3644F68B6502DFC302DDB80367B
3B37C5CCDD510873628D92B352907FEFF0AC2B38C751C2E46C3B97C3E365972B
A4C845187BEC75669234EE07B839DCFF618678D2EAA5596350F0936A400099A5
2C961EF4F2454771B2646ECBAA1D0B7DAA2FD8ECC7228037A36E24FE30C43ABAA446C1B5968C0E2DD141C55557A4CCB8
C4E0D43E9670C2B91F6AF03D60F216625CC19C697331BC443194D2BB88E042DF
3DC5584F43061AF79907D6342DF3344435B5AA6277B33D7DC56C429D1EB81BC4
D9186791E907CBB9EF26EDDAEA9F0DBC8D24E213E55942ED5E1790B5A55F8758
2B54CB727BBFD8567543448D2D24B3865329F89936D3B035ED2185BA88F1DF2E
A08F0D881AF001E52FF2CF9D232A9A566EB1900B351AFEEFEE666BEC40588F64
46A11CA89BA1998A247275EEDF504DAFC5F97B41DFF4943F531A9F8F43DF19C5
EF70A3858E84B13DF606317381B2E9DFFC346E96AF7C1DF0001586B8BF35808F
38EAF18DB8096C7427EBD36CBC5B0E945A3286278CC0227EB056F7ACB9E450D4
28278D1DAA263E9A45ED17F67F6B6B0CF00F8C0F58F86C8161F8D4266FE556CB
0B0C79FDD7C9EA31FAA4AA829EDDD2A3453C05A74F5B53BBEAE83E1F4913FD1C18BD235D14D06D9E567DDB273E4C1F2A
7D663A93AE1B9A2E00E944B92838DED3376E09C5179C8F3037B2EAE9E7326C2A
D64EC2C7BD8CFA152368DF6BB75D66EF24EAA9C864A1386184B793C0585D82BC
51D8EB188E833891CCD15919FAC8FE56ACAB1007699F4FDCB53A6DDAB02E5CAA
650866D34DECD1D1F3559EFD8D2A4C1DB51C005F5932608CB6062B384D7A1F59C8E3FBF2C0A5AEFFB631D7B88A630AAB
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

View File

@@ -93,6 +93,9 @@ void subghz_scene_save_name_on_enter(void* context) {
bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeBack) {
// Set file path to default
furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER);
//
if(!(strcmp(subghz->file_name_tmp, "") == 0) ||
scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
SubGhzCustomEventManagerNoSet) {
@@ -106,8 +109,6 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
} else {
scene_manager_previous_scene(subghz->scene_manager);
}
// Set file path to default
furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER);
return true;
} else if(event.type == SceneManagerEventTypeCustom) {

View File

@@ -1099,7 +1099,7 @@ static void subghz_cli_command_chat(PipeSide* pipe, FuriString* args) {
break;
}
}
if(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
printf("\r\n");
chat_event.event = SubGhzChatEventUserExit;
subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);

View File

@@ -73,8 +73,9 @@ bool desktop_main_input_callback(InputEvent* event, void* context) {
} else if(event->key == InputKeyOk) {
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
main_view->callback(DesktopAnimationEventNewIdleAnimation, main_view->context);
} else {
main_view->callback(DesktopMainEventOpenFavoriteOkLong, main_view->context);
}
main_view->callback(DesktopMainEventOpenFavoriteOkLong, main_view->context);
}
}
} else {

View File

@@ -10,6 +10,7 @@
#include <stdint.h>
#include <m-array.h>
#define SCROLL_INTERVAL (333)
#define ITEM_FIRST_OFFSET 17
#define ITEM_NEXT_OFFSET 4
#define ITEM_HEIGHT 14
@@ -35,13 +36,56 @@ typedef struct {
ButtonMenuItemArray_t items;
size_t position;
const char* header;
size_t scroll_counter;
FuriTimer* scroll_timer;
} ButtonMenuModel;
static void button_menu_draw_text(
Canvas* canvas,
uint8_t item_x,
uint8_t item_y,
const char* text,
bool selected,
ButtonMenuModel* model) {
FuriString* disp_str;
disp_str = furi_string_alloc_set(text);
bool draw_static = true;
if(selected) {
size_t text_width = canvas_string_width(canvas, furi_string_get_cstr(disp_str));
if(text_width >= ITEM_WIDTH - 8) {
elements_scrollable_text_line(
canvas,
item_x + 4,
item_y + ITEM_HEIGHT - 4,
ITEM_WIDTH - 8,
disp_str,
model->scroll_counter,
false);
draw_static = false;
}
}
if(draw_static) {
elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6);
canvas_draw_str_aligned(
canvas,
item_x + (ITEM_WIDTH / 2),
item_y + (ITEM_HEIGHT / 2),
AlignCenter,
AlignCenter,
furi_string_get_cstr(disp_str));
}
furi_string_free(disp_str);
}
static void button_menu_draw_control_button(
Canvas* canvas,
uint8_t item_position,
const char* text,
bool selected) {
bool selected,
ButtonMenuModel* model) {
furi_assert(canvas);
furi_assert(text);
@@ -54,20 +98,16 @@ static void button_menu_draw_control_button(
elements_slightly_rounded_box(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_str_aligned(
canvas,
item_x + (ITEM_WIDTH / 2),
item_y + (ITEM_HEIGHT / 2),
AlignCenter,
AlignCenter,
text);
button_menu_draw_text(canvas, item_x, item_y, text, selected, model);
}
static void button_menu_draw_common_button(
Canvas* canvas,
uint8_t item_position,
const char* text,
bool selected) {
bool selected,
ButtonMenuModel* model) {
furi_assert(canvas);
furi_assert(text);
@@ -83,19 +123,7 @@ static void button_menu_draw_common_button(
canvas_draw_rframe(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT, 5);
}
FuriString* disp_str;
disp_str = furi_string_alloc_set(text);
elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6);
canvas_draw_str_aligned(
canvas,
item_x + (ITEM_WIDTH / 2),
item_y + (ITEM_HEIGHT / 2),
AlignCenter,
AlignCenter,
furi_string_get_cstr(disp_str));
furi_string_free(disp_str);
button_menu_draw_text(canvas, item_x, item_y, text, selected, model);
}
static void button_menu_view_draw_callback(Canvas* canvas, void* _model) {
@@ -120,9 +148,17 @@ static void button_menu_view_draw_callback(Canvas* canvas, void* _model) {
if(model->header) {
FuriString* disp_str;
disp_str = furi_string_alloc_set(model->header);
elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6);
canvas_draw_str_aligned(
canvas, 32, 10, AlignCenter, AlignCenter, furi_string_get_cstr(disp_str));
size_t header_width = canvas_string_width(canvas, furi_string_get_cstr(disp_str));
if(header_width >= ITEM_WIDTH - 8) {
elements_scrollable_text_line(
canvas, 3, 13, ITEM_WIDTH - 8, disp_str, model->scroll_counter, false);
} else {
elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 8);
canvas_draw_str_aligned(
canvas, 32, 10, AlignCenter, AlignCenter, furi_string_get_cstr(disp_str));
}
furi_string_free(disp_str);
}
@@ -137,13 +173,15 @@ static void button_menu_view_draw_callback(Canvas* canvas, void* _model) {
canvas,
item_position % BUTTONS_PER_SCREEN,
ButtonMenuItemArray_cref(it)->label,
(item_position == model->position));
(item_position == model->position),
model);
} else if(ButtonMenuItemArray_cref(it)->type == ButtonMenuItemTypeCommon) {
button_menu_draw_common_button(
canvas,
item_position % BUTTONS_PER_SCREEN,
ButtonMenuItemArray_cref(it)->label,
(item_position == model->position));
(item_position == model->position),
model);
}
}
}
@@ -158,8 +196,10 @@ static void button_menu_process_up(ButtonMenu* button_menu) {
{
if(model->position > 0) {
model->position--;
model->scroll_counter = 0;
} else {
model->position = ButtonMenuItemArray_size(model->items) - 1;
model->scroll_counter = 0;
}
},
true);
@@ -174,8 +214,10 @@ static void button_menu_process_down(ButtonMenu* button_menu) {
{
if(model->position < (ButtonMenuItemArray_size(model->items) - 1)) {
model->position++;
model->scroll_counter = 0;
} else {
model->position = 0;
model->scroll_counter = 0;
}
},
true);
@@ -193,8 +235,10 @@ static void button_menu_process_right(ButtonMenu* button_menu) {
position_candidate -= position_candidate % BUTTONS_PER_SCREEN;
if(position_candidate < (ButtonMenuItemArray_size(model->items))) {
model->position = position_candidate;
model->scroll_counter = 0;
} else {
model->position = 0;
model->scroll_counter = 0;
}
}
},
@@ -217,6 +261,7 @@ static void button_menu_process_left(ButtonMenu* button_menu) {
};
position_candidate -= position_candidate % BUTTONS_PER_SCREEN;
model->position = position_candidate;
model->scroll_counter = 0;
}
},
true);
@@ -314,6 +359,7 @@ void button_menu_reset(ButtonMenu* button_menu) {
ButtonMenuItemArray_reset(model->items);
model->position = 0;
model->header = NULL;
model->scroll_counter = 0;
},
true);
}
@@ -351,6 +397,12 @@ ButtonMenuItem* button_menu_add_item(
return item;
}
static void button_menu_process_timer_callback(void* context) {
ButtonMenu* button_menu = context;
with_view_model(
button_menu->view, ButtonMenuModel * model, { model->scroll_counter++; }, true);
}
ButtonMenu* button_menu_alloc(void) {
ButtonMenu* button_menu = malloc(sizeof(ButtonMenu));
button_menu->view = view_alloc();
@@ -367,6 +419,10 @@ ButtonMenu* button_menu_alloc(void) {
ButtonMenuItemArray_init(model->items);
model->position = 0;
model->header = NULL;
model->scroll_counter = 0;
model->scroll_timer = furi_timer_alloc(
button_menu_process_timer_callback, FuriTimerTypePeriodic, button_menu);
furi_timer_start(model->scroll_timer, SCROLL_INTERVAL);
},
true);
@@ -380,7 +436,11 @@ void button_menu_free(ButtonMenu* button_menu) {
with_view_model(
button_menu->view,
ButtonMenuModel * model,
{ ButtonMenuItemArray_clear(model->items); },
{
ButtonMenuItemArray_clear(model->items);
furi_timer_stop(model->scroll_timer);
furi_timer_free(model->scroll_timer);
},
true);
view_free(button_menu->view);
free(button_menu);

View File

@@ -399,9 +399,7 @@ static void rgb_backlight_installed_changed(VariableItem* item) {
}
// Lock/Unlock all rgb settings depent from rgb_backlight_installed switch
int slide = 1;
for(int i = slide; i < (slide + 8); i++) {
for(int i = 1; i < 9; i++) {
VariableItem* t_item = variable_item_list_get(app->variable_item_list_rgb, i);
if(index == 0) {
variable_item_set_locked(t_item, true, "RGB\nOFF!");
@@ -534,7 +532,7 @@ static void rgb_backlight_rainbow_wide_changed(VariableItem* item) {
notification_message_save_settings(app->notification);
}
// open settings.rgb_view if user press OK on first (index=0) menu string and (debug mode or rgb_backlight_installed is true)
// open settings.rgb_view if user press OK on last (index=10) menu string
void variable_item_list_enter_callback(void* context, uint32_t index) {
UNUSED(context);
NotificationAppSettings* app = context;
@@ -562,12 +560,10 @@ static void night_shift_changed(VariableItem* item) {
app->notification->current_night_shift = night_shift_value[index];
app->notification->current_night_shift = night_shift_value[index];
// force demo night_shift brightness ot rgb backlight and stock backlight
// force demo night_shift brightness to rgb backlight and stock backlight
notification_message(app->notification, &sequence_display_backlight_on);
int slide = 0;
for(int i = 4 + slide; i < (6 + slide); i++) {
for(int i = 4; i < 6; i++) {
VariableItem* t_item = variable_item_list_get(app->variable_item_list, i);
if(index == 0) {
variable_item_set_locked(t_item, true, "Night Shift\nOFF!");
@@ -729,18 +725,15 @@ static NotificationAppSettings* alloc_settings(void) {
variable_item_set_current_value_text(item, lcd_inversion_text[value_index]);
//--- RGB BACKLIGHT ---
// Show RGB settings only when debug_mode or rgb_backlight_installed is active
item = variable_item_list_add(app->variable_item_list, "RGB Mod Settings", 0, NULL, app);
//--- RGB BACKLIGHT END ---
app->variable_item_list_rgb = variable_item_list_alloc();
View* view_rgb = variable_item_list_get_view(app->variable_item_list_rgb);
// set callback for exit from settings.rgb_menu
// set callback for exit from rgb settings menu
view_set_previous_callback(view_rgb, notification_app_rgb_settings_exit);
// Show rgb_backlight_installed swith only in Debug mode
item = variable_item_list_add(
app->variable_item_list_rgb,
"RGB backlight installed",

View File

@@ -23,6 +23,7 @@ typedef struct {
} JsGpioPinInst;
ARRAY_DEF(ManagedPinsArray, JsGpioPinInst*, M_PTR_OPLIST); //-V575
#define M_OPL_ManagedPinsArray_t() ARRAY_OPLIST(ManagedPinsArray)
/**
* Per-module instance control structure
@@ -444,20 +445,26 @@ static void js_gpio_destroy(void* inst) {
JsGpioInst* module = (JsGpioInst*)inst;
// reset pins
ManagedPinsArray_it_t iterator;
for(ManagedPinsArray_it(iterator, module->managed_pins); !ManagedPinsArray_end_p(iterator);
ManagedPinsArray_next(iterator)) {
JsGpioPinInst* manager_data = *ManagedPinsArray_cref(iterator);
if(manager_data->had_interrupt) {
furi_hal_gpio_disable_int_callback(manager_data->pin);
furi_hal_gpio_remove_int_callback(manager_data->pin);
for
M_EACH(item, module->managed_pins, ManagedPinsArray_t) {
JsGpioPinInst* manager_data = *item;
if(manager_data->had_interrupt) {
furi_hal_gpio_disable_int_callback(manager_data->pin);
furi_hal_gpio_remove_int_callback(manager_data->pin);
}
if(manager_data->pwm_output != FuriHalPwmOutputIdNone) {
if(furi_hal_pwm_is_running(manager_data->pwm_output))
furi_hal_pwm_stop(manager_data->pwm_output);
}
furi_hal_gpio_init(manager_data->pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_event_loop_maybe_unsubscribe(module->loop, manager_data->interrupt_semaphore);
furi_semaphore_free(manager_data->interrupt_semaphore);
free(manager_data->interrupt_contract);
free(manager_data);
}
furi_hal_gpio_init(manager_data->pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_event_loop_maybe_unsubscribe(module->loop, manager_data->interrupt_semaphore);
furi_semaphore_free(manager_data->interrupt_semaphore);
free(manager_data->interrupt_contract);
free(manager_data);
}
// free buffers
furi_hal_adc_release(module->adc_handle);

View File

@@ -345,7 +345,7 @@ int32_t update_task_worker_flash_writer(void* context) {
furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal);
#ifdef FURI_NDEBUG
// Production
furi_hal_rtc_set_log_level(FuriLogLevelDefault);
furi_hal_rtc_set_log_level(FuriLogLevelNone);
furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug);
furi_hal_rtc_reset_flag(FuriHalRtcFlagLegacySleep);
furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeNone);

View File

@@ -73,6 +73,7 @@ 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
@@ -80,7 +81,29 @@ Watch this video to learn more and see how different boards can be programmed (v
5. Led on the receiver board will flash and turn off
6. Done!
Also you can program new remote using old remote on newer boards! See first video below:
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)
2. Open your new remote file
3. For next steps be close to the receiver board, around 1-2 meters
4. Press second button (lowest one) on the old remote, do not release second button and press 1st (upper) button, hold buttons for 1 sec and release them
5. Press working button on the old remote (the button you use for operating the receiver, aka opening the gate, etc) hold for 1 sec and release
6. Actions with old remote must be done in 5 seconds time, do not hold buttons for too long, and do not make it very fast
7. Receiver will beep, you will have 10 seconds to add new remote, now press Send on new remote on flipper two times holding for at least 1 sec
8. Receiver will beep again telling that new remote is added sucessfuly!
9. Done!
With copy of existing remote on flipper:
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 existing remote (original) file
3. For next steps be close to the receiver board, around 1-2 meters
4. Press left button (0x8) on the flipper, hold for 1 sec and release the button and press right (0xA) button, hold button for 1 sec and release
5. Press working button on the flipper, should be center one aka Send (the button you use for operating the receiver, aka opening the gate, etc) hold for 1 sec and release
6. Actions with original remote copy must be done in 5 seconds time, do not hold buttons for too long, and do not make it very fast
7. Receiver will beep, now hold back and open new remote file, you will have 10 seconds to add new remote, press Send on new remote on flipper two times holding for at least 1 sec
8. Receiver will beep again telling that new remote is added sucessfuly!
9. Done!
Watch this videos to learn more (videos in Russian language): https://www.youtube.com/watch?v=wZ5121HYv50 / https://www.youtube.com/watch?v=1ucrDKF3vWc
## Somfy Telis
@@ -122,19 +145,54 @@ How to get seed to make full clone of your remote (**will conflict with original
1. Open `Read` in SubGHz on your flipper
2. (ONLY FOR ORIGINAL REMOTES) Hold all buttons on your remote at same time, example -> for 2 button remote - press them both at same time and hold OR press hidden button on back of remote with a pin or paper clip
For 4 buttons remote press & hold two buttons at upper row
3. You will receive signal on your flipper, open that signal and see `Fix:` value, it should start from `F` like `F00F1C9B`
4. If `Fix:` is showing first `F` see `Hop:` value -> This is your remote Seed
5. Write down Hop value
4. If `Fix:` is showing first `F` see `Hop:` value -> This is your remote Seed (except first digit `F` (this is the button code, aka programming button pressed means `F`))
5. Write down Hop value and replace first digit - `F` with `0`
6. Press button on your remote that you want to clone and receive its signal on your flipper
7. Open and write down `Fix:` value where first digit will be same as your button ID `Btn:`
8. Create new remote using BFT Mitto [Manual] - Enter FIX from step 7, enter counter `FF F9`, enter seed from step 5
9. Using counter values like `FF F9` can help bypassing current original remote counter value, and in result it also can fully desync original remote, only one remote can work at same time using this method
10. Throw away your original remote since now it needs to be re-added into receiver board :C
10. Also you can do this: Save your signal of the original remote (will say KL: Unknown),
then copy file to the PC and edit it and insert/replace those values after the `Key: 01 23 45 67 89 AB CD EF` (your key will have different value)
```
Seed: 0X XX XX XX
Manufacture: BFT
```
Replace `X`'s with digits from your Seed that you obtained by reading two button hold at the first steps,
Save and copy that file back to the flipper
Now you will have exact clone of your remote that will have same counter, by making couple presses you will make it higher than original and receiver will work with it, but original remote will reguire same amount of presses to work again, and vice versa.
11. Also your original remote may become non working since it needs to be re-added into receiver board if you made counter much higher than original :C
## CAME Atomo
Known names are: TOP42R / TOP44R - TOP44RGR (806TS-0130)
1. Use google to find instructions - `how to program new CAME Atomo remote into receiver`
2. Watch this video to learn more (video in Russian language): https://www.youtube.com/watch?v=XeHUwfcSS30
How to create new remote and bind it to receiver (will not conflict with original remotes):
With original remote (or copy of the original remote):
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> CAME Atomo 433MHz or 868MHz
2. Open your new remote file
3. You need to be in minimum 3 meters to receiver
4. Original Remote: Press and hold button that is bound with that receiver (the one you use with it), and hold it for about 10 seconds.
5. You will have about 20 seconds to add new remote
6. Long press Send on Flipper in new remote for like 3-4 sec and release - this will add new remote to the receiver
7. Press and hold Send again after waiting 20 seconds - this will trigger the receiver
8. Done, when using CAME Atomo from flipper please hold Send button for at least 2 seconds to allow code to be fully transmit, flipper transmits only while button is held
Note: Static 24/12 bit or TWEE remotes cannot trigger programming mode in the receiver and cannot be bound if programming mode was triggered by Atomo type remote, only Atomo remotes can be added if remote programming was done by Atomo remote, Static remotes have option to clone from one remote to another, but it requires first remote to be added to the receiver via button on the receiver board
With access to receiver box:
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> CAME Atomo 433MHz or 868MHz
2. Open your new remote file
3. Open the receiver box and find programming button related to the used channel, for example RE432M/RE862M receiver has two independent channels which can have different remotes / buttons on them, when you found connected channel press "1" or "2" button on the receiver board to enter programming mode
4. Long press Send on Flipper new remote for like 3-4 sec and release - this will add new remote to the receiver
5. Click CLEAR button one time on the receiver board to exit programming mode, or wait about 20 seconds it will exit from programming mode automatically
6. Done, when using CAME Atomo from flipper please hold Send button for at least 2 seconds to allow code to be fully transmit, flipper transmits only while button is held
Watch this video to learn more (video in Russian language): https://www.youtube.com/watch?v=XeHUwfcSS30
## Nice Flor S

View File

@@ -60,6 +60,7 @@ if you need your custom one, make sure it doesn't listed here
434775000, /* LPD433 last channels */
438900000,
440175000,
462750000,
464000000,
467750000,

View File

@@ -38,6 +38,8 @@ static bool rw1990_read_and_compare(OneWireHost* host, const uint8_t* data, size
}
bool rw1990_write_v1(OneWireHost* host, const uint8_t* data, size_t data_size) {
onewire_host_set_timings_default(host);
// Unlock sequence
onewire_host_reset(host);
onewire_host_write(host, RW1990_1_CMD_WRITE_RECORD_FLAG);
@@ -67,6 +69,8 @@ bool rw1990_write_v1(OneWireHost* host, const uint8_t* data, size_t data_size) {
}
bool rw1990_write_v2(OneWireHost* host, const uint8_t* data, size_t data_size) {
onewire_host_set_timings_default(host);
// Unlock sequence
onewire_host_reset(host);
onewire_host_write(host, RW1990_2_CMD_WRITE_RECORD_FLAG);

View File

@@ -0,0 +1,58 @@
#include <core/kernel.h>
#include <one_wire/one_wire_host.h>
#include <one_wire/maxim_crc.h>
#include "tm01x.h"
// Commands for TM01x
#define TM01X_CMD_WRITE_FLAG 0xC1
#define TM01X_CMD_WRITE_ROM 0xC5
#define TM01X_CMD_READ_ROM 0x33
#define TM01X_CMD_FINALIZE_CYFRAL 0xCA
#define TM01X_CMD_FINALIZE_METAKOM 0xCB
static void tm01x_write_byte(OneWireHost* host, uint8_t value) {
for(uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) {
onewire_host_write_bit(host, (bool)(bitMask & value));
furi_delay_us(5000); // 5ms pause after each bit
}
}
// Helper function to read and verify written data
static bool tm01x_read_and_verify(OneWireHost* host, const uint8_t* data, size_t data_size) {
bool success = false;
if(onewire_host_reset(host)) {
success = true;
onewire_host_write(host, TM01X_CMD_READ_ROM);
for(size_t i = 0; i < data_size; ++i) {
if(data[i] != onewire_host_read(host)) {
success = false;
break;
}
}
}
return success;
}
bool tm01x_write_dallas(OneWireHost* host, const uint8_t* data, size_t data_size) {
// Set TM01x specific timings
onewire_host_set_timings_tm01x(host);
// Write sequence
onewire_host_reset(host);
onewire_host_write(host, TM01X_CMD_WRITE_FLAG);
onewire_host_write_bit(host, true);
furi_delay_us(5000);
onewire_host_reset(host);
onewire_host_write(host, TM01X_CMD_WRITE_ROM);
for(size_t i = 0; i < data_size; ++i) {
tm01x_write_byte(host, data[i]);
}
return tm01x_read_and_verify(host, data, data_size);
}

View File

@@ -0,0 +1,7 @@
#pragma once
#include <core/kernel.h>
#include <one_wire/one_wire_host.h>
// Function to write Dallas protocol to TM01x
bool tm01x_write_dallas(OneWireHost* host, const uint8_t* data, size_t data_size);

View File

@@ -9,6 +9,8 @@
#define TM2004_ANSWER_READ_MEMORY 0xF5
bool tm2004_write(OneWireHost* host, const uint8_t* data, size_t data_size) {
onewire_host_set_timings_default(host);
onewire_host_reset(host);
onewire_host_write(host, TM2004_CMD_WRITE_ROM);
// Starting writing from address 0x0000

View File

@@ -7,7 +7,7 @@
#include "../blanks/rw1990.h"
#include "../blanks/tm2004.h"
#include "../blanks/tm01x.h"
#define DS1990_FAMILY_CODE 0x01U
#define DS1990_FAMILY_NAME "DS1990"
@@ -66,7 +66,8 @@ bool dallas_ds1990_write_id(OneWireHost* host, iButtonProtocolData* protocol_dat
return rw1990_write_v1(host, data->rom_data.bytes, sizeof(DallasCommonRomData)) ||
rw1990_write_v2(host, data->rom_data.bytes, sizeof(DallasCommonRomData)) ||
tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData));
tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData)) ||
tm01x_write_dallas(host, data->rom_data.bytes, sizeof(DallasCommonRomData));
}
static bool dallas_ds1990_reset_callback(bool is_short, void* context) {

View File

@@ -445,43 +445,35 @@ static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance
static NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPoller* instance) {
NfcCommand command = NfcCommandContinue;
FURI_LOG_D(TAG, "MfulC auth");
if(mf_ultralight_support_feature(
instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {
instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest;
command = instance->callback(instance->general_event, instance->context);
if(!instance->mfu_event.data->auth_context.skip_auth) {
FURI_LOG_D(TAG, "Trying to authenticate with 3des key");
instance->auth_context.tdes_key = instance->mfu_event.data->auth_context.tdes_key;
do {
uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE];
uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
furi_hal_random_fill_buf(RndA, sizeof(RndA));
instance->error = mf_ultralight_poller_authenticate_start(instance, RndA, output);
if(instance->error != MfUltralightErrorNone) break;
do {
if(mf_ultralight_support_feature(
instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {
instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest;
uint8_t decoded_shifted_RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
const uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
instance->error = mf_ultralight_poller_authenticate_end(
instance, RndB, output, decoded_shifted_RndA);
if(instance->error != MfUltralightErrorNone) break;
command = instance->callback(instance->general_event, instance->context);
if(!instance->mfu_event.data->auth_context.skip_auth) {
FURI_LOG_D(TAG, "Trying to authenticate with 3des key");
instance->auth_context.tdes_key = instance->mfu_event.data->auth_context.tdes_key;
instance->error =
mf_ultralight_poller_auth_tdes(instance, &instance->auth_context);
mf_ultralight_3des_shift_data(RndA);
instance->auth_context.auth_success =
(memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0);
if(instance->auth_context.auth_success) {
if(instance->error == MfUltralightErrorNone &&
instance->auth_context.auth_success) {
FURI_LOG_D(TAG, "Auth success");
} else {
FURI_LOG_D(TAG, "Auth failed");
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
}
} while(false);
if(instance->error != MfUltralightErrorNone || !instance->auth_context.auth_success) {
FURI_LOG_D(TAG, "Auth failed");
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
} else {
// We assume here that it is card read without explicitly provided key
// So we try to auth with default one
instance->state = MfUltralightPollerStateTryDefaultMfulCKey;
break;
}
}
}
instance->state = MfUltralightPollerStateReadPages;
instance->state = MfUltralightPollerStateReadPages;
} while(false);
return command;
}
@@ -578,6 +570,40 @@ static NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoll
return NfcCommandContinue;
}
static NfcCommand
mf_ultralight_poller_handler_try_default_ultralight_c_key(MfUltralightPoller* instance) {
do {
if(!mf_ultralight_support_feature(
instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {
break;
}
if(instance->auth_context.auth_success) {
break;
}
FURI_LOG_D(TAG, "Trying authentication with default 3DES key");
memcpy(
&instance->auth_context.tdes_key.data,
MF_ULTRALIGHT_C_DEFAULT_KEY,
MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE);
instance->error = mf_ultralight_poller_auth_tdes(instance, &instance->auth_context);
if(instance->error == MfUltralightErrorNone && instance->auth_context.auth_success) {
FURI_LOG_D(TAG, "Default 3DES key detected");
} else {
FURI_LOG_D(TAG, "Authentication attempt with default 3DES key failed");
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
}
} while(false);
instance->state = MfUltralightPollerStateReadPages;
return NfcCommandContinue;
}
static NfcCommand
mf_ultralight_poller_handler_check_mfuc_auth_status(MfUltralightPoller* instance) {
instance->state = MfUltralightPollerStateReadSuccess;
@@ -742,6 +768,8 @@ static const MfUltralightPollerReadHandler
mf_ultralight_poller_handler_read_tearing_flags,
[MfUltralightPollerStateAuth] = mf_ultralight_poller_handler_auth,
[MfUltralightPollerStateTryDefaultPass] = mf_ultralight_poller_handler_try_default_pass,
[MfUltralightPollerStateTryDefaultMfulCKey] =
mf_ultralight_poller_handler_try_default_ultralight_c_key,
[MfUltralightPollerStateCheckMfulCAuthStatus] =
mf_ultralight_poller_handler_check_mfuc_auth_status,
[MfUltralightPollerStateAuthMfulC] = mf_ultralight_poller_handler_auth_ultralight_c,

View File

@@ -81,6 +81,19 @@ MfUltralightError mf_ultralight_poller_auth_pwd(
MfUltralightPoller* instance,
MfUltralightPollerAuthContext* data);
/**
* @brief Perform 3DES authentication with key.
*
* Must ONLY be used inside the callback function.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in, out] data pointer to the authentication context.
* @return MfUltralightErrorNone on success, an error code on failure.
*/
MfUltralightError mf_ultralight_poller_auth_tdes(
MfUltralightPoller* instance,
MfUltralightPollerAuthContext* data);
/**
* @brief Start authentication procedure.
*

View File

@@ -1,6 +1,7 @@
#include "mf_ultralight_poller_i.h"
#include <furi.h>
#include <furi_hal.h>
#define TAG "MfUltralightPoller"
@@ -62,6 +63,38 @@ MfUltralightError mf_ultralight_poller_auth_pwd(
return ret;
}
MfUltralightError mf_ultralight_poller_auth_tdes(
MfUltralightPoller* instance,
MfUltralightPollerAuthContext* data) {
furi_check(instance);
furi_check(data);
MfUltralightError ret = MfUltralightErrorNone;
uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE];
uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
furi_hal_random_fill_buf(RndA, sizeof(RndA));
ret = mf_ultralight_poller_authenticate_start(instance, RndA, output);
if(ret != MfUltralightErrorNone) {
return ret;
}
uint8_t decoded_shifted_RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
const uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
ret = mf_ultralight_poller_authenticate_end(instance, RndB, output, decoded_shifted_RndA);
if(ret != MfUltralightErrorNone) {
return ret;
}
mf_ultralight_3des_shift_data(RndA);
data->auth_success = (memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0);
return ret;
}
static MfUltralightError mf_ultralight_poller_send_authenticate_cmd(
MfUltralightPoller* instance,
const uint8_t* cmd,
@@ -134,7 +167,7 @@ MfUltralightError mf_ultralight_poller_authenticate_start(
uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
mf_ultralight_3des_decrypt(
&instance->des_context,
instance->mfu_event.data->auth_context.tdes_key.data,
instance->auth_context.tdes_key.data,
iv,
encRndB,
sizeof(encRndB),
@@ -145,7 +178,7 @@ MfUltralightError mf_ultralight_poller_authenticate_start(
mf_ultralight_3des_encrypt(
&instance->des_context,
instance->mfu_event.data->auth_context.tdes_key.data,
instance->auth_context.tdes_key.data,
encRndB,
output,
MF_ULTRALIGHT_C_AUTH_DATA_SIZE,
@@ -179,7 +212,7 @@ MfUltralightError mf_ultralight_poller_authenticate_end(
mf_ultralight_3des_decrypt(
&instance->des_context,
instance->mfu_event.data->auth_context.tdes_key.data,
instance->auth_context.tdes_key.data,
RndB,
bit_buffer_get_data(instance->rx_buffer) + 1,
MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE,

View File

@@ -59,6 +59,7 @@ typedef enum {
MfUltralightPollerStateAuthMfulC,
MfUltralightPollerStateReadPages,
MfUltralightPollerStateTryDefaultPass,
MfUltralightPollerStateTryDefaultMfulCKey,
MfUltralightPollerStateCheckMfulCAuthStatus,
MfUltralightPollerStateReadFailed,
MfUltralightPollerStateReadSuccess,

View File

@@ -8,16 +8,16 @@
#include "one_wire_host.h"
typedef struct {
uint16_t a;
uint16_t b;
uint16_t c;
uint16_t d;
uint16_t e;
uint16_t f;
uint16_t g;
uint16_t h;
uint16_t i;
uint16_t j;
uint16_t a; // Write 1 low time
uint16_t b; // Write 1 high time
uint16_t c; // Write 0 low time
uint16_t d; // Write 0 high time
uint16_t e; // Read low time
uint16_t f; // Read high time
uint16_t g; // Reset pre-delay
uint16_t h; // Reset pulse
uint16_t i; // Presence detect
uint16_t j; // Reset post-delay
} OneWireHostTimings;
static const OneWireHostTimings onewire_host_timings_normal = {
@@ -46,6 +46,20 @@ static const OneWireHostTimings onewire_host_timings_overdrive = {
.j = 40,
};
// TM01x specific timings
static const OneWireHostTimings onewire_host_timings_tm01x = {
.a = 5,
.b = 80,
.c = 70,
.d = 10,
.e = 5,
.f = 70,
.g = 0,
.h = 740,
.i = 140,
.j = 410,
};
struct OneWireHost {
const GpioPin* gpio_pin;
const OneWireHostTimings* timings;
@@ -354,3 +368,15 @@ void onewire_host_set_overdrive(OneWireHost* host, bool set) {
host->timings = set ? &onewire_host_timings_overdrive : &onewire_host_timings_normal;
}
void onewire_host_set_timings_default(OneWireHost* host) {
furi_check(host);
host->timings = &onewire_host_timings_normal;
}
void onewire_host_set_timings_tm01x(OneWireHost* host) {
furi_check(host);
host->timings = &onewire_host_timings_tm01x;
}

View File

@@ -125,6 +125,10 @@ bool onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearch
*/
void onewire_host_set_overdrive(OneWireHost* host, bool set);
void onewire_host_set_timings_default(OneWireHost* host);
void onewire_host_set_timings_tm01x(OneWireHost* host);
#ifdef __cplusplus
}
#endif

View File

@@ -244,8 +244,11 @@ void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t durat
switch(instance->decoder.parser_step) {
case CameDecoderStepReset:
if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_const.te_short * 56) <
subghz_protocol_came_const.te_delta * 47)) {
subghz_protocol_came_const.te_delta * 63)) {
// 17920 us + 7050 us = 24970 us max possible value old one
// delta = 150 us x 63 = 9450 us + 17920 us = 27370 us max possible value
//Found header CAME
// 26700 us or 24000 us max possible values
instance->decoder.parser_step = CameDecoderStepFoundStartBit;
}
break;

View File

@@ -234,8 +234,10 @@ void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32
switch(instance->decoder.parser_step) {
case Holtek_HT12XDecoderStepReset:
if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short * 36) <
subghz_protocol_holtek_th12x_const.te_delta * 36)) {
if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short * 28) <
subghz_protocol_holtek_th12x_const.te_delta * 20)) {
// 18720 us old max value
// 12960 us corrected max value
//Found Preambula
instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit;
}

View File

@@ -66,6 +66,7 @@ static const uint32_t subghz_frequency_list[] = {
434775000, /* LPD433 last channels */
438900000,
440175000,
462750000,
464000000,
467750000,

View File

@@ -136,9 +136,9 @@ void cli_registry_reload_external_commands(
FURI_LOG_T(TAG, "Plugin: %s", plugin_filename);
furi_string_set_str(plugin_name, plugin_filename);
furi_check(furi_string_end_with_str(plugin_name, ".fal"));
if(!furi_string_end_with_str(plugin_name, ".fal")) continue;
furi_string_replace_all_str(plugin_name, ".fal", "");
furi_check(furi_string_start_with_str(plugin_name, config->fal_prefix));
if(!furi_string_start_with_str(plugin_name, config->fal_prefix)) continue;
furi_string_replace_at(plugin_name, 0, strlen(config->fal_prefix), "");
CliRegistryCommand command = {

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,86.0,,
Version,+,86.1,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
Header,+,applications/services/cli/cli.h,,
@@ -2283,6 +2283,8 @@ Function,+,onewire_host_reset,_Bool,OneWireHost*
Function,+,onewire_host_reset_search,void,OneWireHost*
Function,+,onewire_host_search,_Bool,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
Function,+,onewire_host_set_overdrive,void,"OneWireHost*, _Bool"
Function,+,onewire_host_set_timings_default,void,OneWireHost*
Function,+,onewire_host_set_timings_tm01x,void,OneWireHost*
Function,+,onewire_host_start,void,OneWireHost*
Function,+,onewire_host_stop,void,OneWireHost*
Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t"
1 entry status name type params
2 Version + 86.0 86.1
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/bt/bt_service/bt_keys_storage.h
5 Header + applications/services/cli/cli.h
2283 Function + onewire_host_reset_search void OneWireHost*
2284 Function + onewire_host_search _Bool OneWireHost*, uint8_t*, OneWireHostSearchMode
2285 Function + onewire_host_set_overdrive void OneWireHost*, _Bool
2286 Function + onewire_host_set_timings_default void OneWireHost*
2287 Function + onewire_host_set_timings_tm01x void OneWireHost*
2288 Function + onewire_host_start void OneWireHost*
2289 Function + onewire_host_stop void OneWireHost*
2290 Function + onewire_host_target_search void OneWireHost*, uint8_t

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,86.0,,
Version,+,86.1,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
@@ -2741,6 +2741,7 @@ Function,+,mf_ultralight_is_equal,_Bool,"const MfUltralightData*, const MfUltral
Function,+,mf_ultralight_is_page_pwd_or_pack,_Bool,"MfUltralightType, uint16_t"
Function,+,mf_ultralight_load,_Bool,"MfUltralightData*, FlipperFormat*, uint32_t"
Function,+,mf_ultralight_poller_auth_pwd,MfUltralightError,"MfUltralightPoller*, MfUltralightPollerAuthContext*"
Function,+,mf_ultralight_poller_auth_tdes,MfUltralightError,"MfUltralightPoller*, MfUltralightPollerAuthContext*"
Function,+,mf_ultralight_poller_authenticate_end,MfUltralightError,"MfUltralightPoller*, const uint8_t*, const uint8_t*, uint8_t*"
Function,+,mf_ultralight_poller_authenticate_start,MfUltralightError,"MfUltralightPoller*, const uint8_t*, uint8_t*"
Function,+,mf_ultralight_poller_read_counter,MfUltralightError,"MfUltralightPoller*, uint8_t, MfUltralightCounter*"
@@ -2970,6 +2971,8 @@ Function,+,onewire_host_reset,_Bool,OneWireHost*
Function,+,onewire_host_reset_search,void,OneWireHost*
Function,+,onewire_host_search,_Bool,"OneWireHost*, uint8_t*, OneWireHostSearchMode"
Function,+,onewire_host_set_overdrive,void,"OneWireHost*, _Bool"
Function,+,onewire_host_set_timings_default,void,OneWireHost*
Function,+,onewire_host_set_timings_tm01x,void,OneWireHost*
Function,+,onewire_host_start,void,OneWireHost*
Function,+,onewire_host_stop,void,OneWireHost*
Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t"
1 entry status name type params
2 Version + 86.0 86.1
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/bt/bt_service/bt.h
5 Header + applications/services/bt/bt_service/bt_keys_storage.h
2741 Function + mf_ultralight_is_page_pwd_or_pack _Bool MfUltralightType, uint16_t
2742 Function + mf_ultralight_load _Bool MfUltralightData*, FlipperFormat*, uint32_t
2743 Function + mf_ultralight_poller_auth_pwd MfUltralightError MfUltralightPoller*, MfUltralightPollerAuthContext*
2744 Function + mf_ultralight_poller_auth_tdes MfUltralightError MfUltralightPoller*, MfUltralightPollerAuthContext*
2745 Function + mf_ultralight_poller_authenticate_end MfUltralightError MfUltralightPoller*, const uint8_t*, const uint8_t*, uint8_t*
2746 Function + mf_ultralight_poller_authenticate_start MfUltralightError MfUltralightPoller*, const uint8_t*, uint8_t*
2747 Function + mf_ultralight_poller_read_counter MfUltralightError MfUltralightPoller*, uint8_t, MfUltralightCounter*
2971 Function + onewire_host_reset_search void OneWireHost*
2972 Function + onewire_host_search _Bool OneWireHost*, uint8_t*, OneWireHostSearchMode
2973 Function + onewire_host_set_overdrive void OneWireHost*, _Bool
2974 Function + onewire_host_set_timings_default void OneWireHost*
2975 Function + onewire_host_set_timings_tm01x void OneWireHost*
2976 Function + onewire_host_start void OneWireHost*
2977 Function + onewire_host_stop void OneWireHost*
2978 Function + onewire_host_target_search void OneWireHost*, uint8_t