Merge remote-tracking branch 'ul/dev' into mntm-dev

This commit is contained in:
Willy-JL
2025-04-23 03:48:13 +01:00
34 changed files with 778 additions and 62 deletions

View File

@@ -26,6 +26,8 @@
- USB: Portal Of Flipper (by @Bettse & @sanjay900)
- Sub-GHz:
- UL: Add ReversRB2/RB2M Protocol full support with add manually (by @xMasterX)
- UL: Add Feron static 32-bit protocol full support (by @xMasterX)
- UL: Various bugfixes and experimental options, rolling counter overflow (by @xMasterX)
- OFW: Added support for 42-bit Prastel variation (by @pmazzini)
- RFID:
- OFW: EM4305 support (by @Astrrra)
@@ -102,6 +104,7 @@
- Split NfcProtocolSupport handlers into plugins for ~23kb less RAM usage (#396 by @Willy-JL)
- Enable Asset Packs in NFC app again due to reduced RAM usage (#396 by @Willy-JL)
- Improve loading of parser plugins (by @Willy-JL)
- UL: Use default UL/UL-C pwd/key as default value for key input (by @mishamyte)
- OFW: Added naming for DESFire cards + fix MF3ICD40 cards unable to be read (by @Demae)
- OFW: FeliCa Protocol Expose Read Block API and Allow Specifying Service (by @zinongli)
- OFW: Enable MFUL sync poller to be provided with passwords (by @GMMan)

View File

@@ -453,7 +453,7 @@ static int32_t bad_usb_worker(void* context) {
if(bad_usb->hid->is_connected(bad_usb->hid_inst)) {
worker_state = BadUsbStateIdle; // Ready to run
} else {
worker_state = BadUsbStateNotConnected; // USB not connected
worker_state = BadUsbStateNotConnected; // Not connected
}
} else {
worker_state = BadUsbStateScriptError; // Script preload error

View File

@@ -18,9 +18,11 @@ void mf_ultralight_auth_free(MfUltralightAuth* instance) {
void mf_ultralight_auth_reset(MfUltralightAuth* instance) {
furi_assert(instance);
uint32_t default_password = MF_ULTRALIGHT_DEFAULT_PASSWORD;
instance->type = MfUltralightAuthTypeNone;
memset(&instance->password, 0, sizeof(MfUltralightAuthPassword));
memset(&instance->tdes_key, 0, sizeof(MfUltralightC3DesAuthKey));
memcpy(&instance->password, &default_password, sizeof(MfUltralightAuthPassword));
memcpy(&instance->tdes_key, MF_ULTRALIGHT_C_DEFAULT_KEY, sizeof(MfUltralightC3DesAuthKey));
memset(&instance->pack, 0, sizeof(MfUltralightAuthPack));
}

View File

@@ -3,16 +3,37 @@
#include <cli/cli_main_commands.h>
#include <lib/toolbox/args.h>
#include <lib/toolbox/hex.h>
#include <lib/toolbox/bit_buffer.h>
#include <lib/nfc/nfc_poller.h>
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
#include <lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
#include <lib/nfc/protocols/iso15693_3/iso15693_3_poller.h>
#include <toolbox/pipe.h>
#include <furi_hal_nfc.h>
#define FLAG_EVENT (1 << 10)
#define NFC_MAX_BUFFER_SIZE (256)
#define NFC_BASE_PROTOCOL_MAX (3)
#define POLLER_DONE (1 << 0)
#define POLLER_ERR (1 << 1)
static NfcProtocol BASE_PROTOCOL[NFC_BASE_PROTOCOL_MAX] = {
NfcProtocolIso14443_4a,
NfcProtocolIso14443_4b,
NfcProtocolIso15693_3};
typedef struct ApduContext {
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
bool ready;
FuriThreadId thread_id;
} ApduContext;
static void nfc_cli_print_usage(void) {
printf("Usage:\r\n");
printf("nfc <cmd>\r\n");
printf("Cmd list:\r\n");
printf("\tapdu\t - Send APDU and print response \r\n");
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
printf("\tfield\t - turn field on\r\n");
}
@@ -41,6 +62,144 @@ static void nfc_cli_field(PipeSide* pipe, FuriString* args) {
furi_hal_nfc_release();
}
static NfcCommand trx_callback(NfcGenericEvent event, void* context) {
furi_check(context);
ApduContext* apdu_context = (ApduContext*)context;
if(apdu_context->ready) {
apdu_context->ready = false;
if(NfcProtocolIso14443_4a == event.protocol) {
Iso14443_4aError err = iso14443_4a_poller_send_block(
event.instance, apdu_context->tx_buffer, apdu_context->rx_buffer);
if(Iso14443_4aErrorNone == err) {
furi_thread_flags_set(apdu_context->thread_id, POLLER_DONE);
return NfcCommandContinue;
} else {
furi_thread_flags_set(apdu_context->thread_id, POLLER_ERR);
return NfcCommandStop;
}
} else if(NfcProtocolIso14443_4b == event.protocol) {
Iso14443_4bError err = iso14443_4b_poller_send_block(
event.instance, apdu_context->tx_buffer, apdu_context->rx_buffer);
if(Iso14443_4bErrorNone == err) {
furi_thread_flags_set(apdu_context->thread_id, POLLER_DONE);
return NfcCommandContinue;
} else {
furi_thread_flags_set(apdu_context->thread_id, POLLER_ERR);
return NfcCommandStop;
}
} else if(NfcProtocolIso15693_3 == event.protocol) {
Iso15693_3Error err = iso15693_3_poller_send_frame(
event.instance,
apdu_context->tx_buffer,
apdu_context->rx_buffer,
ISO15693_3_FDT_POLL_FC);
if(Iso15693_3ErrorNone == err) {
furi_thread_flags_set(apdu_context->thread_id, POLLER_DONE);
return NfcCommandContinue;
} else {
furi_thread_flags_set(apdu_context->thread_id, POLLER_ERR);
return NfcCommandStop;
}
} else {
// should never reach here
furi_crash("Unknown protocol");
}
} else {
furi_delay_ms(100);
}
return NfcCommandContinue;
}
static void nfc_cli_apdu(PipeSide* pipe, FuriString* args) {
UNUSED(pipe);
Nfc* nfc = NULL;
NfcPoller* poller = NULL;
FuriString* data = furi_string_alloc();
uint8_t* req_buffer = NULL;
uint8_t* resp_buffer = NULL;
size_t apdu_size = 0;
size_t resp_size = 0;
NfcProtocol current_protocol = NfcProtocolInvalid;
do {
if(0 == args_get_first_word_length(args)) {
printf(
"Use like `nfc apdu 00a404000e325041592e5359532e444446303100 00a4040008a0000003010102` \r\n");
break;
}
nfc = nfc_alloc();
printf("detecting tag\r\n");
for(int i = 0; i < NFC_BASE_PROTOCOL_MAX; i++) {
poller = nfc_poller_alloc(nfc, BASE_PROTOCOL[i]);
bool is_detected = nfc_poller_detect(poller);
nfc_poller_free(poller);
if(is_detected) {
current_protocol = BASE_PROTOCOL[i];
printf("detected tag:%d\r\n", BASE_PROTOCOL[i]);
break;
}
}
if(NfcProtocolInvalid == current_protocol) {
nfc_free(nfc);
printf("Can not find any tag\r\n");
break;
}
poller = nfc_poller_alloc(nfc, current_protocol);
ApduContext* apdu_context = malloc(sizeof(ApduContext));
apdu_context->tx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
apdu_context->rx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
apdu_context->ready = false;
apdu_context->thread_id = furi_thread_get_current_id();
nfc_poller_start(poller, trx_callback, apdu_context);
while(args_read_string_and_trim(args, data)) {
bit_buffer_reset(apdu_context->tx_buffer);
bit_buffer_reset(apdu_context->rx_buffer);
apdu_size = furi_string_size(data) / 2;
req_buffer = malloc(apdu_size);
hex_chars_to_uint8(furi_string_get_cstr(data), req_buffer);
printf("Sending APDU:%s to Tag\r\n", furi_string_get_cstr(data));
bit_buffer_copy_bytes(apdu_context->tx_buffer, req_buffer, apdu_size);
apdu_context->ready = true;
uint32_t flags = furi_thread_flags_wait(POLLER_DONE, FuriFlagWaitAny, 3000);
if(0 == (flags & POLLER_DONE)) {
printf("Error or Timeout");
free(req_buffer);
break;
}
furi_assert(apdu_context->ready == false);
resp_size = bit_buffer_get_size_bytes(apdu_context->rx_buffer) * 2;
if(!resp_size) {
printf("No response\r\n");
free(req_buffer);
continue;
}
resp_buffer = malloc(resp_size);
uint8_to_hex_chars(
bit_buffer_get_data(apdu_context->rx_buffer), resp_buffer, resp_size);
resp_buffer[resp_size] = 0;
printf("Response: %s\r\n", resp_buffer);
free(req_buffer);
free(resp_buffer);
}
nfc_poller_stop(poller);
nfc_poller_free(poller);
nfc_free(nfc);
bit_buffer_free(apdu_context->tx_buffer);
bit_buffer_free(apdu_context->rx_buffer);
free(apdu_context);
} while(false);
furi_string_free(data);
}
static void execute(PipeSide* pipe, FuriString* args, void* context) {
UNUSED(context);
FuriString* cmd;
@@ -51,6 +210,10 @@ static void execute(PipeSide* pipe, FuriString* args, void* context) {
nfc_cli_print_usage();
break;
}
if(furi_string_cmp_str(cmd, "apdu") == 0) {
nfc_cli_apdu(pipe, args);
break;
}
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
if(furi_string_cmp_str(cmd, "field") == 0) {
nfc_cli_field(pipe, args);

View File

@@ -36,7 +36,7 @@ const char* const gps_text[GPS_COUNT] = {
"115200",
};
#define DEBUG_COUNTER_COUNT 13
#define DEBUG_COUNTER_COUNT 16
const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = {
"+1",
"+2",
@@ -44,21 +44,26 @@ const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = {
"+4",
"+5",
"+10",
"0",
"+50",
"OVFL",
"No",
"-1",
"-2",
"-3",
"-4",
"-5",
"-10",
"-50",
};
const uint32_t debug_counter_val[DEBUG_COUNTER_COUNT] = {
const int32_t debug_counter_val[DEBUG_COUNTER_COUNT] = {
1,
2,
3,
4,
5,
10,
50,
65535,
0,
-1,
-2,
@@ -66,6 +71,7 @@ const uint32_t debug_counter_val[DEBUG_COUNTER_COUNT] = {
-4,
-5,
-10,
-50,
};
static void subghz_scene_radio_settings_set_device(VariableItem* item) {
@@ -149,7 +155,7 @@ void subghz_scene_radio_settings_on_enter(void* context) {
SubGhz* subghz = context;
VariableItemList* variable_item_list = subghz->variable_item_list;
uint8_t value_index;
int32_t value_index;
VariableItem* item;
uint8_t value_count_device = RADIO_DEVICE_COUNT;
@@ -196,7 +202,7 @@ void subghz_scene_radio_settings_on_enter(void* context) {
furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? DEBUG_COUNTER_COUNT : 3,
subghz_scene_receiver_config_set_debug_counter,
subghz);
value_index = value_index_uint32(
value_index = value_index_int32(
furi_hal_subghz_get_rolling_counter_mult(),
debug_counter_val,
furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? DEBUG_COUNTER_COUNT : 3);

View File

@@ -96,7 +96,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
subghz_txrx_stop(subghz->txrx);
if(subghz_custom_btn_get() != SUBGHZ_CUSTOM_BTN_OK) {
subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_OK);
int8_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
int32_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
furi_hal_subghz_set_rolling_counter_mult(0);
// Calling restore!
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));

View File

@@ -816,7 +816,6 @@ void subghz_cli_command_tx_from_file(PipeSide* pipe, FuriString* args, void* con
subghz_devices_deinit();
// Reset custom settings
subghz_environment_reset_keeloq(environment);
faac_slh_reset_prog_mode();
subghz_custom_btns_reset();
// Free environment
subghz_environment_free(environment);
@@ -1117,7 +1116,8 @@ static void subghz_cli_command_chat(PipeSide* pipe, FuriString* args) {
}
static void execute(PipeSide* pipe, FuriString* args, void* context) {
FuriString* cmd = furi_string_alloc();
FuriString* cmd;
cmd = furi_string_alloc();
do {
if(!args_read_string_and_trim(args, cmd)) {

View File

@@ -501,7 +501,10 @@ void submenu_process_ok(Submenu* submenu, InputType input_type) {
},
true);
if(!item || item->locked) return;
if(!item) return;
if(item->locked) {
return;
}
if(!item->has_extended_events && input_type == InputTypeShort && item->callback) {
item->callback(item->callback_context, item->index);

View File

@@ -54,22 +54,6 @@ void submenu_add_item(
SubmenuItemCallback callback,
void* callback_context);
/** Add item to submenu with extended press events
*
* @param submenu Submenu instance
* @param label menu item label
* @param index menu item index, used for callback, may be
* the same with other items
* @param callback menu item extended callback
* @param callback_context menu item callback context
*/
void submenu_add_item_ex(
Submenu* submenu,
const char* label,
uint32_t index,
SubmenuItemCallbackEx callback,
void* callback_context);
/** Add lockable item to submenu
*
* @param submenu Submenu instance
@@ -90,6 +74,22 @@ void submenu_add_lockable_item(
bool locked,
const char* locked_message);
/** Add item to submenu with extended press events
*
* @param submenu Submenu instance
* @param label menu item label
* @param index menu item index, used for callback, may be
* the same with other items
* @param callback menu item extended callback
* @param callback_context menu item callback context
*/
void submenu_add_item_ex(
Submenu* submenu,
const char* label,
uint32_t index,
SubmenuItemCallbackEx callback,
void* callback_context);
/** Change label of an existing item
*
* @param submenu Submenu instance

View File

@@ -7,10 +7,10 @@
#include <stdio.h>
#include <furi.h>
#include <furi_hal_gpio.h>
#include <furi_hal_vibro.h>
#include <toolbox/cli/cli_command.h>
#include <cli/cli_main_commands.h>
#include <toolbox/pipe.h>
#include <furi_hal_vibro.h>
#define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2)
#define INPUT_PRESS_TICKS 150
@@ -155,8 +155,10 @@ int32_t input_srv(void* p) {
// Send Press/Release event
event.type = pin_states[i].state ? InputTypePress : InputTypeRelease;
furi_pubsub_publish(event_pubsub, &event);
// do vibro if user setup vibro touch level in Settings-Input.
// vibro signal if user setup vibro touch level in Settings-Input.
if(settings->vibro_touch_level) {
//delay 1 ticks for compatibility with rgb_backlight_mod
furi_delay_tick(1);
furi_hal_vibro_on(true);
furi_delay_tick(settings->vibro_touch_level);
furi_hal_vibro_on(false);

View File

@@ -29,7 +29,6 @@ void input_settings_load(InputSettings* settings) {
sizeof(InputSettings),
INPUT_SETTINGS_MAGIC,
INPUT_SETTINGS_VER);
// if config previous version - load it and inicialize new settings
}
// in case of another config version we exit from useless cycle to next step
} while(false);

View File

@@ -388,14 +388,19 @@ static void power_handle_reboot(PowerBootMode mode) {
//start furi timer for autopoweroff
static void power_start_auto_poweroff_timer(Power* power) {
if(furi_timer_is_running(power->auto_poweroff_timer)) {
furi_timer_stop(power->auto_poweroff_timer);
}
furi_timer_start(
power->auto_poweroff_timer, furi_ms_to_ticks(power->settings.auto_poweroff_delay_ms));
}
//stop furi timer for autopoweroff
static void power_stop_auto_poweroff_timer(Power* power) {
if(furi_timer_is_running(power->auto_poweroff_timer)) {
furi_timer_stop(power->auto_poweroff_timer);
}
}
static uint32_t power_is_running_auto_poweroff_timer(Power* power) {
return furi_timer_is_running(power->auto_poweroff_timer);

View File

@@ -6,6 +6,7 @@ App(
"passport",
"system_settings",
"clock_settings",
"input_settings",
"about",
],
)

View File

@@ -38,6 +38,7 @@ extern "C" {
#define MF_ULTRALIGHT_TEARING_FLAG_NUM (3)
#define MF_ULTRALIGHT_AUTH_PASSWORD_SIZE (4)
#define MF_ULTRALIGHT_AUTH_PACK_SIZE (2)
#define MF_ULTRALIGHT_DEFAULT_PASSWORD (0xffffffffUL)
#define MF_ULTRALIGHT_C_AUTH_RESPONSE_SIZE (9)
#define MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE (16)
@@ -47,6 +48,11 @@ extern "C" {
#define MF_ULTRALIGHT_C_AUTH_RND_A_BLOCK_OFFSET (0)
#define MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET (8)
#define MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE (MF_ULTRALIGHT_C_AUTH_DATA_SIZE + 1)
#define MF_ULTRALIGHT_C_DEFAULT_KEY \
(uint8_t[]) { \
0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, \
0x46 \
}
typedef enum {
MfUltralightErrorNone,

View File

@@ -11,8 +11,6 @@ extern "C" {
#define MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC (60000)
#define MF_ULTRALIGHT_MAX_BUFF_SIZE (64)
#define MF_ULTRALIGHT_DEFAULT_PASSWORD (0xffffffffUL)
#define MF_ULTRALIGHT_IS_NTAG_I2C(type) \
(((type) == MfUltralightTypeNTAGI2C1K) || ((type) == MfUltralightTypeNTAGI2C2K) || \
((type) == MfUltralightTypeNTAGI2CPlus1K) || ((type) == MfUltralightTypeNTAGI2CPlus2K))

View File

@@ -281,7 +281,7 @@ static bool subghz_protocol_alutech_at_4n_gen_data(
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
} else if(instance->generic.cnt >= 0xFFFF) {
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
instance->generic.cnt = 0;
}
crc = subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)(instance->generic.cnt & 0xFF));

View File

@@ -193,7 +193,7 @@ static void subghz_protocol_encoder_came_atomo_get_upload(
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
} else if(instance->generic.cnt >= 0xFFFF) {
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
instance->generic.cnt = 0;
}

View File

@@ -142,10 +142,41 @@ static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* inst
data_prg[0] = 0x00;
if(allow_zero_seed || (instance->generic.seed != 0x0)) {
if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFF)) {
if(instance->generic.cnt < 0xFFFFF) {
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >
0xFFFFF) {
instance->generic.cnt = 0;
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
} else if(
(instance->generic.cnt >= 0xFFFFF) &&
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
instance->generic.cnt = 0;
}
} else {
instance->generic.cnt += 1;
}
if(temp_counter_backup != 0x0) {
if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFF)) {
if(temp_counter_backup < 0xFFFFF) {
if((temp_counter_backup + furi_hal_subghz_get_rolling_counter_mult()) >
0xFFFFF) {
temp_counter_backup = 0;
} else {
temp_counter_backup += furi_hal_subghz_get_rolling_counter_mult();
}
} else if(
(temp_counter_backup >= 0xFFFFF) &&
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
temp_counter_backup = 0;
}
} else {
temp_counter_backup += 1;
}
}
}
data_prg[1] = instance->generic.cnt & 0xFF;
@@ -195,8 +226,10 @@ static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* inst
(temp_fix_backup != 0x0) && !faac_prog_mode) {
instance->generic.serial = temp_fix_backup >> 4;
instance->generic.btn = temp_fix_backup & 0xF;
if(temp_counter_backup != 0x0) {
instance->generic.cnt = temp_counter_backup;
}
}
uint32_t fix = instance->generic.serial << 4 | instance->generic.btn;
uint32_t hop = 0;
uint32_t decrypt = 0;
@@ -209,8 +242,33 @@ static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* inst
}
if(allow_zero_seed || (instance->generic.seed != 0x0)) {
if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFF)) {
if(instance->generic.cnt < 0xFFFFF) {
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >
0xFFFFF) {
instance->generic.cnt = 0;
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
} else if(
(instance->generic.cnt >= 0xFFFFF) &&
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
instance->generic.cnt = 0;
}
} else {
if(instance->generic.cnt < 0xFFFFF) {
if((instance->generic.cnt + 0xFFFFF) > 0xFFFFF) {
instance->generic.cnt = 0;
} else {
instance->generic.cnt += 0xFFFFF;
}
} else if(
(instance->generic.cnt >= 0xFFFFF) &&
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
instance->generic.cnt = 0;
}
}
}
if((instance->generic.cnt % 2) == 0) {
decrypt = fixx[6] << 28 | fixx[7] << 24 | fixx[5] << 20 |
@@ -250,7 +308,7 @@ bool subghz_protocol_faac_slh_create_data(
const char* manufacture_name,
SubGhzRadioPreset* preset) {
furi_assert(context);
// roguemaster don't steal!!!
// OwO
SubGhzProtocolEncoderFaacSLH* instance = context;
instance->generic.serial = serial;
instance->generic.btn = btn;

View File

@@ -0,0 +1,352 @@
#include "feron.h"
#include "../blocks/const.h"
#include "../blocks/decoder.h"
#include "../blocks/encoder.h"
#include "../blocks/generic.h"
#include "../blocks/math.h"
#define TAG "SubGhzProtocolFeron"
static const SubGhzBlockConst subghz_protocol_feron_const = {
.te_short = 350,
.te_long = 750,
.te_delta = 150,
.min_count_bit_for_found = 32,
};
struct SubGhzProtocolDecoderFeron {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
};
struct SubGhzProtocolEncoderFeron {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
};
typedef enum {
FeronDecoderStepReset = 0,
FeronDecoderStepSaveDuration,
FeronDecoderStepCheckDuration,
} FeronDecoderStep;
const SubGhzProtocolDecoder subghz_protocol_feron_decoder = {
.alloc = subghz_protocol_decoder_feron_alloc,
.free = subghz_protocol_decoder_feron_free,
.feed = subghz_protocol_decoder_feron_feed,
.reset = subghz_protocol_decoder_feron_reset,
.get_hash_data = NULL,
.get_hash_data_long = subghz_protocol_decoder_feron_get_hash_data,
.serialize = subghz_protocol_decoder_feron_serialize,
.deserialize = subghz_protocol_decoder_feron_deserialize,
.get_string = subghz_protocol_decoder_feron_get_string,
.get_string_brief = NULL,
};
const SubGhzProtocolEncoder subghz_protocol_feron_encoder = {
.alloc = subghz_protocol_encoder_feron_alloc,
.free = subghz_protocol_encoder_feron_free,
.deserialize = subghz_protocol_encoder_feron_deserialize,
.stop = subghz_protocol_encoder_feron_stop,
.yield = subghz_protocol_encoder_feron_yield,
};
const SubGhzProtocol subghz_protocol_feron = {
.name = SUBGHZ_PROTOCOL_FERON_NAME,
.type = SubGhzProtocolTypeStatic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_feron_decoder,
.encoder = &subghz_protocol_feron_encoder,
};
void* subghz_protocol_encoder_feron_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderFeron* instance = malloc(sizeof(SubGhzProtocolEncoderFeron));
instance->base.protocol = &subghz_protocol_feron;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 256;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
return instance;
}
void subghz_protocol_encoder_feron_free(void* context) {
furi_assert(context);
SubGhzProtocolEncoderFeron* instance = context;
free(instance->encoder.upload);
free(instance);
}
/**
* Generating an upload from data.
* @param instance Pointer to a SubGhzProtocolEncoderFeron instance
*/
static void subghz_protocol_encoder_feron_get_upload(SubGhzProtocolEncoderFeron* instance) {
furi_assert(instance);
size_t index = 0;
// Send key and GAP
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
if(bit_read(instance->generic.data, i - 1)) {
// Send bit 1
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_feron_const.te_long);
if(i == 1) {
//Send 500/500 and gap if bit was last
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_feron_const.te_short + 150);
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_feron_const.te_short + 150);
// Gap
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_feron_const.te_long * 6);
} else {
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_feron_const.te_short);
}
} else {
// Send bit 0
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_feron_const.te_short);
if(i == 1) {
//Send 500/500 and gap if bit was last
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_feron_const.te_short + 150);
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_feron_const.te_short + 150);
// Gap
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_feron_const.te_long * 6);
} else {
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_feron_const.te_long);
}
}
}
instance->encoder.size_upload = index;
return;
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_feron_check_remote_controller(SubGhzBlockGeneric* instance) {
instance->serial = instance->data >> 16;
}
SubGhzProtocolStatus
subghz_protocol_encoder_feron_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolEncoderFeron* instance = context;
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
do {
ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
subghz_protocol_feron_const.min_count_bit_for_found);
if(ret != SubGhzProtocolStatusOk) {
break;
}
//optional parameter parameter
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_feron_check_remote_controller(&instance->generic);
subghz_protocol_encoder_feron_get_upload(instance);
instance->encoder.is_running = true;
} while(false);
return ret;
}
void subghz_protocol_encoder_feron_stop(void* context) {
SubGhzProtocolEncoderFeron* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_feron_yield(void* context) {
SubGhzProtocolEncoderFeron* instance = context;
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
instance->encoder.is_running = false;
return level_duration_reset();
}
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
instance->encoder.front = 0;
}
return ret;
}
void* subghz_protocol_decoder_feron_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderFeron* instance = malloc(sizeof(SubGhzProtocolDecoderFeron));
instance->base.protocol = &subghz_protocol_feron;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_feron_free(void* context) {
furi_assert(context);
SubGhzProtocolDecoderFeron* instance = context;
free(instance);
}
void subghz_protocol_decoder_feron_reset(void* context) {
furi_assert(context);
SubGhzProtocolDecoderFeron* instance = context;
instance->decoder.parser_step = FeronDecoderStepReset;
}
void subghz_protocol_decoder_feron_feed(void* context, bool level, volatile uint32_t duration) {
furi_assert(context);
SubGhzProtocolDecoderFeron* instance = context;
// Feron Decoder
// 2025.04 - @xMasterX (MMX)
// Key samples
/*
0110001100111000 1000010101111010 - ON
0110001100111000 1000010001111011 - OFF
0110001100111000 1000011001111001 - brightness up
0110001100111000 1000011101111000 - brightness down
0110001100111000 1000001001111101 - scroll mode command
------------------------------------------
0110001100111000 0111000010001111 - R
0110001100111000 0001101011100101 - B
0110001100111000 0100000010111111 - G
*/
switch(instance->decoder.parser_step) {
case FeronDecoderStepReset:
if((!level) && (DURATION_DIFF(duration, subghz_protocol_feron_const.te_long * 6) <
subghz_protocol_feron_const.te_delta * 4)) {
//Found GAP
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->decoder.parser_step = FeronDecoderStepSaveDuration;
}
break;
case FeronDecoderStepSaveDuration:
if(level) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = FeronDecoderStepCheckDuration;
} else {
instance->decoder.parser_step = FeronDecoderStepReset;
}
break;
case FeronDecoderStepCheckDuration:
if(!level) {
// Bit 0 is short and long timing = 350us HIGH (te_last) and 750us LOW
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_feron_const.te_short) <
subghz_protocol_feron_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_feron_const.te_long) <
subghz_protocol_feron_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = FeronDecoderStepSaveDuration;
// Bit 1 is long and short timing = 750us HIGH (te_last) and 350us LOW
} else if(
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_feron_const.te_long) <
subghz_protocol_feron_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_feron_const.te_short) <
subghz_protocol_feron_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = FeronDecoderStepSaveDuration;
} else if(
// End of the key 500Low(we are here)/500High us
DURATION_DIFF(
duration, (uint16_t)(subghz_protocol_feron_const.te_short + (uint16_t)150)) <
subghz_protocol_feron_const.te_delta) {
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_feron_const.te_short) <
subghz_protocol_feron_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
}
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_feron_const.te_long) <
subghz_protocol_feron_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
}
// If got 32 bits key reading is finished
if(instance->decoder.decode_count_bit ==
subghz_protocol_feron_const.min_count_bit_for_found) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->decoder.parser_step = FeronDecoderStepReset;
} else {
instance->decoder.parser_step = FeronDecoderStepReset;
}
} else {
instance->decoder.parser_step = FeronDecoderStepReset;
}
break;
}
}
uint32_t subghz_protocol_decoder_feron_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderFeron* instance = context;
return subghz_protocol_blocks_get_hash_data_long(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus subghz_protocol_decoder_feron_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
SubGhzProtocolDecoderFeron* instance = context;
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
}
SubGhzProtocolStatus
subghz_protocol_decoder_feron_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolDecoderFeron* instance = context;
return subghz_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, subghz_protocol_feron_const.min_count_bit_for_found);
}
void subghz_protocol_decoder_feron_get_string(void* context, FuriString* output) {
furi_assert(context);
SubGhzProtocolDecoderFeron* instance = context;
subghz_protocol_feron_check_remote_controller(&instance->generic);
furi_string_cat_printf(
output,
"%s %db\r\n"
"Key: 0x%08lX\r\n"
"Serial: 0x%04lX\r\n"
"Command: 0x%04lX\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
instance->generic.serial,
(uint32_t)(instance->generic.data & 0xFFFF));
}

