Merge remote-tracking branch 'mntm/dev' into ofw-3822-nestednonces

This commit is contained in:
Willy-JL
2024-09-18 02:12:13 +01:00
53 changed files with 1918 additions and 156 deletions
+22 -6
View File
@@ -1,11 +1,18 @@
### Added:
- Apps:
- NFC: Cyborg Detector (by @RocketGod-git)
- Sub-GHz: Radio Scanner (by @RocketGod-git)
- Sub-GHz:
- UL: Add Marantec24 (static 24 bit) with add manually (by @xMasterX)
- Show satellites count with an icon (#215 by @m7i-org)
- Add Bresser 3CH weather station protocol (#217 by @m7i-org)
- UL: Add Marantec24 protocol (static 24 bit) with add manually (by @xMasterX)
- UL: Add GangQi protocol (static 34 bit) with button parsing and add manually (by @xMasterX & @Skorpionm)
- UL: Add Hollarm protocol (static 42 bit) with button parsing and add manually (by @xMasterX & @Skorpionm)
- UL: Add Hay21 protocol (dynamic 21 bit) with button parsing (by @xMasterX)
- UL: Princeton custom buttons support (0x1, 0x2, 0x4, 0x8, 0xF) (by @xMasterX)
- NFC:
- Add SmartRider Parser (#203 by @jaylikesbunda)
- Add API to enforce ISO15693 mode (#225 by @aaronjamt)
- BadKB:
- OFW: Add linux/gnome badusb demo files (by @thomasnemer)
- Add older qFlipper install demos for windows and macos (by @DXVVAY & @grugnoymeme)
@@ -13,19 +20,24 @@
- OFW: GUI: Add up and down button drawing functions to GUI elements (by @DerSkythe)
- OFW: RPC: Support 5V on GPIO control for ext. modules (by @gsurkov)
- OFW: Toolbox: Proper integer parsing library `strint` (by @portasynthinca3)
- OFW: Furi: Put errno into TCB (by @portasynthinca3)
### Updated:
- Apps:
- WAV Player: Better fix for unresponsiveness, handle thread exit signal (by @CookiePLMonster)
- Laster Tag: External Infrared board support (by @RocketGod-git), RFID support for ammo reload (by @jamisonderek)
- Laser Tag: External Infrared board support, crash fixes (by @RocketGod-git), RFID support for ammo reload, thread leak fix (by @jamisonderek)
- ESP Flasher: Update blackmagic bin with WiFi Logs (by @DrZlo13)
- Picopass: File loading improvements and fixes (by @bettse)
- Quac!: Setting for external IR board support (by @daniilty), option to import all IR signals from file, code improvements (by @rdefeo)
- Picopass: File loading improvements and fixes (by @bettse), force ISO15693 1OutOf4 mode (by @aaronjamt)
- Quac!: External IR board support (by @daniilty), import all IR from file, iButton support, code improvements (by @rdefeo)
- DTMF Dolphin: Add EAS tone support (by @JendrBendr)
- NFC Playlist: Add playlist already exists error, general improvements (by @acegoal07)
- UL: Sub-GHz Bruteforcer: Add new protocols for existing dump option (by @xMasterX)
- UL: NRF24 Apps: Use string library compatible with OFW SDK (by @xMasterX)
- OFW: SPI Mem Manager: Fixed UI rendering bug related to line breaks (by @portasynthinca3)
- CLI: Print plugin name on load fail (by @Willy-JL)
- OFW: NFC: Rename 'Detect Reader' to 'Extract MF Keys' (by @bettse)
- NFC:
- Added 6 new Mifare Classic keys from Bulgaria Hotel (#216 by @z3r0l1nk)
- OFW: Rename 'Detect Reader' to 'Extract MF Keys' (by @bettse)
- Infrared:
- OFW: IR button operation fails now shows more informative messages (by @RebornedBrain)
- OFW: Add Airwell AW-HKD012-N91 to univeral AC remote (by @valeraOlexienko)
@@ -45,13 +57,17 @@
- OFW: Fix Guard GProxII False Positive and 36-bit Parsing (by @zinongli)
- OFW: GProxII Fix Writing and Rendering Conflict (by @zinongli)
- Desktop: Fallback Poweroff prompt when power settings is unavailable (by @Willy-JL)
- Storage: Fallback SD format prompt when storage settings is unavailable (by @Willy-JL)
- Storage:
- Fallback SD format prompt when storage settings is unavailable (by @Willy-JL)
- OFW: Fix folder rename fails (by @portasynthinca3)
- About: Fix BLE stack version string (by @Willy-JL)
- OFW: Loader: Warn about missing SD card for main apps (by @Willy-JL)
- NFC:
- OFW: Fix crash on Ultralight unlock (by @Astrrra)
- OFW: FeliCa anti-collision fix (by @RebornedBrain)
- OFW: RPC: Broken file interaction fixes (by @RebornedBrain)
- OFW: GPIO: Fix USB-UART bridge exit screen stopping the bridge prematurely (by @portasynthinca3)
- OFW: GUI: Fix dialog_ex NULL ptr crash (by @Willy-JL)
- OFW: Furi: Clean up of LFS traces (by @hedger)
- OFW: Debug: Use proper hook for handle_exit in flipperapps (by @skotopes)
- OFW: API: Fix kerel typo in documentation (by @EntranceJew)
@@ -0,0 +1,16 @@
Filetype: Flipper SubGhz Key File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok270Async
Latitute: nan
Longitude: nan
Protocol: Bresser-3CH
Id: 23
Bit: 40
Data: 00 00 00 17 26 0C 40 89
Batt: 0
Hum: 64
Ts: 1726436378
Ch: 2
Btn: 0
Temp: 18.222225
File diff suppressed because one or more lines are too long
@@ -0,0 +1,51 @@
#include <furi.h>
#include <errno.h>
#include "../test.h" // IWYU pragma: keep
#define TAG "ErrnoTest"
#define THREAD_CNT 16
#define ITER_CNT 1000
static int32_t errno_fuzzer(void* context) {
int start_value = (int)context;
int32_t fails = 0;
for(int i = start_value; i < start_value + ITER_CNT; i++) {
errno = i;
furi_thread_yield();
if(errno != i) fails++;
}
for(int i = 0; i < ITER_CNT; i++) {
errno = 0;
furi_thread_yield();
UNUSED(strtol("123456", NULL, 10)); // -V530
furi_thread_yield();
if(errno != 0) fails++;
errno = 0;
furi_thread_yield();
UNUSED(strtol("123456123456123456123456123456123456123456123456", NULL, 10)); // -V530
furi_thread_yield();
if(errno != ERANGE) fails++;
}
return fails;
}
void test_errno_saving(void) {
FuriThread* threads[THREAD_CNT];
for(int i = 0; i < THREAD_CNT; i++) {
int start_value = i * ITER_CNT;
threads[i] = furi_thread_alloc_ex("ErrnoFuzzer", 1024, errno_fuzzer, (void*)start_value);
furi_thread_set_priority(threads[i], FuriThreadPriorityNormal);
furi_thread_start(threads[i]);
}
for(int i = 0; i < THREAD_CNT; i++) {
furi_thread_join(threads[i]);
mu_assert_int_eq(0, furi_thread_get_return_code(threads[i]));
furi_thread_free(threads[i]);
}
}
@@ -8,6 +8,7 @@ void test_furi_concurrent_access(void);
void test_furi_pubsub(void);
void test_furi_memmgr(void);
void test_furi_event_loop(void);
void test_errno_saving(void);
static int foo = 0;
@@ -42,6 +43,10 @@ MU_TEST(mu_test_furi_event_loop) {
test_furi_event_loop();
}
MU_TEST(mu_test_errno_saving) {
test_errno_saving();
}
MU_TEST_SUITE(test_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_check);
@@ -51,6 +56,7 @@ MU_TEST_SUITE(test_suite) {
MU_RUN_TEST(mu_test_furi_pubsub);
MU_RUN_TEST(mu_test_furi_memmgr);
MU_RUN_TEST(mu_test_furi_event_loop);
MU_RUN_TEST(mu_test_errno_saving);
}
int run_minunit_test_furi(void) {
@@ -6,9 +6,10 @@
// This is a hack to access internal storage functions and definitions
#include <storage/storage_i.h>
#define UNIT_TESTS_PATH(path) EXT_PATH("unit_tests/" path)
#define UNIT_TESTS_RESOURCES_PATH(path) EXT_PATH("unit_tests/" path)
#define UNIT_TESTS_PATH(path) EXT_PATH(".tmp/unit_tests/" path)
#define STORAGE_LOCKED_FILE EXT_PATH("locked_file.test")
#define STORAGE_LOCKED_FILE UNIT_TESTS_PATH("locked_file.test")
#define STORAGE_LOCKED_DIR STORAGE_INT_PATH_PREFIX
#define STORAGE_TEST_DIR UNIT_TESTS_PATH("test_dir")
@@ -369,33 +370,78 @@ MU_TEST(storage_file_rename) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
mu_check(write_file_13DA(storage, EXT_PATH("file.old")));
mu_check(check_file_13DA(storage, EXT_PATH("file.old")));
mu_check(write_file_13DA(storage, UNIT_TESTS_PATH("file.old")));
mu_check(check_file_13DA(storage, UNIT_TESTS_PATH("file.old")));
mu_assert_int_eq(
FSE_OK, storage_common_rename(storage, EXT_PATH("file.old"), EXT_PATH("file.new")));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("file.old"), NULL));
mu_assert_int_eq(FSE_OK, storage_common_stat(storage, EXT_PATH("file.new"), NULL));
mu_check(check_file_13DA(storage, EXT_PATH("file.new")));
mu_assert_int_eq(FSE_OK, storage_common_remove(storage, EXT_PATH("file.new")));
FSE_OK,
storage_common_rename(storage, UNIT_TESTS_PATH("file.old"), UNIT_TESTS_PATH("file.new")));
mu_assert_int_eq(
FSE_NOT_EXIST, storage_common_stat(storage, UNIT_TESTS_PATH("file.old"), NULL));
mu_assert_int_eq(FSE_OK, storage_common_stat(storage, UNIT_TESTS_PATH("file.new"), NULL));
mu_check(check_file_13DA(storage, UNIT_TESTS_PATH("file.new")));
mu_assert_int_eq(FSE_OK, storage_common_remove(storage, UNIT_TESTS_PATH("file.new")));
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
}
static const char* dir_rename_tests[][2] = {
{UNIT_TESTS_PATH("dir.old"), UNIT_TESTS_PATH("dir.new")},
{UNIT_TESTS_PATH("test_dir"), UNIT_TESTS_PATH("test_dir-new")},
{UNIT_TESTS_PATH("test"), UNIT_TESTS_PATH("test-test")},
};
MU_TEST(storage_dir_rename) {
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_dir_create(storage, EXT_PATH("dir.old"));
for(size_t i = 0; i < COUNT_OF(dir_rename_tests); i++) {
const char* old_path = dir_rename_tests[i][0];
const char* new_path = dir_rename_tests[i][1];
mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.old")));
storage_dir_create(storage, old_path);
mu_check(storage_dir_rename_check(storage, old_path));
mu_assert_int_eq(FSE_OK, storage_common_rename(storage, old_path, new_path));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, old_path, NULL));
mu_check(storage_dir_rename_check(storage, new_path));
storage_dir_remove(storage, new_path);
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, new_path, NULL));
}
furi_record_close(RECORD_STORAGE);
}
MU_TEST(storage_equiv_and_subdir) {
Storage* storage = furi_record_open(RECORD_STORAGE);
mu_assert_int_eq(
FSE_OK, storage_common_rename(storage, EXT_PATH("dir.old"), EXT_PATH("dir.new")));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.old"), NULL));
mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.new")));
true,
storage_common_equivalent_path(storage, UNIT_TESTS_PATH("blah"), UNIT_TESTS_PATH("blah")));
mu_assert_int_eq(
true,
storage_common_equivalent_path(
storage, UNIT_TESTS_PATH("blah/"), UNIT_TESTS_PATH("blah/")));
mu_assert_int_eq(
false,
storage_common_equivalent_path(
storage, UNIT_TESTS_PATH("blah"), UNIT_TESTS_PATH("blah-blah")));
mu_assert_int_eq(
false,
storage_common_equivalent_path(
storage, UNIT_TESTS_PATH("blah/"), UNIT_TESTS_PATH("blah-blah/")));
storage_dir_remove(storage, EXT_PATH("dir.new"));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.new"), NULL));
mu_assert_int_eq(
true, storage_common_is_subdir(storage, UNIT_TESTS_PATH("blah"), UNIT_TESTS_PATH("blah")));
mu_assert_int_eq(
true,
storage_common_is_subdir(storage, UNIT_TESTS_PATH("blah"), UNIT_TESTS_PATH("blah/blah")));
mu_assert_int_eq(
false,
storage_common_is_subdir(storage, UNIT_TESTS_PATH("blah/blah"), UNIT_TESTS_PATH("blah")));
mu_assert_int_eq(
false,
storage_common_is_subdir(storage, UNIT_TESTS_PATH("blah"), UNIT_TESTS_PATH("blah-blah")));
furi_record_close(RECORD_STORAGE);
}
@@ -403,10 +449,13 @@ MU_TEST(storage_dir_rename) {
MU_TEST_SUITE(storage_rename) {
MU_RUN_TEST(storage_file_rename);
MU_RUN_TEST(storage_dir_rename);
MU_RUN_TEST(storage_equiv_and_subdir);
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_dir_remove(storage, EXT_PATH("dir.old"));
storage_dir_remove(storage, EXT_PATH("dir.new"));
for(size_t i = 0; i < COUNT_OF(dir_rename_tests); i++) {
storage_dir_remove(storage, dir_rename_tests[i][0]);
storage_dir_remove(storage, dir_rename_tests[i][1]);
}
furi_record_close(RECORD_STORAGE);
}
@@ -653,7 +702,7 @@ MU_TEST(test_md5_calc) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
const char* path = UNIT_TESTS_PATH("storage/md5.txt");
const char* path = UNIT_TESTS_RESOURCES_PATH("storage/md5.txt");
const char* md5_cstr = "2a456fa43e75088fdde41c93159d62a2";
const uint8_t md5[MD5_HASH_SIZE] = {
0x2a,
@@ -677,6 +677,13 @@ MU_TEST(subghz_decoder_solight_te44_test) {
"Test decoder " WS_PROTOCOL_SOLIGHT_TE44_NAME " error\r\n");
}
MU_TEST(subghz_decoder_bresser_3ch_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/bresser_3ch_raw.sub"), WS_PROTOCOL_BRESSER_3CH_NAME),
"Test decoder " WS_PROTOCOL_BRESSER_3CH_NAME " error\r\n");
}
//test encoders
MU_TEST(subghz_encoder_princeton_test) {
mu_assert(
@@ -3,6 +3,5 @@ ADD_SCENE(gpio, test, Test)
ADD_SCENE(gpio, usb_uart, UsbUart)
ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg)
ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc)
ADD_SCENE(gpio, exit_confirm, ExitConfirm)
ADD_SCENE(gpio, i2c_scanner, I2CScanner)
ADD_SCENE(gpio, i2c_sfp, I2CSfp)
@@ -1,44 +0,0 @@
#include "gpio_app_i.h"
void gpio_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) {
GpioApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
void gpio_scene_exit_confirm_on_enter(void* context) {
GpioApp* app = context;
DialogEx* dialog = app->dialog;
dialog_ex_set_context(dialog, app);
dialog_ex_set_left_button_text(dialog, "Exit");
dialog_ex_set_right_button_text(dialog, "Stay");
dialog_ex_set_header(dialog, "Exit USB-UART?", 22, 12, AlignLeft, AlignTop);
dialog_ex_set_result_callback(dialog, gpio_scene_exit_confirm_dialog_callback);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewExitConfirm);
}
bool gpio_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {
GpioApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultRight) {
consumed = scene_manager_previous_scene(app->scene_manager);
} else if(event.event == DialogExResultLeft) {
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, GpioSceneStart);
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = true;
}
return consumed;
}
void gpio_scene_exit_confirm_on_exit(void* context) {
GpioApp* app = context;
// Clean view
dialog_ex_reset(app->dialog);
}
@@ -6,7 +6,7 @@ typedef struct {
UsbUartState state;
} SceneUsbUartBridge;
static SceneUsbUartBridge* scene_usb_uart;
static SceneUsbUartBridge* scene_usb_uart = NULL;
void gpio_scene_usb_uart_callback(GpioCustomEvent event, void* context) {
furi_assert(context);
@@ -14,10 +14,21 @@ void gpio_scene_usb_uart_callback(GpioCustomEvent event, void* context) {
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void gpio_scene_usb_uart_dialog_callback(DialogExResult result, void* context) {
GpioApp* app = context;
if(result == DialogExResultLeft) {
usb_uart_disable(app->usb_uart_bridge);
free(scene_usb_uart);
scene_usb_uart = NULL;
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, GpioSceneStart);
} else {
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);
}
}
void gpio_scene_usb_uart_on_enter(void* context) {
GpioApp* app = context;
uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart);
if(prev_state == 0) {
if(!scene_usb_uart) {
scene_usb_uart = malloc(sizeof(SceneUsbUartBridge));
scene_usb_uart->cfg.vcp_ch = 0;
scene_usb_uart->cfg.uart_ch = 0;
@@ -31,7 +42,6 @@ void gpio_scene_usb_uart_on_enter(void* context) {
usb_uart_get_state(app->usb_uart_bridge, &scene_usb_uart->state);
gpio_usb_uart_set_callback(app->gpio_usb_uart, gpio_scene_usb_uart_callback, app);
scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 0);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);
notification_message(app->notifications, &sequence_display_backlight_enforce_on);
}
@@ -39,11 +49,16 @@ void gpio_scene_usb_uart_on_enter(void* context) {
bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
GpioApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 1);
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg);
return true;
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_next_scene(app->scene_manager, GpioSceneExitConfirm);
DialogEx* dialog = app->dialog;
dialog_ex_set_context(dialog, app);
dialog_ex_set_left_button_text(dialog, "Exit");
dialog_ex_set_right_button_text(dialog, "Stay");
dialog_ex_set_header(dialog, "Exit USB-UART?", 22, 12, AlignLeft, AlignTop);
dialog_ex_set_result_callback(dialog, gpio_scene_usb_uart_dialog_callback);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewExitConfirm);
return true;
} else if(event.type == SceneManagerEventTypeTick) {
uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt;
@@ -61,10 +76,5 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
void gpio_scene_usb_uart_on_exit(void* context) {
GpioApp* app = context;
uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioSceneUsbUart);
if(prev_state == 0) {
usb_uart_disable(app->usb_uart_bridge);
free(scene_usb_uart);
}
notification_message(app->notifications, &sequence_display_backlight_enforce_auto);
}
+9
View File
@@ -29,6 +29,15 @@ App(
sources=["plugins/supported_cards/all_in_one.c"],
)
App(
appid="smartrider_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="smartrider_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/smartrider.c"],
)
App(
appid="microel_parser",
apptype=FlipperAppType.PLUGIN,
@@ -0,0 +1,334 @@
#include "nfc_supported_card_plugin.h"
#include <bit_lib.h>
#include <flipper_application.h>
#include <furi.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <string.h>
#define MAX_TRIPS 10
#define TAG "SmartRider"
#define MAX_BLOCKS 64
#define MAX_DATE_ITERATIONS 366
static const uint8_t STANDARD_KEYS[3][6] = {
{0x20, 0x31, 0xD1, 0xE5, 0x7A, 0x3B},
{0x4C, 0xA6, 0x02, 0x9F, 0x94, 0x73},
{0x19, 0x19, 0x53, 0x98, 0xE3, 0x2F}};
typedef struct {
uint32_t timestamp;
uint16_t cost;
uint16_t transaction_number;
uint16_t journey_number;
char route[5];
uint8_t tap_on : 1;
uint8_t block;
} __attribute__((packed)) TripData;
typedef struct {
uint32_t balance;
uint16_t issued_days;
uint16_t expiry_days;
uint16_t purchase_cost;
uint16_t auto_load_threshold;
uint16_t auto_load_value;
char card_serial_number[11];
uint8_t token;
TripData trips[MAX_TRIPS];
uint8_t trip_count;
} __attribute__((packed)) SmartRiderData;
static const char* const CONCESSION_TYPES[] = {
"Pre-issue",
"Standard Fare",
"Student",
NULL,
"Tertiary",
NULL,
"Seniors",
"Health Care",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"PTA Staff",
"Pensioner",
"Free Travel"};
static inline const char* get_concession_type(uint8_t token) {
return (token <= 0x10) ? CONCESSION_TYPES[token] : "Unknown";
}
static bool authenticate_and_read(
Nfc* nfc,
uint8_t sector,
const uint8_t* key,
MfClassicKeyType key_type,
MfClassicBlock* block_data) {
MfClassicKey mf_key;
memcpy(mf_key.data, key, 6);
uint8_t block = mf_classic_get_first_block_num_of_sector(sector);
if(mf_classic_poller_sync_auth(nfc, block, &mf_key, key_type, NULL) != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Authentication failed for sector %d key type %d", sector, key_type);
return false;
}
if(mf_classic_poller_sync_read_block(nfc, block, &mf_key, key_type, block_data) !=
MfClassicErrorNone) {
FURI_LOG_D(TAG, "Read failed for sector %d", sector);
return false;
}
return true;
}
static bool smartrider_verify(Nfc* nfc) {
furi_assert(nfc);
MfClassicBlock block_data;
for(int i = 0; i < 3; i++) {
if(!authenticate_and_read(
nfc,
i * 6,
STANDARD_KEYS[i],
i % 2 == 0 ? MfClassicKeyTypeA : MfClassicKeyTypeB,
&block_data) ||
memcmp(block_data.data, STANDARD_KEYS[i], 6) != 0) {
FURI_LOG_D(TAG, "Authentication or key mismatch for key %d", i);
return false;
}
}
FURI_LOG_I(TAG, "SmartRider card verified");
return true;
}
static inline bool
parse_trip_data(const MfClassicBlock* block_data, TripData* trip, uint8_t block_number) {
trip->timestamp = bit_lib_bytes_to_num_le(block_data->data + 3, 4);
trip->tap_on = (block_data->data[7] & 0x10) == 0x10;
memcpy(trip->route, block_data->data + 8, 4);
trip->route[4] = '\0';
trip->cost = bit_lib_bytes_to_num_le(block_data->data + 13, 2);
trip->transaction_number = bit_lib_bytes_to_num_le(block_data->data, 2);
trip->journey_number = bit_lib_bytes_to_num_le(block_data->data + 2, 2);
trip->block = block_number;
return true;
}
static bool smartrider_read(Nfc* nfc, NfcDevice* device) {
furi_assert(nfc);
furi_assert(device);
MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
MfClassicType type;
if(mf_classic_poller_sync_detect_type(nfc, &type) != MfClassicErrorNone ||
type != MfClassicType1k) {
mf_classic_free(data);
return false;
}
data->type = type;
MfClassicDeviceKeys keys = {.key_a_mask = 0, .key_b_mask = 0};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
memcpy(keys.key_a[i].data, STANDARD_KEYS[i == 0 ? 0 : 1], sizeof(STANDARD_KEYS[0]));
if(i > 0) {
memcpy(keys.key_b[i].data, STANDARD_KEYS[2], sizeof(STANDARD_KEYS[0]));
FURI_BIT_SET(keys.key_b_mask, i);
}
FURI_BIT_SET(keys.key_a_mask, i);
}
MfClassicError error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
mf_classic_free(data);
return false;
}
nfc_device_set_data(device, NfcProtocolMfClassic, data);
mf_classic_free(data);
return true;
}
static bool is_leap_year(uint16_t year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
static void calculate_date(uint32_t timestamp, char* date_str, size_t date_str_size) {
uint32_t seconds_since_2000 = timestamp;
uint32_t days_since_2000 = seconds_since_2000 / 86400;
uint16_t year = 2000;
uint8_t month = 1;
uint16_t day = 1;
static const uint16_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
while(days_since_2000 >= (is_leap_year(year) ? 366 : 365)) {
days_since_2000 -= (is_leap_year(year) ? 366 : 365);
year++;
}
for(month = 0; month < 12; month++) {
uint16_t dim = days_in_month[month];
if(month == 1 && is_leap_year(year)) {
dim++;
}
if(days_since_2000 < dim) {
break;
}
days_since_2000 -= dim;
}
day = days_since_2000 + 1;
month++; // Adjust month to 1-based
if(date_str_size > 0) {
size_t written = 0;
written += snprintf(date_str + written, date_str_size - written, "%02u", day);
if(written < date_str_size - 1) {
written += snprintf(date_str + written, date_str_size - written, "/");
}
if(written < date_str_size - 1) {
written += snprintf(date_str + written, date_str_size - written, "%02u", month);
}
if(written < date_str_size - 1) {
written += snprintf(date_str + written, date_str_size - written, "/");
}
if(written < date_str_size - 1) {
snprintf(date_str + written, date_str_size - written, "%02u", year % 100);
}
} else {
// If the buffer size is 0, do nothing
}
}
static bool smartrider_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
SmartRiderData sr_data = {0};
if(data->type != MfClassicType1k) {
FURI_LOG_E(TAG, "Invalid card type");
return false;
}
const MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);
if(!sec_tr || memcmp(sec_tr->key_a.data, STANDARD_KEYS[0], 6) != 0) {
FURI_LOG_E(TAG, "Key verification failed for sector 0");
return false;
}
static const uint8_t required_blocks[] = {14, 4, 5, 1, 52, 50, 0};
for(size_t i = 0; i < COUNT_OF(required_blocks); i++) {
if(required_blocks[i] >= MAX_BLOCKS ||
!mf_classic_is_block_read(data, required_blocks[i])) {
FURI_LOG_E(TAG, "Required block %d is not read or out of range", required_blocks[i]);
return false;
}
}
sr_data.balance = bit_lib_bytes_to_num_le(data->block[14].data + 7, 2);
sr_data.issued_days = bit_lib_bytes_to_num_le(data->block[4].data + 16, 2);
sr_data.expiry_days = bit_lib_bytes_to_num_le(data->block[4].data + 18, 2);
sr_data.auto_load_threshold = bit_lib_bytes_to_num_le(data->block[4].data + 20, 2);
sr_data.auto_load_value = bit_lib_bytes_to_num_le(data->block[4].data + 22, 2);
sr_data.token = data->block[5].data[8];
sr_data.purchase_cost = bit_lib_bytes_to_num_le(data->block[0].data + 14, 2);
snprintf(
sr_data.card_serial_number,
sizeof(sr_data.card_serial_number),
"%02X%02X%02X%02X%02X",
data->block[1].data[6],
data->block[1].data[7],
data->block[1].data[8],
data->block[1].data[9],
data->block[1].data[10]);
for(uint8_t block_number = 40; block_number <= 52 && sr_data.trip_count < MAX_TRIPS;
block_number++) {
if((block_number != 43 && block_number != 47 && block_number != 51) &&
mf_classic_is_block_read(data, block_number) &&
parse_trip_data(
&data->block[block_number], &sr_data.trips[sr_data.trip_count], block_number)) {
sr_data.trip_count++;
}
}
// Sort trips by timestamp (descending order)
for(uint8_t i = 0; i < sr_data.trip_count - 1; i++) {
for(uint8_t j = 0; j < sr_data.trip_count - i - 1; j++) {
if(sr_data.trips[j].timestamp < sr_data.trips[j + 1].timestamp) {
TripData temp = sr_data.trips[j];
sr_data.trips[j] = sr_data.trips[j + 1];
sr_data.trips[j + 1] = temp;
}
}
}
furi_string_printf(
parsed_data,
"\e#SmartRider\nBalance: $%lu.%02lu\nConcession: %s\nSerial: %s%s\n"
"Total Cost: $%u.%02u\nAuto-Load: $%u.%02u/$%u.%02u\n\e#Tag On/Off History\n",
sr_data.balance / 100,
sr_data.balance % 100,
get_concession_type(sr_data.token),
memcmp(sr_data.card_serial_number, "00", 2) == 0 ? "SR0" : "",
memcmp(sr_data.card_serial_number, "00", 2) == 0 ? sr_data.card_serial_number + 2 :
sr_data.card_serial_number,
sr_data.purchase_cost / 100,
sr_data.purchase_cost % 100,
sr_data.auto_load_threshold / 100,
sr_data.auto_load_threshold % 100,
sr_data.auto_load_value / 100,
sr_data.auto_load_value % 100);
for(uint8_t i = 0; i < sr_data.trip_count; i++) {
char date_str[9];
calculate_date(sr_data.trips[i].timestamp, date_str, sizeof(date_str));
uint32_t cost = sr_data.trips[i].cost;
if(cost > 0) {
furi_string_cat_printf(
parsed_data,
"%s %c $%lu.%02lu %s\n",
date_str,
sr_data.trips[i].tap_on ? '+' : '-',
cost / 100,
cost % 100,
sr_data.trips[i].route);
} else {
furi_string_cat_printf(
parsed_data,
"%s %c %s\n",
date_str,
sr_data.trips[i].tap_on ? '+' : '-',
sr_data.trips[i].route);
}
}
return true;
}
static const NfcSupportedCardsPlugin smartrider_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = smartrider_verify,
.read = smartrider_read,
.parse = smartrider_parse,
};
__attribute__((used)) const FlipperAppPluginDescriptor* smartrider_plugin_ep() {
static const FlipperAppPluginDescriptor plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &smartrider_plugin,
};
return &plugin_descriptor;
}
// made with love by jay candel <3
@@ -2495,6 +2495,18 @@ EA19E58DD046
3F41891454EE
7EDAE7923287
11DDA4862A1C
# +------------------------------------------------------------------------------------------------------------------------+
# | https://github.com/Next-Flip/Momentum-Firmware/blob/dev/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc |
# +------------------------------------------------------------------------------------------------------------------------+
##############################################
# BG Hotels - keys from Bulgaria
# Found with FlipperNestedRecovery by Z3r0L1nk
8000806B5072
8430A669558C
1202165D4EAB
B02094F92A71
0402B44FB679
A23412F92811
# +---------------------------------------------------------------------+
# | https://github.com/Stepzor11/NFC_keys/blob/main/mf_classic_dict.nfc |
# +---------------------------------------------------------------------+
@@ -7,11 +7,13 @@
static void subghz_scene_receiver_update_statusbar(void* context) {
SubGhz* subghz = context;
FuriString* history_stat_str = furi_string_alloc();
bool show_sats = subghz->gps && furi_hal_rtc_get_timestamp() % 2;
if(!subghz_history_get_text_space_left(
subghz->history,
history_stat_str,
subghz->gps ? subghz->gps->satellites : 0,
subghz->last_settings->delete_old_signals)) {
subghz->last_settings->delete_old_signals,
show_sats,
show_sats ? subghz->gps->satellites : 0)) {
FuriString* frequency_str = furi_string_alloc();
FuriString* modulation_str = furi_string_alloc();
@@ -25,6 +27,7 @@ static void subghz_scene_receiver_update_statusbar(void* context) {
furi_string_get_cstr(history_stat_str),
subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF,
READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0,
show_sats,
subghz->repeater);
furi_string_free(frequency_str);
@@ -37,6 +40,7 @@ static void subghz_scene_receiver_update_statusbar(void* context) {
"",
subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF,
READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0,
show_sats,
subghz->repeater);
}
furi_string_free(history_stat_str);
@@ -50,11 +50,13 @@ const NotificationSequence subghz_sequence_tx_beep = {
static void subghz_scene_receiver_update_statusbar(void* context) {
SubGhz* subghz = context;
FuriString* history_stat_str = furi_string_alloc();
bool show_sats = subghz->gps && furi_hal_rtc_get_timestamp() % 2;
if(!subghz_history_get_text_space_left(
subghz->history,
history_stat_str,
subghz->gps ? subghz->gps->satellites : 0,
subghz->last_settings->delete_old_signals)) {
subghz->last_settings->delete_old_signals,
show_sats,
show_sats ? subghz->gps->satellites : 0)) {
FuriString* frequency_str = furi_string_alloc();
FuriString* modulation_str = furi_string_alloc();
@@ -87,6 +89,7 @@ static void subghz_scene_receiver_update_statusbar(void* context) {
furi_string_get_cstr(history_stat_str),
subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF,
READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0,
show_sats,
subghz->repeater);
furi_string_free(frequency_str);
@@ -99,6 +102,7 @@ static void subghz_scene_receiver_update_statusbar(void* context) {
"",
subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF,
READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0,
show_sats,
subghz->repeater);
}
furi_string_free(history_stat_str);
+6 -9
View File
@@ -193,8 +193,9 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx
bool subghz_history_get_text_space_left(
SubGhzHistory* instance,
FuriString* output,
uint8_t sats,
bool ignore_full) {
bool ignore_full,
bool show_sats,
uint8_t sats) {
furi_assert(instance);
if(!ignore_full) {
if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) {
@@ -207,14 +208,10 @@ bool subghz_history_get_text_space_left(
}
}
if(output != NULL) {
if(sats == 0) {
furi_string_printf(output, "%02u", instance->last_index_write);
if(show_sats) {
furi_string_printf(output, "%d", sats);
} else {
if(furi_hal_rtc_get_timestamp() % 2) {
furi_string_printf(output, "%02u", instance->last_index_write);
} else {
furi_string_printf(output, "%d sats", sats);
}
furi_string_printf(output, "%02u", instance->last_index_write);
}
}
return false;
+8 -6
View File
@@ -118,19 +118,21 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* outp
*/
void subghz_history_get_time_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx);
/** Get string the remaining number of records to history
/** Get string the remaining number of records to history, or sats
*
* @param instance - SubGhzHistory instance
* @param output - FuriString* output
* @param sats - Number of satellites
* @param instance - SubGhzHistory instance
* @param output - FuriString* output
* @param ignore_full - Ignore if history is full
* @param show_sats - Whether to show the satellite number
* @param sats - Number of satellites
* @return bool - is FULL
*/
bool subghz_history_get_text_space_left(
SubGhzHistory* instance,
FuriString* output,
uint8_t sats,
bool ignore_full);
bool ignore_full,
bool show_sats,
uint8_t sats);
/** Return last index
*
+13 -2
View File
@@ -66,6 +66,7 @@ typedef struct {
FuriString* progress_str;
bool hopping_enabled;
bool bin_raw_enabled;
bool show_sats;
SubGhzRepeaterState repeater_state;
SubGhzReceiverHistory* history;
uint16_t idx;
@@ -210,6 +211,7 @@ void subghz_view_receiver_add_data_statusbar(
const char* history_stat_str,
bool hopping_enabled,
bool bin_raw_enabled,
bool show_sats,
SubGhzRepeaterState repeater_state) {
furi_assert(subghz_receiver);
with_view_model(
@@ -221,6 +223,7 @@ void subghz_view_receiver_add_data_statusbar(
furi_string_set(model->history_stat_str, history_stat_str);
model->hopping_enabled = hopping_enabled;
model->bin_raw_enabled = bin_raw_enabled;
model->show_sats = show_sats;
model->repeater_state = repeater_state;
},
true);
@@ -408,7 +411,11 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
AlignRight,
AlignBottom,
furi_string_get_cstr(model->history_stat_str));
canvas_draw_icon(canvas, 116, 53, &I_sub1_10px);
if(model->show_sats) {
canvas_draw_icon(canvas, 118, 54, &I_Sats_6x9);
} else {
canvas_draw_icon(canvas, 116, 53, &I_sub1_10px);
}
}
canvas_set_font(canvas, FontSecondary);
elements_bold_rounded_frame(canvas, 14, 8, 99, 48);
@@ -453,7 +460,11 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
AlignRight,
AlignBottom,
furi_string_get_cstr(model->history_stat_str));
canvas_draw_icon(canvas, 116, 53, &I_sub1_10px);
if(model->show_sats) {
canvas_draw_icon(canvas, 118, 54, &I_Sats_6x9);
} else {
canvas_draw_icon(canvas, 116, 53, &I_sub1_10px);
}
}
} break;
}
@@ -34,6 +34,7 @@ void subghz_view_receiver_add_data_statusbar(
const char* history_stat_str,
bool hopping_enabled,
bool bin_raw_enabled,
bool show_sats,
SubGhzRepeaterState repeater_enabled);
void subghz_view_receiver_set_radio_device_type(
+15 -10
View File
@@ -428,21 +428,26 @@ bool storage_common_exists(Storage* storage, const char* path);
* - /int/Test and /int/test -> false (Case-sensitive storage),
* - /ext/Test and /ext/test -> true (Case-insensitive storage).
*
* If the truncate parameter is set to true, the second path will be
* truncated to be no longer than the first one. It is useful to determine
* whether path2 is a subdirectory of path1.
*
* @param storage pointer to a storage API instance.
* @param path1 pointer to a zero-terminated string containing the first path.
* @param path2 pointer to a zero-terminated string containing the second path.
* @param truncate whether to truncate path2 to be no longer than path1.
* @return true if paths are equivalent, false otherwise.
*/
bool storage_common_equivalent_path(
Storage* storage,
const char* path1,
const char* path2,
bool truncate);
bool storage_common_equivalent_path(Storage* storage, const char* path1, const char* path2);
/**
* @brief Check whether a path is a subpath of another path.
*
* This function respects storage-defined equivalence rules
* (see `storage_common_equivalent_path`).
*
* @param storage pointer to a storage API instance.
* @param parent pointer to a zero-terminated string containing the parent path.
* @param child pointer to a zero-terminated string containing the child path.
* @return true if `child` is a subpath of `parent`, or if `child` is equivalent
* to `parent`; false otherwise.
*/
bool storage_common_is_subdir(Storage* storage, const char* parent, const char* child);
/******************* Error Functions *******************/
@@ -507,13 +507,13 @@ FS_Error storage_common_rename(Storage* storage, const char* old_path, const cha
}
// Cannot rename a directory to itself or to a nested directory
if(storage_common_equivalent_path(storage, old_path, new_path, true)) {
if(storage_common_is_subdir(storage, old_path, new_path)) {
error = FSE_INVALID_NAME;
break;
}
// Renaming a regular file to itself does nothing and always succeeds
} else if(storage_common_equivalent_path(storage, old_path, new_path, false)) {
} else if(storage_common_equivalent_path(storage, old_path, new_path)) {
error = FSE_OK;
break;
}
@@ -567,13 +567,13 @@ FS_Error storage_common_rename_safe(Storage* storage, const char* old_path, cons
if(storage_dir_exists(storage, old_path)) {
// Cannot rename a directory to itself or to a nested directory
if(storage_common_equivalent_path(storage, old_path, new_path, true)) {
if(storage_common_is_subdir(storage, old_path, new_path)) {
error = FSE_INVALID_NAME;
break;
}
// Renaming a regular file to itself does nothing and always succeeds
} else if(storage_common_equivalent_path(storage, old_path, new_path, false)) {
} else if(storage_common_equivalent_path(storage, old_path, new_path)) {
error = FSE_OK;
break;
}
@@ -608,7 +608,7 @@ FS_Error storage_common_rename_safe(Storage* storage, const char* old_path, cons
static FS_Error
storage_copy_recursive(Storage* storage, const char* old_path, const char* new_path) {
if(storage_common_equivalent_path(storage, old_path, new_path, true)) {
if(storage_common_is_subdir(storage, old_path, new_path)) {
return FSE_INVALID_NAME;
}
@@ -927,11 +927,11 @@ bool storage_common_exists(Storage* storage, const char* path) {
return storage_common_stat(storage, path, &file_info) == FSE_OK;
}
bool storage_common_equivalent_path(
static bool storage_internal_equivalent_path(
Storage* storage,
const char* path1,
const char* path2,
bool truncate) {
bool check_subdir) {
furi_check(storage);
S_API_PROLOGUE;
@@ -940,7 +940,7 @@ bool storage_common_equivalent_path(
.cequivpath = {
.path1 = path1,
.path2 = path2,
.truncate = truncate,
.check_subdir = check_subdir,
.thread_id = furi_thread_get_current_id(),
}};
@@ -950,6 +950,14 @@ bool storage_common_equivalent_path(
return S_RETURN_BOOL;
}
bool storage_common_equivalent_path(Storage* storage, const char* path1, const char* path2) {
return storage_internal_equivalent_path(storage, path1, path2, false);
}
bool storage_common_is_subdir(Storage* storage, const char* parent, const char* child) {
return storage_internal_equivalent_path(storage, parent, child, true);
}
/****************** ERROR ******************/
const char* storage_error_get_desc(FS_Error error_id) {
@@ -77,7 +77,7 @@ typedef struct {
typedef struct {
const char* path1;
const char* path2;
bool truncate;
bool check_subdir;
FuriThreadId thread_id;
} SADataCEquivPath;
@@ -757,11 +757,23 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
storage_path_trim_trailing_slashes(path2);
storage_process_alias(app, path1, message->data->cequivpath.thread_id, false);
storage_process_alias(app, path2, message->data->cequivpath.thread_id, false);
// Comparison is done on path name, same beginning of name != same file/folder
// Check with a / suffixed to ensure same file/folder name
furi_string_cat(path1, "/");
furi_string_cat(path2, "/");
if(message->data->cequivpath.truncate) {
if(message->data->cequivpath.check_subdir) {
// by appending slashes at the end and then truncating the second path, we can
// effectively check for shared path components:
// example 1:
// path1: "/ext/blah" -> "/ext/blah/" -> "/ext/blah/"
// path2: "/ext/blah-blah" -> "/ect/blah-blah/" -> "/ext/blah-"
// results unequal, conclusion: path2 is not a subpath of path1
// example 2:
// path1: "/ext/blah" -> "/ext/blah/" -> "/ext/blah/"
// path2: "/ext/blah/blah" -> "/ect/blah/blah/" -> "/ext/blah/"
// results equal, conclusion: path2 is a subpath of path1
// example 3:
// path1: "/ext/blah/blah" -> "/ect/blah/blah/" -> "/ext/blah/blah/"
// path2: "/ext/blah" -> "/ext/blah/" -> "/ext/blah/"
// results unequal, conclusion: path2 is not a subpath of path1
furi_string_push_back(path1, '/');
furi_string_push_back(path2, '/');
furi_string_left(path2, furi_string_size(path1));
}
message->return_data->bool_value =
Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

+16
View File
@@ -0,0 +1,16 @@
# How to change Flipper name:
## Instruction
1. Go to Momentum -> Misc -> Spoofing Options -> Flipper Name
2. Enter your new custom name for your flipper and click `Save`, **name will be saved on microSD card, and will stay same after firmware updates**
3. You will need to exit from Momentum app and Flipper will automatically reboot!
4. After reboot you will see your new custom name in device info and right screen `passport`
5. Done!
**To reset device name to default - do same steps but do not enter any characters, leave it empty and click** `Save`
Currently, changing Flipper name on Momentum Firmware also affects:
- Bluetooth device name
- Bluetooth MAC address (3 bytes of Flipper ID + 3 bytes of ASCII from custom name)
- USB device name
- Serial number (ASCII from custom name)
+83
View File
@@ -0,0 +1,83 @@
# Infrared Captures
**Credits go to @gsurkov, @skotopes, @knrn-ai, @DrZlo13 and @ahumeniy for making and contributing to the original `UniversalRemotes.md` Documentation located [Here](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/UniversalRemotes.md).**
**slightly adapted by @amec0e**
## Televisions, Fans, Audio and Projectors
Each signal is recorded using the following process:
1. Get the remote and point it to Flipper's IR receiver.
2. Start learning a new remote if it's the first button or press `+` to add a new button otherwise.
3. Do a Quick Press of a remote button and save it under a corresponding name. **(NOTE: Don't hold the remote button down, this will result in long captures and long playbacks ultimately slowing down the universal remotes performance)**
4. Repeat steps 2-3 until all required signals are saved.
The signal names are self-explanatory. Remember to make sure that every recorded signal does what it's supposed to.
**NOTE:** It's possible some devices around you will cause interference and may force your capture into raw data instead of a parsed code.
If you notice you get a parsed code when capturing it's best to click "Retry" a few times on the flipper when capturing to ensure the device is not suffering from any interference, and that the cleanest capture is possible.
## Types of data
**Parsed data**
This is the cleanest type of data because it means it is a recognized code.
```
name: EXAMPLE
type: parsed
protocol: NEC
address: 07 00 00 00
command: 02 00 00 00
```
**Raw Data**
With raw data its important not to hold the remotes button down when capturing on your flipper as this increases not only the size of the capture but the repeats and also how long it takes to send the signal back. Below is an ideal button capture.
```
#
name: EXAMPLE
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2410 597 1189 599 592 600 1186 602 589 603 1183 606 595 597 593 598 1208 605 596 596 594 597 593 599 592 25604 2403 604 1182 606 595 597 1189 599 591 601 1185 603 618 573 617 575 1211 602 588 603 588 605 596 596 594 25605 2402 604 1192 596 594 597 1189 599 592 601 1185 628 593 598 593 600 1186 602 589 603 588 604 597 595 596
```
**Capturing Raw Data:**
If you are sure your remote is using raw data the best way to capture it will be to do a quick button press **(don't hold the remotes button down)** and look at how many samples you get, the general idea here is to get the lowest amount of raw data samples captured (around 100 samples is about right) while making sure that the playback on the device is still working. This is usually accomplished by doing a quick button press on the remote when capturing.
## Air Conditioners
Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote.
The majority of A/C remotes have a small display that shows the current mode, temperature, and other settings.
When the user presses a button, a whole set of parameters is transmitted to the device, which must be recorded and used as a whole.
In order to capture a particular air conditioner, there is a particular process require to capturing and this is done using the following process:
1. Get the remote and press the **Power Button** so that the display shows that A/C is ON.
2. Set the A/C to the corresponding mode (see table below), leaving other parameters such as fan speed or vane on **AUTO** (if applicable).
3. Press the **POWER** button to switch the A/C off.
4. Start learning a new remote on Flipper if it's the first button or press `+` to add a new button otherwise.
5. Point the remote to Flipper's IR receiver as directed and press **POWER** button once again.
6. Save the resulting signal under the specified name.
7. Repeat steps 2-6 for each signal from the table below.
| Signal | Mode | Temperature | Note |
| :-----: | :--------: | :---------: | ----------------------------------- |
| Dh | Dehumidify | N/A | |
| Cool_hi | Cooling | See note | Lowest temperature in cooling mode |
| Cool_lo | Cooling | 23°C | |
| Heat_hi | Heating | See note | Highest temperature in heating mode |
| Heat_lo | Heating | 23°C | |
Finally, record the `Off` signal:
1. Make sure the display shows that the A/C is ON.
2. Start learning a new signal on Flipper and point the remote towards the IR receiver.
3. Press the **POWER** button so that the remote shows the OFF state.
4. Save the resulting signal under the name `Off`.
Test the file against the actual device. Make sure that every signal does what it's supposed to.
+61
View File
@@ -0,0 +1,61 @@
# MultiConverter
## Author: [theisolinearchip](https://github.com/theisolinearchip/flipperzero_stuff/tree/main/applications/multi_converter)
An expanded version of my previous __Dec/Hex Converter__, this time allowing more units and a _(probably poorly made from a design-point-of-view)_ selector mode
to swap between different unit groups.
I wrote it with the idea of _expanding the unit list_ on mind, so adding new ones it's a matter of increasing an array of constants + defining the proper conversion functions.
(Actually the whole project is more about "making the framework" rather than providing _ALL_ of the possible units : D)
![Img 1](http://albertgonzalez.coffee/projects/flipperzero/multi_converter/img/1_small.png) ![Img 2](http://albertgonzalez.coffee/projects/flipperzero/multi_converter/img/2_small.png)
## Current conversions
- `Decimal / Hexadecimal / Binary`
- `Celsius / Fahrenheit / Kelvin`
- `Kilometers / Meters / Centimeters / Miles / Feet / Inches`
- `Degree / Radian`
## Usage
Base keyboard allows numbers from `0` to `F`, being disabled (or not) according to the current selected unit.
Long press on `0` toggles a __negative__ value; long press on `1` sets a __decimal point__ (only if allowed by the current selected unit).
`<` removes the last character; `#` changes to __Unit Select Mode__.
### Unit Select Mode
`Left` and `Right` to swap between __origin unit__ and __destination unit__ (notice the _destination_ will change according to the current selected _origin_).
`Ok` to save the changes and go back to the __Display Mode__; `Back` to go back without changing any unit.
## Adding new units
1. Add the new units in the `MultiConverterUnitType` enum on `multi_converter_definitions.h` (basic definitions header). Notice each enum element will be used as an array index later.
2. Increase the `MULTI_CONVERTER_AVAILABLE_UNITS` constant on `multi_converter_units.h` (units main header file).
3. Set a pair of functions for __converting__ units and to __check__ if a target unit is allowed to work with the destination unit (both on `multi_converter_units.h`
and `multi_converter_units.c`; follow the already built-in units for more info).
4. Add the proper `MultiConverterUnit` structs for each new unit.
5. Add each new struct to the main `multi_converter_available_units` array.
And that's it! The system will fetch the new units and display it!
## Known issues, TODO-list, etc.
This is an initial release, so expect some bugs and issues (also I don't work with C that much, so there're probably lots of things that can be improved and/or changed!).
- I've noticed some small decimal variations when "going deep" with some units (like converting __miles__ to __centimeters__ and things like that); probably due to the precision-level required. Need to check that.
- Pending: improve overflow checks.
- The way some long numbers are shown could probably be improved to look fancier.
- Both _origin_ and _destination buffers_ are the same. The destination one could probably be longer in order to avoid certain _overflow scenarios_.
- The GUI needs improvement too: there's a whole __widget/views system__ built in the Flipper that allows things like setting up keys, showing "Save/Back/Cancel" messages with
callbacks and stuff like that. Didn't know anything about them, so I moved on with something more basic (which is probably fine since it's not a "very big project"); but
a more "standard" way with the regular GUI stuff provided by the firmware will be interesting...
- More GUI stuff: the _long click buttons_ for adding a decimal point / negative number aren't very clear on the view itself (I tried to add a small dot / dash symbol, but I think those are small enough to be a little bit confusing)
+51
View File
@@ -0,0 +1,51 @@
# flipperzero-nrf24
## Author: [mothball187](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker)
An [NRF24](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf) driver for the [Flipper Zero](https://flipperzero.one/) device. The NRF24 is a popular line of 2.4GHz radio transceivers from Nordic Semiconductors. This library is not currently complete, but functional.
# How to use
- Connect NRF24 to flipper using provided pinouts
- Open NRF24: Sniffer, and scan channels, switch between modes/channels using buttons
- When you got address -> Open NRF24: Mouse Jacker
- Select Address and open badusb file
- Done
# Demo (YouTube)
[![YouTube](https://img.youtube.com/vi/C5hbyAjuU4k/0.jpg)](https://www.youtube.com/watch?v=C5hbyAjuU4k)
## Warning
These apps are for **educational purposes** only. Please use this code responsibly and only use these apps on your own equipment.
## Acknowledgments
The NRF24 sniffing technique was discovered and shared by Travis Goodspeed in [his blog](http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html).
The mousejack vulnerabilities were discovered and reported by Marc Newlin, see [the blog](https://www.bastille.net/research/vulnerabilities/mousejack/technical-details) for technical details.
Much of the driver code was inspired by [RadioHead's Arduino library](https://www.airspayce.com/mikem/arduino/RadioHead/classRH__NRF24.html).
Much of the mousejack code was inspired by the [Jackit project](https://github.com/insecurityofthings/jackit).
# Pinout from from NoComp/Frog
<img src="https://media.discordapp.net/attachments/937479784726949900/994495234618687509/unknown.png?width=567&height=634">
# Mousejacker / NRF24 pinout by UberGuidoZ
2/A7 on FZ goes to MOSI/6 on nrf24l01<br>
3/A6 on FZ goes to MISO/7 on nrf24l01<br>
4/A4 on FZ goes to CSN/4 on nrf24l01<br>
5/B3 on FZ goes to SCK/5 on nrf24l01<br>
6/B2 on FZ goes to CE/3 on nrf24l01<br>
8/GND on FZ goes to GND/1 on nrf24l01<br>
9/3V3 on FZ goes to VCC/2 on nrf24l01<br>
IRQ/8 is left disconnected on nrf24l01<br>
![NRF_Pins](https://user-images.githubusercontent.com/57457139/178093717-39effd5c-ebe2-4253-b13c-70517d7902f9.png)
If the nRF module is acting a bit flakey, try adding a capacitor to the vcc/gnd lines!
I've not tried the Plus model so it may have a bigger need for a cap.
Otherwise, I haven't had any major issues.
Anything from a 3.3 uF to 10 uF should do. (Watch your positive/negative placement! Negative to ground.)
I learned if you wanna get fancy, include a 0.1 uF cap in parallel.
The 3.3 uF to 10 uF will respond to slow freq changes while the 0.1 uF will respond to the high freq switching spikes that the larger one cannot. That said, a single 10 uF will likely suffice for the Mousejack attack. ¯\\\_(ツ)_/¯
![NRF_Capacitor](https://user-images.githubusercontent.com/57457139/178169959-d030f9a6-d2ac-46af-af8b-470ff092c8a7.jpg)
+17
View File
@@ -0,0 +1,17 @@
# Sentry Safe plugin
## Author: [H4ckd4ddy](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin)
Flipper zero exploiting vulnerability to open any Sentry Safe and Master Lock electronic safe without any pin code.
[Demo and Vulnerability described here](https://github.com/H4ckd4ddy/bypass-sentry-safe)
### Usage
- Start "Sentry Safe" plugin
- Place wires as described on the plugin screen
<br>(Flipper GPIO) 8/GND -> Black wire (Safe)
<br>(Flipper GPIO) 15/C1 -> Green wire (Safe)
- Press enter
- Open safe
+33
View File
@@ -0,0 +1,33 @@
# Possible errors and what to do
When installing firmware with WebUpdater, Flipper Lab, or Flipper Mobile App, `/int/.region_data` file is created on SD card.
This file has information on allowed frequencies for country you are located in. When this is not present, SubGHz app will say "Region in not provisioned" when transmitting.
On Official Firmware, SubGHz app does not open when region not provisioned, and also receiving is not allowed if frequency is not allowed in your region.
On Momentum Firmware, SubGHz app does not restrict receiving in any way, only transmit. If transmit not allowed, it will tell you why and explain what to do. Here is more info:
- Region in not provisioned: `/int/.region_data` not found, update/reinstall firmware using WebUpdater, Flipper Lab, or Flipper Mobile App
- Frequency outside of region range: not allowed in your country, you can use Bypass Region (read below)
- Frequency outside of default range: not officially supported by Flipper, you can use Extend bands (READ BELOW!!!!)
- Frequency is outside of supported range: will not work with Flipper in any way
## How to disable SubGHz region lock restriction
#### CC1101 Frequency range specs: 300-348 MHz, 386-464 MHz, and 778-928 MHz (+ 350MHz and 467MHz was added to default range)
This setting will unlock whole CC1101 Frequency range specifications, regardless of your current country limits. Use with caution, and check local laws!!!
You can also do this when "Region is not provisioned", but this is discouraged: it is possible that frequency is allowed for you, the error means "I don't know what is allowed or not" because `/int/.region_data` file is missing. Better to update/reinstall firmware. But if this is not possible, Bypass Region works too.
You can enable in `Momentum > Protocols > SubGHz Bypass Region Lock`.
## How to extend SubGHz supported frequency range
#### CC1101 Frequency range specs: 300-348 MHz, 386-464 MHz, and 778-928 MHz (+ 350MHz and 467MHz was added to default range)
#### This setting will extend to: 281-361 MHz, 378-481 MHz, and 749-962 MHz
1. Please do not do that unless you know what exactly you are doing
2. You don't need extended range for almost all use cases
3. Extending frequency range and transmitting on frequencies that outside of hardware specs can damage your hardware!
4. Flipper Devices team and/or Momentum FW developers are not responsible for any damage that can be caused by using CFW or extending frequency ranges!!!
If you really sure you need that change, enable in `Momentum > Protocols > SubGHz Extend Freq Bands`.
+78
View File
@@ -0,0 +1,78 @@
# Sub-GHz Remote
Credit to [Unleashed Firmware](https://github.com/DarkFlippers/unleashed-firmware) team for documentation and the [app itself](https://github.com/DarkFlippers/SubGHz_Remote)!
# UPDATE!!!!!!
## Now you can create and edit map files directly on flipper, go into Sub-GHz Remote and click back button
<br>
<br>
<br>
### The SubGHz Remote Tool *requires* the creation of custom user map with `.txt` extension in the `subghz/remote` folder on the sdcard.
#### If these files are not exist or not configured properly, **you will receive an error each time you try to select wrong file in the UniRF Tool**.
## You can add as many `.txt` map files as you want, file name doesn't matter!
## Incorrect or unconfigured file error
If the `.txt` file has not been properly configured, the following error will be thrown when trying to run the UniRF Remix app:
```
Config is incorrect.
Please configure map
Press Back to Exit
```
## Setting up the `subghz/remote/example.txt` file:
```
UP: /ext/subghz/Up.sub
DOWN: /ext/subghz/Down.sub
LEFT: /ext/subghz/Left.sub
RIGHT: /ext/subghz/Right.sub
OK: /ext/subghz/Ok.sub
ULABEL: Up Label
DLABEL: Down Label
LLABEL: Left Label
RLABEL: Right Label
OKLABEL: Ok Label
```
The UP/DOWN/LEFT/RIGHT/OK file locations must be set to the specific file you want mapped to that directional pad direction.
The ULABEL/DLABEL/LLABEL/RLABEL/OKLABEL variables should be set to the text to be displayed for each of the files set earlier.
## Example:
```
UP: /ext/subghz/Fan1.sub
DOWN: /ext/subghz/Fan2.sub
LEFT: /ext/subghz/Door.sub
RIGHT: /ext/subghz/Garage3.sub
OK: /ext/subghz/Garage3l.sub
ULABEL: Fan ON
DLABEL: Fan OFF
LLABEL: Doorbell
RLABEL: Garage OPEN
OKLABEL: Garage CLOSE
```
## Notes
* ##### App Usage
- Press a button to send the assigned capture file.
- Press Back button to exit app.
* ##### SubGHz Remote Map
- File path should not have any spaces or special characters (- and _ excluded).
- Labels are limited to 16 characters.
- Why? This is to prevent overlapping elements on screen.
- For example: If you set your label or file to ```WWWWWWWWWWWWWWW``` you'll be over the screen limits.
+181
View File
@@ -0,0 +1,181 @@
# How to use Flipper as a new SubGHz remote (not clone of original remote)
Many rolling codes support, and this guide, comes from [Unleashed Firmware](https://github.com/DarkFlippers/unleashed-firmware). Go show them some love and maybe donate to them too!
### If your system is not added here that doesn't mean flipper don't support it! Look into add manually menu, and search for your manufacturers inscturctions!
### Also many supported systems can be used only from `Read` mode, `Add Manually` is used only to make new remotes that can be binded with receiver
## FAAC SLH (NEW!)
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> FAAC SLH (select your frequency)
2. Open your new remote file
3. Open your receiver box, find programming button on the receiver board.
4. Hold Up arrow button on the flipper to send programming signal - at same time press and hold programming button on the receiver board.
5. Led on the receiver board will light on, then off, then on, then off again then on again
6. Release all buttons
7. Press send button on the flipper couple times holding it for 1-3 seconds
8. Done!
Watch this video to learn more : https://www.youtube.com/watch?v=NfZmMy37XUs
...
How to get Seed value from your original remote or bind new remote using existing (master) remote?
1. Go to SubGHz -> Read - Select frequency 868.35 or 433.92 and modulation AM650
2. Hold two buttons on the original master remote until led turns on
3. Click one button that you want to get seed from (Seed is unique for each button on original remote!)
4. You will get signal in the read screen on flipper, open that and see your original remote seed for button you used
5. You can create new remote using that seed and bind that to receiver without opening the box! Faac has procedure that allows to bind new remotes using master remote, you can use flipper for that
6. Go to SubGHz -> Add Manually -> FAAC SLH Man. (your Freq)
7. Enter those values -> REPLACE `R` with any random digits like 1,2,3..
FIX -> A0 RR RR R6
COUNTER -> 00 00 02
SEED -> Your seed from the remote button you got earlier
8. Flipper will act as new remote, press Send button couple times near the receiver to register new remote
9. Done!
## Dea Mio
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> Dea Mio 433Mhz
2. Open your new remote file
3. Right arrow button on the flipper simulates press of hidden button in original remote
4. Send button simulates one of basic buttons of the remote, can be programmed into the receiver
5. Follow manufacturer instructions on new remotes programming
## AN-Motors AT4
**This instruction for older boards, if your has no** `Learn` **button but has buttons** `F`, `CL`, `+`, `-` **read instruction from Alutech AT4N**
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> AN-Motors AT4 433Mhz
2. Open your new remote file
3. Open your receiver box, find button `Learn` click it one time, led will turn on.
4. Press `Send` on your flipper one time, led on receiver board will turn off.
5. Press `Send` on your flipper again, led on receiver will start flashing, wait couple seconds until led turns off.
6. Done
Watch this video to learn more (video in Russian language): https://www.youtube.com/watch?v=URVMtTELcnU
## Alutech AT4N (AN-Motors)
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> Alutech AT4N 433Mhz
2. Open your new remote file
3. Open your receiver box, find button `F` press it for ~3sec, display will show `Pr`.
4. Click `F` button couple times until you see `Lr` on screen
5. Using buttons `+` / `-` select free number that has no remotes in it (if it has remote programmed on that number, it will show a red dot on the down right corner)
6. Press `Send` on your flipper one time, display on receiver board will flash and red dot will appear next to remote number.
7. Press button `F` on receiver board for ~3sec to exit programming mode
8. Done
Watch this video to learn more and see how different boards can be programmed (video in Russian language): https://www.youtube.com/watch?v=XrOVVYhFXDg
## Aprimatic TR
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> KL: Aprimatic 433Mhz
2. Open your new remote file
3. Push all 4 buttons at same time on your existing remote thats already works with receiver
4. Receiver makes a continuous beep
5. Press `Send` on your flipper for ~2 seconds
6. Wait until receiver stops beeping
7. Done?
## Doorhan
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> KL: Doorhan 433Mhz or 315Mhz depends on your receiver (find out by reading your existing remote)
2. Open your new remote file
3. Push `P` button for ~2 sec, led will start flashing
4. Press `Send` on your flipper for ~2 seconds
5. Led on the receiver board will flash and turn off
6. Done!
Also you can program new remote using old remote on newer boards! See first video below:
Watch this videos to learn more (videos in Russian language): https://www.youtube.com/watch?v=wZ5121HYv50 / https://www.youtube.com/watch?v=1ucrDKF3vWc
## Somfy Telis
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> Somfy Telis 433Mhz
2. Open your new remote file
3. Long press (hold) the Prog button on a remote that is already registered to the device, until the blinds move shortly up and down.
4. Press and hold the Prog button on the flipper (Left Arrow), until the blinds move shortly up and down again.
5. Done?
## BFT Mitto
How to create new remote and bind it to receiver (will not conflict with original remotes):
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> BFT Mitto 433Mhz
2. Open your new remote file
3. You need to be in minimum 3 meters to receiver
4. Original Remote: Press hidden button on back of remote with a pin or paper clip OR press Button 1 & 2 together until remote LED lights.
5. Original Remote: Momentarily press button that opens device
6. Long press (Right Arrow) - (0xF button - Btn:F) on Flipper for like 3-5 sec
7. Press the button you want to bind to open the device on the flipper
8. Press (Right Arrow) - (0xF button - Btn:F) again
9. Done?
OR
1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> BFT Mitto 433Mhz
2. Open your new remote file
3. Open your receiver board box
4. **Watch this video**: https://www.youtube.com/watch?v=5QXMBKI_-Ls
5. Long press (Right Arrow) - (0xF button - Btn:F) on Flipper for like 3-5 sec -> Will act like holding Button 1 & 2 on original remote as shown on video
6. Done?
--
How to get seed to make full clone of your remote (**will conflict with original remote!!!!!**):
**WARNING!!!! This method can desync your original remote, please avoid using it! It can be used in rare cases like when your remote works poorly or has broken buttons and you want to replace it with flipper**
1. Open `Read` in SubGHz on your flipper
2. (ONLY FOR ORIGINAL REMOTES) Hold all buttons on your remote at same time, example -> for 2 button remote - press them both at same time and hold OR press hidden button on back of remote with a pin or paper clip
3. You will receive signal on your flipper, open that signal and see `Fix:` value, it should start from `F` like `F00F1C9B`
4. If `Fix:` is showing first `F` see `Hop:` value -> This is your remote Seed
5. Write down Hop value
6. Press button on your remote that you want to clone and receive its signal on your flipper
7. Open and write down `Fix:` value where first digit will be same as your button ID `Btn:`
8. Create new remote using BFT Mitto [Manual] - Enter FIX from step 7, enter counter `FF F9`, enter seed from step 5
9. Using counter values like `FF F9` can help bypassing current original remote counter value, and in result it also can fully desync original remote, only one remote can work at same time using this method
10. Throw away your original remote since now it needs to be re-added into receiver board :C
## CAME Atomo
1. Use google to find instructions - `how to program new CAME Atomo remote into receiver`
2. Watch this video to learn more (video in Russian language): https://www.youtube.com/watch?v=XeHUwfcSS30
## Nice Flor S
- Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> Nice FloR-S 433Mhz
- Open your new remote file
### Coding using an existing remote
To enter the code of a new remote control without using your receiver, you will need
an authorised remote control (note: the first remote control must always be entered
using the receiver key). Now, with the two remote controls (your already coded
remote, and your new remote), which we shall call NEW (the one whose code we want
to enter) and OLD (the authorised one), position yourself within 3m of the gate/garage
receiver and then:
1. Press and hold the `Send` button on the flipper for at least 5 seconds and then
release.
2. Press the button on the already programmed remote 3 times slowly.
3. Press the `Send` button on the flipper slowly and then release.
### Coding directly to your receiver
Your new remote will program to your receiver as per your original remote
instructions, so please refer to your manual. But for a typical NICE FLOX2R Receiver,
the programming procedure is as follows:
1. Press the learning button on your receiver for 1-2 seconds. The LED will turn on
for 5 seconds. Within 5 seconds, complete the next step.
2. Press a `Send` button on your flipper until the LED on your receiver turns off.
3. Release the remote button and wait for 2 seconds.
4. Press the `Send` button on your flipper again. The LED on your receiver
will now flash 3 times. This indicates that your remote has been successfully
coded. If this does not happen, repeat the whole procedure from the
beginning, and try again.
5. Wait 5 seconds. Press the button on your new remote to test if it opens your
garage/gate.
#### Follow links below to find more detailed instructions!!!
#### Materials used:
- [FAAC SLH](https://www.youtube.com/watch?v=NfZmMy37XUs)
- [Somfy Telis](https://pushstack.wordpress.com/somfy-rts-protocol/)
- [BFT Mitto](https://www.retroremotes.com.au/wp-content/uploads/2017/03/BFT-MITTO-2-4-19-6-17.pdf)
- [NICE FLOX2R Receiver Programming](https://apollogateopeners.com/store/pdf/apollo-flor-s-receiver-programming-guide.pdf)
- [Nice Flor S Programming](https://motepro.com.au/Instructions/Nice.pdf)
+117
View File
@@ -0,0 +1,117 @@
## How to add new SubGHz frequencies
#### CC1101 Frequency range specs: 300-348 MHz, 386-464 MHz, and 778-928 MHz (+ 350MHz and 467MHz was added to default range)
### From Flipper
On Momentum Firmware, you can add manage frequencies list from Flipper from `Momentum > Protocols > SubGHz Freqs`:
- Use Defaults: whether to include default frequency list, if yes your custom frequencies go at END of default list
- Static Freqs: list used by `Read`, `Read RAW` and `Frequency Analyzer`
- Hopper Freqs: list used by `Read > Config > Hopping: ON`
This menu is a utility for configuring the normal config file that all firmwares use directly from Flipper. See below for a guide written by [Unleashed Firmware](https://github.com/DarkFlippers/unleashed-firmware) team on how to use the config file manually.
### From config file
Edit user settings file located on your microSD card - `subghz/assets/setting_user` (remove .example from name to use config)
in this file you will find we already have extra frequencies added
if you need your custom one, make sure it doesn't listed here
### Default frequency list
```
/* 300 - 348 */
300000000,
302757000,
303875000,
303900000,
304250000,
307000000,
307500000,
307800000,
309000000,
310000000,
312000000,
312100000,
312200000,
313000000,
313850000,
314000000,
314350000,
314980000,
315000000,
318000000,
330000000,
345000000,
348000000,
350000000,
/* 387 - 464 */
387000000,
390000000,
418000000,
430000000,
430500000,
431000000,
431500000,
433075000, /* LPD433 first */
433220000,
433420000,
433657070,
433889000,
433920000 | FREQUENCY_FLAG_DEFAULT, /* LPD433 mid */
434075000,
434176948,
434190000,
434390000,
434420000,
434620000,
434775000, /* LPD433 last channels */
438900000,
440175000,
464000000,
467750000,
/* 779 - 928 */
779000000,
868350000,
868400000,
868800000,
868950000,
906400000,
915000000,
925000000,
928000000,
```
### User frequencies added AFTER that default list! You need to continue until you reach the end of that list
### If you want to disable default list and use ONLY user added frequencies from user settings file
Change that line
`#Add_standard_frequencies: true`
to
`Add_standard_frequencies: false`
**You need to have custom frequencies added in both lists! in main frequency list and in hopping list! Replacing only hopping freqs will not work with that setting set on false, you need to add something in main list since it will be empty**
### To add your own frequency to user list
Just add new line
`Frequency: 928000000` - where `928000000` is your frequency, keep it in that format! it should be 9 digits!
### Hopper frequency list
To add new frequency to hopper:
add new line `Hopper_frequency: 345000000`<br>
But remember! You should keep it as small as possible, or hopper functionality would be useless!<br>
If `#Add_standard_frequencies: true` is not changed<br>
Your frequencies will be added after default ones
### Default hopper list
```
310000000,
315000000,
318000000,
418000000,
433920000,
868350000,
```
@@ -178,7 +178,7 @@ Data_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 DE 02 D3 54 D5 4C D2 C
Frequency: 433920000
Preset: FuriHalSubGhzPresetCustom
Custom_preset_module: CC1101
Сustom_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
Protocol: RAW
RAW_Data: 29262 361 -68 2635 -66 24113 -66 11 ...
RAW_Data: -424 205 -412 159 -412 381 -240 181 ...
@@ -26,7 +26,7 @@ Changelog:
| Name | Type | Description |
| ----------- | ------ | -------------------------------------------- |
| Protocol | string | Currently supported: DS1990, DS1992, DS1996, DS1971, DSGeneric*, Cyfral, Metakom |
| Protocol | string | Currently supported: DS1990, DS1992, DS1996, DS1971, DS1420, DSGeneric, Cyfral, Metakom |
| Rom Data | hex | Read-only memory data (Dallas protocols only) |
| Sram Data | hex | Static RAM data (DS1992 and DS1996 only)
| Eeprom Data | hex | EEPROM data (DS1971 only)
@@ -37,6 +37,8 @@ It can also be used if a key with a deliberately invalid family code or checksum
NOTE 2: When adding new protocols, it is not necessarily to increase the format version, define the format in the protocol implementation instead.
**DS1420 is fully compatible with DS1990, only difference is a familiy code 0x01 for DS1990 and 0x81 for DS1420**
### 1. Initial version.
Deprecated, will be converted to current version upon saving.
+1 -1
View File
@@ -79,7 +79,7 @@ void furi_delay_tick(uint32_t ticks);
*
* @warning This should never be called in interrupt request context.
*
* @param[in] tick The tick until which kerel should delay task execution
* @param[in] tick The tick until which kernel should delay task execution
*
* @return The furi status.
*/
+27
View File
@@ -646,6 +646,33 @@ NfcError nfc_iso15693_listener_tx_sof(Nfc* instance) {
return ret;
}
NfcError nfc_iso15693_detect_mode(Nfc* instance) {
furi_check(instance);
FuriHalNfcError error = furi_hal_nfc_iso15693_detect_mode();
NfcError ret = nfc_process_hal_error(error);
return ret;
}
NfcError nfc_iso15693_force_1outof4(Nfc* instance) {
furi_check(instance);
FuriHalNfcError error = furi_hal_nfc_iso15693_force_1outof4();
NfcError ret = nfc_process_hal_error(error);
return ret;
}
NfcError nfc_iso15693_force_1outof256(Nfc* instance) {
furi_check(instance);
FuriHalNfcError error = furi_hal_nfc_iso15693_force_1outof256();
NfcError ret = nfc_process_hal_error(error);
return ret;
}
NfcError nfc_felica_listener_set_sensf_res_data(
Nfc* instance,
const uint8_t* idm,
+24
View File
@@ -380,6 +380,30 @@ NfcError nfc_felica_listener_set_sensf_res_data(
*/
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance);
/**
* @brief Set ISO15693 parser mode to autodetect
*
* @param[in,out] instance pointer to the instance to be configured.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso15693_detect_mode(Nfc* instance);
/**
* @brief Set ISO15693 parser mode to 1OutOf4, disables autodetection
*
* @param[in,out] instance pointer to the instance to be configured.
* @return NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso15693_force_1outof4(Nfc* instance);
/**
* @brief Set ISO15693 parser mode to 1OutOf256, disables autodetection
*
* @param[in,out] instance pointer to the instance to be configured.
* @return NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso15693_force_1outof256(Nfc* instance);
#ifdef __cplusplus
}
#endif
@@ -26,6 +26,7 @@ typedef enum {
struct Iso15693Parser {
Iso15693ParserState state;
Iso15693ParserMode mode;
bool detect_mode;
SignalReader* signal_reader;
@@ -62,6 +63,7 @@ typedef Iso15693ParserCommand (*Iso15693ParserStateHandler)(Iso15693Parser* inst
Iso15693Parser* iso15693_parser_alloc(const GpioPin* pin, size_t max_frame_size) {
Iso15693Parser* instance = malloc(sizeof(Iso15693Parser));
instance->detect_mode = true;
instance->parsed_frame = bit_buffer_alloc(max_frame_size);
instance->signal_reader = signal_reader_alloc(pin, ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE);
@@ -86,7 +88,7 @@ void iso15693_parser_reset(Iso15693Parser* instance) {
furi_assert(instance);
instance->state = Iso15693ParserStateParseSoF;
instance->mode = Iso15693ParserMode1OutOf4;
if(instance->detect_mode) instance->mode = Iso15693ParserMode1OutOf4;
memset(instance->bitstream_buff, 0x00, sizeof(instance->bitstream_buff));
instance->bitstream_idx = 0;
@@ -122,10 +124,10 @@ static void signal_reader_callback(SignalReaderEvent event, void* context) {
if(instance->state == Iso15693ParserStateParseSoF) {
if(event.data->data[0] == sof_1_out_of_4) {
instance->mode = Iso15693ParserMode1OutOf4;
if(instance->detect_mode) instance->mode = Iso15693ParserMode1OutOf4;
instance->state = Iso15693ParserStateParseFrame;
} else if(event.data->data[0] == sof_1_out_of_256) {
instance->mode = Iso15693ParserMode1OutOf256;
if(instance->detect_mode) instance->mode = Iso15693ParserMode1OutOf256;
instance->state = Iso15693ParserStateParseFrame;
} else if(event.data->data[0] == eof_single) {
instance->eof_received = true;
@@ -298,3 +300,23 @@ void iso15693_parser_get_data(
bit_buffer_write_bytes(instance->parsed_frame, buff, buff_size);
*data_bits = bit_buffer_get_size(instance->parsed_frame);
}
void iso15693_parser_detect_mode(Iso15693Parser* instance) {
furi_assert(instance);
instance->detect_mode = true;
}
void iso15693_parser_force_1outof4(Iso15693Parser* instance) {
furi_assert(instance);
instance->detect_mode = false;
instance->mode = Iso15693ParserMode1OutOf4;
}
void iso15693_parser_force_1outof256(Iso15693Parser* instance) {
furi_assert(instance);
instance->detect_mode = false;
instance->mode = Iso15693ParserMode1OutOf256;
}
@@ -37,6 +37,10 @@ void iso15693_parser_get_data(
size_t buff_size,
size_t* data_bits);
void iso15693_parser_detect_mode(Iso15693Parser* instance);
void iso15693_parser_force_1outof4(Iso15693Parser* instance);
void iso15693_parser_force_1outof256(Iso15693Parser* instance);
#ifdef __cplusplus
}
#endif
+264
View File
@@ -0,0 +1,264 @@
#include "bresser_3ch.h"
#include "furi/core/log.h"
#define TAG "WSProtocolBresser3ch"
/*
* Help:
* https://github.com/merbanan/rtl_433/blob/master/src/devices/bresser_3ch.c
*
* Bresser sensor protocol.
*
* The protocol is for the wireless Temperature/Humidity sensor
* - Bresser Thermo-/Hygro-Sensor 3CH
* - also works for Renkforce DM-7511
*
* The sensor sends 15 identical packages of 40 bits each ~60s.
* The bits are PWM modulated with On Off Keying.
*
* A short pulse of 250 us followed by a 500 us gap is a 0 bit,
* a long pulse of 500 us followed by a 250 us gap is a 1 bit,
* there is a sync preamble of pulse, gap, 750 us each, repeated 4 times.
* Actual received and demodulated timings might be 2% shorter.
*
* The data is grouped in 5 bytes / 10 nibbles
*
* [id] [id] [flags] [temp] [temp] [temp] [humi] [humi] [chk] [chk]
*
* - id is an 8 bit random id that is generated when the sensor starts
* - flags are 4 bits battery low indicator, test button press and channel
* - temp is 12 bit unsigned fahrenheit offset by 90 and scaled by 10
* - humi is 8 bit relative humidity percentage
* - chk is the sum of the four data bytes
*
* @m7i-org - because there's more stuff screaming in the ether than you might think
*
*/
static const SubGhzBlockConst ws_protocol_bresser_3ch_const = {
.te_short = 250,
.te_long = 500,
.te_delta = 150,
.min_count_bit_for_found = 40,
};
struct WSProtocolDecoderBresser3ch {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
WSBlockGeneric generic;
};
struct WSProtocolEncoderBresser3ch {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
WSBlockGeneric generic;
};
const SubGhzProtocolDecoder ws_protocol_bresser_3ch_decoder = {
.alloc = ws_protocol_decoder_bresser_3ch_alloc,
.free = ws_protocol_decoder_bresser_3ch_free,
.feed = ws_protocol_decoder_bresser_3ch_feed,
.reset = ws_protocol_decoder_bresser_3ch_reset,
.get_hash_data = NULL,
.get_hash_data_long = ws_protocol_decoder_bresser_3ch_get_hash_data,
.serialize = ws_protocol_decoder_bresser_3ch_serialize,
.deserialize = ws_protocol_decoder_bresser_3ch_deserialize,
.get_string = ws_protocol_decoder_bresser_3ch_get_string,
.get_string_brief = NULL,
};
const SubGhzProtocolEncoder ws_protocol_bresser_3ch_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol ws_protocol_bresser_3ch = {
.name = WS_PROTOCOL_BRESSER_3CH_NAME,
.type = SubGhzProtocolTypeStatic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |
SubGhzProtocolFlag_Save,
.filter = SubGhzProtocolFilter_Weather,
.decoder = &ws_protocol_bresser_3ch_decoder,
.encoder = &ws_protocol_bresser_3ch_encoder,
};
typedef enum {
Bresser3chDecoderStepReset = 0,
Bresser3chDecoderStepPreambleDn,
Bresser3chDecoderStepPreambleUp,
Bresser3chDecoderStepSaveDuration,
Bresser3chDecoderStepCheckDuration,
} Bresser3chDecoderStep;
void* ws_protocol_decoder_bresser_3ch_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
WSProtocolDecoderBresser3ch* instance = malloc(sizeof(WSProtocolDecoderBresser3ch));
instance->base.protocol = &ws_protocol_bresser_3ch;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void ws_protocol_decoder_bresser_3ch_free(void* context) {
furi_assert(context);
WSProtocolDecoderBresser3ch* instance = context;
free(instance);
}
void ws_protocol_decoder_bresser_3ch_reset(void* context) {
furi_assert(context);
WSProtocolDecoderBresser3ch* instance = context;
instance->decoder.parser_step = Bresser3chDecoderStepReset;
}
static bool ws_protocol_bresser_3ch_check(WSProtocolDecoderBresser3ch* instance) {
if(!instance->decoder.decode_data) return false;
uint8_t sum = (((instance->decoder.decode_data >> 32) & 0xff) +
((instance->decoder.decode_data >> 24) & 0xff) +
((instance->decoder.decode_data >> 16) & 0xff) +
((instance->decoder.decode_data >> 8) & 0xff)) &
0xff;
return (instance->decoder.decode_data & 0xff) == sum;
}
/**
* Analysis of received data
* @param instance Pointer to a WSBlockGeneric* instance
*/
static void ws_protocol_bresser_3ch_extract_data(WSBlockGeneric* instance) {
instance->id = (instance->data >> 32) & 0xff;
instance->battery_low = ((instance->data >> 31) & 0x1);
instance->btn = (instance->data >> 30) & 0x1;
instance->channel = (instance->data >> 28) & 0x3;
int16_t temp = (instance->data >> 16) & 0xfff;
instance->temp = locale_fahrenheit_to_celsius((float)(temp - 900) / 10.0);
instance->humidity = (instance->data >> 8) & 0xff;
}
void ws_protocol_decoder_bresser_3ch_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
WSProtocolDecoderBresser3ch* instance = context;
switch(instance->decoder.parser_step) {
case Bresser3chDecoderStepReset:
if(level && DURATION_DIFF(duration, ws_protocol_bresser_3ch_const.te_short * 3) <
ws_protocol_bresser_3ch_const.te_delta) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = Bresser3chDecoderStepPreambleDn;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
}
break;
case Bresser3chDecoderStepPreambleDn:
if((!level) && DURATION_DIFF(duration, ws_protocol_bresser_3ch_const.te_short * 3) <
ws_protocol_bresser_3ch_const.te_delta) {
if(DURATION_DIFF(
instance->decoder.te_last, ws_protocol_bresser_3ch_const.te_short * 12) <
ws_protocol_bresser_3ch_const.te_delta * 2) {
// End of sync after 4*750 (12*250) high values, start reading the message
instance->decoder.parser_step = Bresser3chDecoderStepSaveDuration;
} else {
instance->decoder.parser_step = Bresser3chDecoderStepPreambleUp;
}
} else {
instance->decoder.parser_step = Bresser3chDecoderStepReset;
}
break;
case Bresser3chDecoderStepPreambleUp:
if(level && DURATION_DIFF(duration, ws_protocol_bresser_3ch_const.te_short * 3) <
ws_protocol_bresser_3ch_const.te_delta) {
instance->decoder.te_last = instance->decoder.te_last + duration;
instance->decoder.parser_step = Bresser3chDecoderStepPreambleDn;
} else {
instance->decoder.parser_step = Bresser3chDecoderStepReset;
}
break;
case Bresser3chDecoderStepSaveDuration:
if(instance->decoder.decode_count_bit ==
ws_protocol_bresser_3ch_const.min_count_bit_for_found) {
if(ws_protocol_bresser_3ch_check(instance)) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
ws_protocol_bresser_3ch_extract_data(&instance->generic);
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 = Bresser3chDecoderStepReset;
} else if(level) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = Bresser3chDecoderStepCheckDuration;
} else {
instance->decoder.parser_step = Bresser3chDecoderStepReset;
}
break;
case Bresser3chDecoderStepCheckDuration:
if(!level) {
if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_bresser_3ch_const.te_short) <
ws_protocol_bresser_3ch_const.te_delta &&
DURATION_DIFF(duration, ws_protocol_bresser_3ch_const.te_long) <
ws_protocol_bresser_3ch_const.te_delta) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = Bresser3chDecoderStepSaveDuration;
} else if(
DURATION_DIFF(instance->decoder.te_last, ws_protocol_bresser_3ch_const.te_long) <
ws_protocol_bresser_3ch_const.te_delta &&
DURATION_DIFF(duration, ws_protocol_bresser_3ch_const.te_short) <
ws_protocol_bresser_3ch_const.te_delta) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = Bresser3chDecoderStepSaveDuration;
} else
instance->decoder.parser_step = Bresser3chDecoderStepReset;
} else
instance->decoder.parser_step = Bresser3chDecoderStepReset;
break;
}
}
uint32_t ws_protocol_decoder_bresser_3ch_get_hash_data(void* context) {
furi_assert(context);
WSProtocolDecoderBresser3ch* instance = context;
return subghz_protocol_blocks_get_hash_data_long(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus ws_protocol_decoder_bresser_3ch_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
WSProtocolDecoderBresser3ch* instance = context;
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
}
SubGhzProtocolStatus
ws_protocol_decoder_bresser_3ch_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
WSProtocolDecoderBresser3ch* instance = context;
return ws_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, ws_protocol_bresser_3ch_const.min_count_bit_for_found);
}
void ws_protocol_decoder_bresser_3ch_get_string(void* context, FuriString* output) {
furi_assert(context);
WSProtocolDecoderBresser3ch* instance = context;
ws_block_generic_get_string(&instance->generic, output);
}
+80
View File
@@ -0,0 +1,80 @@
#pragma once
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/encoder.h>
#include "ws_generic.h"
#include <lib/subghz/blocks/math.h>
#define WS_PROTOCOL_BRESSER_3CH_NAME "Bresser-3CH"
typedef struct WSProtocolDecoderBresser3ch WSProtocolDecoderBresser3ch;
typedef struct WSProtocolEncoderBresser3ch WSProtocolEncoderBresser3ch;
extern const SubGhzProtocolDecoder ws_protocol_bresser_3ch_decoder;
extern const SubGhzProtocolEncoder ws_protocol_bresser_3ch_encoder;
extern const SubGhzProtocol ws_protocol_bresser_3ch;
/**
* Allocate WSProtocolDecoderBresser3ch.
* @param environment Pointer to a SubGhzEnvironment instance
* @return WSProtocolDecoderBresser3ch* pointer to a WSProtocolDecoderBresser3ch instance
*/
void* ws_protocol_decoder_bresser_3ch_alloc(SubGhzEnvironment* environment);
/**
* Free WSProtocolDecoderBresser3ch.
* @param context Pointer to a WSProtocolDecoderBresser3ch instance
*/
void ws_protocol_decoder_bresser_3ch_free(void* context);
/**
* Reset decoder WSProtocolDecoderBresser3ch.
* @param context Pointer to a WSProtocolDecoderBresser3ch instance
*/
void ws_protocol_decoder_bresser_3ch_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a WSProtocolDecoderBresser3ch instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void ws_protocol_decoder_bresser_3ch_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a WSProtocolDecoderBresser3ch instance
* @return hash Hash sum
*/
uint32_t ws_protocol_decoder_bresser_3ch_get_hash_data(void* context);
/**
* Serialize data WSProtocolDecoderBresser3ch.
* @param context Pointer to a WSProtocolDecoderBresser3ch instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return status
*/
SubGhzProtocolStatus ws_protocol_decoder_bresser_3ch_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data WSProtocolDecoderBresser3ch.
* @param context Pointer to a WSProtocolDecoderBresser3ch instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return status
*/
SubGhzProtocolStatus
ws_protocol_decoder_bresser_3ch_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a WSProtocolDecoderBresser3ch instance
* @param output Resulting text
*/
void ws_protocol_decoder_bresser_3ch_get_string(void* context, FuriString* output);
+1 -2
View File
@@ -498,8 +498,7 @@ void subghz_protocol_decoder_gangqi_get_string(void* context, FuriString* output
bool serial_is_valid =
(((!(sum_3bytes_serial & 0x3)) &&
((0xB < sum_3bytes_serial) && (sum_3bytes_serial < 0x141))) &&
((((instance->generic.serial >> 16) & 0xFF) == 0x2) ||
(((instance->generic.serial >> 16) & 0xFF) == 0x3)));
(((instance->generic.serial >> 16) & 0xFF) <= 0x3));
furi_string_cat_printf(
output,
+6 -6
View File
@@ -404,16 +404,16 @@ static const char* subghz_protocol_hollarm_get_button_name(uint8_t btn) {
"Disarm", // B (2)
"Arm", // A (1)
"0x3",
"Alarm", // C (3)
"Ringtone/Alarm", // C (3)
"0x5",
"0x6",
"0x7",
"Ring", // D (4)
"0x9",
"0xA",
"0xB",
"0xC",
"0xD",
"Settings mode",
"Exit settings",
"Vibro sens. setting",
"Not used\n(in settings)",
"Volume setting",
"0xE",
"0xF"};
return btn <= 0xf ? name_btn[btn] : name_btn[0];
+143 -9
View File
@@ -6,6 +6,8 @@
#include "../blocks/generic.h"
#include "../blocks/math.h"
#include "../blocks/custom_btn_i.h"
/*
* Help
* https://phreakerclub.com/447
@@ -109,6 +111,109 @@ void subghz_protocol_encoder_princeton_free(void* context) {
free(instance);
}
// Get custom button code
static uint8_t subghz_protocol_princeton_get_btn_code(void) {
uint8_t custom_btn_id = subghz_custom_btn_get();
uint8_t original_btn_code = subghz_custom_btn_get_original();
uint8_t btn = original_btn_code;
// Set custom button
if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0)) {
// Restore original button code
btn = original_btn_code;
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_UP) {
switch(original_btn_code) {
case 0x1:
btn = 0x2;
break;
case 0x2:
btn = 0x1;
break;
case 0x4:
btn = 0x2;
break;
case 0x8:
btn = 0x2;
break;
case 0xF:
btn = 0x2;
break;
default:
btn = 0x2;
break;
}
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) {
switch(original_btn_code) {
case 0x1:
btn = 0x4;
break;
case 0x2:
btn = 0x4;
break;
case 0x4:
btn = 0x1;
break;
case 0x8:
btn = 0x1;
break;
case 0xF:
btn = 0x1;
break;
default:
btn = 0x1;
break;
}
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) {
switch(original_btn_code) {
case 0x1:
btn = 0x8;
break;
case 0x2:
btn = 0x8;
break;
case 0x4:
btn = 0x8;
break;
case 0x8:
btn = 0x4;
break;
case 0xF:
btn = 0x4;
break;
default:
btn = 0x4;
break;
}
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_RIGHT) {
switch(original_btn_code) {
case 0x1:
btn = 0xF;
break;
case 0x2:
btn = 0xF;
break;
case 0x4:
btn = 0xF;
break;
case 0x8:
btn = 0xF;
break;
case 0xF:
btn = 0x8;
break;
default:
btn = 0x8;
break;
}
}
return btn;
}
/**
* Generating an upload from data.
* @param instance Pointer to a SubGhzProtocolEncoderPrinceton instance
@@ -118,6 +223,13 @@ static bool
subghz_protocol_encoder_princeton_get_upload(SubGhzProtocolEncoderPrinceton* instance) {
furi_assert(instance);
// Generate new key using custom or default button
instance->generic.btn = subghz_protocol_princeton_get_btn_code();
// Reconstruction of the data
instance->generic.data =
((uint64_t)instance->generic.serial << 4 | (uint64_t)instance->generic.btn);
size_t index = 0;
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
if(size_upload > instance->encoder.size_upload) {
@@ -151,6 +263,21 @@ static bool
return true;
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_princeton_check_remote_controller(SubGhzBlockGeneric* instance) {
instance->serial = instance->data >> 4;
instance->btn = instance->data & 0xF;
// Save original button for later use
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(instance->btn);
}
subghz_custom_btn_set_max(4);
}
SubGhzProtocolStatus
subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
@@ -188,10 +315,26 @@ SubGhzProtocolStatus
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
// Get button and serial before calling get_upload
subghz_protocol_princeton_check_remote_controller(&instance->generic);
if(!subghz_protocol_encoder_princeton_get_upload(instance)) {
ret = SubGhzProtocolStatusErrorEncoderGetUpload;
break;
}
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
uint8_t key_data[sizeof(uint64_t)] = {0};
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF;
}
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Unable to add Key");
break;
}
instance->encoder.is_running = true;
} while(false);
@@ -320,15 +463,6 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t
}
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_princeton_check_remote_controller(SubGhzBlockGeneric* instance) {
instance->serial = instance->data >> 4;
instance->btn = instance->data & 0xF;
}
uint32_t subghz_protocol_decoder_princeton_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderPrinceton* instance = context;
+1
View File
@@ -51,6 +51,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = {
&ws_protocol_acurite_606tx,
&ws_protocol_acurite_609txc,
&ws_protocol_acurite_986,
&ws_protocol_bresser_3ch, // Should be before lacrosse
&ws_protocol_lacrosse_tx,
&ws_protocol_lacrosse_tx141thbv2,
&ws_protocol_oregon2,
+1
View File
@@ -67,6 +67,7 @@
#include "emos_e601x.h"
#include "acurite_5n1.h"
#include "solight_te44.h"
#include "bresser_3ch.h"
#include "pocsag.h"
#include "schrader_gg4.h"
#include "bin_raw.h"
+2 -1
View File
@@ -2491,9 +2491,10 @@ Function,+,st25r3916_write_pttsn_mem,void,"FuriHalSpiBusHandle*, uint8_t*, size_
Function,+,st25r3916_write_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t"
Function,+,st25r3916_write_test_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t"
Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*"
Function,+,storage_common_equivalent_path,_Bool,"Storage*, const char*, const char*, _Bool"
Function,+,storage_common_equivalent_path,_Bool,"Storage*, const char*, const char*"
Function,+,storage_common_exists,_Bool,"Storage*, const char*"
Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*"
Function,+,storage_common_is_subdir,_Bool,"Storage*, const char*, const char*"
Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*"
Function,+,storage_common_migrate,FS_Error,"Storage*, const char*, const char*"
Function,+,storage_common_mkdir,FS_Error,"Storage*, const char*"
1 entry status name type params
2491 Function + st25r3916_write_reg void FuriHalSpiBusHandle*, uint8_t, uint8_t
2492 Function + st25r3916_write_test_reg void FuriHalSpiBusHandle*, uint8_t, uint8_t
2493 Function + storage_common_copy FS_Error Storage*, const char*, const char*
2494 Function + storage_common_equivalent_path _Bool Storage*, const char*, const char*, _Bool Storage*, const char*, const char*
2495 Function + storage_common_exists _Bool Storage*, const char*
2496 Function + storage_common_fs_info FS_Error Storage*, const char*, uint64_t*, uint64_t*
2497 Function + storage_common_is_subdir _Bool Storage*, const char*, const char*
2498 Function + storage_common_merge FS_Error Storage*, const char*, const char*
2499 Function + storage_common_migrate FS_Error Storage*, const char*, const char*
2500 Function + storage_common_mkdir FS_Error Storage*, const char*
+10 -2
View File
@@ -978,8 +978,8 @@ Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed
Function,+,dolphin_flush,void,Dolphin*
Function,+,dolphin_get_pubsub,FuriPubSub*,Dolphin*
Function,+,dolphin_get_settings,void,"Dolphin*, DolphinSettings*"
Function,+,dolphin_set_settings,void,"Dolphin*, DolphinSettings*"
Function,+,dolphin_reload_state,void,Dolphin*
Function,+,dolphin_set_settings,void,"Dolphin*, DolphinSettings*"
Function,+,dolphin_stats,DolphinStats,Dolphin*
Function,+,dolphin_upgrade_level,void,Dolphin*
Function,-,dprintf,int,"int, const char*, ..."
@@ -1511,6 +1511,9 @@ Function,+,furi_hal_nfc_iso14443a_poller_trx_short_frame,FuriHalNfcError,FuriHal
Function,+,furi_hal_nfc_iso14443a_poller_tx_custom_parity,FuriHalNfcError,"const uint8_t*, size_t"
Function,+,furi_hal_nfc_iso14443a_rx_sdd_frame,FuriHalNfcError,"uint8_t*, size_t, size_t*"
Function,+,furi_hal_nfc_iso14443a_tx_sdd_frame,FuriHalNfcError,"const uint8_t*, size_t"
Function,+,furi_hal_nfc_iso15693_detect_mode,FuriHalNfcError,
Function,+,furi_hal_nfc_iso15693_force_1outof256,FuriHalNfcError,
Function,+,furi_hal_nfc_iso15693_force_1outof4,FuriHalNfcError,
Function,+,furi_hal_nfc_iso15693_listener_tx_sof,FuriHalNfcError,
Function,+,furi_hal_nfc_listener_enable_rx,FuriHalNfcError,
Function,+,furi_hal_nfc_listener_idle,FuriHalNfcError,
@@ -2865,6 +2868,9 @@ Function,+,nfc_iso14443a_listener_tx_custom_parity,NfcError,"Nfc*, const BitBuff
Function,+,nfc_iso14443a_poller_trx_custom_parity,NfcError,"Nfc*, const BitBuffer*, BitBuffer*, uint32_t"
Function,+,nfc_iso14443a_poller_trx_sdd_frame,NfcError,"Nfc*, const BitBuffer*, BitBuffer*, uint32_t"
Function,+,nfc_iso14443a_poller_trx_short_frame,NfcError,"Nfc*, NfcIso14443aShortFrame, BitBuffer*, uint32_t"
Function,+,nfc_iso15693_detect_mode,NfcError,Nfc*
Function,+,nfc_iso15693_force_1outof256,NfcError,Nfc*
Function,+,nfc_iso15693_force_1outof4,NfcError,Nfc*
Function,+,nfc_iso15693_listener_tx_sof,NfcError,Nfc*
Function,+,nfc_listener_alloc,NfcListener*,"Nfc*, NfcProtocol, const NfcDeviceData*"
Function,+,nfc_listener_free,void,NfcListener*
@@ -3270,9 +3276,10 @@ Function,+,st25tb_save,_Bool,"const St25tbData*, FlipperFormat*"
Function,+,st25tb_set_uid,_Bool,"St25tbData*, const uint8_t*, size_t"
Function,+,st25tb_verify,_Bool,"St25tbData*, const FuriString*"
Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*"
Function,+,storage_common_equivalent_path,_Bool,"Storage*, const char*, const char*, _Bool"
Function,+,storage_common_equivalent_path,_Bool,"Storage*, const char*, const char*"
Function,+,storage_common_exists,_Bool,"Storage*, const char*"
Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*"
Function,+,storage_common_is_subdir,_Bool,"Storage*, const char*, const char*"
Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*"
Function,+,storage_common_migrate,FS_Error,"Storage*, const char*, const char*"
Function,+,storage_common_mkdir,FS_Error,"Storage*, const char*"
@@ -3985,6 +3992,7 @@ Variable,+,I_Rpc_active_7x8,const Icon,
Variable,+,I_SDQuestion_35x43,const Icon,
Variable,+,I_SDcardFail_11x8,const Icon,
Variable,+,I_SDcardMounted_11x8,const Icon,
Variable,+,I_Sats_6x9,const Icon,
Variable,+,I_Scanning_123x52,const Icon,
Variable,+,I_SmallArrowDown_3x5,const Icon,
Variable,+,I_SmallArrowUp_3x5,const Icon,
1 entry status name type params
978 Function + dolphin_flush void Dolphin*
979 Function + dolphin_get_pubsub FuriPubSub* Dolphin*
980 Function + dolphin_get_settings void Dolphin*, DolphinSettings*
Function + dolphin_set_settings void Dolphin*, DolphinSettings*
981 Function + dolphin_reload_state void Dolphin*
982 Function + dolphin_set_settings void Dolphin*, DolphinSettings*
983 Function + dolphin_stats DolphinStats Dolphin*
984 Function + dolphin_upgrade_level void Dolphin*
985 Function - dprintf int int, const char*, ...
1511 Function + furi_hal_nfc_iso14443a_poller_tx_custom_parity FuriHalNfcError const uint8_t*, size_t
1512 Function + furi_hal_nfc_iso14443a_rx_sdd_frame FuriHalNfcError uint8_t*, size_t, size_t*
1513 Function + furi_hal_nfc_iso14443a_tx_sdd_frame FuriHalNfcError const uint8_t*, size_t
1514 Function + furi_hal_nfc_iso15693_detect_mode FuriHalNfcError
1515 Function + furi_hal_nfc_iso15693_force_1outof256 FuriHalNfcError
1516 Function + furi_hal_nfc_iso15693_force_1outof4 FuriHalNfcError
1517 Function + furi_hal_nfc_iso15693_listener_tx_sof FuriHalNfcError
1518 Function + furi_hal_nfc_listener_enable_rx FuriHalNfcError
1519 Function + furi_hal_nfc_listener_idle FuriHalNfcError
2868 Function + nfc_iso14443a_poller_trx_custom_parity NfcError Nfc*, const BitBuffer*, BitBuffer*, uint32_t
2869 Function + nfc_iso14443a_poller_trx_sdd_frame NfcError Nfc*, const BitBuffer*, BitBuffer*, uint32_t
2870 Function + nfc_iso14443a_poller_trx_short_frame NfcError Nfc*, NfcIso14443aShortFrame, BitBuffer*, uint32_t
2871 Function + nfc_iso15693_detect_mode NfcError Nfc*
2872 Function + nfc_iso15693_force_1outof256 NfcError Nfc*
2873 Function + nfc_iso15693_force_1outof4 NfcError Nfc*
2874 Function + nfc_iso15693_listener_tx_sof NfcError Nfc*
2875 Function + nfc_listener_alloc NfcListener* Nfc*, NfcProtocol, const NfcDeviceData*
2876 Function + nfc_listener_free void NfcListener*
3276 Function + st25tb_set_uid _Bool St25tbData*, const uint8_t*, size_t
3277 Function + st25tb_verify _Bool St25tbData*, const FuriString*
3278 Function + storage_common_copy FS_Error Storage*, const char*, const char*
3279 Function + storage_common_equivalent_path _Bool Storage*, const char*, const char*, _Bool Storage*, const char*, const char*
3280 Function + storage_common_exists _Bool Storage*, const char*
3281 Function + storage_common_fs_info FS_Error Storage*, const char*, uint64_t*, uint64_t*
3282 Function + storage_common_is_subdir _Bool Storage*, const char*, const char*
3283 Function + storage_common_merge FS_Error Storage*, const char*, const char*
3284 Function + storage_common_migrate FS_Error Storage*, const char*, const char*
3285 Function + storage_common_mkdir FS_Error Storage*, const char*
3992 Variable + I_SDQuestion_35x43 const Icon
3993 Variable + I_SDcardFail_11x8 const Icon
3994 Variable + I_SDcardMounted_11x8 const Icon
3995 Variable + I_Sats_6x9 const Icon
3996 Variable + I_Scanning_123x52 const Icon
3997 Variable + I_SmallArrowDown_3x5 const Icon
3998 Variable + I_SmallArrowUp_3x5 const Icon
@@ -406,6 +406,24 @@ FuriHalNfcError furi_hal_nfc_iso15693_listener_tx_sof(void) {
return FuriHalNfcErrorNone;
}
FuriHalNfcError furi_hal_nfc_iso15693_detect_mode(void) {
iso15693_parser_detect_mode(furi_hal_nfc_iso15693_listener->parser);
return FuriHalNfcErrorNone;
}
FuriHalNfcError furi_hal_nfc_iso15693_force_1outof4(void) {
iso15693_parser_force_1outof4(furi_hal_nfc_iso15693_listener->parser);
return FuriHalNfcErrorNone;
}
FuriHalNfcError furi_hal_nfc_iso15693_force_1outof256(void) {
iso15693_parser_force_1outof256(furi_hal_nfc_iso15693_listener->parser);
return FuriHalNfcErrorNone;
}
static FuriHalNfcError furi_hal_nfc_iso15693_listener_rx(
FuriHalSpiBusHandle* handle,
uint8_t* rx_data,
+10 -3
View File
@@ -2,6 +2,7 @@
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
#include <errno.h>
#pragma GCC diagnostic ignored "-Wredundant-decls"
#endif
@@ -26,6 +27,7 @@
#define configUSE_16_BIT_TICKS 0
#define configMAX_PRIORITIES (32)
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
#define configUSE_POSIX_ERRNO 1
/* Heap size determined automatically by linker */
// #define configTOTAL_HEAP_SIZE ((size_t)0)
@@ -146,9 +148,14 @@ standard names. */
#define configOVERRIDE_DEFAULT_TICK_CONFIGURATION \
1 /* required only for Keil but does not hurt otherwise */
#define traceTASK_SWITCHED_IN() \
extern void furi_hal_mpu_set_stack_protection(uint32_t* stack); \
furi_hal_mpu_set_stack_protection((uint32_t*)pxCurrentTCB->pxStack)
#define traceTASK_SWITCHED_IN() \
extern void furi_hal_mpu_set_stack_protection(uint32_t* stack); \
furi_hal_mpu_set_stack_protection((uint32_t*)pxCurrentTCB->pxStack); \
errno = pxCurrentTCB->iTaskErrno
// ^^^^^ acquire errno directly from TCB because FreeRTOS assigns its `FreeRTOS_errno' _after_ our hook is called
// referencing `FreeRTOS_errno' here vvvvv because FreeRTOS calls our hook _before_ copying the value into the TCB, hence a manual write to the TCB would get overwritten
#define traceTASK_SWITCHED_OUT() FreeRTOS_errno = errno
#define portCLEAN_UP_TCB(pxTCB) \
extern void furi_thread_cleanup_tcb_event(TaskHandle_t task); \
+18
View File
@@ -452,6 +452,24 @@ FuriHalNfcError furi_hal_nfc_iso14443a_listener_tx_custom_parity(
*/
FuriHalNfcError furi_hal_nfc_iso15693_listener_tx_sof(void);
/** Set ISO15693 parser mode to autodetect
*
* @return FuriHalNfcError
*/
FuriHalNfcError furi_hal_nfc_iso15693_detect_mode(void);
/** Set ISO15693 parser mode to 1OutOf4, disables autodetection
*
* @return FuriHalNfcError
*/
FuriHalNfcError furi_hal_nfc_iso15693_force_1outof4(void);
/** Set ISO15693 parser mode to 1OutOf256, disables autodetection
*
* @return FuriHalNfcError
*/
FuriHalNfcError furi_hal_nfc_iso15693_force_1outof256(void);
/**
* @brief Set FeliCa collision resolution parameters in listener mode.
*