Compare commits

...

2 Commits

Author SHA1 Message Date
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
8 changed files with 188 additions and 49 deletions

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>
@@ -494,28 +495,39 @@ void subghz_txrx_hopper_pause(SubGhzTxRx* instance) {
} }
} }
#define SUBGHZ_MOD_HOPPER_DWELL_TICKS 3
bool subghz_txrx_mod_hopper_get_running(SubGhzTxRx* instance) { bool subghz_txrx_mod_hopper_get_running(SubGhzTxRx* instance) {
furi_assert(instance); furi_assert(instance);
return instance->mod_hopper_running; return instance->mod_hopper_running;
} }
void subghz_txrx_mod_hopper_set_running(SubGhzTxRx* instance, bool running) { void subghz_txrx_mod_hopper_set_running(
SubGhzTxRx* instance,
bool running,
uint8_t dwell_ticks,
float rssi_threshold) {
furi_assert(instance); furi_assert(instance);
instance->mod_hopper_running = running; instance->mod_hopper_running = running;
if(running) instance->mod_hopper_timer = SUBGHZ_MOD_HOPPER_DWELL_TICKS; instance->mod_hopper_dwell = dwell_ticks;
instance->mod_hopper_rssi_threshold = rssi_threshold;
if(running) instance->mod_hopper_timer = dwell_ticks;
} }
void subghz_txrx_mod_hopper_update(SubGhzTxRx* instance) { void subghz_txrx_mod_hopper_update(SubGhzTxRx* instance, float current_rssi) {
furi_assert(instance); furi_assert(instance);
if(!instance->mod_hopper_running) return; if(!instance->mod_hopper_running) return;
// If RSSI gating is enabled and signal is present, pause hopping
if(!isnan(instance->mod_hopper_rssi_threshold) &&
current_rssi > instance->mod_hopper_rssi_threshold) {
instance->mod_hopper_timer = instance->mod_hopper_dwell;
return;
}
if(instance->mod_hopper_timer > 0) { if(instance->mod_hopper_timer > 0) {
instance->mod_hopper_timer--; instance->mod_hopper_timer--;
return; return;
} }
instance->mod_hopper_timer = SUBGHZ_MOD_HOPPER_DWELL_TICKS; instance->mod_hopper_timer = instance->mod_hopper_dwell;
size_t count = subghz_setting_get_preset_count(instance->setting); size_t count = subghz_setting_get_preset_count(instance->setting);
if(count == 0) return; if(count == 0) return;

View File

@@ -166,19 +166,27 @@ void subghz_txrx_hopper_pause(SubGhzTxRx* instance);
/** /**
* Update modulation (preset) CC1101 in automatic mode (mod hopper) * Update modulation (preset) CC1101 in automatic mode (mod hopper)
* Cycles through available presets at a fixed dwell time. * Cycles through available presets at the configured dwell time.
* Pauses hopping when current_rssi exceeds the configured threshold.
* *
* @param instance Pointer to a SubGhzTxRx * @param instance Pointer to a SubGhzTxRx
* @param current_rssi Current RSSI reading from the radio
*/ */
void subghz_txrx_mod_hopper_update(SubGhzTxRx* instance); void subghz_txrx_mod_hopper_update(SubGhzTxRx* instance, float current_rssi);
/** /**
* Set mod hopper running state * Set mod hopper running state with configuration
* *
* @param instance Pointer to a SubGhzTxRx * @param instance Pointer to a SubGhzTxRx
* @param running true to enable, false to disable * @param running true to enable, false to disable
* @param dwell_ticks Ticks to dwell on each modulation (100ms per tick)
* @param rssi_threshold RSSI threshold to pause hopping (NAN = no gating)
*/ */
void subghz_txrx_mod_hopper_set_running(SubGhzTxRx* instance, bool running); void subghz_txrx_mod_hopper_set_running(
SubGhzTxRx* instance,
bool running,
uint8_t dwell_ticks,
float rssi_threshold);
/** /**
* Get mod hopper running state * Get mod hopper running state

View File

@@ -19,9 +19,11 @@ 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 mod_hopper_idx; // index into setting presets (wraps around)
uint8_t mod_hopper_timer; // countdown ticks before advancing modulation uint8_t mod_hopper_timer; // countdown ticks before advancing modulation
bool mod_hopper_running; // is mod hopping active uint8_t mod_hopper_dwell; // stored dwell ticks (configurable)
float mod_hopper_rssi_threshold; // RSSI threshold; NAN = no RSSI gating
bool mod_hopper_running; // is mod hopping active
SubGhzTxRxState txrx_state; SubGhzTxRxState txrx_state;
SubGhzSpeakerState speaker_state; SubGhzSpeakerState speaker_state;

View File

@@ -1,4 +1,5 @@
#include "../subghz_i.h" #include "../subghz_i.h"
#include <math.h>
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#include <lib/subghz/protocols/bin_raw.h> #include <lib/subghz/protocols/bin_raw.h>
@@ -213,7 +214,11 @@ 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); subghz_txrx_mod_hopper_set_running(
subghz->txrx,
!isnan(subghz->last_settings->mod_hopping_threshold),
(uint8_t)subghz->last_settings->mod_hopping_dwell,
subghz->last_settings->mod_hopping_threshold);
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);
@@ -303,7 +308,8 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);
} }
if(subghz_txrx_mod_hopper_get_running(subghz->txrx)) { if(subghz_txrx_mod_hopper_get_running(subghz->txrx)) {
subghz_txrx_mod_hopper_update(subghz->txrx); float rssi = subghz_txrx_radio_device_get_rssi(subghz->txrx);
subghz_txrx_mod_hopper_update(subghz->txrx, rssi);
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);
} }

View File

@@ -1,5 +1,6 @@
#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"
@@ -81,6 +82,44 @@ const float hopping_mode_value[HOPPING_MODE_COUNT] = {
-40.0f, -40.0f,
}; };
#define MOD_HOP_DBM_COUNT 8
const char* const mod_hop_dbm_text[MOD_HOP_DBM_COUNT] = {
"OFF",
"-90",
"-85",
"-80",
"-75",
"-70",
"-65",
"-60",
};
const float mod_hop_dbm_value[MOD_HOP_DBM_COUNT] = {
NAN,
-90.0f,
-85.0f,
-80.0f,
-75.0f,
-70.0f,
-65.0f,
-60.0f,
};
#define MOD_HOP_TIME_COUNT 5
const char* const mod_hop_time_text[MOD_HOP_TIME_COUNT] = {
"0.5s",
"1s",
"2s",
"5s",
"10s",
};
const uint32_t mod_hop_time_ticks[MOD_HOP_TIME_COUNT] = {
5,
10,
20,
50,
100,
};
#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] = {
@@ -276,13 +315,31 @@ static void subghz_scene_receiver_config_set_hopping(VariableItem* item) {
subghz->txrx, index != 0 ? SubGhzHopperStateRunning : SubGhzHopperStateOFF); subghz->txrx, index != 0 ? SubGhzHopperStateRunning : SubGhzHopperStateOFF);
} }
static void subghz_scene_receiver_config_set_mod_hopping(VariableItem* item) { static void subghz_scene_receiver_config_set_mod_hop_dbm(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]); variable_item_set_current_value_text(item, mod_hop_dbm_text[index]);
bool enabled = index == 1; subghz->last_settings->mod_hopping_threshold = mod_hop_dbm_value[index];
subghz->last_settings->enable_mod_hopping = enabled; bool enabled = index != 0;
subghz_txrx_mod_hopper_set_running(subghz->txrx, enabled); subghz_txrx_mod_hopper_set_running(
subghz->txrx,
enabled,
(uint8_t)subghz->last_settings->mod_hopping_dwell,
mod_hop_dbm_value[index]);
}
static void subghz_scene_receiver_config_set_mod_hop_time(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, mod_hop_time_text[index]);
subghz->last_settings->mod_hopping_dwell = mod_hop_time_ticks[index];
if(subghz_txrx_mod_hopper_get_running(subghz->txrx)) {
subghz_txrx_mod_hopper_set_running(
subghz->txrx,
true,
(uint8_t)mod_hop_time_ticks[index],
subghz->last_settings->mod_hopping_threshold);
}
} }
static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
@@ -385,8 +442,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->mod_hopping_threshold = NAN;
subghz_txrx_mod_hopper_set_running(subghz->txrx, false); subghz->last_settings->mod_hopping_dwell = 20;
subghz_txrx_mod_hopper_set_running(subghz->txrx, false, 20, NAN);
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 +509,47 @@ 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 // Mod Hop dBm
item = variable_item_list_add( {
subghz->variable_item_list, uint8_t mod_dbm_idx = 0; // OFF
"Mod Hopping", float thresh = subghz->last_settings->mod_hopping_threshold;
COMBO_BOX_COUNT, if(!isnan(thresh)) {
subghz_scene_receiver_config_set_mod_hopping, for(uint8_t k = 1; k < MOD_HOP_DBM_COUNT; k++) {
subghz); if(mod_hop_dbm_value[k] == thresh) {
value_index = subghz->last_settings->enable_mod_hopping ? 1 : 0; mod_dbm_idx = k;
variable_item_set_current_value_index(item, value_index); break;
variable_item_set_current_value_text(item, combobox_text[value_index]); }
}
if(mod_dbm_idx == 0) mod_dbm_idx = 1; // fallback to -90
}
item = variable_item_list_add(
subghz->variable_item_list,
"Mod Hop dBm",
MOD_HOP_DBM_COUNT,
subghz_scene_receiver_config_set_mod_hop_dbm,
subghz);
variable_item_set_current_value_index(item, mod_dbm_idx);
variable_item_set_current_value_text(item, mod_hop_dbm_text[mod_dbm_idx]);
}
// Mod Hop Time
{
uint8_t mod_time_idx = 2; // default 2s
for(uint8_t k = 0; k < MOD_HOP_TIME_COUNT; k++) {
if(mod_hop_time_ticks[k] == subghz->last_settings->mod_hopping_dwell) {
mod_time_idx = k;
break;
}
}
item = variable_item_list_add(
subghz->variable_item_list,
"Mod Hop Time",
MOD_HOP_TIME_COUNT,
subghz_scene_receiver_config_set_mod_hop_time,
subghz);
variable_item_set_current_value_index(item, mod_time_idx);
variable_item_set_current_value_text(item, mod_hop_time_text[mod_time_idx]);
}
} }
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=

View File

@@ -1,5 +1,6 @@
#include "subghz_last_settings.h" #include "subghz_last_settings.h"
#include "subghz_i.h" #include "subghz_i.h"
#include <math.h>
#define TAG "SubGhzLastSettings" #define TAG "SubGhzLastSettings"
@@ -13,7 +14,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_MOD_HOP_THRESHOLD "ModHopThreshold"
#define SUBGHZ_LAST_SETTING_FIELD_MOD_HOP_DWELL "ModHopDwell"
#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 +48,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->mod_hopping_threshold = NAN; // disabled by default
instance->mod_hopping_dwell = 20; // 2 seconds (20 × 100ms ticks)
instance->leds_and_amp = true; instance->leds_and_amp = true;
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
@@ -99,10 +103,17 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
1)) { 1)) {
flipper_format_rewind(fff_data_file); flipper_format_rewind(fff_data_file);
} }
if(!flipper_format_read_bool( if(!flipper_format_read_float(
fff_data_file, fff_data_file,
SUBGHZ_LAST_SETTING_FIELD_MOD_HOPPING, SUBGHZ_LAST_SETTING_FIELD_MOD_HOP_THRESHOLD,
&instance->enable_mod_hopping, &instance->mod_hopping_threshold,
1)) {
flipper_format_rewind(fff_data_file);
}
if(!flipper_format_read_uint32(
fff_data_file,
SUBGHZ_LAST_SETTING_FIELD_MOD_HOP_DWELL,
&instance->mod_hopping_dwell,
1)) { 1)) {
flipper_format_rewind(fff_data_file); flipper_format_rewind(fff_data_file);
} }
@@ -221,10 +232,17 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
file, SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE, &instance->enable_hopping, 1)) { file, SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE, &instance->enable_hopping, 1)) {
break; break;
} }
if(!flipper_format_write_bool( if(!flipper_format_write_float(
file, file,
SUBGHZ_LAST_SETTING_FIELD_MOD_HOPPING, SUBGHZ_LAST_SETTING_FIELD_MOD_HOP_THRESHOLD,
&instance->enable_mod_hopping, &instance->mod_hopping_threshold,
1)) {
break;
}
if(!flipper_format_write_uint32(
file,
SUBGHZ_LAST_SETTING_FIELD_MOD_HOP_DWELL,
&instance->mod_hopping_dwell,
1)) { 1)) {
break; break;
} }

View File

@@ -20,7 +20,8 @@ typedef struct {
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; float mod_hopping_threshold; // RSSI threshold for mod hopping, NAN = disabled
uint32_t mod_hopping_dwell; // Dwell time in ticks (100ms units)
uint32_t ignore_filter; uint32_t ignore_filter;
uint32_t filter; uint32_t filter;
float rssi; float rssi;

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";
} }