View File

@@ -0,0 +1,109 @@
#pragma once
#include "base.h"
#define SUBGHZ_PROTOCOL_FERON_NAME "Feron"
typedef struct SubGhzProtocolDecoderFeron SubGhzProtocolDecoderFeron;
typedef struct SubGhzProtocolEncoderFeron SubGhzProtocolEncoderFeron;
extern const SubGhzProtocolDecoder subghz_protocol_feron_decoder;
extern const SubGhzProtocolEncoder subghz_protocol_feron_encoder;
extern const SubGhzProtocol subghz_protocol_feron;
/**
* Allocate SubGhzProtocolEncoderFeron.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolEncoderFeron* pointer to a SubGhzProtocolEncoderFeron instance
*/
void* subghz_protocol_encoder_feron_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolEncoderFeron.
* @param context Pointer to a SubGhzProtocolEncoderFeron instance
*/
void subghz_protocol_encoder_feron_free(void* context);
/**
* Deserialize and generating an upload to send.
* @param context Pointer to a SubGhzProtocolEncoderFeron instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return status
*/
SubGhzProtocolStatus
subghz_protocol_encoder_feron_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Forced transmission stop.
* @param context Pointer to a SubGhzProtocolEncoderFeron instance
*/
void subghz_protocol_encoder_feron_stop(void* context);
/**
* Getting the level and duration of the upload to be loaded into DMA.
* @param context Pointer to a SubGhzProtocolEncoderFeron instance
* @return LevelDuration
*/
LevelDuration subghz_protocol_encoder_feron_yield(void* context);
/**
* Allocate SubGhzProtocolDecoderFeron.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolDecoderFeron* pointer to a SubGhzProtocolDecoderFeron instance
*/
void* subghz_protocol_decoder_feron_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolDecoderFeron.
* @param context Pointer to a SubGhzProtocolDecoderFeron instance
*/
void subghz_protocol_decoder_feron_free(void* context);
/**
* Reset decoder SubGhzProtocolDecoderFeron.
* @param context Pointer to a SubGhzProtocolDecoderFeron instance
*/
void subghz_protocol_decoder_feron_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a SubGhzProtocolDecoderFeron instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void subghz_protocol_decoder_feron_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a SubGhzProtocolDecoderFeron instance
* @return hash Hash sum
*/
uint32_t subghz_protocol_decoder_feron_get_hash_data(void* context);
/**
* Serialize data SubGhzProtocolDecoderFeron.
* @param context Pointer to a SubGhzProtocolDecoderFeron instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return status
*/
SubGhzProtocolStatus subghz_protocol_decoder_feron_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data SubGhzProtocolDecoderFeron.
* @param context Pointer to a SubGhzProtocolDecoderFeron instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return status
*/
SubGhzProtocolStatus
subghz_protocol_decoder_feron_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a SubGhzProtocolDecoderFeron instance
* @param output Resulting text
*/
void subghz_protocol_decoder_feron_get_string(void* context, FuriString* output);

