Compare commits

...

6 Commits

Author SHA1 Message Date
Andrea Santaniello
730bb318fb 47lecoste's advanced modulation hopper merge 2026-03-13 14:50:24 +01:00
Andrea
ce085b6895 Revise automotive RKE security references and add new entries, fixed a DOI 2026-03-12 21:26:45 +01:00
D4rk$1d3
f4c753b673 Update README.md 2026-03-12 15:10:57 -03:00
Andrea
41191df7fd Remove Implemented Protocols section from README (redundant)
Removed the Implemented Protocols section from the README.
2026-03-12 15:33:00 +01:00
Andrea Santaniello
d5eb983caa Modulation hopping settings for time and rssi 2026-03-12 15:18:54 +01:00
Andrea Santaniello
853c609977 Marelli BSI buttons 2026-03-12 15:00:34 +01:00
14 changed files with 448 additions and 139 deletions

View File

@@ -16,7 +16,6 @@ This project may incorporate, adapt, or build upon **other open-source projects*
- [Supported Systems](#supported-systems) - [Supported Systems](#supported-systems)
- [How to Build](#how-to-build) - [How to Build](#how-to-build)
- [Project Scope](#project-scope) - [Project Scope](#project-scope)
- [Implemented Protocols](#implemented-protocols)
- [To Do / Planned Features](#to-do--planned-features) - [To Do / Planned Features](#to-do--planned-features)
- [Design Philosophy](#design-philosophy) - [Design Philosophy](#design-philosophy)
- [Research Direction](#research-direction) - [Research Direction](#research-direction)
@@ -132,22 +131,10 @@ Flipper-ARF aims to achieve:
--- ---
## Implemented Protocols
- [x] Mazda Siemens Protocol (5WK49365D) — ported from open-source references (testing required)
- [x] Full VAG, Fiat, Ford, Subaru, Kia, PSA support
- [x] D-Pad mapping (Lock / Unlock / Boot / Trunk) during emulation
- [x] VAG MFKey support and updated Keeloq codes
- [x] PSA XTEA brute force for saved → emulation workflow
- [x] Brute force of counter in saved → can be accellerated trough the companion app via bluetooth
- [X] Keeloq Key Manager inside firmware
- [x] RollJam app (Internal CC1101 for RX & TX captured signal; External CC1101 for jamming) — requires more real-world testing (no available yet)
---
## To Do / Planned Features ## To Do / Planned Features
- [ ] Add Scher Khan & Starline protocols - [ ] Add Scher Khan & Starline protocols
- [ ] Marelli BSI encodere and encryption
- [ ] Fix and reintegrate RollJam app (future updates) - [ ] 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
@@ -202,7 +189,8 @@ The following academic publications have been invaluable to the development and
- **Lock It and Still Lose It — On the (In)Security of Automotive Remote Keyless Entry Systems** - **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 Flavio D. Garcia, David Oswald, Timo Kasper, Pierre Pavlidès
*USENIX Security 2016* *USENIX Security 2016, pp. 929944*
DOI: [10.5555/3241094.3241166](https://doi.org/10.5555/3241094.3241166)
https://www.usenix.org/system/files/conference/usenixsecurity16/sec16_paper_garcia.pdf https://www.usenix.org/system/files/conference/usenixsecurity16/sec16_paper_garcia.pdf
- **Clonable Key Fobs: Analyzing and Breaking RKE Protocols** - **Clonable Key Fobs: Analyzing and Breaking RKE Protocols**
@@ -225,33 +213,65 @@ The following academic publications have been invaluable to the development and
*Wiley, February 2025* *Wiley, February 2025*
DOI: [10.1002/9781394351930.ch11](https://doi.org/10.1002/9781394351930.ch11) DOI: [10.1002/9781394351930.ch11](https://doi.org/10.1002/9781394351930.ch11)
### DST Cipher Family (DST40 / DST80)
- **Security Analysis of a Cryptographically-Enabled RFID Device**
Steve Bono, Matthew Green, Adam Stubblefield, Ari Juels, Avi Rubin, Michael Szydlo
*14th USENIX Security Symposium (USENIX Security '05)*
https://www.usenix.org/conference/14th-usenix-security-symposium/security-analysis-cryptographically-enabled-rfid-device
https://www.usenix.org/legacy/event/sec05/tech/bono/bono.pdf
- **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. 2020(2), pp. 99127*
DOI: [10.13154/tches.v2020.i2.99-127](https://doi.org/10.13154/tches.v2020.i2.99-127)
### KeeLoq Cryptanalysis ### KeeLoq Cryptanalysis
- **Cryptanalysis of the KeeLoq Block Cipher** - **Cryptanalysis of the KeeLoq Block Cipher**
Andrey Bogdanov Andrey Bogdanov
*Cryptology ePrint Archive, Paper 2007/055* *Cryptology ePrint Archive, Paper 2007/055; also presented at RFIDSec 2007*
https://eprint.iacr.org/2007/055 https://eprint.iacr.org/2007/055
- **On the Power of Power Analysis in the Real World: A Complete Break of the KeeLoq Code Hopping Scheme**
Thomas Eisenbarth, Timo Kasper, Amir Moradi, Christof Paar, Mahmoud Salmasizadeh, Mohammad T. Manzuri Shalmani
*CRYPTO 2008*
https://www.iacr.org/archive/crypto2008/51570204/51570204.pdf
- **A Practical Attack on KeeLoq** - **A Practical Attack on KeeLoq**
Sebastiaan Indesteege, Nathan Keller, Orr Dunkelman, Eli Biham, Bart Preneel Sebastiaan Indesteege, Nathan Keller, Orr Dunkelman, Eli Biham, Bart Preneel
*EUROCRYPT 2008* *EUROCRYPT 2008 (LNCS vol. 4965, pp. 118)*
DOI: [10.1007/978-3-540-78967-3_1](https://doi.org/10.1007/978-3-540-78967-3_1)
https://www.iacr.org/archive/eurocrypt2008/49650001/49650001.pdf https://www.iacr.org/archive/eurocrypt2008/49650001/49650001.pdf
- **Algebraic and Slide Attacks on KeeLoq**
Nicolas T. Courtois, Gregory V. Bard, David Wagner
*FSE 2008 (LNCS vol. 5086, pp. 97115)*
DOI: [10.1007/978-3-540-71039-4_6](https://doi.org/10.1007/978-3-540-71039-4_6)
- **On the Power of Power Analysis in the Real World: A Complete Break of the KeeLoq Code Hopping Scheme**
Thomas Eisenbarth, Timo Kasper, Amir Moradi, Christof Paar, Mahmoud Salmasizadeh, Mohammad T. Manzuri Shalmani
*CRYPTO 2008 (LNCS vol. 5157, pp. 203220)*
DOI: [10.1007/978-3-540-85174-5_12](https://doi.org/10.1007/978-3-540-85174-5_12)
https://www.iacr.org/archive/crypto2008/51570204/51570204.pdf
- **Breaking KeeLoq in a Flash: On Extracting Keys at Lightning Speed** - **Breaking KeeLoq in a Flash: On Extracting Keys at Lightning Speed**
*Springer* Markus Kasper, Timo Kasper, Amir Moradi, Christof Paar
*AFRICACRYPT 2009 (LNCS vol. 5580, pp. 403420)*
DOI: [10.1007/978-3-642-02384-2_25](https://doi.org/10.1007/978-3-642-02384-2_25) DOI: [10.1007/978-3-642-02384-2_25](https://doi.org/10.1007/978-3-642-02384-2_25)
### Immobiliser & Transponder Systems ### Immobiliser & Transponder Cipher Attacks
- **Dismantling DST80-based Immobiliser Systems** - **Gone in 360 Seconds: Hijacking with Hitag2**
Lennert Wouters, Jan Van den Herrewegen, Flavio D. Garcia, David Oswald, Benedikt Gierlichs, Bart Preneel Roel Verdult, Flavio D. Garcia, Josep Balasch
*IACR Transactions on Cryptographic Hardware and Embedded Systems (TCHES), 2020, Vol. 2* *21st USENIX Security Symposium (USENIX Security '12), pp. 237252*
DOI: [10.13154/tches.v2020.i2.99-127](https://doi.org/10.13154/tches.v2020.i2.99-127) DOI: [10.5555/2362793.2362830](https://doi.org/10.5555/2362793.2362830)
https://www.usenix.org/system/files/conference/usenixsecurity12/sec12-final95.pdf
- **Dismantling Megamos Crypto: Wirelessly Lockpicking a Vehicle Immobilizer**
Roel Verdult, Flavio D. Garcia, Baris Ege
*Supplement to 22nd USENIX Security Symposium (USENIX Security '13/15), pp. 703718*
https://www.usenix.org/sites/default/files/sec15_supplement.pdf
- **Dismantling the AUT64 Automotive Cipher**
Christopher Hicks, Flavio D. Garcia, David Oswald
*IACR Transactions on Cryptographic Hardware and Embedded Systems (TCHES), 2018, Vol. 2018(2), pp. 4669*
DOI: [10.13154/tches.v2018.i2.46-69](https://doi.org/10.13154/tches.v2018.i2.46-69)
### RFID & Protocol Analysis Tooling ### RFID & Protocol Analysis Tooling
@@ -262,6 +282,11 @@ The following academic publications have been invaluable to the development and
### Relay & Replay Attacks ### Relay & Replay Attacks
- **Relay Attacks on Passive Keyless Entry and Start Systems in Modern Cars**
Aurélien Francillon, Boris Danev, Srdjan Čapkun
*NDSS 2011*
https://www.ndss-symposium.org/ndss2011/relay-attacks-on-passive-keyless-entry-and-start-systems-in-modern-cars/
- **Implementing and Testing RollJam on Software-Defined Radios** - **Implementing and Testing RollJam on Software-Defined Radios**
*Università di Bologna (UNIBO), CRIS* *Università di Bologna (UNIBO), CRIS*
https://cris.unibo.it/handle/11585/999874 https://cris.unibo.it/handle/11585/999874
@@ -272,13 +297,14 @@ The following academic publications have been invaluable to the development and
- **RollBack: A New Time-Agnostic Replay Attack Against the Automotive Remote Keyless Entry Systems** - **RollBack: A New Time-Agnostic Replay Attack Against the Automotive Remote Keyless Entry Systems**
Levente Csikor, Hoon Wei Lim, Jun Wen Wong, Soundarya Ramesh, Rohini Poolat Parameswarath, Mun Choon Chan Levente Csikor, Hoon Wei Lim, Jun Wen Wong, Soundarya Ramesh, Rohini Poolat Parameswarath, Mun Choon Chan
*ACM* *Black Hat USA 2022; ACM Transactions on Cyber-Physical Systems, 2024*
DOI: [10.1145/3627827](https://doi.org/10.1145/3627827) DOI: [10.1145/3627827](https://doi.org/10.1145/3627827)
https://i.blackhat.com/USA-22/Thursday/US-22-Csikor-Rollback-A-New-Time-Agnostic-Replay-wp.pdf
- **Relay Attacks on Passive Keyless Entry and Start Systems in Modern Cars** - **Rolling-PWN Attack (Honda RKE Vulnerability)**
Aurelien Francillon, Boris Danev, Srdjan Capkun Kevin2600 (Haoqi Shan), Wesley Li — Star-V Lab
*NDSS 2011* *Independent disclosure, 2022 (CVE-2021-46145)*
https://www.ndss-symposium.org/ndss2011/relay-attacks-on-passive-keyless-entry-and-start-systems-in-modern-cars/ https://rollingpwn.github.io/rolling-pwn/
--- ---

View File

@@ -1,5 +1,6 @@
#include "subghz_txrx_i.h" // IWYU pragma: keep #include "subghz_txrx_i.h" // IWYU pragma: keep
#include <math.h>
#include <lib/subghz/protocols/protocol_items.h> #include <lib/subghz/protocols/protocol_items.h>
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h> #include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h> #include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
@@ -34,6 +35,9 @@ SubGhzTxRx* subghz_txrx_alloc(void) {
instance->txrx_state = SubGhzTxRxStateSleep; instance->txrx_state = SubGhzTxRxStateSleep;
subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF); subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF);
subghz_txrx_preset_hopper_set_state(instance, SubGhzPresetHopperStateOFF);
instance->preset_hopper_idx = 0;
instance->preset_hopper_timeout = 0;
subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable); subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable);
subghz_txrx_set_debug_pin_state(instance, false); subghz_txrx_set_debug_pin_state(instance, false);
@@ -494,52 +498,153 @@ void subghz_txrx_hopper_pause(SubGhzTxRx* instance) {
} }
} }
#define SUBGHZ_MOD_HOPPER_DWELL_TICKS 3 void subghz_txrx_preset_hopper_update(SubGhzTxRx* instance, float stay_threshold) {
bool subghz_txrx_mod_hopper_get_running(SubGhzTxRx* instance) {
furi_assert(instance); furi_assert(instance);
return instance->mod_hopper_running;
}
void subghz_txrx_mod_hopper_set_running(SubGhzTxRx* instance, bool running) { switch(instance->preset_hopper_state) {
furi_assert(instance); case SubGhzPresetHopperStateOFF:
instance->mod_hopper_running = running; case SubGhzPresetHopperStatePause:
if(running) instance->mod_hopper_timer = SUBGHZ_MOD_HOPPER_DWELL_TICKS;
}
void subghz_txrx_mod_hopper_update(SubGhzTxRx* instance) {
furi_assert(instance);
if(!instance->mod_hopper_running) return;
if(instance->mod_hopper_timer > 0) {
instance->mod_hopper_timer--;
return; return;
case SubGhzPresetHopperStateRSSITimeOut:
if(instance->preset_hopper_timeout != 0) {
instance->preset_hopper_timeout--;
return;
}
break;
default:
break;
} }
instance->mod_hopper_timer = SUBGHZ_MOD_HOPPER_DWELL_TICKS;
size_t count = subghz_setting_get_preset_count(instance->setting); if(instance->preset_hopper_state != SubGhzPresetHopperStateRSSITimeOut) {
if(count == 0) return; float rssi = subghz_devices_get_rssi(instance->radio_device);
// Advance index, skip CUSTOM presets if(rssi > stay_threshold) {
uint8_t tries = 0; instance->preset_hopper_timeout = 20;
do { instance->preset_hopper_state = SubGhzPresetHopperStateRSSITimeOut;
instance->mod_hopper_idx = (instance->mod_hopper_idx + 1) % count; return;
tries++; }
} while(tries < count && } else {
strcmp( instance->preset_hopper_state = SubGhzPresetHopperStateRunning;
subghz_setting_get_preset_name(instance->setting, instance->mod_hopper_idx), }
"CUSTOM") == 0);
const char* preset_name = size_t hopper_preset_count = subghz_setting_get_hopper_preset_count(instance->setting);
subghz_setting_get_preset_name(instance->setting, instance->mod_hopper_idx);
uint8_t* preset_data =
subghz_setting_get_preset_data(instance->setting, instance->mod_hopper_idx);
size_t preset_data_size =
subghz_setting_get_preset_data_size(instance->setting, instance->mod_hopper_idx);
subghz_txrx_set_preset( if(hopper_preset_count > 0) {
instance, preset_name, instance->preset->frequency, preset_data, preset_data_size); if(instance->preset_hopper_idx < hopper_preset_count - 1) {
subghz_txrx_rx_start(instance); instance->preset_hopper_idx++;
} else {
instance->preset_hopper_idx = 0;
}
size_t actual_preset_idx = subghz_setting_get_hopper_preset_index(
instance->setting, instance->preset_hopper_idx);
if(instance->txrx_state == SubGhzTxRxStateRx) {
subghz_txrx_rx_end(instance);
}
if(instance->txrx_state == SubGhzTxRxStateIDLE) {
const char* old_preset_name = furi_string_get_cstr(instance->preset->name);
const char* preset_name =
subghz_setting_get_preset_name(instance->setting, actual_preset_idx);
subghz_txrx_set_preset_internal(
instance, instance->preset->frequency, actual_preset_idx, 0);
bool old_is_am = (strstr(old_preset_name, "AM") != NULL);
bool new_is_am = (strstr(preset_name, "AM") != NULL);
bool modulation_changed = (old_is_am != new_is_am);
if(modulation_changed) {
subghz_devices_reset(instance->radio_device);
subghz_devices_load_preset(
instance->radio_device,
FuriHalSubGhzPresetCustom,
instance->preset->data);
} else {
subghz_devices_load_preset(
instance->radio_device,
FuriHalSubGhzPresetCustom,
instance->preset->data);
}
subghz_txrx_rx(instance, instance->preset->frequency);
}
} else {
size_t preset_count = subghz_setting_get_preset_count(instance->setting);
if(instance->preset_hopper_idx < preset_count - 1) {
instance->preset_hopper_idx++;
} else {
instance->preset_hopper_idx = 0;
}
if(instance->txrx_state == SubGhzTxRxStateRx) {
subghz_txrx_rx_end(instance);
}
if(instance->txrx_state == SubGhzTxRxStateIDLE) {
const char* old_preset_name = furi_string_get_cstr(instance->preset->name);
const char* preset_name =
subghz_setting_get_preset_name(instance->setting, instance->preset_hopper_idx);
subghz_txrx_set_preset_internal(
instance, instance->preset->frequency, instance->preset_hopper_idx, 0);
bool old_is_am = (strstr(old_preset_name, "AM") != NULL);
bool new_is_am = (strstr(preset_name, "AM") != NULL);
bool modulation_changed = (old_is_am != new_is_am);
if(modulation_changed) {
subghz_devices_reset(instance->radio_device);
subghz_devices_load_preset(
instance->radio_device,
FuriHalSubGhzPresetCustom,
instance->preset->data);
} else {
subghz_devices_load_preset(
instance->radio_device,
FuriHalSubGhzPresetCustom,
instance->preset->data);
}
subghz_txrx_rx(instance, instance->preset->frequency);
}
}
}
SubGhzPresetHopperState subghz_txrx_preset_hopper_get_state(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->preset_hopper_state;
}
void subghz_txrx_preset_hopper_set_state(SubGhzTxRx* instance, SubGhzPresetHopperState state) {
furi_assert(instance);
instance->preset_hopper_state = state;
if(state == SubGhzPresetHopperStateRunning) {
subghz_devices_reset(instance->radio_device);
subghz_devices_load_preset(
instance->radio_device,
FuriHalSubGhzPresetCustom,
instance->preset->data);
}
}
void subghz_txrx_preset_hopper_unpause(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->preset_hopper_state == SubGhzPresetHopperStatePause) {
instance->preset_hopper_state = SubGhzPresetHopperStateRunning;
}
}
void subghz_txrx_preset_hopper_pause(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->preset_hopper_state == SubGhzPresetHopperStateRunning) {
instance->preset_hopper_state = SubGhzPresetHopperStatePause;
}
}
void subghz_txrx_preset_hopper_reset_index(SubGhzTxRx* instance, size_t index) {
furi_assert(instance);
instance->preset_hopper_idx = index;
} }
void subghz_txrx_speaker_on(SubGhzTxRx* instance) { void subghz_txrx_speaker_on(SubGhzTxRx* instance) {

View File

@@ -164,29 +164,17 @@ void subghz_txrx_hopper_unpause(SubGhzTxRx* instance);
*/ */
void subghz_txrx_hopper_pause(SubGhzTxRx* instance); void subghz_txrx_hopper_pause(SubGhzTxRx* instance);
/** void subghz_txrx_preset_hopper_update(SubGhzTxRx* instance, float stay_threshold);
* Update modulation (preset) CC1101 in automatic mode (mod hopper)
* Cycles through available presets at a fixed dwell time.
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_mod_hopper_update(SubGhzTxRx* instance);
/** SubGhzPresetHopperState subghz_txrx_preset_hopper_get_state(SubGhzTxRx* instance);
* Set mod hopper running state
*
* @param instance Pointer to a SubGhzTxRx
* @param running true to enable, false to disable
*/
void subghz_txrx_mod_hopper_set_running(SubGhzTxRx* instance, bool running);
/** void subghz_txrx_preset_hopper_set_state(SubGhzTxRx* instance, SubGhzPresetHopperState state);
* Get mod hopper running state
* void subghz_txrx_preset_hopper_unpause(SubGhzTxRx* instance);
* @param instance Pointer to a SubGhzTxRx
* @return true if mod hopping is active void subghz_txrx_preset_hopper_pause(SubGhzTxRx* instance);
*/
bool subghz_txrx_mod_hopper_get_running(SubGhzTxRx* instance); void subghz_txrx_preset_hopper_reset_index(SubGhzTxRx* instance, size_t index);
/** /**
* Speaker on * Speaker on

View File

@@ -19,9 +19,9 @@ struct SubGhzTxRx {
bool is_database_loaded; bool is_database_loaded;
SubGhzHopperState hopper_state; SubGhzHopperState hopper_state;
uint8_t mod_hopper_idx; // index into setting presets (wraps around) uint8_t preset_hopper_timeout;
uint8_t mod_hopper_timer; // countdown ticks before advancing modulation size_t preset_hopper_idx;
bool mod_hopper_running; // is mod hopping active SubGhzPresetHopperState preset_hopper_state;
SubGhzTxRxState txrx_state; SubGhzTxRxState txrx_state;
SubGhzSpeakerState speaker_state; SubGhzSpeakerState speaker_state;

View File

@@ -29,6 +29,14 @@ typedef enum {
SubGhzHopperStateRSSITimeOut, SubGhzHopperStateRSSITimeOut,
} SubGhzHopperState; } SubGhzHopperState;
/** SubGhzPresetHopperState state */
typedef enum {
SubGhzPresetHopperStateOFF,
SubGhzPresetHopperStateRunning,
SubGhzPresetHopperStatePause,
SubGhzPresetHopperStateRSSITimeOut,
} SubGhzPresetHopperState;
/** SubGhzSpeakerState state */ /** SubGhzSpeakerState state */
typedef enum { typedef enum {
SubGhzSpeakerStateDisable, SubGhzSpeakerStateDisable,

View File

@@ -40,3 +40,8 @@ Version: 1
#Custom_preset_name: AM_2 #Custom_preset_name: AM_2
#Custom_preset_module: CC1101 #Custom_preset_module: CC1101
#Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 #Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00
# Presets used for preset hopping mode (cycles through these modulations)
#Hopping_Preset: AM650
#Hopping_Preset: FM238
#Hopping_Preset: FM476

View File

@@ -213,7 +213,12 @@ void subghz_scene_receiver_on_enter(void* context) {
} else { } else {
subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
} }
subghz_txrx_mod_hopper_set_running(subghz->txrx, subghz->last_settings->enable_mod_hopping);
if(subghz->last_settings->enable_preset_hopping) {
subghz_txrx_preset_hopper_set_state(subghz->txrx, SubGhzPresetHopperStateRunning);
} else {
subghz_txrx_preset_hopper_set_state(subghz->txrx, SubGhzPresetHopperStateOFF);
}
subghz_txrx_rx_start(subghz->txrx); subghz_txrx_rx_start(subghz->txrx);
subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen); subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen);
@@ -242,6 +247,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz_txrx_stop(subghz->txrx); subghz_txrx_stop(subghz->txrx);
subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
subghz_txrx_preset_hopper_set_state(subghz->txrx, SubGhzPresetHopperStateOFF);
subghz_txrx_set_rx_callback(subghz->txrx, NULL, subghz); subghz_txrx_set_rx_callback(subghz->txrx, NULL, subghz);
if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) {
@@ -302,8 +308,8 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
subghz_txrx_hopper_update(subghz->txrx, subghz->last_settings->hopping_threshold); subghz_txrx_hopper_update(subghz->txrx, subghz->last_settings->hopping_threshold);
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);
} }
if(subghz_txrx_mod_hopper_get_running(subghz->txrx)) { if(subghz_txrx_preset_hopper_get_state(subghz->txrx) != SubGhzPresetHopperStateOFF) {
subghz_txrx_mod_hopper_update(subghz->txrx); subghz_txrx_preset_hopper_update(subghz->txrx, subghz->last_settings->preset_hopping_threshold);
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);
} }

View File

@@ -1,13 +1,14 @@
#include "../subghz_i.h" #include "../subghz_i.h"
#include <lib/toolbox/value_index.h> #include <lib/toolbox/value_index.h>
#include <math.h>
#define TAG "SubGhzSceneReceiverConfig" #define TAG "SubGhzSceneReceiverConfig"
enum SubGhzSettingIndex { enum SubGhzSettingIndex {
SubGhzSettingIndexFrequency, SubGhzSettingIndexFrequency,
SubGhzSettingIndexHopping,
SubGhzSettingIndexModulation, SubGhzSettingIndexModulation,
SubGhzSettingIndexModHopping, SubGhzSettingIndexHopping,
SubGhzSettingIndexPresetHopping,
SubGhzSettingIndexBinRAW, SubGhzSettingIndexBinRAW,
SubGhzSettingIndexIgnoreReversRB2, SubGhzSettingIndexIgnoreReversRB2,
SubGhzSettingIndexIgnoreAlarms, SubGhzSettingIndexIgnoreAlarms,
@@ -81,6 +82,36 @@ const float hopping_mode_value[HOPPING_MODE_COUNT] = {
-40.0f, -40.0f,
}; };
#define PRESET_HOPPING_MODE_COUNT 12
const char* const preset_hopping_mode_text[PRESET_HOPPING_MODE_COUNT] = {
"OFF",
"-90dBm",
"-85dBm",
"-80dBm",
"-75dBm",
"-70dBm",
"-65dBm",
"-60dBm",
"-55dBm",
"-50dBm",
"-45dBm",
"-40dBm",
};
const float preset_hopping_mode_value[PRESET_HOPPING_MODE_COUNT] = {
NAN,
-90.0f,
-85.0f,
-80.0f,
-75.0f,
-70.0f,
-65.0f,
-60.0f,
-55.0f,
-50.0f,
-45.0f,
-40.0f,
};
#define COMBO_BOX_COUNT 2 #define COMBO_BOX_COUNT 2
const uint32_t hopping_value[COMBO_BOX_COUNT] = { const uint32_t hopping_value[COMBO_BOX_COUNT] = {
@@ -153,6 +184,23 @@ uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void*
return index; return index;
} }
uint8_t subghz_scene_receiver_config_preset_hopper_value_index(void* context) {
furi_assert(context);
SubGhz* subghz = context;
if(subghz_txrx_preset_hopper_get_state(subghz->txrx) == SubGhzPresetHopperStateOFF) {
return 0;
} else {
variable_item_set_current_value_text(
variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexModulation),
" -----");
return value_index_float(
subghz->last_settings->preset_hopping_threshold,
preset_hopping_mode_value,
PRESET_HOPPING_MODE_COUNT);
}
}
uint8_t subghz_scene_receiver_config_hopper_value_index(void* context) { uint8_t subghz_scene_receiver_config_hopper_value_index(void* context) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
@@ -213,19 +261,21 @@ static void subghz_scene_receiver_config_set_preset(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
const char* preset_name = subghz_setting_get_preset_name(setting, index); if(subghz_txrx_preset_hopper_get_state(subghz->txrx) == SubGhzPresetHopperStateOFF) {
variable_item_set_current_value_text(item, preset_name); const char* preset_name = subghz_setting_get_preset_name(setting, index);
//subghz->last_settings->preset = index; variable_item_set_current_value_text(item, preset_name);
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
uint8_t* preset_data = subghz_setting_get_preset_data(setting, index); uint8_t* preset_data = subghz_setting_get_preset_data(setting, index);
size_t preset_data_size = subghz_setting_get_preset_data_size(setting, index); size_t preset_data_size = subghz_setting_get_preset_data_size(setting, index);
//Edit TX power, if necessary. subghz_txrx_set_tx_power(preset_data, preset_data_size, subghz->tx_power);
subghz_txrx_set_tx_power(preset_data, preset_data_size, subghz->tx_power);
subghz_txrx_set_preset( subghz_txrx_set_preset(
subghz->txrx, preset_name, preset.frequency, preset_data, preset_data_size); subghz->txrx, preset_name, preset.frequency, preset_data, preset_data_size);
subghz->last_settings->preset_index = index; subghz->last_settings->preset_index = index;
} else {
variable_item_set_current_value_index(item, subghz->last_settings->preset_index);
}
} }
static void subghz_scene_receiver_config_set_hopping(VariableItem* item) { static void subghz_scene_receiver_config_set_hopping(VariableItem* item) {
@@ -274,15 +324,57 @@ static void subghz_scene_receiver_config_set_hopping(VariableItem* item) {
subghz->last_settings->hopping_threshold = hopping_mode_value[index]; subghz->last_settings->hopping_threshold = hopping_mode_value[index];
subghz_txrx_hopper_set_state( subghz_txrx_hopper_set_state(
subghz->txrx, index != 0 ? SubGhzHopperStateRunning : SubGhzHopperStateOFF); subghz->txrx, index != 0 ? SubGhzHopperStateRunning : SubGhzHopperStateOFF);
VariableItem* preset_hopping_item =
variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexPresetHopping);
variable_item_set_locked(
preset_hopping_item,
index != 0,
"Turn off\nHopping\nfirst!");
} }
static void subghz_scene_receiver_config_set_mod_hopping(VariableItem* item) { static void subghz_scene_receiver_config_set_preset_hopping(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item); SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, combobox_text[index]); SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
bool enabled = index == 1; VariableItem* preset_item =
subghz->last_settings->enable_mod_hopping = enabled; variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexModulation);
subghz_txrx_mod_hopper_set_running(subghz->txrx, enabled);
variable_item_set_current_value_text(item, preset_hopping_mode_text[index]);
if(index == 0) {
SubGhzRadioPreset current_preset = subghz_txrx_get_preset(subghz->txrx);
const char* current_preset_name = furi_string_get_cstr(current_preset.name);
int current_preset_index = subghz_setting_get_inx_preset_by_name(setting, current_preset_name);
if(current_preset_index >= 0) {
subghz->last_settings->preset_index = current_preset_index;
}
variable_item_set_current_value_text(preset_item, current_preset_name);
variable_item_set_current_value_index(preset_item, subghz->last_settings->preset_index);
subghz->last_settings->enable_preset_hopping = false;
subghz_txrx_preset_hopper_set_state(subghz->txrx, SubGhzPresetHopperStateOFF);
} else {
bool was_running = (subghz_txrx_preset_hopper_get_state(subghz->txrx) == SubGhzPresetHopperStateRunning);
if(was_running) {
subghz_txrx_preset_hopper_pause(subghz->txrx);
}
subghz->last_settings->preset_hopping_threshold = preset_hopping_mode_value[index];
variable_item_set_current_value_text(preset_item, " -----");
variable_item_set_current_value_index(preset_item, subghz->last_settings->preset_index);
subghz->last_settings->enable_preset_hopping = true;
subghz_txrx_preset_hopper_set_state(subghz->txrx, SubGhzPresetHopperStateRunning);
}
VariableItem* hopping_item =
variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexHopping);
variable_item_set_locked(
hopping_item,
index != 0,
"Turn off\nPreset\nHopping\nfirst!");
} }
static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
@@ -385,8 +477,9 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context,
subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[default_index]); subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[default_index]);
subghz->last_settings->enable_hopping = hopping_value[default_index]; subghz->last_settings->enable_hopping = hopping_value[default_index];
subghz->last_settings->enable_mod_hopping = false; subghz->last_settings->enable_preset_hopping = false;
subghz_txrx_mod_hopper_set_running(subghz->txrx, false); subghz->last_settings->preset_hopping_threshold = SUBGHZ_LAST_SETTING_DEFAULT_PRESET_HOPPING_THRESHOLD;
subghz_txrx_preset_hopper_set_state(subghz->txrx, SubGhzPresetHopperStateOFF);
variable_item_list_set_selected_item(subghz->variable_item_list, default_index); variable_item_list_set_selected_item(subghz->variable_item_list, default_index);
variable_item_list_reset(subghz->variable_item_list); variable_item_list_reset(subghz->variable_item_list);
@@ -451,16 +544,26 @@ void subghz_scene_receiver_config_on_enter(void* context) {
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, hopping_mode_text[value_index]); variable_item_set_current_value_text(item, hopping_mode_text[value_index]);
// Mod Hopping variable_item_set_locked(
item,
subghz_txrx_preset_hopper_get_state(subghz->txrx) != SubGhzPresetHopperStateOFF,
"Turn off\nPreset\nHopping\nfirst!");
value_index = subghz_scene_receiver_config_preset_hopper_value_index(subghz);
item = variable_item_list_add( item = variable_item_list_add(
subghz->variable_item_list, subghz->variable_item_list,
"Mod Hopping", "Preset Hopping",
COMBO_BOX_COUNT, PRESET_HOPPING_MODE_COUNT,
subghz_scene_receiver_config_set_mod_hopping, subghz_scene_receiver_config_set_preset_hopping,
subghz); subghz);
value_index = subghz->last_settings->enable_mod_hopping ? 1 : 0;
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, combobox_text[value_index]); variable_item_set_current_value_text(item, preset_hopping_mode_text[value_index]);
variable_item_set_locked(
item,
subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF,
"Turn off\nHopping\nfirst!");
} }
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=

