diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c840f37f..acaef9411 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/bresser_3ch.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/bresser_3ch.sub new file mode 100644 index 000000000..6a64916d7 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/bresser_3ch.sub @@ -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 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/bresser_3ch_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/bresser_3ch_raw.sub new file mode 100644 index 000000000..b13775385 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/bresser_3ch_raw.sub @@ -0,0 +1,6 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok270Async +Protocol: RAW +RAW_Data: 268 -704 756 -708 760 -704 756 -716 260 -464 272 -464 268 -460 512 -224 268 -460 512 -224 508 -228 500 -228 264 -472 264 -468 504 -228 264 -472 256 -476 504 -232 496 -232 260 -480 256 -472 256 -480 256 -480 252 -472 508 -228 508 -216 268 -468 268 -468 256 -468 512 -224 268 -460 268 -468 264 -464 264 -472 264 -472 256 -476 504 -232 260 -468 260 -476 260 -476 500 -232 256 -480 256 -472 504 -232 256 -956 756 -704 760 -708 752 -712 752 -716 260 -472 256 -476 260 -476 500 -228 260 -480 500 -228 500 -236 500 -232 260 -472 260 -476 504 -220 264 -472 264 -472 504 -220 512 -224 268 -460 268 -464 268 -468 260 -468 264 -472 508 -224 504 -228 264 -472 256 -472 264 -476 496 -232 260 -476 260 -476 252 -480 260 -472 260 -468 264 -472 508 -212 272 -464 268 -468 268 -460 512 -224 268 -460 264 -472 508 -228 256 -964 752 -716 748 -716 752 -720 744 -716 264 -460 268 -464 272 -456 516 -220 268 -468 504 -224 512 -224 508 -220 264 -472 264 -472 500 -232 260 -476 256 -476 500 -232 504 -232 260 -472 256 -480 256 -468 264 -472 264 -468 504 -224 512 -220 268 -460 268 -468 268 -464 508 -224 264 -472 264 -464 260 -476 260 -476 252 -480 256 -480 500 -228 256 -480 260 -476 252 -476 504 -232 260 -464 264 -472 512 -224 256 -956 752 -712 760 -704 756 -708 752 -720 260 -472 256 -476 260 -472 504 -232 256 -480 496 -232 504 -232 500 -228 260 -476 260 -476 500 -224 264 -468 268 -460 516 -220 512 -224 268 -456 272 -464 272 -456 268 -468 268 -468 500 -228 508 -228 264 -464 264 -472 260 -476 500 -232 256 -480 256 -476 252 -480 260 -476 256 -472 260 -476 504 -224 260 -476 260 -472 260 -464 512 -224 268 -456 268 -468 516 -220 260 -960 756 -708 752 -716 748 -724 740 -724 256 -476 256 -476 260 -476 500 -228 260 -476 500 -224 512 -224 508 -228 256 -468 268 -468 512 -212 268 -468 268 -468 504 -224 508 -228 268 -460 264 -472 264 -472 256 -472 264 -476 504 -224 504 -232 256 -480 252 -476 260 -480 500 -224 260 -476 260 -476 252 -472 268 -464 268 -460 268 -464 516 -220 260 -468 264 -472 264 -468 504 -228 264 -476 252 -476 504 -232 260 -960 748 -720 744 -724 744 -720 744 -716 264 -472 256 -468 268 -464 508 -220 268 -468 516 -220 504 -224 512 -224 268 -460 264 -472 508 -228 256 -472 264 -472 504 -228 504 -232 256 -480 252 -476 256 -480 260 -468 260 -476 504 -232 496 -232 260 -472 264 -460 268 -468 512 -224 260 -464 268 -468 272 -456 268 -468 264 -472 256 -472 508 -228 264 -468 260 -476 260 -476 496 -232 260 -476 260 -472 504 -228 260 -960 752 -716 748 -712 752 -708 760 -704 268 -468 268 -460 264 -472 508 -228 256 -472 508 -228 508 -220 508 -228 260 -476 260 -472 500 -232 260 -472 256 -480 500 -232 496 -236 256 -480 256 -472 260 -476 256 -480 256 -464 512 -224 512 -224 260 -468 264 -468 268 -460 512 -224 268 -464 260 -472 264 -468 264 -468 260 -476 260 -472 504 -228 260 -476 260 -472 256 -480 500 -232 256 -476 260 -476 496 -228 264 -964 744 -712 752 -712 760 -700 760 -704 264 -472 264 -472 264 -468 504 -232 260 -468 504 -232 504 -228 500 -232 260 -476 260 -476 496 -232 260 -476 260 -472 500 -232 504 -232 252 -476 260 -476 260 -464 264 -468 268 -468 504 -220 516 -220 268 -460 268 -468 268 -468 500 -228 264 -472 264 -468 260 -472 264 -472 256 -476 260 -476 504 -224 260 -480 256 -480 248 -476 504 -232 260 -464 268 -468 512 -224 264 -948 760 -704 760 -704 756 -716 744 -720 260 -472 256 -480 256 -480 496 -232 256 -480 500 -228 504 -232 500 -232 260 -472 260 -476 500 -228 260 -476 256 -480 496 -228 508 -228 260 -476 256 -468 268 -464 272 -456 268 -468 516 -216 508 -220 272 -464 268 -460 268 -468 512 -224 256 -472 264 -472 264 -468 260 -472 264 -472 256 -476 504 -232 256 -476 256 -480 252 -480 496 -236 256 -476 260 -468 504 -232 260 -952 756 -716 752 -712 748 -712 760 -704 264 -468 268 -464 264 -468 512 -224 256 -476 504 -232 504 -224 504 -232 260 -472 264 -468 504 -232 256 -472 260 -476 504 -232 496 -232 260 -476 256 -480 252 -476 260 -476 260 -464 508 -228 508 -228 256 -472 264 -468 268 -460 512 -224 268 -464 264 -464 268 -468 268 -460 264 -472 264 -472 500 -228 264 -472 260 -472 256 -480 504 -228 252 -480 256 -480 500 -228 260 -968 740 -724 748 -712 752 -708 756 -716 260 -464 272 -464 268 -456 516 -220 272 -464 504 -224 516 -220 512 -216 268 -468 268 -468 500 -228 264 -472 264 -472 500 -228 504 -232 260 -468 260 -476 256 -480 252 -480 256 -476 504 -228 500 -232 260 -480 248 -480 256 -480 500 -224 260 -476 260 -472 260 -468 268 -464 268 -460 268 -468 516 -216 272 -456 268 -468 268 -460 512 -224 268 -468 264 -468 504 -228 264 -960 748 -716 748 -720 744 -720 748 -720 256 -476 260 -472 260 -476 504 -232 252 -472 508 -228 500 -228 504 -232 260 -472 256 -472 508 -228 264 -460 268 -468 512 -224 504 -224 268 -464 272 -456 268 -468 268 -468 268 -460 512 -220 516 -212 268 -468 268 -468 264 -468 504 -228 264 -472 256 -476 260 -476 260 -468 260 -476 260 -476 496 -232 260 -476 256 -476 256 -476 504 -232 252 -476 260 -476 504 -224 260 -964 744 -720 752 -708 756 -704 760 -712 260 -468 268 -464 272 -456 516 -220 268 -468 504 -224 512 -224 512 -216 268 -464 272 -464 504 -228 264 -468 268 -468 504 -224 508 -228 264 -468 260 -472 264 -472 256 -476 260 -472 508 -224 504 -228 260 -476 252 -480 260 -476 500 -228 256 -480 256 -480 256 -472 260 -476 260 -464 264 -472 508 -228 264 -460 268 -468 268 -456 516 -220 272 -464 268 -460 512 -224 268 -948 756 -708 756 -716 748 -716 752 -712 260 -476 260 -468 260 -476 504 -232 252 -476 504 -232 504 -224 504 -232 256 -480 252 -476 504 -232 256 -472 260 -476 504 -232 496 -232 260 -476 260 -476 252 -472 264 -472 264 -460 512 -228 508 -228 256 -468 268 -464 272 -456 516 -220 268 -468 260 -464 268 -472 264 -464 264 -468 264 -472 504 -228 260 -472 264 -468 260 -476 504 -232 260 -468 260 -476 504 -228 256 -1212 744 -720 752 -712 752 -708 756 -712 260 -468 268 -464 268 -460 512 -224 268 -468 504 -224 512 -224 512 -216 264 -468 268 -468 504 -228 260 -472 264 -468 504 -232 504 -228 264 -468 256 -480 260 -476 252 -476 260 -476 496 -232 504 -232 256 -480 252 -476 260 -476 504 -224 260 -476 260 -476 252 -472 264 -472 264 -460 268 -468 512 -224 268 -456 272 -464 268 -460 512 -224 268 -468 264 -464 508 -228 264 -10004 diff --git a/applications/debug/unit_tests/tests/furi/furi_errno_test.c b/applications/debug/unit_tests/tests/furi/furi_errno_test.c new file mode 100644 index 000000000..b42e7c082 --- /dev/null +++ b/applications/debug/unit_tests/tests/furi/furi_errno_test.c @@ -0,0 +1,51 @@ +#include +#include +#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]); + } +} diff --git a/applications/debug/unit_tests/tests/furi/furi_test.c b/applications/debug/unit_tests/tests/furi/furi_test.c index be579d2b8..2a76d5184 100644 --- a/applications/debug/unit_tests/tests/furi/furi_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_test.c @@ -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) { diff --git a/applications/debug/unit_tests/tests/storage/storage_test.c b/applications/debug/unit_tests/tests/storage/storage_test.c index f317fbf68..75c52ef9a 100644 --- a/applications/debug/unit_tests/tests/storage/storage_test.c +++ b/applications/debug/unit_tests/tests/storage/storage_test.c @@ -6,9 +6,10 @@ // This is a hack to access internal storage functions and definitions #include -#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, diff --git a/applications/debug/unit_tests/tests/subghz/subghz_test.c b/applications/debug/unit_tests/tests/subghz/subghz_test.c index 5888d4e21..752fcec2e 100644 --- a/applications/debug/unit_tests/tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/tests/subghz/subghz_test.c @@ -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( diff --git a/applications/external b/applications/external index 2f67c3c91..4915916f6 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 2f67c3c91826ff3147d57276a64ce4bdf00a3597 +Subproject commit 4915916f6f0dead861cb5b48948ff9b559861947 diff --git a/applications/main/gpio/scenes/gpio_scene_config.h b/applications/main/gpio/scenes/gpio_scene_config.h index d2a98a847..faca22d8d 100644 --- a/applications/main/gpio/scenes/gpio_scene_config.h +++ b/applications/main/gpio/scenes/gpio_scene_config.h @@ -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) diff --git a/applications/main/gpio/scenes/gpio_scene_exit_confirm.c b/applications/main/gpio/scenes/gpio_scene_exit_confirm.c deleted file mode 100644 index efb0734a3..000000000 --- a/applications/main/gpio/scenes/gpio_scene_exit_confirm.c +++ /dev/null @@ -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); -} diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart.c b/applications/main/gpio/scenes/gpio_scene_usb_uart.c index 9a3514ca4..e3e7e8c24 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart.c @@ -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); } diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 08eb19eaf..cdb215334 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -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, diff --git a/applications/main/nfc/plugins/supported_cards/smartrider.c b/applications/main/nfc/plugins/supported_cards/smartrider.c new file mode 100644 index 000000000..8cfda0660 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/smartrider.c @@ -0,0 +1,334 @@ +#include "nfc_supported_card_plugin.h" +#include +#include +#include +#include +#include + +#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 diff --git a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc index d62bb6e89..c20f3691b 100644 --- a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc @@ -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 | # +---------------------------------------------------------------------+ diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index 9ce344c00..bdab0a434 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -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); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index a0946b313..ce824fed0 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -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); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index ccc6e3ecb..eefa798ed 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -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; diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index d7f53d78f..c64cf01c0 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -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 * diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 232ff9be4..af4a68f1d 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -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; } diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index 941ea2e92..21faa41d6 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -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( diff --git a/applications/services/storage/storage.h b/applications/services/storage/storage.h index 1fd99d30a..931b39c91 100644 --- a/applications/services/storage/storage.h +++ b/applications/services/storage/storage.h @@ -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 *******************/ diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index 7a08424ba..95119561c 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -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) { diff --git a/applications/services/storage/storage_message.h b/applications/services/storage/storage_message.h index 31910fde3..cbd095144 100644 --- a/applications/services/storage/storage_message.h +++ b/applications/services/storage/storage_message.h @@ -77,7 +77,7 @@ typedef struct { typedef struct { const char* path1; const char* path2; - bool truncate; + bool check_subdir; FuriThreadId thread_id; } SADataCEquivPath; diff --git a/applications/services/storage/storage_processing.c b/applications/services/storage/storage_processing.c index b5ba5b1ac..642d986a4 100644 --- a/applications/services/storage/storage_processing.c +++ b/applications/services/storage/storage_processing.c @@ -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 = diff --git a/assets/icons/SubGhz/Sats_6x9.png b/assets/icons/SubGhz/Sats_6x9.png new file mode 100644 index 000000000..cbb6c102b Binary files /dev/null and b/assets/icons/SubGhz/Sats_6x9.png differ diff --git a/documentation/CustomFlipperName.md b/documentation/CustomFlipperName.md new file mode 100644 index 000000000..048283e44 --- /dev/null +++ b/documentation/CustomFlipperName.md @@ -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) \ No newline at end of file diff --git a/documentation/InfraredCaptures.md b/documentation/InfraredCaptures.md new file mode 100644 index 000000000..4f23e6570 --- /dev/null +++ b/documentation/InfraredCaptures.md @@ -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. diff --git a/documentation/MultiConverter.md b/documentation/MultiConverter.md new file mode 100644 index 000000000..9e27f40f0 --- /dev/null +++ b/documentation/MultiConverter.md @@ -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) diff --git a/documentation/NRF24.md b/documentation/NRF24.md new file mode 100644 index 000000000..9d6e7c60a --- /dev/null +++ b/documentation/NRF24.md @@ -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 + + +# Mousejacker / NRF24 pinout by UberGuidoZ +2/A7 on FZ goes to MOSI/6 on nrf24l01
+3/A6 on FZ goes to MISO/7 on nrf24l01
+4/A4 on FZ goes to CSN/4 on nrf24l01
+5/B3 on FZ goes to SCK/5 on nrf24l01
+6/B2 on FZ goes to CE/3 on nrf24l01
+8/GND on FZ goes to GND/1 on nrf24l01
+9/3V3 on FZ goes to VCC/2 on nrf24l01
+IRQ/8 is left disconnected on nrf24l01
+ +![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) diff --git a/documentation/SentrySafe.md b/documentation/SentrySafe.md new file mode 100644 index 000000000..7c55ef22d --- /dev/null +++ b/documentation/SentrySafe.md @@ -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 +
(Flipper GPIO) 8/GND -> Black wire (Safe) +
(Flipper GPIO) 15/C1 -> Green wire (Safe) + +- Press enter +- Open safe diff --git a/documentation/SubGHzBypass&Extend.md b/documentation/SubGHzBypass&Extend.md new file mode 100644 index 000000000..0266e274b --- /dev/null +++ b/documentation/SubGHzBypass&Extend.md @@ -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`. \ No newline at end of file diff --git a/documentation/SubGHzRemotePlugin.md b/documentation/SubGHzRemotePlugin.md new file mode 100644 index 000000000..39edf5bc9 --- /dev/null +++ b/documentation/SubGHzRemotePlugin.md @@ -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 + +
+
+
+ + +### 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. diff --git a/documentation/SubGHzRemoteProg.md b/documentation/SubGHzRemoteProg.md new file mode 100644 index 000000000..ac95edaab --- /dev/null +++ b/documentation/SubGHzRemoteProg.md @@ -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) diff --git a/documentation/SubGHzSettings.md b/documentation/SubGHzSettings.md new file mode 100644 index 000000000..770c3b84e --- /dev/null +++ b/documentation/SubGHzSettings.md @@ -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`
+But remember! You should keep it as small as possible, or hopper functionality would be useless!
+If `#Add_standard_frequencies: true` is not changed
+Your frequencies will be added after default ones + +### Default hopper list +``` + 310000000, + 315000000, + 318000000, + 418000000, + 433920000, + 868350000, +``` diff --git a/documentation/file_formats/SubGhzFileFormats.md b/documentation/file_formats/SubGhzFileFormats.md index c4d63835e..80047faf7 100644 --- a/documentation/file_formats/SubGhzFileFormats.md +++ b/documentation/file_formats/SubGhzFileFormats.md @@ -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 ... diff --git a/documentation/file_formats/iButtonFileFormat.md b/documentation/file_formats/iButtonFileFormat.md index 414d73045..01919b9c6 100644 --- a/documentation/file_formats/iButtonFileFormat.md +++ b/documentation/file_formats/iButtonFileFormat.md @@ -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. diff --git a/furi/core/kernel.h b/furi/core/kernel.h index 2973a90e5..8362746e2 100644 --- a/furi/core/kernel.h +++ b/furi/core/kernel.h @@ -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. */ diff --git a/lib/nfc/nfc.c b/lib/nfc/nfc.c index 90e65b282..4f4358711 100644 --- a/lib/nfc/nfc.c +++ b/lib/nfc/nfc.c @@ -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, diff --git a/lib/nfc/nfc.h b/lib/nfc/nfc.h index 8fbf90d1f..ebd29dc4b 100644 --- a/lib/nfc/nfc.h +++ b/lib/nfc/nfc.h @@ -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 diff --git a/lib/signal_reader/parsers/iso15693/iso15693_parser.c b/lib/signal_reader/parsers/iso15693/iso15693_parser.c index a2c6912e6..f4065e497 100644 --- a/lib/signal_reader/parsers/iso15693/iso15693_parser.c +++ b/lib/signal_reader/parsers/iso15693/iso15693_parser.c @@ -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; +} diff --git a/lib/signal_reader/parsers/iso15693/iso15693_parser.h b/lib/signal_reader/parsers/iso15693/iso15693_parser.h index 3017a96d7..66486e0e7 100644 --- a/lib/signal_reader/parsers/iso15693/iso15693_parser.h +++ b/lib/signal_reader/parsers/iso15693/iso15693_parser.h @@ -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 diff --git a/lib/subghz/protocols/bresser_3ch.c b/lib/subghz/protocols/bresser_3ch.c new file mode 100644 index 000000000..70957081c --- /dev/null +++ b/lib/subghz/protocols/bresser_3ch.c @@ -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); +} diff --git a/lib/subghz/protocols/bresser_3ch.h b/lib/subghz/protocols/bresser_3ch.h new file mode 100644 index 000000000..c0235e720 --- /dev/null +++ b/lib/subghz/protocols/bresser_3ch.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#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); diff --git a/lib/subghz/protocols/gangqi.c b/lib/subghz/protocols/gangqi.c index a2195dd33..b5917f449 100644 --- a/lib/subghz/protocols/gangqi.c +++ b/lib/subghz/protocols/gangqi.c @@ -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, diff --git a/lib/subghz/protocols/hollarm.c b/lib/subghz/protocols/hollarm.c index 292b7054a..114bacb46 100644 --- a/lib/subghz/protocols/hollarm.c +++ b/lib/subghz/protocols/hollarm.c @@ -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]; diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index b63ff3e0b..b14a0ee15 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -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; diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 42d87bd8d..c6a1c08f5 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -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, diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 12ce8bd42..48e72cc44 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -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" diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 553cf1472..63ab4a477 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -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*" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 5a9b9cff7..507084a43 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -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, diff --git a/targets/f7/furi_hal/furi_hal_nfc_iso15693.c b/targets/f7/furi_hal/furi_hal_nfc_iso15693.c index 3245b67cc..0fd5dbca5 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_iso15693.c +++ b/targets/f7/furi_hal/furi_hal_nfc_iso15693.c @@ -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, diff --git a/targets/f7/inc/FreeRTOSConfig.h b/targets/f7/inc/FreeRTOSConfig.h index 2948faef9..82cda2c6d 100644 --- a/targets/f7/inc/FreeRTOSConfig.h +++ b/targets/f7/inc/FreeRTOSConfig.h @@ -2,6 +2,7 @@ #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) #include +#include #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); \ diff --git a/targets/furi_hal_include/furi_hal_nfc.h b/targets/furi_hal_include/furi_hal_nfc.h index 3a81de6f5..a651c9777 100644 --- a/targets/furi_hal_include/furi_hal_nfc.h +++ b/targets/furi_hal_include/furi_hal_nfc.h @@ -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. *