View File

@@ -153,6 +153,9 @@ static void subghz_protocol_encoder_hay21_get_upload(SubGhzProtocolEncoderHay21*
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
if(furi_hal_subghz_get_rolling_counter_mult() >= 0xF) {
instance->generic.cnt = 0xF;
}
} else if(instance->generic.cnt >= 0xF) {
instance->generic.cnt = 0;
}

View File

@@ -192,7 +192,9 @@ static bool subghz_protocol_keeloq_gen_data(
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
} else if(instance->generic.cnt >= 0xFFFF) {
} else if(
(instance->generic.cnt >= 0xFFFF) &&
(furi_hal_subghz_get_rolling_counter_mult() != 0)) {
instance->generic.cnt = 0;
}
}

View File

@@ -163,7 +163,7 @@ static bool subghz_protocol_kinggates_stylo_4k_gen_data(
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
} else if(instance->generic.cnt >= 0xFFFF) {
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
instance->generic.cnt = 0;
}

View File

@@ -161,7 +161,7 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload(
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
} else if(instance->generic.cnt >= 0xFFFF) {
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
instance->generic.cnt = 0;
}
uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt;

View File

@@ -81,6 +81,7 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = {
&subghz_protocol_hollarm,
&subghz_protocol_hay21,
&subghz_protocol_revers_rb2,
&subghz_protocol_feron,
};
const SubGhzProtocolRegistry subghz_protocol_registry = {

View File

@@ -82,3 +82,4 @@
#include "hollarm.h"
#include "hay21.h"
#include "revers_rb2.h"
#include "feron.h"

View File

@@ -138,7 +138,7 @@ static bool
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
} else if(instance->generic.cnt >= 0xFFFF) {
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
instance->generic.cnt = 0;
}