View File

@@ -13,7 +13,8 @@
#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger" #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger"
#define SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES "ProtocolNames" #define SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES "ProtocolNames"
#define SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE "Hopping" #define SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE "Hopping"
#define SUBGHZ_LAST_SETTING_FIELD_MOD_HOPPING "ModHopping" #define SUBGHZ_LAST_SETTING_FIELD_PRESET_HOPPING "PresetHopping"
#define SUBGHZ_LAST_SETTING_FIELD_PRESET_HOPPING_THRESHOLD "PresetHoppingThreshold"
#define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter" #define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter"
#define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter" #define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter"
#define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI" #define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI"
@@ -46,6 +47,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
instance->filter = SubGhzProtocolFlag_Decodable; instance->filter = SubGhzProtocolFlag_Decodable;
instance->rssi = SUBGHZ_RAW_THRESHOLD_MIN; instance->rssi = SUBGHZ_RAW_THRESHOLD_MIN;
instance->hopping_threshold = -90.0f; instance->hopping_threshold = -90.0f;
instance->enable_preset_hopping = false;
instance->preset_hopping_threshold = SUBGHZ_LAST_SETTING_DEFAULT_PRESET_HOPPING_THRESHOLD;
instance->leds_and_amp = true; instance->leds_and_amp = true;
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
@@ -101,11 +104,23 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
} }
if(!flipper_format_read_bool( if(!flipper_format_read_bool(
fff_data_file, fff_data_file,
SUBGHZ_LAST_SETTING_FIELD_MOD_HOPPING, SUBGHZ_LAST_SETTING_FIELD_PRESET_HOPPING,
&instance->enable_mod_hopping, &instance->enable_preset_hopping,
1)) { 1)) {
instance->enable_preset_hopping = false;
flipper_format_rewind(fff_data_file); flipper_format_rewind(fff_data_file);
} }
float temp_preset_threshold = 0;
if(!flipper_format_read_float(
fff_data_file,
SUBGHZ_LAST_SETTING_FIELD_PRESET_HOPPING_THRESHOLD,
&temp_preset_threshold,
1)) {
instance->preset_hopping_threshold = SUBGHZ_LAST_SETTING_DEFAULT_PRESET_HOPPING_THRESHOLD;
flipper_format_rewind(fff_data_file);
} else {
instance->preset_hopping_threshold = temp_preset_threshold;
}
if(!flipper_format_read_uint32( if(!flipper_format_read_uint32(
fff_data_file, fff_data_file,
SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER, SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER,
@@ -223,8 +238,15 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
} }
if(!flipper_format_write_bool( if(!flipper_format_write_bool(
file, file,
SUBGHZ_LAST_SETTING_FIELD_MOD_HOPPING, SUBGHZ_LAST_SETTING_FIELD_PRESET_HOPPING,
&instance->enable_mod_hopping, &instance->enable_preset_hopping,
1)) {
break;
}
if(!flipper_format_write_float(
file,
SUBGHZ_LAST_SETTING_FIELD_PRESET_HOPPING_THRESHOLD,
&instance->preset_hopping_threshold,
1)) { 1)) {
break; break;
} }

View File

@@ -12,20 +12,22 @@
#define SUBGHZ_LAST_SETTING_DEFAULT_PRESET 1 #define SUBGHZ_LAST_SETTING_DEFAULT_PRESET 1
#define SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY 433920000 #define SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY 433920000
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2 #define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2
#define SUBGHZ_LAST_SETTING_DEFAULT_PRESET_HOPPING_THRESHOLD (-80.0f)
typedef struct { typedef struct {
uint32_t frequency; uint32_t frequency;
uint32_t preset_index; // AKA Modulation uint32_t preset_index;
uint32_t frequency_analyzer_feedback_level; uint32_t frequency_analyzer_feedback_level;
float frequency_analyzer_trigger; float frequency_analyzer_trigger;
bool protocol_file_names; bool protocol_file_names;
bool enable_hopping; bool enable_hopping;
bool enable_mod_hopping;
uint32_t ignore_filter; uint32_t ignore_filter;
uint32_t filter; uint32_t filter;
float rssi; float rssi;
bool delete_old_signals; bool delete_old_signals;
float hopping_threshold; float hopping_threshold;
bool enable_preset_hopping;
float preset_hopping_threshold;
bool leds_and_amp; bool leds_and_amp;
uint8_t tx_power; uint8_t tx_power;
} SubGhzLastSettings; } SubGhzLastSettings;

View File

@@ -17,8 +17,9 @@
// //
// Frame layout (after stripping 16-bit 0xFFFF preamble): // Frame layout (after stripping 16-bit 0xFFFF preamble):
// Bytes 0-3: Fixed ID / Serial (32 bits) // Bytes 0-3: Fixed ID / Serial (32 bits)
// Byte 4: Button (upper nibble) | Type (lower nibble, always 0x2) // Byte 4: Button (upper nibble) | Type (lower nibble)
// Bytes 5-10: Rolling/encrypted code (48 bits) // Buttons: 0x7=Lock, 0xB=Unlock, 0xD=Trunk
// Bytes 5-10: Rolling/encrypted code (48 bits)
#define FIAT_MARELLI_PREAMBLE_MIN 200 // Min preamble pulses (100 pairs) #define FIAT_MARELLI_PREAMBLE_MIN 200 // Min preamble pulses (100 pairs)
#define FIAT_MARELLI_GAP_MIN 2500 // Gap detection threshold (us) #define FIAT_MARELLI_GAP_MIN 2500 // Gap detection threshold (us)
#define FIAT_MARELLI_SYNC_MIN 1500 // Sync pulse minimum (us) #define FIAT_MARELLI_SYNC_MIN 1500 // Sync pulse minimum (us)
@@ -402,10 +403,12 @@ SubGhzProtocolStatus subghz_protocol_decoder_fiat_marelli_deserialize(
static const char* fiat_marelli_button_name(uint8_t btn) { static const char* fiat_marelli_button_name(uint8_t btn) {
switch(btn) { switch(btn) {
case 0x2: case 0x7:
return "Btn A"; return "Lock";
case 0x4: case 0xB:
return "Btn B"; return "Unlock";
case 0xD:
return "Trunk";
default: default:
return "Unknown"; return "Unknown";
} }

View File

@@ -117,6 +117,7 @@ typedef struct {
struct SubGhzSetting { struct SubGhzSetting {
FrequencyList_t frequencies; FrequencyList_t frequencies;
FrequencyList_t hopper_frequencies; FrequencyList_t hopper_frequencies;
PresetIndexList_t hopper_preset_indices;
SubGhzSettingCustomPresetStruct* preset; SubGhzSettingCustomPresetStruct* preset;
}; };
@@ -124,6 +125,7 @@ SubGhzSetting* subghz_setting_alloc(void) {
SubGhzSetting* instance = malloc(sizeof(SubGhzSetting)); SubGhzSetting* instance = malloc(sizeof(SubGhzSetting));
FrequencyList_init(instance->frequencies); FrequencyList_init(instance->frequencies);
FrequencyList_init(instance->hopper_frequencies); FrequencyList_init(instance->hopper_frequencies);
PresetIndexList_init(instance->hopper_preset_indices);
instance->preset = malloc(sizeof(SubGhzSettingCustomPresetStruct)); instance->preset = malloc(sizeof(SubGhzSettingCustomPresetStruct));
SubGhzSettingCustomPresetItemArray_init(instance->preset->data); SubGhzSettingCustomPresetItemArray_init(instance->preset->data);
return instance; return instance;
@@ -142,6 +144,7 @@ void subghz_setting_free(SubGhzSetting* instance) {
furi_check(instance); furi_check(instance);
FrequencyList_clear(instance->frequencies); FrequencyList_clear(instance->frequencies);
FrequencyList_clear(instance->hopper_frequencies); FrequencyList_clear(instance->hopper_frequencies);
PresetIndexList_clear(instance->hopper_preset_indices);
for for
M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) {
furi_string_free(item->custom_preset_name); furi_string_free(item->custom_preset_name);
@@ -183,6 +186,7 @@ static void subghz_setting_load_default_region(
FrequencyList_reset(instance->frequencies); FrequencyList_reset(instance->frequencies);
FrequencyList_reset(instance->hopper_frequencies); FrequencyList_reset(instance->hopper_frequencies);
PresetIndexList_reset(instance->hopper_preset_indices);
subghz_setting_preset_reset(instance); subghz_setting_preset_reset(instance);
while(*frequencies) { while(*frequencies) {
@@ -308,6 +312,22 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) {
instance, furi_string_get_cstr(temp_str), fff_data_file); instance, furi_string_get_cstr(temp_str), fff_data_file);
} }
if(!flipper_format_rewind(fff_data_file)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
furi_string_reset(temp_str);
while(flipper_format_read_string(fff_data_file, "Hopping_Preset", temp_str)) {
const char* preset_name = furi_string_get_cstr(temp_str);
int preset_index = subghz_setting_get_inx_preset_by_name(instance, preset_name);
if(preset_index >= 0) {
PresetIndexList_push_back(instance->hopper_preset_indices, (size_t)preset_index);
FURI_LOG_I(TAG, "Hopper preset loaded: %s (index %d)", preset_name, preset_index);
} else {
FURI_LOG_E(TAG, "Hopper preset not found: %s", preset_name);
}
}
} while(false); } while(false);
} }
@@ -347,6 +367,19 @@ size_t subghz_setting_get_preset_count(SubGhzSetting* instance) {
return SubGhzSettingCustomPresetItemArray_size(instance->preset->data); return SubGhzSettingCustomPresetItemArray_size(instance->preset->data);
} }
size_t subghz_setting_get_hopper_preset_count(SubGhzSetting* instance) {
furi_check(instance);
return PresetIndexList_size(instance->hopper_preset_indices);
}
size_t subghz_setting_get_hopper_preset_index(SubGhzSetting* instance, size_t idx) {
furi_check(instance);
if(idx < PresetIndexList_size(instance->hopper_preset_indices)) {
return *PresetIndexList_get(instance->hopper_preset_indices, idx);
}
return 0;
}
const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) { const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) {
furi_check(instance); furi_check(instance);
if(idx >= SubGhzSettingCustomPresetItemArray_size(instance->preset->data)) { if(idx >= SubGhzSettingCustomPresetItemArray_size(instance->preset->data)) {
@@ -367,7 +400,6 @@ int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* p
} }
idx++; idx++;
} }
furi_crash("SubGhz: No name preset.");
return -1; return -1;
} }

View File

@@ -3,6 +3,7 @@
#include <math.h> #include <math.h>
#include <furi.h> #include <furi.h>
#include <m-list.h>
#include <furi_hal.h> #include <furi_hal.h>
#include <lib/flipper_format/flipper_format.h> #include <lib/flipper_format/flipper_format.h>
@@ -12,6 +13,9 @@ extern "C" {
#define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4 #define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4
LIST_DEF(PresetIndexList, size_t)
#define M_OPL_PresetIndexList_t() LIST_OPLIST(PresetIndexList)
typedef struct SubGhzSetting SubGhzSetting; typedef struct SubGhzSetting SubGhzSetting;
SubGhzSetting* subghz_setting_alloc(void); SubGhzSetting* subghz_setting_alloc(void);
@@ -55,6 +59,9 @@ void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t freq
uint8_t subghz_setting_customs_presets_to_log(SubGhzSetting* instance); uint8_t subghz_setting_customs_presets_to_log(SubGhzSetting* instance);
size_t subghz_setting_get_hopper_preset_count(SubGhzSetting* instance);
size_t subghz_setting_get_hopper_preset_index(SubGhzSetting* instance, size_t idx);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,87.9,, Version,+,87.10,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/applications.h,, Header,+,applications/services/applications.h,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
@@ -3692,6 +3692,8 @@ Function,+,subghz_setting_get_frequency_count,size_t,SubGhzSetting*
Function,+,subghz_setting_get_frequency_default_index,uint32_t,SubGhzSetting* Function,+,subghz_setting_get_frequency_default_index,uint32_t,SubGhzSetting*
Function,+,subghz_setting_get_hopper_frequency,uint32_t,"SubGhzSetting*, size_t" Function,+,subghz_setting_get_hopper_frequency,uint32_t,"SubGhzSetting*, size_t"
Function,+,subghz_setting_get_hopper_frequency_count,size_t,SubGhzSetting* Function,+,subghz_setting_get_hopper_frequency_count,size_t,SubGhzSetting*
Function,+,subghz_setting_get_hopper_preset_count,size_t,SubGhzSetting*
Function,+,subghz_setting_get_hopper_preset_index,size_t,"SubGhzSetting*, size_t"
Function,+,subghz_setting_get_inx_preset_by_name,int,"SubGhzSetting*, const char*" Function,+,subghz_setting_get_inx_preset_by_name,int,"SubGhzSetting*, const char*"
Function,+,subghz_setting_get_preset_count,size_t,SubGhzSetting* Function,+,subghz_setting_get_preset_count,size_t,SubGhzSetting*
Function,+,subghz_setting_get_preset_data,uint8_t*,"SubGhzSetting*, size_t" Function,+,subghz_setting_get_preset_data,uint8_t*,"SubGhzSetting*, size_t"
1 entry status name type params
2 Version + 87.9 87.10
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/applications.h
5 Header + applications/services/bt/bt_service/bt.h
3692 Function + subghz_setting_get_frequency_default_index uint32_t SubGhzSetting*
3693 Function + subghz_setting_get_hopper_frequency uint32_t SubGhzSetting*, size_t
3694 Function + subghz_setting_get_hopper_frequency_count size_t SubGhzSetting*
3695 Function + subghz_setting_get_hopper_preset_count size_t SubGhzSetting*
3696 Function + subghz_setting_get_hopper_preset_index size_t SubGhzSetting*, size_t
3697 Function + subghz_setting_get_inx_preset_by_name int SubGhzSetting*, const char*
3698 Function + subghz_setting_get_preset_count size_t SubGhzSetting*
3699 Function + subghz_setting_get_preset_data uint8_t* SubGhzSetting*, size_t