View File

@@ -132,7 +132,7 @@ static bool subghz_protocol_somfy_telis_gen_data(
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
} else if(instance->generic.cnt >= 0xFFFF) {
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
instance->generic.cnt = 0;
}

View File

@@ -138,7 +138,7 @@ static bool
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
} else if(instance->generic.cnt >= 0xFFFF) {
} else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) {
instance->generic.cnt = 0;
}
uint32_t fix = btn << 24 | instance->generic.serial;

View File

@@ -282,6 +282,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
furi_string_reset(temp_str);
while(flipper_format_read_string(fff_data_file, "Custom_preset_name", temp_str)) {
FURI_LOG_I(TAG, "Custom preset loaded %s", furi_string_get_cstr(temp_str));
subghz_setting_load_custom_preset(

View File

@@ -1749,7 +1749,7 @@ Function,+,furi_hal_subghz_flush_rx,void,
Function,+,furi_hal_subghz_flush_tx,void,
Function,+,furi_hal_subghz_get_data_gpio,const GpioPin*,
Function,+,furi_hal_subghz_get_lqi,uint8_t,
Function,+,furi_hal_subghz_get_rolling_counter_mult,int8_t,
Function,+,furi_hal_subghz_get_rolling_counter_mult,int32_t,
Function,+,furi_hal_subghz_get_rssi,float,
Function,+,furi_hal_subghz_idle,void,
Function,-,furi_hal_subghz_init,void,
@@ -1768,7 +1768,7 @@ Function,+,furi_hal_subghz_set_async_mirror_pin,void,const GpioPin*
Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t
Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t
Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath
Function,+,furi_hal_subghz_set_rolling_counter_mult,void,int8_t
Function,+,furi_hal_subghz_set_rolling_counter_mult,void,int32_t
Function,+,furi_hal_subghz_shutdown,void,
Function,+,furi_hal_subghz_sleep,void,
Function,+,furi_hal_subghz_start_async_rx,void,"FuriHalSubGhzCaptureCallback, void*"
@@ -3586,7 +3586,7 @@ Function,+,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore*
Function,+,subghz_keystore_load,_Bool,"SubGhzKeystore*, const char*"
Function,+,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, uint8_t*"
Function,+,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t"
Function,-,subghz_keystore_reset_kl,void,SubGhzKeystore*
Function,+,subghz_keystore_reset_kl,void,SubGhzKeystore*
Function,+,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*"
Function,+,subghz_protocol_alutech_at_4n_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*"
Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t"
1 entry status name type params
1749 Function + furi_hal_subghz_flush_tx void
1750 Function + furi_hal_subghz_get_data_gpio const GpioPin*
1751 Function + furi_hal_subghz_get_lqi uint8_t
1752 Function + furi_hal_subghz_get_rolling_counter_mult int8_t int32_t
1753 Function + furi_hal_subghz_get_rssi float
1754 Function + furi_hal_subghz_idle void
1755 Function - furi_hal_subghz_init void
1768 Function + furi_hal_subghz_set_frequency uint32_t uint32_t
1769 Function + furi_hal_subghz_set_frequency_and_path uint32_t uint32_t
1770 Function + furi_hal_subghz_set_path void FuriHalSubGhzPath
1771 Function + furi_hal_subghz_set_rolling_counter_mult void int8_t int32_t
1772 Function + furi_hal_subghz_shutdown void
1773 Function + furi_hal_subghz_sleep void
1774 Function + furi_hal_subghz_start_async_rx void FuriHalSubGhzCaptureCallback, void*
3586 Function + subghz_keystore_load _Bool SubGhzKeystore*, const char*
3587 Function + subghz_keystore_raw_encrypted_save _Bool const char*, const char*, uint8_t*
3588 Function + subghz_keystore_raw_get_data _Bool const char*, size_t, uint8_t*, size_t
3589 Function - + subghz_keystore_reset_kl void SubGhzKeystore*
3590 Function + subghz_keystore_save _Bool SubGhzKeystore*, const char*, uint8_t*
3591 Function + subghz_protocol_alutech_at_4n_create_data _Bool void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*
3592 Function + subghz_protocol_blocks_add_bit void SubGhzBlockDecoder*, uint8_t

View File

@@ -52,7 +52,7 @@ typedef struct {
volatile SubGhzRegulation regulation;
const GpioPin* async_mirror_pin;
int8_t rolling_counter_mult;
int32_t rolling_counter_mult;
bool extended_range : 1;
bool bypass_region : 1;
} FuriHalSubGhz;
@@ -66,11 +66,11 @@ volatile FuriHalSubGhz furi_hal_subghz = {
.bypass_region = false,
};
int8_t furi_hal_subghz_get_rolling_counter_mult(void) {
int32_t furi_hal_subghz_get_rolling_counter_mult(void) {
return furi_hal_subghz.rolling_counter_mult;
}
void furi_hal_subghz_set_rolling_counter_mult(int8_t mult) {
void furi_hal_subghz_set_rolling_counter_mult(int32_t mult) {
furi_hal_subghz.rolling_counter_mult = mult;
}

View File

@@ -183,14 +183,14 @@ SubGhzTx furi_hal_subghz_check_tx(uint32_t value);
bool furi_hal_subghz_is_tx_allowed(uint32_t value);
/** Get the current rolling protocols counter ++/-- value
* @return int8_t current value
* @return int32_t current value
*/
int8_t furi_hal_subghz_get_rolling_counter_mult(void);
int32_t furi_hal_subghz_get_rolling_counter_mult(void);
/** Set the current rolling protocols counter ++/-- value
* @param mult int8_t = -1, -10, -100, 0, 1, 10, 100
* @param mult int32_t = -1, -10, -50, 0, 1, 10, 50
*/
void furi_hal_subghz_set_rolling_counter_mult(int8_t mult);
void furi_hal_subghz_set_rolling_counter_mult(int32_t mult);
/** Set frequency
*

View File

@@ -14,11 +14,12 @@
extern "C" {
#endif
#define FURI_HAL_VERSION_NAME_LENGTH 8
#define FURI_HAL_VERSION_NAME_LENGTH (8)
#define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1)
#define FURI_HAL_BT_ADV_NAME_LENGTH (18 + 1) // 18 characters + null terminator
#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH \
(1 + FURI_HAL_BT_ADV_NAME_LENGTH) // Used for custom BT name, BLE symbol + name
/** 31b BLE Adv - 3b flags - 2b name prefix - 4b service uuid - 3b tx power = 19, + 1b null terminator (not present in packet) */
#define FURI_HAL_BT_ADV_NAME_LENGTH (20)
/** BLE symbol + name */
#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + FURI_HAL_BT_ADV_NAME_LENGTH)
/** OTP Versions enum */
typedef enum {