From 8ec5527ae4a486608e4003b51e21b41045daf7fd Mon Sep 17 00:00:00 2001 From: hedger Date: Sun, 16 Oct 2022 21:11:27 +0400 Subject: [PATCH 01/42] fbt: fix for cincludes in app's private library definition (#1882) --- scripts/fbt_tools/fbt_extapps.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 34fb942a..9f81d414 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -80,10 +80,7 @@ def BuildAppElf(env, app): *lib_def.cflags, ], CPPDEFINES=lib_def.cdefines, - CPPPATH=list( - os.path.join(app._appdir.path, cinclude) - for cinclude in lib_def.cincludes - ), + CPPPATH=list(map(app._appdir.Dir, lib_def.cincludes)), ) lib = private_lib_env.StaticLibrary( From e7aaf3dbb2f86ec6cb51b0be34095411274182ab Mon Sep 17 00:00:00 2001 From: Ivan Podogov Date: Mon, 17 Oct 2022 16:17:04 +0100 Subject: [PATCH 02/42] Enable all `view_` methods in SDK (#1884) --- firmware/targets/f7/api_symbols.csv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index c9e2371f..33e28994 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,2.2,, +Version,+,2.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2511,17 +2511,17 @@ Function,+,view_port_alloc,ViewPort*, Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" Function,+,view_port_free,void,ViewPort* -Function,-,view_port_get_height,uint8_t,ViewPort* +Function,+,view_port_get_height,uint8_t,ViewPort* Function,+,view_port_get_orientation,ViewPortOrientation,const ViewPort* Function,+,view_port_get_width,uint8_t,ViewPort* Function,+,view_port_input_callback_set,void,"ViewPort*, ViewPortInputCallback, void*" Function,+,view_port_is_enabled,_Bool,ViewPort* -Function,-,view_port_set_height,void,"ViewPort*, uint8_t" +Function,+,view_port_set_height,void,"ViewPort*, uint8_t" Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" Function,+,view_port_set_width,void,"ViewPort*, uint8_t" Function,+,view_port_update,void,ViewPort* Function,+,view_set_context,void,"View*, void*" -Function,-,view_set_custom_callback,void,"View*, ViewCustomCallback" +Function,+,view_set_custom_callback,void,"View*, ViewCustomCallback" Function,+,view_set_draw_callback,void,"View*, ViewDrawCallback" Function,+,view_set_enter_callback,void,"View*, ViewCallback" Function,+,view_set_exit_callback,void,"View*, ViewCallback" From dfbe21e7203688d33a7c002a4a19fc4714469aa6 Mon Sep 17 00:00:00 2001 From: gornekich Date: Mon, 17 Oct 2022 21:10:41 +0400 Subject: [PATCH 03/42] NFC fixes part 3 (#1885) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc: fix read next key * nfc: verify new line ending in user dictionary file * nfc: fix cache save * nfc: add unit test for dict load * nfc: fix total key count in dictionary Co-authored-by: あく --- applications/debug/unit_tests/nfc/nfc_test.c | 68 ++++++++++++++++++++ lib/nfc/helpers/mf_classic_dict.c | 22 +++++-- lib/nfc/nfc_device.c | 2 +- 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index f149508b..8009f6a1 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -16,6 +16,7 @@ #define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/") #define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc" #define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc" +#define NFC_TEST_DICT_PATH EXT_PATH("unit_tests/mf_classic_dict.nfc") static const char* nfc_test_file_type = "Flipper NFC test"; static const uint32_t nfc_test_file_version = 1; @@ -220,11 +221,78 @@ MU_TEST(mf_classic_dict_test) { furi_string_free(temp_str); } +MU_TEST(mf_classic_dict_load_test) { + Storage* storage = furi_record_open(RECORD_STORAGE); + mu_assert(storage != NULL, "storage != NULL assert failed\r\n"); + + // Delete unit test dict file if exists + if(storage_file_exists(storage, NFC_TEST_DICT_PATH)) { + mu_assert( + storage_simply_remove(storage, NFC_TEST_DICT_PATH), + "remove == true assert failed\r\n"); + } + + // Create unit test dict file + Stream* file_stream = file_stream_alloc(storage); + mu_assert(file_stream != NULL, "file_stream != NULL assert failed\r\n"); + mu_assert( + file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS), + "file_stream_open == true assert failed\r\n"); + + // Write unit test dict file + char key_str[] = "a0a1a2a3a4a5"; + mu_assert( + stream_write_cstring(file_stream, key_str) == strlen(key_str), + "write == true assert failed\r\n"); + // Close unit test dict file + mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n"); + + // Load unit test dict file + MfClassicDict* instance = NULL; + instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest); + mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n"); + uint32_t total_keys = mf_classic_dict_get_total_keys(instance); + mu_assert(total_keys == 1, "total_keys == 1 assert failed\r\n"); + + // Read key + uint64_t key_ref = 0xa0a1a2a3a4a5; + uint64_t key_dut = 0; + FuriString* temp_str = furi_string_alloc(); + mu_assert( + mf_classic_dict_get_next_key_str(instance, temp_str), + "get_next_key_str == true assert failed\r\n"); + mu_assert(furi_string_cmp_str(temp_str, key_str) == 0, "invalid key loaded\r\n"); + mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n"); + mu_assert( + mf_classic_dict_get_next_key(instance, &key_dut), + "get_next_key == true assert failed\r\n"); + mu_assert(key_dut == key_ref, "invalid key loaded\r\n"); + furi_string_free(temp_str); + mf_classic_dict_free(instance); + + // Check that MfClassicDict added new line to the end of the file + mu_assert( + file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_READ, FSOM_OPEN_EXISTING), + "file_stream_open == true assert failed\r\n"); + mu_assert(stream_seek(file_stream, -1, StreamOffsetFromEnd), "seek == true assert failed\r\n"); + uint8_t last_char = 0; + mu_assert(stream_read(file_stream, &last_char, 1) == 1, "read == true assert failed\r\n"); + mu_assert(last_char == '\n', "last_char == '\\n' assert failed\r\n"); + mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n"); + + // Delete unit test dict file + mu_assert( + storage_simply_remove(storage, NFC_TEST_DICT_PATH), "remove == true assert failed\r\n"); + stream_free(file_stream); + furi_record_close(RECORD_STORAGE); +} + MU_TEST_SUITE(nfc) { nfc_test_alloc(); MU_RUN_TEST(nfc_digital_signal_test); MU_RUN_TEST(mf_classic_dict_test); + MU_RUN_TEST(mf_classic_dict_load_test); nfc_test_free(); } diff --git a/lib/nfc/helpers/mf_classic_dict.c b/lib/nfc/helpers/mf_classic_dict.c index a842ed92..690bba61 100644 --- a/lib/nfc/helpers/mf_classic_dict.c +++ b/lib/nfc/helpers/mf_classic_dict.c @@ -44,7 +44,10 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { do { if(dict_type == MfClassicDictTypeFlipper) { if(!buffered_file_stream_open( - dict->stream, MF_CLASSIC_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + dict->stream, + MF_CLASSIC_DICT_FLIPPER_PATH, + FSAM_READ_WRITE, + FSOM_OPEN_EXISTING)) { buffered_file_stream_close(dict->stream); break; } @@ -59,12 +62,24 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { dict->stream, MF_CLASSIC_DICT_UNIT_TEST_PATH, FSAM_READ_WRITE, - FSOM_CREATE_ALWAYS)) { + FSOM_OPEN_ALWAYS)) { buffered_file_stream_close(dict->stream); break; } } + // Check for new line ending + if(!stream_eof(dict->stream)) { + if(!stream_seek(dict->stream, -1, StreamOffsetFromEnd)) break; + uint8_t last_char = 0; + if(stream_read(dict->stream, &last_char, 1) != 1) break; + if(last_char != '\n') { + FURI_LOG_D(TAG, "Adding new line ending"); + if(stream_write_char(dict->stream, '\n') != 1) break; + } + if(!stream_rewind(dict->stream)) break; + } + // Read total amount of keys FuriString* next_line; next_line = furi_string_alloc(); @@ -73,14 +88,13 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { FURI_LOG_T(TAG, "No keys left in dict"); break; } - furi_string_trim(next_line); FURI_LOG_T( TAG, "Read line: %s, len: %d", furi_string_get_cstr(next_line), furi_string_size(next_line)); if(furi_string_get_char(next_line, 0) == '#') continue; - if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN - 1) continue; + if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; dict->total_keys++; } furi_string_free(next_line); diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 06d57a0c..740cfae5 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -921,7 +921,7 @@ static bool nfc_device_save_mifare_classic_keys(NfcDevice* dev) { file, furi_string_get_cstr(temp_str), sec_tr->key_a, 6); } if(!key_save_success) break; - if(FURI_BIT(data->key_a_mask, i)) { + if(FURI_BIT(data->key_b_mask, i)) { furi_string_printf(temp_str, "Key B sector %d", i); key_save_success = flipper_format_write_hex( file, furi_string_get_cstr(temp_str), sec_tr->key_b, 6); From 5e35e51c578d938e2b3fb3dee476d88c405b0105 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Mon, 17 Oct 2022 20:49:00 +0300 Subject: [PATCH 04/42] [FL-2907] Remove the back button from MFC keys list #1878 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c index a2e6ae74..54cc18d3 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -34,8 +34,6 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) { widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); widget_add_button_element( nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); - widget_add_button_element( - nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_mf_classic_keys_widget_callback, nfc); widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36); if(user_dict_keys_total > 0) { widget_add_button_element( @@ -57,9 +55,6 @@ bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) if(event.event == GuiButtonTypeCenter) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd); consumed = true; - } else if(event.event == GuiButtonTypeLeft) { - scene_manager_previous_scene(nfc->scene_manager); - consumed = true; } else if(event.event == GuiButtonTypeRight) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList); consumed = true; From f61a8fda536656ee4900e24ed1b5a029b0de533f Mon Sep 17 00:00:00 2001 From: Travis Montoya <99630881+sqlsquirreltm@users.noreply.github.com> Date: Mon, 17 Oct 2022 12:07:05 -0600 Subject: [PATCH 05/42] Feature/infrared add remote to cli (#1856) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial testing of remote using cli * More fixes for cli ir remote * Fixes. Turns off power now * Finished adding other tv remote commands * Changed if-else formatting * Cleaned up unused variables * Updating cli unviersal remote to accept tv, ac and more modular. Listing signals still does not work properly * Using mlib dictionary to get unique signals from files for ir universal list * Fixing progress bar * Added error checking for invalid signal to stop freezing cli * Added error checking for arg length * Api symbols was changed somehow.. changed back and updated the argument check to account for newline * Fixing string compares and argument length issue * Freeing InfraredBruteForce in cli brute force signals Co-authored-by: sqlsquirreltm Co-authored-by: あく --- applications/main/infrared/infrared_cli.c | 178 ++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 5ec57c75..54e3e251 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,12 +7,24 @@ #include #include "infrared_signal.h" +#include "infrared_brute_force.h" + +#include "m-dict.h" +#include "m-string.h" #define INFRARED_CLI_BUF_SIZE 10 +DICT_DEF2(dict_signals, string_t, STRING_OPLIST, int, M_DEFAULT_OPLIST) + +enum RemoteTypes { TV = 0, AC = 1 }; + static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args); static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args); static void infrared_cli_process_decode(Cli* cli, FuriString* args); +static void infrared_cli_process_universal(Cli* cli, FuriString* args); +static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type); +static void + infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal); static const struct { const char* cmd; @@ -20,6 +33,7 @@ static const struct { {.cmd = "rx", .process_function = infrared_cli_start_ir_rx}, {.cmd = "tx", .process_function = infrared_cli_start_ir_tx}, {.cmd = "decode", .process_function = infrared_cli_process_decode}, + {.cmd = "universal", .process_function = infrared_cli_process_universal}, }; static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { @@ -90,6 +104,8 @@ static void infrared_cli_print_usage(void) { INFRARED_MIN_FREQUENCY, INFRARED_MAX_FREQUENCY); printf("\tir decode []\r\n"); + printf("\tir universal \r\n"); + printf("\tir universal list \r\n"); } static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { @@ -328,6 +344,168 @@ static void infrared_cli_process_decode(Cli* cli, FuriString* args) { furi_record_close(RECORD_STORAGE); } +static void infrared_cli_process_universal(Cli* cli, FuriString* args) { + enum RemoteTypes Remote; + + FuriString* command; + FuriString* remote; + FuriString* signal; + command = furi_string_alloc(); + remote = furi_string_alloc(); + signal = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, command)) { + infrared_cli_print_usage(); + break; + } + + if(furi_string_cmp_str(command, "list") == 0) { + args_read_string_and_trim(args, remote); + if(furi_string_cmp_str(remote, "tv") == 0) { + Remote = TV; + } else if(furi_string_cmp_str(remote, "ac") == 0) { + Remote = AC; + } else { + printf("Invalid remote type.\r\n"); + break; + } + infrared_cli_list_remote_signals(Remote); + break; + } + + if(furi_string_cmp_str(command, "tv") == 0) { + Remote = TV; + } else if(furi_string_cmp_str(command, "ac") == 0) { + Remote = AC; + } else { + printf("Invalid remote type.\r\n"); + break; + } + + args_read_string_and_trim(args, signal); + if(furi_string_empty(signal)) { + printf("Must supply a valid signal for type of remote selected.\r\n"); + break; + } + + infrared_cli_brute_force_signals(cli, Remote, signal); + break; + + } while(false); + + furi_string_free(command); + furi_string_free(remote); + furi_string_free(signal); +} + +static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + dict_signals_t signals_dict; + string_t key; + const char* remote_file = NULL; + bool success = false; + int max = 1; + + switch(remote_type) { + case TV: + remote_file = EXT_PATH("infrared/assets/tv.ir"); + break; + case AC: + remote_file = EXT_PATH("infrared/assets/ac.ir"); + break; + default: + break; + } + + dict_signals_init(signals_dict); + string_init(key); + + success = flipper_format_buffered_file_open_existing(ff, remote_file); + if(success) { + FuriString* signal_name; + signal_name = furi_string_alloc(); + printf("Valid signals:\r\n"); + while(flipper_format_read_string(ff, "name", signal_name)) { + string_set_str(key, furi_string_get_cstr(signal_name)); + int* v = dict_signals_get(signals_dict, key); + if(v != NULL) { + (*v)++; + max = M_MAX(*v, max); + } else { + dict_signals_set_at(signals_dict, key, 1); + } + } + dict_signals_it_t it; + for(dict_signals_it(it, signals_dict); !dict_signals_end_p(it); dict_signals_next(it)) { + const struct dict_signals_pair_s* pair = dict_signals_cref(it); + printf("\t%s\r\n", string_get_cstr(pair->key)); + } + furi_string_free(signal_name); + } + + string_clear(key); + dict_signals_clear(signals_dict); + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); +} + +static void + infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal) { + InfraredBruteForce* brute_force = infrared_brute_force_alloc(); + const char* remote_file = NULL; + uint32_t i = 0; + bool success = false; + + switch(remote_type) { + case TV: + remote_file = EXT_PATH("infrared/assets/tv.ir"); + break; + case AC: + remote_file = EXT_PATH("infrared/assets/ac.ir"); + break; + default: + break; + } + + infrared_brute_force_set_db_filename(brute_force, remote_file); + infrared_brute_force_add_record(brute_force, i++, furi_string_get_cstr(signal)); + + success = infrared_brute_force_calculate_messages(brute_force); + if(success) { + uint32_t record_count; + uint32_t index = 0; + int records_sent = 0; + bool running = false; + + running = infrared_brute_force_start(brute_force, index, &record_count); + if(record_count <= 0) { + printf("Invalid signal.\n"); + infrared_brute_force_reset(brute_force); + return; + } + + printf("Sending %ld codes to the tv.\r\n", record_count); + printf("Press Ctrl-C to stop.\r\n"); + while(running) { + running = infrared_brute_force_send_next(brute_force); + + if(cli_cmd_interrupt_received(cli)) break; + + printf("\r%d%% complete.", (int)((float)records_sent++ / (float)record_count * 100)); + fflush(stdout); + } + + infrared_brute_force_stop(brute_force); + } else { + printf("Invalid signal.\r\n"); + } + + infrared_brute_force_reset(brute_force); + infrared_brute_force_free(brute_force); +} + static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) { UNUSED(context); if(furi_hal_infrared_is_busy()) { From 4942bd2105aa5690d9736dfd0a13efc81ff45aea Mon Sep 17 00:00:00 2001 From: hedger Date: Tue, 18 Oct 2022 16:13:28 +0400 Subject: [PATCH 06/42] scripts: fixed c2 bundle format (#1889) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * scripts: fixed c2 bundle format * scripts: copro.py: small refactoring Co-authored-by: あく --- scripts/flipper/assets/copro.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/flipper/assets/copro.py b/scripts/flipper/assets/copro.py index d39f3033..b61ac032 100644 --- a/scripts/flipper/assets/copro.py +++ b/scripts/flipper/assets/copro.py @@ -3,6 +3,8 @@ import json from io import BytesIO import tarfile import xml.etree.ElementTree as ET +import posixpath +import os from flipper.utils import * from flipper.assets.coprobin import CoproBinary, get_stack_type @@ -23,6 +25,8 @@ MANIFEST_TEMPLATE = { class Copro: + COPRO_TAR_DIR = "core2_firmware" + def __init__(self, mcu): self.mcu = mcu self.version = None @@ -50,9 +54,8 @@ class Copro: raise Exception(f"Unsupported cube version") self.version = cube_version - @staticmethod - def _getFileName(name): - return os.path.join("core2_firmware", name) + def _getFileName(self, name): + return posixpath.join(self.COPRO_TAR_DIR, name) def addFile(self, array, filename, **kwargs): source_file = os.path.join(self.mcu_copro, filename) @@ -61,6 +64,9 @@ class Copro: def bundle(self, output_file, stack_file_name, stack_type, stack_addr=None): self.output_tar = tarfile.open(output_file, "w:gz", format=tarfile.USTAR_FORMAT) + fw_directory = tarfile.TarInfo(self.COPRO_TAR_DIR) + fw_directory.type = tarfile.DIRTYPE + self.output_tar.addfile(fw_directory) stack_file = os.path.join(self.mcu_copro, stack_file_name) # Form Manifest From 02c27becb0d682c6ab757e9f7fa6774e1e5d783e Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Tue, 18 Oct 2022 17:10:21 +0300 Subject: [PATCH 07/42] [FL-2912] Forced RAW receive option for Infrared CLI #1891 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/infrared/infrared_cli.c | 47 +++++++++++++++-------- lib/infrared/worker/infrared_worker.c | 10 ++++- lib/infrared/worker/infrared_worker.h | 8 ++++ 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 54e3e251..7723dc97 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -71,25 +71,9 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv } } -static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { - UNUSED(cli); - UNUSED(args); - InfraredWorker* worker = infrared_worker_alloc(); - infrared_worker_rx_start(worker); - infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); - - printf("Receiving INFRARED...\r\nPress Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(50); - } - - infrared_worker_rx_stop(worker); - infrared_worker_free(worker); -} - static void infrared_cli_print_usage(void) { printf("Usage:\r\n"); - printf("\tir rx\r\n"); + printf("\tir rx [raw]\r\n"); printf("\tir tx
\r\n"); printf("\t and
are hex-formatted\r\n"); printf("\tAvailable protocols:"); @@ -108,6 +92,35 @@ static void infrared_cli_print_usage(void) { printf("\tir universal list \r\n"); } +static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { + UNUSED(cli); + + bool enable_decoding = true; + + if(!furi_string_empty(args)) { + if(!furi_string_cmp_str(args, "raw")) { + enable_decoding = false; + } else { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + return; + } + } + + InfraredWorker* worker = infrared_worker_alloc(); + infrared_worker_rx_enable_signal_decoding(worker, enable_decoding); + infrared_worker_rx_start(worker); + infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); + + printf("Receiving %s INFRARED...\r\nPress Ctrl+C to abort\r\n", enable_decoding ? "" : "RAW"); + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_ms(50); + } + + infrared_worker_rx_stop(worker); + infrared_worker_free(worker); +} + static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { char protocol_name[32]; InfraredMessage message; diff --git a/lib/infrared/worker/infrared_worker.c b/lib/infrared/worker/infrared_worker.c index 86b19114..c03f180f 100644 --- a/lib/infrared/worker/infrared_worker.c +++ b/lib/infrared/worker/infrared_worker.c @@ -57,6 +57,7 @@ struct InfraredWorker { InfraredDecoderHandler* infrared_decoder; NotificationApp* notification; bool blink_enable; + bool decode_enable; union { struct { @@ -131,7 +132,8 @@ static void infrared_worker_process_timeout(InfraredWorker* instance) { static void infrared_worker_process_timings(InfraredWorker* instance, uint32_t duration, bool level) { const InfraredMessage* message_decoded = - infrared_decode(instance->infrared_decoder, level, duration); + instance->decode_enable ? infrared_decode(instance->infrared_decoder, level, duration) : + NULL; if(message_decoded) { instance->signal.message = *message_decoded; instance->signal.timings_cnt = 0; @@ -233,6 +235,7 @@ InfraredWorker* infrared_worker_alloc() { instance->infrared_decoder = infrared_alloc_decoder(); instance->infrared_encoder = infrared_alloc_encoder(); instance->blink_enable = false; + instance->decode_enable = true; instance->notification = furi_record_open(RECORD_NOTIFICATION); instance->state = InfraredWorkerStateIdle; @@ -316,6 +319,11 @@ void infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool instance->blink_enable = enable; } +void infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable) { + furi_assert(instance); + instance->decode_enable = enable; +} + void infrared_worker_tx_start(InfraredWorker* instance) { furi_assert(instance); furi_assert(instance->state == InfraredWorkerStateIdle); diff --git a/lib/infrared/worker/infrared_worker.h b/lib/infrared/worker/infrared_worker.h index c6617e50..26919c4f 100644 --- a/lib/infrared/worker/infrared_worker.h +++ b/lib/infrared/worker/infrared_worker.h @@ -76,6 +76,14 @@ void infrared_worker_rx_set_received_signal_callback( */ void infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool enable); +/** Enable decoding of received infrared signals. + * + * @param[in] instance - instance of InfraredWorker + * @param[in] enable - true if you want to enable decoding + * false otherwise + */ +void infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable); + /** Clarify is received signal either decoded or raw * * @param[in] signal - received signal From 68009c6230c5898d96409255897c35ead7991bcf Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Tue, 18 Oct 2022 18:24:53 +0400 Subject: [PATCH 08/42] [FL-2919] SubGhz: CAME Wrong number of bits in key (add protocol Airforce) (#1890) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- lib/subghz/protocols/came.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/subghz/protocols/came.c b/lib/subghz/protocols/came.c index 7c037bd1..3fc61bf6 100644 --- a/lib/subghz/protocols/came.c +++ b/lib/subghz/protocols/came.c @@ -16,6 +16,8 @@ #define CAME_24_COUNT_BIT 24 #define PRASTEL_COUNT_BIT 25 #define PRASTEL_NAME "Prastel" +#define AIRFORCE_COUNT_BIT 18 +#define AIRFORCE_NAME "Airforce" static const SubGhzBlockConst subghz_protocol_came_const = { .te_short = 320, @@ -86,7 +88,7 @@ void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment) { instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 10; - instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop) + instance->encoder.size_upload = 128; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; @@ -151,10 +153,7 @@ bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flip FURI_LOG_E(TAG, "Deserialize error"); break; } - if((instance->generic.data_count_bit != - subghz_protocol_came_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != CAME_24_COUNT_BIT) && - (instance->generic.data_count_bit != PRASTEL_COUNT_BIT)) { + if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } @@ -310,10 +309,7 @@ bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flip if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { break; } - if((instance->generic.data_count_bit != - subghz_protocol_came_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != CAME_24_COUNT_BIT) && - (instance->generic.data_count_bit != PRASTEL_COUNT_BIT)) { + if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } @@ -338,8 +334,11 @@ void subghz_protocol_decoder_came_get_string(void* context, FuriString* output) "%s %dbit\r\n" "Key:0x%08lX\r\n" "Yek:0x%08lX\r\n", - (instance->generic.data_count_bit == PRASTEL_COUNT_BIT ? PRASTEL_NAME : - instance->generic.protocol_name), + (instance->generic.data_count_bit == PRASTEL_COUNT_BIT ? + PRASTEL_NAME : + (instance->generic.data_count_bit == AIRFORCE_COUNT_BIT ? + AIRFORCE_NAME : + instance->generic.protocol_name)), instance->generic.data_count_bit, code_found_lo, code_found_reverse_lo); From 56f760aa07ba5e659f7d8278819f3699afc61504 Mon Sep 17 00:00:00 2001 From: Patrick Cunningham Date: Tue, 18 Oct 2022 09:58:26 -0500 Subject: [PATCH 09/42] Picopass: Read Elite (#1888) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * working elite dict * add csn to display Co-authored-by: あく --- .../picopass/helpers/iclass_elite_dict.c | 151 ++++++++++++++++++ .../picopass/helpers/iclass_elite_dict.h | 28 ++++ .../picopass/lib/loclass/optimized_elite.c | 2 +- .../plugins/picopass/picopass_device.h | 2 + .../plugins/picopass/picopass_worker.c | 103 ++++++++++-- .../plugins/picopass/picopass_worker_i.h | 1 + .../scenes/picopass_scene_read_card_success.c | 29 ++-- 7 files changed, 289 insertions(+), 27 deletions(-) create mode 100644 applications/plugins/picopass/helpers/iclass_elite_dict.c create mode 100644 applications/plugins/picopass/helpers/iclass_elite_dict.h diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.c b/applications/plugins/picopass/helpers/iclass_elite_dict.c new file mode 100644 index 00000000..455eb23c --- /dev/null +++ b/applications/plugins/picopass/helpers/iclass_elite_dict.c @@ -0,0 +1,151 @@ +#include "iclass_elite_dict.h" + +#include +#include + +#define ICLASS_ELITE_DICT_FLIPPER_PATH EXT_PATH("picopass/assets/iclass_elite_dict.txt") +#define ICLASS_ELITE_DICT_USER_PATH EXT_PATH("picopass/assets/iclass_elite_dict_user.txt") + +#define TAG "IclassEliteDict" + +#define ICLASS_ELITE_KEY_LINE_LEN (17) +#define ICLASS_ELITE_KEY_LEN (8) + +struct IclassEliteDict { + Stream* stream; + uint32_t total_keys; +}; + +bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool dict_present = false; + if(dict_type == IclassEliteDictTypeFlipper) { + dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_PATH, NULL) == + FSE_OK; + } else if(dict_type == IclassEliteDictTypeUser) { + dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_USER_PATH, NULL) == FSE_OK; + } + + furi_record_close(RECORD_STORAGE); + + return dict_present; +} + +IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) { + IclassEliteDict* dict = malloc(sizeof(IclassEliteDict)); + Storage* storage = furi_record_open(RECORD_STORAGE); + dict->stream = buffered_file_stream_alloc(storage); + furi_record_close(RECORD_STORAGE); + FuriString* next_line = furi_string_alloc(); + + bool dict_loaded = false; + do { + if(dict_type == IclassEliteDictTypeFlipper) { + if(!buffered_file_stream_open( + dict->stream, ICLASS_ELITE_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + buffered_file_stream_close(dict->stream); + break; + } + } else if(dict_type == IclassEliteDictTypeUser) { + if(!buffered_file_stream_open( + dict->stream, ICLASS_ELITE_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { + buffered_file_stream_close(dict->stream); + break; + } + } + + // Read total amount of keys + while(true) { + if(!stream_read_line(dict->stream, next_line)) break; + if(furi_string_get_char(next_line, 0) == '#') continue; + if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue; + dict->total_keys++; + } + furi_string_reset(next_line); + stream_rewind(dict->stream); + + dict_loaded = true; + FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys); + } while(false); + + if(!dict_loaded) { + buffered_file_stream_close(dict->stream); + free(dict); + dict = NULL; + } + + furi_string_free(next_line); + + return dict; +} + +void iclass_elite_dict_free(IclassEliteDict* dict) { + furi_assert(dict); + furi_assert(dict->stream); + + buffered_file_stream_close(dict->stream); + stream_free(dict->stream); + free(dict); +} + +uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict) { + furi_assert(dict); + + return dict->total_keys; +} + +bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key) { + furi_assert(dict); + furi_assert(dict->stream); + + uint8_t key_byte_tmp = 0; + FuriString* next_line = furi_string_alloc(); + + bool key_read = false; + *key = 0ULL; + while(!key_read) { + if(!stream_read_line(dict->stream, next_line)) break; + if(furi_string_get_char(next_line, 0) == '#') continue; + if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue; + for(uint8_t i = 0; i < ICLASS_ELITE_KEY_LEN * 2; i += 2) { + args_char_to_hex( + furi_string_get_char(next_line, i), + furi_string_get_char(next_line, i + 1), + &key_byte_tmp); + key[i / 2] = key_byte_tmp; + } + key_read = true; + } + + furi_string_free(next_line); + return key_read; +} + +bool iclass_elite_dict_rewind(IclassEliteDict* dict) { + furi_assert(dict); + furi_assert(dict->stream); + + return stream_rewind(dict->stream); +} + +bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key) { + furi_assert(dict); + furi_assert(dict->stream); + + FuriString* key_str = furi_string_alloc(); + for(size_t i = 0; i < 6; i++) { + furi_string_cat_printf(key_str, "%02X", key[i]); + } + furi_string_cat_printf(key_str, "\n"); + + bool key_added = false; + do { + if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break; + if(!stream_insert_string(dict->stream, key_str)) break; + key_added = true; + } while(false); + + furi_string_free(key_str); + return key_added; +} \ No newline at end of file diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.h b/applications/plugins/picopass/helpers/iclass_elite_dict.h new file mode 100644 index 00000000..e5ec8dfc --- /dev/null +++ b/applications/plugins/picopass/helpers/iclass_elite_dict.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include +#include + +typedef enum { + IclassEliteDictTypeUser, + IclassEliteDictTypeFlipper, +} IclassEliteDictType; + +typedef struct IclassEliteDict IclassEliteDict; + +bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type); + +IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type); + +void iclass_elite_dict_free(IclassEliteDict* dict); + +uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict); + +bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key); + +bool iclass_elite_dict_rewind(IclassEliteDict* dict); + +bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key); \ No newline at end of file diff --git a/applications/plugins/picopass/lib/loclass/optimized_elite.c b/applications/plugins/picopass/lib/loclass/optimized_elite.c index c175f398..34e98706 100644 --- a/applications/plugins/picopass/lib/loclass/optimized_elite.c +++ b/applications/plugins/picopass/lib/loclass/optimized_elite.c @@ -185,7 +185,7 @@ static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8 * @param loclass_hash1 loclass_hash1 * @param key_sel output key_sel=h[loclass_hash1[i]] */ -void hash2(uint8_t* key64, uint8_t* outp_keytable) { +void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable) { /** *Expected: * High Security Key Table diff --git a/applications/plugins/picopass/picopass_device.h b/applications/plugins/picopass/picopass_device.h index ed6bb478..26f21594 100644 --- a/applications/plugins/picopass/picopass_device.h +++ b/applications/plugins/picopass/picopass_device.h @@ -9,6 +9,7 @@ #include "rfal_picopass.h" #include #include +#include "helpers/iclass_elite_dict.h" #define PICOPASS_DEV_NAME_MAX_LEN 22 #define PICOPASS_READER_DATA_MAX_SIZE 64 @@ -49,6 +50,7 @@ typedef struct { bool se_enabled; bool sio; bool biometrics; + uint8_t key[8]; uint8_t pin_length; PicopassEncryption encryption; uint8_t credential[8]; diff --git a/applications/plugins/picopass/picopass_worker.c b/applications/plugins/picopass/picopass_worker.c index 532effd9..2c9b1a0c 100644 --- a/applications/plugins/picopass/picopass_worker.c +++ b/applications/plugins/picopass/picopass_worker.c @@ -1,5 +1,7 @@ #include "picopass_worker_i.h" +#include + #define TAG "PicopassWorker" const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; @@ -176,7 +178,7 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) { return ERR_NONE; } -ReturnCode picopass_read_card(PicopassBlock* AA1) { +ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { rfalPicoPassReadCheckRes rcRes; rfalPicoPassCheckRes chkRes; @@ -197,10 +199,68 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) { loclass_opt_doReaderMAC(ccnr, div_key, mac); err = rfalPicoPassPollerCheck(mac, &chkRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); - return err; + if(err == ERR_NONE) { + return ERR_NONE; } + FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); + + FURI_LOG_E(TAG, "Starting dictionary attack"); + + size_t index = 0; + uint8_t key[PICOPASS_BLOCK_LEN] = {0}; + + if(!iclass_elite_dict_check_presence(IclassEliteDictTypeFlipper)) { + FURI_LOG_E(TAG, "Dictionary not found"); + return ERR_PARAM; + } + + IclassEliteDict* dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper); + if(!dict) { + FURI_LOG_E(TAG, "Dictionary not allocated"); + return ERR_PARAM; + } + + FURI_LOG_D(TAG, "Loaded %lu keys", iclass_elite_dict_get_total_keys(dict)); + while(iclass_elite_dict_get_next_key(dict, key)) { + FURI_LOG_D( + TAG, + "Try to auth with key %d %02x%02x%02x%02x%02x%02x%02x%02x", + index++, + key[0], + key[1], + key[2], + key[3], + key[4], + key[5], + key[6], + key[7]); + + err = rfalPicoPassPollerReadCheck(&rcRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); + return err; + } + memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 + + loclass_iclass_calc_div_key(AA1[PICOPASS_CSN_BLOCK_INDEX].data, key, div_key, true); + loclass_opt_doReaderMAC(ccnr, div_key, mac); + + err = rfalPicoPassPollerCheck(mac, &chkRes); + if(err == ERR_NONE) { + memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); + break; + } + } + + if(dict) { + iclass_elite_dict_free(dict); + } + + return err; +} + +ReturnCode picopass_read_card(PicopassBlock* AA1) { + ReturnCode err; size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ? AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] : @@ -352,28 +412,39 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); if(pacs->se_enabled) { FURI_LOG_D(TAG, "SE enabled"); + nextState = PicopassWorkerEventFail; } - err = picopass_read_card(AA1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_read_card error %d", err); - nextState = PicopassWorkerEventFail; + if(nextState == PicopassWorkerEventSuccess) { + err = picopass_auth(AA1, pacs); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_try_auth error %d", err); + nextState = PicopassWorkerEventFail; + } + } + + if(nextState == PicopassWorkerEventSuccess) { + err = picopass_read_card(AA1); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_read_card error %d", err); + nextState = PicopassWorkerEventFail; + } } if(nextState == PicopassWorkerEventSuccess) { err = picopass_device_parse_credential(AA1, pacs); - } - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); - nextState = PicopassWorkerEventFail; + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); + nextState = PicopassWorkerEventFail; + } } if(nextState == PicopassWorkerEventSuccess) { err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); - } - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); - nextState = PicopassWorkerEventFail; + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); + nextState = PicopassWorkerEventFail; + } } // Notify caller and exit diff --git a/applications/plugins/picopass/picopass_worker_i.h b/applications/plugins/picopass/picopass_worker_i.h index 9b215fb7..101d4d8f 100644 --- a/applications/plugins/picopass/picopass_worker_i.h +++ b/applications/plugins/picopass/picopass_worker_i.h @@ -18,6 +18,7 @@ struct PicopassWorker { FuriThread* thread; Storage* storage; + Stream* dict_stream; PicopassDeviceData* dev_data; PicopassWorkerCallback callback; diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c index 37f1db4f..bb170ac4 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c @@ -15,12 +15,10 @@ void picopass_scene_read_card_success_widget_callback( void picopass_scene_read_card_success_on_enter(void* context) { Picopass* picopass = context; - FuriString* credential_str; - FuriString* wiegand_str; - FuriString* sio_str; - credential_str = furi_string_alloc(); - wiegand_str = furi_string_alloc(); - sio_str = furi_string_alloc(); + FuriString* csn_str = furi_string_alloc_set("CSN:"); + FuriString* credential_str = furi_string_alloc(); + FuriString* wiegand_str = furi_string_alloc(); + FuriString* sio_str = furi_string_alloc(); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); @@ -28,10 +26,18 @@ void picopass_scene_read_card_success_on_enter(void* context) { notification_message(picopass->notifications, &sequence_success); // Setup view + PicopassBlock* AA1 = picopass->dev->dev_data.AA1; PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; - if(pacs->record.bitLength == 0) { + uint8_t csn[PICOPASS_BLOCK_LEN]; + memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + furi_string_cat_printf(csn_str, " %02X", csn[i]); + } + + // Neither of these are valid. Indicates the block was all 0x00 or all 0xff + if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { furi_string_cat_printf(wiegand_str, "Read Failed"); if(pacs->se_enabled) { @@ -79,18 +85,21 @@ void picopass_scene_read_card_success_on_enter(void* context) { } widget_add_string_element( - widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); + widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); + widget_add_string_element( + widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); widget_add_string_element( widget, 64, - 32, + 36, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(credential_str)); widget_add_string_element( - widget, 64, 42, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); + widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); + furi_string_free(csn_str); furi_string_free(credential_str); furi_string_free(wiegand_str); furi_string_free(sio_str); From 72713d6f4e8525d9550285bd6c57c30d789a1dc1 Mon Sep 17 00:00:00 2001 From: Kevin Kwok Date: Tue, 18 Oct 2022 08:06:18 -0700 Subject: [PATCH 10/42] Allow pins 0 and 1 as RTS/DTR for USB UART Bridge (#1864) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow pins 0 and 1 as RTS/DTR for USB UART Bridge * add logic to gpio_scene_usb_uart_config, fix flow_pins * fixing count of pins * disable PC0,PC1 RTS/DTR when using LPUART * add logic to ensure flow pins dont overlap with uart lines Co-authored-by: あく --- applications/main/gpio/gpio_app_i.h | 1 + .../gpio/scenes/gpio_scene_usb_uart_config.c | 26 +++++++++++++++++-- applications/main/gpio/usb_uart_bridge.c | 1 + .../services/gui/modules/variable_item_list.c | 4 +++ .../services/gui/modules/variable_item_list.h | 7 +++++ 5 files changed, 37 insertions(+), 2 deletions(-) diff --git a/applications/main/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h index fa91e8f7..fff35c95 100644 --- a/applications/main/gpio/gpio_app_i.h +++ b/applications/main/gpio/gpio_app_i.h @@ -24,6 +24,7 @@ struct GpioApp { Widget* widget; VariableItemList* var_item_list; + VariableItem* var_item_flow; GpioTest* gpio_test; GpioUsbUart* gpio_usb_uart; UsbUartBridge* usb_uart_bridge; diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c index 653a306a..c114d79a 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c @@ -13,7 +13,7 @@ static UsbUartConfig* cfg_set; static const char* vcp_ch[] = {"0 (CLI)", "1"}; static const char* uart_ch[] = {"13,14", "15,16"}; -static const char* flow_pins[] = {"None", "2,3", "6,7"}; +static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"}; static const char* baudrate_mode[] = {"Host"}; static const uint32_t baudrate_list[] = { 2400, @@ -33,6 +33,24 @@ bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) { return false; } +void line_ensure_flow_invariant(GpioApp* app) { + // GPIO pins PC0, PC1 (16,15) are unavailable for RTS/DTR when LPUART is + // selected. This function enforces that invariant by resetting flow_pins + // to None if it is configured to 16,15 when LPUART is selected. + + uint8_t available_flow_pins = cfg_set->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4; + VariableItem* item = app->var_item_flow; + variable_item_set_values_count(item, available_flow_pins); + + if(cfg_set->flow_pins >= available_flow_pins) { + cfg_set->flow_pins = 0; + usb_uart_set_config(app->usb_uart_bridge, cfg_set); + + variable_item_set_current_value_index(item, cfg_set->flow_pins); + variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); + } +} + static void line_vcp_cb(VariableItem* item) { GpioApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -54,6 +72,7 @@ static void line_port_cb(VariableItem* item) { else if(index == 1) cfg_set->uart_ch = FuriHalUartIdLPUART1; usb_uart_set_config(app->usb_uart_bridge, cfg_set); + line_ensure_flow_invariant(app); } static void line_flow_cb(VariableItem* item) { @@ -116,9 +135,12 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) { variable_item_set_current_value_index(item, cfg_set->uart_ch); variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]); - item = variable_item_list_add(var_item_list, "RTS/DTR Pins", 3, line_flow_cb, app); + item = variable_item_list_add( + var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app); variable_item_set_current_value_index(item, cfg_set->flow_pins); variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); + app->var_item_flow = item; + line_ensure_flow_invariant(app); variable_item_list_set_selected_item( var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg)); diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c index 6e0bce73..a5caceaf 100644 --- a/applications/main/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -14,6 +14,7 @@ static const GpioPin* flow_pins[][2] = { {&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3 {&gpio_ext_pb2, &gpio_ext_pc3}, // 6, 7 + {&gpio_ext_pc0, &gpio_ext_pc1}, // 16, 15 }; typedef enum { diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index acd46ee4..a9b89d63 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -396,6 +396,10 @@ void variable_item_set_current_value_index(VariableItem* item, uint8_t current_v item->current_value_index = current_value_index; } +void variable_item_set_values_count(VariableItem* item, uint8_t values_count) { + item->values_count = values_count; +} + void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text) { furi_string_set(item->current_value_text, current_value_text); } diff --git a/applications/services/gui/modules/variable_item_list.h b/applications/services/gui/modules/variable_item_list.h index 78c7769c..db2a5899 100644 --- a/applications/services/gui/modules/variable_item_list.h +++ b/applications/services/gui/modules/variable_item_list.h @@ -81,6 +81,13 @@ uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_it */ void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index); +/** Set number of values for item + * + * @param item VariableItem* instance + * @param values_count The new values count + */ +void variable_item_set_values_count(VariableItem* item, uint8_t values_count); + /** Set item current selected text * * @param item VariableItem* instance From 79c3040629cc7390ffad9e081c165dc03846699f Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 19 Oct 2022 18:28:48 +0400 Subject: [PATCH 11/42] fbt: fixed dependency issues with SDK definition file (#1893) * fbt: fixed dependency issues with SDK definition file * fbt: more path fixes; marked up new symbols --- firmware.scons | 2 +- firmware/SConscript | 4 +++- firmware/targets/f7/api_symbols.csv | 5 ++++- scripts/fbt_tools/fbt_extapps.py | 4 ++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/firmware.scons b/firmware.scons index d28309d0..5219a2f3 100644 --- a/firmware.scons +++ b/firmware.scons @@ -329,7 +329,7 @@ if fwenv["IS_BASE_FIRMWARE"]: # AlwaysBuild(sdk_tree) Alias("sdk_tree", sdk_tree) - sdk_apicheck = fwenv.SDKSymUpdater(fwenv.subst("$SDK_DEFINITION"), "sdk_origin") + sdk_apicheck = fwenv.SDKSymUpdater(fwenv["SDK_DEFINITION"], "sdk_origin") Precious(sdk_apicheck) NoClean(sdk_apicheck) AlwaysBuild(sdk_apicheck) diff --git a/firmware/SConscript b/firmware/SConscript index 5795e242..2285a6f2 100644 --- a/firmware/SConscript +++ b/firmware/SConscript @@ -9,8 +9,10 @@ env.Append( File("#/firmware/targets/f7/platform_specific/intrinsic_export.h"), ], ) -env.SetDefault(SDK_DEFINITION=env.File("./targets/f${TARGET_HW}/api_symbols.csv")) +env.SetDefault( + SDK_DEFINITION=env.File("./targets/f${TARGET_HW}/api_symbols.csv").srcnode() +) libenv = env.Clone(FW_LIB_NAME="flipper${TARGET_HW}") libenv.Append( diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 33e28994..d3d5a901 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,2.3,, +Version,+,2.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -929,6 +929,7 @@ Function,+,furi_hal_bt_nvm_sram_sem_release,void, Function,+,furi_hal_bt_reinit,void, Function,+,furi_hal_bt_serial_notify_buffer_is_empty,void, Function,+,furi_hal_bt_serial_set_event_callback,void,"uint16_t, FuriHalBtSerialCallback, void*" +Function,+,furi_hal_bt_serial_set_rpc_status,void,FuriHalBtSerialRpcStatus Function,+,furi_hal_bt_serial_start,void, Function,+,furi_hal_bt_serial_stop,void, Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" @@ -2071,6 +2072,7 @@ Function,-,select,int,"int, fd_set*, fd_set*, fd_set*, timeval*" Function,-,serial_svc_is_started,_Bool, Function,-,serial_svc_notify_buffer_is_empty,void, Function,-,serial_svc_set_callbacks,void,"uint16_t, SerialServiceEventCallback, void*" +Function,-,serial_svc_set_rpc_status,void,SerialServiceRpcStatus Function,-,serial_svc_start,void, Function,-,serial_svc_stop,void, Function,-,serial_svc_update_tx,_Bool,"uint8_t*, uint16_t" @@ -2467,6 +2469,7 @@ Function,+,variable_item_list_set_enter_callback,void,"VariableItemList*, Variab Function,+,variable_item_list_set_selected_item,void,"VariableItemList*, uint8_t" Function,+,variable_item_set_current_value_index,void,"VariableItem*, uint8_t" Function,+,variable_item_set_current_value_text,void,"VariableItem*, const char*" +Function,+,variable_item_set_values_count,void,"VariableItem*, uint8_t" Function,-,vasiprintf,int,"char**, const char*, __gnuc_va_list" Function,-,vasniprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" Function,-,vasnprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 9f81d414..e40a4efc 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -145,7 +145,7 @@ def BuildAppElf(env, app): def prepare_app_metadata(target, source, env): - sdk_cache = SdkCache(env.subst("$SDK_DEFINITION"), load_version_only=True) + sdk_cache = SdkCache(env["SDK_DEFINITION"].path, load_version_only=True) if not sdk_cache.is_buildable(): raise UserError( @@ -166,7 +166,7 @@ def prepare_app_metadata(target, source, env): def validate_app_imports(target, source, env): - sdk_cache = SdkCache(env.subst("$SDK_DEFINITION"), load_version_only=False) + sdk_cache = SdkCache(env["SDK_DEFINITION"].path, load_version_only=False) app_syms = set() with open(target[0].path, "rt") as f: for line in f: From 9a9abd59e9a97e0e215041eca7fd1ee0570bd10b Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 19 Oct 2022 21:27:26 +0400 Subject: [PATCH 12/42] [FL-2904, FL-2900, FL-2890] WS: add app WeatherStation (#1833) * WeatherStation: start * SubGhz: rename protocol magellen -> magellan * WeatherStation: err Unresolved symbols: {'subghz_protocol_decoder_base_get_string'} * WeatherStation: fix Unresolved symbols: {'subghz_protocol_decoder_base_get_string'} * Subghz: add set protocol_items * WeatherStation: adding your protocols * WS: add Infactory protocol * WS: add history * WS: add setting * WS: add lock * WS: add hopper frequency * WS: fix history * WS fix string_t -> FuriString* * WS: add images * WS: history record update when receiving data from the sensor again * WS: add receiver info, delete extra code * WS: add protocol ThermoPRO_TX4 * [FL-2900] SubGhz: Move icons in Sub-GHz * WS: add Notification * [FL-2890] SubGhz: Rename *_user files in resources to _user.example * WS: add about scene * WS: removing redundant code * WS: add protocol Nexus-TH * WS: add protocol GT_WT03 * WS: fix notification and rename "Weather Station" -> "Read Weather Station" * SubGhz: partial unit tests fix * SubGhz: fix unit_test * SubGhz: remove dead code * SubGhz: rename SubGhzPresetDefinition into SubGhzRadioPreset, cleanup subghz types. Co-authored-by: Aleksandr Kutuzov --- .../debug/unit_tests/subghz/subghz_test.c | 20 +- .../lfrfid/scenes/lfrfid_scene_raw_info.c | 2 +- .../main/subghz/helpers/subghz_types.h | 9 - .../scenes/subghz_scene_receiver_info.c | 4 +- .../subghz/scenes/subghz_scene_set_type.c | 2 +- applications/main/subghz/subghz.c | 5 +- applications/main/subghz/subghz_cli.c | 4 + applications/main/subghz/subghz_history.c | 8 +- applications/main/subghz/subghz_history.h | 8 +- applications/main/subghz/subghz_i.h | 9 +- applications/main/subghz/views/receiver.c | 2 +- .../plugins/weather_station/application.fam | 13 + .../helpers/weather_station_event.h | 14 + .../helpers/weather_station_types.h | 49 ++ .../weather_station/images/Humid_10x15.png | Bin 0 -> 3624 bytes .../weather_station/images/Therm_7x16.png | Bin 0 -> 3611 bytes .../weather_station/images/station_icon.png | Bin 0 -> 3607 bytes .../weather_station/protocols/gt_wt_03.c | 341 ++++++++++++++ .../weather_station/protocols/gt_wt_03.h | 79 ++++ .../weather_station/protocols/infactory.c | 296 ++++++++++++ .../weather_station/protocols/infactory.h | 79 ++++ .../weather_station/protocols/nexus_th.c | 261 +++++++++++ .../weather_station/protocols/nexus_th.h | 79 ++++ .../protocols/protocol_items.c | 12 + .../protocols/protocol_items.h | 9 + .../weather_station/protocols/thermopro_tx4.c | 260 +++++++++++ .../weather_station/protocols/thermopro_tx4.h | 79 ++++ .../weather_station/protocols/ws_generic.c | 198 ++++++++ .../weather_station/protocols/ws_generic.h | 61 +++ .../scenes/weather_station_receiver.c | 207 +++++++++ .../scenes/weather_station_scene.c | 30 ++ .../scenes/weather_station_scene.h | 29 ++ .../scenes/weather_station_scene_about.c | 78 ++++ .../scenes/weather_station_scene_config.h | 5 + .../weather_station_scene_receiver_config.c | 223 +++++++++ .../weather_station_scene_receiver_info.c | 50 ++ .../scenes/weather_station_scene_start.c | 58 +++ .../views/weather_station_receiver.c | 437 ++++++++++++++++++ .../views/weather_station_receiver.h | 36 ++ .../views/weather_station_receiver_info.c | 150 ++++++ .../views/weather_station_receiver_info.h | 16 + .../weather_station/weather_station_10px.png | Bin 0 -> 175 bytes .../weather_station/weather_station_app.c | 178 +++++++ .../weather_station/weather_station_app_i.c | 159 +++++++ .../weather_station/weather_station_app_i.h | 73 +++ .../weather_station/weather_station_history.c | 246 ++++++++++ .../weather_station/weather_station_history.h | 112 +++++ ...codes_user => keeloq_mfcodes_user.example} | 1 + .../{setting_user => setting_user.example} | 1 + .../subghz/{magellen.sub => magellan.sub} | 2 +- .../{magellen_raw.sub => magellan_raw.sub} | 0 firmware/targets/f7/api_symbols.csv | 59 ++- lib/subghz/SConscript | 6 + lib/subghz/blocks/const.h | 8 + lib/subghz/blocks/decoder.h | 8 + lib/subghz/blocks/encoder.h | 8 + lib/subghz/blocks/generic.c | 2 +- lib/subghz/blocks/generic.h | 12 +- lib/subghz/blocks/math.c | 65 +++ lib/subghz/blocks/math.h | 52 +++ lib/subghz/environment.c | 33 ++ lib/subghz/environment.h | 24 + lib/subghz/protocols/base.c | 2 +- lib/subghz/protocols/base.h | 4 +- lib/subghz/protocols/bett.c | 2 +- lib/subghz/protocols/bett.h | 4 +- lib/subghz/protocols/came.c | 2 +- lib/subghz/protocols/came.h | 4 +- lib/subghz/protocols/came_atomo.c | 2 +- lib/subghz/protocols/came_atomo.h | 4 +- lib/subghz/protocols/came_twee.c | 2 +- lib/subghz/protocols/came_twee.h | 4 +- lib/subghz/protocols/chamberlain_code.c | 2 +- lib/subghz/protocols/chamberlain_code.h | 4 +- lib/subghz/protocols/clemsa.c | 2 +- lib/subghz/protocols/clemsa.h | 4 +- lib/subghz/protocols/doitrand.c | 2 +- lib/subghz/protocols/doitrand.h | 4 +- lib/subghz/protocols/faac_slh.c | 2 +- lib/subghz/protocols/faac_slh.h | 4 +- lib/subghz/protocols/gate_tx.c | 2 +- lib/subghz/protocols/gate_tx.h | 4 +- lib/subghz/protocols/holtek.c | 2 +- lib/subghz/protocols/holtek.h | 4 +- lib/subghz/protocols/honeywell_wdb.c | 2 +- lib/subghz/protocols/honeywell_wdb.h | 4 +- lib/subghz/protocols/hormann.c | 2 +- lib/subghz/protocols/hormann.h | 4 +- lib/subghz/protocols/ido.c | 2 +- lib/subghz/protocols/ido.h | 4 +- lib/subghz/protocols/intertechno_v3.c | 2 +- lib/subghz/protocols/intertechno_v3.h | 4 +- lib/subghz/protocols/keeloq.c | 4 +- lib/subghz/protocols/keeloq.h | 8 +- lib/subghz/protocols/kia.c | 2 +- lib/subghz/protocols/kia.h | 4 +- lib/subghz/protocols/linear.c | 2 +- lib/subghz/protocols/linear.h | 4 +- .../protocols/{magellen.c => magellan.c} | 256 +++++----- lib/subghz/protocols/magellan.h | 107 +++++ lib/subghz/protocols/magellen.h | 107 ----- lib/subghz/protocols/marantec.c | 2 +- lib/subghz/protocols/marantec.h | 4 +- lib/subghz/protocols/megacode.c | 2 +- lib/subghz/protocols/megacode.h | 4 +- lib/subghz/protocols/nero_radio.c | 2 +- lib/subghz/protocols/nero_radio.h | 4 +- lib/subghz/protocols/nero_sketch.c | 2 +- lib/subghz/protocols/nero_sketch.h | 4 +- lib/subghz/protocols/nice_flo.c | 2 +- lib/subghz/protocols/nice_flo.h | 4 +- lib/subghz/protocols/nice_flor_s.c | 2 +- lib/subghz/protocols/nice_flor_s.h | 4 +- lib/subghz/protocols/oregon2.c | 2 +- lib/subghz/protocols/phoenix_v2.c | 2 +- lib/subghz/protocols/phoenix_v2.h | 4 +- lib/subghz/protocols/power_smart.c | 2 +- lib/subghz/protocols/power_smart.h | 4 +- lib/subghz/protocols/princeton.c | 2 +- lib/subghz/protocols/princeton.h | 4 +- .../{registry.c => protocol_items.c} | 29 +- .../{registry.h => protocol_items.h} | 25 +- lib/subghz/protocols/raw.c | 2 +- lib/subghz/protocols/raw.h | 4 +- lib/subghz/protocols/scher_khan.c | 2 +- lib/subghz/protocols/scher_khan.h | 4 +- lib/subghz/protocols/secplus_v1.c | 2 +- lib/subghz/protocols/secplus_v1.h | 4 +- lib/subghz/protocols/secplus_v2.c | 4 +- lib/subghz/protocols/secplus_v2.h | 8 +- lib/subghz/protocols/somfy_keytis.c | 2 +- lib/subghz/protocols/somfy_keytis.h | 4 +- lib/subghz/protocols/somfy_telis.c | 2 +- lib/subghz/protocols/somfy_telis.h | 4 +- lib/subghz/protocols/star_line.c | 2 +- lib/subghz/protocols/star_line.h | 4 +- lib/subghz/receiver.c | 11 +- lib/subghz/registry.c | 30 ++ lib/subghz/registry.h | 39 ++ .../main => lib}/subghz/subghz_setting.c | 5 +- .../main => lib}/subghz/subghz_setting.h | 8 + lib/subghz/transmitter.c | 10 +- lib/subghz/types.h | 19 +- 143 files changed, 4677 insertions(+), 458 deletions(-) create mode 100644 applications/plugins/weather_station/application.fam create mode 100644 applications/plugins/weather_station/helpers/weather_station_event.h create mode 100644 applications/plugins/weather_station/helpers/weather_station_types.h create mode 100644 applications/plugins/weather_station/images/Humid_10x15.png create mode 100644 applications/plugins/weather_station/images/Therm_7x16.png create mode 100644 applications/plugins/weather_station/images/station_icon.png create mode 100644 applications/plugins/weather_station/protocols/gt_wt_03.c create mode 100644 applications/plugins/weather_station/protocols/gt_wt_03.h create mode 100644 applications/plugins/weather_station/protocols/infactory.c create mode 100644 applications/plugins/weather_station/protocols/infactory.h create mode 100644 applications/plugins/weather_station/protocols/nexus_th.c create mode 100644 applications/plugins/weather_station/protocols/nexus_th.h create mode 100644 applications/plugins/weather_station/protocols/protocol_items.c create mode 100644 applications/plugins/weather_station/protocols/protocol_items.h create mode 100644 applications/plugins/weather_station/protocols/thermopro_tx4.c create mode 100644 applications/plugins/weather_station/protocols/thermopro_tx4.h create mode 100644 applications/plugins/weather_station/protocols/ws_generic.c create mode 100644 applications/plugins/weather_station/protocols/ws_generic.h create mode 100644 applications/plugins/weather_station/scenes/weather_station_receiver.c create mode 100644 applications/plugins/weather_station/scenes/weather_station_scene.c create mode 100644 applications/plugins/weather_station/scenes/weather_station_scene.h create mode 100644 applications/plugins/weather_station/scenes/weather_station_scene_about.c create mode 100644 applications/plugins/weather_station/scenes/weather_station_scene_config.h create mode 100644 applications/plugins/weather_station/scenes/weather_station_scene_receiver_config.c create mode 100644 applications/plugins/weather_station/scenes/weather_station_scene_receiver_info.c create mode 100644 applications/plugins/weather_station/scenes/weather_station_scene_start.c create mode 100644 applications/plugins/weather_station/views/weather_station_receiver.c create mode 100644 applications/plugins/weather_station/views/weather_station_receiver.h create mode 100644 applications/plugins/weather_station/views/weather_station_receiver_info.c create mode 100644 applications/plugins/weather_station/views/weather_station_receiver_info.h create mode 100644 applications/plugins/weather_station/weather_station_10px.png create mode 100644 applications/plugins/weather_station/weather_station_app.c create mode 100644 applications/plugins/weather_station/weather_station_app_i.c create mode 100644 applications/plugins/weather_station/weather_station_app_i.h create mode 100644 applications/plugins/weather_station/weather_station_history.c create mode 100644 applications/plugins/weather_station/weather_station_history.h rename assets/resources/subghz/assets/{keeloq_mfcodes_user => keeloq_mfcodes_user.example} (80%) rename assets/resources/subghz/assets/{setting_user => setting_user.example} (91%) rename assets/unit_tests/subghz/{magellen.sub => magellan.sub} (88%) rename assets/unit_tests/subghz/{magellen_raw.sub => magellan_raw.sub} (100%) rename lib/subghz/protocols/{magellen.c => magellan.c} (59%) create mode 100644 lib/subghz/protocols/magellan.h delete mode 100644 lib/subghz/protocols/magellen.h rename lib/subghz/protocols/{registry.c => protocol_items.c} (58%) rename lib/subghz/protocols/{registry.h => protocol_items.h} (54%) create mode 100644 lib/subghz/registry.c create mode 100644 lib/subghz/registry.h rename {applications/main => lib}/subghz/subghz_setting.c (99%) rename {applications/main => lib}/subghz/subghz_setting.h (95%) diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 210d3770..2dbf2eed 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #define TAG "SubGhz TEST" @@ -43,6 +43,8 @@ static void subghz_test_init(void) { environment_handler, CAME_ATOMO_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment_handler, NICE_FLOR_S_DIR_NAME); + subghz_environment_set_protocol_registry( + environment_handler, (void*)&subghz_protocol_registry); receiver_handler = subghz_receiver_alloc_init(environment_handler); subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable); @@ -413,11 +415,11 @@ MU_TEST(subghz_decoder_honeywell_wdb_test) { "Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); } -MU_TEST(subghz_decoder_magellen_test) { +MU_TEST(subghz_decoder_magellan_test) { mu_assert( subghz_decoder_test( - EXT_PATH("unit_tests/subghz/magellen_raw.sub"), SUBGHZ_PROTOCOL_MAGELLEN_NAME), - "Test decoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n"); + EXT_PATH("unit_tests/subghz/magellan_raw.sub"), SUBGHZ_PROTOCOL_MAGELLAN_NAME), + "Test decoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n"); } MU_TEST(subghz_decoder_intertechno_v3_test) { @@ -545,10 +547,10 @@ MU_TEST(subghz_encoder_honeywell_wdb_test) { "Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); } -MU_TEST(subghz_encoder_magellen_test) { +MU_TEST(subghz_encoder_magellan_test) { mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellen.sub")), - "Test encoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n"); + subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellan.sub")), + "Test encoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n"); } MU_TEST(subghz_encoder_intertechno_v3_test) { @@ -600,7 +602,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_doitrand_test); MU_RUN_TEST(subghz_decoder_phoenix_v2_test); MU_RUN_TEST(subghz_decoder_honeywell_wdb_test); - MU_RUN_TEST(subghz_decoder_magellen_test); + MU_RUN_TEST(subghz_decoder_magellan_test); MU_RUN_TEST(subghz_decoder_intertechno_v3_test); MU_RUN_TEST(subghz_decoder_clemsa_test); MU_RUN_TEST(subghz_decoder_oregon2_test); @@ -622,7 +624,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_doitrand_test); MU_RUN_TEST(subghz_encoder_phoenix_v2_test); MU_RUN_TEST(subghz_encoder_honeywell_wdb_test); - MU_RUN_TEST(subghz_encoder_magellen_test); + MU_RUN_TEST(subghz_encoder_magellan_test); MU_RUN_TEST(subghz_encoder_intertechno_v3_test); MU_RUN_TEST(subghz_encoder_clemsa_test); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_info.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_info.c index e5193521..f403c1f3 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_raw_info.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_info.c @@ -34,7 +34,7 @@ void lfrfid_scene_raw_info_on_enter(void* context) { } view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); - //string_clear(tmp_string); + //furi_string_free(tmp_string); } bool lfrfid_scene_raw_info_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 023fb4b6..bd6451f2 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -69,12 +69,3 @@ typedef enum { SubGhzViewIdTestCarrier, SubGhzViewIdTestPacket, } SubGhzViewId; - -struct SubGhzPresetDefinition { - FuriString* name; - uint32_t frequency; - uint8_t* data; - size_t data_size; -}; - -typedef struct SubGhzPresetDefinition SubGhzPresetDefinition; diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 2e833edd..03a8ebbc 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -28,8 +28,8 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { subghz->txrx->decoder_result, subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); - SubGhzPresetDefinition* preset = - subghz_history_get_preset_def(subghz->txrx->history, subghz->txrx->idx_menu_chosen); + SubGhzRadioPreset* preset = + subghz_history_get_radio_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen); subghz_preset_init( subghz, furi_string_get_cstr(preset->name), diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 1e7a7474..44fe2fc7 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #define TAG "SubGhzSetType" diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 71c6bc2c..ba70f36d 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -3,6 +3,7 @@ #include "subghz/types.h" #include "subghz_i.h" #include +#include bool subghz_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -169,7 +170,7 @@ SubGhz* subghz_alloc() { //init Worker & Protocol & History & KeyBoard subghz->lock = SubGhzLockOff; subghz->txrx = malloc(sizeof(SubGhzTxRx)); - subghz->txrx->preset = malloc(sizeof(SubGhzPresetDefinition)); + subghz->txrx->preset = malloc(sizeof(SubGhzRadioPreset)); subghz->txrx->preset->name = furi_string_alloc(); subghz_preset_init( subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); @@ -186,6 +187,8 @@ SubGhz* subghz_alloc() { subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s")); + subghz_environment_set_protocol_registry( + subghz->txrx->environment, (void*)&subghz_protocol_registry); subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment); subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index a1474885..ad5d8afb 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "helpers/subghz_chat.h" @@ -164,6 +165,7 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string)); SubGhzEnvironment* environment = subghz_environment_alloc(); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton"); subghz_transmitter_deserialize(transmitter, flipper_format); @@ -257,6 +259,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/nice_flor_s")); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -376,6 +379,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/nice_flor_s")); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 0f67b4fc..beecc6e8 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -11,7 +11,7 @@ typedef struct { FuriString* item_str; FlipperFormat* flipper_string; uint8_t type; - SubGhzPresetDefinition* preset; + SubGhzRadioPreset* preset; } SubGhzHistoryItem; ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) @@ -60,7 +60,7 @@ uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) { return item->preset->frequency; } -SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx) { +SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); return item->preset; @@ -138,7 +138,7 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* outp bool subghz_history_add_to_history( SubGhzHistory* instance, void* context, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(instance); furi_assert(context); @@ -158,7 +158,7 @@ bool subghz_history_add_to_history( FuriString* text; text = furi_string_alloc(); SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); - item->preset = malloc(sizeof(SubGhzPresetDefinition)); + item->preset = malloc(sizeof(SubGhzRadioPreset)); item->type = decoder_base->protocol->type; item->preset->frequency = preset->frequency; item->preset->name = furi_string_alloc(); diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index 7bff3df5..5b2b57d3 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -5,7 +5,7 @@ #include #include #include -#include "helpers/subghz_types.h" +#include typedef struct SubGhzHistory SubGhzHistory; @@ -35,7 +35,7 @@ void subghz_history_reset(SubGhzHistory* instance); */ uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx); -SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx); +SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx); /** Get preset to history[idx] * @@ -88,13 +88,13 @@ bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* out * * @param instance - SubGhzHistory instance * @param context - SubGhzProtocolCommon context - * @param preset - SubGhzPresetDefinition preset + * @param preset - SubGhzRadioPreset preset * @return bool; */ bool subghz_history_add_to_history( SubGhzHistory* instance, void* context, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data * diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 58d30717..284f7ccf 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -1,6 +1,7 @@ #pragma once #include "helpers/subghz_types.h" +#include #include "subghz.h" #include "views/receiver.h" #include "views/transmitter.h" @@ -11,8 +12,6 @@ #include "views/subghz_test_carrier.h" #include "views/subghz_test_packet.h" -// #include -// #include #include #include #include @@ -24,14 +23,12 @@ #include #include - #include - +#include #include #include #include "subghz_history.h" -#include "subghz_setting.h" #include #include @@ -49,7 +46,7 @@ struct SubGhzTxRx { SubGhzProtocolDecoderBase* decoder_result; FlipperFormat* fff_data; - SubGhzPresetDefinition* preset; + SubGhzRadioPreset* preset; SubGhzHistory* history; uint16_t idx_menu_chosen; SubGhzTxRxState txrx_state; diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index cdebc632..7aa69922 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -192,7 +192,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { } else { canvas_set_color(canvas, ColorBlack); } - canvas_draw_icon(canvas, 1, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); + canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); furi_string_reset(str_buff); } diff --git a/applications/plugins/weather_station/application.fam b/applications/plugins/weather_station/application.fam new file mode 100644 index 00000000..1074fc04 --- /dev/null +++ b/applications/plugins/weather_station/application.fam @@ -0,0 +1,13 @@ +App( + appid="weather_station", + name="Weather Station", + apptype=FlipperAppType.PLUGIN, + entry_point="weather_station_app", + cdefines=["APP_WEATHER_STATION"], + requires=["gui"], + stack_size=4 * 1024, + order=50, + fap_icon="weather_station_10px.png", + fap_category="Tools", + fap_icon_assets="images", +) diff --git a/applications/plugins/weather_station/helpers/weather_station_event.h b/applications/plugins/weather_station/helpers/weather_station_event.h new file mode 100644 index 00000000..b0486183 --- /dev/null +++ b/applications/plugins/weather_station/helpers/weather_station_event.h @@ -0,0 +1,14 @@ +#pragma once + +typedef enum { + //WSCustomEvent + WSCustomEventStartId = 100, + + WSCustomEventSceneSettingLock, + + WSCustomEventViewReceiverOK, + WSCustomEventViewReceiverConfig, + WSCustomEventViewReceiverBack, + WSCustomEventViewReceiverOffDisplay, + WSCustomEventViewReceiverUnlock, +} WSCustomEvent; diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h new file mode 100644 index 00000000..a6905e82 --- /dev/null +++ b/applications/plugins/weather_station/helpers/weather_station_types.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#define WS_VERSION_APP "0.1" +#define WS_DEVELOPED "SkorP" +#define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" + +#define WS_KEY_FILE_VERSION 1 +#define WS_KEY_FILE_TYPE "Flipper Weather Station Key File" + +/** WSRxKeyState state */ +typedef enum { + WSRxKeyStateIDLE, + WSRxKeyStateBack, + WSRxKeyStateStart, + WSRxKeyStateAddKey, +} WSRxKeyState; + +/** WSHopperState state */ +typedef enum { + WSHopperStateOFF, + WSHopperStateRunnig, + WSHopperStatePause, + WSHopperStateRSSITimeOut, +} WSHopperState; + +/** WSLock */ +typedef enum { + WSLockOff, + WSLockOn, +} WSLock; + +typedef enum { + WeatherStationViewVariableItemList, + WeatherStationViewSubmenu, + WeatherStationViewReceiver, + WeatherStationViewReceiverInfo, + WeatherStationViewWidget, +} WeatherStationView; + +/** WeatherStationTxRx state */ +typedef enum { + WSTxRxStateIDLE, + WSTxRxStateRx, + WSTxRxStateTx, + WSTxRxStateSleep, +} WSTxRxState; diff --git a/applications/plugins/weather_station/images/Humid_10x15.png b/applications/plugins/weather_station/images/Humid_10x15.png new file mode 100644 index 0000000000000000000000000000000000000000..34b074e5f75150853ec976d7521e1663f0f30f1c GIT binary patch literal 3624 zcmaJ@c{r4N`+r3CJxfSu3?VU#wV7rtGh^Sv$cV}qV@#UGm>Nro%2pkc?2V{oR}m7* zmL(xdWv`HM@E%K)@Q(AI&ini0*ZW-0^?dL9zV6TGUZ3mw#vgXFmJn4I1pq+8)&}Rw zJGW&iVSe6sMy(9R(=Dl3>|t9h7Q|#R{HdqN01z_Bb)(?jrWMeuqstikxX2s!3|Dz! zkSpd&q+F7wj+%(HU7T9(fV@kijHRW3N_$Qme?mg!Re2X(@ynv`g(lQ)CtSP}clpKo z$M8FWZ|hb+cWqX_Go30~;#TwsH3*BR+8DSPMT!?<_R4&?*w)heaROo|UP$Kim0LqJK- zk;|3<0S3tV+qWQq_j&-#*2CWhcu);AbW4ks1H$3}%q1>*KOhhe__V95hX9u{06D8g z57eIr%A}`sc%8~9N7ZN`ETg=H^@4;vJRp0uyKNN@$QcuN5HrmoO`#b|`cZ~bAC_JM zKu(f8uiB-JkZ#Gc?r!6RD#;UiGtUIKz`nlYo0C1oOmhJE$d2gU)P+_kM;;Q4q;1~b zH!l!yTrB7G>J|TTDf3DoXL`_MiMiby%iL=<0|S#26YuR>FkZwL9_KbGO(z;WHcowu zK>b)<`SA3UMwI@sC~JYW4^1zZ9rE_{To<|IJN!A(`bV|c)(_R!;1*lo8iJ18xQlF1 z0xt9Fl71dI9&>&F^L>3=exJs4*ZEDyjDQCxP5Hu;^a_rV_`lj~NfX!&pH=~2v6j*J zMq8LaGT`FJ9?sT+*@kt_J|NQH_IeNi9LH%u@GmON+JpfBmlLJ)z(QrYakp-R;GV{v z!;NA;e2gz)G+LT4(il;{$UQ8d{UsML+A&=ZRCRoyZ_HH<8(acnl9`f_CilmZXr|P6 zqHuPjc3qT+fJM9TE~46C9G~xHf_j3mVn+0uTBD7C>=g}AN1U7s*gna~2JU(p4|2Cr zT|~2XAY#3(o+KS=2lOxeh^e!N--s%ALBA2N#MTs;C||O=E%wTf4bMze$jN%edZdiL zYMeXusyIMuFwqp-25b1TTgag06b#bZjCpuaS0tI#`4C(pUfinu;7AF7ZTt$U=OITx zHp;R=#8`lX0TK6F*bp2DPVa3BKzlR{Wd=n|MEEbcG--j83+x|hK9Tv>vfEc59!s#% zRevj+xC<&B9*1o)(U6VD>TA_p+hP0gF1}B;&#I5^sy?k-m}O|Ate)I4=oeTngt(y# zI?x_H!JTNHFqlx8P+Rm8<@%Zj-CcA0r0x3Rq@B{F^rYdWAUR#%!u?LB>qtQ^UdAZ# zD5f;G%JsfWY{4$W)0v2_iwd^(d8M~gUMmME2CP!=e_=n78A;jel=jM_uXEb^OWGIy zWsbN+jQqv6IEuDX)^4HQ6eZ5?`{@q%lwMy^YQw`!;Irvd8B!SxcY;op&RO}S7osV4 zDVixNI#7IJ(Y>P4A~E+R_fC9b;c>TfWmfJ6ZsUa_Z&Hihi@1kp-BjEtg@+1aizo#Q zyxH9d&y9FN&t`{aXY5^smo#B&CWFU9~`o;+WG>MlG5Ty9Uml(Wy<}P_4a! zE-K7LU=8dHJStq5ZupxCji(2#-DEq7Oljw*Ek#@&m0Q^VX}`)nLx&nT**mZ(H7%7; zY*Xw~Y&~0VTsD`_y;pBp>$x5!Y0+k<<*j8+N$lRqopKv+8_5^VS8zllSIQtofq5#q zwK&c*dj5QR_S55$*$#~S(a`#-?|aTcH}D&@@A)g%;sn78aSg#C@$TKI=SD#clq$4s z=ua2yv1W5@9x;WO_VH3uO)u(Bzt!(nQdg<1-s2kMv{qW{9Zf+^HBEcR8OQldSI3%r z`|llcIONdQ^|I@B*V_!EEHwO`{#4df*1N2+YM-MaM|G~$ds|ytn=g}JSUI{w(F|2Qen^lq3G*>Wm zf8KbWIv+cH>!snX{n?%d!LORzu^(I}d(FgdrN9EmN+O)G&QX-gDRn3bn&eUX?m=}P zr)ZV9plJHllyz&|bR1wD^mF5U>t?1Zjj~KHAW*kAe7oKLs=^e%fkKw-KQgNeM6u2|uzMh?tj%g9( zBx=y)iQyBoR*1jn%YFivV0+4b4+5f7W=uczbnM66QtT)0C$aHx#dK)s5I%_8xkwgwORQClTeSpwJ=FarvDGVvY!wpdMeY(xLS`7teX5 zl||HRhB*dC9dCSbp|O%La8}G+bTazf?C`s}W6lJq=U652dkj~_R6hQ4ncR?Kn*90q z+QT7}DzS_g&oYK@JSr@1sqyRa@AIGjJgS%NC7D{3_Bl&W>X-Cc*w@OSac`0se*`M!}#;=46^@4QNQ-B-gu`iH#gRyRyL zo({S5xjXjz_mkIc*DF@d%HoTr*HYJM$4Z@OL33^Vef%3j>XKFOYTop#_M!2viEj_g zT1&S5_H>iGz|oU1mT>?5X6q+)CN6YhdR1g>b*}_+@XXcll8-{Ke>l?y-|njD;uC?2mnxTUVwI)g9{gUVO}6EFYTOStK z?1QEV#3wV>#`KSTY>!`$X13zy?aj_IMFnWYTL0|3?%wp?+_c5CQ{o;V9Sue}xU?cs{stNit3rR3x-0si!*9}7k| zF7WP^N^DC4+l}GR<`7wAz`~E=O9t7}h!nCbndlc9)IsLmI{CG!cmkW?=zt_KXb|GI z4ooWfsCDk^;$WkT01+rK7sHsvjEcVdMyNWMatyRGTms*)7ZoPYMep zA^gB*rXW-Zl1D%zvx%S(+9`T4G6W6&ixPOnyQ-g#j*kD^l}7u=JDBZC{%^kj zFL5wFlu3rVl7ktiStQ=<{MENZF_BmnnaF0a@C?SOpN%{mz+f|i0~kz@z5xUd(sm@0 zsPt{i{=XoOj!0X2Fq=pxk!^8kFpmU6rTQTeCRT>pAs#PEI$!NU%COWwJ) zwUsw;YlJ5m*y1ekA%mx0dWr%tVgBBZ4QT1}%17T|QZh+GD|>c8)x1$$<;$bFUFXY^ eH%$H@?+^!+6#;XN=3*rt9I&-?!j)lsF8mviygF|H literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/images/Therm_7x16.png b/applications/plugins/weather_station/images/Therm_7x16.png new file mode 100644 index 0000000000000000000000000000000000000000..7c55500b7e7ea9112c92ba966643e3684887eb92 GIT binary patch literal 3611 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 literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/images/station_icon.png b/applications/plugins/weather_station/images/station_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b839eeb7aebfcf8aee9b2ba9570a3d9cf1d8af12 GIT binary patch literal 3607 zcmaJ@c|276-#;SzmMlrSV+e^EGu9c#GBfrqj0{=~GsdJbV@!=DqOzq+l0Bl5T}6>l zwopRIzGX{t@mx#XY`<~ut>5#<({oM616_IinfXlyJJOj zkl+P5pku?t6BJeC_(UzE<#Glk?CTGhm~hFoW=C_z#f9CJuvZnl!9Tz=Eq6ce+JopD z?=~lbANcgutbcCbdERd@TfCB4-pNzPE0}DqVXqY?Sb#oy;M291rsj!hh*3Dd0v!4N$jm_A&>aR04G!q5@?AuOOgqA02TV;0gxZTjk{gfa__1 zxaqR9{+}mGMQ2sml}anquTnsmguC&o`SCbALyjtUSV)}^*Cy;Eq#HpR0@I}7;hG|! zR^9_Zc7g;u+m-Er4&l;{4(+%K;d5$VUvuw*Jv^p%W;%=LMgTCu4DH%cg_a)<)8h`K z?%Y4N$mM-jAp7EatXG+c(Q_bsZpe9szE>NQoLg0cgEQ091C~u_H^MvTkR5WvxjN_V z3x9>`Z>_I=dNrvK4nGra#lXFNH-St*I)nRS!v0I*z5 ztN*CX%NgcEv00KjI4t2{B5S*<^$TE~5) ze*Z|lz?QrAb+kfL16mZ#PYgT4a6evr6|EfE{zO*A9s1EoRqVw9H`+H*g>) zbJ=ASV;>^wwCT$Py;cBzbt|&Q40b2H%pUbo@HwU`U+3_3)B6G+h)h^|ykvI92gb9* zsGHh3vmc7QMTX)HFfHnJUk>qTG+j`KG&iE3>ZfLUi-iUejvAZet?{-4=J}u_3YrIo_;mO&9e zZ>}oeCl-GYDjFP(Y0^;;i^0H^s&4JGc={06E!J(??du>vr&^GqX?|Ef@$C@Nr;G=2 zM-_CUHDWq*x^Y@#T4q{q*^NKb!^rR9hU28N!@KjA(leqnnls#_RJgIzgLH?{{2bf? zt$0^-Nlt~sWBaK5gPIr95$)F`Ev#}&?kDve_LlNqr#$|`e0g9r>8NeW2j}j#IkPV* zUpQA;fqa}wL;LKf=ca0!K?0uAj#6^wM+r!YAs z8DC^xWM5=9U#nfZkeB(W)}-2HGhEoX#Zu|Ck{LO^V}ItXCCB=zORDn@P%^7}T5M<{AgxJcGjHq`$aLmYVuIhNjWchNB9&1&)-l#K5b?HtgU zsyNtoyor+On9*ZKmLgaAUt5Wejj_7g21zl1WXLp+w$@HtGS~dhOayhWWoDFTG%Vx~ zKVH;cq%1~_+s-V*=8F6-aW`nU3&;yQ#zE$c z2{#UR+qbz9bXo3ooFQ_U^sQ`g!T4r&m9d0z{MC}HGxa5M-mQP!Dv?{CP3=hNpa@5t z4E@sgfrs#!5Zf3ks1y+u;T&lgM~}uI?t-OgvARYu{^Qv1*ktTj1{r3Dc&uirrD9MB zIj=vY^HTGrVKcIed&QmXBH;nn!o!b;R+=A^(>uv99v^$a~Qr=wvt zB2TgaBBqK=HnNVk)xGmS#-b|uk~fbnA7mYi;}2|*Z6Jf8UD{pI1DMk)M{SqQRcwN8 z|B+Cm6{zq=BUg2%>bg?Ftr}|~>(LBkmSp-R5EYI>*21pcPPpZVE|jxLtRR0SfA+f6 zR!oX0+j-*~TM0dy#Pimt{8sUP7d8G0^rJ60SLJ>co-#7Y+3R(C%sWJKQPzp}h4N5VE@I5k9#y}$GMydF)REorv z0p*c^8JQ^ByVq$Wcb*j#HB}Kiy}G^TK98woxd|s1rhHBj_7xf&dL}e}>e<5DH)}tGMqt?ZxiV=Y;+Z->yZO;F@omfAkK!)vRwD zynQw7#NlMq>(xuzwAd_PH!O`QoZCp=q@F3UrTQ(jaCUQU^T-QV^jCfCmF-619OC;< z%$o_f{Pt=mbBU!Uq%Gw1``zYAc{(<5dTyO>eEp~pa_G{{;Nl6Mp5HFXl4YUi(dorJF>ae#V6XK#nJk@-(bd|(KP7kjdb1Fy(b|7f2^T9Z3GPiwsY8@4V#qT+xvj28qC=F>o~6g&&1H=}lwO{Jm*(5L^QS11Z}O zJSo0=mg9d@Y;2GgzWz*tuP4a@ivqJnAQTD_iO@0Cgz4&fn>%=Kl)VF6V2yDBLmuu#BGDc20P%5&ib>ZsNy~+CaLb9y8-;NI`}zRsF?bn; PjRq`CY_X+i_pAQ_NTe=6 literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/protocols/gt_wt_03.c b/applications/plugins/weather_station/protocols/gt_wt_03.c new file mode 100644 index 00000000..1492374c --- /dev/null +++ b/applications/plugins/weather_station/protocols/gt_wt_03.c @@ -0,0 +1,341 @@ +#include "gt_wt_03.h" + +#define TAG "WSProtocolGT_WT03" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/5f0ff6db624270a4598958ab9dd79bb385ced3ef/src/devices/gt_wt_03.c + * + * + * Globaltronics GT-WT-03 sensor on 433.92MHz. + * The 01-set sensor has 60 ms packet gap with 10 repeats. + * The 02-set sensor has no packet gap with 23 repeats. + * Example: + * {41} 17 cf be fa 6a 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-Yes ] + * {41} 17 cf be fa 6a 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-Yes Batt-Changed ] + * {41} 17 cf fe fa ea 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-No Batt-Changed ] + * {41} 01 cf 6f 11 b2 80 [ S2 C2 23,8 C 74.8 F 48% Bat-LOW Manual-No ] + * {41} 01 c8 d0 2b 76 80 [ S2 C3 -4,4 C 24.1 F 55% Bat-Good Manual-No Batt-Changed ] + * Format string: + * ID:8h HUM:8d B:b M:b C:2d TEMP:12d CHK:8h 1x + * Data layout: + * TYP IIIIIIII HHHHHHHH BMCCTTTT TTTTTTTT XXXXXXXX + * - I: Random Device Code: changes with battery reset + * - H: Humidity: 8 Bit 00-99, Display LL=10%, Display HH=110% (Range 20-95%) + * - B: Battery: 0=OK 1=LOW + * - M: Manual Send Button Pressed: 0=not pressed, 1=pressed + * - C: Channel: 00=CH1, 01=CH2, 10=CH3 + * - T: Temperature: 12 Bit 2's complement, scaled by 10, range-50.0 C (-50.1 shown as Lo) to +70.0 C (+70.1 C is shown as Hi) + * - X: Checksum, xor shifting key per byte + * Humidity: + * - the working range is 20-95 % + * - if "LL" in display view it sends 10 % + * - if "HH" in display view it sends 110% + * Checksum: + * Per byte xor the key for each 1-bit, shift per bit. Key list per bit, starting at MSB: + * - 0x00 [07] + * - 0x80 [06] + * - 0x40 [05] + * - 0x20 [04] + * - 0x10 [03] + * - 0x88 [02] + * - 0xc4 [01] + * - 0x62 [00] + * Note: this can also be seen as lower byte of a Galois/Fibonacci LFSR-16, gen 0x00, init 0x3100 (or 0x62 if reversed) resetting at every byte. + * Battery voltages: + * - U=<2,65V +- ~5% Battery indicator + * - U=>2.10C +- 5% plausible readings + * - U=2,00V +- ~5% Temperature offset -5°C Humidity offset unknown + * - U=<1,95V +- ~5% does not initialize anymore + * - U=1,90V +- 5% temperature offset -15°C + * - U=1,80V +- 5% Display is showing refresh pattern + * - U=1.75V +- ~5% TX causes cut out + * + */ + +static const SubGhzBlockConst ws_protocol_gt_wt_03_const = { + .te_short = 285, + .te_long = 570, + .te_delta = 120, + .min_count_bit_for_found = 41, +}; + +struct WSProtocolDecoderGT_WT03 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderGT_WT03 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + GT_WT03DecoderStepReset = 0, + GT_WT03DecoderStepCheckPreambule, + GT_WT03DecoderStepSaveDuration, + GT_WT03DecoderStepCheckDuration, +} GT_WT03DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_gt_wt_03_decoder = { + .alloc = ws_protocol_decoder_gt_wt_03_alloc, + .free = ws_protocol_decoder_gt_wt_03_free, + + .feed = ws_protocol_decoder_gt_wt_03_feed, + .reset = ws_protocol_decoder_gt_wt_03_reset, + + .get_hash_data = ws_protocol_decoder_gt_wt_03_get_hash_data, + .serialize = ws_protocol_decoder_gt_wt_03_serialize, + .deserialize = ws_protocol_decoder_gt_wt_03_deserialize, + .get_string = ws_protocol_decoder_gt_wt_03_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_gt_wt_03_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_gt_wt_03 = { + .name = WS_PROTOCOL_GT_WT_03_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_gt_wt_03_decoder, + .encoder = &ws_protocol_gt_wt_03_encoder, +}; + +void* ws_protocol_decoder_gt_wt_03_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderGT_WT03* instance = malloc(sizeof(WSProtocolDecoderGT_WT03)); + instance->base.protocol = &ws_protocol_gt_wt_03; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_gt_wt_03_free(void* context) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + free(instance); +} + +void ws_protocol_decoder_gt_wt_03_reset(void* context) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + instance->decoder.parser_step = GT_WT03DecoderStepReset; +} + +static bool ws_protocol_gt_wt_03_check_crc(WSProtocolDecoderGT_WT03* instance) { + uint8_t msg[] = { + instance->decoder.decode_data >> 33, + instance->decoder.decode_data >> 25, + instance->decoder.decode_data >> 17, + instance->decoder.decode_data >> 9}; + + uint8_t sum = 0; + for(unsigned k = 0; k < sizeof(msg); ++k) { + uint8_t data = msg[k]; + uint16_t key = 0x3100; + for(int i = 7; i >= 0; --i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) sum ^= key & 0xff; + // roll the key right + key = (key >> 1); + } + } + return ((sum ^ (uint8_t)((instance->decoder.decode_data >> 1) & 0xFF)) == 0x2D); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_gt_wt_03_remote_controller(WSBlockGeneric* instance) { + instance->id = instance->data >> 33; + instance->humidity = (instance->data >> 25) & 0xFF; + + if(instance->humidity <= 10) { // actually the sensors sends 10 below working range of 20% + instance->humidity = 0; + } else if(instance->humidity > 95) { // actually the sensors sends 110 above working range of 90% + instance->humidity = 100; + } + + instance->battery_low = (instance->data >> 24) & 1; + instance->btn = (instance->data >> 23) & 1; + instance->channel = ((instance->data >> 21) & 0x03) + 1; + + if(!((instance->data >> 20) & 1)) { + instance->temp = (float)((instance->data >> 9) & 0x07FF) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 9) & 0x07FF) + 1) / -10.0f; + } +} + +void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + + switch(instance->decoder.parser_step) { + case GT_WT03DecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) < + ws_protocol_gt_wt_03_const.te_delta * 2)) { + instance->decoder.parser_step = GT_WT03DecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case GT_WT03DecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short * 3) < + ws_protocol_gt_wt_03_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) < + ws_protocol_gt_wt_03_const.te_delta * 2)) { + //Found preambule + instance->header_count++; + } else if(instance->header_count == 4) { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short) < + ws_protocol_gt_wt_03_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_long) < + ws_protocol_gt_wt_03_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_long) < + ws_protocol_gt_wt_03_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short) < + ws_protocol_gt_wt_03_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = GT_WT03DecoderStepReset; + } + } else { + instance->decoder.parser_step = GT_WT03DecoderStepReset; + } + } + break; + + case GT_WT03DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = GT_WT03DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = GT_WT03DecoderStepReset; + } + break; + + case GT_WT03DecoderStepCheckDuration: + if(!level) { + if(((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short * 3) < + ws_protocol_gt_wt_03_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) < + ws_protocol_gt_wt_03_const.te_delta * 2))) { + if((instance->decoder.decode_count_bit == + ws_protocol_gt_wt_03_const.min_count_bit_for_found) && + ws_protocol_gt_wt_03_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_gt_wt_03_remote_controller(&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->header_count = 1; + instance->decoder.parser_step = GT_WT03DecoderStepCheckPreambule; + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short) < + ws_protocol_gt_wt_03_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_long) < + ws_protocol_gt_wt_03_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_long) < + ws_protocol_gt_wt_03_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short) < + ws_protocol_gt_wt_03_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = GT_WT03DecoderStepReset; + } + } else { + instance->decoder.parser_step = GT_WT03DecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_gt_wt_03_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_gt_wt_03_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%d.%d C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (int16_t)instance->generic.temp, + abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/gt_wt_03.h b/applications/plugins/weather_station/protocols/gt_wt_03.h new file mode 100644 index 00000000..fd9536e3 --- /dev/null +++ b/applications/plugins/weather_station/protocols/gt_wt_03.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_GT_WT_03_NAME "GT-WT03" + +typedef struct WSProtocolDecoderGT_WT03 WSProtocolDecoderGT_WT03; +typedef struct WSProtocolEncoderGT_WT03 WSProtocolEncoderGT_WT03; + +extern const SubGhzProtocolDecoder ws_protocol_gt_wt_03_decoder; +extern const SubGhzProtocolEncoder ws_protocol_gt_wt_03_encoder; +extern const SubGhzProtocol ws_protocol_gt_wt_03; + +/** + * Allocate WSProtocolDecoderGT_WT03. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderGT_WT03* pointer to a WSProtocolDecoderGT_WT03 instance + */ +void* ws_protocol_decoder_gt_wt_03_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderGT_WT03. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + */ +void ws_protocol_decoder_gt_wt_03_free(void* context); + +/** + * Reset decoder WSProtocolDecoderGT_WT03. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + */ +void ws_protocol_decoder_gt_wt_03_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderGT_WT03. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_gt_wt_03_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderGT_WT03. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + * @param output Resulting text + */ +void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/infactory.c b/applications/plugins/weather_station/protocols/infactory.c new file mode 100644 index 00000000..525b5557 --- /dev/null +++ b/applications/plugins/weather_station/protocols/infactory.c @@ -0,0 +1,296 @@ +#include "infactory.h" + +#define TAG "WSProtocolInfactory" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/infactory.c + * + * Analysis using Genuino (see http://gitlab.com/hp-uno, e.g. uno_log_433): + * Observed On-Off-Key (OOK) data pattern: + * preamble syncPrefix data...(40 bit) syncPostfix + * HHLL HHLL HHLL HHLL HLLLLLLLLLLLLLLLL (HLLLL HLLLLLLLL HLLLL HLLLLLLLL ....) HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL + * Breakdown: + * - four preamble pairs '1'/'0' each with a length of ca. 1000us + * - syncPre, syncPost, data0, data1 have a '1' start pulse of ca. 500us + * - syncPre pulse before dataPtr has a '0' pulse length of ca. 8000us + * - data0 (0-bits) have then a '0' pulse length of ca. 2000us + * - data1 (1-bits) have then a '0' pulse length of ca. 4000us + * - syncPost after dataPtr has a '0' pulse length of ca. 16000us + * This analysis is the reason for the new r_device definitions below. + * NB: pulse_slicer_ppm does not use .gap_limit if .tolerance is set. + * + * Outdoor sensor, transmits temperature and humidity data + * - inFactory NC-3982-913/NX-5817-902, Pearl (for FWS-686 station) + * - nor-tec 73383 (weather station + sensor), Schou Company AS, Denmark + * - DAY 73365 (weather station + sensor), Schou Company AS, Denmark + * Known brand names: inFactory, nor-tec, GreenBlue, DAY. Manufacturer in China. + * Transmissions includes an id. Every 60 seconds the sensor transmits 6 packets: + * 0000 1111 | 0011 0000 | 0101 1100 | 1110 0111 | 0110 0001 + * iiii iiii | cccc ub?? | tttt tttt | tttt hhhh | hhhh ??nn + * - i: identification; changes on battery switch + * - c: CRC-4; CCITT checksum, see below for computation specifics + * - u: unknown; (sometimes set at power-on, but not always) + * - b: battery low; flag to indicate low battery voltage + * - h: Humidity; BCD-encoded, each nibble is one digit, 'A0' means 100%rH + * - t: Temperature; in °F as binary number with one decimal place + 90 °F offset + * - n: Channel; Channel number 1 - 3 + * + */ + +static const SubGhzBlockConst ws_protocol_infactory_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 40, +}; + +struct WSProtocolDecoderInfactory { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderInfactory { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + InfactoryDecoderStepReset = 0, + InfactoryDecoderStepCheckPreambule, + InfactoryDecoderStepSaveDuration, + InfactoryDecoderStepCheckDuration, +} InfactoryDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_infactory_decoder = { + .alloc = ws_protocol_decoder_infactory_alloc, + .free = ws_protocol_decoder_infactory_free, + + .feed = ws_protocol_decoder_infactory_feed, + .reset = ws_protocol_decoder_infactory_reset, + + .get_hash_data = ws_protocol_decoder_infactory_get_hash_data, + .serialize = ws_protocol_decoder_infactory_serialize, + .deserialize = ws_protocol_decoder_infactory_deserialize, + .get_string = ws_protocol_decoder_infactory_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_infactory_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_infactory = { + .name = WS_PROTOCOL_INFACTORY_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_infactory_decoder, + .encoder = &ws_protocol_infactory_encoder, +}; + +void* ws_protocol_decoder_infactory_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderInfactory* instance = malloc(sizeof(WSProtocolDecoderInfactory)); + instance->base.protocol = &ws_protocol_infactory; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_infactory_free(void* context) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + free(instance); +} + +void ws_protocol_decoder_infactory_reset(void* context) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + instance->decoder.parser_step = InfactoryDecoderStepReset; +} + +static bool ws_protocol_infactory_check_crc(WSProtocolDecoderInfactory* instance) { + uint8_t msg[] = { + instance->decoder.decode_data >> 32, + (((instance->decoder.decode_data >> 24) & 0x0F) | (instance->decoder.decode_data & 0x0F) + << 4), + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8, + instance->decoder.decode_data}; + + uint8_t crc = + subghz_protocol_blocks_crc4(msg, 4, 0x13, 0); // Koopmann 0x9, CCITT-4; FP-4; ITU-T G.704 + crc ^= msg[4] >> 4; // last nibble is only XORed + return (crc == ((instance->decoder.decode_data >> 28) & 0x0F)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_infactory_remote_controller(WSBlockGeneric* instance) { + instance->id = instance->data >> 32; + instance->battery_low = (instance->data >> 26) & 1; + instance->temp = ws_block_generic_fahrenheit_to_celsius( + ((float)((instance->data >> 12) & 0x0FFF) - 900.0f) / 10.0f); + instance->humidity = + (((instance->data >> 8) & 0x0F) * 10) + ((instance->data >> 4) & 0x0F); // BCD, 'A0'=100%rH + instance->channel = instance->data & 0x03; +} + +void ws_protocol_decoder_infactory_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + + switch(instance->decoder.parser_step) { + case InfactoryDecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 2) < + ws_protocol_infactory_const.te_delta * 2)) { + instance->decoder.parser_step = InfactoryDecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case InfactoryDecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short * 2) < + ws_protocol_infactory_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 2) < + ws_protocol_infactory_const.te_delta * 2)) { + //Found preambule + instance->header_count++; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) < + ws_protocol_infactory_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 16) < + ws_protocol_infactory_const.te_delta * 8)) { + //Found syncPrefix + if(instance->header_count > 3) { + instance->decoder.parser_step = InfactoryDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + } else { + instance->decoder.parser_step = InfactoryDecoderStepReset; + } + } + break; + + case InfactoryDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = InfactoryDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = InfactoryDecoderStepReset; + } + break; + + case InfactoryDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)ws_protocol_infactory_const.te_short * 30)) { + //Found syncPostfix + if((instance->decoder.decode_count_bit == + ws_protocol_infactory_const.min_count_bit_for_found) && + ws_protocol_infactory_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_infactory_remote_controller(&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 = InfactoryDecoderStepReset; + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) < + ws_protocol_infactory_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_infactory_const.te_long) < + ws_protocol_infactory_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = InfactoryDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) < + ws_protocol_infactory_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_infactory_const.te_long * 2) < + ws_protocol_infactory_const.te_delta * 4)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = InfactoryDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = InfactoryDecoderStepReset; + } + } else { + instance->decoder.parser_step = InfactoryDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_infactory_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_infactory_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%d.%d C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (int16_t)instance->generic.temp, + abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/infactory.h b/applications/plugins/weather_station/protocols/infactory.h new file mode 100644 index 00000000..2792ab98 --- /dev/null +++ b/applications/plugins/weather_station/protocols/infactory.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_INFACTORY_NAME "inFactory-TH" + +typedef struct WSProtocolDecoderInfactory WSProtocolDecoderInfactory; +typedef struct WSProtocolEncoderInfactory WSProtocolEncoderInfactory; + +extern const SubGhzProtocolDecoder ws_protocol_infactory_decoder; +extern const SubGhzProtocolEncoder ws_protocol_infactory_encoder; +extern const SubGhzProtocol ws_protocol_infactory; + +/** + * Allocate WSProtocolDecoderInfactory. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderInfactory* pointer to a WSProtocolDecoderInfactory instance + */ +void* ws_protocol_decoder_infactory_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderInfactory. + * @param context Pointer to a WSProtocolDecoderInfactory instance + */ +void ws_protocol_decoder_infactory_free(void* context); + +/** + * Reset decoder WSProtocolDecoderInfactory. + * @param context Pointer to a WSProtocolDecoderInfactory instance + */ +void ws_protocol_decoder_infactory_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderInfactory instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_infactory_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderInfactory instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderInfactory. + * @param context Pointer to a WSProtocolDecoderInfactory instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_infactory_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderInfactory. + * @param context Pointer to a WSProtocolDecoderInfactory instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderInfactory instance + * @param output Resulting text + */ +void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/nexus_th.c b/applications/plugins/weather_station/protocols/nexus_th.c new file mode 100644 index 00000000..e40f3348 --- /dev/null +++ b/applications/plugins/weather_station/protocols/nexus_th.c @@ -0,0 +1,261 @@ +#include "nexus_th.h" + +#define TAG "WSProtocolNexus_TH" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/ef2d37cf51e3264d11cde9149ef87de2f0a4d37a/src/devices/nexus.c + * + * Nexus sensor protocol with ID, temperature and optional humidity + * also FreeTec (Pearl) NC-7345 sensors for FreeTec Weatherstation NC-7344, + * also infactory/FreeTec (Pearl) NX-3980 sensors for infactory/FreeTec NX-3974 station, + * also Solight TE82S sensors for Solight TE76/TE82/TE83/TE84 stations, + * also TFA 30.3209.02 temperature/humidity sensor. + * The sensor sends 36 bits 12 times, + * the packets are ppm modulated (distance coding) with a pulse of ~500 us + * followed by a short gap of ~1000 us for a 0 bit or a long ~2000 us gap for a + * 1 bit, the sync gap is ~4000 us. + * The data is grouped in 9 nibbles: + * [id0] [id1] [flags] [temp0] [temp1] [temp2] [const] [humi0] [humi1] + * - The 8-bit id changes when the battery is changed in the sensor. + * - flags are 4 bits B 0 C C, where B is the battery status: 1=OK, 0=LOW + * - and CC is the channel: 0=CH1, 1=CH2, 2=CH3 + * - temp is 12 bit signed scaled by 10 + * - const is always 1111 (0x0F) + * - humidity is 8 bits + * The sensors can be bought at Clas Ohlsen (Nexus) and Pearl (infactory/FreeTec). + * + */ + +#define NEXUS_TH_CONST_DATA 0b1111 + +static const SubGhzBlockConst ws_protocol_nexus_th_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 36, +}; + +struct WSProtocolDecoderNexus_TH { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderNexus_TH { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + Nexus_THDecoderStepReset = 0, + Nexus_THDecoderStepSaveDuration, + Nexus_THDecoderStepCheckDuration, +} Nexus_THDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_nexus_th_decoder = { + .alloc = ws_protocol_decoder_nexus_th_alloc, + .free = ws_protocol_decoder_nexus_th_free, + + .feed = ws_protocol_decoder_nexus_th_feed, + .reset = ws_protocol_decoder_nexus_th_reset, + + .get_hash_data = ws_protocol_decoder_nexus_th_get_hash_data, + .serialize = ws_protocol_decoder_nexus_th_serialize, + .deserialize = ws_protocol_decoder_nexus_th_deserialize, + .get_string = ws_protocol_decoder_nexus_th_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_nexus_th_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_nexus_th = { + .name = WS_PROTOCOL_NEXUS_TH_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_nexus_th_decoder, + .encoder = &ws_protocol_nexus_th_encoder, +}; + +void* ws_protocol_decoder_nexus_th_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderNexus_TH* instance = malloc(sizeof(WSProtocolDecoderNexus_TH)); + instance->base.protocol = &ws_protocol_nexus_th; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_nexus_th_free(void* context) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + free(instance); +} + +void ws_protocol_decoder_nexus_th_reset(void* context) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + instance->decoder.parser_step = Nexus_THDecoderStepReset; +} + +static bool ws_protocol_nexus_th_check(WSProtocolDecoderNexus_TH* instance) { + uint8_t type = (instance->decoder.decode_data >> 8) & 0x0F; + + if((type == NEXUS_TH_CONST_DATA) && ((instance->decoder.decode_data >> 4) != 0xffffffff)) { + return true; + } else { + return false; + } + return true; +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_nexus_th_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 28) & 0xFF; + instance->battery_low = !((instance->data >> 27) & 1); + instance->channel = ((instance->data >> 24) & 0x03) + 1; + + if(!((instance->data >> 23) & 1)) { + instance->temp = (float)((instance->data >> 12) & 0x07FF) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 12) & 0x07FF) + 1) / -10.0f; + } + + instance->humidity = instance->data & 0xFF; +} + +void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + + switch(instance->decoder.parser_step) { + case Nexus_THDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 8) < + ws_protocol_nexus_th_const.te_delta * 4)) { + //Found sync + instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case Nexus_THDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Nexus_THDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Nexus_THDecoderStepReset; + } + break; + + case Nexus_THDecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 8) < + ws_protocol_nexus_th_const.te_delta * 4) { + //Found sync + instance->decoder.parser_step = Nexus_THDecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_nexus_th_const.min_count_bit_for_found) && + ws_protocol_nexus_th_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_nexus_th_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.parser_step = Nexus_THDecoderStepCheckDuration; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_nexus_th_const.te_short) < + ws_protocol_nexus_th_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 2) < + ws_protocol_nexus_th_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_nexus_th_const.te_short) < + ws_protocol_nexus_th_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 4) < + ws_protocol_nexus_th_const.te_delta * 4)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Nexus_THDecoderStepReset; + } + } else { + instance->decoder.parser_step = Nexus_THDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_nexus_th_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_nexus_th_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%d.%d C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (int16_t)instance->generic.temp, + abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/nexus_th.h b/applications/plugins/weather_station/protocols/nexus_th.h new file mode 100644 index 00000000..5450f004 --- /dev/null +++ b/applications/plugins/weather_station/protocols/nexus_th.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_NEXUS_TH_NAME "Nexus-TH" + +typedef struct WSProtocolDecoderNexus_TH WSProtocolDecoderNexus_TH; +typedef struct WSProtocolEncoderNexus_TH WSProtocolEncoderNexus_TH; + +extern const SubGhzProtocolDecoder ws_protocol_nexus_th_decoder; +extern const SubGhzProtocolEncoder ws_protocol_nexus_th_encoder; +extern const SubGhzProtocol ws_protocol_nexus_th; + +/** + * Allocate WSProtocolDecoderNexus_TH. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderNexus_TH* pointer to a WSProtocolDecoderNexus_TH instance + */ +void* ws_protocol_decoder_nexus_th_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderNexus_TH. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + */ +void ws_protocol_decoder_nexus_th_free(void* context); + +/** + * Reset decoder WSProtocolDecoderNexus_TH. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + */ +void ws_protocol_decoder_nexus_th_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderNexus_TH. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_nexus_th_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderNexus_TH. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + * @param output Resulting text + */ +void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/plugins/weather_station/protocols/protocol_items.c new file mode 100644 index 00000000..ea02ec05 --- /dev/null +++ b/applications/plugins/weather_station/protocols/protocol_items.c @@ -0,0 +1,12 @@ +#include "protocol_items.h" + +const SubGhzProtocol* weather_station_protocol_registry_items[] = { + &ws_protocol_infactory, + &ws_protocol_thermopro_tx4, + &ws_protocol_nexus_th, + &ws_protocol_gt_wt_03, +}; + +const SubGhzProtocolRegistry weather_station_protocol_registry = { + .items = weather_station_protocol_registry_items, + .size = COUNT_OF(weather_station_protocol_registry_items)}; \ No newline at end of file diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/plugins/weather_station/protocols/protocol_items.h new file mode 100644 index 00000000..eac28a23 --- /dev/null +++ b/applications/plugins/weather_station/protocols/protocol_items.h @@ -0,0 +1,9 @@ +#pragma once +#include "../weather_station_app_i.h" + +#include "infactory.h" +#include "thermopro_tx4.h" +#include "nexus_th.h" +#include "gt_wt_03.h" + +extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/applications/plugins/weather_station/protocols/thermopro_tx4.c b/applications/plugins/weather_station/protocols/thermopro_tx4.c new file mode 100644 index 00000000..9a2eacb2 --- /dev/null +++ b/applications/plugins/weather_station/protocols/thermopro_tx4.c @@ -0,0 +1,260 @@ +#include "thermopro_tx4.h" + +#define TAG "WSProtocolThermoPRO_TX4" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/thermopro_tx2.c + * + * The sensor sends 37 bits 6 times, before the first packet there is a sync pulse. + * The packets are ppm modulated (distance coding) with a pulse of ~500 us + * followed by a short gap of ~2000 us for a 0 bit or a long ~4000 us gap for a + * 1 bit, the sync gap is ~9000 us. + * The data is grouped in 9 nibbles + * [type] [id0] [id1] [flags] [temp0] [temp1] [temp2] [humi0] [humi1] + * - type: 4 bit fixed 1001 (9) or 0110 (5) + * - id: 8 bit a random id that is generated when the sensor starts, could include battery status + * the same batteries often generate the same id + * - flags(3): is 1 when the battery is low, otherwise 0 (ok) + * - flags(2): is 1 when the sensor sends a reading when pressing the button on the sensor + * - flags(1,0): the channel number that can be set by the sensor (1, 2, 3, X) + * - temp: 12 bit signed scaled by 10 + * - humi: 8 bit always 11001100 (0xCC) if no humidity sensor is available + * + */ + +#define THERMO_PRO_TX4_TYPE_1 0b1001 +#define THERMO_PRO_TX4_TYPE_2 0b0110 + +static const SubGhzBlockConst ws_protocol_thermopro_tx4_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 37, +}; + +struct WSProtocolDecoderThermoPRO_TX4 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderThermoPRO_TX4 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + ThermoPRO_TX4DecoderStepReset = 0, + ThermoPRO_TX4DecoderStepSaveDuration, + ThermoPRO_TX4DecoderStepCheckDuration, +} ThermoPRO_TX4DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_thermopro_tx4_decoder = { + .alloc = ws_protocol_decoder_thermopro_tx4_alloc, + .free = ws_protocol_decoder_thermopro_tx4_free, + + .feed = ws_protocol_decoder_thermopro_tx4_feed, + .reset = ws_protocol_decoder_thermopro_tx4_reset, + + .get_hash_data = ws_protocol_decoder_thermopro_tx4_get_hash_data, + .serialize = ws_protocol_decoder_thermopro_tx4_serialize, + .deserialize = ws_protocol_decoder_thermopro_tx4_deserialize, + .get_string = ws_protocol_decoder_thermopro_tx4_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_thermopro_tx4_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_thermopro_tx4 = { + .name = WS_PROTOCOL_THERMOPRO_TX4_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_thermopro_tx4_decoder, + .encoder = &ws_protocol_thermopro_tx4_encoder, +}; + +void* ws_protocol_decoder_thermopro_tx4_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderThermoPRO_TX4* instance = malloc(sizeof(WSProtocolDecoderThermoPRO_TX4)); + instance->base.protocol = &ws_protocol_thermopro_tx4; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_thermopro_tx4_free(void* context) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + free(instance); +} + +void ws_protocol_decoder_thermopro_tx4_reset(void* context) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; +} + +static bool ws_protocol_thermopro_tx4_check(WSProtocolDecoderThermoPRO_TX4* instance) { + uint8_t type = instance->decoder.decode_data >> 33; + + if((type == THERMO_PRO_TX4_TYPE_1) || (type == THERMO_PRO_TX4_TYPE_2)) { + return true; + } else { + return false; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_thermopro_tx4_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 25) & 0xFF; + instance->battery_low = (instance->data >> 24) & 1; + instance->btn = (instance->data >> 23) & 1; + instance->channel = ((instance->data >> 21) & 0x03) + 1; + + if(!((instance->data >> 20) & 1)) { + instance->temp = (float)((instance->data >> 9) & 0x07FF) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 9) & 0x07FF) + 1) / -10.0f; + } + + instance->humidity = (instance->data >> 1) & 0xFF; +} + +void ws_protocol_decoder_thermopro_tx4_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + + switch(instance->decoder.parser_step) { + case ThermoPRO_TX4DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_short * 18) < + ws_protocol_thermopro_tx4_const.te_delta * 10)) { + //Found sync + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case ThermoPRO_TX4DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; + } + break; + + case ThermoPRO_TX4DecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_short * 18) < + ws_protocol_thermopro_tx4_const.te_delta * 10) { + //Found sync + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_thermopro_tx4_const.min_count_bit_for_found) && + ws_protocol_thermopro_tx4_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_thermopro_tx4_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepCheckDuration; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_thermopro_tx4_const.te_short) < + ws_protocol_thermopro_tx4_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_long) < + ws_protocol_thermopro_tx4_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_thermopro_tx4_const.te_short) < + ws_protocol_thermopro_tx4_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_long * 2) < + ws_protocol_thermopro_tx4_const.te_delta * 4)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; + } + } else { + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_thermopro_tx4_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_thermopro_tx4_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%d.%d C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (int16_t)instance->generic.temp, + abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/thermopro_tx4.h b/applications/plugins/weather_station/protocols/thermopro_tx4.h new file mode 100644 index 00000000..1feae58d --- /dev/null +++ b/applications/plugins/weather_station/protocols/thermopro_tx4.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_THERMOPRO_TX4_NAME "ThermoPRO-TX4" + +typedef struct WSProtocolDecoderThermoPRO_TX4 WSProtocolDecoderThermoPRO_TX4; +typedef struct WSProtocolEncoderThermoPRO_TX4 WSProtocolEncoderThermoPRO_TX4; + +extern const SubGhzProtocolDecoder ws_protocol_thermopro_tx4_decoder; +extern const SubGhzProtocolEncoder ws_protocol_thermopro_tx4_encoder; +extern const SubGhzProtocol ws_protocol_thermopro_tx4; + +/** + * Allocate WSProtocolDecoderThermoPRO_TX4. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderThermoPRO_TX4* pointer to a WSProtocolDecoderThermoPRO_TX4 instance + */ +void* ws_protocol_decoder_thermopro_tx4_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderThermoPRO_TX4. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + */ +void ws_protocol_decoder_thermopro_tx4_free(void* context); + +/** + * Reset decoder WSProtocolDecoderThermoPRO_TX4. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + */ +void ws_protocol_decoder_thermopro_tx4_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_thermopro_tx4_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderThermoPRO_TX4. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_thermopro_tx4_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderThermoPRO_TX4. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + * @param output Resulting text + */ +void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/ws_generic.c b/applications/plugins/weather_station/protocols/ws_generic.c new file mode 100644 index 00000000..5740750a --- /dev/null +++ b/applications/plugins/weather_station/protocols/ws_generic.c @@ -0,0 +1,198 @@ +#include "ws_generic.h" +#include +#include +#include "../helpers/weather_station_types.h" + +#define TAG "WSBlockGeneric" + +void ws_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str) { + const char* preset_name_temp; + if(!strcmp(preset_name, "AM270")) { + preset_name_temp = "FuriHalSubGhzPresetOok270Async"; + } else if(!strcmp(preset_name, "AM650")) { + preset_name_temp = "FuriHalSubGhzPresetOok650Async"; + } else if(!strcmp(preset_name, "FM238")) { + preset_name_temp = "FuriHalSubGhzPreset2FSKDev238Async"; + } else if(!strcmp(preset_name, "FM476")) { + preset_name_temp = "FuriHalSubGhzPreset2FSKDev476Async"; + } else { + preset_name_temp = "FuriHalSubGhzPresetCustom"; + } + furi_string_set(preset_str, preset_name_temp); +} + +bool ws_block_generic_serialize( + WSBlockGeneric* instance, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(instance); + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + if(!flipper_format_write_header_cstr( + flipper_format, WS_KEY_FILE_TYPE, WS_KEY_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { + FURI_LOG_E(TAG, "Unable to add Frequency"); + break; + } + + ws_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + if(!flipper_format_write_string_cstr( + flipper_format, "Preset", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Preset"); + break; + } + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + flipper_format, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } + if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + + uint32_t temp_data = instance->id; + if(!flipper_format_write_uint32(flipper_format, "Id", &temp_data, 1)) { + FURI_LOG_E(TAG, "Unable to add Id"); + break; + } + + temp_data = instance->data_count_bit; + if(!flipper_format_write_uint32(flipper_format, "Bit", &temp_data, 1)) { + FURI_LOG_E(TAG, "Unable to add Bit"); + 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->data >> i * 8) & 0xFF; + } + + if(!flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Data"); + break; + } + + temp_data = instance->battery_low; + if(!flipper_format_write_uint32(flipper_format, "Batt", &temp_data, 1)) { + FURI_LOG_E(TAG, "Unable to add Battery_low"); + break; + } + + temp_data = instance->humidity; + if(!flipper_format_write_uint32(flipper_format, "Hum", &temp_data, 1)) { + FURI_LOG_E(TAG, "Unable to add Humidity"); + break; + } + + temp_data = instance->channel; + if(!flipper_format_write_uint32(flipper_format, "Ch", &temp_data, 1)) { + FURI_LOG_E(TAG, "Unable to add Channel"); + break; + } + + // temp_data = instance->btn; + // if(!flipper_format_write_uint32(flipper_format, "Btn", &temp_data, 1)) { + // FURI_LOG_E(TAG, "Unable to add Btn"); + // break; + // } + + float temp = instance->temp; + if(!flipper_format_write_float(flipper_format, "Temp", &temp, 1)) { + FURI_LOG_E(TAG, "Unable to add Temperature"); + break; + } + + res = true; + } while(false); + furi_string_free(temp_str); + return res; +} + +bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format) { + furi_assert(instance); + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + if(!flipper_format_read_uint32(flipper_format, "Id", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Id"); + break; + } + instance->id = (uint32_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + instance->data_count_bit = (uint8_t)temp_data; + + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->data = instance->data << 8 | key_data[i]; + } + + if(!flipper_format_read_uint32(flipper_format, "Batt", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Battery_low"); + break; + } + instance->battery_low = (uint8_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "Hum", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Humidity"); + break; + } + instance->humidity = (uint8_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "Ch", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Channel"); + break; + } + instance->channel = (uint8_t)temp_data; + + // if(!flipper_format_read_uint32(flipper_format, "Btn", (uint32_t*)&temp_data, 1)) { + // FURI_LOG_E(TAG, "Missing Btn"); + // break; + // } + // instance->btn = (uint8_t)temp_data; + + float temp; + if(!flipper_format_read_float(flipper_format, "Temp", (float*)&temp, 1)) { + FURI_LOG_E(TAG, "Missing Temperature"); + break; + } + instance->temp = temp; + + res = true; + } while(0); + + return res; +} + +float ws_block_generic_fahrenheit_to_celsius(float fahrenheit) { + return (fahrenheit - 32.0f) / 1.8f; +} \ No newline at end of file diff --git a/applications/plugins/weather_station/protocols/ws_generic.h b/applications/plugins/weather_station/protocols/ws_generic.h new file mode 100644 index 00000000..e0402b81 --- /dev/null +++ b/applications/plugins/weather_station/protocols/ws_generic.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include +#include "furi.h" +#include "furi_hal.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct WSBlockGeneric WSBlockGeneric; + +struct WSBlockGeneric { + const char* protocol_name; + uint64_t data; + uint32_t id; + uint8_t data_count_bit; + uint8_t battery_low; + uint8_t humidity; + uint8_t channel; + uint8_t btn; + float temp; +}; + +/** + * Get name preset. + * @param preset_name name preset + * @param preset_str Output name preset + */ +void ws_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str); + +/** + * Serialize data WSBlockGeneric. + * @param instance Pointer to a WSBlockGeneric instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_block_generic_serialize( + WSBlockGeneric* instance, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSBlockGeneric. + * @param instance Pointer to a WSBlockGeneric instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format); + +float ws_block_generic_fahrenheit_to_celsius(float fahrenheit); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/plugins/weather_station/scenes/weather_station_receiver.c b/applications/plugins/weather_station/scenes/weather_station_receiver.c new file mode 100644 index 00000000..670c8c38 --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_receiver.c @@ -0,0 +1,207 @@ +#include "../weather_station_app_i.h" +#include "../views/weather_station_receiver.h" + +static const NotificationSequence subghs_sequence_rx = { + &message_green_255, + + &message_vibro_on, + &message_note_c6, + &message_delay_50, + &message_sound_off, + &message_vibro_off, + + &message_delay_50, + NULL, +}; + +static const NotificationSequence subghs_sequence_rx_locked = { + &message_green_255, + + &message_display_backlight_on, + + &message_vibro_on, + &message_note_c6, + &message_delay_50, + &message_sound_off, + &message_vibro_off, + + &message_delay_500, + + &message_display_backlight_off, + NULL, +}; + +static void weather_station_scene_receiver_update_statusbar(void* context) { + WeatherStationApp* app = context; + FuriString* history_stat_str; + history_stat_str = furi_string_alloc(); + if(!ws_history_get_text_space_left(app->txrx->history, history_stat_str)) { + FuriString* frequency_str; + FuriString* modulation_str; + + frequency_str = furi_string_alloc(); + modulation_str = furi_string_alloc(); + + ws_get_frequency_modulation(app, frequency_str, modulation_str); + + ws_view_receiver_add_data_statusbar( + app->ws_receiver, + furi_string_get_cstr(frequency_str), + furi_string_get_cstr(modulation_str), + furi_string_get_cstr(history_stat_str)); + + furi_string_free(frequency_str); + furi_string_free(modulation_str); + } else { + ws_view_receiver_add_data_statusbar( + app->ws_receiver, furi_string_get_cstr(history_stat_str), "", ""); + } + furi_string_free(history_stat_str); +} + +void weather_station_scene_receiver_callback(WSCustomEvent event, void* context) { + furi_assert(context); + WeatherStationApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +static void weather_station_scene_receiver_add_to_history_callback( + SubGhzReceiver* receiver, + SubGhzProtocolDecoderBase* decoder_base, + void* context) { + furi_assert(context); + WeatherStationApp* app = context; + FuriString* str_buff; + str_buff = furi_string_alloc(); + + if(ws_history_add_to_history(app->txrx->history, decoder_base, app->txrx->preset) == + WSHistoryStateAddKeyNewDada) { + furi_string_reset(str_buff); + + ws_history_get_text_item_menu( + app->txrx->history, str_buff, ws_history_get_item(app->txrx->history) - 1); + ws_view_receiver_add_item_to_menu( + app->ws_receiver, + furi_string_get_cstr(str_buff), + ws_history_get_type_protocol( + app->txrx->history, ws_history_get_item(app->txrx->history) - 1)); + + weather_station_scene_receiver_update_statusbar(app); + notification_message(app->notifications, &sequence_blink_green_10); + if(app->lock != WSLockOn) { + notification_message(app->notifications, &subghs_sequence_rx); + } else { + notification_message(app->notifications, &subghs_sequence_rx_locked); + } + } + subghz_receiver_reset(receiver); + furi_string_free(str_buff); + app->txrx->rx_key_state = WSRxKeyStateAddKey; +} + +void weather_station_scene_receiver_on_enter(void* context) { + WeatherStationApp* app = context; + + FuriString* str_buff; + str_buff = furi_string_alloc(); + + if(app->txrx->rx_key_state == WSRxKeyStateIDLE) { + ws_preset_init(app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0); + ws_history_reset(app->txrx->history); + app->txrx->rx_key_state = WSRxKeyStateStart; + } + + ws_view_receiver_set_lock(app->ws_receiver, app->lock); + + //Load history to receiver + ws_view_receiver_exit(app->ws_receiver); + for(uint8_t i = 0; i < ws_history_get_item(app->txrx->history); i++) { + furi_string_reset(str_buff); + ws_history_get_text_item_menu(app->txrx->history, str_buff, i); + ws_view_receiver_add_item_to_menu( + app->ws_receiver, + furi_string_get_cstr(str_buff), + ws_history_get_type_protocol(app->txrx->history, i)); + app->txrx->rx_key_state = WSRxKeyStateAddKey; + } + furi_string_free(str_buff); + weather_station_scene_receiver_update_statusbar(app); + + ws_view_receiver_set_callback(app->ws_receiver, weather_station_scene_receiver_callback, app); + subghz_receiver_set_rx_callback( + app->txrx->receiver, weather_station_scene_receiver_add_to_history_callback, app); + + if(app->txrx->txrx_state == WSTxRxStateRx) { + ws_rx_end(app); + }; + if((app->txrx->txrx_state == WSTxRxStateIDLE) || (app->txrx->txrx_state == WSTxRxStateSleep)) { + ws_begin( + app, + subghz_setting_get_preset_data_by_name( + app->setting, furi_string_get_cstr(app->txrx->preset->name))); + + ws_rx(app, app->txrx->preset->frequency); + } + + ws_view_receiver_set_idx_menu(app->ws_receiver, app->txrx->idx_menu_chosen); + view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewReceiver); +} + +bool weather_station_scene_receiver_on_event(void* context, SceneManagerEvent event) { + WeatherStationApp* app = context; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case WSCustomEventViewReceiverBack: + // Stop CC1101 Rx + if(app->txrx->txrx_state == WSTxRxStateRx) { + ws_rx_end(app); + ws_sleep(app); + }; + app->txrx->hopper_state = WSHopperStateOFF; + app->txrx->idx_menu_chosen = 0; + subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, app); + + app->txrx->rx_key_state = WSRxKeyStateIDLE; + ws_preset_init( + app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0); + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, WeatherStationSceneStart); + consumed = true; + break; + case WSCustomEventViewReceiverOK: + app->txrx->idx_menu_chosen = ws_view_receiver_get_idx_menu(app->ws_receiver); + scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiverInfo); + consumed = true; + break; + case WSCustomEventViewReceiverConfig: + app->txrx->idx_menu_chosen = ws_view_receiver_get_idx_menu(app->ws_receiver); + scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiverConfig); + consumed = true; + break; + case WSCustomEventViewReceiverOffDisplay: + notification_message(app->notifications, &sequence_display_backlight_off); + consumed = true; + break; + case WSCustomEventViewReceiverUnlock: + app->lock = WSLockOff; + consumed = true; + break; + default: + break; + } + } else if(event.type == SceneManagerEventTypeTick) { + if(app->txrx->hopper_state != WSHopperStateOFF) { + ws_hopper_update(app); + weather_station_scene_receiver_update_statusbar(app); + } + if(app->txrx->txrx_state == WSTxRxStateRx) { + notification_message(app->notifications, &sequence_blink_cyan_10); + } + } + return consumed; +} + +void weather_station_scene_receiver_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/weather_station/scenes/weather_station_scene.c b/applications/plugins/weather_station/scenes/weather_station_scene.c new file mode 100644 index 00000000..f9306e5f --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene.c @@ -0,0 +1,30 @@ +#include "../weather_station_app_i.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const weather_station_scene_on_enter_handlers[])(void*) = { +#include "weather_station_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const weather_station_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "weather_station_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const weather_station_scene_on_exit_handlers[])(void* context) = { +#include "weather_station_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers weather_station_scene_handlers = { + .on_enter_handlers = weather_station_scene_on_enter_handlers, + .on_event_handlers = weather_station_scene_on_event_handlers, + .on_exit_handlers = weather_station_scene_on_exit_handlers, + .scene_num = WeatherStationSceneNum, +}; diff --git a/applications/plugins/weather_station/scenes/weather_station_scene.h b/applications/plugins/weather_station/scenes/weather_station_scene.h new file mode 100644 index 00000000..8cee4ee6 --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) WeatherStationScene##id, +typedef enum { +#include "weather_station_scene_config.h" + WeatherStationSceneNum, +} WeatherStationScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers weather_station_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "weather_station_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "weather_station_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "weather_station_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_about.c b/applications/plugins/weather_station/scenes/weather_station_scene_about.c new file mode 100644 index 00000000..df784ec9 --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene_about.c @@ -0,0 +1,78 @@ +#include "../weather_station_app_i.h" +#include "../helpers/weather_station_types.h" + +void weather_station_scene_about_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + WeatherStationApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void weather_station_scene_about_on_enter(void* context) { + WeatherStationApp* app = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", WS_VERSION_APP); + furi_string_cat_printf(temp_str, "Developed by: %s\n", WS_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", WS_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf( + temp_str, "Reading messages from\nweather station that work\nwith SubGhz sensors\n\n"); + + furi_string_cat_printf(temp_str, "Supported protocols:\n"); + size_t i = 0; + const char* protocol_name = + subghz_environment_get_protocol_name_registry(app->txrx->environment, i++); + do { + furi_string_cat_printf(temp_str, "%s\n", protocol_name); + protocol_name = subghz_environment_get_protocol_name_registry(app->txrx->environment, i++); + } while(protocol_name != NULL); + + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + app->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! Weather station \e!\n", + false); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewWidget); +} + +bool weather_station_scene_about_on_event(void* context, SceneManagerEvent event) { + WeatherStationApp* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void weather_station_scene_about_on_exit(void* context) { + WeatherStationApp* app = context; + + // Clear views + widget_reset(app->widget); +} diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_config.h b/applications/plugins/weather_station/scenes/weather_station_scene_config.h new file mode 100644 index 00000000..0ba8ec01 --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene_config.h @@ -0,0 +1,5 @@ +ADD_SCENE(weather_station, start, Start) +ADD_SCENE(weather_station, about, About) +ADD_SCENE(weather_station, receiver, Receiver) +ADD_SCENE(weather_station, receiver_config, ReceiverConfig) +ADD_SCENE(weather_station, receiver_info, ReceiverInfo) diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_receiver_config.c b/applications/plugins/weather_station/scenes/weather_station_scene_receiver_config.c new file mode 100644 index 00000000..fcd8f6d3 --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene_receiver_config.c @@ -0,0 +1,223 @@ +#include "../weather_station_app_i.h" + +enum WSSettingIndex { + WSSettingIndexFrequency, + WSSettingIndexHopping, + WSSettingIndexModulation, + WSSettingIndexLock, +}; + +#define HOPPING_COUNT 2 +const char* const hopping_text[HOPPING_COUNT] = { + "OFF", + "ON", +}; +const uint32_t hopping_value[HOPPING_COUNT] = { + WSHopperStateOFF, + WSHopperStateRunnig, +}; + +uint8_t weather_station_scene_receiver_config_next_frequency(const uint32_t value, void* context) { + furi_assert(context); + WeatherStationApp* app = context; + uint8_t index = 0; + for(uint8_t i = 0; i < subghz_setting_get_frequency_count(app->setting); i++) { + if(value == subghz_setting_get_frequency(app->setting, i)) { + index = i; + break; + } else { + index = subghz_setting_get_frequency_default_index(app->setting); + } + } + return index; +} + +uint8_t weather_station_scene_receiver_config_next_preset(const char* preset_name, void* context) { + furi_assert(context); + WeatherStationApp* app = context; + uint8_t index = 0; + for(uint8_t i = 0; i < subghz_setting_get_preset_count(app->setting); i++) { + if(!strcmp(subghz_setting_get_preset_name(app->setting, i), preset_name)) { + index = i; + break; + } else { + // index = subghz_setting_get_frequency_default_index(app ->setting); + } + } + return index; +} + +uint8_t weather_station_scene_receiver_config_hopper_value_index( + const uint32_t value, + const uint32_t values[], + uint8_t values_count, + void* context) { + furi_assert(context); + UNUSED(values_count); + WeatherStationApp* app = context; + + if(value == values[0]) { + return 0; + } else { + variable_item_set_current_value_text( + (VariableItem*)scene_manager_get_scene_state( + app->scene_manager, WeatherStationSceneReceiverConfig), + " -----"); + return 1; + } +} + +static void weather_station_scene_receiver_config_set_frequency(VariableItem* item) { + WeatherStationApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + if(app->txrx->hopper_state == WSHopperStateOFF) { + char text_buf[10] = {0}; + snprintf( + text_buf, + sizeof(text_buf), + "%lu.%02lu", + subghz_setting_get_frequency(app->setting, index) / 1000000, + (subghz_setting_get_frequency(app->setting, index) % 1000000) / 10000); + variable_item_set_current_value_text(item, text_buf); + app->txrx->preset->frequency = subghz_setting_get_frequency(app->setting, index); + } else { + variable_item_set_current_value_index( + item, subghz_setting_get_frequency_default_index(app->setting)); + } +} + +static void weather_station_scene_receiver_config_set_preset(VariableItem* item) { + WeatherStationApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text( + item, subghz_setting_get_preset_name(app->setting, index)); + ws_preset_init( + app, + subghz_setting_get_preset_name(app->setting, index), + app->txrx->preset->frequency, + subghz_setting_get_preset_data(app->setting, index), + subghz_setting_get_preset_data_size(app->setting, index)); +} + +static void weather_station_scene_receiver_config_set_hopping_running(VariableItem* item) { + WeatherStationApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, hopping_text[index]); + if(hopping_value[index] == WSHopperStateOFF) { + char text_buf[10] = {0}; + snprintf( + text_buf, + sizeof(text_buf), + "%lu.%02lu", + subghz_setting_get_default_frequency(app->setting) / 1000000, + (subghz_setting_get_default_frequency(app->setting) % 1000000) / 10000); + variable_item_set_current_value_text( + (VariableItem*)scene_manager_get_scene_state( + app->scene_manager, WeatherStationSceneReceiverConfig), + text_buf); + app->txrx->preset->frequency = subghz_setting_get_default_frequency(app->setting); + variable_item_set_current_value_index( + (VariableItem*)scene_manager_get_scene_state( + app->scene_manager, WeatherStationSceneReceiverConfig), + subghz_setting_get_frequency_default_index(app->setting)); + } else { + variable_item_set_current_value_text( + (VariableItem*)scene_manager_get_scene_state( + app->scene_manager, WeatherStationSceneReceiverConfig), + " -----"); + variable_item_set_current_value_index( + (VariableItem*)scene_manager_get_scene_state( + app->scene_manager, WeatherStationSceneReceiverConfig), + subghz_setting_get_frequency_default_index(app->setting)); + } + + app->txrx->hopper_state = hopping_value[index]; +} + +static void + weather_station_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { + furi_assert(context); + WeatherStationApp* app = context; + if(index == WSSettingIndexLock) { + view_dispatcher_send_custom_event(app->view_dispatcher, WSCustomEventSceneSettingLock); + } +} + +void weather_station_scene_receiver_config_on_enter(void* context) { + WeatherStationApp* app = context; + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + app->variable_item_list, + "Frequency:", + subghz_setting_get_frequency_count(app->setting), + weather_station_scene_receiver_config_set_frequency, + app); + value_index = + weather_station_scene_receiver_config_next_frequency(app->txrx->preset->frequency, app); + scene_manager_set_scene_state( + app->scene_manager, WeatherStationSceneReceiverConfig, (uint32_t)item); + variable_item_set_current_value_index(item, value_index); + char text_buf[10] = {0}; + snprintf( + text_buf, + sizeof(text_buf), + "%lu.%02lu", + subghz_setting_get_frequency(app->setting, value_index) / 1000000, + (subghz_setting_get_frequency(app->setting, value_index) % 1000000) / 10000); + variable_item_set_current_value_text(item, text_buf); + + item = variable_item_list_add( + app->variable_item_list, + "Hopping:", + HOPPING_COUNT, + weather_station_scene_receiver_config_set_hopping_running, + app); + value_index = weather_station_scene_receiver_config_hopper_value_index( + app->txrx->hopper_state, hopping_value, HOPPING_COUNT, app); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, hopping_text[value_index]); + + item = variable_item_list_add( + app->variable_item_list, + "Modulation:", + subghz_setting_get_preset_count(app->setting), + weather_station_scene_receiver_config_set_preset, + app); + value_index = weather_station_scene_receiver_config_next_preset( + furi_string_get_cstr(app->txrx->preset->name), app); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text( + item, subghz_setting_get_preset_name(app->setting, value_index)); + + variable_item_list_add(app->variable_item_list, "Lock Keyboard", 1, NULL, NULL); + variable_item_list_set_enter_callback( + app->variable_item_list, + weather_station_scene_receiver_config_var_list_enter_callback, + app); + + view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewVariableItemList); +} + +bool weather_station_scene_receiver_config_on_event(void* context, SceneManagerEvent event) { + WeatherStationApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == WSCustomEventSceneSettingLock) { + app->lock = WSLockOn; + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + } + return consumed; +} + +void weather_station_scene_receiver_config_on_exit(void* context) { + WeatherStationApp* app = context; + variable_item_list_set_selected_item(app->variable_item_list, 0); + variable_item_list_reset(app->variable_item_list); +} diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_receiver_info.c b/applications/plugins/weather_station/scenes/weather_station_scene_receiver_info.c new file mode 100644 index 00000000..b26661be --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene_receiver_info.c @@ -0,0 +1,50 @@ +#include "../weather_station_app_i.h" +#include "../views/weather_station_receiver.h" + +void weather_station_scene_receiver_info_callback(WSCustomEvent event, void* context) { + furi_assert(context); + WeatherStationApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +static void weather_station_scene_receiver_info_add_to_history_callback( + SubGhzReceiver* receiver, + SubGhzProtocolDecoderBase* decoder_base, + void* context) { + furi_assert(context); + WeatherStationApp* app = context; + + if(ws_history_add_to_history(app->txrx->history, decoder_base, app->txrx->preset) == + WSHistoryStateAddKeyUpdateData) { + ws_view_receiver_info_update( + app->ws_receiver_info, + ws_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen)); + subghz_receiver_reset(receiver); + + notification_message(app->notifications, &sequence_blink_green_10); + app->txrx->rx_key_state = WSRxKeyStateAddKey; + } +} + +void weather_station_scene_receiver_info_on_enter(void* context) { + WeatherStationApp* app = context; + + subghz_receiver_set_rx_callback( + app->txrx->receiver, weather_station_scene_receiver_info_add_to_history_callback, app); + ws_view_receiver_info_update( + app->ws_receiver_info, + ws_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen)); + view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewReceiverInfo); +} + +bool weather_station_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { + WeatherStationApp* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + return consumed; +} + +void weather_station_scene_receiver_info_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_start.c b/applications/plugins/weather_station/scenes/weather_station_scene_start.c new file mode 100644 index 00000000..56dd6fa8 --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene_start.c @@ -0,0 +1,58 @@ +#include "../weather_station_app_i.h" + +typedef enum { + SubmenuIndexWeatherStationReceiver, + SubmenuIndexWeatherStationAbout, +} SubmenuIndex; + +void weather_station_scene_start_submenu_callback(void* context, uint32_t index) { + WeatherStationApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void weather_station_scene_start_on_enter(void* context) { + UNUSED(context); + WeatherStationApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Read Weather Station", + SubmenuIndexWeatherStationReceiver, + weather_station_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "About", + SubmenuIndexWeatherStationAbout, + weather_station_scene_start_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WeatherStationSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewSubmenu); +} + +bool weather_station_scene_start_on_event(void* context, SceneManagerEvent event) { + WeatherStationApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexWeatherStationAbout) { + scene_manager_next_scene(app->scene_manager, WeatherStationSceneAbout); + consumed = true; + } else if(event.event == SubmenuIndexWeatherStationReceiver) { + scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiver); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, WeatherStationSceneStart, event.event); + } + + return consumed; +} + +void weather_station_scene_start_on_exit(void* context) { + WeatherStationApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/plugins/weather_station/views/weather_station_receiver.c b/applications/plugins/weather_station/views/weather_station_receiver.c new file mode 100644 index 00000000..d30b7926 --- /dev/null +++ b/applications/plugins/weather_station/views/weather_station_receiver.c @@ -0,0 +1,437 @@ +#include "weather_station_receiver.h" +#include "../weather_station_app_i.h" +#include "weather_station_icons.h" +#include + +#include +#include +#include +#include + +#define FRAME_HEIGHT 12 +#define MAX_LEN_PX 100 +#define MENU_ITEMS 4u +#define UNLOCK_CNT 3 + +typedef struct { + FuriString* item_str; + uint8_t type; +} WSReceiverMenuItem; + +ARRAY_DEF(WSReceiverMenuItemArray, WSReceiverMenuItem, M_POD_OPLIST) + +#define M_OPL_WSReceiverMenuItemArray_t() ARRAY_OPLIST(WSReceiverMenuItemArray, M_POD_OPLIST) + +struct WSReceiverHistory { + WSReceiverMenuItemArray_t data; +}; + +typedef struct WSReceiverHistory WSReceiverHistory; + +static const Icon* ReceiverItemIcons[] = { + [SubGhzProtocolTypeUnknown] = &I_Quest_7x8, + [SubGhzProtocolTypeStatic] = &I_Unlock_7x8, + [SubGhzProtocolTypeDynamic] = &I_Lock_7x8, + [SubGhzProtocolWeatherStation] = &I_station_icon, +}; + +typedef enum { + WSReceiverBarShowDefault, + WSReceiverBarShowLock, + WSReceiverBarShowToUnlockPress, + WSReceiverBarShowUnlock, +} WSReceiverBarShow; + +struct WSReceiver { + WSLock lock; + uint8_t lock_count; + FuriTimer* timer; + View* view; + WSReceiverCallback callback; + void* context; +}; + +typedef struct { + FuriString* frequency_str; + FuriString* preset_str; + FuriString* history_stat_str; + WSReceiverHistory* history; + uint16_t idx; + uint16_t list_offset; + uint16_t history_item; + WSReceiverBarShow bar_show; +} WSReceiverModel; + +void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock lock) { + furi_assert(ws_receiver); + ws_receiver->lock_count = 0; + if(lock == WSLockOn) { + ws_receiver->lock = lock; + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { model->bar_show = WSReceiverBarShowLock; }, + true); + furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(1000)); + } else { + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { model->bar_show = WSReceiverBarShowDefault; }, + true); + } +} + +void ws_view_receiver_set_callback( + WSReceiver* ws_receiver, + WSReceiverCallback callback, + void* context) { + furi_assert(ws_receiver); + furi_assert(callback); + ws_receiver->callback = callback; + ws_receiver->context = context; +} + +static void ws_view_receiver_update_offset(WSReceiver* ws_receiver) { + furi_assert(ws_receiver); + + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + size_t history_item = model->history_item; + uint16_t bounds = history_item > 3 ? 2 : history_item; + + if(history_item > 3 && model->idx >= (int16_t)(history_item - 1)) { + model->list_offset = model->idx - 3; + } else if(model->list_offset < model->idx - bounds) { + model->list_offset = + CLAMP(model->list_offset + 1, (int16_t)(history_item - bounds), 0); + } else if(model->list_offset > model->idx - bounds) { + model->list_offset = CLAMP(model->idx - 1, (int16_t)(history_item - bounds), 0); + } + }, + true); +} + +void ws_view_receiver_add_item_to_menu(WSReceiver* ws_receiver, const char* name, uint8_t type) { + furi_assert(ws_receiver); + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + WSReceiverMenuItem* item_menu = WSReceiverMenuItemArray_push_raw(model->history->data); + item_menu->item_str = furi_string_alloc_set(name); + item_menu->type = type; + if((model->idx == model->history_item - 1)) { + model->history_item++; + model->idx++; + } else { + model->history_item++; + } + }, + true); + ws_view_receiver_update_offset(ws_receiver); +} + +void ws_view_receiver_add_data_statusbar( + WSReceiver* ws_receiver, + const char* frequency_str, + const char* preset_str, + const char* history_stat_str) { + furi_assert(ws_receiver); + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + furi_string_set_str(model->frequency_str, frequency_str); + furi_string_set_str(model->preset_str, preset_str); + furi_string_set_str(model->history_stat_str, history_stat_str); + }, + true); +} + +static void ws_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 0, 0 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 1, 0 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 1); + + canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 11); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, 0 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); +} + +void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + + elements_button_left(canvas, "Config"); + canvas_draw_line(canvas, 46, 51, 125, 51); + + bool scrollbar = model->history_item > 4; + FuriString* str_buff; + str_buff = furi_string_alloc(); + + WSReceiverMenuItem* item_menu; + + for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) { + size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0); + item_menu = WSReceiverMenuItemArray_get(model->history->data, idx); + furi_string_set(str_buff, item_menu->item_str); + elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); + if(model->idx == idx) { + ws_view_receiver_draw_frame(canvas, i, scrollbar); + } else { + canvas_set_color(canvas, ColorBlack); + } + canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); + canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); + furi_string_reset(str_buff); + } + if(scrollbar) { + elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item); + } + furi_string_free(str_buff); + + canvas_set_color(canvas, ColorBlack); + + if(model->history_item == 0) { + canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 63, 46, "Scanning..."); + canvas_draw_line(canvas, 46, 51, 125, 51); + canvas_set_font(canvas, FontSecondary); + } + + switch(model->bar_show) { + case WSReceiverBarShowLock: + canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); + canvas_draw_str(canvas, 74, 62, "Locked"); + break; + case WSReceiverBarShowToUnlockPress: + canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + canvas_set_font(canvas, FontSecondary); + elements_bold_rounded_frame(canvas, 14, 8, 99, 48); + elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); + canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42); + canvas_draw_dot(canvas, 17, 61); + break; + case WSReceiverBarShowUnlock: + canvas_draw_icon(canvas, 64, 55, &I_Unlock_7x8); + canvas_draw_str(canvas, 74, 62, "Unlocked"); + break; + default: + canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + break; + } +} + +static void ws_view_receiver_timer_callback(void* context) { + furi_assert(context); + WSReceiver* ws_receiver = context; + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { model->bar_show = WSReceiverBarShowDefault; }, + true); + if(ws_receiver->lock_count < UNLOCK_CNT) { + ws_receiver->callback(WSCustomEventViewReceiverOffDisplay, ws_receiver->context); + } else { + ws_receiver->lock = WSLockOff; + ws_receiver->callback(WSCustomEventViewReceiverUnlock, ws_receiver->context); + } + ws_receiver->lock_count = 0; +} + +bool ws_view_receiver_input(InputEvent* event, void* context) { + furi_assert(context); + WSReceiver* ws_receiver = context; + + if(ws_receiver->lock == WSLockOn) { + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { model->bar_show = WSReceiverBarShowToUnlockPress; }, + true); + if(ws_receiver->lock_count == 0) { + furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(1000)); + } + if(event->key == InputKeyBack && event->type == InputTypeShort) { + ws_receiver->lock_count++; + } + if(ws_receiver->lock_count >= UNLOCK_CNT) { + ws_receiver->callback(WSCustomEventViewReceiverUnlock, ws_receiver->context); + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { model->bar_show = WSReceiverBarShowUnlock; }, + true); + ws_receiver->lock = WSLockOff; + furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(650)); + } + + return true; + } + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + ws_receiver->callback(WSCustomEventViewReceiverBack, ws_receiver->context); + } else if( + event->key == InputKeyUp && + (event->type == InputTypeShort || event->type == InputTypeRepeat)) { + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + if(model->idx != 0) model->idx--; + }, + true); + } else if( + event->key == InputKeyDown && + (event->type == InputTypeShort || event->type == InputTypeRepeat)) { + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + if(model->idx != model->history_item - 1) model->idx++; + }, + true); + } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { + ws_receiver->callback(WSCustomEventViewReceiverConfig, ws_receiver->context); + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + if(model->history_item != 0) { + ws_receiver->callback(WSCustomEventViewReceiverOK, ws_receiver->context); + } + }, + false); + } + + ws_view_receiver_update_offset(ws_receiver); + + return true; +} + +void ws_view_receiver_enter(void* context) { + furi_assert(context); +} + +void ws_view_receiver_exit(void* context) { + furi_assert(context); + WSReceiver* ws_receiver = context; + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + furi_string_reset(model->frequency_str); + furi_string_reset(model->preset_str); + furi_string_reset(model->history_stat_str); + for + M_EACH(item_menu, model->history->data, WSReceiverMenuItemArray_t) { + furi_string_free(item_menu->item_str); + item_menu->type = 0; + } + WSReceiverMenuItemArray_reset(model->history->data); + model->idx = 0; + model->list_offset = 0; + model->history_item = 0; + }, + false); + furi_timer_stop(ws_receiver->timer); +} + +WSReceiver* ws_view_receiver_alloc() { + WSReceiver* ws_receiver = malloc(sizeof(WSReceiver)); + + // View allocation and configuration + ws_receiver->view = view_alloc(); + + ws_receiver->lock = WSLockOff; + ws_receiver->lock_count = 0; + view_allocate_model(ws_receiver->view, ViewModelTypeLocking, sizeof(WSReceiverModel)); + view_set_context(ws_receiver->view, ws_receiver); + view_set_draw_callback(ws_receiver->view, (ViewDrawCallback)ws_view_receiver_draw); + view_set_input_callback(ws_receiver->view, ws_view_receiver_input); + view_set_enter_callback(ws_receiver->view, ws_view_receiver_enter); + view_set_exit_callback(ws_receiver->view, ws_view_receiver_exit); + + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + model->frequency_str = furi_string_alloc(); + model->preset_str = furi_string_alloc(); + model->history_stat_str = furi_string_alloc(); + model->bar_show = WSReceiverBarShowDefault; + model->history = malloc(sizeof(WSReceiverHistory)); + WSReceiverMenuItemArray_init(model->history->data); + }, + true); + ws_receiver->timer = + furi_timer_alloc(ws_view_receiver_timer_callback, FuriTimerTypeOnce, ws_receiver); + return ws_receiver; +} + +void ws_view_receiver_free(WSReceiver* ws_receiver) { + furi_assert(ws_receiver); + + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + furi_string_free(model->frequency_str); + furi_string_free(model->preset_str); + furi_string_free(model->history_stat_str); + for + M_EACH(item_menu, model->history->data, WSReceiverMenuItemArray_t) { + furi_string_free(item_menu->item_str); + item_menu->type = 0; + } + WSReceiverMenuItemArray_clear(model->history->data); + free(model->history); + }, + false); + furi_timer_free(ws_receiver->timer); + view_free(ws_receiver->view); + free(ws_receiver); +} + +View* ws_view_receiver_get_view(WSReceiver* ws_receiver) { + furi_assert(ws_receiver); + return ws_receiver->view; +} + +uint16_t ws_view_receiver_get_idx_menu(WSReceiver* ws_receiver) { + furi_assert(ws_receiver); + uint32_t idx = 0; + with_view_model( + ws_receiver->view, WSReceiverModel * model, { idx = model->idx; }, false); + return idx; +} + +void ws_view_receiver_set_idx_menu(WSReceiver* ws_receiver, uint16_t idx) { + furi_assert(ws_receiver); + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + model->idx = idx; + if(model->idx > 2) model->list_offset = idx - 2; + }, + true); + ws_view_receiver_update_offset(ws_receiver); +} diff --git a/applications/plugins/weather_station/views/weather_station_receiver.h b/applications/plugins/weather_station/views/weather_station_receiver.h new file mode 100644 index 00000000..30c6516d --- /dev/null +++ b/applications/plugins/weather_station/views/weather_station_receiver.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include "../helpers/weather_station_types.h" +#include "../helpers/weather_station_event.h" + +typedef struct WSReceiver WSReceiver; + +typedef void (*WSReceiverCallback)(WSCustomEvent event, void* context); + +void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock keyboard); + +void ws_view_receiver_set_callback( + WSReceiver* ws_receiver, + WSReceiverCallback callback, + void* context); + +WSReceiver* ws_view_receiver_alloc(); + +void ws_view_receiver_free(WSReceiver* ws_receiver); + +View* ws_view_receiver_get_view(WSReceiver* ws_receiver); + +void ws_view_receiver_add_data_statusbar( + WSReceiver* ws_receiver, + const char* frequency_str, + const char* preset_str, + const char* history_stat_str); + +void ws_view_receiver_add_item_to_menu(WSReceiver* ws_receiver, const char* name, uint8_t type); + +uint16_t ws_view_receiver_get_idx_menu(WSReceiver* ws_receiver); + +void ws_view_receiver_set_idx_menu(WSReceiver* ws_receiver, uint16_t idx); + +void ws_view_receiver_exit(void* context); diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.c b/applications/plugins/weather_station/views/weather_station_receiver_info.c new file mode 100644 index 00000000..31870627 --- /dev/null +++ b/applications/plugins/weather_station/views/weather_station_receiver_info.c @@ -0,0 +1,150 @@ +#include "weather_station_receiver.h" +#include "../weather_station_app_i.h" +#include "weather_station_icons.h" +#include "../protocols/ws_generic.h" +#include +#include +#include "math.h" + +#define abs(x) ((x) > 0 ? (x) : -(x)) + +struct WSReceiverInfo { + View* view; +}; + +typedef struct { + FuriString* protocol_name; + WSBlockGeneric* generic; +} WSReceiverInfoModel; + +void ws_view_receiver_info_update(WSReceiverInfo* ws_receiver_info, FlipperFormat* fff) { + furi_assert(ws_receiver_info); + furi_assert(fff); + + with_view_model( + ws_receiver_info->view, + WSReceiverInfoModel * model, + { + flipper_format_rewind(fff); + flipper_format_read_string(fff, "Protocol", model->protocol_name); + + ws_block_generic_deserialize(model->generic, fff); + }, + true); +} + +void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) { + char buffer[64]; + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + + snprintf( + buffer, + sizeof(buffer), + "%s %db", + furi_string_get_cstr(model->protocol_name), + model->generic->data_count_bit); + canvas_draw_str(canvas, 5, 8, buffer); + + snprintf(buffer, sizeof(buffer), "Ch: %01d", model->generic->channel); + canvas_draw_str(canvas, 105, 8, buffer); + + snprintf(buffer, sizeof(buffer), "Sn: 0x%02lX", model->generic->id); + canvas_draw_str(canvas, 5, 20, buffer); + + snprintf(buffer, sizeof(buffer), "Batt: %s", (!model->generic->battery_low ? "ok" : "low")); + canvas_draw_str(canvas, 85, 20, buffer); + + snprintf(buffer, sizeof(buffer), "Data: 0x%llX", model->generic->data); + canvas_draw_str(canvas, 5, 32, buffer); + + elements_bold_rounded_frame(canvas, 2, 37, 123, 25); + canvas_set_font(canvas, FontPrimary); + + canvas_draw_icon(canvas, 13 + 5, 42, &I_Therm_7x16); + snprintf( + buffer, + sizeof(buffer), + "%3.2d.%d C", + (int16_t)model->generic->temp, + abs(((int16_t)(model->generic->temp * 10) - (((int16_t)model->generic->temp) * 10)))); + canvas_draw_str_aligned(canvas, 58 + 5, 46, AlignRight, AlignTop, buffer); + canvas_draw_circle(canvas, 50 + 5, 45, 1); + + canvas_draw_icon(canvas, 70 + 5, 42, &I_Humid_10x15); + snprintf(buffer, sizeof(buffer), "%d%%", model->generic->humidity); + canvas_draw_str(canvas, 86 + 5, 54, buffer); +} + +bool ws_view_receiver_info_input(InputEvent* event, void* context) { + furi_assert(context); + //WSReceiverInfo* ws_receiver_info = context; + + if(event->key == InputKeyBack) { + return false; + } + + return true; +} + +void ws_view_receiver_info_enter(void* context) { + furi_assert(context); +} + +void ws_view_receiver_info_exit(void* context) { + furi_assert(context); + WSReceiverInfo* ws_receiver_info = context; + + with_view_model( + ws_receiver_info->view, + WSReceiverInfoModel * model, + { furi_string_reset(model->protocol_name); }, + false); +} + +WSReceiverInfo* ws_view_receiver_info_alloc() { + WSReceiverInfo* ws_receiver_info = malloc(sizeof(WSReceiverInfo)); + + // View allocation and configuration + ws_receiver_info->view = view_alloc(); + + view_allocate_model(ws_receiver_info->view, ViewModelTypeLocking, sizeof(WSReceiverInfoModel)); + view_set_context(ws_receiver_info->view, ws_receiver_info); + view_set_draw_callback(ws_receiver_info->view, (ViewDrawCallback)ws_view_receiver_info_draw); + view_set_input_callback(ws_receiver_info->view, ws_view_receiver_info_input); + view_set_enter_callback(ws_receiver_info->view, ws_view_receiver_info_enter); + view_set_exit_callback(ws_receiver_info->view, ws_view_receiver_info_exit); + + with_view_model( + ws_receiver_info->view, + WSReceiverInfoModel * model, + { + model->generic = malloc(sizeof(WSBlockGeneric)); + model->protocol_name = furi_string_alloc(); + }, + true); + + return ws_receiver_info; +} + +void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info) { + furi_assert(ws_receiver_info); + + with_view_model( + ws_receiver_info->view, + WSReceiverInfoModel * model, + { + furi_string_free(model->protocol_name); + free(model->generic); + }, + false); + + view_free(ws_receiver_info->view); + free(ws_receiver_info); +} + +View* ws_view_receiver_info_get_view(WSReceiverInfo* ws_receiver_info) { + furi_assert(ws_receiver_info); + return ws_receiver_info->view; +} diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.h b/applications/plugins/weather_station/views/weather_station_receiver_info.h new file mode 100644 index 00000000..705434a2 --- /dev/null +++ b/applications/plugins/weather_station/views/weather_station_receiver_info.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include "../helpers/weather_station_types.h" +#include "../helpers/weather_station_event.h" +#include + +typedef struct WSReceiverInfo WSReceiverInfo; + +void ws_view_receiver_info_update(WSReceiverInfo* ws_receiver_info, FlipperFormat* fff); + +WSReceiverInfo* ws_view_receiver_info_alloc(); + +void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info); + +View* ws_view_receiver_info_get_view(WSReceiverInfo* ws_receiver_info); diff --git a/applications/plugins/weather_station/weather_station_10px.png b/applications/plugins/weather_station/weather_station_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..7d5cc318c369f583c531ca5e8846a9a498ec44b6 GIT binary patch literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ihk44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!GcfQS24TkI`72U@f-asejv*Ssy?udP3<@01TYulb=@zZ(cJX(> z9E<0IiRH6?9buO;SoeAj)2m&2)(%T&iEpuE4*KbLE`4W{EU(#4ulkv@OZPmG6Pd23 R{2OQ +#include +#include "protocols/protocol_items.h" + +static bool weather_station_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + WeatherStationApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool weather_station_app_back_event_callback(void* context) { + furi_assert(context); + WeatherStationApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void weather_station_app_tick_event_callback(void* context) { + furi_assert(context); + WeatherStationApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +WeatherStationApp* weather_station_app_alloc() { + WeatherStationApp* app = malloc(sizeof(WeatherStationApp)); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&weather_station_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, weather_station_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, weather_station_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, weather_station_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Variable Item List + app->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + WeatherStationViewVariableItemList, + variable_item_list_get_view(app->variable_item_list)); + + // SubMenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, WeatherStationViewSubmenu, submenu_get_view(app->submenu)); + + // Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, WeatherStationViewWidget, widget_get_view(app->widget)); + + // Receiver + app->ws_receiver = ws_view_receiver_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + WeatherStationViewReceiver, + ws_view_receiver_get_view(app->ws_receiver)); + + // Receiver Info + app->ws_receiver_info = ws_view_receiver_info_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + WeatherStationViewReceiverInfo, + ws_view_receiver_info_get_view(app->ws_receiver_info)); + + //init setting + app->setting = subghz_setting_alloc(); + + //ToDo FIX file name setting + subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user")); + + //init Worker & Protocol & History + app->lock = WSLockOff; + app->txrx = malloc(sizeof(WeatherStationTxRx)); + app->txrx->preset = malloc(sizeof(SubGhzRadioPreset)); + app->txrx->preset->name = furi_string_alloc(); + ws_preset_init(app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0); + + app->txrx->hopper_state = WSHopperStateOFF; + app->txrx->history = ws_history_alloc(); + app->txrx->worker = subghz_worker_alloc(); + app->txrx->environment = subghz_environment_alloc(); + subghz_environment_set_protocol_registry( + app->txrx->environment, (void*)&weather_station_protocol_registry); + app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment); + + subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable); + subghz_worker_set_overrun_callback( + app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); + subghz_worker_set_pair_callback( + app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); + subghz_worker_set_context(app->txrx->worker, app->txrx->receiver); + + furi_hal_power_suppress_charge_enter(); + + scene_manager_next_scene(app->scene_manager, WeatherStationSceneStart); + + return app; +} + +void weather_station_app_free(WeatherStationApp* app) { + furi_assert(app); + + //CC1101 off + ws_sleep(app); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewSubmenu); + submenu_free(app->submenu); + + // Variable Item List + view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewVariableItemList); + variable_item_list_free(app->variable_item_list); + + // Widget + view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewWidget); + widget_free(app->widget); + + // Receiver + view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewReceiver); + ws_view_receiver_free(app->ws_receiver); + + // Receiver Info + view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewReceiverInfo); + ws_view_receiver_info_free(app->ws_receiver_info); + + //setting + subghz_setting_free(app->setting); + + //Worker & Protocol & History + subghz_receiver_free(app->txrx->receiver); + subghz_environment_free(app->txrx->environment); + ws_history_free(app->txrx->history); + subghz_worker_free(app->txrx->worker); + furi_string_free(app->txrx->preset->name); + free(app->txrx->preset); + free(app->txrx); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Close records + furi_record_close(RECORD_GUI); + + furi_hal_power_suppress_charge_exit(); + + free(app); +} + +int32_t weather_station_app(void* p) { + UNUSED(p); + WeatherStationApp* weather_station_app = weather_station_app_alloc(); + + view_dispatcher_run(weather_station_app->view_dispatcher); + + weather_station_app_free(weather_station_app); + + return 0; +} diff --git a/applications/plugins/weather_station/weather_station_app_i.c b/applications/plugins/weather_station/weather_station_app_i.c new file mode 100644 index 00000000..052bb853 --- /dev/null +++ b/applications/plugins/weather_station/weather_station_app_i.c @@ -0,0 +1,159 @@ +#include "weather_station_app_i.h" + +#define TAG "WeatherStation" +#include + +void ws_preset_init( + void* context, + const char* preset_name, + uint32_t frequency, + uint8_t* preset_data, + size_t preset_data_size) { + furi_assert(context); + WeatherStationApp* app = context; + furi_string_set(app->txrx->preset->name, preset_name); + app->txrx->preset->frequency = frequency; + app->txrx->preset->data = preset_data; + app->txrx->preset->data_size = preset_data_size; +} + +bool ws_set_preset(WeatherStationApp* app, const char* preset) { + if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { + furi_string_set(app->txrx->preset->name, "AM270"); + } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { + furi_string_set(app->txrx->preset->name, "AM650"); + } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) { + furi_string_set(app->txrx->preset->name, "FM238"); + } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) { + furi_string_set(app->txrx->preset->name, "FM476"); + } else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) { + furi_string_set(app->txrx->preset->name, "CUSTOM"); + } else { + FURI_LOG_E(TAG, "Unknown preset"); + return false; + } + return true; +} + +void ws_get_frequency_modulation( + WeatherStationApp* app, + FuriString* frequency, + FuriString* modulation) { + furi_assert(app); + if(frequency != NULL) { + furi_string_printf( + frequency, + "%03ld.%02ld", + app->txrx->preset->frequency / 1000000 % 1000, + app->txrx->preset->frequency / 10000 % 100); + } + if(modulation != NULL) { + furi_string_printf(modulation, "%.2s", furi_string_get_cstr(app->txrx->preset->name)); + } +} + +void ws_begin(WeatherStationApp* app, uint8_t* preset_data) { + furi_assert(app); + UNUSED(preset_data); + furi_hal_subghz_reset(); + furi_hal_subghz_idle(); + furi_hal_subghz_load_custom_preset(preset_data); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + app->txrx->txrx_state = WSTxRxStateIDLE; +} + +uint32_t ws_rx(WeatherStationApp* app, uint32_t frequency) { + furi_assert(app); + if(!furi_hal_subghz_is_frequency_valid(frequency)) { + furi_crash("WeatherStation: Incorrect RX frequency."); + } + furi_assert( + app->txrx->txrx_state != WSTxRxStateRx && app->txrx->txrx_state != WSTxRxStateSleep); + + furi_hal_subghz_idle(); + uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_subghz_flush_rx(); + furi_hal_subghz_rx(); + + furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, app->txrx->worker); + subghz_worker_start(app->txrx->worker); + app->txrx->txrx_state = WSTxRxStateRx; + return value; +} + +void ws_idle(WeatherStationApp* app) { + furi_assert(app); + furi_assert(app->txrx->txrx_state != WSTxRxStateSleep); + furi_hal_subghz_idle(); + app->txrx->txrx_state = WSTxRxStateIDLE; +} + +void ws_rx_end(WeatherStationApp* app) { + furi_assert(app); + furi_assert(app->txrx->txrx_state == WSTxRxStateRx); + if(subghz_worker_is_running(app->txrx->worker)) { + subghz_worker_stop(app->txrx->worker); + furi_hal_subghz_stop_async_rx(); + } + furi_hal_subghz_idle(); + app->txrx->txrx_state = WSTxRxStateIDLE; +} + +void ws_sleep(WeatherStationApp* app) { + furi_assert(app); + furi_hal_subghz_sleep(); + app->txrx->txrx_state = WSTxRxStateSleep; +} + +void ws_hopper_update(WeatherStationApp* app) { + furi_assert(app); + + switch(app->txrx->hopper_state) { + case WSHopperStateOFF: + return; + break; + case WSHopperStatePause: + return; + break; + case WSHopperStateRSSITimeOut: + if(app->txrx->hopper_timeout != 0) { + app->txrx->hopper_timeout--; + return; + } + break; + default: + break; + } + float rssi = -127.0f; + if(app->txrx->hopper_state != WSHopperStateRSSITimeOut) { + // See RSSI Calculation timings in CC1101 17.3 RSSI + rssi = furi_hal_subghz_get_rssi(); + + // Stay if RSSI is high enough + if(rssi > -90.0f) { + app->txrx->hopper_timeout = 10; + app->txrx->hopper_state = WSHopperStateRSSITimeOut; + return; + } + } else { + app->txrx->hopper_state = WSHopperStateRunnig; + } + // Select next frequency + if(app->txrx->hopper_idx_frequency < + subghz_setting_get_hopper_frequency_count(app->setting) - 1) { + app->txrx->hopper_idx_frequency++; + } else { + app->txrx->hopper_idx_frequency = 0; + } + + if(app->txrx->txrx_state == WSTxRxStateRx) { + ws_rx_end(app); + }; + if(app->txrx->txrx_state == WSTxRxStateIDLE) { + subghz_receiver_reset(app->txrx->receiver); + app->txrx->preset->frequency = + subghz_setting_get_hopper_frequency(app->setting, app->txrx->hopper_idx_frequency); + ws_rx(app, app->txrx->preset->frequency); + } +} diff --git a/applications/plugins/weather_station/weather_station_app_i.h b/applications/plugins/weather_station/weather_station_app_i.h new file mode 100644 index 00000000..41e24811 --- /dev/null +++ b/applications/plugins/weather_station/weather_station_app_i.h @@ -0,0 +1,73 @@ +#pragma once + +#include "helpers/weather_station_types.h" + +#include "scenes/weather_station_scene.h" +#include +#include +#include +#include +#include +#include +#include +#include "views/weather_station_receiver.h" +#include "views/weather_station_receiver_info.h" +#include "weather_station_history.h" + +#include +#include +#include +#include +#include + +typedef struct WeatherStationApp WeatherStationApp; + +struct WeatherStationTxRx { + SubGhzWorker* worker; + + SubGhzEnvironment* environment; + SubGhzReceiver* receiver; + SubGhzRadioPreset* preset; + WSHistory* history; + uint16_t idx_menu_chosen; + WSTxRxState txrx_state; + WSHopperState hopper_state; + uint8_t hopper_timeout; + uint8_t hopper_idx_frequency; + WSRxKeyState rx_key_state; +}; + +typedef struct WeatherStationTxRx WeatherStationTxRx; + +struct WeatherStationApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + WeatherStationTxRx* txrx; + SceneManager* scene_manager; + NotificationApp* notifications; + VariableItemList* variable_item_list; + Submenu* submenu; + Widget* widget; + WSReceiver* ws_receiver; + WSReceiverInfo* ws_receiver_info; + WSLock lock; + SubGhzSetting* setting; +}; + +void ws_preset_init( + void* context, + const char* preset_name, + uint32_t frequency, + uint8_t* preset_data, + size_t preset_data_size); +bool ws_set_preset(WeatherStationApp* app, const char* preset); +void ws_get_frequency_modulation( + WeatherStationApp* app, + FuriString* frequency, + FuriString* modulation); +void ws_begin(WeatherStationApp* app, uint8_t* preset_data); +uint32_t ws_rx(WeatherStationApp* app, uint32_t frequency); +void ws_idle(WeatherStationApp* app); +void ws_rx_end(WeatherStationApp* app); +void ws_sleep(WeatherStationApp* app); +void ws_hopper_update(WeatherStationApp* app); diff --git a/applications/plugins/weather_station/weather_station_history.c b/applications/plugins/weather_station/weather_station_history.c new file mode 100644 index 00000000..2eddf6cd --- /dev/null +++ b/applications/plugins/weather_station/weather_station_history.c @@ -0,0 +1,246 @@ +#include "weather_station_history.h" +#include +#include +#include + +#include + +#define WS_HISTORY_MAX 50 +#define TAG "WSHistory" + +typedef struct { + FuriString* item_str; + FlipperFormat* flipper_string; + uint8_t type; + uint32_t id; + SubGhzRadioPreset* preset; +} WSHistoryItem; + +ARRAY_DEF(WSHistoryItemArray, WSHistoryItem, M_POD_OPLIST) + +#define M_OPL_WSHistoryItemArray_t() ARRAY_OPLIST(WSHistoryItemArray, M_POD_OPLIST) + +typedef struct { + WSHistoryItemArray_t data; +} WSHistoryStruct; + +struct WSHistory { + uint32_t last_update_timestamp; + uint16_t last_index_write; + uint8_t code_last_hash_data; + FuriString* tmp_string; + WSHistoryStruct* history; +}; + +WSHistory* ws_history_alloc(void) { + WSHistory* instance = malloc(sizeof(WSHistory)); + instance->tmp_string = furi_string_alloc(); + instance->history = malloc(sizeof(WSHistoryStruct)); + WSHistoryItemArray_init(instance->history->data); + return instance; +} + +void ws_history_free(WSHistory* instance) { + furi_assert(instance); + furi_string_free(instance->tmp_string); + for + M_EACH(item, instance->history->data, WSHistoryItemArray_t) { + furi_string_free(item->item_str); + furi_string_free(item->preset->name); + free(item->preset); + flipper_format_free(item->flipper_string); + item->type = 0; + } + WSHistoryItemArray_clear(instance->history->data); + free(instance->history); + free(instance); +} + +uint32_t ws_history_get_frequency(WSHistory* instance, uint16_t idx) { + furi_assert(instance); + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + return item->preset->frequency; +} + +SubGhzRadioPreset* ws_history_get_radio_preset(WSHistory* instance, uint16_t idx) { + furi_assert(instance); + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + return item->preset; +} + +const char* ws_history_get_preset(WSHistory* instance, uint16_t idx) { + furi_assert(instance); + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + return furi_string_get_cstr(item->preset->name); +} + +void ws_history_reset(WSHistory* instance) { + furi_assert(instance); + furi_string_reset(instance->tmp_string); + for + M_EACH(item, instance->history->data, WSHistoryItemArray_t) { + furi_string_free(item->item_str); + furi_string_free(item->preset->name); + free(item->preset); + flipper_format_free(item->flipper_string); + item->type = 0; + } + WSHistoryItemArray_reset(instance->history->data); + instance->last_index_write = 0; + instance->code_last_hash_data = 0; +} + +uint16_t ws_history_get_item(WSHistory* instance) { + furi_assert(instance); + return instance->last_index_write; +} + +uint8_t ws_history_get_type_protocol(WSHistory* instance, uint16_t idx) { + furi_assert(instance); + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + return item->type; +} + +const char* ws_history_get_protocol_name(WSHistory* instance, uint16_t idx) { + furi_assert(instance); + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + flipper_format_rewind(item->flipper_string); + if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { + FURI_LOG_E(TAG, "Missing Protocol"); + furi_string_reset(instance->tmp_string); + } + return furi_string_get_cstr(instance->tmp_string); +} + +FlipperFormat* ws_history_get_raw_data(WSHistory* instance, uint16_t idx) { + furi_assert(instance); + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + if(item->flipper_string) { + return item->flipper_string; + } else { + return NULL; + } +} +bool ws_history_get_text_space_left(WSHistory* instance, FuriString* output) { + furi_assert(instance); + if(instance->last_index_write == WS_HISTORY_MAX) { + if(output != NULL) furi_string_printf(output, "Memory is FULL"); + return true; + } + if(output != NULL) + furi_string_printf(output, "%02u/%02u", instance->last_index_write, WS_HISTORY_MAX); + return false; +} + +void ws_history_get_text_item_menu(WSHistory* instance, FuriString* output, uint16_t idx) { + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + furi_string_set(output, item->item_str); +} + +WSHistoryStateAddKey + ws_history_add_to_history(WSHistory* instance, void* context, SubGhzRadioPreset* preset) { + furi_assert(instance); + furi_assert(context); + + if(instance->last_index_write >= WS_HISTORY_MAX) return WSHistoryStateAddKeyOverflow; + + SubGhzProtocolDecoderBase* decoder_base = context; + if((instance->code_last_hash_data == + subghz_protocol_decoder_base_get_hash_data(decoder_base)) && + ((furi_get_tick() - instance->last_update_timestamp) < 500)) { + instance->last_update_timestamp = furi_get_tick(); + return WSHistoryStateAddKeyTimeOut; + } + + instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + instance->last_update_timestamp = furi_get_tick(); + + FlipperFormat* fff = flipper_format_string_alloc(); + uint32_t id = 0; + subghz_protocol_decoder_base_serialize(decoder_base, fff, preset); + + do { + if(!flipper_format_rewind(fff)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(fff, "Id", (uint32_t*)&id, 1)) { + FURI_LOG_E(TAG, "Missing Id"); + break; + } + } while(false); + flipper_format_free(fff); + + //Update record if found + bool sensor_found = false; + for(size_t i = 0; i < WSHistoryItemArray_size(instance->history->data); i++) { + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, i); + if(item->id == id) { + sensor_found = true; + Stream* flipper_string_stream = flipper_format_get_raw_stream(item->flipper_string); + stream_clean(flipper_string_stream); + subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset); + return WSHistoryStateAddKeyUpdateData; + } + } + + // or add new record + if(!sensor_found) { + WSHistoryItem* item = WSHistoryItemArray_push_raw(instance->history->data); + item->preset = malloc(sizeof(SubGhzRadioPreset)); + item->type = decoder_base->protocol->type; + item->preset->frequency = preset->frequency; + item->preset->name = furi_string_alloc(); + furi_string_set(item->preset->name, preset->name); + item->preset->data = preset->data; + item->preset->data_size = preset->data_size; + item->id = id; + + item->item_str = furi_string_alloc(); + item->flipper_string = flipper_format_string_alloc(); + subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset); + + do { + if(!flipper_format_rewind(item->flipper_string)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_string( + item->flipper_string, "Protocol", instance->tmp_string)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + + if(!flipper_format_rewind(item->flipper_string)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(item->flipper_string, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + uint64_t data = 0; + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + data = (data << 8) | key_data[i]; + } + if(!(uint32_t)(data >> 32)) { + furi_string_printf( + item->item_str, + "%s %lX", + furi_string_get_cstr(instance->tmp_string), + (uint32_t)(data & 0xFFFFFFFF)); + } else { + furi_string_printf( + item->item_str, + "%s %lX%08lX", + furi_string_get_cstr(instance->tmp_string), + (uint32_t)(data >> 32), + (uint32_t)(data & 0xFFFFFFFF)); + } + } while(false); + instance->last_index_write++; + return WSHistoryStateAddKeyNewDada; + } + return WSHistoryStateAddKeyUnknown; +} diff --git a/applications/plugins/weather_station/weather_station_history.h b/applications/plugins/weather_station/weather_station_history.h new file mode 100644 index 00000000..11601fe7 --- /dev/null +++ b/applications/plugins/weather_station/weather_station_history.h @@ -0,0 +1,112 @@ + +#pragma once + +#include +#include +#include +#include +#include + +typedef struct WSHistory WSHistory; + +/** History state add key */ +typedef enum { + WSHistoryStateAddKeyUnknown, + WSHistoryStateAddKeyTimeOut, + WSHistoryStateAddKeyNewDada, + WSHistoryStateAddKeyUpdateData, + WSHistoryStateAddKeyOverflow, +} WSHistoryStateAddKey; + +/** Allocate WSHistory + * + * @return WSHistory* + */ +WSHistory* ws_history_alloc(void); + +/** Free WSHistory + * + * @param instance - WSHistory instance + */ +void ws_history_free(WSHistory* instance); + +/** Clear history + * + * @param instance - WSHistory instance + */ +void ws_history_reset(WSHistory* instance); + +/** Get frequency to history[idx] + * + * @param instance - WSHistory instance + * @param idx - record index + * @return frequency - frequency Hz + */ +uint32_t ws_history_get_frequency(WSHistory* instance, uint16_t idx); + +SubGhzRadioPreset* ws_history_get_radio_preset(WSHistory* instance, uint16_t idx); + +/** Get preset to history[idx] + * + * @param instance - WSHistory instance + * @param idx - record index + * @return preset - preset name + */ +const char* ws_history_get_preset(WSHistory* instance, uint16_t idx); + +/** Get history index write + * + * @param instance - WSHistory instance + * @return idx - current record index + */ +uint16_t ws_history_get_item(WSHistory* instance); + +/** Get type protocol to history[idx] + * + * @param instance - WSHistory instance + * @param idx - record index + * @return type - type protocol + */ +uint8_t ws_history_get_type_protocol(WSHistory* instance, uint16_t idx); + +/** Get name protocol to history[idx] + * + * @param instance - WSHistory instance + * @param idx - record index + * @return name - const char* name protocol + */ +const char* ws_history_get_protocol_name(WSHistory* instance, uint16_t idx); + +/** Get string item menu to history[idx] + * + * @param instance - WSHistory instance + * @param output - FuriString* output + * @param idx - record index + */ +void ws_history_get_text_item_menu(WSHistory* instance, FuriString* output, uint16_t idx); + +/** Get string the remaining number of records to history + * + * @param instance - WSHistory instance + * @param output - FuriString* output + * @return bool - is FUUL + */ +bool ws_history_get_text_space_left(WSHistory* instance, FuriString* output); + +/** Add protocol to history + * + * @param instance - WSHistory instance + * @param context - SubGhzProtocolCommon context + * @param preset - SubGhzRadioPreset preset + * @return WSHistoryStateAddKey; + */ +WSHistoryStateAddKey + ws_history_add_to_history(WSHistory* instance, void* context, SubGhzRadioPreset* preset); + +/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data + * + * @param instance - WSHistory instance + * @param idx - record index + * @return SubGhzProtocolCommonLoad* + */ +FlipperFormat* ws_history_get_raw_data(WSHistory* instance, uint16_t idx); diff --git a/assets/resources/subghz/assets/keeloq_mfcodes_user b/assets/resources/subghz/assets/keeloq_mfcodes_user.example similarity index 80% rename from assets/resources/subghz/assets/keeloq_mfcodes_user rename to assets/resources/subghz/assets/keeloq_mfcodes_user.example index f013afa2..0d43c559 100644 --- a/assets/resources/subghz/assets/keeloq_mfcodes_user +++ b/assets/resources/subghz/assets/keeloq_mfcodes_user.example @@ -1,3 +1,4 @@ +# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user # for adding manufacture keys # AABBCCDDEEFFAABB:X:NAME\r\n # AABBCCDDEEFFAABB - man 64 bit diff --git a/assets/resources/subghz/assets/setting_user b/assets/resources/subghz/assets/setting_user.example similarity index 91% rename from assets/resources/subghz/assets/setting_user rename to assets/resources/subghz/assets/setting_user.example index 1f37a2eb..a0cbb9a0 100644 --- a/assets/resources/subghz/assets/setting_user +++ b/assets/resources/subghz/assets/setting_user.example @@ -1,3 +1,4 @@ +# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user Filetype: Flipper SubGhz Setting File Version: 1 diff --git a/assets/unit_tests/subghz/magellen.sub b/assets/unit_tests/subghz/magellan.sub similarity index 88% rename from assets/unit_tests/subghz/magellen.sub rename to assets/unit_tests/subghz/magellan.sub index 3317fd4b..11684803 100644 --- a/assets/unit_tests/subghz/magellen.sub +++ b/assets/unit_tests/subghz/magellan.sub @@ -2,6 +2,6 @@ Filetype: Flipper SubGhz Key File Version: 1 Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async -Protocol: Magellen +Protocol: Magellan Bit: 32 Key: 00 00 00 00 37 AE 48 28 diff --git a/assets/unit_tests/subghz/magellen_raw.sub b/assets/unit_tests/subghz/magellan_raw.sub similarity index 100% rename from assets/unit_tests/subghz/magellen_raw.sub rename to assets/unit_tests/subghz/magellan_raw.sub diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d3d5a901..b65de9a9 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,2.4,, +Version,+,3.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -124,9 +124,15 @@ Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_host_timing.h,, Header,+,lib/one_wire/one_wire_slave.h,, Header,+,lib/print/wrappers.h,, +Header,+,lib/subghz/blocks/const.h,, +Header,+,lib/subghz/blocks/decoder.h,, +Header,+,lib/subghz/blocks/encoder.h,, +Header,+,lib/subghz/blocks/generic.h,, +Header,+,lib/subghz/blocks/math.h,, Header,+,lib/subghz/environment.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, +Header,+,lib/subghz/subghz_setting.h,, Header,+,lib/subghz/subghz_tx_rx_worker.h,, Header,+,lib/subghz/subghz_worker.h,, Header,+,lib/subghz/transmitter.h,, @@ -2240,14 +2246,20 @@ Function,-,strupr,char*,char* Function,-,strverscmp,int,"const char*, const char*" Function,-,strxfrm,size_t,"char*, const char*, size_t" Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t" +Function,+,subghz_block_generic_deserialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*" +Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" +Function,+,subghz_block_generic_serialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_environment_alloc,SubGhzEnvironment*, Function,+,subghz_environment_free,void,SubGhzEnvironment* -Function,-,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment* -Function,-,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* -Function,-,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* +Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment* +Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* +Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* +Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t" +Function,+,subghz_environment_get_protocol_registry,void*,SubGhzEnvironment* Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" -Function,-,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" -Function,-,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, void*" Function,-,subghz_keystore_alloc,SubGhzKeystore*, Function,-,subghz_keystore_free,void,SubGhzKeystore* Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore* @@ -2255,10 +2267,20 @@ Function,-,subghz_keystore_load,_Bool,"SubGhzKeystore*, const char*" Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, uint8_t*" Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" +Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" +Function,+,subghz_protocol_blocks_crc4,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_crc7,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_crc8,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t" +Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t" +Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t" +Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t" +Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" +Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,-,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" -Function,-,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* -Function,-,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" -Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzPresetDefinition*" +Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* +Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" +Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_decoder_raw_deserialize,_Bool,"void*, FlipperFormat*" @@ -2274,7 +2296,7 @@ Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void* Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* -Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzPresetDefinition*" +Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzRadioPreset*" Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" @@ -2283,6 +2305,23 @@ Function,+,subghz_receiver_reset,void,SubGhzReceiver* Function,+,subghz_receiver_search_decoder_base_by_name,SubGhzProtocolDecoderBase*,"SubGhzReceiver*, const char*" Function,+,subghz_receiver_set_filter,void,"SubGhzReceiver*, SubGhzProtocolFlag" Function,+,subghz_receiver_set_rx_callback,void,"SubGhzReceiver*, SubGhzReceiverCallback, void*" +Function,+,subghz_setting_alloc,SubGhzSetting*, +Function,+,subghz_setting_delete_custom_preset,_Bool,"SubGhzSetting*, const char*" +Function,+,subghz_setting_free,void,SubGhzSetting* +Function,+,subghz_setting_get_default_frequency,uint32_t,SubGhzSetting* +Function,+,subghz_setting_get_frequency,uint32_t,"SubGhzSetting*, size_t" +Function,+,subghz_setting_get_frequency_count,size_t,SubGhzSetting* +Function,+,subghz_setting_get_frequency_default_index,uint32_t,SubGhzSetting* +Function,+,subghz_setting_get_hopper_frequency,uint32_t,"SubGhzSetting*, size_t" +Function,+,subghz_setting_get_hopper_frequency_count,size_t,SubGhzSetting* +Function,+,subghz_setting_get_inx_preset_by_name,int,"SubGhzSetting*, const char*" +Function,+,subghz_setting_get_preset_count,size_t,SubGhzSetting* +Function,+,subghz_setting_get_preset_data,uint8_t*,"SubGhzSetting*, size_t" +Function,+,subghz_setting_get_preset_data_by_name,uint8_t*,"SubGhzSetting*, const char*" +Function,+,subghz_setting_get_preset_data_size,size_t,"SubGhzSetting*, size_t" +Function,+,subghz_setting_get_preset_name,const char*,"SubGhzSetting*, size_t" +Function,+,subghz_setting_load,void,"SubGhzSetting*, const char*" +Function,+,subghz_setting_load_custom_preset,_Bool,"SubGhzSetting*, const char*, FlipperFormat*" Function,+,subghz_transmitter_alloc_init,SubGhzTransmitter*,"SubGhzEnvironment*, const char*" Function,+,subghz_transmitter_deserialize,_Bool,"SubGhzTransmitter*, FlipperFormat*" Function,+,subghz_transmitter_free,void,SubGhzTransmitter* diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index eff2f168..e25d122c 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -11,6 +11,12 @@ env.Append( File("#/lib/subghz/subghz_tx_rx_worker.h"), File("#/lib/subghz/transmitter.h"), File("#/lib/subghz/protocols/raw.h"), + File("#/lib/subghz/blocks/const.h"), + File("#/lib/subghz/blocks/decoder.h"), + File("#/lib/subghz/blocks/encoder.h"), + File("#/lib/subghz/blocks/generic.h"), + File("#/lib/subghz/blocks/math.h"), + File("#/lib/subghz/subghz_setting.h"), ], ) diff --git a/lib/subghz/blocks/const.h b/lib/subghz/blocks/const.h index 57b47d50..f32334e2 100644 --- a/lib/subghz/blocks/const.h +++ b/lib/subghz/blocks/const.h @@ -4,9 +4,17 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { const uint16_t te_long; const uint16_t te_short; const uint16_t te_delta; const uint8_t min_count_bit_for_found; } SubGhzBlockConst; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/decoder.h b/lib/subghz/blocks/decoder.h index 339e27c1..25549fab 100644 --- a/lib/subghz/blocks/decoder.h +++ b/lib/subghz/blocks/decoder.h @@ -4,6 +4,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzBlockDecoder SubGhzBlockDecoder; struct SubGhzBlockDecoder { @@ -26,3 +30,7 @@ void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit); * @return hash Hash sum */ uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/encoder.h b/lib/subghz/blocks/encoder.h index 6ad734cb..1ff07772 100644 --- a/lib/subghz/blocks/encoder.h +++ b/lib/subghz/blocks/encoder.h @@ -6,6 +6,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { bool is_running; size_t repeat; @@ -50,3 +54,7 @@ size_t subghz_protocol_blocks_get_upload( LevelDuration* upload, size_t max_size_upload, uint32_t duration_bit); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 7496aea3..1bad5f0a 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -23,7 +23,7 @@ void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* p bool subghz_block_generic_serialize( SubGhzBlockGeneric* instance, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(instance); bool res = false; FuriString* temp_str; diff --git a/lib/subghz/blocks/generic.h b/lib/subghz/blocks/generic.h index 300807da..1448f0ea 100644 --- a/lib/subghz/blocks/generic.h +++ b/lib/subghz/blocks/generic.h @@ -9,6 +9,10 @@ #include "furi_hal.h" #include "../types.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzBlockGeneric SubGhzBlockGeneric; struct SubGhzBlockGeneric { @@ -31,13 +35,13 @@ void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* p * Serialize data SubGhzBlockGeneric. * @param instance Pointer to a SubGhzBlockGeneric instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_block_generic_serialize( SubGhzBlockGeneric* instance, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzBlockGeneric. @@ -46,3 +50,7 @@ bool subghz_block_generic_serialize( * @return true On success */ bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/math.c b/lib/subghz/blocks/math.c index fca50c8f..782076e3 100644 --- a/lib/subghz/blocks/math.c +++ b/lib/subghz/blocks/math.c @@ -14,4 +14,69 @@ uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit) { parity += bit_read(key, i); } return parity & 0x01; +} + +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + unsigned nBytes, + uint8_t polynomial, + uint8_t init) { + unsigned remainder = init << 4; // LSBs are unused + unsigned poly = polynomial << 4; + unsigned bit; + + while(nBytes--) { + remainder ^= *message++; + for(bit = 0; bit < 8; bit++) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 4 & 0x0f; // discard the LSBs +} + +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + unsigned nBytes, + uint8_t polynomial, + uint8_t init) { + unsigned remainder = init << 1; // LSB is unused + unsigned poly = polynomial << 1; + unsigned byte, bit; + + for(byte = 0; byte < nBytes; ++byte) { + remainder ^= message[byte]; + for(bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 1 & 0x7f; // discard the LSB +} + +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + unsigned nBytes, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init; + unsigned byte, bit; + + for(byte = 0; byte < nBytes; ++byte) { + remainder ^= message[byte]; + for(bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; } \ No newline at end of file diff --git a/lib/subghz/blocks/math.h b/lib/subghz/blocks/math.h index 85b146eb..c312b607 100644 --- a/lib/subghz/blocks/math.h +++ b/lib/subghz/blocks/math.h @@ -9,7 +9,11 @@ #define bit_clear(value, bit) ((value) &= ~(1UL << (bit))) #define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit)) #define DURATION_DIFF(x, y) ((x < y) ? (y - x) : (x - y)) +#define abs(x) ((x) > 0 ? (x) : -(x)) +#ifdef __cplusplus +extern "C" { +#endif /** * Flip the data bitwise. * @param key In data @@ -25,3 +29,51 @@ uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit); * @return parity */ uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit); + +/** + * CRC-4. + * @param message array of bytes to check + * @param nBytes number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + unsigned nBytes, + uint8_t polynomial, + uint8_t init); + +/** + * CRC-7. + * @param message array of bytes to check + * @param nBytes number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + unsigned nBytes, + uint8_t polynomial, + uint8_t init); + +/** + * Generic Cyclic Redundancy Check CRC-8. + * Example polynomial: 0x31 = x8 + x5 + x4 + 1 (x8 is implicit) + * Example polynomial: 0x80 = x8 + x7 (a normal bit-by-bit parity XOR) + * @param message array of bytes to check + * @param nBytes number of bytes in message + * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) + * @param init starting crc value + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + unsigned nBytes, + uint8_t polynomial, + uint8_t init); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index 46d0d368..0a4b7b60 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -1,7 +1,9 @@ #include "environment.h" +#include "registry.h" struct SubGhzEnvironment { SubGhzKeystore* keystore; + const SubGhzProtocolRegistry* protocol_registry; const char* came_atomo_rainbow_table_file_name; const char* nice_flor_s_rainbow_table_file_name; }; @@ -10,6 +12,7 @@ SubGhzEnvironment* subghz_environment_alloc() { SubGhzEnvironment* instance = malloc(sizeof(SubGhzEnvironment)); instance->keystore = subghz_keystore_alloc(); + instance->protocol_registry = NULL; instance->came_atomo_rainbow_table_file_name = NULL; instance->nice_flor_s_rainbow_table_file_name = NULL; @@ -19,6 +22,9 @@ SubGhzEnvironment* subghz_environment_alloc() { void subghz_environment_free(SubGhzEnvironment* instance) { furi_assert(instance); + instance->protocol_registry = NULL; + instance->came_atomo_rainbow_table_file_name = NULL; + instance->nice_flor_s_rainbow_table_file_name = NULL; subghz_keystore_free(instance->keystore); free(instance); @@ -65,3 +71,30 @@ const char* return instance->nice_flor_s_rainbow_table_file_name; } + +void subghz_environment_set_protocol_registry( + SubGhzEnvironment* instance, + void* protocol_registry_items) { + furi_assert(instance); + const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items; + instance->protocol_registry = protocol_registry; +} + +void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { + furi_assert(instance); + furi_assert(instance->protocol_registry); + return (void*)instance->protocol_registry; +} + +const char* + subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx) { + furi_assert(instance); + furi_assert(instance->protocol_registry); + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_index(instance->protocol_registry, idx); + if(protocol != NULL) { + return protocol->name; + } else { + return NULL; + } +} \ No newline at end of file diff --git a/lib/subghz/environment.h b/lib/subghz/environment.h index d4678e41..5f8fcf1f 100644 --- a/lib/subghz/environment.h +++ b/lib/subghz/environment.h @@ -69,6 +69,30 @@ void subghz_environment_set_nice_flor_s_rainbow_table_file_name( const char* subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance); +/** + * Set list of protocols to work. + * @param instance Pointer to a SubGhzEnvironment instance + * @param protocol_registry_items Pointer to a SubGhzProtocolRegistry + */ +void subghz_environment_set_protocol_registry( + SubGhzEnvironment* instance, + void* protocol_registry_items); + +/** + * Get list of protocols to work. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Pointer to a SubGhzProtocolRegistry + */ +void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); + +/** + * Get list of protocols names. + * @param instance Pointer to a SubGhzEnvironment instance + * @param idx index protocols + * @return Pointer to a SubGhzProtocolRegistry + */ +const char* subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx); + #ifdef __cplusplus } #endif diff --git a/lib/subghz/protocols/base.c b/lib/subghz/protocols/base.c index 4ee7a3f8..36f33b9a 100644 --- a/lib/subghz/protocols/base.c +++ b/lib/subghz/protocols/base.c @@ -26,7 +26,7 @@ bool subghz_protocol_decoder_base_get_string( bool subghz_protocol_decoder_base_serialize( SubGhzProtocolDecoderBase* decoder_base, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { bool status = false; if(decoder_base->protocol && decoder_base->protocol->decoder && diff --git a/lib/subghz/protocols/base.h b/lib/subghz/protocols/base.h index 47b4e482..1f3d3e1b 100644 --- a/lib/subghz/protocols/base.h +++ b/lib/subghz/protocols/base.h @@ -48,13 +48,13 @@ bool subghz_protocol_decoder_base_get_string( * Serialize data SubGhzProtocolDecoderBase. * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_base_serialize( SubGhzProtocolDecoderBase* decoder_base, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderBase. diff --git a/lib/subghz/protocols/bett.c b/lib/subghz/protocols/bett.c index 605a922c..2dd39af9 100644 --- a/lib/subghz/protocols/bett.c +++ b/lib/subghz/protocols/bett.c @@ -299,7 +299,7 @@ uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) { bool subghz_protocol_decoder_bett_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderBETT* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/bett.h b/lib/subghz/protocols/bett.h index 04bf46b5..c0ce0b7f 100644 --- a/lib/subghz/protocols/bett.h +++ b/lib/subghz/protocols/bett.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderBETT. * @param context Pointer to a SubGhzProtocolDecoderBETT instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_bett_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderBETT. diff --git a/lib/subghz/protocols/came.c b/lib/subghz/protocols/came.c index 3fc61bf6..1ac4ec05 100644 --- a/lib/subghz/protocols/came.c +++ b/lib/subghz/protocols/came.c @@ -295,7 +295,7 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context) { bool subghz_protocol_decoder_came_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderCame* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/came.h b/lib/subghz/protocols/came.h index d1ac7628..253c93aa 100644 --- a/lib/subghz/protocols/came.h +++ b/lib/subghz/protocols/came.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderCame. * @param context Pointer to a SubGhzProtocolDecoderCame instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_came_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderCame. diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index 8c2a542c..3f6045be 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -301,7 +301,7 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) { bool subghz_protocol_decoder_came_atomo_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderCameAtomo* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/came_atomo.h b/lib/subghz/protocols/came_atomo.h index aa1cffd0..3781a0d8 100644 --- a/lib/subghz/protocols/came_atomo.h +++ b/lib/subghz/protocols/came_atomo.h @@ -48,13 +48,13 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderCameAtomo. * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_came_atomo_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderCameAtomo. diff --git a/lib/subghz/protocols/came_twee.c b/lib/subghz/protocols/came_twee.c index 3d5029ec..e7eb33c4 100644 --- a/lib/subghz/protocols/came_twee.c +++ b/lib/subghz/protocols/came_twee.c @@ -422,7 +422,7 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) { bool subghz_protocol_decoder_came_twee_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderCameTwee* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/came_twee.h b/lib/subghz/protocols/came_twee.h index aa1f0e0d..359b964d 100644 --- a/lib/subghz/protocols/came_twee.h +++ b/lib/subghz/protocols/came_twee.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderCameTwee. * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_came_twee_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderCameTwee. diff --git a/lib/subghz/protocols/chamberlain_code.c b/lib/subghz/protocols/chamberlain_code.c index d2d19af2..3650a986 100644 --- a/lib/subghz/protocols/chamberlain_code.c +++ b/lib/subghz/protocols/chamberlain_code.c @@ -427,7 +427,7 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) { bool subghz_protocol_decoder_chamb_code_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderChamb_Code* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/chamberlain_code.h b/lib/subghz/protocols/chamberlain_code.h index 923a3151..f87b64d9 100644 --- a/lib/subghz/protocols/chamberlain_code.h +++ b/lib/subghz/protocols/chamberlain_code.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderChamb_Code. * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_chamb_code_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderChamb_Code. diff --git a/lib/subghz/protocols/clemsa.c b/lib/subghz/protocols/clemsa.c index dbee0ac9..a2cb7a18 100644 --- a/lib/subghz/protocols/clemsa.c +++ b/lib/subghz/protocols/clemsa.c @@ -319,7 +319,7 @@ uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) { bool subghz_protocol_decoder_clemsa_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderClemsa* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/clemsa.h b/lib/subghz/protocols/clemsa.h index 8287af23..8858c1a3 100644 --- a/lib/subghz/protocols/clemsa.h +++ b/lib/subghz/protocols/clemsa.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderClemsa. * @param context Pointer to a SubGhzProtocolDecoderClemsa instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_clemsa_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderClemsa. diff --git a/lib/subghz/protocols/doitrand.c b/lib/subghz/protocols/doitrand.c index 5c340214..6b31d4f2 100644 --- a/lib/subghz/protocols/doitrand.c +++ b/lib/subghz/protocols/doitrand.c @@ -313,7 +313,7 @@ uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) { bool subghz_protocol_decoder_doitrand_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderDoitrand* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/doitrand.h b/lib/subghz/protocols/doitrand.h index c96946a1..30f1fffd 100644 --- a/lib/subghz/protocols/doitrand.h +++ b/lib/subghz/protocols/doitrand.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderDoitrand. * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_doitrand_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderDoitrand. diff --git a/lib/subghz/protocols/faac_slh.c b/lib/subghz/protocols/faac_slh.c index b5443590..ad186738 100644 --- a/lib/subghz/protocols/faac_slh.c +++ b/lib/subghz/protocols/faac_slh.c @@ -183,7 +183,7 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) { bool subghz_protocol_decoder_faac_slh_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderFaacSLH* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/faac_slh.h b/lib/subghz/protocols/faac_slh.h index 9b18f5ea..52c265d2 100644 --- a/lib/subghz/protocols/faac_slh.h +++ b/lib/subghz/protocols/faac_slh.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderFaacSLH. * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_faac_slh_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderFaacSLH. diff --git a/lib/subghz/protocols/gate_tx.c b/lib/subghz/protocols/gate_tx.c index 5cf3a871..4c7c2d48 100644 --- a/lib/subghz/protocols/gate_tx.c +++ b/lib/subghz/protocols/gate_tx.c @@ -293,7 +293,7 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context) { bool subghz_protocol_decoder_gate_tx_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderGateTx* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/gate_tx.h b/lib/subghz/protocols/gate_tx.h index 36eeb9a9..4bfba359 100644 --- a/lib/subghz/protocols/gate_tx.h +++ b/lib/subghz/protocols/gate_tx.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderGateTx. * @param context Pointer to a SubGhzProtocolDecoderGateTx instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_gate_tx_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderGateTx. diff --git a/lib/subghz/protocols/holtek.c b/lib/subghz/protocols/holtek.c index 230f4cfe..39e27bbf 100644 --- a/lib/subghz/protocols/holtek.c +++ b/lib/subghz/protocols/holtek.c @@ -326,7 +326,7 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context) { bool subghz_protocol_decoder_holtek_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderHoltek* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/holtek.h b/lib/subghz/protocols/holtek.h index 1dac783b..252a26dc 100644 --- a/lib/subghz/protocols/holtek.h +++ b/lib/subghz/protocols/holtek.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderHoltek. * @param context Pointer to a SubGhzProtocolDecoderHoltek instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_holtek_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderHoltek. diff --git a/lib/subghz/protocols/honeywell_wdb.c b/lib/subghz/protocols/honeywell_wdb.c index 3cd62698..3b940fc6 100644 --- a/lib/subghz/protocols/honeywell_wdb.c +++ b/lib/subghz/protocols/honeywell_wdb.c @@ -348,7 +348,7 @@ uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context) { bool subghz_protocol_decoder_honeywell_wdb_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderHoneywell_WDB* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/honeywell_wdb.h b/lib/subghz/protocols/honeywell_wdb.h index c61aa822..82863183 100644 --- a/lib/subghz/protocols/honeywell_wdb.h +++ b/lib/subghz/protocols/honeywell_wdb.h @@ -85,13 +85,13 @@ uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderHoneywell_WDB. * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_honeywell_wdb_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderHoneywell_WDB. diff --git a/lib/subghz/protocols/hormann.c b/lib/subghz/protocols/hormann.c index d8438604..cb6adaf6 100644 --- a/lib/subghz/protocols/hormann.c +++ b/lib/subghz/protocols/hormann.c @@ -314,7 +314,7 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context) { bool subghz_protocol_decoder_hormann_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderHormann* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/hormann.h b/lib/subghz/protocols/hormann.h index 743ad0b3..857a5004 100644 --- a/lib/subghz/protocols/hormann.h +++ b/lib/subghz/protocols/hormann.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderHormann. * @param context Pointer to a SubGhzProtocolDecoderHormann instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_hormann_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderHormann. diff --git a/lib/subghz/protocols/ido.c b/lib/subghz/protocols/ido.c index 6cc60bde..31ff20e4 100644 --- a/lib/subghz/protocols/ido.c +++ b/lib/subghz/protocols/ido.c @@ -182,7 +182,7 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context) { bool subghz_protocol_decoder_ido_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderIDo* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/ido.h b/lib/subghz/protocols/ido.h index 67d2b4d8..634f6ff8 100644 --- a/lib/subghz/protocols/ido.h +++ b/lib/subghz/protocols/ido.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderIDo. * @param context Pointer to a SubGhzProtocolDecoderIDo instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_ido_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderIDo. diff --git a/lib/subghz/protocols/intertechno_v3.c b/lib/subghz/protocols/intertechno_v3.c index 14e137ca..2c4e514c 100644 --- a/lib/subghz/protocols/intertechno_v3.c +++ b/lib/subghz/protocols/intertechno_v3.c @@ -407,7 +407,7 @@ uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) { bool subghz_protocol_decoder_intertechno_v3_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderIntertechno_V3* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/intertechno_v3.h b/lib/subghz/protocols/intertechno_v3.h index 078d0397..ffee14b0 100644 --- a/lib/subghz/protocols/intertechno_v3.h +++ b/lib/subghz/protocols/intertechno_v3.h @@ -85,13 +85,13 @@ uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderIntertechno_V3. * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_intertechno_v3_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderIntertechno_V3. diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 039d97a6..ae6588e7 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -173,7 +173,7 @@ bool subghz_protocol_keeloq_create_data( uint8_t btn, uint16_t cnt, const char* manufacture_name, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolEncoderKeeloq* instance = context; instance->generic.serial = serial; @@ -646,7 +646,7 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) { bool subghz_protocol_decoder_keeloq_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderKeeloq* instance = context; subghz_protocol_keeloq_check_remote_controller( diff --git a/lib/subghz/protocols/keeloq.h b/lib/subghz/protocols/keeloq.h index c0575bd9..7b1aaee2 100644 --- a/lib/subghz/protocols/keeloq.h +++ b/lib/subghz/protocols/keeloq.h @@ -32,7 +32,7 @@ void subghz_protocol_encoder_keeloq_free(void* context); * @param btn Button number, 4 bit * @param cnt Container value, 16 bit * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzPresetDefinition + * @param preset Modulation, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_keeloq_create_data( @@ -42,7 +42,7 @@ bool subghz_protocol_keeloq_create_data( uint8_t btn, uint16_t cnt, const char* manufacture_name, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize and generating an upload to send. @@ -103,13 +103,13 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderKeeloq. * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_keeloq_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderKeeloq. diff --git a/lib/subghz/protocols/kia.c b/lib/subghz/protocols/kia.c index d4683866..997f8e1d 100644 --- a/lib/subghz/protocols/kia.c +++ b/lib/subghz/protocols/kia.c @@ -233,7 +233,7 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) { bool subghz_protocol_decoder_kia_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderKIA* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/kia.h b/lib/subghz/protocols/kia.h index cf186921..a9afcf14 100644 --- a/lib/subghz/protocols/kia.h +++ b/lib/subghz/protocols/kia.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderKIA. * @param context Pointer to a SubGhzProtocolDecoderKIA instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_kia_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderKIA. diff --git a/lib/subghz/protocols/linear.c b/lib/subghz/protocols/linear.c index 582c4aaf..2fc8b20c 100644 --- a/lib/subghz/protocols/linear.c +++ b/lib/subghz/protocols/linear.c @@ -303,7 +303,7 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context) { bool subghz_protocol_decoder_linear_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderLinear* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/linear.h b/lib/subghz/protocols/linear.h index 6111ad85..923337ac 100644 --- a/lib/subghz/protocols/linear.h +++ b/lib/subghz/protocols/linear.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderLinear. * @param context Pointer to a SubGhzProtocolDecoderLinear instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_linear_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderLinear. diff --git a/lib/subghz/protocols/magellen.c b/lib/subghz/protocols/magellan.c similarity index 59% rename from lib/subghz/protocols/magellen.c rename to lib/subghz/protocols/magellan.c index 160ec4a2..71497525 100644 --- a/lib/subghz/protocols/magellen.c +++ b/lib/subghz/protocols/magellan.c @@ -1,4 +1,4 @@ -#include "magellen.h" +#include "magellan.h" #include "../blocks/const.h" #include "../blocks/decoder.h" @@ -6,16 +6,16 @@ #include "../blocks/generic.h" #include "../blocks/math.h" -#define TAG "SubGhzProtocolMagellen" +#define TAG "SubGhzProtocolMagellan" -static const SubGhzBlockConst subghz_protocol_magellen_const = { +static const SubGhzBlockConst subghz_protocol_magellan_const = { .te_short = 200, .te_long = 400, .te_delta = 100, .min_count_bit_for_found = 32, }; -struct SubGhzProtocolDecoderMagellen { +struct SubGhzProtocolDecoderMagellan { SubGhzProtocolDecoderBase base; SubGhzBlockDecoder decoder; @@ -23,7 +23,7 @@ struct SubGhzProtocolDecoderMagellen { uint16_t header_count; }; -struct SubGhzProtocolEncoderMagellen { +struct SubGhzProtocolEncoderMagellan { SubGhzProtocolEncoderBase base; SubGhzProtocolBlockEncoder encoder; @@ -31,50 +31,50 @@ struct SubGhzProtocolEncoderMagellen { }; typedef enum { - MagellenDecoderStepReset = 0, - MagellenDecoderStepCheckPreambula, - MagellenDecoderStepFoundPreambula, - MagellenDecoderStepSaveDuration, - MagellenDecoderStepCheckDuration, -} MagellenDecoderStep; + MagellanDecoderStepReset = 0, + MagellanDecoderStepCheckPreambula, + MagellanDecoderStepFoundPreambula, + MagellanDecoderStepSaveDuration, + MagellanDecoderStepCheckDuration, +} MagellanDecoderStep; -const SubGhzProtocolDecoder subghz_protocol_magellen_decoder = { - .alloc = subghz_protocol_decoder_magellen_alloc, - .free = subghz_protocol_decoder_magellen_free, +const SubGhzProtocolDecoder subghz_protocol_magellan_decoder = { + .alloc = subghz_protocol_decoder_magellan_alloc, + .free = subghz_protocol_decoder_magellan_free, - .feed = subghz_protocol_decoder_magellen_feed, - .reset = subghz_protocol_decoder_magellen_reset, + .feed = subghz_protocol_decoder_magellan_feed, + .reset = subghz_protocol_decoder_magellan_reset, - .get_hash_data = subghz_protocol_decoder_magellen_get_hash_data, - .serialize = subghz_protocol_decoder_magellen_serialize, - .deserialize = subghz_protocol_decoder_magellen_deserialize, - .get_string = subghz_protocol_decoder_magellen_get_string, + .get_hash_data = subghz_protocol_decoder_magellan_get_hash_data, + .serialize = subghz_protocol_decoder_magellan_serialize, + .deserialize = subghz_protocol_decoder_magellan_deserialize, + .get_string = subghz_protocol_decoder_magellan_get_string, }; -const SubGhzProtocolEncoder subghz_protocol_magellen_encoder = { - .alloc = subghz_protocol_encoder_magellen_alloc, - .free = subghz_protocol_encoder_magellen_free, +const SubGhzProtocolEncoder subghz_protocol_magellan_encoder = { + .alloc = subghz_protocol_encoder_magellan_alloc, + .free = subghz_protocol_encoder_magellan_free, - .deserialize = subghz_protocol_encoder_magellen_deserialize, - .stop = subghz_protocol_encoder_magellen_stop, - .yield = subghz_protocol_encoder_magellen_yield, + .deserialize = subghz_protocol_encoder_magellan_deserialize, + .stop = subghz_protocol_encoder_magellan_stop, + .yield = subghz_protocol_encoder_magellan_yield, }; -const SubGhzProtocol subghz_protocol_magellen = { - .name = SUBGHZ_PROTOCOL_MAGELLEN_NAME, +const SubGhzProtocol subghz_protocol_magellan = { + .name = SUBGHZ_PROTOCOL_MAGELLAN_NAME, .type = SubGhzProtocolTypeStatic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - .decoder = &subghz_protocol_magellen_decoder, - .encoder = &subghz_protocol_magellen_encoder, + .decoder = &subghz_protocol_magellan_decoder, + .encoder = &subghz_protocol_magellan_encoder, }; -void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment) { +void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment) { UNUSED(environment); - SubGhzProtocolEncoderMagellen* instance = malloc(sizeof(SubGhzProtocolEncoderMagellen)); + SubGhzProtocolEncoderMagellan* instance = malloc(sizeof(SubGhzProtocolEncoderMagellan)); - instance->base.protocol = &subghz_protocol_magellen; + instance->base.protocol = &subghz_protocol_magellan; instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 10; @@ -84,75 +84,75 @@ void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment) { return instance; } -void subghz_protocol_encoder_magellen_free(void* context) { +void subghz_protocol_encoder_magellan_free(void* context) { furi_assert(context); - SubGhzProtocolEncoderMagellen* instance = context; + SubGhzProtocolEncoderMagellan* instance = context; free(instance->encoder.upload); free(instance); } /** * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderMagellen instance + * @param instance Pointer to a SubGhzProtocolEncoderMagellan instance * @return true On success */ -static bool subghz_protocol_encoder_magellen_get_upload(SubGhzProtocolEncoderMagellen* instance) { +static bool subghz_protocol_encoder_magellan_get_upload(SubGhzProtocolEncoderMagellan* instance) { furi_assert(instance); size_t index = 0; //Send header instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short * 4); + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short * 4); instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short); + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); for(uint8_t i = 0; i < 12; i++) { instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short); + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); } instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long); + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); //Send start bit instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_long * 3); + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long * 3); instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long); + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); //Send key data for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { if(bit_read(instance->generic.data, i - 1)) { //send bit 1 instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long); + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); } else { //send bit 0 instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_long); + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long); instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short); + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); } } //Send stop bit instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long * 100); + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long * 100); instance->encoder.size_upload = index; return true; } -bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* flipper_format) { +bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); - SubGhzProtocolEncoderMagellen* instance = context; + SubGhzProtocolEncoderMagellan* instance = context; bool res = false; do { if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { @@ -160,7 +160,7 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* break; } if(instance->generic.data_count_bit != - subghz_protocol_magellen_const.min_count_bit_for_found) { + subghz_protocol_magellan_const.min_count_bit_for_found) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } @@ -168,7 +168,7 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_magellen_get_upload(instance)) break; + if(!subghz_protocol_encoder_magellan_get_upload(instance)) break; instance->encoder.is_running = true; res = true; @@ -177,13 +177,13 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* return res; } -void subghz_protocol_encoder_magellen_stop(void* context) { - SubGhzProtocolEncoderMagellen* instance = context; +void subghz_protocol_encoder_magellan_stop(void* context) { + SubGhzProtocolEncoderMagellan* instance = context; instance->encoder.is_running = false; } -LevelDuration subghz_protocol_encoder_magellen_yield(void* context) { - SubGhzProtocolEncoderMagellen* instance = context; +LevelDuration subghz_protocol_encoder_magellan_yield(void* context) { + SubGhzProtocolEncoderMagellan* instance = context; if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { instance->encoder.is_running = false; @@ -200,27 +200,27 @@ LevelDuration subghz_protocol_encoder_magellen_yield(void* context) { return ret; } -void* subghz_protocol_decoder_magellen_alloc(SubGhzEnvironment* environment) { +void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment) { UNUSED(environment); - SubGhzProtocolDecoderMagellen* instance = malloc(sizeof(SubGhzProtocolDecoderMagellen)); - instance->base.protocol = &subghz_protocol_magellen; + SubGhzProtocolDecoderMagellan* instance = malloc(sizeof(SubGhzProtocolDecoderMagellan)); + instance->base.protocol = &subghz_protocol_magellan; instance->generic.protocol_name = instance->base.protocol->name; return instance; } -void subghz_protocol_decoder_magellen_free(void* context) { +void subghz_protocol_decoder_magellan_free(void* context) { furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; + SubGhzProtocolDecoderMagellan* instance = context; free(instance); } -void subghz_protocol_decoder_magellen_reset(void* context) { +void subghz_protocol_decoder_magellan_reset(void* context) { furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; - instance->decoder.parser_step = MagellenDecoderStepReset; + SubGhzProtocolDecoderMagellan* instance = context; + instance->decoder.parser_step = MagellanDecoderStepReset; } -uint8_t subghz_protocol_magellen_crc8(uint8_t* data, size_t len) { +uint8_t subghz_protocol_magellan_crc8(uint8_t* data, size_t len) { uint8_t crc = 0x00; size_t i, j; for(i = 0; i < len; i++) { @@ -235,99 +235,99 @@ uint8_t subghz_protocol_magellen_crc8(uint8_t* data, size_t len) { return crc; } -static bool subghz_protocol_magellen_check_crc(SubGhzProtocolDecoderMagellen* instance) { +static bool subghz_protocol_magellan_check_crc(SubGhzProtocolDecoderMagellan* instance) { uint8_t data[3] = { instance->decoder.decode_data >> 24, instance->decoder.decode_data >> 16, instance->decoder.decode_data >> 8}; return (instance->decoder.decode_data & 0xFF) == - subghz_protocol_magellen_crc8(data, sizeof(data)); + subghz_protocol_magellan_crc8(data, sizeof(data)); } -void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t duration) { +void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration) { furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; + SubGhzProtocolDecoderMagellan* instance = context; switch(instance->decoder.parser_step) { - case MagellenDecoderStepReset: - if((level) && (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) < - subghz_protocol_magellen_const.te_delta)) { - instance->decoder.parser_step = MagellenDecoderStepCheckPreambula; + case MagellanDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + instance->decoder.parser_step = MagellanDecoderStepCheckPreambula; instance->decoder.te_last = duration; instance->header_count = 0; } break; - case MagellenDecoderStepCheckPreambula: + case MagellanDecoderStepCheckPreambula: if(level) { instance->decoder.te_last = duration; } else { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) < - subghz_protocol_magellen_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) < - subghz_protocol_magellen_const.te_delta)) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { // Found header instance->header_count++; } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) < - subghz_protocol_magellen_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) < - subghz_protocol_magellen_const.te_delta * 2) && + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta * 2) && (instance->header_count > 10)) { - instance->decoder.parser_step = MagellenDecoderStepFoundPreambula; + instance->decoder.parser_step = MagellanDecoderStepFoundPreambula; } else { - instance->decoder.parser_step = MagellenDecoderStepReset; + instance->decoder.parser_step = MagellanDecoderStepReset; } } break; - case MagellenDecoderStepFoundPreambula: + case MagellanDecoderStepFoundPreambula: if(level) { instance->decoder.te_last = duration; } else { if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_magellen_const.te_short * 6) < - subghz_protocol_magellen_const.te_delta * 3) && - (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) < - subghz_protocol_magellen_const.te_delta * 2)) { - instance->decoder.parser_step = MagellenDecoderStepSaveDuration; + instance->decoder.te_last, subghz_protocol_magellan_const.te_short * 6) < + subghz_protocol_magellan_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta * 2)) { + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; instance->decoder.decode_data = 0; instance->decoder.decode_count_bit = 0; } else { - instance->decoder.parser_step = MagellenDecoderStepReset; + instance->decoder.parser_step = MagellanDecoderStepReset; } } break; - case MagellenDecoderStepSaveDuration: + case MagellanDecoderStepSaveDuration: if(level) { instance->decoder.te_last = duration; - instance->decoder.parser_step = MagellenDecoderStepCheckDuration; + instance->decoder.parser_step = MagellanDecoderStepCheckDuration; } else { - instance->decoder.parser_step = MagellenDecoderStepReset; + instance->decoder.parser_step = MagellanDecoderStepReset; } break; - case MagellenDecoderStepCheckDuration: + case MagellanDecoderStepCheckDuration: if(!level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) < - subghz_protocol_magellen_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) < - subghz_protocol_magellen_const.te_delta)) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta)) { subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = MagellenDecoderStepSaveDuration; + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_long) < - subghz_protocol_magellen_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) < - subghz_protocol_magellen_const.te_delta)) { + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = MagellenDecoderStepSaveDuration; - } else if(duration >= (subghz_protocol_magellen_const.te_long * 3)) { + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + } else if(duration >= (subghz_protocol_magellan_const.te_long * 3)) { //Found stop bit if((instance->decoder.decode_count_bit == - subghz_protocol_magellen_const.min_count_bit_for_found) && - subghz_protocol_magellen_check_crc(instance)) { + subghz_protocol_magellan_const.min_count_bit_for_found) && + subghz_protocol_magellan_check_crc(instance)) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; if(instance->base.callback) @@ -335,12 +335,12 @@ void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t d } instance->decoder.decode_data = 0; instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = MagellenDecoderStepReset; + instance->decoder.parser_step = MagellanDecoderStepReset; } else { - instance->decoder.parser_step = MagellenDecoderStepReset; + instance->decoder.parser_step = MagellanDecoderStepReset; } } else { - instance->decoder.parser_step = MagellenDecoderStepReset; + instance->decoder.parser_step = MagellanDecoderStepReset; } break; } @@ -350,7 +350,7 @@ void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t d * Analysis of received data * @param instance Pointer to a SubGhzBlockGeneric* instance */ -static void subghz_protocol_magellen_check_remote_controller(SubGhzBlockGeneric* instance) { +static void subghz_protocol_magellan_check_remote_controller(SubGhzBlockGeneric* instance) { /* * package 32b data 24b CRC8 * 0x037AE4828 => 001101111010111001001000 00101000 @@ -375,7 +375,7 @@ static void subghz_protocol_magellen_check_remote_controller(SubGhzBlockGeneric* instance->btn = (data_rev >> 16) & 0xFF; } -static void subghz_protocol_magellen_get_event_serialize(uint8_t event, FuriString* output) { +static void subghz_protocol_magellan_get_event_serialize(uint8_t event, FuriString* output) { furi_string_cat_printf( output, "%s%s%s%s%s%s%s%s", @@ -390,32 +390,32 @@ static void subghz_protocol_magellen_get_event_serialize(uint8_t event, FuriStri ((event >> 7) & 0x1 ? ", ?" : "")); } -uint8_t subghz_protocol_decoder_magellen_get_hash_data(void* context) { +uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context) { furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; + SubGhzProtocolDecoderMagellan* instance = context; return subghz_protocol_blocks_get_hash_data( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_magellen_serialize( +bool subghz_protocol_decoder_magellan_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; + SubGhzProtocolDecoderMagellan* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_magellen_deserialize(void* context, FlipperFormat* flipper_format) { +bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; + SubGhzProtocolDecoderMagellan* instance = context; bool ret = false; do { if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { break; } if(instance->generic.data_count_bit != - subghz_protocol_magellen_const.min_count_bit_for_found) { + subghz_protocol_magellan_const.min_count_bit_for_found) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } @@ -424,10 +424,10 @@ bool subghz_protocol_decoder_magellen_deserialize(void* context, FlipperFormat* return ret; } -void subghz_protocol_decoder_magellen_get_string(void* context, FuriString* output) { +void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output) { furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; - subghz_protocol_magellen_check_remote_controller(&instance->generic); + SubGhzProtocolDecoderMagellan* instance = context; + subghz_protocol_magellan_check_remote_controller(&instance->generic); furi_string_cat_printf( output, "%s %dbit\r\n" @@ -441,5 +441,5 @@ void subghz_protocol_decoder_magellen_get_string(void* context, FuriString* outp instance->generic.serial & 0xFF, instance->generic.btn); - subghz_protocol_magellen_get_event_serialize(instance->generic.btn, output); + subghz_protocol_magellan_get_event_serialize(instance->generic.btn, output); } diff --git a/lib/subghz/protocols/magellan.h b/lib/subghz/protocols/magellan.h new file mode 100644 index 00000000..a179c9cb --- /dev/null +++ b/lib/subghz/protocols/magellan.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MAGELLAN_NAME "Magellan" + +typedef struct SubGhzProtocolDecoderMagellan SubGhzProtocolDecoderMagellan; +typedef struct SubGhzProtocolEncoderMagellan SubGhzProtocolEncoderMagellan; + +extern const SubGhzProtocolDecoder subghz_protocol_magellan_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_magellan_encoder; +extern const SubGhzProtocol subghz_protocol_magellan; + +/** + * Allocate SubGhzProtocolEncoderMagellan. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMagellan* pointer to a SubGhzProtocolEncoderMagellan instance + */ +void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMagellan. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + */ +void subghz_protocol_encoder_magellan_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + */ +void subghz_protocol_encoder_magellan_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_magellan_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMagellan. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMagellan* pointer to a SubGhzProtocolDecoderMagellan instance + */ +void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + */ +void subghz_protocol_decoder_magellan_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + */ +void subghz_protocol_decoder_magellan_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_magellan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param output Resulting text + */ +void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/magellen.h b/lib/subghz/protocols/magellen.h deleted file mode 100644 index 226919c8..00000000 --- a/lib/subghz/protocols/magellen.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_MAGELLEN_NAME "Magellen" - -typedef struct SubGhzProtocolDecoderMagellen SubGhzProtocolDecoderMagellen; -typedef struct SubGhzProtocolEncoderMagellen SubGhzProtocolEncoderMagellen; - -extern const SubGhzProtocolDecoder subghz_protocol_magellen_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_magellen_encoder; -extern const SubGhzProtocol subghz_protocol_magellen; - -/** - * Allocate SubGhzProtocolEncoderMagellen. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderMagellen* pointer to a SubGhzProtocolEncoderMagellen instance - */ -void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderMagellen. - * @param context Pointer to a SubGhzProtocolEncoderMagellen instance - */ -void subghz_protocol_encoder_magellen_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderMagellen instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderMagellen instance - */ -void subghz_protocol_encoder_magellen_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderMagellen instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_magellen_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderMagellen. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderMagellen* pointer to a SubGhzProtocolDecoderMagellen instance - */ -void* subghz_protocol_decoder_magellen_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderMagellen. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - */ -void subghz_protocol_decoder_magellen_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderMagellen. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - */ -void subghz_protocol_decoder_magellen_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_magellen_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderMagellen. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition - * @return true On success - */ -bool subghz_protocol_decoder_magellen_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); - -/** - * Deserialize data SubGhzProtocolDecoderMagellen. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_magellen_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - * @param output Resulting text - */ -void subghz_protocol_decoder_magellen_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/marantec.c b/lib/subghz/protocols/marantec.c index a72238a4..d557c29b 100644 --- a/lib/subghz/protocols/marantec.c +++ b/lib/subghz/protocols/marantec.c @@ -349,7 +349,7 @@ uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context) { bool subghz_protocol_decoder_marantec_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderMarantec* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/marantec.h b/lib/subghz/protocols/marantec.h index 5fc042e7..e330ccf1 100644 --- a/lib/subghz/protocols/marantec.h +++ b/lib/subghz/protocols/marantec.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderMarantec. * @param context Pointer to a SubGhzProtocolDecoderMarantec instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_marantec_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderMarantec. diff --git a/lib/subghz/protocols/megacode.c b/lib/subghz/protocols/megacode.c index baa8188d..1b871a0c 100644 --- a/lib/subghz/protocols/megacode.c +++ b/lib/subghz/protocols/megacode.c @@ -384,7 +384,7 @@ uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context) { bool subghz_protocol_decoder_megacode_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderMegaCode* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/megacode.h b/lib/subghz/protocols/megacode.h index 0fafddb6..e31434fa 100644 --- a/lib/subghz/protocols/megacode.h +++ b/lib/subghz/protocols/megacode.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderMegaCode. * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_megacode_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderMegaCode. diff --git a/lib/subghz/protocols/nero_radio.c b/lib/subghz/protocols/nero_radio.c index d6925d48..5fffaa19 100644 --- a/lib/subghz/protocols/nero_radio.c +++ b/lib/subghz/protocols/nero_radio.c @@ -346,7 +346,7 @@ uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context) { bool subghz_protocol_decoder_nero_radio_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNeroRadio* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/nero_radio.h b/lib/subghz/protocols/nero_radio.h index ef270342..361da617 100644 --- a/lib/subghz/protocols/nero_radio.h +++ b/lib/subghz/protocols/nero_radio.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNeroRadio. * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_nero_radio_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderNeroRadio. diff --git a/lib/subghz/protocols/nero_sketch.c b/lib/subghz/protocols/nero_sketch.c index 8080bf9e..b124b717 100644 --- a/lib/subghz/protocols/nero_sketch.c +++ b/lib/subghz/protocols/nero_sketch.c @@ -331,7 +331,7 @@ uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context) { bool subghz_protocol_decoder_nero_sketch_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNeroSketch* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/nero_sketch.h b/lib/subghz/protocols/nero_sketch.h index 4536e704..ac87fb00 100644 --- a/lib/subghz/protocols/nero_sketch.h +++ b/lib/subghz/protocols/nero_sketch.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNeroSketch. * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_nero_sketch_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderNeroSketch. diff --git a/lib/subghz/protocols/nice_flo.c b/lib/subghz/protocols/nice_flo.c index 06538db1..ecd866a7 100644 --- a/lib/subghz/protocols/nice_flo.c +++ b/lib/subghz/protocols/nice_flo.c @@ -283,7 +283,7 @@ uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context) { bool subghz_protocol_decoder_nice_flo_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlo* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/nice_flo.h b/lib/subghz/protocols/nice_flo.h index dd374006..e382e614 100644 --- a/lib/subghz/protocols/nice_flo.h +++ b/lib/subghz/protocols/nice_flo.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNiceFlo. * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_nice_flo_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderNiceFlo. diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index d567ddf2..411ceeac 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -330,7 +330,7 @@ uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context) { bool subghz_protocol_decoder_nice_flor_s_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/nice_flor_s.h b/lib/subghz/protocols/nice_flor_s.h index 286d03d4..593a7915 100644 --- a/lib/subghz/protocols/nice_flor_s.h +++ b/lib/subghz/protocols/nice_flor_s.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNiceFlorS. * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_nice_flor_s_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderNiceFlorS. diff --git a/lib/subghz/protocols/oregon2.c b/lib/subghz/protocols/oregon2.c index 561df819..244ac58d 100644 --- a/lib/subghz/protocols/oregon2.c +++ b/lib/subghz/protocols/oregon2.c @@ -193,7 +193,7 @@ uint8_t subghz_protocol_decoder_oregon2_get_hash_data(void* context) { bool subghz_protocol_decoder_oregon2_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderOregon2* instance = context; if(!subghz_block_generic_serialize(&instance->generic, flipper_format, preset)) return false; diff --git a/lib/subghz/protocols/phoenix_v2.c b/lib/subghz/protocols/phoenix_v2.c index 019c3ec2..b3d6f1e9 100644 --- a/lib/subghz/protocols/phoenix_v2.c +++ b/lib/subghz/protocols/phoenix_v2.c @@ -296,7 +296,7 @@ uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) { bool subghz_protocol_decoder_phoenix_v2_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderPhoenix_V2* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/phoenix_v2.h b/lib/subghz/protocols/phoenix_v2.h index b2f18279..48487535 100644 --- a/lib/subghz/protocols/phoenix_v2.h +++ b/lib/subghz/protocols/phoenix_v2.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderPhoenix_V2. * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_phoenix_v2_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderPhoenix_V2. diff --git a/lib/subghz/protocols/power_smart.c b/lib/subghz/protocols/power_smart.c index 3ec35c74..63ca7871 100644 --- a/lib/subghz/protocols/power_smart.c +++ b/lib/subghz/protocols/power_smart.c @@ -349,7 +349,7 @@ uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context) { bool subghz_protocol_decoder_power_smart_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderPowerSmart* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/power_smart.h b/lib/subghz/protocols/power_smart.h index 21e5dcf4..806729f8 100644 --- a/lib/subghz/protocols/power_smart.h +++ b/lib/subghz/protocols/power_smart.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderPowerSmart. * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_power_smart_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderPowerSmart. diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index 370f33fb..a4a0921a 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -312,7 +312,7 @@ uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) { bool subghz_protocol_decoder_princeton_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderPrinceton* instance = context; bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/princeton.h b/lib/subghz/protocols/princeton.h index 4e482f74..f63004db 100644 --- a/lib/subghz/protocols/princeton.h +++ b/lib/subghz/protocols/princeton.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderPrinceton. * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_princeton_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderPrinceton. diff --git a/lib/subghz/protocols/registry.c b/lib/subghz/protocols/protocol_items.c similarity index 58% rename from lib/subghz/protocols/registry.c rename to lib/subghz/protocols/protocol_items.c index 453fa747..65ba0328 100644 --- a/lib/subghz/protocols/registry.c +++ b/lib/subghz/protocols/protocol_items.c @@ -1,6 +1,6 @@ -#include "registry.h" +#include "protocol_items.h" -const SubGhzProtocol* subghz_protocol_registry[] = { +const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_gate_tx, &subghz_protocol_keeloq, &subghz_protocol_star_line, &subghz_protocol_nice_flo, &subghz_protocol_came, &subghz_protocol_faac_slh, &subghz_protocol_nice_flor_s, &subghz_protocol_came_twee, &subghz_protocol_came_atomo, @@ -11,26 +11,9 @@ const SubGhzProtocol* subghz_protocol_registry[] = { &subghz_protocol_secplus_v1, &subghz_protocol_megacode, &subghz_protocol_holtek, &subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec, &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, - &subghz_protocol_honeywell_wdb, &subghz_protocol_magellen, &subghz_protocol_intertechno_v3, + &subghz_protocol_honeywell_wdb, &subghz_protocol_magellan, &subghz_protocol_intertechno_v3, &subghz_protocol_clemsa, &subghz_protocol_oregon2}; -const SubGhzProtocol* subghz_protocol_registry_get_by_name(const char* name) { - for(size_t i = 0; i < subghz_protocol_registry_count(); i++) { - if(strcmp(name, subghz_protocol_registry[i]->name) == 0) { - return subghz_protocol_registry[i]; - } - } - return NULL; -} - -const SubGhzProtocol* subghz_protocol_registry_get_by_index(size_t index) { - if(index < subghz_protocol_registry_count()) { - return subghz_protocol_registry[index]; - } else { - return NULL; - } -} - -size_t subghz_protocol_registry_count() { - return COUNT_OF(subghz_protocol_registry); -} +const SubGhzProtocolRegistry subghz_protocol_registry = { + .items = subghz_protocol_registry_items, + .size = COUNT_OF(subghz_protocol_registry_items)}; \ No newline at end of file diff --git a/lib/subghz/protocols/registry.h b/lib/subghz/protocols/protocol_items.h similarity index 54% rename from lib/subghz/protocols/registry.h rename to lib/subghz/protocols/protocol_items.h index 8eb464ad..9c187e79 100644 --- a/lib/subghz/protocols/registry.h +++ b/lib/subghz/protocols/protocol_items.h @@ -1,6 +1,5 @@ #pragma once - -#include "../types.h" +#include "../registry.h" #include "princeton.h" #include "keeloq.h" @@ -33,27 +32,9 @@ #include "doitrand.h" #include "phoenix_v2.h" #include "honeywell_wdb.h" -#include "magellen.h" +#include "magellan.h" #include "intertechno_v3.h" #include "clemsa.h" #include "oregon2.h" -/** - * Registration by name SubGhzProtocol. - * @param name Protocol name - * @return SubGhzProtocol* pointer to a SubGhzProtocol instance - */ -const SubGhzProtocol* subghz_protocol_registry_get_by_name(const char* name); - -/** - * Registration protocol by index in array SubGhzProtocol. - * @param index Protocol by index in array - * @return SubGhzProtocol* pointer to a SubGhzProtocol instance - */ -const SubGhzProtocol* subghz_protocol_registry_get_by_index(size_t index); - -/** - * Getting the number of registered protocols. - * @return Number of protocols - */ -size_t subghz_protocol_registry_count(); +extern const SubGhzProtocolRegistry subghz_protocol_registry; diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index fd234502..b8e93c3d 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -84,7 +84,7 @@ const SubGhzProtocol subghz_protocol_raw = { bool subghz_protocol_raw_save_to_file_init( SubGhzProtocolDecoderRAW* instance, const char* dev_name, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(instance); instance->storage = furi_record_open(RECORD_STORAGE); diff --git a/lib/subghz/protocols/raw.h b/lib/subghz/protocols/raw.h index be03dea2..96c77553 100644 --- a/lib/subghz/protocols/raw.h +++ b/lib/subghz/protocols/raw.h @@ -21,13 +21,13 @@ extern const SubGhzProtocol subghz_protocol_raw; * Open file for writing * @param instance Pointer to a SubGhzProtocolDecoderRAW instance * @param dev_name File name - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_raw_save_to_file_init( SubGhzProtocolDecoderRAW* instance, const char* dev_name, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Stop writing file to flash diff --git a/lib/subghz/protocols/scher_khan.c b/lib/subghz/protocols/scher_khan.c index 837dcf05..a9a3078e 100644 --- a/lib/subghz/protocols/scher_khan.c +++ b/lib/subghz/protocols/scher_khan.c @@ -251,7 +251,7 @@ uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context) { bool subghz_protocol_decoder_scher_khan_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderScherKhan* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/scher_khan.h b/lib/subghz/protocols/scher_khan.h index f0fe595b..b7e84ea1 100644 --- a/lib/subghz/protocols/scher_khan.h +++ b/lib/subghz/protocols/scher_khan.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderScherKhan. * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_scher_khan_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderScherKhan. diff --git a/lib/subghz/protocols/secplus_v1.c b/lib/subghz/protocols/secplus_v1.c index cfe6bdee..7bd0b5b7 100644 --- a/lib/subghz/protocols/secplus_v1.c +++ b/lib/subghz/protocols/secplus_v1.c @@ -519,7 +519,7 @@ uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context) { bool subghz_protocol_decoder_secplus_v1_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v1* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/secplus_v1.h b/lib/subghz/protocols/secplus_v1.h index ba1762f5..99480b62 100644 --- a/lib/subghz/protocols/secplus_v1.h +++ b/lib/subghz/protocols/secplus_v1.h @@ -82,13 +82,13 @@ uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSecPlus_v1. * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_secplus_v1_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderSecPlus_v1. diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index 73bb7802..90cc805a 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -592,7 +592,7 @@ bool subghz_protocol_secplus_v2_create_data( uint32_t serial, uint8_t btn, uint32_t cnt, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolEncoderSecPlus_v2* instance = context; instance->generic.serial = serial; @@ -759,7 +759,7 @@ uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) { bool subghz_protocol_decoder_secplus_v2_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v2* instance = context; bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/secplus_v2.h b/lib/subghz/protocols/secplus_v2.h index 007609fc..bea8cdb5 100644 --- a/lib/subghz/protocols/secplus_v2.h +++ b/lib/subghz/protocols/secplus_v2.h @@ -52,7 +52,7 @@ LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context); * @param btn Button number, 8 bit * @param cnt Container value, 28 bit * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzPresetDefinition + * @param preset Modulation, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_secplus_v2_create_data( @@ -61,7 +61,7 @@ bool subghz_protocol_secplus_v2_create_data( uint32_t serial, uint8_t btn, uint32_t cnt, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Allocate SubGhzProtocolDecoderSecPlus_v2. @@ -101,13 +101,13 @@ uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSecPlus_v2. * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_secplus_v2_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderSecPlus_v2. diff --git a/lib/subghz/protocols/somfy_keytis.c b/lib/subghz/protocols/somfy_keytis.c index cf627ef3..00bb8a8c 100644 --- a/lib/subghz/protocols/somfy_keytis.c +++ b/lib/subghz/protocols/somfy_keytis.c @@ -382,7 +382,7 @@ uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context) { bool subghz_protocol_decoder_somfy_keytis_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSomfyKeytis* instance = context; bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/somfy_keytis.h b/lib/subghz/protocols/somfy_keytis.h index af1df91d..3b595061 100644 --- a/lib/subghz/protocols/somfy_keytis.h +++ b/lib/subghz/protocols/somfy_keytis.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSomfyKeytis. * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_somfy_keytis_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderSomfyKeytis. diff --git a/lib/subghz/protocols/somfy_telis.c b/lib/subghz/protocols/somfy_telis.c index 6e375bb3..362b1b07 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -339,7 +339,7 @@ uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context) { bool subghz_protocol_decoder_somfy_telis_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSomfyTelis* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/somfy_telis.h b/lib/subghz/protocols/somfy_telis.h index b6e58e34..a6a9fa5b 100644 --- a/lib/subghz/protocols/somfy_telis.h +++ b/lib/subghz/protocols/somfy_telis.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSomfyTelis. * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_somfy_telis_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderSomfyTelis. diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index 4b89bd2b..8e5e07c1 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -319,7 +319,7 @@ uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context) { bool subghz_protocol_decoder_star_line_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderStarLine* instance = context; subghz_protocol_star_line_check_remote_controller( diff --git a/lib/subghz/protocols/star_line.h b/lib/subghz/protocols/star_line.h index 74c99410..34c25150 100644 --- a/lib/subghz/protocols/star_line.h +++ b/lib/subghz/protocols/star_line.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderStarLine. * @param context Pointer to a SubGhzProtocolDecoderStarLine instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_star_line_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderStarLine. diff --git a/lib/subghz/receiver.c b/lib/subghz/receiver.c index cf4085df..fd6f1493 100644 --- a/lib/subghz/receiver.c +++ b/lib/subghz/receiver.c @@ -1,6 +1,7 @@ #include "receiver.h" -#include "protocols/registry.h" +#include "registry.h" +#include "protocols/protocol_items.h" #include @@ -22,9 +23,12 @@ struct SubGhzReceiver { SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment) { SubGhzReceiver* instance = malloc(sizeof(SubGhzReceiver)); SubGhzReceiverSlotArray_init(instance->slots); + const SubGhzProtocolRegistry* protocol_registry_items = + subghz_environment_get_protocol_registry(environment); - for(size_t i = 0; i < subghz_protocol_registry_count(); ++i) { - const SubGhzProtocol* protocol = subghz_protocol_registry_get_by_index(i); + for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry_items); ++i) { + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_index(protocol_registry_items, i); if(protocol->decoder && protocol->decoder->alloc) { SubGhzReceiverSlot* slot = SubGhzReceiverSlotArray_push_new(instance->slots); @@ -34,7 +38,6 @@ SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment) { instance->callback = NULL; instance->context = NULL; - return instance; } diff --git a/lib/subghz/registry.c b/lib/subghz/registry.c new file mode 100644 index 00000000..d0c22ea8 --- /dev/null +++ b/lib/subghz/registry.c @@ -0,0 +1,30 @@ +#include "registry.h" + +const SubGhzProtocol* subghz_protocol_registry_get_by_name( + const SubGhzProtocolRegistry* protocol_registry, + const char* name) { + furi_assert(protocol_registry); + + for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry); i++) { + if(strcmp(name, protocol_registry->items[i]->name) == 0) { + return protocol_registry->items[i]; + } + } + return NULL; +} + +const SubGhzProtocol* subghz_protocol_registry_get_by_index( + const SubGhzProtocolRegistry* protocol_registry, + size_t index) { + furi_assert(protocol_registry); + if(index < subghz_protocol_registry_count(protocol_registry)) { + return protocol_registry->items[index]; + } else { + return NULL; + } +} + +size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry) { + furi_assert(protocol_registry); + return protocol_registry->size; +} diff --git a/lib/subghz/registry.h b/lib/subghz/registry.h new file mode 100644 index 00000000..062cdf68 --- /dev/null +++ b/lib/subghz/registry.h @@ -0,0 +1,39 @@ +#pragma once + +#include "types.h" + +typedef struct SubGhzEnvironment SubGhzEnvironment; + +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; + +struct SubGhzProtocolRegistry { + const SubGhzProtocol** items; + const size_t size; +}; + +/** + * Registration by name SubGhzProtocol. + * @param protocol_registry SubGhzProtocolRegistry + * @param name Protocol name + * @return SubGhzProtocol* pointer to a SubGhzProtocol instance + */ +const SubGhzProtocol* subghz_protocol_registry_get_by_name( + const SubGhzProtocolRegistry* protocol_registry, + const char* name); + +/** + * Registration protocol by index in array SubGhzProtocol. + * @param protocol_registry SubGhzProtocolRegistry + * @param index Protocol by index in array + * @return SubGhzProtocol* pointer to a SubGhzProtocol instance + */ +const SubGhzProtocol* subghz_protocol_registry_get_by_index( + const SubGhzProtocolRegistry* protocol_registry, + size_t index); + +/** + * Getting the number of registered protocols. + * @param protocol_registry SubGhzProtocolRegistry + * @return Number of protocols + */ +size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry); diff --git a/applications/main/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c similarity index 99% rename from applications/main/subghz/subghz_setting.c rename to lib/subghz/subghz_setting.c index e0322f6a..c5ec5db7 100644 --- a/applications/main/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -1,5 +1,6 @@ #include "subghz_setting.h" -#include "subghz_i.h" +#include "types.h" +//#include "subghz_i.h" #include #include @@ -324,7 +325,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { if(file_path) { do { if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_E(TAG, "Error open file %s", file_path); + FURI_LOG_I(TAG, "File is not used %s", file_path); break; } diff --git a/applications/main/subghz/subghz_setting.h b/lib/subghz/subghz_setting.h similarity index 95% rename from applications/main/subghz/subghz_setting.h rename to lib/subghz/subghz_setting.h index 0590cf49..cd82b302 100644 --- a/applications/main/subghz/subghz_setting.h +++ b/lib/subghz/subghz_setting.h @@ -6,6 +6,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4 typedef struct SubGhzSetting SubGhzSetting; @@ -46,3 +50,7 @@ uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance); uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/transmitter.c b/lib/subghz/transmitter.c index 0dc2bacd..8507ee05 100644 --- a/lib/subghz/transmitter.c +++ b/lib/subghz/transmitter.c @@ -1,7 +1,8 @@ #include "transmitter.h" #include "protocols/base.h" -#include "protocols/registry.h" +#include "registry.h" +#include "protocols/protocol_items.h" struct SubGhzTransmitter { const SubGhzProtocol* protocol; @@ -11,14 +12,17 @@ struct SubGhzTransmitter { SubGhzTransmitter* subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name) { SubGhzTransmitter* instance = NULL; - const SubGhzProtocol* protocol = subghz_protocol_registry_get_by_name(protocol_name); + const SubGhzProtocolRegistry* protocol_registry_items = + subghz_environment_get_protocol_registry(environment); + + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_name(protocol_registry_items, protocol_name); if(protocol && protocol->encoder && protocol->encoder->alloc) { instance = malloc(sizeof(SubGhzTransmitter)); instance->protocol = protocol; instance->protocol_instance = instance->protocol->encoder->alloc(environment); } - return instance; } diff --git a/lib/subghz/types.h b/lib/subghz/types.h index dd9cefb8..6c34dc72 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -10,7 +10,6 @@ #include "environment.h" #include #include -#include #define SUBGHZ_APP_FOLDER ANY_PATH("subghz") #define SUBGHZ_RAW_FOLDER EXT_PATH("subghz") @@ -22,19 +21,21 @@ #define SUBGHZ_RAW_FILE_VERSION 1 #define SUBGHZ_RAW_FILE_TYPE "Flipper SubGhz RAW File" -// -// Abstract method types -// +// Radio Preset +typedef struct { + FuriString* name; + uint32_t frequency; + uint8_t* data; + size_t data_size; +} SubGhzRadioPreset; // Allocator and Deallocator typedef void* (*SubGhzAlloc)(SubGhzEnvironment* environment); typedef void (*SubGhzFree)(void* context); // Serialize and Deserialize -typedef bool (*SubGhzSerialize)( - void* context, - FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); +typedef bool ( + *SubGhzSerialize)(void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); typedef bool (*SubGhzDeserialize)(void* context, FlipperFormat* flipper_format); // Decoder specific @@ -74,6 +75,8 @@ typedef enum { SubGhzProtocolTypeStatic, SubGhzProtocolTypeDynamic, SubGhzProtocolTypeRAW, + SubGhzProtocolWeatherStation, + SubGhzProtocolCustom, } SubGhzProtocolType; typedef enum { From 42df7aa04a65314e1ae943ecd5c5beea32cda031 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Thu, 20 Oct 2022 20:58:11 +1000 Subject: [PATCH 13/42] Fix FuriString oplist (init move) (#1894) * FuriString, Infrared: fix oplist and drop string_t. * Elf loader: log size * Comment fix * API: furi_hal_console_init --- applications/main/infrared/infrared_cli.c | 15 +++++------ firmware/targets/f7/api_symbols.csv | 4 +-- furi/core/string.h | 31 +++++++++++++---------- lib/flipper_application/elf/elf_file.c | 10 ++++++++ 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 7723dc97..5a04f749 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -9,12 +9,11 @@ #include "infrared_signal.h" #include "infrared_brute_force.h" -#include "m-dict.h" -#include "m-string.h" +#include #define INFRARED_CLI_BUF_SIZE 10 -DICT_DEF2(dict_signals, string_t, STRING_OPLIST, int, M_DEFAULT_OPLIST) +DICT_DEF2(dict_signals, FuriString*, FURI_STRING_OPLIST, int, M_DEFAULT_OPLIST) enum RemoteTypes { TV = 0, AC = 1 }; @@ -416,7 +415,7 @@ static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); dict_signals_t signals_dict; - string_t key; + FuriString* key; const char* remote_file = NULL; bool success = false; int max = 1; @@ -433,7 +432,7 @@ static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) { } dict_signals_init(signals_dict); - string_init(key); + key = furi_string_alloc(); success = flipper_format_buffered_file_open_existing(ff, remote_file); if(success) { @@ -441,7 +440,7 @@ static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) { signal_name = furi_string_alloc(); printf("Valid signals:\r\n"); while(flipper_format_read_string(ff, "name", signal_name)) { - string_set_str(key, furi_string_get_cstr(signal_name)); + furi_string_set_str(key, furi_string_get_cstr(signal_name)); int* v = dict_signals_get(signals_dict, key); if(v != NULL) { (*v)++; @@ -453,12 +452,12 @@ static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) { dict_signals_it_t it; for(dict_signals_it(it, signals_dict); !dict_signals_end_p(it); dict_signals_next(it)) { const struct dict_signals_pair_s* pair = dict_signals_cref(it); - printf("\t%s\r\n", string_get_cstr(pair->key)); + printf("\t%s\r\n", furi_string_get_cstr(pair->key)); } furi_string_free(signal_name); } - string_clear(key); + furi_string_free(key); dict_signals_clear(signals_dict); flipper_format_free(ff); furi_record_close(RECORD_STORAGE); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index b65de9a9..6aa8591e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,3.3,, +Version,+,3.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -976,7 +976,7 @@ Function,-,furi_hal_compress_icon_decode,void,"const uint8_t*, uint8_t**" Function,-,furi_hal_compress_icon_init,void, Function,+,furi_hal_console_disable,void, Function,+,furi_hal_console_enable,void, -Function,-,furi_hal_console_init,void, +Function,+,furi_hal_console_init,void, Function,+,furi_hal_console_printf,void,"const char[], ..." Function,+,furi_hal_console_puts,void,const char* Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" diff --git a/furi/core/string.h b/furi/core/string.h index 01b26cce..0523d3ba 100644 --- a/furi/core/string.h +++ b/furi/core/string.h @@ -718,22 +718,27 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico */ #define F_STR_INIT_SET(a, b) ((a) = furi_string_alloc_set(b)) +/** + * @brief INIT MOVE OPLIST for FuriString. + */ +#define F_STR_INIT_MOVE(a, b) ((a) = furi_string_alloc_move(b)) + /** * @brief OPLIST for FuriString. */ -#define FURI_STRING_OPLIST \ - (INIT(F_STR_INIT), \ - INIT_SET(F_STR_INIT_SET), \ - SET(furi_string_set), \ - INIT_MOVE(furi_string_alloc_move), \ - MOVE(furi_string_move), \ - SWAP(furi_string_swap), \ - RESET(furi_string_reset), \ - EMPTY_P(furi_string_empty), \ - CLEAR(furi_string_free), \ - HASH(furi_string_hash), \ - EQUAL(furi_string_equal), \ - CMP(furi_string_cmp), \ +#define FURI_STRING_OPLIST \ + (INIT(F_STR_INIT), \ + INIT_SET(F_STR_INIT_SET), \ + SET(furi_string_set), \ + INIT_MOVE(F_STR_INIT_MOVE), \ + MOVE(furi_string_move), \ + SWAP(furi_string_swap), \ + RESET(furi_string_reset), \ + EMPTY_P(furi_string_empty), \ + CLEAR(furi_string_free), \ + HASH(furi_string_hash), \ + EQUAL(furi_string_equal), \ + CMP(furi_string_cmp), \ TYPE(FuriString*)) #ifdef __cplusplus diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 3fe57162..2082e550 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -786,6 +786,16 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { FURI_LOG_D(TAG, "Trampoline cache size: %u", AddressCache_size(elf->trampoline_cache)); AddressCache_clear(elf->relocation_cache); + { + size_t total_size = 0; + for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); + ELFSectionDict_next(it)) { + ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); + total_size += itref->value.size; + } + FURI_LOG_I(TAG, "Total size of loaded sections: %u", total_size); + } + return status; } From f56c94922d839f161be9f699b024f2a25d0c41db Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Thu, 20 Oct 2022 21:42:55 +1000 Subject: [PATCH 14/42] CMSIS DAP/DAP Link Debugger (#1897) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Apps: DAP Link * API: furi_hal_console_init Co-authored-by: あく --- .gitmodules | 3 + applications/plugins/dap_link/README.md | 105 ++ applications/plugins/dap_link/application.fam | 24 + applications/plugins/dap_link/dap_config.h | 234 +++++ applications/plugins/dap_link/dap_link.c | 521 +++++++++ applications/plugins/dap_link/dap_link.h | 55 + applications/plugins/dap_link/dap_link.png | Bin 0 -> 143 bytes applications/plugins/dap_link/gui/dap_gui.c | 92 ++ applications/plugins/dap_link/gui/dap_gui.h | 4 + .../dap_link/gui/dap_gui_custom_event.h | 7 + applications/plugins/dap_link/gui/dap_gui_i.h | 34 + .../dap_link/gui/scenes/config/dap_scene.c | 30 + .../dap_link/gui/scenes/config/dap_scene.h | 29 + .../gui/scenes/config/dap_scene_config.h | 4 + .../dap_link/gui/scenes/dap_scene_about.c | 68 ++ .../dap_link/gui/scenes/dap_scene_config.c | 107 ++ .../dap_link/gui/scenes/dap_scene_help.c | 102 ++ .../dap_link/gui/scenes/dap_scene_main.c | 154 +++ .../dap_link/gui/views/dap_main_view.c | 189 ++++ .../dap_link/gui/views/dap_main_view.h | 45 + .../dap_link/icons/ArrowDownEmpty_12x18.png | Bin 0 -> 160 bytes .../dap_link/icons/ArrowDownFilled_12x18.png | Bin 0 -> 168 bytes .../dap_link/icons/ArrowUpEmpty_12x18.png | Bin 0 -> 159 bytes .../dap_link/icons/ArrowUpFilled_12x18.png | Bin 0 -> 173 bytes applications/plugins/dap_link/lib/free-dap | 1 + .../plugins/dap_link/usb/dap_v2_usb.c | 994 ++++++++++++++++++ .../plugins/dap_link/usb/dap_v2_usb.h | 55 + .../plugins/dap_link/usb/usb_winusb.h | 143 +++ 28 files changed, 3000 insertions(+) create mode 100644 applications/plugins/dap_link/README.md create mode 100644 applications/plugins/dap_link/application.fam create mode 100644 applications/plugins/dap_link/dap_config.h create mode 100644 applications/plugins/dap_link/dap_link.c create mode 100644 applications/plugins/dap_link/dap_link.h create mode 100644 applications/plugins/dap_link/dap_link.png create mode 100644 applications/plugins/dap_link/gui/dap_gui.c create mode 100644 applications/plugins/dap_link/gui/dap_gui.h create mode 100644 applications/plugins/dap_link/gui/dap_gui_custom_event.h create mode 100644 applications/plugins/dap_link/gui/dap_gui_i.h create mode 100644 applications/plugins/dap_link/gui/scenes/config/dap_scene.c create mode 100644 applications/plugins/dap_link/gui/scenes/config/dap_scene.h create mode 100644 applications/plugins/dap_link/gui/scenes/config/dap_scene_config.h create mode 100644 applications/plugins/dap_link/gui/scenes/dap_scene_about.c create mode 100644 applications/plugins/dap_link/gui/scenes/dap_scene_config.c create mode 100644 applications/plugins/dap_link/gui/scenes/dap_scene_help.c create mode 100644 applications/plugins/dap_link/gui/scenes/dap_scene_main.c create mode 100644 applications/plugins/dap_link/gui/views/dap_main_view.c create mode 100644 applications/plugins/dap_link/gui/views/dap_main_view.h create mode 100644 applications/plugins/dap_link/icons/ArrowDownEmpty_12x18.png create mode 100644 applications/plugins/dap_link/icons/ArrowDownFilled_12x18.png create mode 100644 applications/plugins/dap_link/icons/ArrowUpEmpty_12x18.png create mode 100644 applications/plugins/dap_link/icons/ArrowUpFilled_12x18.png create mode 160000 applications/plugins/dap_link/lib/free-dap create mode 100644 applications/plugins/dap_link/usb/dap_v2_usb.c create mode 100644 applications/plugins/dap_link/usb/dap_v2_usb.h create mode 100644 applications/plugins/dap_link/usb/usb_winusb.h diff --git a/.gitmodules b/.gitmodules index ba764498..308d60fd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "lib/cxxheaderparser"] path = lib/cxxheaderparser url = https://github.com/robotpy/cxxheaderparser.git +[submodule "applications/plugins/dap_link/lib/free-dap"] + path = applications/plugins/dap_link/lib/free-dap + url = https://github.com/ataradov/free-dap.git diff --git a/applications/plugins/dap_link/README.md b/applications/plugins/dap_link/README.md new file mode 100644 index 00000000..aead0a60 --- /dev/null +++ b/applications/plugins/dap_link/README.md @@ -0,0 +1,105 @@ +# Flipper Zero as CMSIS DAP/DAP Link +Flipper Zero as a [Free-DAP](https://github.com/ataradov/free-dap) based SWD\JTAG debugger. Free-DAP is a free and open source firmware implementation of the [CMSIS-DAP](https://www.keil.com/pack/doc/CMSIS_Dev/DAP/html/index.html) debugger. + +## Protocols +SWD, JTAG , CMSIS-DAP v1 (18 KiB/s), CMSIS-DAP v2 (46 KiB/s), VCP (USB-UART). + +WinUSB for driverless installation for Windows 8 and above. + +## Usage + +### VSCode + Cortex-Debug + Set `"device": "cmsis-dap"` + +
+ BluePill configuration example + + ```json +{ + "name": "Attach (DAP)", + "cwd": "${workspaceFolder}", + "executable": "./build/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "cmsis-dap", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/stm32f1x.cfg", + ], +}, + ``` +
+ +
+ Flipper Zero configuration example + + ```json +{ + "name": "Attach (DAP)", + "cwd": "${workspaceFolder}", + "executable": "./build/latest/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "cmsis-dap", + "svdFile": "./debug/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "configFiles": [ + "interface/cmsis-dap.cfg", + "./debug/stm32wbx.cfg", + ], + "postAttachCommands": [ + "source debug/flipperapps.py", + ], +}, + ``` +
+ +### OpenOCD +Use `interface/cmsis-dap.cfg`. You will need OpenOCD v0.11.0. + +Additional commands: +* `cmsis_dap_backend hid` for CMSIS-DAP v1 protocol. +* `cmsis_dap_backend usb_bulk` for CMSIS-DAP v2 protocol. +* `cmsis_dap_serial DAP_Oyevoxo` use DAP-Link running on Flipper named `Oyevoxo`. +* `cmsis-dap cmd 81` - reboot connected DAP-Link. + +
+ Flash BluePill + + ``` +openocd -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg -c init -c "program build/firmware.bin reset exit 0x8000000" + ``` +
+ +
+ Flash Flipper Zero using DAP v2 protocol + + ``` +openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_backend usb_bulk" -f debug/stm32wbx.cfg -c init -c "program build/latest/firmware.bin reset exit 0x8000000" + ``` +
+ +
+ Reboot connected DAP-Link on Flipper named Oyevoxo + + ``` +openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_serial DAP_Oyevoxo" -c "transport select swd" -c "adapter speed 4000000" -c init -c "cmsis-dap cmd 81" -c "exit" + ``` +
+ +### PlatformIO +Use `debug_tool = cmsis-dap` and `upload_protocol = cmsis-dap`. [Documentation](https://docs.platformio.org/en/latest/plus/debug-tools/cmsis-dap.html#debugging-tool-cmsis-dap). Remember that Windows 8 and above do not require drivers. + +
+ BluePill platformio.ini example + + ``` +[env:bluepill_f103c8] +platform = ststm32 +board = bluepill_f103c8 +debug_tool = cmsis-dap +upload_protocol = cmsis-dap + ``` +
diff --git a/applications/plugins/dap_link/application.fam b/applications/plugins/dap_link/application.fam new file mode 100644 index 00000000..3b99d5ef --- /dev/null +++ b/applications/plugins/dap_link/application.fam @@ -0,0 +1,24 @@ +App( + appid="dap_link", + name="DAP Link", + apptype=FlipperAppType.PLUGIN, + entry_point="dap_link_app", + requires=[ + "gui", + "dialogs", + ], + stack_size=4 * 1024, + order=20, + fap_icon="dap_link.png", + fap_category="Tools", + fap_private_libs=[ + Lib( + name="free-dap", + cincludes=["."], + sources=[ + "dap.c", + ], + ), + ], + fap_icon_assets="icons", +) diff --git a/applications/plugins/dap_link/dap_config.h b/applications/plugins/dap_link/dap_config.h new file mode 100644 index 00000000..88b90bd3 --- /dev/null +++ b/applications/plugins/dap_link/dap_config.h @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2022, Alex Taradov . All rights reserved. + +#ifndef _DAP_CONFIG_H_ +#define _DAP_CONFIG_H_ + +/*- Includes ----------------------------------------------------------------*/ +#include + +/*- Definitions -------------------------------------------------------------*/ +#define DAP_CONFIG_ENABLE_JTAG + +#define DAP_CONFIG_DEFAULT_PORT DAP_PORT_SWD +#define DAP_CONFIG_DEFAULT_CLOCK 4200000 // Hz + +#define DAP_CONFIG_PACKET_SIZE 64 +#define DAP_CONFIG_PACKET_COUNT 1 + +#define DAP_CONFIG_JTAG_DEV_COUNT 8 + +// DAP_CONFIG_PRODUCT_STR must contain "CMSIS-DAP" to be compatible with the standard +#define DAP_CONFIG_VENDOR_STR "Flipper Zero" +#define DAP_CONFIG_PRODUCT_STR "Generic CMSIS-DAP Adapter" +#define DAP_CONFIG_SER_NUM_STR usb_serial_number +#define DAP_CONFIG_CMSIS_DAP_VER_STR "2.0.0" + +#define DAP_CONFIG_RESET_TARGET_FN dap_app_target_reset +#define DAP_CONFIG_VENDOR_FN dap_app_vendor_cmd + +// Attribute to use for performance-critical functions +#define DAP_CONFIG_PERFORMANCE_ATTR + +// A value at which dap_clock_test() produces 1 kHz output on the SWCLK pin +// #define DAP_CONFIG_DELAY_CONSTANT 19000 +#define DAP_CONFIG_DELAY_CONSTANT 6290 + +// A threshold for switching to fast clock (no added delays) +// This is the frequency produced by dap_clock_test(1) on the SWCLK pin +#define DAP_CONFIG_FAST_CLOCK 2400000 // Hz + +/*- Prototypes --------------------------------------------------------------*/ +extern char usb_serial_number[16]; + +/*- Implementations ---------------------------------------------------------*/ +extern GpioPin flipper_dap_swclk_pin; +extern GpioPin flipper_dap_swdio_pin; +extern GpioPin flipper_dap_reset_pin; +extern GpioPin flipper_dap_tdo_pin; +extern GpioPin flipper_dap_tdi_pin; + +extern void dap_app_vendor_cmd(uint8_t cmd); +extern void dap_app_target_reset(); +extern void dap_app_disconnect(); +extern void dap_app_connect_swd(); +extern void dap_app_connect_jtag(); + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWCLK_TCK_write(int value) { + furi_hal_gpio_write(&flipper_dap_swclk_pin, value); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWDIO_TMS_write(int value) { + furi_hal_gpio_write(&flipper_dap_swdio_pin, value); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_TDI_write(int value) { +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_write(&flipper_dap_tdi_pin, value); +#else + (void)value; +#endif +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_TDO_write(int value) { +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_write(&flipper_dap_tdo_pin, value); +#else + (void)value; +#endif +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_nTRST_write(int value) { + (void)value; +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_nRESET_write(int value) { + furi_hal_gpio_write(&flipper_dap_reset_pin, value); +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_SWCLK_TCK_read(void) { + return furi_hal_gpio_read(&flipper_dap_swclk_pin); +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_SWDIO_TMS_read(void) { + return furi_hal_gpio_read(&flipper_dap_swdio_pin); +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_TDO_read(void) { +#ifdef DAP_CONFIG_ENABLE_JTAG + return furi_hal_gpio_read(&flipper_dap_tdo_pin); +#else + return 0; +#endif +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_TDI_read(void) { +#ifdef DAP_CONFIG_ENABLE_JTAG + return furi_hal_gpio_read(&flipper_dap_tdi_pin); +#else + return 0; +#endif +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_nTRST_read(void) { + return 0; +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_nRESET_read(void) { + return furi_hal_gpio_read(&flipper_dap_reset_pin); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWCLK_TCK_set(void) { + LL_GPIO_SetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWCLK_TCK_clr(void) { + LL_GPIO_ResetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWDIO_TMS_in(void) { + LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_INPUT); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWDIO_TMS_out(void) { + LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_OUTPUT); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SETUP(void) { + furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#endif +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_DISCONNECT(void) { + furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#endif + dap_app_disconnect(); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_CONNECT_SWD(void) { + furi_hal_gpio_init( + &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_swdio_pin, true); + + furi_hal_gpio_init( + &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_swclk_pin, true); + + furi_hal_gpio_init( + &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_reset_pin, true); + +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#endif + dap_app_connect_swd(); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_CONNECT_JTAG(void) { + furi_hal_gpio_init( + &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_swdio_pin, true); + + furi_hal_gpio_init( + &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_swclk_pin, true); + + furi_hal_gpio_init( + &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_reset_pin, true); + +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + + furi_hal_gpio_init( + &flipper_dap_tdi_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_tdi_pin, true); +#endif + dap_app_connect_jtag(); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_LED(int index, int state) { + (void)index; + (void)state; +} + +//----------------------------------------------------------------------------- +__attribute__((always_inline)) static inline void DAP_CONFIG_DELAY(uint32_t cycles) { + asm volatile("1: subs %[cycles], %[cycles], #1 \n" + " bne 1b \n" + : [cycles] "+l"(cycles)); +} + +#endif // _DAP_CONFIG_H_ diff --git a/applications/plugins/dap_link/dap_link.c b/applications/plugins/dap_link/dap_link.c new file mode 100644 index 00000000..58d032b9 --- /dev/null +++ b/applications/plugins/dap_link/dap_link.c @@ -0,0 +1,521 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dap_link.h" +#include "dap_config.h" +#include "gui/dap_gui.h" +#include "usb/dap_v2_usb.h" + +/***************************************************************************/ +/****************************** DAP COMMON *********************************/ +/***************************************************************************/ + +struct DapApp { + FuriThread* dap_thread; + FuriThread* cdc_thread; + FuriThread* gui_thread; + + DapState state; + DapConfig config; +}; + +void dap_app_get_state(DapApp* app, DapState* state) { + *state = app->state; +} + +#define DAP_PROCESS_THREAD_TICK 500 + +typedef enum { + DapThreadEventStop = (1 << 0), +} DapThreadEvent; + +void dap_thread_send_stop(FuriThread* thread) { + furi_thread_flags_set(furi_thread_get_id(thread), DapThreadEventStop); +} + +GpioPin flipper_dap_swclk_pin; +GpioPin flipper_dap_swdio_pin; +GpioPin flipper_dap_reset_pin; +GpioPin flipper_dap_tdo_pin; +GpioPin flipper_dap_tdi_pin; + +/***************************************************************************/ +/****************************** DAP PROCESS ********************************/ +/***************************************************************************/ + +typedef struct { + uint8_t data[DAP_CONFIG_PACKET_SIZE]; + uint8_t size; +} DapPacket; + +typedef enum { + DAPThreadEventStop = DapThreadEventStop, + DAPThreadEventRxV1 = (1 << 1), + DAPThreadEventRxV2 = (1 << 2), + DAPThreadEventUSBConnect = (1 << 3), + DAPThreadEventUSBDisconnect = (1 << 4), + DAPThreadEventApplyConfig = (1 << 5), + DAPThreadEventAll = DAPThreadEventStop | DAPThreadEventRxV1 | DAPThreadEventRxV2 | + DAPThreadEventUSBConnect | DAPThreadEventUSBDisconnect | + DAPThreadEventApplyConfig, +} DAPThreadEvent; + +#define USB_SERIAL_NUMBER_LEN 16 +char usb_serial_number[USB_SERIAL_NUMBER_LEN] = {0}; + +const char* dap_app_get_serial(DapApp* app) { + UNUSED(app); + return usb_serial_number; +} + +static void dap_app_rx1_callback(void* context) { + furi_assert(context); + FuriThreadId thread_id = (FuriThreadId)context; + furi_thread_flags_set(thread_id, DAPThreadEventRxV1); +} + +static void dap_app_rx2_callback(void* context) { + furi_assert(context); + FuriThreadId thread_id = (FuriThreadId)context; + furi_thread_flags_set(thread_id, DAPThreadEventRxV2); +} + +static void dap_app_usb_state_callback(bool state, void* context) { + furi_assert(context); + FuriThreadId thread_id = (FuriThreadId)context; + if(state) { + furi_thread_flags_set(thread_id, DAPThreadEventUSBConnect); + } else { + furi_thread_flags_set(thread_id, DAPThreadEventUSBDisconnect); + } +} + +static void dap_app_process_v1() { + DapPacket tx_packet; + DapPacket rx_packet; + memset(&tx_packet, 0, sizeof(DapPacket)); + rx_packet.size = dap_v1_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE); + dap_process_request(rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE); + dap_v1_usb_tx(tx_packet.data, DAP_CONFIG_PACKET_SIZE); +} + +static void dap_app_process_v2() { + DapPacket tx_packet; + DapPacket rx_packet; + memset(&tx_packet, 0, sizeof(DapPacket)); + rx_packet.size = dap_v2_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE); + size_t len = dap_process_request( + rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE); + dap_v2_usb_tx(tx_packet.data, len); +} + +void dap_app_vendor_cmd(uint8_t cmd) { + // openocd -c "cmsis-dap cmd 81" + if(cmd == 0x01) { + furi_hal_power_reset(); + } +} + +void dap_app_target_reset() { + FURI_LOG_I("DAP", "Target reset"); +} + +static void dap_init_gpio(DapSwdPins swd_pins) { + switch(swd_pins) { + case DapSwdPinsPA7PA6: + flipper_dap_swclk_pin = gpio_ext_pa7; + flipper_dap_swdio_pin = gpio_ext_pa6; + break; + case DapSwdPinsPA14PA13: + flipper_dap_swclk_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_14}; + flipper_dap_swdio_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_13}; + break; + } + + flipper_dap_reset_pin = gpio_ext_pa4; + flipper_dap_tdo_pin = gpio_ext_pb3; + flipper_dap_tdi_pin = gpio_ext_pb2; +} + +static void dap_deinit_gpio(DapSwdPins swd_pins) { + // setup gpio pins to default state + furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + if(DapSwdPinsPA14PA13 == swd_pins) { + // PA14 and PA13 are used by SWD + furi_hal_gpio_init_ex( + &flipper_dap_swclk_pin, + GpioModeAltFunctionPushPull, + GpioPullDown, + GpioSpeedLow, + GpioAltFn0JTCK_SWCLK); + furi_hal_gpio_init_ex( + &flipper_dap_swdio_pin, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn0JTMS_SWDIO); + } else { + furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } +} + +static int32_t dap_process(void* p) { + DapApp* app = p; + DapState* dap_state = &(app->state); + + // allocate resources + FuriHalUsbInterface* usb_config_prev; + app->config.swd_pins = DapSwdPinsPA7PA6; + DapSwdPins swd_pins_prev = app->config.swd_pins; + + // init pins + dap_init_gpio(swd_pins_prev); + + // init dap + dap_init(); + + // get name + const char* name = furi_hal_version_get_name_ptr(); + if(!name) { + name = "Flipper"; + } + snprintf(usb_serial_number, USB_SERIAL_NUMBER_LEN, "DAP_%s", name); + + // init usb + usb_config_prev = furi_hal_usb_get_config(); + dap_common_usb_alloc_name(usb_serial_number); + dap_common_usb_set_context(furi_thread_get_id(furi_thread_get_current())); + dap_v1_usb_set_rx_callback(dap_app_rx1_callback); + dap_v2_usb_set_rx_callback(dap_app_rx2_callback); + dap_common_usb_set_state_callback(dap_app_usb_state_callback); + furi_hal_usb_set_config(&dap_v2_usb_hid, NULL); + + // work + uint32_t events; + while(1) { + events = furi_thread_flags_wait(DAPThreadEventAll, FuriFlagWaitAny, FuriWaitForever); + + if(!(events & FuriFlagError)) { + if(events & DAPThreadEventRxV1) { + dap_app_process_v1(); + dap_state->dap_counter++; + dap_state->dap_version = DapVersionV1; + } + + if(events & DAPThreadEventRxV2) { + dap_app_process_v2(); + dap_state->dap_counter++; + dap_state->dap_version = DapVersionV2; + } + + if(events & DAPThreadEventUSBConnect) { + dap_state->usb_connected = true; + } + + if(events & DAPThreadEventUSBDisconnect) { + dap_state->usb_connected = false; + dap_state->dap_version = DapVersionUnknown; + } + + if(events & DAPThreadEventApplyConfig) { + if(swd_pins_prev != app->config.swd_pins) { + dap_deinit_gpio(swd_pins_prev); + swd_pins_prev = app->config.swd_pins; + dap_init_gpio(swd_pins_prev); + } + } + + if(events & DAPThreadEventStop) { + break; + } + } + } + + // deinit usb + furi_hal_usb_set_config(usb_config_prev, NULL); + dap_common_wait_for_deinit(); + dap_common_usb_free_name(); + dap_deinit_gpio(swd_pins_prev); + return 0; +} + +/***************************************************************************/ +/****************************** CDC PROCESS ********************************/ +/***************************************************************************/ + +typedef enum { + CDCThreadEventStop = DapThreadEventStop, + CDCThreadEventUARTRx = (1 << 1), + CDCThreadEventCDCRx = (1 << 2), + CDCThreadEventCDCConfig = (1 << 3), + CDCThreadEventApplyConfig = (1 << 4), + CDCThreadEventAll = CDCThreadEventStop | CDCThreadEventUARTRx | CDCThreadEventCDCRx | + CDCThreadEventCDCConfig | CDCThreadEventApplyConfig, +} CDCThreadEvent; + +typedef struct { + FuriStreamBuffer* rx_stream; + FuriThreadId thread_id; + FuriHalUartId uart_id; + struct usb_cdc_line_coding line_coding; +} CDCProcess; + +static void cdc_uart_irq_cb(UartIrqEvent ev, uint8_t data, void* ctx) { + CDCProcess* app = ctx; + + if(ev == UartIrqEventRXNE) { + furi_stream_buffer_send(app->rx_stream, &data, 1, 0); + furi_thread_flags_set(app->thread_id, CDCThreadEventUARTRx); + } +} + +static void cdc_usb_rx_callback(void* context) { + CDCProcess* app = context; + furi_thread_flags_set(app->thread_id, CDCThreadEventCDCRx); +} + +static void cdc_usb_control_line_callback(uint8_t state, void* context) { + UNUSED(context); + UNUSED(state); +} + +static void cdc_usb_config_callback(struct usb_cdc_line_coding* config, void* context) { + CDCProcess* app = context; + app->line_coding = *config; + furi_thread_flags_set(app->thread_id, CDCThreadEventCDCConfig); +} + +static FuriHalUartId cdc_init_uart( + DapUartType type, + DapUartTXRX swap, + uint32_t baudrate, + void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), + void* ctx) { + FuriHalUartId uart_id = FuriHalUartIdUSART1; + if(baudrate == 0) baudrate = 115200; + + switch(type) { + case DapUartTypeUSART1: + uart_id = FuriHalUartIdUSART1; + furi_hal_console_disable(); + furi_hal_uart_deinit(uart_id); + if(swap == DapUartTXRXSwap) { + LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_SWAPPED); + } else { + LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD); + } + furi_hal_uart_init(uart_id, baudrate); + furi_hal_uart_set_irq_cb(uart_id, cb, ctx); + break; + case DapUartTypeLPUART1: + uart_id = FuriHalUartIdLPUART1; + furi_hal_uart_deinit(uart_id); + if(swap == DapUartTXRXSwap) { + LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_SWAPPED); + } else { + LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD); + } + furi_hal_uart_init(uart_id, baudrate); + furi_hal_uart_set_irq_cb(uart_id, cb, ctx); + break; + } + + return uart_id; +} + +static void cdc_deinit_uart(DapUartType type) { + switch(type) { + case DapUartTypeUSART1: + furi_hal_uart_deinit(FuriHalUartIdUSART1); + LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD); + furi_hal_console_init(); + break; + case DapUartTypeLPUART1: + furi_hal_uart_deinit(FuriHalUartIdLPUART1); + LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD); + break; + } +} + +static int32_t cdc_process(void* p) { + DapApp* dap_app = p; + DapState* dap_state = &(dap_app->state); + + dap_app->config.uart_pins = DapUartTypeLPUART1; + dap_app->config.uart_swap = DapUartTXRXNormal; + + DapUartType uart_pins_prev = dap_app->config.uart_pins; + DapUartTXRX uart_swap_prev = dap_app->config.uart_swap; + + CDCProcess* app = malloc(sizeof(CDCProcess)); + app->thread_id = furi_thread_get_id(furi_thread_get_current()); + app->rx_stream = furi_stream_buffer_alloc(512, 1); + + const uint8_t rx_buffer_size = 64; + uint8_t* rx_buffer = malloc(rx_buffer_size); + + app->uart_id = cdc_init_uart( + uart_pins_prev, uart_swap_prev, dap_state->cdc_baudrate, cdc_uart_irq_cb, app); + + dap_cdc_usb_set_context(app); + dap_cdc_usb_set_rx_callback(cdc_usb_rx_callback); + dap_cdc_usb_set_control_line_callback(cdc_usb_control_line_callback); + dap_cdc_usb_set_config_callback(cdc_usb_config_callback); + + uint32_t events; + while(1) { + events = furi_thread_flags_wait(CDCThreadEventAll, FuriFlagWaitAny, FuriWaitForever); + + if(!(events & FuriFlagError)) { + if(events & CDCThreadEventCDCConfig) { + if(dap_state->cdc_baudrate != app->line_coding.dwDTERate) { + dap_state->cdc_baudrate = app->line_coding.dwDTERate; + if(dap_state->cdc_baudrate > 0) { + furi_hal_uart_set_br(app->uart_id, dap_state->cdc_baudrate); + } + } + } + + if(events & CDCThreadEventUARTRx) { + size_t len = + furi_stream_buffer_receive(app->rx_stream, rx_buffer, rx_buffer_size, 0); + + if(len > 0) { + dap_cdc_usb_tx(rx_buffer, len); + } + dap_state->cdc_rx_counter += len; + } + + if(events & CDCThreadEventCDCRx) { + size_t len = dap_cdc_usb_rx(rx_buffer, rx_buffer_size); + if(len > 0) { + furi_hal_uart_tx(app->uart_id, rx_buffer, len); + } + dap_state->cdc_tx_counter += len; + } + + if(events & CDCThreadEventApplyConfig) { + if(uart_pins_prev != dap_app->config.uart_pins || + uart_swap_prev != dap_app->config.uart_swap) { + cdc_deinit_uart(uart_pins_prev); + uart_pins_prev = dap_app->config.uart_pins; + uart_swap_prev = dap_app->config.uart_swap; + app->uart_id = cdc_init_uart( + uart_pins_prev, + uart_swap_prev, + dap_state->cdc_baudrate, + cdc_uart_irq_cb, + app); + } + } + + if(events & CDCThreadEventStop) { + break; + } + } + } + + cdc_deinit_uart(uart_pins_prev); + free(rx_buffer); + furi_stream_buffer_free(app->rx_stream); + free(app); + + return 0; +} + +/***************************************************************************/ +/******************************* MAIN APP **********************************/ +/***************************************************************************/ + +static FuriThread* furi_thread_alloc_ex( + const char* name, + uint32_t stack_size, + FuriThreadCallback callback, + void* context) { + FuriThread* thread = furi_thread_alloc(); + furi_thread_set_name(thread, name); + furi_thread_set_stack_size(thread, stack_size); + furi_thread_set_callback(thread, callback); + furi_thread_set_context(thread, context); + return thread; +} + +static DapApp* dap_app_alloc() { + DapApp* dap_app = malloc(sizeof(DapApp)); + dap_app->dap_thread = furi_thread_alloc_ex("DAP Process", 1024, dap_process, dap_app); + dap_app->cdc_thread = furi_thread_alloc_ex("DAP CDC", 1024, cdc_process, dap_app); + dap_app->gui_thread = furi_thread_alloc_ex("DAP GUI", 1024, dap_gui_thread, dap_app); + return dap_app; +} + +static void dap_app_free(DapApp* dap_app) { + furi_assert(dap_app); + furi_thread_free(dap_app->dap_thread); + furi_thread_free(dap_app->cdc_thread); + furi_thread_free(dap_app->gui_thread); + free(dap_app); +} + +static DapApp* app_handle = NULL; + +void dap_app_disconnect() { + app_handle->state.dap_mode = DapModeDisconnected; +} + +void dap_app_connect_swd() { + app_handle->state.dap_mode = DapModeSWD; +} + +void dap_app_connect_jtag() { + app_handle->state.dap_mode = DapModeJTAG; +} + +void dap_app_set_config(DapApp* app, DapConfig* config) { + app->config = *config; + furi_thread_flags_set(furi_thread_get_id(app->dap_thread), DAPThreadEventApplyConfig); + furi_thread_flags_set(furi_thread_get_id(app->cdc_thread), CDCThreadEventApplyConfig); +} + +DapConfig* dap_app_get_config(DapApp* app) { + return &app->config; +} + +int32_t dap_link_app(void* p) { + UNUSED(p); + + // alloc app + DapApp* app = dap_app_alloc(); + app_handle = app; + + furi_thread_start(app->dap_thread); + furi_thread_start(app->cdc_thread); + furi_thread_start(app->gui_thread); + + // wait until gui thread is finished + furi_thread_join(app->gui_thread); + + // send stop event to threads + dap_thread_send_stop(app->dap_thread); + dap_thread_send_stop(app->cdc_thread); + + // wait for threads to stop + furi_thread_join(app->dap_thread); + furi_thread_join(app->cdc_thread); + + // free app + dap_app_free(app); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/dap_link/dap_link.h b/applications/plugins/dap_link/dap_link.h new file mode 100644 index 00000000..d51726c4 --- /dev/null +++ b/applications/plugins/dap_link/dap_link.h @@ -0,0 +1,55 @@ +#pragma once +#include + +typedef enum { + DapModeDisconnected, + DapModeSWD, + DapModeJTAG, +} DapMode; + +typedef enum { + DapVersionUnknown, + DapVersionV1, + DapVersionV2, +} DapVersion; + +typedef struct { + bool usb_connected; + DapMode dap_mode; + DapVersion dap_version; + uint32_t dap_counter; + uint32_t cdc_baudrate; + uint32_t cdc_tx_counter; + uint32_t cdc_rx_counter; +} DapState; + +typedef enum { + DapSwdPinsPA7PA6, // Pins 2, 3 + DapSwdPinsPA14PA13, // Pins 10, 12 +} DapSwdPins; + +typedef enum { + DapUartTypeUSART1, // Pins 13, 14 + DapUartTypeLPUART1, // Pins 15, 16 +} DapUartType; + +typedef enum { + DapUartTXRXNormal, + DapUartTXRXSwap, +} DapUartTXRX; + +typedef struct { + DapSwdPins swd_pins; + DapUartType uart_pins; + DapUartTXRX uart_swap; +} DapConfig; + +typedef struct DapApp DapApp; + +void dap_app_get_state(DapApp* app, DapState* state); + +const char* dap_app_get_serial(DapApp* app); + +void dap_app_set_config(DapApp* app, DapConfig* config); + +DapConfig* dap_app_get_config(DapApp* app); \ No newline at end of file diff --git a/applications/plugins/dap_link/dap_link.png b/applications/plugins/dap_link/dap_link.png new file mode 100644 index 0000000000000000000000000000000000000000..2278ce2b61cf97e51720bafb2d07dae48d986560 GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>@$__Y43U`H zI>D2R!GMF={pJ7tHnyBWw`O@WO=4vXnQOryxnPsm9sxmyCsV>A?tXRt_DY<&Wq)IO q>DB4a%~XFc{Gk)L^WR4G>Gvdb>|Y;BDpm&?%HZkh=d#Wzp$P!!VJ~6; literal 0 HcmV?d00001 diff --git a/applications/plugins/dap_link/gui/dap_gui.c b/applications/plugins/dap_link/gui/dap_gui.c new file mode 100644 index 00000000..4dd98615 --- /dev/null +++ b/applications/plugins/dap_link/gui/dap_gui.c @@ -0,0 +1,92 @@ +#include "dap_gui.h" +#include "dap_gui_i.h" + +#define DAP_GUI_TICK 250 + +static bool dap_gui_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + DapGuiApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool dap_gui_back_event_callback(void* context) { + furi_assert(context); + DapGuiApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void dap_gui_tick_event_callback(void* context) { + furi_assert(context); + DapGuiApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +DapGuiApp* dap_gui_alloc() { + DapGuiApp* app = malloc(sizeof(DapGuiApp)); + app->gui = furi_record_open(RECORD_GUI); + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&dap_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_set_custom_event_callback(app->view_dispatcher, dap_gui_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, dap_gui_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, dap_gui_tick_event_callback, DAP_GUI_TICK); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + DapGuiAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + app->main_view = dap_main_view_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, DapGuiAppViewMainView, dap_main_view_get_view(app->main_view)); + + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, DapGuiAppViewWidget, widget_get_view(app->widget)); + + scene_manager_next_scene(app->scene_manager, DapSceneMain); + + return app; +} + +void dap_gui_free(DapGuiApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewVarItemList); + variable_item_list_free(app->var_item_list); + + view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewMainView); + dap_main_view_free(app->main_view); + + view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewWidget); + widget_free(app->widget); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Close records + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + + free(app); +} + +int32_t dap_gui_thread(void* arg) { + DapGuiApp* app = dap_gui_alloc(); + app->dap_app = arg; + + notification_message_block(app->notifications, &sequence_display_backlight_enforce_on); + view_dispatcher_run(app->view_dispatcher); + notification_message_block(app->notifications, &sequence_display_backlight_enforce_auto); + + dap_gui_free(app); + return 0; +} \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/dap_gui.h b/applications/plugins/dap_link/gui/dap_gui.h new file mode 100644 index 00000000..3d8e6bdf --- /dev/null +++ b/applications/plugins/dap_link/gui/dap_gui.h @@ -0,0 +1,4 @@ +#pragma once +#include + +int32_t dap_gui_thread(void* arg); \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/dap_gui_custom_event.h b/applications/plugins/dap_link/gui/dap_gui_custom_event.h new file mode 100644 index 00000000..8b127c9d --- /dev/null +++ b/applications/plugins/dap_link/gui/dap_gui_custom_event.h @@ -0,0 +1,7 @@ +#pragma once + +typedef enum { + DapAppCustomEventConfig, + DapAppCustomEventHelp, + DapAppCustomEventAbout, +} DapAppCustomEvent; diff --git a/applications/plugins/dap_link/gui/dap_gui_i.h b/applications/plugins/dap_link/gui/dap_gui_i.h new file mode 100644 index 00000000..59411e78 --- /dev/null +++ b/applications/plugins/dap_link/gui/dap_gui_i.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "dap_gui.h" +#include "../dap_link.h" +#include "scenes/config/dap_scene.h" +#include "dap_gui_custom_event.h" +#include "views/dap_main_view.h" + +typedef struct { + DapApp* dap_app; + + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + + VariableItemList* var_item_list; + DapMainView* main_view; + Widget* widget; +} DapGuiApp; + +typedef enum { + DapGuiAppViewVarItemList, + DapGuiAppViewMainView, + DapGuiAppViewWidget, +} DapGuiAppView; diff --git a/applications/plugins/dap_link/gui/scenes/config/dap_scene.c b/applications/plugins/dap_link/gui/scenes/config/dap_scene.c new file mode 100644 index 00000000..37e23554 --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/config/dap_scene.c @@ -0,0 +1,30 @@ +#include "dap_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const dap_scene_on_enter_handlers[])(void*) = { +#include "dap_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const dap_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "dap_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const dap_scene_on_exit_handlers[])(void* context) = { +#include "dap_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers dap_scene_handlers = { + .on_enter_handlers = dap_scene_on_enter_handlers, + .on_event_handlers = dap_scene_on_event_handlers, + .on_exit_handlers = dap_scene_on_exit_handlers, + .scene_num = DapSceneNum, +}; diff --git a/applications/plugins/dap_link/gui/scenes/config/dap_scene.h b/applications/plugins/dap_link/gui/scenes/config/dap_scene.h new file mode 100644 index 00000000..6fb38da4 --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/config/dap_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) DapScene##id, +typedef enum { +#include "dap_scene_config.h" + DapSceneNum, +} DapScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers dap_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "dap_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "dap_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "dap_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/dap_link/gui/scenes/config/dap_scene_config.h b/applications/plugins/dap_link/gui/scenes/config/dap_scene_config.h new file mode 100644 index 00000000..8957aca0 --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/config/dap_scene_config.h @@ -0,0 +1,4 @@ +ADD_SCENE(dap, main, Main) +ADD_SCENE(dap, config, Config) +ADD_SCENE(dap, help, Help) +ADD_SCENE(dap, about, About) \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_about.c b/applications/plugins/dap_link/gui/scenes/dap_scene_about.c new file mode 100644 index 00000000..0974e60a --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/dap_scene_about.c @@ -0,0 +1,68 @@ +#include "../dap_gui_i.h" + +#define DAP_VERSION_APP "0.1.0" +#define DAP_DEVELOPED "Dr_Zlo" +#define DAP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" + +void dap_scene_about_on_enter(void* context) { + DapGuiApp* app = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", DAP_VERSION_APP); + furi_string_cat_printf(temp_str, "Developed by: %s\n", DAP_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", DAP_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf( + temp_str, "CMSIS-DAP debugger\nbased on Free-DAP\nThanks to Alex Taradov\n\n"); + + furi_string_cat_printf( + temp_str, + "Supported protocols:\n" + "SWD, JTAG, UART\n" + "DAP v1 (cmsis_backend hid), DAP v2 (cmsis_backend usb_bulk), VCP\n"); + + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + app->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! DAP Link \e!\n", + false); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget); +} + +bool dap_scene_about_on_event(void* context, SceneManagerEvent event) { + DapGuiApp* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void dap_scene_about_on_exit(void* context) { + DapGuiApp* app = context; + + // Clear views + widget_reset(app->widget); +} diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_config.c b/applications/plugins/dap_link/gui/scenes/dap_scene_config.c new file mode 100644 index 00000000..56b06411 --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/dap_scene_config.c @@ -0,0 +1,107 @@ +#include "../dap_gui_i.h" + +static const char* swd_pins[] = {[DapSwdPinsPA7PA6] = "2,3", [DapSwdPinsPA14PA13] = "10,12"}; +static const char* uart_pins[] = {[DapUartTypeUSART1] = "13,14", [DapUartTypeLPUART1] = "15,16"}; +static const char* uart_swap[] = {[DapUartTXRXNormal] = "No", [DapUartTXRXSwap] = "Yes"}; + +static void swd_pins_cb(VariableItem* item) { + DapGuiApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, swd_pins[index]); + + DapConfig* config = dap_app_get_config(app->dap_app); + config->swd_pins = index; + dap_app_set_config(app->dap_app, config); +} + +static void uart_pins_cb(VariableItem* item) { + DapGuiApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, uart_pins[index]); + + DapConfig* config = dap_app_get_config(app->dap_app); + config->uart_pins = index; + dap_app_set_config(app->dap_app, config); +} + +static void uart_swap_cb(VariableItem* item) { + DapGuiApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, uart_swap[index]); + + DapConfig* config = dap_app_get_config(app->dap_app); + config->uart_swap = index; + dap_app_set_config(app->dap_app, config); +} + +static void ok_cb(void* context, uint32_t index) { + DapGuiApp* app = context; + switch(index) { + case 3: + view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventHelp); + break; + case 4: + view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventAbout); + break; + default: + break; + } +} + +void dap_scene_config_on_enter(void* context) { + DapGuiApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + DapConfig* config = dap_app_get_config(app->dap_app); + + item = variable_item_list_add( + var_item_list, "SWC SWD Pins", COUNT_OF(swd_pins), swd_pins_cb, app); + variable_item_set_current_value_index(item, config->swd_pins); + variable_item_set_current_value_text(item, swd_pins[config->swd_pins]); + + item = + variable_item_list_add(var_item_list, "UART Pins", COUNT_OF(uart_pins), uart_pins_cb, app); + variable_item_set_current_value_index(item, config->uart_pins); + variable_item_set_current_value_text(item, uart_pins[config->uart_pins]); + + item = variable_item_list_add( + var_item_list, "Swap TX RX", COUNT_OF(uart_swap), uart_swap_cb, app); + variable_item_set_current_value_index(item, config->uart_swap); + variable_item_set_current_value_text(item, uart_swap[config->uart_swap]); + + item = variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL); + item = variable_item_list_add(var_item_list, "About", 0, NULL, NULL); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, DapSceneConfig)); + + variable_item_list_set_enter_callback(var_item_list, ok_cb, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewVarItemList); +} + +bool dap_scene_config_on_event(void* context, SceneManagerEvent event) { + DapGuiApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DapAppCustomEventHelp) { + scene_manager_next_scene(app->scene_manager, DapSceneHelp); + return true; + } else if(event.event == DapAppCustomEventAbout) { + scene_manager_next_scene(app->scene_manager, DapSceneAbout); + return true; + } + } + return false; +} + +void dap_scene_config_on_exit(void* context) { + DapGuiApp* app = context; + scene_manager_set_scene_state( + app->scene_manager, + DapSceneConfig, + variable_item_list_get_selected_item_index(app->var_item_list)); + variable_item_list_reset(app->var_item_list); +} \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_help.c b/applications/plugins/dap_link/gui/scenes/dap_scene_help.c new file mode 100644 index 00000000..7193f4f4 --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/dap_scene_help.c @@ -0,0 +1,102 @@ +#include "../dap_gui_i.h" + +void dap_scene_help_on_enter(void* context) { + DapGuiApp* app = context; + DapConfig* config = dap_app_get_config(app->dap_app); + FuriString* string = furi_string_alloc(); + + furi_string_cat(string, "CMSIS DAP/DAP Link v2\r\n"); + furi_string_cat_printf(string, "Serial: %s\r\n", dap_app_get_serial(app->dap_app)); + furi_string_cat( + string, + "Pinout:\r\n" + "\e#SWD:\r\n"); + + switch(config->swd_pins) { + case DapSwdPinsPA7PA6: + furi_string_cat( + string, + " SWC: 2 [A7]\r\n" + " SWD: 3 [A6]\r\n"); + break; + case DapSwdPinsPA14PA13: + furi_string_cat( + string, + " SWC: 10 [SWC]\r\n" + " SWD: 12 [SIO]\r\n"); + break; + default: + break; + } + + furi_string_cat(string, "\e#JTAG:\r\n"); + switch(config->swd_pins) { + case DapSwdPinsPA7PA6: + furi_string_cat( + string, + " TCK: 2 [A7]\r\n" + " TMS: 3 [A6]\r\n" + " RST: 4 [A4]\r\n" + " TDO: 5 [B3]\r\n" + " TDI: 6 [B2]\r\n"); + break; + case DapSwdPinsPA14PA13: + furi_string_cat( + string, + " RST: 4 [A4]\r\n" + " TDO: 5 [B3]\r\n" + " TDI: 6 [B2]\r\n" + " TCK: 10 [SWC]\r\n" + " TMS: 12 [SIO]\r\n"); + break; + default: + break; + } + + furi_string_cat(string, "\e#UART:\r\n"); + switch(config->uart_pins) { + case DapUartTypeUSART1: + if(config->uart_swap == DapUartTXRXNormal) { + furi_string_cat( + string, + " TX: 13 [TX]\r\n" + " RX: 14 [RX]\r\n"); + } else { + furi_string_cat( + string, + " RX: 13 [TX]\r\n" + " TX: 14 [RX]\r\n"); + } + break; + case DapUartTypeLPUART1: + if(config->uart_swap == DapUartTXRXNormal) { + furi_string_cat( + string, + " TX: 15 [С1]\r\n" + " RX: 16 [С0]\r\n"); + } else { + furi_string_cat( + string, + " RX: 15 [С1]\r\n" + " TX: 16 [С0]\r\n"); + } + break; + default: + break; + } + + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, furi_string_get_cstr(string)); + furi_string_free(string); + view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget); +} + +bool dap_scene_help_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void dap_scene_help_on_exit(void* context) { + DapGuiApp* app = context; + widget_reset(app->widget); +} \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_main.c b/applications/plugins/dap_link/gui/scenes/dap_scene_main.c new file mode 100644 index 00000000..8c19bd6a --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/dap_scene_main.c @@ -0,0 +1,154 @@ +#include "../dap_gui_i.h" +#include "../../dap_link.h" + +typedef struct { + DapState dap_state; + bool dap_active; + bool tx_active; + bool rx_active; +} DapSceneMainState; + +static bool process_dap_state(DapGuiApp* app) { + DapSceneMainState* state = + (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain); + if(state == NULL) return true; + + DapState* prev_state = &state->dap_state; + DapState next_state; + dap_app_get_state(app->dap_app, &next_state); + bool need_to_update = false; + + if(prev_state->dap_mode != next_state.dap_mode) { + switch(next_state.dap_mode) { + case DapModeDisconnected: + dap_main_view_set_mode(app->main_view, DapMainViewModeDisconnected); + notification_message(app->notifications, &sequence_blink_stop); + break; + case DapModeSWD: + dap_main_view_set_mode(app->main_view, DapMainViewModeSWD); + notification_message(app->notifications, &sequence_blink_start_blue); + break; + case DapModeJTAG: + dap_main_view_set_mode(app->main_view, DapMainViewModeJTAG); + notification_message(app->notifications, &sequence_blink_start_magenta); + break; + } + need_to_update = true; + } + + if(prev_state->dap_version != next_state.dap_version) { + switch(next_state.dap_version) { + case DapVersionUnknown: + dap_main_view_set_version(app->main_view, DapMainViewVersionUnknown); + break; + case DapVersionV1: + dap_main_view_set_version(app->main_view, DapMainViewVersionV1); + break; + case DapVersionV2: + dap_main_view_set_version(app->main_view, DapMainViewVersionV2); + break; + } + need_to_update = true; + } + + if(prev_state->usb_connected != next_state.usb_connected) { + dap_main_view_set_usb_connected(app->main_view, next_state.usb_connected); + need_to_update = true; + } + + if(prev_state->dap_counter != next_state.dap_counter) { + if(!state->dap_active) { + state->dap_active = true; + dap_main_view_set_dap(app->main_view, state->dap_active); + need_to_update = true; + } + } else { + if(state->dap_active) { + state->dap_active = false; + dap_main_view_set_dap(app->main_view, state->dap_active); + need_to_update = true; + } + } + + if(prev_state->cdc_baudrate != next_state.cdc_baudrate) { + dap_main_view_set_baudrate(app->main_view, next_state.cdc_baudrate); + need_to_update = true; + } + + if(prev_state->cdc_tx_counter != next_state.cdc_tx_counter) { + if(!state->tx_active) { + state->tx_active = true; + dap_main_view_set_tx(app->main_view, state->tx_active); + need_to_update = true; + notification_message(app->notifications, &sequence_blink_start_red); + } + } else { + if(state->tx_active) { + state->tx_active = false; + dap_main_view_set_tx(app->main_view, state->tx_active); + need_to_update = true; + notification_message(app->notifications, &sequence_blink_stop); + } + } + + if(prev_state->cdc_rx_counter != next_state.cdc_rx_counter) { + if(!state->rx_active) { + state->rx_active = true; + dap_main_view_set_rx(app->main_view, state->rx_active); + need_to_update = true; + notification_message(app->notifications, &sequence_blink_start_green); + } + } else { + if(state->rx_active) { + state->rx_active = false; + dap_main_view_set_rx(app->main_view, state->rx_active); + need_to_update = true; + notification_message(app->notifications, &sequence_blink_stop); + } + } + + if(need_to_update) { + dap_main_view_update(app->main_view); + } + + *prev_state = next_state; + return true; +} + +static void dap_scene_main_on_left(void* context) { + DapGuiApp* app = (DapGuiApp*)context; + view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventConfig); +} + +void dap_scene_main_on_enter(void* context) { + DapGuiApp* app = context; + DapSceneMainState* state = malloc(sizeof(DapSceneMainState)); + dap_main_view_set_left_callback(app->main_view, dap_scene_main_on_left, app); + view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewMainView); + scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)state); +} + +bool dap_scene_main_on_event(void* context, SceneManagerEvent event) { + DapGuiApp* app = context; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DapAppCustomEventConfig) { + scene_manager_next_scene(app->scene_manager, DapSceneConfig); + return true; + } + } else if(event.type == SceneManagerEventTypeTick) { + return process_dap_state(app); + } + + return false; +} + +void dap_scene_main_on_exit(void* context) { + DapGuiApp* app = context; + DapSceneMainState* state = + (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain); + scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)NULL); + FURI_SW_MEMBARRIER(); + free(state); + notification_message(app->notifications, &sequence_blink_stop); +} \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/views/dap_main_view.c b/applications/plugins/dap_link/gui/views/dap_main_view.c new file mode 100644 index 00000000..c5c8f9df --- /dev/null +++ b/applications/plugins/dap_link/gui/views/dap_main_view.c @@ -0,0 +1,189 @@ +#include "dap_main_view.h" +#include "dap_link_icons.h" +#include + +// extern const Icon I_ArrowDownEmpty_12x18; +// extern const Icon I_ArrowDownFilled_12x18; +// extern const Icon I_ArrowUpEmpty_12x18; +// extern const Icon I_ArrowUpFilled_12x18; + +struct DapMainView { + View* view; + DapMainViewButtonCallback cb_left; + void* cb_context; +}; + +typedef struct { + DapMainViewMode mode; + DapMainViewVersion version; + bool usb_connected; + uint32_t baudrate; + bool dap_active; + bool tx_active; + bool rx_active; +} DapMainViewModel; + +static void dap_main_view_draw_callback(Canvas* canvas, void* _model) { + DapMainViewModel* model = _model; + UNUSED(model); + canvas_clear(canvas); + elements_button_left(canvas, "Config"); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 0, 127, 11); + canvas_set_color(canvas, ColorWhite); + + const char* header_string; + if(model->usb_connected) { + if(model->version == DapMainViewVersionV1) { + header_string = "DAP Link V1 Connected"; + } else if(model->version == DapMainViewVersionV2) { + header_string = "DAP Link V2 Connected"; + } else { + header_string = "DAP Link Connected"; + } + } else { + header_string = "DAP Link"; + } + + canvas_draw_str_aligned(canvas, 64, 9, AlignCenter, AlignBottom, header_string); + + canvas_set_color(canvas, ColorBlack); + if(model->dap_active) { + canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18); + canvas_draw_icon(canvas, 28, 16, &I_ArrowDownFilled_12x18); + } else { + canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18); + canvas_draw_icon(canvas, 28, 16, &I_ArrowDownEmpty_12x18); + } + + switch(model->mode) { + case DapMainViewModeDisconnected: + canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "----"); + break; + case DapMainViewModeSWD: + canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "SWD"); + break; + case DapMainViewModeJTAG: + canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "JTAG"); + break; + } + + if(model->tx_active) { + canvas_draw_icon(canvas, 87, 16, &I_ArrowUpFilled_12x18); + } else { + canvas_draw_icon(canvas, 87, 16, &I_ArrowUpEmpty_12x18); + } + + if(model->rx_active) { + canvas_draw_icon(canvas, 101, 16, &I_ArrowDownFilled_12x18); + } else { + canvas_draw_icon(canvas, 101, 16, &I_ArrowDownEmpty_12x18); + } + + canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART"); + + canvas_draw_line(canvas, 44, 52, 123, 52); + if(model->baudrate == 0) { + canvas_draw_str(canvas, 45, 62, "Baud: ????"); + } else { + char baudrate_str[18]; + snprintf(baudrate_str, 18, "Baud: %lu", model->baudrate); + canvas_draw_str(canvas, 45, 62, baudrate_str); + } +} + +static bool dap_main_view_input_callback(InputEvent* event, void* context) { + furi_assert(context); + DapMainView* dap_main_view = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + if(dap_main_view->cb_left) { + dap_main_view->cb_left(dap_main_view->cb_context); + } + consumed = true; + } + } + + return consumed; +} + +DapMainView* dap_main_view_alloc() { + DapMainView* dap_main_view = malloc(sizeof(DapMainView)); + + dap_main_view->view = view_alloc(); + view_allocate_model(dap_main_view->view, ViewModelTypeLocking, sizeof(DapMainViewModel)); + view_set_context(dap_main_view->view, dap_main_view); + view_set_draw_callback(dap_main_view->view, dap_main_view_draw_callback); + view_set_input_callback(dap_main_view->view, dap_main_view_input_callback); + return dap_main_view; +} + +void dap_main_view_free(DapMainView* dap_main_view) { + view_free(dap_main_view->view); + free(dap_main_view); +} + +View* dap_main_view_get_view(DapMainView* dap_main_view) { + return dap_main_view->view; +} + +void dap_main_view_set_left_callback( + DapMainView* dap_main_view, + DapMainViewButtonCallback callback, + void* context) { + with_view_model( + dap_main_view->view, + DapMainViewModel * model, + { + UNUSED(model); + dap_main_view->cb_left = callback; + dap_main_view->cb_context = context; + }, + true); +} + +void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->mode = mode; }, false); +} + +void dap_main_view_set_dap(DapMainView* dap_main_view, bool active) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->dap_active = active; }, false); +} + +void dap_main_view_set_tx(DapMainView* dap_main_view, bool active) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->tx_active = active; }, false); +} + +void dap_main_view_set_rx(DapMainView* dap_main_view, bool active) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->rx_active = active; }, false); +} + +void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->baudrate = baudrate; }, false); +} + +void dap_main_view_update(DapMainView* dap_main_view) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { UNUSED(model); }, true); +} + +void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->version = version; }, false); +} + +void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected) { + with_view_model( + dap_main_view->view, + DapMainViewModel * model, + { model->usb_connected = connected; }, + false); +} \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/views/dap_main_view.h b/applications/plugins/dap_link/gui/views/dap_main_view.h new file mode 100644 index 00000000..1fd90045 --- /dev/null +++ b/applications/plugins/dap_link/gui/views/dap_main_view.h @@ -0,0 +1,45 @@ +#pragma once +#include + +typedef struct DapMainView DapMainView; + +typedef void (*DapMainViewButtonCallback)(void* context); + +typedef enum { + DapMainViewVersionUnknown, + DapMainViewVersionV1, + DapMainViewVersionV2, +} DapMainViewVersion; + +typedef enum { + DapMainViewModeDisconnected, + DapMainViewModeSWD, + DapMainViewModeJTAG, +} DapMainViewMode; + +DapMainView* dap_main_view_alloc(); + +void dap_main_view_free(DapMainView* dap_main_view); + +View* dap_main_view_get_view(DapMainView* dap_main_view); + +void dap_main_view_set_left_callback( + DapMainView* dap_main_view, + DapMainViewButtonCallback callback, + void* context); + +void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode); + +void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version); + +void dap_main_view_set_dap(DapMainView* dap_main_view, bool active); + +void dap_main_view_set_tx(DapMainView* dap_main_view, bool active); + +void dap_main_view_set_rx(DapMainView* dap_main_view, bool active); + +void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected); + +void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate); + +void dap_main_view_update(DapMainView* dap_main_view); \ No newline at end of file diff --git a/applications/plugins/dap_link/icons/ArrowDownEmpty_12x18.png b/applications/plugins/dap_link/icons/ArrowDownEmpty_12x18.png new file mode 100644 index 0000000000000000000000000000000000000000..6007f74ab0e89fc751f503a8a5e4e44021c0106c GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eO!3HGrSK5O(jKx9jP7LeL$-D$|qC8z3Lo_D7 zo$SbWK!Jlr{PF+&Hnv;TJQ&v7?g&r!Fz9A{(LGbM+u3CI@thML?q)j=*|$VqJ$T$$ ztuvzCPsSl~7YFluKK0CllcIM`vf0BqQzUWaj&{}5`5zhLf9t51+o=lhbp(50(A=anC2ZSBvNHg_RnWm*uWGEIRgogS6BGwsp^g6qa`c R{s7v>;OXk;vd$@?2>@rkKMeo? literal 0 HcmV?d00001 diff --git a/applications/plugins/dap_link/icons/ArrowUpEmpty_12x18.png b/applications/plugins/dap_link/icons/ArrowUpEmpty_12x18.png new file mode 100644 index 0000000000000000000000000000000000000000..c9365a67d4e6e64761392b238965950762577773 GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eO!3HGrSK5O(jKx9jP7LeL$-D$|B0XIkLo_D7 zooL9{V8FxtcE$hyn7lHVqBK5*LoCO;xQ;Z~RV&b424_kZ9`tW^A4md67B@CXfelF{r G5}E*=05`}0 literal 0 HcmV?d00001 diff --git a/applications/plugins/dap_link/icons/ArrowUpFilled_12x18.png b/applications/plugins/dap_link/icons/ArrowUpFilled_12x18.png new file mode 100644 index 0000000000000000000000000000000000000000..dc481517ebae2d875880d96c3a6863c9ca985690 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eO!3HGrSK5O(jKx9jP7LeL$-D$|(mh=qLo_Cy zop_M%fC7he^yB~ki^>fomK}Y&?Q6<{^IZXxHgu>k9N2PUPNa~E?*^7bKA+0AH5l)R zS}i4d#BjFRWa(K?7>YJFIonF7y_qz@_V#wWmAe;3-!N4C_Ce~$H&1(?8x60XU0}74 WwRe~lwXYaxC4;A{pUXO@geCw-ib4GV literal 0 HcmV?d00001 diff --git a/applications/plugins/dap_link/lib/free-dap b/applications/plugins/dap_link/lib/free-dap new file mode 160000 index 00000000..e7752beb --- /dev/null +++ b/applications/plugins/dap_link/lib/free-dap @@ -0,0 +1 @@ +Subproject commit e7752beb5e8a69119af67b70b9179cb3c90f3ac5 diff --git a/applications/plugins/dap_link/usb/dap_v2_usb.c b/applications/plugins/dap_link/usb/dap_v2_usb.c new file mode 100644 index 00000000..0c303a3b --- /dev/null +++ b/applications/plugins/dap_link/usb/dap_v2_usb.c @@ -0,0 +1,994 @@ +#include +#include +#include +#include +#include +#include + +#include "dap_v2_usb.h" + +// #define DAP_USB_LOG + +#define HID_EP_IN 0x80 +#define HID_EP_OUT 0x00 + +#define DAP_HID_EP_SEND 1 +#define DAP_HID_EP_RECV 2 +#define DAP_HID_EP_BULK_RECV 3 +#define DAP_HID_EP_BULK_SEND 4 +#define DAP_CDC_EP_COMM 5 +#define DAP_CDC_EP_SEND 6 +#define DAP_CDC_EP_RECV 7 + +#define DAP_HID_EP_IN (HID_EP_IN | DAP_HID_EP_SEND) +#define DAP_HID_EP_OUT (HID_EP_OUT | DAP_HID_EP_RECV) +#define DAP_HID_EP_BULK_IN (HID_EP_IN | DAP_HID_EP_BULK_SEND) +#define DAP_HID_EP_BULK_OUT (HID_EP_OUT | DAP_HID_EP_BULK_RECV) + +#define DAP_HID_EP_SIZE 64 +#define DAP_CDC_COMM_EP_SIZE 8 +#define DAP_CDC_EP_SIZE 64 + +#define DAP_BULK_INTERVAL 0 +#define DAP_HID_INTERVAL 1 +#define DAP_CDC_INTERVAL 0 +#define DAP_CDC_COMM_INTERVAL 1 + +#define DAP_HID_VID 0x0483 +#define DAP_HID_PID 0x5740 + +#define DAP_USB_EP0_SIZE 8 + +#define EP_CFG_DECONFIGURE 0 +#define EP_CFG_CONFIGURE 1 + +enum { + USB_INTF_HID, + USB_INTF_BULK, + USB_INTF_CDC_COMM, + USB_INTF_CDC_DATA, + USB_INTF_COUNT, +}; + +enum { + USB_STR_ZERO, + USB_STR_MANUFACTURER, + USB_STR_PRODUCT, + USB_STR_SERIAL_NUMBER, + USB_STR_CMSIS_DAP_V1, + USB_STR_CMSIS_DAP_V2, + USB_STR_COM_PORT, + USB_STR_COUNT, +}; + +// static const char* usb_str[] = { +// [USB_STR_MANUFACTURER] = "Flipper Devices Inc.", +// [USB_STR_PRODUCT] = "Combined VCP and CMSIS-DAP Adapter", +// [USB_STR_COM_PORT] = "Virtual COM-Port", +// [USB_STR_CMSIS_DAP_V1] = "CMSIS-DAP v1 Adapter", +// [USB_STR_CMSIS_DAP_V2] = "CMSIS-DAP v2 Adapter", +// [USB_STR_SERIAL_NUMBER] = "01234567890ABCDEF", +// }; + +static const struct usb_string_descriptor dev_manuf_descr = + USB_STRING_DESC("Flipper Devices Inc."); + +static const struct usb_string_descriptor dev_prod_descr = + USB_STRING_DESC("Combined VCP and CMSIS-DAP Adapter"); + +static struct usb_string_descriptor* dev_serial_descr = NULL; + +static const struct usb_string_descriptor dev_dap_v1_descr = + USB_STRING_DESC("CMSIS-DAP v1 Adapter"); + +static const struct usb_string_descriptor dev_dap_v2_descr = + USB_STRING_DESC("CMSIS-DAP v2 Adapter"); + +static const struct usb_string_descriptor dev_com_descr = USB_STRING_DESC("Virtual COM-Port"); + +struct HidConfigDescriptor { + struct usb_config_descriptor configuration; + + // CMSIS-DAP v1 + struct usb_interface_descriptor hid_interface; + struct usb_hid_descriptor hid; + struct usb_endpoint_descriptor hid_ep_in; + struct usb_endpoint_descriptor hid_ep_out; + + // CMSIS-DAP v2 + struct usb_interface_descriptor bulk_interface; + struct usb_endpoint_descriptor bulk_ep_out; + struct usb_endpoint_descriptor bulk_ep_in; + + // CDC + struct usb_iad_descriptor iad; + struct usb_interface_descriptor interface_comm; + struct usb_cdc_header_desc cdc_header; + struct usb_cdc_call_mgmt_desc cdc_acm; + struct usb_cdc_acm_desc cdc_call_mgmt; + struct usb_cdc_union_desc cdc_union; + struct usb_endpoint_descriptor ep_comm; + struct usb_interface_descriptor interface_data; + struct usb_endpoint_descriptor ep_in; + struct usb_endpoint_descriptor ep_out; + +} __attribute__((packed)); + +static const struct usb_device_descriptor hid_device_desc = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 1, 0), + .bDeviceClass = USB_CLASS_MISC, + .bDeviceSubClass = USB_SUBCLASS_IAD, + .bDeviceProtocol = USB_PROTO_IAD, + .bMaxPacketSize0 = DAP_USB_EP0_SIZE, + .idVendor = DAP_HID_VID, + .idProduct = DAP_HID_PID, + .bcdDevice = VERSION_BCD(1, 0, 0), + .iManufacturer = USB_STR_MANUFACTURER, + .iProduct = USB_STR_PRODUCT, + .iSerialNumber = USB_STR_SERIAL_NUMBER, + .bNumConfigurations = 1, +}; + +static const uint8_t hid_report_desc[] = { + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x00, // Usage (Undefined) + 0xa1, 0x01, // Collection (Application) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xff, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x40, // Report Count (64) + 0x09, 0x00, // Usage (Undefined) + 0x81, 0x82, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x08, // Report Size (8) + 0x95, 0x40, // Report Count (64) + 0x09, 0x00, // Usage (Undefined) + 0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + 0xc0, // End Collection +}; + +static const struct HidConfigDescriptor hid_cfg_desc = { + .configuration = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct HidConfigDescriptor), + .bNumInterfaces = USB_INTF_COUNT, + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED, + .bMaxPower = USB_CFG_POWER_MA(500), + }, + + // CMSIS-DAP v1 + .hid_interface = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = USB_INTF_HID, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = USB_HID_SUBCLASS_NONBOOT, + .bInterfaceProtocol = USB_HID_PROTO_NONBOOT, + .iInterface = USB_STR_CMSIS_DAP_V1, + }, + + .hid = + { + .bLength = sizeof(struct usb_hid_descriptor), + .bDescriptorType = USB_DTYPE_HID, + .bcdHID = VERSION_BCD(1, 1, 1), + .bCountryCode = USB_HID_COUNTRY_NONE, + .bNumDescriptors = 1, + .bDescriptorType0 = USB_DTYPE_HID_REPORT, + .wDescriptorLength0 = sizeof(hid_report_desc), + }, + + .hid_ep_in = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = DAP_HID_EP_IN, + .bmAttributes = USB_EPTYPE_INTERRUPT, + .wMaxPacketSize = DAP_HID_EP_SIZE, + .bInterval = DAP_HID_INTERVAL, + }, + + .hid_ep_out = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = DAP_HID_EP_OUT, + .bmAttributes = USB_EPTYPE_INTERRUPT, + .wMaxPacketSize = DAP_HID_EP_SIZE, + .bInterval = DAP_HID_INTERVAL, + }, + + // CMSIS-DAP v2 + .bulk_interface = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = USB_INTF_BULK, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = USB_STR_CMSIS_DAP_V2, + }, + + .bulk_ep_out = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = DAP_HID_EP_BULK_OUT, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = DAP_HID_EP_SIZE, + .bInterval = DAP_BULK_INTERVAL, + }, + + .bulk_ep_in = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = DAP_HID_EP_BULK_IN, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = DAP_HID_EP_SIZE, + .bInterval = DAP_BULK_INTERVAL, + }, + + // CDC + .iad = + { + .bLength = sizeof(struct usb_iad_descriptor), + .bDescriptorType = USB_DTYPE_INTERFASEASSOC, + .bFirstInterface = USB_INTF_CDC_COMM, + .bInterfaceCount = 2, + .bFunctionClass = USB_CLASS_CDC, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_PROTO_NONE, + .iFunction = USB_STR_COM_PORT, + }, + .interface_comm = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = USB_INTF_CDC_COMM, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_CDC, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_PROTO_NONE, + .iInterface = 0, + }, + + .cdc_header = + { + .bFunctionLength = sizeof(struct usb_cdc_header_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_HEADER, + .bcdCDC = VERSION_BCD(1, 1, 0), + }, + + .cdc_acm = + { + .bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT, + // .bmCapabilities = USB_CDC_CAP_LINE | USB_CDC_CAP_BRK, + .bmCapabilities = 0, + }, + + .cdc_call_mgmt = + { + .bFunctionLength = sizeof(struct usb_cdc_acm_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_ACM, + .bmCapabilities = USB_CDC_CALL_MGMT_CAP_DATA_INTF, + // .bDataInterface = USB_INTF_CDC_DATA, + }, + + .cdc_union = + { + .bFunctionLength = sizeof(struct usb_cdc_union_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_UNION, + .bMasterInterface0 = USB_INTF_CDC_COMM, + .bSlaveInterface0 = USB_INTF_CDC_DATA, + }, + + .ep_comm = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = HID_EP_IN | DAP_CDC_EP_COMM, + .bmAttributes = USB_EPTYPE_INTERRUPT, + .wMaxPacketSize = DAP_CDC_COMM_EP_SIZE, + .bInterval = DAP_CDC_COMM_INTERVAL, + }, + + .interface_data = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = USB_INTF_CDC_DATA, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = USB_SUBCLASS_NONE, + .bInterfaceProtocol = USB_PROTO_NONE, + .iInterface = NO_DESCRIPTOR, + }, + + .ep_in = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = HID_EP_IN | DAP_CDC_EP_SEND, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = DAP_CDC_EP_SIZE, + .bInterval = DAP_CDC_INTERVAL, + }, + + .ep_out = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = HID_EP_OUT | DAP_CDC_EP_RECV, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = DAP_CDC_EP_SIZE, + .bInterval = DAP_CDC_INTERVAL, + }, +}; + +// WinUSB +#include "usb_winusb.h" + +typedef struct USB_PACK { + usb_binary_object_store_descriptor_t bos; + usb_winusb_capability_descriptor_t winusb; +} usb_bos_hierarchy_t; + +typedef struct USB_PACK { + usb_winusb_subset_header_function_t header; + usb_winusb_feature_compatble_id_t comp_id; + usb_winusb_feature_reg_property_guids_t property; +} usb_msos_descriptor_subset_t; + +typedef struct USB_PACK { + usb_winusb_set_header_descriptor_t header; + usb_msos_descriptor_subset_t subset; +} usb_msos_descriptor_set_t; + +#define USB_DTYPE_BINARY_OBJECT_STORE 15 +#define USB_DTYPE_DEVICE_CAPABILITY_DESCRIPTOR 16 +#define USB_DC_TYPE_PLATFORM 5 + +const usb_bos_hierarchy_t usb_bos_hierarchy = { + .bos = + { + .bLength = sizeof(usb_binary_object_store_descriptor_t), + .bDescriptorType = USB_DTYPE_BINARY_OBJECT_STORE, + .wTotalLength = sizeof(usb_bos_hierarchy_t), + .bNumDeviceCaps = 1, + }, + .winusb = + { + .bLength = sizeof(usb_winusb_capability_descriptor_t), + .bDescriptorType = USB_DTYPE_DEVICE_CAPABILITY_DESCRIPTOR, + .bDevCapabilityType = USB_DC_TYPE_PLATFORM, + .bReserved = 0, + .PlatformCapabilityUUID = USB_WINUSB_PLATFORM_CAPABILITY_ID, + .dwWindowsVersion = USB_WINUSB_WINDOWS_VERSION, + .wMSOSDescriptorSetTotalLength = sizeof(usb_msos_descriptor_set_t), + .bMS_VendorCode = USB_WINUSB_VENDOR_CODE, + .bAltEnumCode = 0, + }, +}; + +const usb_msos_descriptor_set_t usb_msos_descriptor_set = { + .header = + { + .wLength = sizeof(usb_winusb_set_header_descriptor_t), + .wDescriptorType = USB_WINUSB_SET_HEADER_DESCRIPTOR, + .dwWindowsVersion = USB_WINUSB_WINDOWS_VERSION, + .wDescriptorSetTotalLength = sizeof(usb_msos_descriptor_set_t), + }, + + .subset = + { + .header = + { + .wLength = sizeof(usb_winusb_subset_header_function_t), + .wDescriptorType = USB_WINUSB_SUBSET_HEADER_FUNCTION, + .bFirstInterface = USB_INTF_BULK, + .bReserved = 0, + .wSubsetLength = sizeof(usb_msos_descriptor_subset_t), + }, + + .comp_id = + { + .wLength = sizeof(usb_winusb_feature_compatble_id_t), + .wDescriptorType = USB_WINUSB_FEATURE_COMPATBLE_ID, + .CompatibleID = "WINUSB\0\0", + .SubCompatibleID = {0}, + }, + + .property = + { + .wLength = sizeof(usb_winusb_feature_reg_property_guids_t), + .wDescriptorType = USB_WINUSB_FEATURE_REG_PROPERTY, + .wPropertyDataType = USB_WINUSB_PROPERTY_DATA_TYPE_MULTI_SZ, + .wPropertyNameLength = + sizeof(usb_msos_descriptor_set.subset.property.PropertyName), + .PropertyName = {'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 'I', 0, + 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, + 'e', 0, 'G', 0, 'U', 0, 'I', 0, 'D', 0, 's', 0, 0, 0}, + .wPropertyDataLength = + sizeof(usb_msos_descriptor_set.subset.property.PropertyData), + .PropertyData = {'{', 0, 'C', 0, 'D', 0, 'B', 0, '3', 0, 'B', 0, '5', 0, + 'A', 0, 'D', 0, '-', 0, '2', 0, '9', 0, '3', 0, 'B', 0, + '-', 0, '4', 0, '6', 0, '6', 0, '3', 0, '-', 0, 'A', 0, + 'A', 0, '3', 0, '6', 0, '-', 0, '1', 0, 'A', 0, 'A', 0, + 'E', 0, '4', 0, '6', 0, '4', 0, '6', 0, '3', 0, '7', 0, + '7', 0, '6', 0, '}', 0, 0, 0, 0, 0}, + }, + }, +}; + +typedef struct { + FuriSemaphore* semaphore_v1; + FuriSemaphore* semaphore_v2; + FuriSemaphore* semaphore_cdc; + bool connected; + usbd_device* usb_dev; + DapStateCallback state_callback; + DapRxCallback rx_callback_v1; + DapRxCallback rx_callback_v2; + DapRxCallback rx_callback_cdc; + DapCDCControlLineCallback control_line_callback_cdc; + DapCDCConfigCallback config_callback_cdc; + void* context; + void* context_cdc; +} DAPState; + +static DAPState dap_state = { + .semaphore_v1 = NULL, + .semaphore_v2 = NULL, + .semaphore_cdc = NULL, + .connected = false, + .usb_dev = NULL, + .state_callback = NULL, + .rx_callback_v1 = NULL, + .rx_callback_v2 = NULL, + .rx_callback_cdc = NULL, + .control_line_callback_cdc = NULL, + .config_callback_cdc = NULL, + .context = NULL, + .context_cdc = NULL, +}; + +static struct usb_cdc_line_coding cdc_config = {0}; +static uint8_t cdc_ctrl_line_state = 0; + +#ifdef DAP_USB_LOG +void furi_console_log_printf(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); + +void furi_console_log_printf(const char* format, ...) { + char buffer[256]; + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + furi_hal_console_puts(buffer); + furi_hal_console_puts("\r\n"); + UNUSED(format); +} +#else +#define furi_console_log_printf(...) +#endif + +int32_t dap_v1_usb_tx(uint8_t* buffer, uint8_t size) { + if((dap_state.semaphore_v1 == NULL) || (dap_state.connected == false)) return 0; + + furi_check(furi_semaphore_acquire(dap_state.semaphore_v1, FuriWaitForever) == FuriStatusOk); + + if(dap_state.connected) { + int32_t len = usbd_ep_write(dap_state.usb_dev, DAP_HID_EP_IN, buffer, size); + furi_console_log_printf("v1 tx %ld", len); + return len; + } else { + return 0; + } +} + +int32_t dap_v2_usb_tx(uint8_t* buffer, uint8_t size) { + if((dap_state.semaphore_v2 == NULL) || (dap_state.connected == false)) return 0; + + furi_check(furi_semaphore_acquire(dap_state.semaphore_v2, FuriWaitForever) == FuriStatusOk); + + if(dap_state.connected) { + int32_t len = usbd_ep_write(dap_state.usb_dev, DAP_HID_EP_BULK_IN, buffer, size); + furi_console_log_printf("v2 tx %ld", len); + return len; + } else { + return 0; + } +} + +int32_t dap_cdc_usb_tx(uint8_t* buffer, uint8_t size) { + if((dap_state.semaphore_cdc == NULL) || (dap_state.connected == false)) return 0; + + furi_check(furi_semaphore_acquire(dap_state.semaphore_cdc, FuriWaitForever) == FuriStatusOk); + + if(dap_state.connected) { + int32_t len = usbd_ep_write(dap_state.usb_dev, HID_EP_IN | DAP_CDC_EP_SEND, buffer, size); + furi_console_log_printf("cdc tx %ld", len); + return len; + } else { + return 0; + } +} + +void dap_v1_usb_set_rx_callback(DapRxCallback callback) { + dap_state.rx_callback_v1 = callback; +} + +void dap_v2_usb_set_rx_callback(DapRxCallback callback) { + dap_state.rx_callback_v2 = callback; +} + +void dap_cdc_usb_set_rx_callback(DapRxCallback callback) { + dap_state.rx_callback_cdc = callback; +} + +void dap_cdc_usb_set_control_line_callback(DapCDCControlLineCallback callback) { + dap_state.control_line_callback_cdc = callback; +} + +void dap_cdc_usb_set_config_callback(DapCDCConfigCallback callback) { + dap_state.config_callback_cdc = callback; +} + +void dap_cdc_usb_set_context(void* context) { + dap_state.context_cdc = context; +} + +void dap_common_usb_set_context(void* context) { + dap_state.context = context; +} + +void dap_common_usb_set_state_callback(DapStateCallback callback) { + dap_state.state_callback = callback; +} + +static void* dap_usb_alloc_string_descr(const char* str) { + furi_assert(str); + + uint8_t len = strlen(str); + uint8_t wlen = (len + 1) * sizeof(uint16_t); + struct usb_string_descriptor* dev_str_desc = malloc(wlen); + dev_str_desc->bLength = wlen; + dev_str_desc->bDescriptorType = USB_DTYPE_STRING; + for(uint8_t i = 0; i < len; i++) { + dev_str_desc->wString[i] = str[i]; + } + + return dev_str_desc; +} + +void dap_common_usb_alloc_name(const char* name) { + dev_serial_descr = dap_usb_alloc_string_descr(name); +} + +void dap_common_usb_free_name() { + free(dev_serial_descr); +} + +static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx); +static void hid_deinit(usbd_device* dev); +static void hid_on_wakeup(usbd_device* dev); +static void hid_on_suspend(usbd_device* dev); + +static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg); +static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); + +FuriHalUsbInterface dap_v2_usb_hid = { + .init = hid_init, + .deinit = hid_deinit, + .wakeup = hid_on_wakeup, + .suspend = hid_on_suspend, + .dev_descr = (struct usb_device_descriptor*)&hid_device_desc, + .cfg_descr = (void*)&hid_cfg_desc, +}; + +static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); + UNUSED(ctx); + + dap_v2_usb_hid.str_manuf_descr = (void*)&dev_manuf_descr; + dap_v2_usb_hid.str_prod_descr = (void*)&dev_prod_descr; + dap_v2_usb_hid.str_serial_descr = (void*)dev_serial_descr; + + dap_state.usb_dev = dev; + if(dap_state.semaphore_v1 == NULL) dap_state.semaphore_v1 = furi_semaphore_alloc(1, 1); + if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1); + if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1); + + usb_hid.dev_descr->idVendor = DAP_HID_VID; + usb_hid.dev_descr->idProduct = DAP_HID_PID; + + usbd_reg_config(dev, hid_ep_config); + usbd_reg_control(dev, hid_control); + + usbd_connect(dev, true); +} + +static bool deinit_flag = false; + +void dap_common_wait_for_deinit() { + while(!deinit_flag) { + furi_delay_ms(50); + } +} + +static void hid_deinit(usbd_device* dev) { + dap_state.usb_dev = NULL; + + furi_semaphore_free(dap_state.semaphore_v1); + furi_semaphore_free(dap_state.semaphore_v2); + furi_semaphore_free(dap_state.semaphore_cdc); + dap_state.semaphore_v1 = NULL; + dap_state.semaphore_v2 = NULL; + dap_state.semaphore_cdc = NULL; + + usbd_reg_config(dev, NULL); + usbd_reg_control(dev, NULL); + + free(usb_hid.str_manuf_descr); + free(usb_hid.str_prod_descr); + + FURI_SW_MEMBARRIER(); + deinit_flag = true; +} + +static void hid_on_wakeup(usbd_device* dev) { + UNUSED(dev); + if(!dap_state.connected) { + dap_state.connected = true; + if(dap_state.state_callback != NULL) { + dap_state.state_callback(dap_state.connected, dap_state.context); + } + } +} + +static void hid_on_suspend(usbd_device* dev) { + UNUSED(dev); + if(dap_state.connected) { + dap_state.connected = false; + if(dap_state.state_callback != NULL) { + dap_state.state_callback(dap_state.connected, dap_state.context); + } + } +} + +size_t dap_v1_usb_rx(uint8_t* buffer, size_t size) { + size_t len = 0; + + if(dap_state.connected) { + len = usbd_ep_read(dap_state.usb_dev, DAP_HID_EP_OUT, buffer, size); + } + + return len; +} + +size_t dap_v2_usb_rx(uint8_t* buffer, size_t size) { + size_t len = 0; + + if(dap_state.connected) { + len = usbd_ep_read(dap_state.usb_dev, DAP_HID_EP_BULK_OUT, buffer, size); + } + + return len; +} + +size_t dap_cdc_usb_rx(uint8_t* buffer, size_t size) { + size_t len = 0; + + if(dap_state.connected) { + len = usbd_ep_read(dap_state.usb_dev, HID_EP_OUT | DAP_CDC_EP_RECV, buffer, size); + } + + return len; +} + +static void hid_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(ep); + + switch(event) { + case usbd_evt_eptx: + furi_semaphore_release(dap_state.semaphore_v1); + furi_console_log_printf("hid tx complete"); + break; + case usbd_evt_eprx: + if(dap_state.rx_callback_v1 != NULL) { + dap_state.rx_callback_v1(dap_state.context); + } + break; + default: + furi_console_log_printf("hid %d, %d", event, ep); + break; + } +} + +static void hid_txrx_ep_bulk_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(ep); + + switch(event) { + case usbd_evt_eptx: + furi_semaphore_release(dap_state.semaphore_v2); + furi_console_log_printf("bulk tx complete"); + break; + case usbd_evt_eprx: + if(dap_state.rx_callback_v2 != NULL) { + dap_state.rx_callback_v2(dap_state.context); + } + break; + default: + furi_console_log_printf("bulk %d, %d", event, ep); + break; + } +} + +static void cdc_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(ep); + + switch(event) { + case usbd_evt_eptx: + furi_semaphore_release(dap_state.semaphore_cdc); + furi_console_log_printf("cdc tx complete"); + break; + case usbd_evt_eprx: + if(dap_state.rx_callback_cdc != NULL) { + dap_state.rx_callback_cdc(dap_state.context_cdc); + } + break; + default: + furi_console_log_printf("cdc %d, %d", event, ep); + break; + } +} + +static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) { + switch(cfg) { + case EP_CFG_DECONFIGURE: + usbd_ep_deconfig(dev, DAP_HID_EP_OUT); + usbd_ep_deconfig(dev, DAP_HID_EP_IN); + usbd_ep_deconfig(dev, DAP_HID_EP_BULK_IN); + usbd_ep_deconfig(dev, DAP_HID_EP_BULK_OUT); + usbd_ep_deconfig(dev, HID_EP_IN | DAP_CDC_EP_COMM); + usbd_ep_deconfig(dev, HID_EP_IN | DAP_CDC_EP_SEND); + usbd_ep_deconfig(dev, HID_EP_OUT | DAP_CDC_EP_RECV); + usbd_reg_endpoint(dev, DAP_HID_EP_OUT, NULL); + usbd_reg_endpoint(dev, DAP_HID_EP_IN, NULL); + usbd_reg_endpoint(dev, DAP_HID_EP_BULK_IN, NULL); + usbd_reg_endpoint(dev, DAP_HID_EP_BULK_OUT, NULL); + usbd_reg_endpoint(dev, HID_EP_IN | DAP_CDC_EP_SEND, 0); + usbd_reg_endpoint(dev, HID_EP_OUT | DAP_CDC_EP_RECV, 0); + return usbd_ack; + case EP_CFG_CONFIGURE: + usbd_ep_config(dev, DAP_HID_EP_IN, USB_EPTYPE_INTERRUPT, DAP_HID_EP_SIZE); + usbd_ep_config(dev, DAP_HID_EP_OUT, USB_EPTYPE_INTERRUPT, DAP_HID_EP_SIZE); + usbd_ep_config(dev, DAP_HID_EP_BULK_OUT, USB_EPTYPE_BULK, DAP_HID_EP_SIZE); + usbd_ep_config(dev, DAP_HID_EP_BULK_IN, USB_EPTYPE_BULK, DAP_HID_EP_SIZE); + usbd_ep_config(dev, HID_EP_OUT | DAP_CDC_EP_RECV, USB_EPTYPE_BULK, DAP_CDC_EP_SIZE); + usbd_ep_config(dev, HID_EP_IN | DAP_CDC_EP_SEND, USB_EPTYPE_BULK, DAP_CDC_EP_SIZE); + usbd_ep_config(dev, HID_EP_IN | DAP_CDC_EP_COMM, USB_EPTYPE_INTERRUPT, DAP_CDC_EP_SIZE); + usbd_reg_endpoint(dev, DAP_HID_EP_IN, hid_txrx_ep_callback); + usbd_reg_endpoint(dev, DAP_HID_EP_OUT, hid_txrx_ep_callback); + usbd_reg_endpoint(dev, DAP_HID_EP_BULK_OUT, hid_txrx_ep_bulk_callback); + usbd_reg_endpoint(dev, DAP_HID_EP_BULK_IN, hid_txrx_ep_bulk_callback); + usbd_reg_endpoint(dev, HID_EP_OUT | DAP_CDC_EP_RECV, cdc_txrx_ep_callback); + usbd_reg_endpoint(dev, HID_EP_IN | DAP_CDC_EP_SEND, cdc_txrx_ep_callback); + // usbd_ep_write(dev, DAP_HID_EP_IN, NULL, 0); + // usbd_ep_write(dev, DAP_HID_EP_BULK_IN, NULL, 0); + // usbd_ep_write(dev, HID_EP_IN | DAP_CDC_EP_SEND, NULL, 0); + return usbd_ack; + default: + return usbd_fail; + } +} + +#ifdef DAP_USB_LOG +static void dump_request_type(uint8_t type) { + switch(type & USB_REQ_DIRECTION) { + case USB_REQ_HOSTTODEV: + furi_hal_console_puts("host to dev, "); + break; + case USB_REQ_DEVTOHOST: + furi_hal_console_puts("dev to host, "); + break; + } + + switch(type & USB_REQ_TYPE) { + case USB_REQ_STANDARD: + furi_hal_console_puts("standard, "); + break; + case USB_REQ_CLASS: + furi_hal_console_puts("class, "); + break; + case USB_REQ_VENDOR: + furi_hal_console_puts("vendor, "); + break; + } + + switch(type & USB_REQ_RECIPIENT) { + case USB_REQ_DEVICE: + furi_hal_console_puts("device"); + break; + case USB_REQ_INTERFACE: + furi_hal_console_puts("interface"); + break; + case USB_REQ_ENDPOINT: + furi_hal_console_puts("endpoint"); + break; + case USB_REQ_OTHER: + furi_hal_console_puts("other"); + break; + } + + furi_hal_console_puts("\r\n"); +} +#else +#define dump_request_type(...) +#endif + +static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(callback); + + dump_request_type(req->bmRequestType); + furi_console_log_printf( + "control: RT %02x, R %02x, V %04x, I %04x, L %04x", + req->bmRequestType, + req->bRequest, + req->wValue, + req->wIndex, + req->wLength); + + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE | USB_REQ_DIRECTION) & req->bmRequestType) == + (USB_REQ_STANDARD | USB_REQ_VENDOR | USB_REQ_DEVTOHOST)) { + // vendor request, device to host + furi_console_log_printf("vendor request"); + if(USB_WINUSB_VENDOR_CODE == req->bRequest) { + // WINUSB request + if(USB_WINUSB_DESCRIPTOR_INDEX == req->wIndex) { + furi_console_log_printf("WINUSB descriptor"); + uint16_t length = req->wLength; + if(length > sizeof(usb_msos_descriptor_set_t)) { + length = sizeof(usb_msos_descriptor_set_t); + } + + dev->status.data_ptr = (uint8_t*)&usb_msos_descriptor_set; + dev->status.data_count = length; + return usbd_ack; + } + } + } + + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_STANDARD | USB_REQ_DEVICE)) { + // device request + if(req->bRequest == USB_STD_GET_DESCRIPTOR) { + const uint8_t dtype = req->wValue >> 8; + const uint8_t dnumber = req->wValue & 0xFF; + // get string descriptor + if(USB_DTYPE_STRING == dtype) { + if(dnumber == USB_STR_CMSIS_DAP_V1) { + furi_console_log_printf("str CMSIS-DAP v1"); + dev->status.data_ptr = (uint8_t*)&dev_dap_v1_descr; + dev->status.data_count = dev_dap_v1_descr.bLength; + return usbd_ack; + } else if(dnumber == USB_STR_CMSIS_DAP_V2) { + furi_console_log_printf("str CMSIS-DAP v2"); + dev->status.data_ptr = (uint8_t*)&dev_dap_v2_descr; + dev->status.data_count = dev_dap_v2_descr.bLength; + return usbd_ack; + } else if(dnumber == USB_STR_COM_PORT) { + furi_console_log_printf("str COM port"); + dev->status.data_ptr = (uint8_t*)&dev_com_descr; + dev->status.data_count = dev_com_descr.bLength; + return usbd_ack; + } + } else if(USB_DTYPE_BINARY_OBJECT_STORE == dtype) { + furi_console_log_printf("BOS descriptor"); + uint16_t length = req->wLength; + if(length > sizeof(usb_bos_hierarchy_t)) { + length = sizeof(usb_bos_hierarchy_t); + } + dev->status.data_ptr = (uint8_t*)&usb_bos_hierarchy; + dev->status.data_count = length; + return usbd_ack; + } + } + } + + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_INTERFACE | USB_REQ_CLASS) && + req->wIndex == 0) { + // class request + switch(req->bRequest) { + // get hid descriptor + case USB_HID_GETREPORT: + furi_console_log_printf("get report"); + return usbd_fail; + // set hid idle + case USB_HID_SETIDLE: + furi_console_log_printf("set idle"); + return usbd_ack; + default: + break; + } + } + + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_INTERFACE | USB_REQ_CLASS) && + req->wIndex == 2) { + // class request + switch(req->bRequest) { + // control line state + case USB_CDC_SET_CONTROL_LINE_STATE: + furi_console_log_printf("set control line state"); + cdc_ctrl_line_state = req->wValue; + if(dap_state.control_line_callback_cdc != NULL) { + dap_state.control_line_callback_cdc(cdc_ctrl_line_state, dap_state.context_cdc); + } + return usbd_ack; + // set cdc line coding + case USB_CDC_SET_LINE_CODING: + furi_console_log_printf("set line coding"); + memcpy(&cdc_config, req->data, sizeof(cdc_config)); + if(dap_state.config_callback_cdc != NULL) { + dap_state.config_callback_cdc(&cdc_config, dap_state.context_cdc); + } + return usbd_ack; + // get cdc line coding + case USB_CDC_GET_LINE_CODING: + furi_console_log_printf("get line coding"); + dev->status.data_ptr = &cdc_config; + dev->status.data_count = sizeof(cdc_config); + return usbd_ack; + default: + break; + } + } + + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_INTERFACE | USB_REQ_STANDARD) && + req->wIndex == 0 && req->bRequest == USB_STD_GET_DESCRIPTOR) { + // standard request + switch(req->wValue >> 8) { + // get hid descriptor + case USB_DTYPE_HID: + furi_console_log_printf("get hid descriptor"); + dev->status.data_ptr = (uint8_t*)&(hid_cfg_desc.hid); + dev->status.data_count = sizeof(hid_cfg_desc.hid); + return usbd_ack; + // get hid report descriptor + case USB_DTYPE_HID_REPORT: + furi_console_log_printf("get hid report descriptor"); + dev->status.data_ptr = (uint8_t*)hid_report_desc; + dev->status.data_count = sizeof(hid_report_desc); + return usbd_ack; + default: + break; + } + } + + return usbd_fail; +} \ No newline at end of file diff --git a/applications/plugins/dap_link/usb/dap_v2_usb.h b/applications/plugins/dap_link/usb/dap_v2_usb.h new file mode 100644 index 00000000..2a0e8605 --- /dev/null +++ b/applications/plugins/dap_link/usb/dap_v2_usb.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include + +extern FuriHalUsbInterface dap_v2_usb_hid; + +// receive callback type +typedef void (*DapRxCallback)(void* context); + +typedef void (*DapStateCallback)(bool state, void* context); + +/************************************ V1 ***************************************/ + +int32_t dap_v1_usb_tx(uint8_t* buffer, uint8_t size); + +size_t dap_v1_usb_rx(uint8_t* buffer, size_t size); + +void dap_v1_usb_set_rx_callback(DapRxCallback callback); + +/************************************ V2 ***************************************/ + +int32_t dap_v2_usb_tx(uint8_t* buffer, uint8_t size); + +size_t dap_v2_usb_rx(uint8_t* buffer, size_t size); + +void dap_v2_usb_set_rx_callback(DapRxCallback callback); + +/************************************ CDC **************************************/ + +typedef void (*DapCDCControlLineCallback)(uint8_t state, void* context); +typedef void (*DapCDCConfigCallback)(struct usb_cdc_line_coding* config, void* context); + +int32_t dap_cdc_usb_tx(uint8_t* buffer, uint8_t size); + +size_t dap_cdc_usb_rx(uint8_t* buffer, size_t size); + +void dap_cdc_usb_set_rx_callback(DapRxCallback callback); + +void dap_cdc_usb_set_control_line_callback(DapCDCControlLineCallback callback); + +void dap_cdc_usb_set_config_callback(DapCDCConfigCallback callback); + +void dap_cdc_usb_set_context(void* context); + +/*********************************** Common ************************************/ + +void dap_common_usb_set_context(void* context); + +void dap_common_usb_set_state_callback(DapStateCallback callback); + +void dap_common_usb_alloc_name(const char* name); + +void dap_common_usb_free_name(); + +void dap_common_wait_for_deinit(); \ No newline at end of file diff --git a/applications/plugins/dap_link/usb/usb_winusb.h b/applications/plugins/dap_link/usb/usb_winusb.h new file mode 100644 index 00000000..9c3a172d --- /dev/null +++ b/applications/plugins/dap_link/usb/usb_winusb.h @@ -0,0 +1,143 @@ +#pragma once +#include + +/*- Definitions -------------------------------------------------------------*/ + +#define USB_PACK __attribute__((packed)) + +#define USB_WINUSB_VENDOR_CODE 0x20 + +#define USB_WINUSB_WINDOWS_VERSION 0x06030000 // Windows 8.1 + +#define USB_WINUSB_PLATFORM_CAPABILITY_ID \ + { \ + 0xdf, 0x60, 0xdd, 0xd8, 0x89, 0x45, 0xc7, 0x4c, 0x9c, 0xd2, 0x65, 0x9d, 0x9e, 0x64, 0x8a, \ + 0x9f \ + } + +enum // WinUSB Microsoft OS 2.0 descriptor request codes +{ + USB_WINUSB_DESCRIPTOR_INDEX = 0x07, + USB_WINUSB_SET_ALT_ENUMERATION = 0x08, +}; + +enum // wDescriptorType +{ + USB_WINUSB_SET_HEADER_DESCRIPTOR = 0x00, + USB_WINUSB_SUBSET_HEADER_CONFIGURATION = 0x01, + USB_WINUSB_SUBSET_HEADER_FUNCTION = 0x02, + USB_WINUSB_FEATURE_COMPATBLE_ID = 0x03, + USB_WINUSB_FEATURE_REG_PROPERTY = 0x04, + USB_WINUSB_FEATURE_MIN_RESUME_TIME = 0x05, + USB_WINUSB_FEATURE_MODEL_ID = 0x06, + USB_WINUSB_FEATURE_CCGP_DEVICE = 0x07, + USB_WINUSB_FEATURE_VENDOR_REVISION = 0x08, +}; + +enum // wPropertyDataType +{ + USB_WINUSB_PROPERTY_DATA_TYPE_SZ = 1, + USB_WINUSB_PROPERTY_DATA_TYPE_EXPAND_SZ = 2, + USB_WINUSB_PROPERTY_DATA_TYPE_BINARY = 3, + USB_WINUSB_PROPERTY_DATA_TYPE_DWORD_LITTLE_ENDIAN = 4, + USB_WINUSB_PROPERTY_DATA_TYPE_DWORD_BIG_ENDIAN = 5, + USB_WINUSB_PROPERTY_DATA_TYPE_LINK = 6, + USB_WINUSB_PROPERTY_DATA_TYPE_MULTI_SZ = 7, +}; + +/*- Types BOS -------------------------------------------------------------------*/ + +typedef struct USB_PACK { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumDeviceCaps; +} usb_binary_object_store_descriptor_t; + +/*- Types WinUSB -------------------------------------------------------------------*/ + +typedef struct USB_PACK { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint8_t bReserved; + uint8_t PlatformCapabilityUUID[16]; + uint32_t dwWindowsVersion; + uint16_t wMSOSDescriptorSetTotalLength; + uint8_t bMS_VendorCode; + uint8_t bAltEnumCode; +} usb_winusb_capability_descriptor_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint32_t dwWindowsVersion; + uint16_t wDescriptorSetTotalLength; +} usb_winusb_set_header_descriptor_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t bConfigurationValue; + uint8_t bReserved; + uint16_t wTotalLength; +} usb_winusb_subset_header_configuration_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t bFirstInterface; + uint8_t bReserved; + uint16_t wSubsetLength; +} usb_winusb_subset_header_function_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t CompatibleID[8]; + uint8_t SubCompatibleID[8]; +} usb_winusb_feature_compatble_id_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint16_t wPropertyDataType; + //uint16_t wPropertyNameLength; + //uint8_t PropertyName[...]; + //uint16_t wPropertyDataLength + //uint8_t PropertyData[...]; +} usb_winusb_feature_reg_property_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint16_t wPropertyDataType; + uint16_t wPropertyNameLength; + uint8_t PropertyName[42]; + uint16_t wPropertyDataLength; + uint8_t PropertyData[80]; +} usb_winusb_feature_reg_property_guids_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t bResumeRecoveryTime; + uint8_t bResumeSignalingTime; +} usb_winusb_feature_min_resume_time_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t ModelID[16]; +} usb_winusb_feature_model_id_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; +} usb_winusb_feature_ccgp_device_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint16_t VendorRevision; +} usb_winusb_feature_vendor_revision_t; \ No newline at end of file From 33892ebfb790c46e7f63aa6dd33e83e9159b8946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Fri, 21 Oct 2022 02:14:46 +0900 Subject: [PATCH 15/42] [FL-2818] FuriHal: add FuriHalCortexTimer, use it for i2c bus timeouts (#1900) * FuriHal: add FuriHalCortexTimer, use it for i2c bus timeouts * Furi: cleanup FuriHalCortexTimer sources and headers --- firmware/targets/f7/api_symbols.csv | 5 +++- .../targets/f7/furi_hal/furi_hal_cortex.c | 21 ++++++++++++-- firmware/targets/f7/furi_hal/furi_hal_i2c.c | 29 ++++++++++--------- .../furi_hal_include/furi_hal_cortex.h | 29 +++++++++++++++++++ 4 files changed, 67 insertions(+), 17 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 6aa8591e..89f44715 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,3.4,, +Version,+,3.5,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -985,6 +985,9 @@ Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" Function,+,furi_hal_cortex_delay_us,void,uint32_t Function,-,furi_hal_cortex_init_early,void, Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, +Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t +Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer +Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,-,furi_hal_crypto_init,void, diff --git a/firmware/targets/f7/furi_hal/furi_hal_cortex.c b/firmware/targets/f7/furi_hal/furi_hal_cortex.c index c9c8400a..c2abd1b8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_cortex.c +++ b/firmware/targets/f7/furi_hal/furi_hal_cortex.c @@ -2,6 +2,8 @@ #include +#define FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND (SystemCoreClock / 1000000) + void furi_hal_cortex_init_early() { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; @@ -13,11 +15,26 @@ void furi_hal_cortex_init_early() { void furi_hal_cortex_delay_us(uint32_t microseconds) { uint32_t start = DWT->CYCCNT; - uint32_t time_ticks = SystemCoreClock / 1000000 * microseconds; + uint32_t time_ticks = FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND * microseconds; while((DWT->CYCCNT - start) < time_ticks) { }; } uint32_t furi_hal_cortex_instructions_per_microsecond() { - return SystemCoreClock / 1000000; + return FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND; } + +FuriHalCortexTimer furi_hal_cortex_timer_get(uint32_t timeout_us) { + FuriHalCortexTimer cortex_timer = {0}; + cortex_timer.start = DWT->CYCCNT; + cortex_timer.value = FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND * timeout_us; + return cortex_timer; +} + +bool furi_hal_cortex_timer_is_expired(FuriHalCortexTimer cortex_timer) { + return !((DWT->CYCCNT - cortex_timer.start) < cortex_timer.value); +} + +void furi_hal_cortex_timer_wait(FuriHalCortexTimer cortex_timer) { + while(!furi_hal_cortex_timer_is_expired(cortex_timer)); +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c.c b/firmware/targets/f7/furi_hal/furi_hal_i2c.c index 36f5230c..6c17d6ad 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_i2c.c +++ b/firmware/targets/f7/furi_hal/furi_hal_i2c.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -60,11 +61,11 @@ bool furi_hal_i2c_tx( furi_assert(timeout > 0); bool ret = true; - uint32_t timeout_tick = furi_get_tick() + timeout; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); do { while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { ret = false; break; } @@ -89,7 +90,7 @@ bool furi_hal_i2c_tx( size--; } - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { ret = false; break; } @@ -111,11 +112,11 @@ bool furi_hal_i2c_rx( furi_assert(timeout > 0); bool ret = true; - uint32_t timeout_tick = furi_get_tick() + timeout; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); do { while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { ret = false; break; } @@ -140,7 +141,7 @@ bool furi_hal_i2c_rx( size--; } - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { ret = false; break; } @@ -175,11 +176,11 @@ bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, furi_assert(timeout > 0); bool ret = true; - uint32_t timeout_tick = furi_get_tick() + timeout; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); do { while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } @@ -190,14 +191,14 @@ bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, while((!LL_I2C_IsActiveFlag_NACK(handle->bus->i2c)) && (!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c))) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } if(LL_I2C_IsActiveFlag_NACK(handle->bus->i2c)) { while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } @@ -214,7 +215,7 @@ bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, } while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } @@ -308,11 +309,11 @@ bool furi_hal_i2c_write_mem( bool ret = true; uint8_t size = len + 1; - uint32_t timeout_tick = furi_get_tick() + timeout; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); do { while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { ret = false; break; } @@ -341,7 +342,7 @@ bool furi_hal_i2c_write_mem( size--; } - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { ret = false; break; } diff --git a/firmware/targets/furi_hal_include/furi_hal_cortex.h b/firmware/targets/furi_hal_include/furi_hal_cortex.h index 13035161..91596ffe 100644 --- a/firmware/targets/furi_hal_include/furi_hal_cortex.h +++ b/firmware/targets/furi_hal_include/furi_hal_cortex.h @@ -6,11 +6,18 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { #endif +/** Cortex timer provides high precision low level expiring timer */ +typedef struct { + uint32_t start; + uint32_t value; +} FuriHalCortexTimer; + /** Early init stage for cortex */ void furi_hal_cortex_init_early(); @@ -27,6 +34,28 @@ void furi_hal_cortex_delay_us(uint32_t microseconds); */ uint32_t furi_hal_cortex_instructions_per_microsecond(); +/** Get Timer + * + * @param[in] timeout_us The expire timeout in us + * + * @return The FuriHalCortexTimer + */ +FuriHalCortexTimer furi_hal_cortex_timer_get(uint32_t timeout_us); + +/** Check if timer expired + * + * @param[in] cortex_timer The FuriHalCortexTimer + * + * @return true if expired + */ +bool furi_hal_cortex_timer_is_expired(FuriHalCortexTimer cortex_timer); + +/** Wait for timer expire + * + * @param[in] cortex_timer The FuriHalCortexTimer + */ +void furi_hal_cortex_timer_wait(FuriHalCortexTimer cortex_timer); + #ifdef __cplusplus } #endif From c1bb10a6947f56e7cb71912b864b6d2b969f6155 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Thu, 20 Oct 2022 21:26:28 +0400 Subject: [PATCH 16/42] [FL-2920] WS: add protocol Acurite-606TX, LaCrosse_TX141THBv2 (#1898) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WS: add protocol Acurite-606TX * WS: history, added display of the channel (if any) in the general list * WS: added display of the button state if it is on the transmitter, and displaying the data that is in the signal * WS: fix batt info * WS: add protocol LaCrosse_TX141THBv2 * WS; fix syntax * Furi: bump api_symbols version Co-authored-by: あく --- .../helpers/weather_station_types.h | 2 +- .../weather_station/protocols/acurite_606tx.c | 252 +++++++++++++++ .../weather_station/protocols/acurite_606tx.h | 79 +++++ .../weather_station/protocols/infactory.c | 1 + .../protocols/lacrosse_tx141thbv2.c | 298 ++++++++++++++++++ .../protocols/lacrosse_tx141thbv2.h | 81 +++++ .../weather_station/protocols/nexus_th.c | 2 +- .../protocols/protocol_items.c | 2 + .../protocols/protocol_items.h | 2 + .../weather_station/protocols/ws_generic.c | 20 +- .../weather_station/protocols/ws_generic.h | 7 + .../views/weather_station_receiver_info.c | 53 ++-- .../weather_station/weather_station_history.c | 25 +- firmware/targets/f7/api_symbols.csv | 8 +- lib/subghz/blocks/math.c | 137 ++++++++ lib/subghz/blocks/math.h | 97 +++++- 16 files changed, 1016 insertions(+), 50 deletions(-) create mode 100644 applications/plugins/weather_station/protocols/acurite_606tx.c create mode 100644 applications/plugins/weather_station/protocols/acurite_606tx.h create mode 100644 applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c create mode 100644 applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h index a6905e82..275d2332 100644 --- a/applications/plugins/weather_station/helpers/weather_station_types.h +++ b/applications/plugins/weather_station/helpers/weather_station_types.h @@ -3,7 +3,7 @@ #include #include -#define WS_VERSION_APP "0.1" +#define WS_VERSION_APP "0.2" #define WS_DEVELOPED "SkorP" #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.c b/applications/plugins/weather_station/protocols/acurite_606tx.c new file mode 100644 index 00000000..a92ec243 --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_606tx.c @@ -0,0 +1,252 @@ +#include "acurite_606tx.h" + +#define TAG "WSProtocolAcurite_606TX" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c#L1644 + * + * 0000 1111 | 0011 0000 | 0101 1100 | 1110 0111 + * iiii iiii | buuu tttt | tttt tttt | cccc cccc + * - i: identification; changes on battery switch + * - c: lfsr_digest8; + * - u: unknown; + * - b: battery low; flag to indicate low battery voltage + * - t: Temperature; in °C + * + */ + +static const SubGhzBlockConst ws_protocol_acurite_606tx_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 32, +}; + +struct WSProtocolDecoderAcurite_606TX { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderAcurite_606TX { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + Acurite_606TXDecoderStepReset = 0, + Acurite_606TXDecoderStepSaveDuration, + Acurite_606TXDecoderStepCheckDuration, +} Acurite_606TXDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_acurite_606tx_decoder = { + .alloc = ws_protocol_decoder_acurite_606tx_alloc, + .free = ws_protocol_decoder_acurite_606tx_free, + + .feed = ws_protocol_decoder_acurite_606tx_feed, + .reset = ws_protocol_decoder_acurite_606tx_reset, + + .get_hash_data = ws_protocol_decoder_acurite_606tx_get_hash_data, + .serialize = ws_protocol_decoder_acurite_606tx_serialize, + .deserialize = ws_protocol_decoder_acurite_606tx_deserialize, + .get_string = ws_protocol_decoder_acurite_606tx_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_acurite_606tx_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_acurite_606tx = { + .name = WS_PROTOCOL_ACURITE_606TX_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_acurite_606tx_decoder, + .encoder = &ws_protocol_acurite_606tx_encoder, +}; + +void* ws_protocol_decoder_acurite_606tx_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderAcurite_606TX* instance = malloc(sizeof(WSProtocolDecoderAcurite_606TX)); + instance->base.protocol = &ws_protocol_acurite_606tx; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_acurite_606tx_free(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* instance = context; + free(instance); +} + +void ws_protocol_decoder_acurite_606tx_reset(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* instance = context; + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; +} + +static bool ws_protocol_acurite_606tx_check(WSProtocolDecoderAcurite_606TX* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t msg[] = { + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8}; + + uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 3, 0x98, 0xF1); + return (crc == (instance->decoder.decode_data & 0xFF)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_acurite_606tx_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 24) & 0xFF; + instance->battery_low = (instance->data >> 23) & 1; + + instance->channel = WS_NO_CHANNEL; + + if(!((instance->data >> 19) & 1)) { + instance->temp = (float)((instance->data >> 8) & 0x07FF) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 8) & 0x07FF) + 1) / -10.0f; + } + instance->btn = WS_NO_BTN; + instance->humidity = WS_NO_HUMIDITY; +} + +void ws_protocol_decoder_acurite_606tx_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* instance = context; + + switch(instance->decoder.parser_step) { + case Acurite_606TXDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_short * 17) < + ws_protocol_acurite_606tx_const.te_delta * 8)) { + //Found syncPrefix + instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case Acurite_606TXDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Acurite_606TXDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; + } + break; + + case Acurite_606TXDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_606tx_const.te_short) < + ws_protocol_acurite_606tx_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_short) < + ws_protocol_acurite_606tx_const.te_delta)) { + //Found syncPostfix + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_acurite_606tx_const.min_count_bit_for_found) && + ws_protocol_acurite_606tx_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_acurite_606tx_remote_controller(&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; + + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_acurite_606tx_const.te_short) < + ws_protocol_acurite_606tx_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_long) < + ws_protocol_acurite_606tx_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_acurite_606tx_const.te_short) < + ws_protocol_acurite_606tx_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_long * 2) < + ws_protocol_acurite_606tx_const.te_delta * 4)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; + } + } else { + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_acurite_606tx_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_acurite_606tx_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_acurite_606tx_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_acurite_606tx_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%d.%d C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (int16_t)instance->generic.temp, + abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.h b/applications/plugins/weather_station/protocols/acurite_606tx.h new file mode 100644 index 00000000..5bab3bcb --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_606tx.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_ACURITE_606TX_NAME "Acurite-606TX" + +typedef struct WSProtocolDecoderAcurite_606TX WSProtocolDecoderAcurite_606TX; +typedef struct WSProtocolEncoderAcurite_606TX WSProtocolEncoderAcurite_606TX; + +extern const SubGhzProtocolDecoder ws_protocol_acurite_606tx_decoder; +extern const SubGhzProtocolEncoder ws_protocol_acurite_606tx_encoder; +extern const SubGhzProtocol ws_protocol_acurite_606tx; + +/** + * Allocate WSProtocolDecoderAcurite_606TX. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderAcurite_606TX* pointer to a WSProtocolDecoderAcurite_606TX instance + */ +void* ws_protocol_decoder_acurite_606tx_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderAcurite_606TX. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + */ +void ws_protocol_decoder_acurite_606tx_free(void* context); + +/** + * Reset decoder WSProtocolDecoderAcurite_606TX. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + */ +void ws_protocol_decoder_acurite_606tx_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_acurite_606tx_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_acurite_606tx_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderAcurite_606TX. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_acurite_606tx_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderAcurite_606TX. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_acurite_606tx_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + * @param output Resulting text + */ +void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/infactory.c b/applications/plugins/weather_station/protocols/infactory.c index 525b5557..b08a4e9d 100644 --- a/applications/plugins/weather_station/protocols/infactory.c +++ b/applications/plugins/weather_station/protocols/infactory.c @@ -142,6 +142,7 @@ static bool ws_protocol_infactory_check_crc(WSProtocolDecoderInfactory* instance static void ws_protocol_infactory_remote_controller(WSBlockGeneric* instance) { instance->id = instance->data >> 32; instance->battery_low = (instance->data >> 26) & 1; + instance->btn = WS_NO_BTN; instance->temp = ws_block_generic_fahrenheit_to_celsius( ((float)((instance->data >> 12) & 0x0FFF) - 900.0f) / 10.0f); instance->humidity = diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c new file mode 100644 index 00000000..828d49be --- /dev/null +++ b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c @@ -0,0 +1,298 @@ +#include "lacrosse_tx141thbv2.h" + +#define TAG "WSProtocolLaCrosse_TX141THBv2" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/7e83cfd27d14247b6c3c81732bfe4a4f9a974d30/src/devices/lacrosse_tx141x.c + * + * iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | u + * - i: identification; changes on battery switch + * - c: lfsr_digest8_reflect; + * - u: unknown; + * - b: battery low; flag to indicate low battery voltage + * - h: Humidity; + * - t: Temperature; in °F as binary number with one decimal place + 50 °F offset + * - n: Channel; Channel number 1 - 3 + */ + +static const SubGhzBlockConst ws_protocol_lacrosse_tx141thbv2_const = { + .te_short = 250, + .te_long = 500, + .te_delta = 120, + .min_count_bit_for_found = 41, +}; + +struct WSProtocolDecoderLaCrosse_TX141THBv2 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderLaCrosse_TX141THBv2 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + LaCrosse_TX141THBv2DecoderStepReset = 0, + LaCrosse_TX141THBv2DecoderStepCheckPreambule, + LaCrosse_TX141THBv2DecoderStepSaveDuration, + LaCrosse_TX141THBv2DecoderStepCheckDuration, +} LaCrosse_TX141THBv2DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_lacrosse_tx141thbv2_decoder = { + .alloc = ws_protocol_decoder_lacrosse_tx141thbv2_alloc, + .free = ws_protocol_decoder_lacrosse_tx141thbv2_free, + + .feed = ws_protocol_decoder_lacrosse_tx141thbv2_feed, + .reset = ws_protocol_decoder_lacrosse_tx141thbv2_reset, + + .get_hash_data = ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data, + .serialize = ws_protocol_decoder_lacrosse_tx141thbv2_serialize, + .deserialize = ws_protocol_decoder_lacrosse_tx141thbv2_deserialize, + .get_string = ws_protocol_decoder_lacrosse_tx141thbv2_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_lacrosse_tx141thbv2_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_lacrosse_tx141thbv2 = { + .name = WS_PROTOCOL_LACROSSE_TX141THBV2_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_lacrosse_tx141thbv2_decoder, + .encoder = &ws_protocol_lacrosse_tx141thbv2_encoder, +}; + +void* ws_protocol_decoder_lacrosse_tx141thbv2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = + malloc(sizeof(WSProtocolDecoderLaCrosse_TX141THBv2)); + instance->base.protocol = &ws_protocol_lacrosse_tx141thbv2; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_lacrosse_tx141thbv2_free(void* context) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + free(instance); +} + +void ws_protocol_decoder_lacrosse_tx141thbv2_reset(void* context) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; +} + +static bool + ws_protocol_lacrosse_tx141thbv2_check_crc(WSProtocolDecoderLaCrosse_TX141THBv2* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t msg[] = { + instance->decoder.decode_data >> 33, + instance->decoder.decode_data >> 25, + instance->decoder.decode_data >> 17, + instance->decoder.decode_data >> 9}; + + uint8_t crc = subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4); + return (crc == ((instance->decoder.decode_data >> 1) & 0xFF)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_lacrosse_tx141thbv2_remote_controller(WSBlockGeneric* instance) { + instance->id = instance->data >> 33; + instance->battery_low = (instance->data >> 32) & 1; + instance->btn = (instance->data >> 31) & 1; + instance->channel = ((instance->data >> 29) & 0x03) + 1; + instance->temp = ((float)((instance->data >> 17) & 0x0FFF) - 500.0f) / 10.0f; + instance->humidity = (instance->data >> 9) & 0xFF; +} + +void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + + switch(instance->decoder.parser_step) { + case LaCrosse_TX141THBv2DecoderStepReset: + if((level) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) { + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case LaCrosse_TX141THBv2DecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF( + instance->decoder.te_last, + ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) { + //Found preambule + instance->header_count++; + } else if(instance->header_count == 4) { + if((DURATION_DIFF( + instance->decoder.te_last, + ws_protocol_lacrosse_tx141thbv2_const.te_short) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_long) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, + ws_protocol_lacrosse_tx141thbv2_const.te_long) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; + } + } else { + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; + } + } + break; + + case LaCrosse_TX141THBv2DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; + } + break; + + case LaCrosse_TX141THBv2DecoderStepCheckDuration: + if(!level) { + if(((DURATION_DIFF( + instance->decoder.te_last, + ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2))) { + if((instance->decoder.decode_count_bit == + ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) && + ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_lacrosse_tx141thbv2_remote_controller(&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->header_count = 1; + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_short) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_long) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_long) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; + } + } else { + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_lacrosse_tx141thbv2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_lacrosse_tx141thbv2_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%d.%d C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (int16_t)instance->generic.temp, + abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h new file mode 100644 index 00000000..941b0105 --- /dev/null +++ b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_LACROSSE_TX141THBV2_NAME "TX141THBv2" + +typedef struct WSProtocolDecoderLaCrosse_TX141THBv2 WSProtocolDecoderLaCrosse_TX141THBv2; +typedef struct WSProtocolEncoderLaCrosse_TX141THBv2 WSProtocolEncoderLaCrosse_TX141THBv2; + +extern const SubGhzProtocolDecoder ws_protocol_lacrosse_tx141thbv2_decoder; +extern const SubGhzProtocolEncoder ws_protocol_lacrosse_tx141thbv2_encoder; +extern const SubGhzProtocol ws_protocol_lacrosse_tx141thbv2; + +/** + * Allocate WSProtocolDecoderLaCrosse_TX141THBv2. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderLaCrosse_TX141THBv2* pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + */ +void* ws_protocol_decoder_lacrosse_tx141thbv2_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderLaCrosse_TX141THBv2. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + */ +void ws_protocol_decoder_lacrosse_tx141thbv2_free(void* context); + +/** + * Reset decoder WSProtocolDecoderLaCrosse_TX141THBv2. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + */ +void ws_protocol_decoder_lacrosse_tx141thbv2_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderLaCrosse_TX141THBv2. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_lacrosse_tx141thbv2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderLaCrosse_TX141THBv2. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_lacrosse_tx141thbv2_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + * @param output Resulting text + */ +void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/nexus_th.c b/applications/plugins/weather_station/protocols/nexus_th.c index e40f3348..a2ab0f41 100644 --- a/applications/plugins/weather_station/protocols/nexus_th.c +++ b/applications/plugins/weather_station/protocols/nexus_th.c @@ -127,7 +127,7 @@ static void ws_protocol_nexus_th_remote_controller(WSBlockGeneric* instance) { instance->id = (instance->data >> 28) & 0xFF; instance->battery_low = !((instance->data >> 27) & 1); instance->channel = ((instance->data >> 24) & 0x03) + 1; - + instance->btn = WS_NO_BTN; if(!((instance->data >> 23) & 1)) { instance->temp = (float)((instance->data >> 12) & 0x07FF) / 10.0f; } else { diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/plugins/weather_station/protocols/protocol_items.c index ea02ec05..5b753994 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.c +++ b/applications/plugins/weather_station/protocols/protocol_items.c @@ -5,6 +5,8 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_thermopro_tx4, &ws_protocol_nexus_th, &ws_protocol_gt_wt_03, + &ws_protocol_acurite_606tx, + &ws_protocol_lacrosse_tx141thbv2, }; const SubGhzProtocolRegistry weather_station_protocol_registry = { diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/plugins/weather_station/protocols/protocol_items.h index eac28a23..c4f4ecd9 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.h +++ b/applications/plugins/weather_station/protocols/protocol_items.h @@ -5,5 +5,7 @@ #include "thermopro_tx4.h" #include "nexus_th.h" #include "gt_wt_03.h" +#include "acurite_606tx.h" +#include "lacrosse_tx141thbv2.h" extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/applications/plugins/weather_station/protocols/ws_generic.c b/applications/plugins/weather_station/protocols/ws_generic.c index 5740750a..17453109 100644 --- a/applications/plugins/weather_station/protocols/ws_generic.c +++ b/applications/plugins/weather_station/protocols/ws_generic.c @@ -105,11 +105,11 @@ bool ws_block_generic_serialize( break; } - // temp_data = instance->btn; - // if(!flipper_format_write_uint32(flipper_format, "Btn", &temp_data, 1)) { - // FURI_LOG_E(TAG, "Unable to add Btn"); - // break; - // } + temp_data = instance->btn; + if(!flipper_format_write_uint32(flipper_format, "Btn", &temp_data, 1)) { + FURI_LOG_E(TAG, "Unable to add Btn"); + break; + } float temp = instance->temp; if(!flipper_format_write_float(flipper_format, "Temp", &temp, 1)) { @@ -174,11 +174,11 @@ bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipp } instance->channel = (uint8_t)temp_data; - // if(!flipper_format_read_uint32(flipper_format, "Btn", (uint32_t*)&temp_data, 1)) { - // FURI_LOG_E(TAG, "Missing Btn"); - // break; - // } - // instance->btn = (uint8_t)temp_data; + if(!flipper_format_read_uint32(flipper_format, "Btn", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Btn"); + break; + } + instance->btn = (uint8_t)temp_data; float temp; if(!flipper_format_read_float(flipper_format, "Temp", (float*)&temp, 1)) { diff --git a/applications/plugins/weather_station/protocols/ws_generic.h b/applications/plugins/weather_station/protocols/ws_generic.h index e0402b81..b2a84df8 100644 --- a/applications/plugins/weather_station/protocols/ws_generic.h +++ b/applications/plugins/weather_station/protocols/ws_generic.h @@ -13,6 +13,13 @@ extern "C" { #endif +#define WS_NO_ID 0xFFFFFFFF +#define WS_NO_BATT 0xFF +#define WS_NO_HUMIDITY 0xFF +#define WS_NO_CHANNEL 0xFF +#define WS_NO_BTN 0xFF +#define WS_NO_TEMPERATURE -273.0f + typedef struct WSBlockGeneric WSBlockGeneric; struct WSBlockGeneric { diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.c b/applications/plugins/weather_station/views/weather_station_receiver_info.c index 31870627..34ec122d 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver_info.c +++ b/applications/plugins/weather_station/views/weather_station_receiver_info.c @@ -4,7 +4,6 @@ #include "../protocols/ws_generic.h" #include #include -#include "math.h" #define abs(x) ((x) > 0 ? (x) : -(x)) @@ -47,14 +46,26 @@ void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) { model->generic->data_count_bit); canvas_draw_str(canvas, 5, 8, buffer); - snprintf(buffer, sizeof(buffer), "Ch: %01d", model->generic->channel); - canvas_draw_str(canvas, 105, 8, buffer); + if(model->generic->channel != WS_NO_CHANNEL) { + snprintf(buffer, sizeof(buffer), "Ch: %01d", model->generic->channel); + canvas_draw_str(canvas, 105, 8, buffer); + } - snprintf(buffer, sizeof(buffer), "Sn: 0x%02lX", model->generic->id); - canvas_draw_str(canvas, 5, 20, buffer); + if(model->generic->id != WS_NO_ID) { + snprintf(buffer, sizeof(buffer), "Sn: 0x%02lX", model->generic->id); + canvas_draw_str(canvas, 5, 20, buffer); + } - snprintf(buffer, sizeof(buffer), "Batt: %s", (!model->generic->battery_low ? "ok" : "low")); - canvas_draw_str(canvas, 85, 20, buffer); + if(model->generic->btn != WS_NO_BTN) { + snprintf(buffer, sizeof(buffer), "Btn: %01d", model->generic->btn); + canvas_draw_str(canvas, 62, 20, buffer); + } + + if(model->generic->battery_low != WS_NO_BATT) { + snprintf( + buffer, sizeof(buffer), "Batt: %s", (!model->generic->battery_low ? "ok" : "low")); + canvas_draw_str(canvas, 90, 20, buffer); + } snprintf(buffer, sizeof(buffer), "Data: 0x%llX", model->generic->data); canvas_draw_str(canvas, 5, 32, buffer); @@ -62,19 +73,23 @@ void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) { elements_bold_rounded_frame(canvas, 2, 37, 123, 25); canvas_set_font(canvas, FontPrimary); - canvas_draw_icon(canvas, 13 + 5, 42, &I_Therm_7x16); - snprintf( - buffer, - sizeof(buffer), - "%3.2d.%d C", - (int16_t)model->generic->temp, - abs(((int16_t)(model->generic->temp * 10) - (((int16_t)model->generic->temp) * 10)))); - canvas_draw_str_aligned(canvas, 58 + 5, 46, AlignRight, AlignTop, buffer); - canvas_draw_circle(canvas, 50 + 5, 45, 1); + if(model->generic->temp != WS_NO_TEMPERATURE) { + canvas_draw_icon(canvas, 18, 42, &I_Therm_7x16); + snprintf( + buffer, + sizeof(buffer), + "%3.2d.%d C", + (int16_t)model->generic->temp, + abs(((int16_t)(model->generic->temp * 10) - (((int16_t)model->generic->temp) * 10)))); + canvas_draw_str_aligned(canvas, 63, 46, AlignRight, AlignTop, buffer); + canvas_draw_circle(canvas, 55, 45, 1); + } - canvas_draw_icon(canvas, 70 + 5, 42, &I_Humid_10x15); - snprintf(buffer, sizeof(buffer), "%d%%", model->generic->humidity); - canvas_draw_str(canvas, 86 + 5, 54, buffer); + if(model->generic->humidity != WS_NO_HUMIDITY) { + canvas_draw_icon(canvas, 75, 42, &I_Humid_10x15); + snprintf(buffer, sizeof(buffer), "%d%%", model->generic->humidity); + canvas_draw_str(canvas, 91, 54, buffer); + } } bool ws_view_receiver_info_input(InputEvent* event, void* context) { diff --git a/applications/plugins/weather_station/weather_station_history.c b/applications/plugins/weather_station/weather_station_history.c index 2eddf6cd..b37009c4 100644 --- a/applications/plugins/weather_station/weather_station_history.c +++ b/applications/plugins/weather_station/weather_station_history.c @@ -2,6 +2,7 @@ #include #include #include +#include "protocols/ws_generic.h" #include @@ -224,20 +225,18 @@ WSHistoryStateAddKey for(uint8_t i = 0; i < sizeof(uint64_t); i++) { data = (data << 8) | key_data[i]; } - if(!(uint32_t)(data >> 32)) { - furi_string_printf( - item->item_str, - "%s %lX", - furi_string_get_cstr(instance->tmp_string), - (uint32_t)(data & 0xFFFFFFFF)); - } else { - furi_string_printf( - item->item_str, - "%s %lX%08lX", - furi_string_get_cstr(instance->tmp_string), - (uint32_t)(data >> 32), - (uint32_t)(data & 0xFFFFFFFF)); + uint32_t temp_data = 0; + if(!flipper_format_read_uint32(item->flipper_string, "Ch", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Channel"); + break; } + if(temp_data != WS_NO_CHANNEL) { + furi_string_cat_printf(instance->tmp_string, " Ch:%X", (uint8_t)temp_data); + } + + furi_string_printf( + item->item_str, "%s %llX", furi_string_get_cstr(instance->tmp_string), data); + } while(false); instance->last_index_write++; return WSHistoryStateAddKeyNewDada; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 89f44715..d0dfcbfa 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,3.5,, +Version,+,3.6,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2271,13 +2271,19 @@ Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, u Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" +Function,+,subghz_protocol_blocks_crc16,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" +Function,+,subghz_protocol_blocks_crc16lsb,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" Function,+,subghz_protocol_blocks_crc4,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_crc7,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_crc8,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_crc8le,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t" Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t" Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t" +Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" +Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], int, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,-,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" diff --git a/lib/subghz/blocks/math.c b/lib/subghz/blocks/math.c index 782076e3..588d4eff 100644 --- a/lib/subghz/blocks/math.c +++ b/lib/subghz/blocks/math.c @@ -79,4 +79,141 @@ uint8_t subghz_protocol_blocks_crc8( } } return remainder; +} + +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + unsigned nBytes, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8); + unsigned byte, bit; + polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8); + + for(byte = 0; byte < nBytes; ++byte) { + remainder ^= message[byte]; + for(bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + unsigned nBytes, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + unsigned byte, bit; + + for(byte = 0; byte < nBytes; ++byte) { + remainder ^= message[byte]; + for(bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + unsigned nBytes, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + unsigned byte, bit; + + for(byte = 0; byte < nBytes; ++byte) { + remainder ^= message[byte] << 8; + for(bit = 0; bit < 8; ++bit) { + if(remainder & 0x8000) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + unsigned bytes, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + for(unsigned k = 0; k < bytes; ++k) { + uint8_t data = message[k]; + for(int i = 7; i >= 0; --i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) sum ^= key; + + // roll the key right (actually the lsb is dropped here) + // and apply the gen (needs to include the dropped lsb as msb) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + int bytes, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + // Process message from last byte to first byte (reflected) + for(int k = bytes - 1; k >= 0; --k) { + uint8_t data = message[k]; + // Process individual bits of each byte (reflected) + for(int i = 0; i < 8; ++i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) { + sum ^= key; + } + + // roll the key left (actually the lsb is dropped here) + // and apply the gen (needs to include the dropped lsb as msb) + if(key & 0x80) + key = (key << 1) ^ gen; + else + key = (key << 1); + } + } + return sum; +} + +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + unsigned bytes, + uint16_t gen, + uint16_t key) { + uint16_t sum = 0; + for(unsigned k = 0; k < bytes; ++k) { + uint8_t data = message[k]; + for(int i = 7; i >= 0; --i) { + // if data bit is set then xor with key + if((data >> i) & 1) sum ^= key; + + // roll the key right (actually the lsb is dropped here) + // and apply the gen (needs to include the dropped lsb as msb) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; } \ No newline at end of file diff --git a/lib/subghz/blocks/math.h b/lib/subghz/blocks/math.h index c312b607..27588948 100644 --- a/lib/subghz/blocks/math.h +++ b/lib/subghz/blocks/math.h @@ -19,7 +19,7 @@ extern "C" { * @param key In data * @param count_bit number of data bits * @return Reverse data - */ + **/ uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit); /** @@ -27,7 +27,7 @@ uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit); * @param key In data * @param count_bit number of data bits * @return parity - */ + **/ uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit); /** @@ -37,7 +37,7 @@ uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit); * @param polynomial CRC polynomial * @param init starting crc value * @return CRC value - */ + **/ uint8_t subghz_protocol_blocks_crc4( uint8_t const message[], unsigned nBytes, @@ -51,7 +51,7 @@ uint8_t subghz_protocol_blocks_crc4( * @param polynomial CRC polynomial * @param init starting crc value * @return CRC value - */ + **/ uint8_t subghz_protocol_blocks_crc7( uint8_t const message[], unsigned nBytes, @@ -67,13 +67,100 @@ uint8_t subghz_protocol_blocks_crc7( * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) * @param init starting crc value * @return CRC value - */ + **/ uint8_t subghz_protocol_blocks_crc8( uint8_t const message[], unsigned nBytes, uint8_t polynomial, uint8_t init); +/** + * "Little-endian" Cyclic Redundancy Check CRC-8 LE + * Input and output are reflected, i.e. least significant bit is shifted in first. + * @param message array of bytes to check + * @param nBytes number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * @return CRC value + **/ +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + unsigned nBytes, + uint8_t polynomial, + uint8_t init); + +/** + * CRC-16 LSB. + * Input and output are reflected, i.e. least significant bit is shifted in first. + * Note that poly and init already need to be reflected. + * @param message array of bytes to check + * @param nBytes number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * @return CRC value + **/ +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + unsigned nBytes, + uint16_t polynomial, + uint16_t init); + +/** + * CRC-16. + * @param message array of bytes to check + * @param nBytes number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * @return CRC value + **/ +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + unsigned nBytes, + uint16_t polynomial, + uint16_t init); + +/** + * Digest-8 by "LFSR-based Toeplitz hash". + * @param message bytes of message data + * @param bytes number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the LFSR is rolling + * @param key initial key + * @return digest value + **/ +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + unsigned bytes, + uint8_t gen, + uint8_t key); + +/** + * Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect. + * @param message bytes of message data + * @param bytes number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the LFSR is rolling + * @param key initial key + * @return digest value + **/ +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + int bytes, + uint8_t gen, + uint8_t key); + +/** + * Digest-16 by "LFSR-based Toeplitz hash". + * @param message bytes of message data + * @param bytes number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the LFSR is rolling + * @param key initial key + * @return digest value + **/ +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + unsigned bytes, + uint16_t gen, + uint16_t key); + #ifdef __cplusplus } #endif From f8af0c1509245e1bce3d9651a138db8490d69c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Sun, 23 Oct 2022 00:21:10 +0900 Subject: [PATCH 17/42] [FL-2892] Gui: update statusbar attention icon and better crash handling (#1908) * Gui: update statusbar attention icon * Furi: snapshot registers on crash and restore in halt * Furi: document check routines --- applications/services/gui/gui.c | 4 +- assets/icons/StatusBar/Alert_9x8.png | Bin 0 -> 3611 bytes assets/icons/StatusBar/Hidden_window_9x8.png | Bin 0 -> 3604 bytes firmware/targets/f7/api_symbols.csv | 9 ++-- furi/core/check.c | 45 ++++++++++++----- furi/core/check.h | 49 ++++++++++++++----- 6 files changed, 78 insertions(+), 29 deletions(-) create mode 100644 assets/icons/StatusBar/Alert_9x8.png create mode 100644 assets/icons/StatusBar/Hidden_window_9x8.png diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 0535b32b..8c5ed91a 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -152,7 +152,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { } // Extra notification if(need_attention) { - width = icon_get_width(&I_Attention_5x8); + width = icon_get_width(&I_Hidden_window_9x8); // Prepare work area background canvas_frame_set( gui->canvas, @@ -166,7 +166,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { // Draw Icon canvas_frame_set( gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); - canvas_draw_icon(gui->canvas, 0, 0, &I_Attention_5x8); + canvas_draw_icon(gui->canvas, 0, 0, &I_Hidden_window_9x8); // Recalculate next position left_used += (width + 2); x += (width + 2); diff --git a/assets/icons/StatusBar/Alert_9x8.png b/assets/icons/StatusBar/Alert_9x8.png new file mode 100644 index 0000000000000000000000000000000000000000..d03f107ef1e957fdc15c896c2f65d52ec691f9b3 GIT binary patch literal 3611 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 T?I80e8v&Rb+u+JDu3`TM+ZHm< literal 0 HcmV?d00001 diff --git a/assets/icons/StatusBar/Hidden_window_9x8.png b/assets/icons/StatusBar/Hidden_window_9x8.png new file mode 100644 index 0000000000000000000000000000000000000000..d6fc2b326d03d5b6a3dbabb1d5ed3a8a0140d35f GIT binary patch literal 3604 zcmaJ@c{o&iA3q}dmMkINF=UGwGu9b1mKpmRMn+l;GsdJbV@!>uL}jZkN%n}!zEl(m zWy_KfW#6(V7w@&iO?bz>xBI?-yuIf+=lp)Z@ALV5zUz6O6LZ|&LWp069{>O$D@#)+ z_R7b8czHS5@52X=Mgai783l_yZiU5y=nR@S>TL-;sRZgeMFq+LUO_<9F*-sO;0ggeS``!mf$OP& zxcQ1J>i1)%qH}8ON~M;esuU4s;qJT|{yeU(kRvLcR#L~rbqV_jX~vL-pj2sixVFfr z)pr1p6)(Zgc6B?jQ@FIVQ#ZCk_^ig4*W9~cPtU2HnXV$bF#t?3L%Vm>q2)4R2HauP zod-vkxO~nCWL?;}>0Ksb{2a)dA97u#@72aE`*zjf;LP;&fOQkbo$%K6@(#J-Y@O@& z`9DHY-#6Ajy_!@Eho26&;M)Au@NUgE=Wy2@zU!VNvvC&RD+PAH%Y6VhS$S6B)x@V9 z#r$JU({8Q8bHFi}JF0Jv_UsZT$JMtu4aaqWC+7?~LgF>@PkV8GwNVzuGwUWd0AQt> zR{v3jmov=g+C&#?ciCv$BI_s+=52My9{|pnDnf8?%Z*xi0l+jXT{|UH^*Zju z_4`Na1-^UeUdJdlHDE+B{KT-s3=h@fs~DBYjwfY)ekhv5^9EiamRBk@VpG1}l1uESBB(7|+!1UOuVV>wx`6|^ zSjw%S8T$}Xr_5i*8?*xOtKTDQEMRxT%N)@k1)pQ9^7Uk{n?DeEPh`T<;v}=u-!q=c zpl|7B&%H155gCdz#I|VGeLlpa)pTAH(%gu7s;|zM;MM*VTr61BEc4_wgz z7MEDKSskA)7YhR-95J!P+v4p9Eb~E!6}1lx@#*n#UUzIz1}Eg(JX3mk;E$$5o^V(c z-cntmPb}^rR5T2rhTgHR9 zs}4HW8Zn(Q-8ii^Ejum0;?AGpY3%=K(|JFqgd=^4=(?HO)T3f#oyVVcuuem3ra zPTWaMNp^+PW5=lcgW4C;5FOVft!#2i9wdH{@saY`pgjJ}e0g9r@rZr$d)J=!d5h1< zpE=i9feCCaKnaRr6Bg=2*RE~_r>xVVBd&X-HgU9_C13iv)I6}c2<9A9vjbtV@WOip+opcYTelFzbC(M$at{l39zwm*@u#8W zpe@6{J7P~a8e?+^iG<{d>+x0HsE57gznY8OqBNhHQkxa*!&!zXLk5_t}3_Ph`kzetGWK}kOGS_XXqQ)=c zB*HBR?{e~Vxp_%O$l+t)Nz1CXhX) z$dzwO8L64S^@7HTd|)UXS$69?k8&cju(eIU>(BA&*v1NUeI{glKv-^ zIjq7B!m^a%S3}1ADZ?q0-WYt$?d&_fy2{qdc85k<=GlmG>=lJw;%@M6H;@;0l!MF} z6K)djzHe(Y_>%nVSR>>x>1)~GqRFkSDiaBlxT_^sXX;DjeOiCJT_V3Rm(m&ENfC}X z9{RcOJr7})aI-N~P&pzh!Zp$;mL83l>4qiiu)0UE0pq6MOp{E<(#bFf#A6L>9W}#3 z$T`%I-b?M5M$Hg!w|Bkx47}BOo5Ki*TB)wz`B&rL7gwVuGbRP5LZnilt*7Zh*_{n* z5qXjY5HWRRlCf>Hz5d;QnTV?8OWr!Va*%a2mOrRLu7M1OcI$RW4`5HJ9kE}rRI>}d z^ILq4PLSdotsM24s_V)Hw`**LY)8|PnUZN2K~y*zSqr;%C;qlSrcmB4p@RH{JnMb2 zy_ghHw)4jCW+n7UB2Sxd`R(HUZW{a*nB32}lPdjcJY{5(tM}`Un74?eqRe9*i|@+c z4&TZbF9DcU8_#C1P8L8hoVK- zYX@H6#quhu{3@Ldpa;h%}PyfD(gc-jFEbsK+9_!gNVYbX(Oqf~rYDp}&n zfXYbVjBFI`?d#N$yUz-WnyQFh-aX#)vtt^W?!rlh$+OAAenNvo&xFQLJX>7c7~+*~7Wbqaa+-U-hw9t_O{Cit9VJ zU?JS{%d4G?WtN7Ju8_;G_nIda=%zu_^Ba8Q8%KnYLl;O`d0zlhw^M!5x9uj1M$L-SWZB#>9SZe=89Tn>f&G2$`& zpdw_>t8@IE2yOYLD08C6Z1?7zL5)G@sg)PZm9pWPgRU=J(;g$wQok_2pmq{IIJ8vX zt4=GeI^|on6kPi}#Cv02HdC*2bgee%cgfwp-b@5~w01R^w((|TeOz)hl%c%c*8CV{KQ1+{JyIxg zAyeNFftWO6Y|nqs!*&t7lrp7u9_+N2N=ZUPH+vWQJG)c5vy)en6JAp<E|P{l2Vo z_bU6pQkrRoXFAionJfZ>1eg$Mo+OYJl|UvrkqE>f|27f|060}BI6MC`2^aRmV=#j*cbyP%MKPBL2K@Vhc8=(;p8c=uj9$b1$nOKs&qt52aH7K?g9MNdJlV ze+mcSg6Jfu6Dfdpf#JpOowvfCD>@R(AQ6}}298EM|D%w{eP~QtfDerhf@?uwAQd}; z7sYSSqWUMr&JJni7r-R=d6BG4(O|X+ghC-A&Gbz4b>MmkZMe2R3}$AE(8eGzdit7L zFszx0o+j)E*OcaUflBgY{@@b-!!`LScW)r5bhc+x5`%J)L^Nm6sGvWyMpAwri{4N5 z{^k;Y9*h1@xlnd6(7oRNSFeAV*aNh;{bzXDhkwSOUQ9XU^4}<%-EzXR+ z?(Xj94Bj7Q?|!i|HNgcAd3q4TIL!4U)SG#^Rc?U5ypsDg;rqA_0i2HjnOW+@zt~W~ M%FN!h6ytIA-_)%w8UO$Q literal 0 HcmV?d00001 diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d0dfcbfa..188cd748 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,3.6,, +Version,+,4.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -260,6 +260,8 @@ Function,-,__eprintf,void,"const char*, const char*, unsigned int, const char*" Function,+,__errno,int*, Function,-,__fpclassifyd,int,double Function,-,__fpclassifyf,int,float +Function,+,__furi_crash,void, +Function,+,__furi_halt,void, Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" Function,-,__getline,ssize_t,"char**, size_t*, FILE*" Function,-,__isinfd,int,double @@ -891,7 +893,6 @@ Function,-,ftello,off_t,FILE* Function,-,ftrylockfile,int,FILE* Function,-,funlockfile,void,FILE* Function,-,funopen,FILE*,"const void*, int (*)(void*, char*, int), int (*)(void*, const char*, int), fpos_t (*)(void*, fpos_t, int), int (*)(void*)" -Function,+,furi_crash,void,const char* Function,+,furi_delay_ms,void,uint32_t Function,+,furi_delay_tick,void,uint32_t Function,+,furi_delay_until_tick,FuriStatus,uint32_t @@ -1308,7 +1309,6 @@ Function,+,furi_hal_version_uid,const uint8_t*, Function,+,furi_hal_version_uid_size,size_t, Function,-,furi_hal_vibro_init,void, Function,+,furi_hal_vibro_on,void,_Bool -Function,+,furi_halt,void,const char* Function,-,furi_init,void, Function,+,furi_kernel_get_tick_frequency,uint32_t, Function,+,furi_kernel_lock,int32_t, @@ -2674,6 +2674,7 @@ Variable,+,A_iButton_14,const Icon, Variable,-,ITM_RxBuffer,volatile int32_t, Variable,+,I_125_10px,const Icon, Variable,+,I_ActiveConnection_50x64,const Icon, +Variable,+,I_Alert_9x8,const Icon, Variable,+,I_ArrowC_1_36x36,const Icon, Variable,+,I_ArrowDownEmpty_14x15,const Icon, Variable,+,I_ArrowDownFilled_14x15,const Icon, @@ -2741,6 +2742,7 @@ Variable,+,I_HeatHi_25x27,const Icon, Variable,+,I_HeatHi_hvr_25x27,const Icon, Variable,+,I_HeatLo_25x27,const Icon, Variable,+,I_HeatLo_hvr_25x27,const Icon, +Variable,+,I_Hidden_window_9x8,const Icon, Variable,+,I_InfraredArrowDown_4x8,const Icon, Variable,+,I_InfraredArrowUp_4x8,const Icon, Variable,+,I_InfraredLearnShort_128x31,const Icon, @@ -2841,6 +2843,7 @@ Variable,+,I_update_10px,const Icon, Variable,-,MSIRangeTable,const uint32_t[16], Variable,-,SmpsPrescalerTable,const uint32_t[4][6], Variable,+,SystemCoreClock,uint32_t, +Variable,+,__furi_check_message,const char*, Variable,+,_ctype_,const char[], Variable,+,_global_impure_ptr,_reent*, Variable,+,_impure_ptr,_reent*, diff --git a/furi/core/check.c b/furi/core/check.c index 613b52f4..ed38038a 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -11,6 +11,9 @@ #include #include +PLACE_IN_SECTION("MB_MEM2") const char* __furi_check_message = NULL; +PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0}; + extern size_t xPortGetTotalHeapSize(void); extern size_t xPortGetFreeHeapSize(void); extern size_t xPortGetMinimumEverFreeHeapSize(void); @@ -52,8 +55,10 @@ static void __furi_print_name(bool isr) { } } -static FURI_NORETURN void __furi_halt() { +static FURI_NORETURN void __furi_halt_mcu() { + register const void* r12 asm ("r12") = (void*)__furi_check_registers; asm volatile( + "ldm r12, {r0-r11} \n" #ifdef FURI_DEBUG "bkpt 0x00 \n" #endif @@ -61,22 +66,29 @@ static FURI_NORETURN void __furi_halt() { "wfi \n" "b loop%= \n" : - : + : "r" (r12) : "memory"); __builtin_unreachable(); } -FURI_NORETURN void furi_crash(const char* message) { +FURI_NORETURN void __furi_crash() { + register const void* r12 asm ("r12") = (void*)__furi_check_registers; + asm volatile( + "stm r12, {r0-r11} \n" + : + : "r" (r12) + : "memory"); + bool isr = FURI_IS_ISR(); __disable_irq(); - if(message == NULL) { - message = "Fatal Error"; + if(__furi_check_message == NULL) { + __furi_check_message = "Fatal Error"; } furi_hal_console_puts("\r\n\033[0;31m[CRASH]"); __furi_print_name(isr); - furi_hal_console_puts(message); + furi_hal_console_puts(__furi_check_message); if(!isr) { __furi_print_stack_info(); @@ -86,9 +98,9 @@ FURI_NORETURN void furi_crash(const char* message) { #ifdef FURI_DEBUG furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); furi_hal_console_puts("\033[0m\r\n"); - __furi_halt(); + __furi_halt_mcu(); #else - furi_hal_rtc_set_fault_data((uint32_t)message); + furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); furi_hal_console_puts("\r\nRebooting system.\r\n"); furi_hal_console_puts("\033[0m\r\n"); furi_hal_power_reset(); @@ -96,18 +108,25 @@ FURI_NORETURN void furi_crash(const char* message) { __builtin_unreachable(); } -FURI_NORETURN void furi_halt(const char* message) { +FURI_NORETURN void __furi_halt() { + register const void* r12 asm ("r12") = (void*)__furi_check_registers; + asm volatile( + "stm r12, {r0-r11} \n" + : + : "r" (r12) + : "memory"); + bool isr = FURI_IS_ISR(); __disable_irq(); - if(message == NULL) { - message = "System halt requested."; + if(__furi_check_message == NULL) { + __furi_check_message = "System halt requested."; } furi_hal_console_puts("\r\n\033[0;31m[HALT]"); __furi_print_name(isr); - furi_hal_console_puts(message); + furi_hal_console_puts(__furi_check_message); furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n"); furi_hal_console_puts("\033[0m\r\n"); - __furi_halt(); + __furi_halt_mcu(); } diff --git a/furi/core/check.h b/furi/core/check.h index 30efdf01..e77891f7 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -8,25 +8,52 @@ extern "C" { #define FURI_NORETURN noreturn #endif +/** Pointer to pass message to __furi_crash and __furi_halt */ +extern const char* __furi_check_message; + +/** Crash system */ +FURI_NORETURN void __furi_crash(); + +/** Halt system */ +FURI_NORETURN void __furi_halt(); + +/** Crash system with message. Show message after reboot. */ +#define furi_crash(message) \ + do { \ + __furi_check_message = message; \ + __furi_crash(); \ + } while(0) + +/** Halt system with message. */ +#define furi_halt(message) \ + do { \ + __furi_check_message = message; \ + __furi_halt(); \ + } while(0) + /** Check condition and crash if check failed */ -#define furi_check(__e) ((__e) ? (void)0 : furi_crash("furi_check failed\r\n")) +#define furi_check(__e) \ + do { \ + if ((__e) == 0) { \ + furi_crash("furi_check failed\r\n"); \ + } \ + } while(0) /** Only in debug build: Assert condition and crash if assert failed */ #ifdef FURI_DEBUG -#define furi_assert(__e) ((__e) ? (void)0 : furi_crash("furi_assert failed\r\n")) +#define furi_assert(__e) \ + do { \ + if ((__e) == 0) { \ + furi_crash("furi_assert failed\r\n"); \ + } \ + } while(0) #else -#define furi_assert(__e) \ - do { \ - ((void)(__e)); \ +#define furi_assert(__e) \ + do { \ + ((void)(__e)); \ } while(0) #endif -/** Crash system */ -FURI_NORETURN void furi_crash(const char* message); - -/** Halt system */ -FURI_NORETURN void furi_halt(const char* message); - #ifdef __cplusplus } #endif From d8fbaba7a0e67ec6c9462aa96a97ac194152da64 Mon Sep 17 00:00:00 2001 From: Max Lapan Date: Sat, 22 Oct 2022 17:50:26 +0200 Subject: [PATCH 18/42] Move Oregon2 to Weather Station FAP (#1910) * Init copy of oregon2 to weather station app * WS decoder * Reuse decoded data * Delete old protocol * Delete oregon2 unit test * Decrement count of random test Co-authored-by: Aleksandr Kutuzov --- .../debug/unit_tests/subghz/subghz_test.c | 10 +- .../weather_station}/protocols/oregon2.c | 165 +++++++++++------- .../weather_station/protocols/oregon2.h | 6 + .../protocols/protocol_items.c | 1 + .../protocols/protocol_items.h | 1 + assets/unit_tests/subghz/oregon2_raw.sub | 20 --- assets/unit_tests/subghz/test_random_raw.sub | 15 -- lib/subghz/protocols/oregon2.h | 5 - lib/subghz/protocols/protocol_items.c | 3 +- lib/subghz/protocols/protocol_items.h | 1 - 10 files changed, 109 insertions(+), 118 deletions(-) rename {lib/subghz => applications/plugins/weather_station}/protocols/oregon2.c (66%) create mode 100644 applications/plugins/weather_station/protocols/oregon2.h delete mode 100644 assets/unit_tests/subghz/oregon2_raw.sub delete mode 100644 lib/subghz/protocols/oregon2.h diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 2dbf2eed..dbe03d88 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -13,7 +13,7 @@ #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 233 +#define TEST_RANDOM_COUNT_PARSE 232 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -437,13 +437,6 @@ MU_TEST(subghz_decoder_clemsa_test) { "Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n"); } -MU_TEST(subghz_decoder_oregon2_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/oregon2_raw.sub"), SUBGHZ_PROTOCOL_OREGON2_NAME), - "Test decoder " SUBGHZ_PROTOCOL_OREGON2_NAME " error\r\n"); -} - //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -605,7 +598,6 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_magellan_test); MU_RUN_TEST(subghz_decoder_intertechno_v3_test); MU_RUN_TEST(subghz_decoder_clemsa_test); - MU_RUN_TEST(subghz_decoder_oregon2_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); diff --git a/lib/subghz/protocols/oregon2.c b/applications/plugins/weather_station/protocols/oregon2.c similarity index 66% rename from lib/subghz/protocols/oregon2.c rename to applications/plugins/weather_station/protocols/oregon2.c index 244ac58d..c4e4471a 100644 --- a/lib/subghz/protocols/oregon2.c +++ b/applications/plugins/weather_station/protocols/oregon2.c @@ -1,14 +1,17 @@ #include "oregon2.h" -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" + +#include +#include +#include +#include +#include "ws_generic.h" + #include #include -#define TAG "SubGhzProtocolOregon2" +#define TAG "WSProtocolOregon2" -static const SubGhzBlockConst oregon2_const = { +static const SubGhzBlockConst ws_oregon2_const = { .te_long = 1000, .te_short = 500, .te_delta = 200, @@ -26,11 +29,11 @@ static const SubGhzBlockConst oregon2_const = { // bit indicating the low battery #define OREGON2_FLAG_BAT_LOW 0x4 -struct SubGhzProtocolDecoderOregon2 { +struct WSProtocolDecoderOregon2 { SubGhzProtocolDecoderBase base; SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; + WSBlockGeneric generic; ManchesterState manchester_state; bool prev_bit; bool have_bit; @@ -39,7 +42,7 @@ struct SubGhzProtocolDecoderOregon2 { uint32_t var_data; }; -typedef struct SubGhzProtocolDecoderOregon2 SubGhzProtocolDecoderOregon2; +typedef struct WSProtocolDecoderOregon2 WSProtocolDecoderOregon2; typedef enum { Oregon2DecoderStepReset = 0, @@ -47,23 +50,29 @@ typedef enum { Oregon2DecoderStepVarData, } Oregon2DecoderStep; -void* subghz_protocol_decoder_oregon2_alloc(SubGhzEnvironment* environment) { +void* ws_protocol_decoder_oregon2_alloc(SubGhzEnvironment* environment) { UNUSED(environment); - SubGhzProtocolDecoderOregon2* instance = malloc(sizeof(SubGhzProtocolDecoderOregon2)); - instance->base.protocol = &subghz_protocol_oregon2; + WSProtocolDecoderOregon2* instance = malloc(sizeof(WSProtocolDecoderOregon2)); + instance->base.protocol = &ws_protocol_oregon2; instance->generic.protocol_name = instance->base.protocol->name; + instance->generic.humidity = WS_NO_HUMIDITY; + instance->generic.temp = WS_NO_TEMPERATURE; + instance->generic.btn = WS_NO_BTN; + instance->generic.channel = WS_NO_CHANNEL; + instance->generic.battery_low = WS_NO_BATT; + instance->generic.id = WS_NO_ID; return instance; } -void subghz_protocol_decoder_oregon2_free(void* context) { +void ws_protocol_decoder_oregon2_free(void* context) { furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; + WSProtocolDecoderOregon2* instance = context; free(instance); } -void subghz_protocol_decoder_oregon2_reset(void* context) { +void ws_protocol_decoder_oregon2_reset(void* context) { furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; + WSProtocolDecoderOregon2* instance = context; instance->decoder.parser_step = Oregon2DecoderStepReset; instance->decoder.decode_data = 0UL; instance->decoder.decode_count_bit = 0; @@ -77,9 +86,9 @@ void subghz_protocol_decoder_oregon2_reset(void* context) { static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) { bool is_long = false; - if(DURATION_DIFF(duration, oregon2_const.te_long) < oregon2_const.te_delta) { + if(DURATION_DIFF(duration, ws_oregon2_const.te_long) < ws_oregon2_const.te_delta) { is_long = true; - } else if(DURATION_DIFF(duration, oregon2_const.te_short) < oregon2_const.te_delta) { + } else if(DURATION_DIFF(duration, ws_oregon2_const.te_short) < ws_oregon2_const.te_delta) { is_long = false; } else { return ManchesterEventReset; @@ -97,9 +106,36 @@ static uint8_t oregon2_sensor_id_var_bits(uint16_t sensor_id) { return 0; } -void subghz_protocol_decoder_oregon2_feed(void* context, bool level, uint32_t duration) { +static void ws_oregon2_decode_const_data(WSBlockGeneric* ws_block) { + ws_block->id = OREGON2_SENSOR_ID(ws_block->data); + + uint8_t ch_bits = (ws_block->data >> 12) & 0xF; + ws_block->channel = 1; + while(ch_bits > 1) { + ws_block->channel++; + ch_bits >>= 1; + } + + ws_block->battery_low = (ws_block->data & OREGON2_FLAG_BAT_LOW) ? 1 : 0; +} + +static void + ws_oregon2_decode_var_data(WSBlockGeneric* ws_block, uint16_t sensor_id, uint32_t var_data) { + int16_t temp_val; + if(sensor_id == 0xEC40) { + temp_val = ((var_data >> 4) & 0xF) * 10 + ((var_data >> 8) & 0xF); + temp_val *= 10; + temp_val += (var_data >> 12) & 0xF; + if(var_data & 0xF) temp_val = -temp_val; + } else + return; + + ws_block->temp = (float)temp_val / 10.0; +} + +void ws_protocol_decoder_oregon2_feed(void* context, bool level, uint32_t duration) { furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; + WSProtocolDecoderOregon2* instance = context; // oregon v2.1 signal is inverted ManchesterEvent event = level_and_duration_to_event(!level, duration); bool data; @@ -118,7 +154,7 @@ void subghz_protocol_decoder_oregon2_feed(void* context, bool level, uint32_t du } else if(instance->prev_bit && !data) { subghz_protocol_blocks_add_bit(&instance->decoder, 0); } else { - subghz_protocol_decoder_oregon2_reset(context); + ws_protocol_decoder_oregon2_reset(context); } instance->have_bit = false; } else { @@ -151,6 +187,7 @@ void subghz_protocol_decoder_oregon2_feed(void* context, bool level, uint32_t du instance->generic.data = (instance->generic.data & 0x33333333) << 2 | (instance->generic.data & 0xCCCCCCCC) >> 2; + ws_oregon2_decode_const_data(&instance->generic); instance->var_bits = oregon2_sensor_id_var_bits(OREGON2_SENSOR_ID(instance->generic.data)); @@ -175,6 +212,11 @@ void subghz_protocol_decoder_oregon2_feed(void* context, bool level, uint32_t du instance->var_data = (instance->var_data & 0x33333333) << 2 | (instance->var_data & 0xCCCCCCCC) >> 2; + ws_oregon2_decode_var_data( + &instance->generic, + OREGON2_SENSOR_ID(instance->generic.data), + instance->var_data >> OREGON2_CHECKSUM_BITS); + instance->decoder.parser_step = Oregon2DecoderStepReset; if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); @@ -183,20 +225,20 @@ void subghz_protocol_decoder_oregon2_feed(void* context, bool level, uint32_t du } } -uint8_t subghz_protocol_decoder_oregon2_get_hash_data(void* context) { +uint8_t ws_protocol_decoder_oregon2_get_hash_data(void* context) { furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; + WSProtocolDecoderOregon2* instance = context; return subghz_protocol_blocks_get_hash_data( &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_oregon2_serialize( +bool ws_protocol_decoder_oregon2_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; - if(!subghz_block_generic_serialize(&instance->generic, flipper_format, preset)) return false; + WSProtocolDecoderOregon2* instance = context; + if(!ws_block_generic_serialize(&instance->generic, flipper_format, preset)) return false; uint32_t temp = instance->var_bits; if(!flipper_format_write_uint32(flipper_format, "VarBits", &temp, 1)) { FURI_LOG_E(TAG, "Error adding VarBits"); @@ -213,13 +255,13 @@ bool subghz_protocol_decoder_oregon2_serialize( return true; } -bool subghz_protocol_decoder_oregon2_deserialize(void* context, FlipperFormat* flipper_format) { +bool ws_protocol_decoder_oregon2_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; + WSProtocolDecoderOregon2* instance = context; bool ret = false; uint32_t temp_data; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { break; } if(!flipper_format_read_uint32(flipper_format, "VarBits", &temp_data, 1)) { @@ -235,7 +277,7 @@ bool subghz_protocol_decoder_oregon2_deserialize(void* context, FlipperFormat* f FURI_LOG_E(TAG, "Missing VarData"); break; } - if(instance->generic.data_count_bit != oregon2_const.min_count_bit_for_found) { + if(instance->generic.data_count_bit != ws_oregon2_const.min_count_bit_for_found) { FURI_LOG_E(TAG, "Wrong number of bits in key: %d", instance->generic.data_count_bit); break; } @@ -244,22 +286,6 @@ bool subghz_protocol_decoder_oregon2_deserialize(void* context, FlipperFormat* f return ret; } -// append string of the variable data -static void - oregon2_var_data_append_string(uint16_t sensor_id, uint32_t var_data, FuriString* output) { - uint32_t val; - - if(sensor_id == 0xEC40) { - val = ((var_data >> 4) & 0xF) * 10 + ((var_data >> 8) & 0xF); - furi_string_cat_printf( - output, - "Temp: %s%ld.%ld C\r\n", - (var_data & 0xF) ? "-" : "+", - val, - (uint32_t)(var_data >> 12) & 0xF); - } -} - static void oregon2_append_check_sum(uint32_t fix_data, uint32_t var_data, FuriString* output) { uint8_t sum = fix_data & 0xF; uint8_t ref_sum = var_data & 0xFF; @@ -279,45 +305,50 @@ static void oregon2_append_check_sum(uint32_t fix_data, uint32_t var_data, FuriS furi_string_cat_printf(output, "Sum err: 0x%hhX vs 0x%hhX", ref_sum, sum); } -void subghz_protocol_decoder_oregon2_get_string(void* context, FuriString* output) { +void ws_protocol_decoder_oregon2_get_string(void* context, FuriString* output) { furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; - uint16_t sensor_id = OREGON2_SENSOR_ID(instance->generic.data); + WSProtocolDecoderOregon2* instance = context; furi_string_cat_printf( output, "%s\r\n" - "ID: 0x%04lX, ch: %ld%s, rc: 0x%02lX\r\n", + "ID: 0x%04lX, ch: %d, bat: %d, rc: 0x%02lX\r\n", instance->generic.protocol_name, - (uint32_t)sensor_id, - (uint32_t)(instance->generic.data >> 12) & 0xF, - ((instance->generic.data & OREGON2_FLAG_BAT_LOW) ? ", low bat" : ""), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, (uint32_t)(instance->generic.data >> 4) & 0xFF); if(instance->var_bits > 0) { - oregon2_var_data_append_string( - sensor_id, instance->var_data >> OREGON2_CHECKSUM_BITS, output); + furi_string_cat_printf( + output, + "Temp:%d.%d C Hum:%d%%", + (int16_t)instance->generic.temp, + abs( + ((int16_t)(instance->generic.temp * 10) - + (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); oregon2_append_check_sum((uint32_t)instance->generic.data, instance->var_data, output); } } -const SubGhzProtocolDecoder subghz_protocol_oregon2_decoder = { - .alloc = subghz_protocol_decoder_oregon2_alloc, - .free = subghz_protocol_decoder_oregon2_free, +const SubGhzProtocolDecoder ws_protocol_oregon2_decoder = { + .alloc = ws_protocol_decoder_oregon2_alloc, + .free = ws_protocol_decoder_oregon2_free, - .feed = subghz_protocol_decoder_oregon2_feed, - .reset = subghz_protocol_decoder_oregon2_reset, + .feed = ws_protocol_decoder_oregon2_feed, + .reset = ws_protocol_decoder_oregon2_reset, - .get_hash_data = subghz_protocol_decoder_oregon2_get_hash_data, - .serialize = subghz_protocol_decoder_oregon2_serialize, - .deserialize = subghz_protocol_decoder_oregon2_deserialize, - .get_string = subghz_protocol_decoder_oregon2_get_string, + .get_hash_data = ws_protocol_decoder_oregon2_get_hash_data, + .serialize = ws_protocol_decoder_oregon2_serialize, + .deserialize = ws_protocol_decoder_oregon2_deserialize, + .get_string = ws_protocol_decoder_oregon2_get_string, }; -const SubGhzProtocol subghz_protocol_oregon2 = { - .name = SUBGHZ_PROTOCOL_OREGON2_NAME, +const SubGhzProtocol ws_protocol_oregon2 = { + .name = WS_PROTOCOL_OREGON2_NAME, .type = SubGhzProtocolTypeStatic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save, - .decoder = &subghz_protocol_oregon2_decoder, + .decoder = &ws_protocol_oregon2_decoder, }; diff --git a/applications/plugins/weather_station/protocols/oregon2.h b/applications/plugins/weather_station/protocols/oregon2.h new file mode 100644 index 00000000..cfe938e6 --- /dev/null +++ b/applications/plugins/weather_station/protocols/oregon2.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +#define WS_PROTOCOL_OREGON2_NAME "Oregon2" +extern const SubGhzProtocol ws_protocol_oregon2; diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/plugins/weather_station/protocols/protocol_items.c index 5b753994..0740691a 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.c +++ b/applications/plugins/weather_station/protocols/protocol_items.c @@ -7,6 +7,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_gt_wt_03, &ws_protocol_acurite_606tx, &ws_protocol_lacrosse_tx141thbv2, + &ws_protocol_oregon2, }; const SubGhzProtocolRegistry weather_station_protocol_registry = { diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/plugins/weather_station/protocols/protocol_items.h index c4f4ecd9..edc078cd 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.h +++ b/applications/plugins/weather_station/protocols/protocol_items.h @@ -7,5 +7,6 @@ #include "gt_wt_03.h" #include "acurite_606tx.h" #include "lacrosse_tx141thbv2.h" +#include "oregon2.h" extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/assets/unit_tests/subghz/oregon2_raw.sub b/assets/unit_tests/subghz/oregon2_raw.sub deleted file mode 100644 index 549a60db..00000000 --- a/assets/unit_tests/subghz/oregon2_raw.sub +++ /dev/null @@ -1,20 +0,0 @@ -Filetype: Flipper SubGhz RAW File -Version: 1 -Frequency: 433920000 -Preset: FuriHalSubGhzPresetOok270Async -Protocol: RAW -RAW_Data: 889 -130 325 -64 457 -560 165 -68 199 -170 67 -66 265 -132 133 -666 67 -166 431 -66 201 -98 297 -100 595 -66 199 -134 65 -100 795 -132 99 -168 501 -200 331 -132 265 -102 265 -134 423 -98 521 -226 65 -166 431 -134 99 -100 133 -464 195 -326 623 -100 673 -98 321 -200 65 -136 369 -166 65 -68 97 -166 165 -334 265 -102 231 -166 101 -170 65 -170 265 -136 931 -100 133 -134 563 -66 333 -100 427 -66 163 -390 231 -66 193 -130 461 -166 557 -100 99 -198 263 -100 197 -294 231 -232 299 -134 199 -170 267 -134 631 -98 235 -100 499 -68 463 -100 65 -134 335 -170 273 -134 297 -100 67 -66 197 -166 67 -134 301 -168 537 -470 99 -134 433 -132 199 -192 261 -100 523 -164 459 -132 259 -332 359 -64 227 -96 131 -132 687 -132 363 -136 329 -434 99 -334 133 -100 401 -132 233 -700 233 -170 337 -66 371 -68 233 -202 531 -266 731 -66 465 -100 167 -100 133 -232 335 -166 239 -102 367 -232 231 -100 167 -134 201 -136 301 -168 199 -300 231 -98 237 -134 233 -102 329 -132 261 -134 199 -66 265 -136 99 -170 167 -134 199 -166 167 -136 367 -298 197 -200 99 -166 469 -136 439 -66 303 -134 295 -100 433 -134 899 -266 363 -132 197 -160 555 -324 129 -96 97 -128 257 -132 97 -394 257 -98 195 -166 459 -332 395 -132 633 -134 301 -100 131 -332 169 -168 395 -166 263 -540 783 -100 287 -130 295 -96 225 -296 133 -98 99 -100 461 -164 545 -130 99 -66 301 -68 265 -100 235 -134 235 -70 333 -102 497 -66 233 -364 301 -170 103 -66 165 -336 733 -200 133 -100 263 -102 65 -136 465 -200 1035 -198 165 -170 67 -302 631 -100 429 -332 65 -128 129 -130 159 -128 159 -66 161 -96 325 -164 261 -100 197 -162 65 -96 99 -130 65 -102 333 -100 199 -98 389 -330 129 -128 229 -66 425 -366 229 -64 261 -100 227 -96 227 -526 301 -200 97 -66 699 -334 67 -100 399 -198 787 -98 297 -134 429 -100 3245 -64 527 -98 131 -526 633 -68 133 -302 1459 -164 971 -102 237 -136 1439 -266 1131 -66 599 -200 303 -332 325 -130 389 -166 371 -66 333 -102 65 -100 233 -234 327 -266 233 -166 297 -100 225 -130 163 -336 99 -596 199 -330 131 -66 331 -338 263 -358 197 -168 877 -66 227 -96 63 -130 263 -162 225 -290 197 -198 357 -132 297 -262 165 -456 227 -98 399 -296 95 -132 99 -98 457 -200 199 -168 535 -100 567 -134 327 -130 193 -130 683 -102 101 -132 233 -170 943 -166 827 -66 267 -102 503 -68 1325 -164 -RAW_Data: 1607 -68 233 -166 1167 -70 531 -134 335 -168 131 -66 299 -402 899 -66 461 -66 457 -98 953 -98 165 -66 293 -230 881 -64 393 -166 589 -66 289 -66 1093 -204 333 -98 2745 -132 2019 -170 925 -68 269 -102 1469 -136 2301 -68 1355 -100 527 -66 975 -68 1445 -98 2397 -100 1733 -66 703 -100 995 -100 135 -136 235 -202 167 -134 2071 -166 339 -170 201 -268 129 -66 465 -66 365 -100 197 -164 129 -98 161 -96 423 -66 675 -66 1543 -136 567 -200 767 -202 65 -100 1401 -66 623 -136 567 -234 67 -236 197 -194 97 -66 263 -66 1827 -392 1893 -98 165 -268 133 -132 231 -162 225 -98 695 -198 563 -100 301 -332 267 -102 341 -66 99 -132 1299 -130 525 -68 161 -96 357 -98 353 -100 131 -100 131 -98 163 -132 323 -100 535 -66 1323 -130 133 -66 235 -134 1497 -132 387 -98 129 -162 2623 -134 163 -68 167 -66 959 -232 495 -68 131 -134 867 -134 865 -66 333 -98 305 -134 231 -98 765 -198 397 -432 165 -66 165 -366 265 -102 541 -100 261 -162 331 -134 457 -66 491 -196 97 -266 193 -262 65 -166 231 -266 497 -360 263 -98 587 -164 259 -98 231 -66 359 -100 267 -102 271 -168 97 -262 63 -66 261 -130 227 -130 295 -164 65 -66 265 -200 597 -134 267 -170 603 -100 97 -466 231 -264 97 -168 99 -66 65 -200 199 -100 267 -404 303 -102 201 -204 235 -134 131 -198 335 -298 327 -130 291 -164 63 -162 295 -262 197 -130 95 -130 195 -96 159 -130 161 -66 231 -100 165 -66 199 -134 363 -66 267 -168 165 -168 167 -100 165 -530 363 -432 99 -232 65 -132 395 -328 229 -98 197 -132 161 -96 191 -292 197 -204 133 -100 399 -166 531 -332 235 -168 99 -66 325 -158 553 -132 129 -226 231 -134 99 -462 129 -64 289 -100 193 -66 355 -164 291 -198 131 -298 197 -198 373 -268 335 -234 427 -68 199 -132 267 -232 131 -66 783 -326 63 -162 161 -130 227 -66 259 -562 233 -464 303 -102 201 -334 301 -134 297 -198 229 -66 127 -166 99 -100 197 -198 571 -66 457 -134 361 -424 131 -328 163 -98 63 -100 505 -102 201 -1094 229 -164 65 -230 789 -236 2505 -166 201 -170 163 -64 1139 -66 927 -100 295 -198 723 -100 365 -66 459 -196 3033 -272 199 -66 499 -202 1319 -232 295 -298 131 -362 97 -164 129 -132 65 -98 197 -130 129 -98 261 -130 97 -98 229 -96 425 -66 227 -166 483 -66 163 -326 567 -68 235 -68 67 -66 167 -66 235 -330 425 -164 63 -66 427 -102 167 -66 669 -132 429 -200 65 -102 133 -100 197 -368 -RAW_Data: 65 -134 2481 -228 65 -130 229 -228 763 -136 603 -166 1619 -98 1763 -102 837 -166 321 -66 951 -130 2067 -66 259 -132 1835 -66 437 -102 701 -66 565 -68 363 -70 1113 -66 1989 -164 257 -128 351 -162 1055 -232 265 -170 309 -200 435 -166 833 -102 2467 -132 595 -66 773 -166 1615 -98 131 -96 485 -64 517 -166 197 -68 1231 -68 403 -100 263 -134 233 -100 503 -100 333 -266 729 -66 199 -100 369 -68 1239 -100 197 -68 299 -170 337 -100 825 -132 163 -66 4205 -64 161 -100 635 -66 907 -66 1017 -166 1709 -100 201 -266 657 -68 463 -166 331 -164 293 -64 259 -162 129 -262 597 -134 701 -136 67 -168 235 -136 303 -170 1417 -66 263 -98 857 -100 659 -166 97 -100 2497 -64 2495 -98 719 -128 227 -130 2217 -164 623 -264 719 -134 329 -98 1371 -100 553 -294 165 -66 1163 -100 329 -196 649 -200 1123 -68 263 -100 593 -266 333 -102 1133 -136 131 -132 603 -200 1819 -66 489 -66 563 -266 1113 -230 165 -66 423 -68 335 -100 101 -100 1073 -132 897 -100 101 -100 499 -134 173 -138 763 -238 371 -130 403 -166 203 -102 271 -136 269 -166 99 -168 263 -96 425 -66 331 -234 133 -400 231 -132 453 -66 459 -164 199 -68 237 -132 163 -198 161 -196 265 -132 65 -64 195 -130 357 -164 663 -68 167 -600 131 -98 133 -304 203 -134 433 -98 261 -130 199 -100 237 -100 229 -326 99 -98 331 -132 99 -294 165 -66 303 -134 99 -232 133 -136 99 -68 267 -198 233 -138 67 -166 367 -100 333 -168 267 -200 369 -266 135 -404 1939 -132 231 -160 161 -64 293 -98 331 -132 339 -104 135 -100 197 -430 263 -202 233 -64 195 -162 129 -64 227 -298 265 -68 697 -66 301 -68 231 -300 131 -368 769 -234 265 -98 195 -324 97 -752 229 -126 355 -98 257 -98 287 -64 427 -132 295 -262 197 -170 369 -102 267 -100 169 -68 201 -102 2551 -136 635 -134 639 -134 99 -132 197 -200 371 -66 731 -132 199 -138 733 -304 433 -68 729 -440 197 -68 99 -102 165 -266 261 -164 491 -296 489 -194 257 -164 133 -134 237 -68 335 -98 227 -130 229 -98 295 -98 231 -202 267 -236 233 -136 331 -130 195 -128 261 -430 261 -162 97 -224 99 -130 193 -96 197 -162 229 -396 97 -98 227 -364 267 -100 99 -100 233 -236 697 -164 227 -196 63 -98 327 -230 325 -66 129 -196 95 -98 195 -130 325 -430 131 -194 129 -454 161 -196 235 -68 433 -134 667 -164 355 -236 101 -98 2143 -134 1827 -198 63 -198 65 -64 2859 -64 619 -66 97 -130 3157 -66 679 -194 1491 -98 -RAW_Data: 951 -64 393 -100 955 -132 4715 -100 131 -66 199 -204 1541 -66 929 -130 1347 -166 665 -132 233 -132 67 -102 433 -100 595 -228 997 -66 505 -68 133 -98 231 -68 571 -134 1371 -232 231 -270 135 -102 97 -66 867 -100 269 -68 967 -100 1649 -66 65 -66 951 -68 65 -202 363 -200 779 -102 1449 -294 419 -130 361 -230 1079 -164 163 -260 893 -102 333 -100 533 -166 467 -100 135 -66 135 -202 369 -100 199 -100 269 -134 301 -166 229 -66 101 -134 199 -134 1293 -64 779 -62 831 -66 1243 -68 267 -102 197 -100 395 -98 455 -64 621 -132 877 -98 199 -100 2101 -134 503 -100 2035 -134 735 -236 475 -136 237 -132 133 -134 1229 -100 133 -66 167 -68 2655 -100 1807 -100 1095 -264 825 -98 163 -66 491 -98 161 -128 953 -100 773 -100 131 -66 67 -134 457 -130 63 -64 389 -98 715 -66 425 -300 97 -100 1515 -66 303 -68 99 -98 721 -64 887 -132 65 -132 165 -66 635 -68 2801 -66 1561 -100 751 -98 129 -64 725 -136 201 -100 333 -204 573 -104 1745 -134 99 -66 129 -64 595 -134 167 -102 337 -134 567 -134 1131 -138 1207 -100 269 -68 135 -100 1143 -134 2139 -68 1701 -162 991 -596 431 -66 99 -132 657 -66 391 -320 357 -260 259 -98 429 -66 163 -228 65 -130 227 -66 261 -166 99 -98 131 -366 199 -134 463 -102 201 -98 231 -102 639 -238 301 -568 169 -610 265 -102 841 -198 297 -100 335 -132 263 -266 265 -68 469 -134 267 -68 933 -298 333 -298 729 -168 135 -136 437 -132 1137 -134 199 -68 265 -132 463 -166 129 -130 227 -98 297 -98 65 -132 97 -202 199 -232 305 -66 165 -198 365 -66 99 -98 299 -170 65 -136 301 -232 99 -564 133 -132 233 -170 99 -102 131 -134 65 -204 101 -98 297 -98 167 -762 233 -298 99 -326 395 -66 299 -132 369 -504 333 -98 483 -200 457 -164 63 -164 329 -162 65 -622 231 -268 131 -132 133 -134 131 -134 131 -66 99 -100 231 -66 167 -336 165 -98 197 -100 97 -264 321 -98 521 -132 163 -130 129 -294 297 -134 101 -102 265 -168 497 -68 197 -68 499 -134 269 -398 267 -130 203 -302 65 -498 271 -136 465 -292 131 -294 163 -198 329 -96 129 -98 193 -130 391 -330 165 -134 167 -170 297 -102 133 -136 135 -366 199 -132 423 -132 395 -168 65 -166 401 -98 229 -98 329 -98 99 -130 129 -228 261 -160 127 -426 389 -162 193 -132 131 -100 231 -168 67 -304 201 -68 765 -132 161 -162 193 -64 195 -64 295 -130 787 -98 419 -528 429 -66 363 -134 131 -100 133 -200 331 -98 -RAW_Data: 431 -66 1167 -68 937 -68 1003 -66 99 -132 941 -134 65 -66 365 -274 165 -236 367 -96 557 -134 675 -66 261 -164 127 -96 391 -164 161 -98 391 -292 163 -98 519 -196 165 -98 523 -66 195 -160 3343 -66 661 -100 2589 -136 307 -100 629 -136 639 -100 133 -168 405 -100 267 -66 465 -132 1171 -64 749 -64 165 -98 983 -100 163 -202 537 -66 327 -100 669 -100 401 -236 2885 -164 439 -134 97 -426 1931 -66 1385 -98 715 -98 519 -66 289 -162 97 -360 297 -166 163 -66 289 -66 555 -334 167 -230 429 -102 267 -132 943 -136 401 -68 929 -130 193 -68 467 -198 335 -66 963 -100 597 -132 197 -260 523 -232 1115 -102 1935 -66 1395 -134 305 -100 99 -66 199 -66 1071 -66 2357 -66 367 -498 769 -234 163 -130 191 -64 1211 -200 133 -102 201 -100 561 -366 361 -98 195 -100 537 -64 165 -196 1041 -332 133 -102 441 -230 4217 -66 1033 -66 167 -66 933 -100 565 -66 331 -164 673 -104 441 -66 533 -66 2095 -164 525 -66 297 -170 965 -198 421 -100 663 -832 65 -100 331 -164 231 -166 135 -168 237 -466 761 -134 891 -196 791 -198 257 -160 161 -98 293 -66 1081 -98 229 -130 327 -66 1301 -200 331 -166 101 -66 461 -100 2619 -132 1663 -98 1609 -134 499 -332 165 -370 67 -264 97 -96 259 -98 701 -402 197 -128 527 -236 233 -102 167 -134 303 -134 99 -166 299 -132 165 -200 467 -68 305 -168 207 -102 465 -102 729 -136 101 -374 327 -96 259 -98 467 -202 65 -66 673 -98 335 -404 135 -66 339 -204 99 -366 233 -68 365 -166 133 -102 867 -198 163 -162 163 -294 463 -332 165 -68 269 -268 331 -100 131 -166 299 -132 231 -400 263 -164 131 -266 267 -264 367 -66 371 -134 229 -104 267 -232 67 -466 265 -100 101 -100 165 -200 65 -200 301 -66 199 -168 233 -98 267 -66 67 -134 261 -196 261 -234 427 -294 65 -194 193 -66 259 -132 849 -96 63 -198 167 -294 95 -98 361 -164 261 -196 131 -132 437 -100 597 -262 327 -162 295 -98 295 -164 259 -196 425 -230 321 -66 195 -66 261 -496 99 -200 529 -132 133 -966 133 -132 165 -66 63 -128 491 -402 65 -262 299 -66 299 -202 265 -100 99 -668 97 -134 65 -100 101 -66 65 -266 691 -66 431 -166 167 -134 199 -370 899 -134 99 -100 1093 -166 163 -166 399 -98 327 -100 99 -168 135 -200 133 -202 429 -98 65 -98 197 -556 65 -66 97 -326 331 -166 333 -200 135 -100 235 -234 265 -98 65 -68 135 -66 335 -66 133 -298 99 -66 233 -164 435 -232 97 -132 97 -392 -RAW_Data: 99 -198 819 -66 1235 -98 321 -132 1091 -66 1307 -98 3059 -164 3305 -64 227 -98 591 -98 129 -66 229 -98 2143 -98 939 -68 563 -100 361 -232 945 -164 257 -96 229 -230 387 -64 195 -130 981 -294 587 -162 193 -98 1337 -66 293 -98 2665 -66 297 -98 647 -66 459 -132 491 -164 489 -96 595 -66 899 -66 837 -64 1151 -196 259 -98 357 -164 891 -132 1359 -134 197 -98 97 -98 261 -64 229 -96 461 -136 693 -100 201 -98 865 -66 599 -100 517 -132 709 -66 293 -298 655 -66 197 -130 129 -66 197 -98 4291 -66 673 -66 667 -132 1473 -132 133 -104 99 -66 163 -168 333 -134 1743 -132 1097 -132 99 -68 167 -602 1323 -352 99 -166 753 -98 423 -98 97 -66 1317 -228 1309 -98 1849 -66 1939 -132 601 -100 665 -100 1875 -66 695 -132 425 -66 425 -66 263 -134 165 -134 99 -98 829 -66 601 -166 131 -102 565 -66 301 -100 1099 -100 601 -138 533 -66 667 -234 561 -66 99 -68 2741 -98 199 -100 531 -168 101 -434 1027 -68 431 -66 403 -132 99 -98 565 -132 135 -100 399 -166 271 -236 233 -166 197 -366 99 -66 99 -168 503 -66 199 -170 207 -100 673 -368 99 -66 263 -168 133 -98 397 -268 337 -66 131 -132 231 -132 501 -134 99 -168 567 -138 103 -136 267 -298 231 -134 197 -160 321 -332 231 -98 131 -164 257 -64 163 -328 395 -66 331 -202 65 -168 133 -68 167 -100 233 -102 335 -66 197 -326 1101 -132 589 -100 811 -132 399 -136 269 -102 497 -66 559 -100 129 -98 855 -68 637 -102 65 -200 875 -68 233 -166 167 -66 529 -202 235 -102 231 -66 1237 -66 733 -98 1723 -132 101 -100 297 -66 829 -232 197 -100 367 -134 169 -166 167 -434 633 -100 235 -200 131 -134 233 -100 131 -100 331 -134 495 -432 65 -528 161 -130 295 -132 337 -136 133 -166 165 -100 269 -240 201 -336 133 -166 165 -238 199 -202 431 -434 99 -134 501 -166 231 -96 559 -202 167 -66 717 -98 987 -198 65 -64 163 -64 227 -98 555 -164 199 -64 361 -66 163 -98 129 -162 97 -130 161 -460 197 -230 681 -98 197 -98 329 -100 267 -266 291 -264 65 -100 329 -100 459 -200 363 -98 165 -134 231 -134 301 -134 231 -302 99 -132 101 -134 267 -136 233 -68 393 -422 163 -166 361 -166 99 -134 365 -134 133 -336 401 -66 495 -132 401 -168 133 -402 501 -136 1093 -862 165 -132 293 -300 289 -66 131 -164 391 -134 99 -360 359 -130 323 -200 423 -98 195 -162 295 -132 161 -98 129 -782 131 -426 227 -64 259 -166 63 -160 323 -98 261 -230 -RAW_Data: 231 -66 921 -66 355 -64 1019 -98 227 -258 163 -66 597 -232 1313 -132 163 -404 467 -236 901 -164 483 -98 195 -96 489 -134 103 -238 169 -66 67 -68 299 -100 497 -68 65 -134 1635 -304 1153 -100 539 -168 265 -200 499 -166 535 -100 397 -168 931 -100 131 -66 631 -134 897 -270 1233 -100 65 -132 131 -334 663 -66 163 -66 131 -132 705 -98 571 -200 433 -100 237 -234 229 -132 1627 -66 569 -100 715 -66 1863 -272 265 -68 301 -98 465 -68 97 -134 99 -66 395 -136 1405 -66 529 -132 63 -196 579 -132 413 -260 129 -136 101 -166 1201 -134 833 -134 393 -66 335 -172 201 -68 1027 -96 753 -64 815 -66 97 -64 1341 -132 289 -160 127 -66 99 -228 1083 -96 163 -66 259 -64 159 -98 2409 -168 767 -200 367 -66 1675 -66 1067 -98 3407 -200 99 -66 1403 -166 99 -134 439 -200 329 -136 599 -66 637 -66 835 -66 1099 -98 99 -66 463 -166 165 -100 461 -164 3037 -66 655 -66 97 -98 229 -130 355 -132 1443 -66 527 -98 881 -98 229 -162 127 -96 583 -64 65 -162 489 -166 885 -194 257 -98 1539 -66 293 -166 229 -132 655 -98 757 -49522 271 -758 689 -1264 737 -670 293 -1152 811 -1144 341 -664 773 -678 327 -1118 807 -1144 835 -1146 781 -1126 873 -1096 347 -622 877 -624 321 -1106 843 -1098 871 -1098 843 -1106 379 -610 841 -584 381 -1122 365 -602 845 -1116 837 -610 381 -1056 889 -1078 383 -614 827 -1110 877 -592 353 -1108 845 -1120 839 -1120 347 -602 849 -1110 865 -612 361 -1072 869 -1114 351 -618 861 -618 343 -1090 853 -1106 387 -618 797 -674 347 -1084 389 -574 867 -584 381 -1114 841 -1102 845 -1116 839 -1112 843 -1098 875 -1086 383 -584 865 -588 375 -1100 861 -1112 851 -1084 853 -1108 847 -1106 381 -584 857 -610 383 -1080 357 -602 871 -602 385 -1084 383 -616 823 -610 373 -1086 381 -590 871 -1084 839 -628 353 -1102 875 -1100 349 -9404 875 -1060 871 -1086 887 -1088 879 -1058 863 -1086 855 -1132 845 -1078 871 -1076 857 -1098 881 -1082 861 -1088 843 -1120 853 -1074 879 -1074 879 -1068 889 -614 341 -1090 387 -616 863 -624 345 -1088 391 -590 857 -612 385 -1058 393 -596 843 -1088 889 -1078 879 -578 387 -1082 875 -1076 415 -550 881 -1070 877 -592 391 -1114 821 -1104 373 -620 821 -624 361 -1072 903 -1086 855 -1092 843 -1086 905 -1054 387 -614 863 -618 347 -1088 853 -1114 845 -1090 867 -1070 381 -610 885 -584 385 -1052 407 -578 877 -1052 899 -600 389 -1048 907 -1074 383 -586 877 -1072 877 -594 359 -1076 875 -1082 891 -1088 363 -616 855 -1084 857 -592 381 -1088 883 -1086 385 -572 -RAW_Data: 889 -624 353 -1082 853 -1096 379 -594 853 -624 353 -1092 417 -582 847 -612 385 -1076 847 -1080 883 -1052 913 -1044 907 -1076 849 -1088 383 -602 867 -616 361 -1068 901 -1072 865 -1104 831 -1080 879 -1098 397 -586 855 -626 355 -1084 381 -592 873 -616 351 -1084 385 -624 821 -620 359 -1086 387 -584 883 -1086 877 -592 355 -1106 853 -1086 387 -69570 97 -100 99 -2620 131 -636 333 -102 235 -236 67 -68 363 -66 201 -100 567 -102 267 -164 101 -134 65 -68 197 -68 297 -166 671 -100 469 -336 165 -100 201 -66 169 -230 169 -204 329 -624 67 -98 265 -232 193 -168 299 -100 235 -138 101 -370 165 -294 333 -622 231 -130 129 -130 353 -132 195 -162 359 -164 67 -68 333 -100 133 -688 235 -236 497 -198 293 -98 129 -296 293 -164 229 -128 229 -132 193 -400 165 -66 163 -98 361 -164 355 -196 587 -164 131 -98 263 -554 99 -130 129 -130 191 -464 99 -132 67 -100 167 -604 329 -66 199 -68 133 -102 163 -66 2971 -132 785 -66 329 -96 323 -100 201 -136 301 -66 1959 -166 867 -134 467 -66 297 -100 835 -100 753 -166 165 -64 67 -370 335 -66 559 -232 165 -334 65 -162 129 -354 163 -64 131 -134 265 -300 263 -132 267 -296 327 -198 99 -132 535 -132 469 -866 231 -860 99 -232 503 -134 99 -198 233 -134 267 -200 97 -358 297 -164 259 -98 227 -166 135 -66 323 -100 97 -294 131 -164 129 -98 295 -96 129 -426 299 -100 67 -102 623 -100 163 -194 127 -360 563 -134 199 -428 493 -98 229 -130 257 -64 165 -100 131 -98 163 -692 357 -64 161 -98 321 -64 389 -230 65 -692 227 -130 261 -132 231 -162 287 -298 97 -460 393 -130 301 -168 331 -100 269 -202 101 -134 201 -102 99 -132 199 -204 235 -664 65 -562 133 -328 463 -100 291 -194 159 -162 227 -98 293 -328 165 -128 227 -574 535 -332 197 -168 65 -300 131 -66 389 -1078 131 -64 259 -64 223 -98 257 -164 63 -328 433 -134 65 -602 131 -68 333 -136 369 -66 297 -264 427 -66 97 -130 429 -102 133 -136 203 -240 167 -236 329 -526 67 -132 133 -168 331 -360 65 -66 331 -296 267 -134 469 -132 595 -230 661 -662 299 -100 265 -200 203 -168 801 -100 133 -68 399 -132 99 -100 161 -390 65 -298 65 -98 261 -130 161 -128 257 -66 67 -134 621 -98 227 -328 99 -230 129 -294 193 -96 195 -318 425 -526 129 -196 163 -162 65 -132 293 -130 63 -66 325 -128 63 -130 293 -66 199 -200 269 -206 133 -198 325 -98 163 -100 97 -98 261 -164 67 -98 167 -430 131 -494 131 -164 -RAW_Data: 97 -98 861 -66 1199 -166 231 -100 651 -166 197 -104 439 -98 131 -64 493 -98 883 -96 99 -98 3327 -66 131 -264 733 -134 2133 -166 131 -102 303 -136 535 -134 701 -98 355 -228 131 -202 99 -134 99 -100 791 -166 169 -202 671 -100 741 -100 263 -66 165 -68 935 -132 197 -198 673 -100 605 -66 1457 -98 1195 -166 2347 -134 505 -100 1469 -66 391 -100 229 -100 1171 -98 939 -100 459 -170 369 -134 231 -162 127 -98 95 -66 195 -98 195 -66 299 -100 331 -98 65 -232 369 -132 201 -68 167 -166 1481 -102 501 -160 1257 -66 2307 -64 623 -164 2079 -66 1101 -98 423 -64 659 -68 431 -136 99 -100 435 -130 167 -168 835 -200 135 -104 133 -100 503 -68 1437 -232 821 -132 357 -96 463 -66 263 -64 683 -132 165 -96 655 -166 3939 -100 1169 -132 2443 -98 197 -132 425 -234 233 -162 1043 -66 197 -100 2793 -134 167 -104 675 -100 197 -134 1367 -102 763 -132 265 -230 133 -102 365 -100 167 -66 1069 -66 837 -100 295 -160 97 -64 129 -132 617 -164 197 -100 133 -136 337 -172 133 -66 557 -98 951 -66 263 -130 587 -66 729 -196 335 -166 933 -432 369 -100 199 -296 225 -98 355 -66 129 -64 557 -98 289 -66 355 -128 193 -162 267 -134 299 -98 165 -170 303 -640 1031 -134 99 -66 135 -68 771 -166 171 -104 201 -134 131 -68 635 -428 661 -292 749 -430 1161 -100 905 -98 65 -98 657 -262 2837 -132 67 -66 265 -132 631 -66 1037 -296 97 -98 1703 -302 367 -100 505 -232 497 -362 333 -134 591 -100 755 -232 67 -130 587 -66 231 -168 65 -332 99 -66 267 -232 393 -134 65 -132 131 -428 133 -200 165 -202 199 -168 165 -102 269 -100 333 -852 201 -134 233 -202 65 -200 563 -768 265 -136 169 -102 169 -598 333 -202 267 -134 267 -328 163 -130 625 -500 199 -200 99 -270 65 -134 65 -198 65 -100 99 -596 493 -66 99 -66 331 -232 103 -136 373 -168 831 -170 65 -672 163 -102 133 -136 331 -100 333 -234 101 -100 99 -200 99 -100 201 -302 199 -600 301 -202 135 -134 705 -166 435 -530 97 -198 131 -198 195 -66 163 -392 293 -66 295 -370 229 -198 65 -100 405 -134 165 -134 133 -170 337 -236 205 -274 267 -134 329 -132 195 -132 503 -132 133 -136 133 -334 197 -196 299 -168 101 -100 233 -100 439 -134 301 -332 331 -298 433 -406 433 -68 167 -100 203 -100 101 -102 99 -328 397 -234 205 -168 133 -364 63 -202 397 -198 95 -394 267 -134 569 -66 201 -102 133 -136 101 -102 99 -132 99 -196 197 -498 197 -102 135 -170 -RAW_Data: 331 -164 63 -162 1267 -66 163 -130 129 -66 725 -164 231 -64 853 -66 101 -134 199 -102 99 -68 365 -66 357 -130 815 -64 357 -98 97 -98 97 -66 65 -466 231 -172 3749 -66 849 -130 917 -64 327 -64 1013 -98 555 -332 795 -100 571 -132 769 -132 401 -134 1297 -134 377 -138 435 -100 401 -100 667 -100 1761 -66 667 -66 1533 -236 233 -98 885 -130 457 -66 999 -66 165 -66 833 -134 695 -166 501 -66 499 -200 329 -64 197 -134 441 -100 2099 -98 491 -134 197 -130 2225 -132 65 -100 689 -64 193 -160 159 -96 195 -98 323 -164 259 -98 535 -472 771 -66 665 -270 665 -66 595 -266 2191 -64 643 -98 1287 -98 741 -100 233 -200 569 -194 261 -68 637 -100 97 -66 491 -158 395 -138 1017 -66 627 -262 559 -64 327 -98 263 -134 99 -102 201 -102 337 -66 167 -68 679 -100 471 -134 195 -66 133 -202 693 -96 197 -98 391 -164 99 -98 3883 -194 461 -100 237 -168 1891 -68 301 -68 969 -166 1439 -294 551 -130 389 -98 99 -196 167 -102 505 -66 569 -234 901 -98 407 -136 469 -66 769 -98 769 -166 1263 -266 297 -98 1701 -200 203 -168 329 -232 65 -100 329 -164 803 -100 135 -200 233 -166 135 -272 265 -134 197 -100 133 -134 539 -232 197 -396 165 -366 263 -68 233 -102 365 -132 233 -100 135 -266 199 -234 167 -232 97 -524 127 -128 389 -98 305 -364 261 -130 257 -162 589 -464 361 -66 229 -134 161 -100 203 -432 265 -66 199 -66 199 -366 229 -236 99 -134 99 -100 131 -168 133 -100 131 -236 267 -132 297 -264 291 -132 167 -234 65 -100 199 -66 333 -730 237 -440 365 -102 99 -100 99 -132 99 -100 1429 -134 427 -100 97 -100 131 -164 799 -170 1077 -100 431 -66 133 -168 737 -134 197 -230 65 -102 803 -132 491 -98 429 -198 471 -134 365 -66 299 -236 65 -66 2837 -102 399 -64 585 -64 523 -196 97 -98 295 -196 555 -160 261 -500 299 -396 333 -236 133 -68 327 -100 199 -204 699 -66 701 -100 65 -164 65 -370 195 -196 97 -66 193 -130 129 -360 195 -130 231 -96 291 -64 455 -228 293 -196 291 -162 97 -194 621 -130 847 -66 395 -66 161 -128 193 -130 293 -98 231 -170 67 -134 297 -360 167 -266 263 -526 263 -132 229 -98 191 -160 159 -100 721 -234 101 -100 99 -130 259 -258 265 -632 687 -164 133 -134 631 -100 199 -102 165 -560 299 -200 265 -332 431 -870 99 -266 503 -364 135 -66 269 -68 499 -100 265 -102 263 -102 569 -234 719 -132 99 -196 419 -262 163 -688 95 -66 165 -128 95 -66 -RAW_Data: 295 -98 987 -196 517 -100 489 -66 355 -132 563 -198 867 -134 1413 -134 541 -134 767 -100 193 -98 1799 -102 467 -134 299 -96 323 -66 261 -100 259 -66 229 -96 851 -66 369 -266 469 -66 101 -98 163 -136 267 -432 859 -130 523 -66 197 -134 1027 -132 227 -194 393 -98 807 -166 235 -100 133 -66 165 -102 133 -136 371 -162 1411 -132 865 -200 471 -100 133 -68 299 -66 633 -98 329 -234 401 -98 1505 -132 133 -134 331 -262 163 -66 261 -98 289 -64 201 -68 1055 -96 391 -66 951 -298 265 -202 297 -66 401 -68 131 -100 1733 -98 941 -66 803 -98 847 -64 3701 -100 721 -160 357 -166 1799 -66 329 -100 99 -102 363 -198 167 -136 197 -66 567 -66 199 -236 1247 -166 2455 -68 1107 -200 235 -100 2355 -130 913 -98 877 -98 163 -196 97 -66 427 -100 801 -134 867 -98 263 -68 441 -134 561 -98 1671 -134 865 -68 935 -132 163 -102 975 -66 1343 -132 1339 -134 369 -100 1107 -66 1167 -168 631 -232 835 -66 1027 -132 333 -166 265 -98 1207 -98 223 -98 455 -64 2095 -134 933 -136 233 -68 335 -136 305 -100 1737 -66 427 -100 263 -130 323 -66 227 -66 717 -100 265 -100 65 -128 355 -66 367 -132 95 -230 229 -100 131 -64 493 -132 291 -396 393 -130 259 -196 227 -288 397 -68 229 -430 99 -302 237 -700 65 -66 65 -100 133 -200 101 -336 133 -166 237 -202 67 -302 67 -68 333 -132 263 -102 267 -296 163 -166 233 -168 363 -64 295 -298 537 -166 431 -200 431 -166 63 -258 363 -164 563 -234 199 -68 299 -100 325 -754 295 -196 65 -98 165 -132 301 -134 131 -134 97 -68 405 -68 233 -134 271 -134 67 -168 101 -136 133 -366 99 -132 67 -132 265 -200 233 -100 201 -136 101 -66 263 -132 129 -66 293 -582 263 -132 1103 -134 203 -168 97 -66 197 -264 131 -168 133 -132 65 -134 199 -134 101 -100 131 -436 99 -232 97 -398 231 -362 65 -202 301 -396 297 -98 199 -134 265 -164 101 -168 267 -102 405 -170 99 -102 397 -132 97 -98 295 -98 1179 -100 135 -136 131 -134 765 -134 465 -168 439 -232 403 -100 65 -134 931 -100 169 -136 237 -68 231 -234 199 -68 401 -134 541 -166 429 -166 1607 -368 533 -66 363 -66 133 -134 433 -166 297 -238 201 -100 201 -170 199 -134 273 -136 99 -134 167 -238 133 -66 265 -134 165 -132 165 -132 97 -228 723 -198 415 -64 491 -298 257 -66 231 -192 225 -96 227 -98 193 -96 521 -198 65 -66 231 -166 163 -98 465 -66 133 -132 195 -130 225 -162 521 -130 63 -66 199 -228 -RAW_Data: 817 -162 449 -160 719 -198 469 -68 133 -68 1101 -132 593 -230 1105 -100 131 -134 231 -66 329 -196 685 -96 557 -68 1263 -68 101 -68 397 -100 65 -66 625 -66 97 -132 1099 -66 493 -66 757 -98 1151 -66 303 -134 1901 -66 99 -100 665 -262 991 -98 791 -66 1925 -168 865 -232 835 -98 505 -102 99 -100 535 -100 169 -134 427 -132 863 -68 167 -134 975 -100 133 -268 1339 -100 1453 -66 1445 -162 195 -64 3623 -66 237 -68 1063 -308 1449 -98 1111 -132 167 -102 855 -270 199 -134 297 -134 267 -168 863 -234 637 -66 567 -230 99 -200 3325 -198 845 -66 289 -66 131 -66 815 -130 1093 -100 167 -100 429 -98 1703 -166 195 -64 971 -98 163 -192 195 -168 439 -132 329 -132 67 -134 67 -134 1591 -168 407 -100 867 -68 399 -134 661 -100 663 -66 237 -136 395 -232 131 -66 695 -100 627 -264 913 -66 1083 -98 287 -66 199 -132 335 -100 1031 -68 99 -100 3815 -98 165 -66 129 -98 163 -128 563 -98 779 -96 223 -64 161 -164 2025 -66 1741 -172 101 -136 203 -102 665 -100 475 -64 167 -100 637 -98 997 -170 1207 -136 233 -166 233 -168 635 -132 199 -100 235 -270 199 -98 131 -102 169 -170 293 -98 323 -164 427 -334 233 -168 267 -68 369 -100 263 -368 101 -66 665 -98 265 -100 133 -100 99 -168 133 -66 133 -132 133 -66 269 -134 435 -68 267 -136 271 -500 163 -100 163 -166 355 -132 97 -98 323 -194 63 -688 463 -130 97 -396 65 -100 357 -194 461 -98 161 -130 223 -162 165 -352 461 -300 267 -166 233 -464 329 -100 293 -362 163 -228 289 -66 229 -66 195 -162 325 -66 261 -98 127 -424 299 -302 367 -68 265 -272 429 -98 161 -98 393 -296 65 -130 161 -196 261 -66 473 -234 97 -98 263 -160 323 -98 67 -132 697 -298 99 -134 233 -202 97 -134 301 -200 307 -100 101 -134 865 -166 231 -202 233 -100 301 -170 169 -102 169 -200 65 -98 595 -166 231 -234 661 -66 473 -334 165 -304 365 -266 97 -502 363 -134 133 -236 65 -100 99 -134 99 -170 235 -66 333 -100 195 -100 133 -300 133 -102 301 -304 65 -100 99 -100 131 -202 135 -134 65 -200 363 -66 263 -498 67 -68 295 -194 321 -368 435 -100 97 -664 99 -100 569 -66 133 -66 67 -134 199 -136 101 -68 301 -68 405 -198 133 -132 581 -132 165 -98 159 -98 197 -66 229 -130 131 -294 133 -96 423 -100 427 -300 357 -132 291 -64 95 -194 455 -98 263 -100 359 -196 65 -162 227 -162 157 -96 157 -230 589 -132 325 -134 535 -66 267 -100 135 -302 -RAW_Data: 131 -134 599 -166 393 -98 369 -236 197 -100 401 -232 569 -134 135 -70 337 -134 101 -136 135 -100 1895 -66 401 -170 503 -66 1633 -66 601 -66 355 -96 683 -100 729 -68 133 -132 433 -68 569 -100 133 -68 201 -132 835 -100 465 -68 527 -98 193 -200 1129 -166 535 -100 199 -98 259 -132 227 -64 1597 -98 261 -192 753 -100 911 -66 667 -298 131 -100 263 -66 1051 -230 787 -66 935 -66 233 -98 885 -236 431 -66 197 -162 521 -68 167 -196 263 -96 589 -98 517 -66 1439 -64 777 -66 3219 -132 679 -134 205 -68 507 -198 749 -200 199 -168 167 -100 133 -134 201 -68 731 -66 495 -198 737 -66 237 -68 135 -100 167 -234 1535 -68 873 -66 373 -66 67 -232 297 -68 65 -66 1095 -68 327 -130 63 -132 1715 -66 2261 -100 321 -132 197 -164 457 -232 1291 -132 405 -68 1001 -68 1133 -272 471 -66 99 -134 1403 -68 167 -68 1091 -336 933 -134 1207 -132 265 -68 267 -66 99 -366 265 -66 1469 -258 367 -168 429 -132 129 -66 491 -132 343 -100 65 -100 263 -136 199 -164 273 -204 791 -100 901 -66 167 -98 165 -64 559 -132 619 -132 1087 -128 2283 -398 1467 -164 259 -130 1927 -130 421 -98 1085 -66 705 -68 1843 -168 875 -170 203 -136 341 -640 199 -66 133 -554 161 -196 63 -66 521 -292 163 -160 95 -158 127 -192 197 -100 587 -130 397 -662 261 -66 193 -130 259 -66 361 -64 459 -98 197 -560 655 -130 389 -66 1135 -100 133 -130 131 -98 1011 -100 561 -66 685 -164 457 -132 2469 -200 609 -66 665 -66 67 -132 327 -200 1657 -134 919 -132 651 -100 327 -230 191 -130 263 -358 95 -130 549 -98 99 -68 299 -100 461 -132 99 -472 165 -134 99 -66 99 -132 399 -102 169 -102 697 -166 233 -132 333 -632 197 -164 865 -266 101 -68 533 -166 299 -100 163 -228 259 -66 327 -200 65 -66 229 -100 363 -230 197 -336 165 -102 893 -300 65 -132 231 -370 265 -230 99 -98 229 -518 199 -100 401 -724 225 -98 63 -96 231 -64 291 -292 65 -98 131 -98 159 -158 127 -194 161 -292 65 -98 133 -66 297 -66 303 -168 97 -168 231 -234 269 -532 135 -168 99 -168 301 -528 99 -506 199 -368 399 -132 329 -372 99 -68 133 -264 197 -100 201 -200 67 -134 131 -270 133 -134 133 -198 327 -200 65 -100 331 -262 161 -166 469 -534 167 -738 131 -100 367 -232 101 -100 265 -604 65 -170 99 -166 299 -102 169 -132 99 -398 229 -330 197 -166 335 -366 97 -98 131 -200 269 -100 199 -168 131 -134 537 -98 265 -100 335 -236 99 -366 -RAW_Data: 459 -100 453 -130 419 -130 519 -96 63 -130 2077 -66 767 -64 127 -134 1961 -296 529 -202 637 -134 527 -100 201 -68 633 -66 163 -360 1029 -68 765 -100 867 -66 503 -100 131 -66 841 -98 165 -68 237 -66 509 -100 501 -302 235 -66 99 -164 227 -130 551 -196 327 -66 1571 -132 99 -68 867 -66 163 -96 161 -130 129 -130 549 -130 487 -166 1801 -66 229 -66 197 -232 325 -66 425 -198 131 -64 295 -166 735 -66 533 -98 227 -130 129 -262 425 -100 263 -66 129 -132 97 -168 971 -170 405 -68 199 -134 475 -202 297 -98 1445 -98 395 -196 161 -66 225 -134 1803 -100 473 -102 1499 -66 199 -100 701 -132 165 -68 133 -102 303 -98 735 -102 805 -100 827 -100 235 -100 65 -266 637 -68 693 -66 1383 -228 819 -66 233 -304 435 -198 203 -136 1135 -270 1709 -64 227 -64 581 -134 505 -66 2203 -64 293 -64 753 -66 551 -132 747 -64 1303 -64 463 -66 229 -102 1877 -266 871 -166 1357 -64 819 -66 465 -198 693 -68 165 -64 95 -128 3785 -132 1465 -100 299 -102 329 -164 595 -134 1029 -66 299 -168 1263 -166 331 -68 967 -100 101 -102 603 -260 165 -132 467 -66 233 -66 235 -102 475 -100 135 -68 301 -134 297 -98 131 -102 269 -466 99 -134 237 -166 135 -168 203 -102 265 -68 503 -66 233 -66 637 -134 101 -200 199 -166 293 -554 361 -328 367 -264 533 -238 167 -68 135 -170 99 -300 591 -298 133 -236 299 -66 231 -368 263 -232 435 -136 133 -102 133 -200 133 -134 163 -134 167 -168 299 -66 265 -100 133 -240 135 -132 263 -170 269 -200 501 -396 263 -98 227 -132 129 -292 427 -66 165 -102 627 -602 99 -66 301 -168 199 -100 563 -330 165 -134 233 -136 65 -332 499 -100 131 -232 325 -96 65 -132 195 -98 393 -624 323 -68 133 -98 195 -162 231 -100 263 -132 231 -102 133 -236 99 -236 231 -166 65 -102 133 -268 101 -102 299 -136 267 -164 493 -64 229 -258 291 -326 263 -198 391 -134 167 -202 365 -594 133 -102 201 -134 503 -396 429 -204 169 -400 197 -170 267 -132 403 -466 297 -98 469 -234 395 -132 233 -100 165 -100 165 -66 197 -68 297 -166 501 -134 133 -100 65 -166 631 -68 297 -134 199 -100 165 -68 299 -266 133 -66 165 -100 231 -490 557 -134 371 -164 299 -170 733 -164 239 -334 335 -66 299 -300 199 -170 103 -100 233 -102 641 -168 65 -100 995 -66 265 -160 259 -130 129 -226 425 -100 355 -726 97 -688 99 -66 233 -266 299 -942 167 -102 167 -166 65 -100 367 -136 99 -134 199 -134 267 -164 -RAW_Data: 67 -68 233 -66 899 -66 163 -96 485 -98 355 -130 943 -100 235 -168 499 -104 1367 -98 297 -100 635 -68 1169 -100 67 -134 835 -264 959 -164 129 -98 419 -196 589 -66 421 -66 1717 -100 133 -100 265 -134 227 -356 455 -166 163 -66 1055 -100 1455 -134 463 -98 2191 -132 295 -132 335 -66 709 -64 619 -98 959 -68 835 -170 603 -134 1033 -134 635 -168 759 -232 397 -198 397 -164 1267 -166 257 -198 1295 -100 239 -104 563 -204 335 -198 203 -68 901 -68 1255 -134 1697 -66 793 -66 1691 -68 201 -100 765 -66 165 -132 131 -230 131 -66 917 -66 335 -338 231 -170 827 -98 199 -136 301 -196 65 -98 199 -200 765 -134 403 -98 333 -68 1691 -132 2565 -64 569 -170 1255 -264 65 -132 1243 -132 2527 -66 259 -66 1739 -100 1309 -198 167 -238 337 -66 131 -68 1973 -362 299 -100 1387 -96 129 -164 423 -230 3875 -96 4283 -98 165 -98 515 -134 469 -68 171 -102 1163 -100 65 -298 461 -66 367 -136 205 -168 371 -98 491 -164 161 -262 1093 -100 299 -100 269 -334 1205 -98 63 -98 261 -64 457 -98 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index 7d342bb9..928838d3 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -145,18 +145,3 @@ RAW_Data: -66 133 -66 97 -166 561 -100 895 -132 1323 -66 10873 -3752 99 -722 229 RAW_Data: -5434 65 -298 133 -132 131 -68 231 -200 661 -132 9517 -424 97 -1456 99 -1694 393 -100 131 -560 131 -196 197 -298 65 -428 229 -196 297 -266 131 -166 2435 -66 10161 -11230 65 -1320 131 -298 265 -532 231 -200 1291 -68 631 -66 12645 -4048 133 -66 67 -132 167 -266 163 -66 397 -132 197 -132 299 -98 197 -198 2903 -66 2361 -66 9627 -3588 197 -332 165 -68 331 -68 197 -132 99 -100 663 -66 363 -230 231 -166 131 -100 201 -298 163 -132 133 -202 363 -300 397 -102 263 -100 165 -66 1221 -66 1479 -132 165 -98 229 -12976 263 -66 363 -134 231 -66 629 -132 327 -100 97 -130 99 -164 227 -64 297 -132 397 -164 425 -198 97 -198 99 -66 365 -164 199 -102 97 -66 1817 -13524 231 -134 16907 -4086 233 -630 65 -396 201 -66 165 -198 67 -198 99 -664 2117 -166 12473 -446 2649 -440 2661 -420 2651 -422 2681 -418 2703 -400 365 -2724 387 -2696 2695 -414 357 -2704 2707 -386 389 -2700 2687 -392 405 -2706 2695 -402 363 -21268 2707 -388 377 -2706 2691 -404 2699 -382 2717 -382 2707 -378 2693 -416 2687 -396 363 -2736 355 -2748 2659 -416 365 -2708 2715 -388 377 -2708 2697 -404 363 -2730 2673 -420 355 -21268 2655 -460 319 -2766 2663 -448 2631 -436 2665 -418 2683 -410 2681 -416 2701 -386 383 -2700 375 -2744 2669 -416 353 -2730 2685 -416 357 -2708 2721 -380 369 -2724 2697 -382 385 -21260 2701 -418 353 -2720 2673 -418 2675 -408 2693 -384 2715 -386 2717 -386 2691 -404 363 -2732 387 -2702 2669 -412 359 -2736 2699 -380 381 -2728 2675 -416 381 -2720 2675 -414 347 -21280 2685 -390 377 -2724 2689 -416 2673 -408 2705 -382 2695 -410 2689 -414 2661 -418 385 -2704 369 -2704 2693 -416 375 -2726 2661 -420 355 -2728 2711 -388 375 -2702 2691 -410 363 -21252 2659 -488 287 -2794 2651 -448 2629 -436 2671 -416 2695 -416 2663 -406 2699 -384 383 -2730 367 -2702 2695 -418 385 -2702 2685 -412 349 -2744 2693 -366 389 -2714 2693 -394 381 -21266 2685 -418 363 -2730 2683 -382 2693 -418 2675 -410 2699 -384 2719 -382 2707 -380 359 -2734 387 -2704 2709 -380 361 -2732 2699 -418 357 -2728 2667 -416 383 -2696 2709 -380 391 -21228 2685 -458 307 -2800 2647 -412 2659 -432 2667 -416 2695 -416 2675 -406 2675 -416 383 -2700 361 -2730 2687 -414 375 -2696 2701 -420 353 -2720 2711 -382 367 -2728 2675 -416 385 -21222 2735 -386 355 -2744 2687 -396 2679 -418 2701 -386 2705 -382 2681 -410 2697 -384 385 -2736 365 -2704 2715 -384 377 -2696 2697 -416 349 -2722 2707 -386 379 -2732 2671 -410 361 -21258 2681 -464 297 -2796 2629 -456 2655 -420 2661 -448 2663 -404 2695 -382 2715 -380 371 -2740 355 -2744 2679 -384 391 -2728 2675 -388 379 RAW_Data: -2728 2695 -414 357 -2704 2705 -418 357 -21262 2673 -416 383 -2696 2709 -380 2703 -384 2699 -418 2671 -408 2695 -382 2713 -386 379 -2730 357 -2732 2695 -384 383 -2730 2679 -416 357 -2708 2701 -410 349 -2736 2697 -382 385 -21252 2669 -478 289 -2790 2647 -426 2651 -444 2653 -430 2659 -418 2695 -414 2681 -402 349 -2738 383 -2722 2677 -414 347 -2744 2691 -382 369 -2730 2691 -384 383 -2734 2679 -414 347 -21264 2705 -386 379 -2736 2667 -410 2695 -382 2715 -380 2709 -420 2665 -392 2713 -382 383 -2730 365 -2728 2665 -418 383 -2696 2693 -418 357 -2710 2711 -380 375 -2718 2701 -416 357 -21238 2677 -484 311 -2766 2635 -444 2657 -420 2663 -422 2695 -416 2667 -428 2675 -396 363 -73890 133 -98 131 -132 129 -658 99 -66 853 -100 63 -100 361 -98 1589 -66 1231 -132 65 -100 297 -198 65 -132 265 -66 9857 -4672 165 -1030 97 -1394 65 -200 2687 -68 6873 -8336 99 -1156 97 -66 163 -232 163 -262 197 -132 295 -132 263 -166 953 -100 263 -130 393 -164 295 -64 329 -66 393 -164 823 -130 165 -66 6133 -8436 165 -164 265 -266 65 -362 197 -696 3181 -132 363 -98 65 -166 131 -66 399 -132 663 -396 329 -66 7335 -7578 497 -230 627 -264 99 -366 99 -132 131 -134 265 -498 163 -100 1323 -66 265 -66 1129 -100 399 -132 365 -100 795 -68 397 -98 597 -364 297 -132 361 -132 265 -132 8591 -4740 65 -100 131 -166 199 -1088 97 -296 99 -528 131 -98 661 -66 401 -198 1157 -166 361 -164 495 -100 165 -66 297 -100 1423 -66 3067 -5658 67 -6406 197 -1092 65 -530 659 -68 265 -100 991 -68 231 -230 297 -66 327 -66 131 -132 659 -134 131 -100 1183 -132 263 -98 621 -66 2075 -6976 65 -5138 67 -132 129 -664 67 -132 165 -100 331 -466 231 -68 467 -98 563 -66 231 -100 531 -66 465 -66 1023 -166 297 -134 3409 -12290 67 -164 99 -532 133 -166 263 -66 231 -66 721 -64 131 -68 959 -134 495 -100 299 -98 497 -98 365 -100 397 -232 297 -98 531 -66 3029 -12216 265 -132 99 -364 199 -234 131 -66 431 -166 333 -166 397 -132 327 -100 395 -66 197 -132 395 -66 527 -98 295 -100 97 -98 789 -132 363 -132 297 -200 2815 -4914 65 -6620 65 -462 65 -134 297 -66 497 -264 231 -198 2773 -134 365 -100 831 -166 131 -100 297 -132 861 -132 299 -100 561 -66 1381 -6946 65 -5516 231 -266 97 -1362 1093 -68 1621 -134 165 -332 297 -98 361 -228 97 -132 797 -98 3487 -13224 229 -164 65 -132 913 -66 1123 -98 527 -134 929 -98 723 -100 12259 -270 165 -132 67 -132 165 -1326 99 -98 65 -1194 431 -66 695 -66 733 -134 197 RAW_Data: -134 10801 -166 67 -6130 133 -198 231 -334 365 -98 229 -132 165 -68 231 -166 14501 -524 65 -328 131 -498 129 -1288 65 -494 163 -64 165 -66 527 -132 131 -132 1019 -198 129 -166 393 -198 65 -164 6411 -66 3255 -10642 65 -1320 165 -164 493 -492 559 -264 2555 -66 695 -66 1657 -164 855 -66 4001 -10526 97 -596 133 -298 67 -264 65 -300 65 -100 263 -166 231 -134 99 -100 2703 -68 13643 -4922 297 -100 65 -232 133 -198 331 -300 231 -66 331 -100 12047 -3872 97 -196 65 -494 329 -66 65 -890 97 -98 229 -164 195 -596 797 -66 861 -132 65 -66 231 -100 565 -66 65 -66 1297 -132 265 -66 363 -134 265 -364 297 -164 299 -134 297 -134 495 -98 11309 -3790 131 -1380 65 -758 65 -164 129 -460 65 -360 199 -100 563 -68 497 -198 363 -266 263 -100 165 -66 697 -66 1933 -13594 65 -762 1223 -132 1119 -196 361 -134 131 -100 793 -166 695 -68 231 -68 463 -66 11727 -4204 363 -264 131 -132 133 -1124 97 -100 163 -100 327 -100 331 -198 397 -66 397 -100 395 -100 163 -66 197 -564 1059 -7962 65 -100 65 -198 129 -362 99 -394 197 -296 495 -100 1357 -68 459 -66 593 -66 265 -68 301 -132 465 -66 231 -200 397 -66 397 -232 199 -298 12077 -4350 231 -796 363 -198 133 -264 65 -1132 597 -332 3295 -100 755 -98 231 -164 97 -264 459 -166 759 -164 3265 -12138 99 -232 99 -1228 1025 -100 393 -66 531 -132 693 -132 1063 -66 427 -64 297 -294 229 -98 9723 -5404 67 -466 99 -796 267 -98 201 -100 167 -264 461 -98 1415 -66 861 -66 267 -66 331 -134 1663 -66 2089 -7012 65 -100 101 -4804 431 -728 99 -100 65 -100 995 -134 165 -66 929 -100 65 -66 927 -100 1093 -168 99 -100 497 -66 665 -200 6517 -8312 165 -66 129 -66 559 -166 99 -430 65 -398 67 -66 593 -198 459 -132 261 -132 263 -130 723 -66 459 -100 325 -166 67 -198 559 -66 493 -66 11475 -3896 99 -266 99 -66 197 -1092 129 -198 361 -166 163 -98 263 -196 759 -100 265 -100 365 -630 4635 -12748 65 -1712 461 -100 497 -66 395 -98 265 -98 229 -164 529 -132 297 -66 565 -132 987 -132 8665 -2820 2265 -450 313 -2774 2643 -442 325 -2772 2665 -416 359 -2734 2667 -386 379 -21274 2657 -474 293 -2810 2619 -466 2613 -476 2629 -452 2663 -388 2683 -418 2705 -400 365 -2722 387 -2700 2697 -380 361 -2732 2691 -418 361 -2732 2667 -416 383 -2698 2697 -416 357 -21238 2715 -384 383 -2732 2685 -416 2667 -416 2695 -398 2671 -418 2687 -390 2713 -382 383 -2730 365 -2728 2661 -416 379 -2716 2685 -384 379 -2720 2703 -378 401 -2718 2671 -RAW_Data: 889 -130 325 -64 457 -560 165 -68 199 -170 67 -66 265 -132 133 -666 67 -166 431 -66 201 -98 297 -100 595 -66 199 -134 65 -100 795 -132 99 -168 501 -200 331 -132 265 -102 265 -134 423 -98 521 -226 65 -166 431 -134 99 -100 133 -464 195 -326 623 -100 673 -98 321 -200 65 -136 369 -166 65 -68 97 -166 165 -334 265 -102 231 -166 101 -170 65 -170 265 -136 931 -100 133 -134 563 -66 333 -100 427 -66 163 -390 231 -66 193 -130 461 -166 557 -100 99 -198 263 -100 197 -294 231 -232 299 -134 199 -170 267 -134 631 -98 235 -100 499 -68 463 -100 65 -134 335 -170 273 -134 297 -100 67 -66 197 -166 67 -134 301 -168 537 -470 99 -134 433 -132 199 -192 261 -100 523 -164 459 -132 259 -332 359 -64 227 -96 131 -132 687 -132 363 -136 329 -434 99 -334 133 -100 401 -132 233 -700 233 -170 337 -66 371 -68 233 -202 531 -266 731 -66 465 -100 167 -100 133 -232 335 -166 239 -102 367 -232 231 -100 167 -134 201 -136 301 -168 199 -300 231 -98 237 -134 233 -102 329 -132 261 -134 199 -66 265 -136 99 -170 167 -134 199 -166 167 -136 367 -298 197 -200 99 -166 469 -136 439 -66 303 -134 295 -100 433 -134 899 -266 363 -132 197 -160 555 -324 129 -96 97 -128 257 -132 97 -394 257 -98 195 -166 459 -332 395 -132 633 -134 301 -100 131 -332 169 -168 395 -166 263 -540 783 -100 287 -130 295 -96 225 -296 133 -98 99 -100 461 -164 545 -130 99 -66 301 -68 265 -100 235 -134 235 -70 333 -102 497 -66 233 -364 301 -170 103 -66 165 -336 733 -200 133 -100 263 -102 65 -136 465 -200 1035 -198 165 -170 67 -302 631 -100 429 -332 65 -128 129 -130 159 -128 159 -66 161 -96 325 -164 261 -100 197 -162 65 -96 99 -130 65 -102 333 -100 199 -98 389 -330 129 -128 229 -66 425 -366 229 -64 261 -100 227 -96 227 -526 301 -200 97 -66 699 -334 67 -100 399 -198 787 -98 297 -134 429 -100 3245 -64 527 -98 131 -526 633 -68 133 -302 1459 -164 971 -102 237 -136 1439 -266 1131 -66 599 -200 303 -332 325 -130 389 -166 371 -66 333 -102 65 -100 233 -234 327 -266 233 -166 297 -100 225 -130 163 -336 99 -596 199 -330 131 -66 331 -338 263 -358 197 -168 877 -66 227 -96 63 -130 263 -162 225 -290 197 -198 357 -132 297 -262 165 -456 227 -98 399 -296 95 -132 99 -98 457 -200 199 -168 535 -100 567 -134 327 -130 193 -130 683 -102 101 -132 233 -170 943 -166 827 -66 267 -102 503 -68 1325 -164 -RAW_Data: 1607 -68 233 -166 1167 -70 531 -134 335 -168 131 -66 299 -402 899 -66 461 -66 457 -98 953 -98 165 -66 293 -230 881 -64 393 -166 589 -66 289 -66 1093 -204 333 -98 2745 -132 2019 -170 925 -68 269 -102 1469 -136 2301 -68 1355 -100 527 -66 975 -68 1445 -98 2397 -100 1733 -66 703 -100 995 -100 135 -136 235 -202 167 -134 2071 -166 339 -170 201 -268 129 -66 465 -66 365 -100 197 -164 129 -98 161 -96 423 -66 675 -66 1543 -136 567 -200 767 -202 65 -100 1401 -66 623 -136 567 -234 67 -236 197 -194 97 -66 263 -66 1827 -392 1893 -98 165 -268 133 -132 231 -162 225 -98 695 -198 563 -100 301 -332 267 -102 341 -66 99 -132 1299 -130 525 -68 161 -96 357 -98 353 -100 131 -100 131 -98 163 -132 323 -100 535 -66 1323 -130 133 -66 235 -134 1497 -132 387 -98 129 -162 2623 -134 163 -68 167 -66 959 -232 495 -68 131 -134 867 -134 865 -66 333 -98 305 -134 231 -98 765 -198 397 -432 165 -66 165 -366 265 -102 541 -100 261 -162 331 -134 457 -66 491 -196 97 -266 193 -262 65 -166 231 -266 497 -360 263 -98 587 -164 259 -98 231 -66 359 -100 267 -102 271 -168 97 -262 63 -66 261 -130 227 -130 295 -164 65 -66 265 -200 597 -134 267 -170 603 -100 97 -466 231 -264 97 -168 99 -66 65 -200 199 -100 267 -404 303 -102 201 -204 235 -134 131 -198 335 -298 327 -130 291 -164 63 -162 295 -262 197 -130 95 -130 195 -96 159 -130 161 -66 231 -100 165 -66 199 -134 363 -66 267 -168 165 -168 167 -100 165 -530 363 -432 99 -232 65 -132 395 -328 229 -98 197 -132 161 -96 191 -292 197 -204 133 -100 399 -166 531 -332 235 -168 99 -66 325 -158 553 -132 129 -226 231 -134 99 -462 129 -64 289 -100 193 -66 355 -164 291 -198 131 -298 197 -198 373 -268 335 -234 427 -68 199 -132 267 -232 131 -66 783 -326 63 -162 161 -130 227 -66 259 -562 233 -464 303 -102 201 -334 301 -134 297 -198 229 -66 127 -166 99 -100 197 -198 571 -66 457 -134 361 -424 131 -328 163 -98 63 -100 505 -102 201 -1094 229 -164 65 -230 789 -236 2505 -166 201 -170 163 -64 1139 -66 927 -100 295 -198 723 -100 365 -66 459 -196 3033 -272 199 -66 499 -202 1319 -232 295 -298 131 -362 97 -164 129 -132 65 -98 197 -130 129 -98 261 -130 97 -98 229 -96 425 -66 227 -166 483 -66 163 -326 567 -68 235 -68 67 -66 167 -66 235 -330 425 -164 63 -66 427 -102 167 -66 669 -132 429 -200 65 -102 133 -100 197 -368 -RAW_Data: 65 -134 2481 -228 65 -130 229 -228 763 -136 603 -166 1619 -98 1763 -102 837 -166 321 -66 951 -130 2067 -66 259 -132 1835 -66 437 -102 701 -66 565 -68 363 -70 1113 -66 1989 -164 257 -128 351 -162 1055 -232 265 -170 309 -200 435 -166 833 -102 2467 -132 595 -66 773 -166 1615 -98 131 -96 485 -64 517 -166 197 -68 1231 -68 403 -100 263 -134 233 -100 503 -100 333 -266 729 -66 199 -100 369 -68 1239 -100 197 -68 299 -170 337 -100 825 -132 163 -66 4205 -64 161 -100 635 -66 907 -66 1017 -166 1709 -100 201 -266 657 -68 463 -166 331 -164 293 -64 259 -162 129 -262 597 -134 701 -136 67 -168 235 -136 303 -170 1417 -66 263 -98 857 -100 659 -166 97 -100 2497 -64 2495 -98 719 -128 227 -130 2217 -164 623 -264 719 -134 329 -98 1371 -100 553 -294 165 -66 1163 -100 329 -196 649 -200 1123 -68 263 -100 593 -266 333 -102 1133 -136 131 -132 603 -200 1819 -66 489 -66 563 -266 1113 -230 165 -66 423 -68 335 -100 101 -100 1073 -132 897 -100 101 -100 499 -134 173 -138 763 -238 371 -130 403 -166 203 -102 271 -136 269 -166 99 -168 263 -96 425 -66 331 -234 133 -400 231 -132 453 -66 459 -164 199 -68 237 -132 163 -198 161 -196 265 -132 65 -64 195 -130 357 -164 663 -68 167 -600 131 -98 133 -304 203 -134 433 -98 261 -130 199 -100 237 -100 229 -326 99 -98 331 -132 99 -294 165 -66 303 -134 99 -232 133 -136 99 -68 267 -198 233 -138 67 -166 367 -100 333 -168 267 -200 369 -266 135 -404 1939 -132 231 -160 161 -64 293 -98 331 -132 339 -104 135 -100 197 -430 263 -202 233 -64 195 -162 129 -64 227 -298 265 -68 697 -66 301 -68 231 -300 131 -368 769 -234 265 -98 195 -324 97 -752 229 -126 355 -98 257 -98 287 -64 427 -132 295 -262 197 -170 369 -102 267 -100 169 -68 201 -102 2551 -136 635 -134 639 -134 99 -132 197 -200 371 -66 731 -132 199 -138 733 -304 433 -68 729 -440 197 -68 99 -102 165 -266 261 -164 491 -296 489 -194 257 -164 133 -134 237 -68 335 -98 227 -130 229 -98 295 -98 231 -202 267 -236 233 -136 331 -130 195 -128 261 -430 261 -162 97 -224 99 -130 193 -96 197 -162 229 -396 97 -98 227 -364 267 -100 99 -100 233 -236 697 -164 227 -196 63 -98 327 -230 325 -66 129 -196 95 -98 195 -130 325 -430 131 -194 129 -454 161 -196 235 -68 433 -134 667 -164 355 -236 101 -98 2143 -134 1827 -198 63 -198 65 -64 2859 -64 619 -66 97 -130 3157 -66 679 -194 1491 -98 -RAW_Data: 951 -64 393 -100 955 -132 4715 -100 131 -66 199 -204 1541 -66 929 -130 1347 -166 665 -132 233 -132 67 -102 433 -100 595 -228 997 -66 505 -68 133 -98 231 -68 571 -134 1371 -232 231 -270 135 -102 97 -66 867 -100 269 -68 967 -100 1649 -66 65 -66 951 -68 65 -202 363 -200 779 -102 1449 -294 419 -130 361 -230 1079 -164 163 -260 893 -102 333 -100 533 -166 467 -100 135 -66 135 -202 369 -100 199 -100 269 -134 301 -166 229 -66 101 -134 199 -134 1293 -64 779 -62 831 -66 1243 -68 267 -102 197 -100 395 -98 455 -64 621 -132 877 -98 199 -100 2101 -134 503 -100 2035 -134 735 -236 475 -136 237 -132 133 -134 1229 -100 133 -66 167 -68 2655 -100 1807 -100 1095 -264 825 -98 163 -66 491 -98 161 -128 953 -100 773 -100 131 -66 67 -134 457 -130 63 -64 389 -98 715 -66 425 -300 97 -100 1515 -66 303 -68 99 -98 721 -64 887 -132 65 -132 165 -66 635 -68 2801 -66 1561 -100 751 -98 129 -64 725 -136 201 -100 333 -204 573 -104 1745 -134 99 -66 129 -64 595 -134 167 -102 337 -134 567 -134 1131 -138 1207 -100 269 -68 135 -100 1143 -134 2139 -68 1701 -162 991 -596 431 -66 99 -132 657 -66 391 -320 357 -260 259 -98 429 -66 163 -228 65 -130 227 -66 261 -166 99 -98 131 -366 199 -134 463 -102 201 -98 231 -102 639 -238 301 -568 169 -610 265 -102 841 -198 297 -100 335 -132 263 -266 265 -68 469 -134 267 -68 933 -298 333 -298 729 -168 135 -136 437 -132 1137 -134 199 -68 265 -132 463 -166 129 -130 227 -98 297 -98 65 -132 97 -202 199 -232 305 -66 165 -198 365 -66 99 -98 299 -170 65 -136 301 -232 99 -564 133 -132 233 -170 99 -102 131 -134 65 -204 101 -98 297 -98 167 -762 233 -298 99 -326 395 -66 299 -132 369 -504 333 -98 483 -200 457 -164 63 -164 329 -162 65 -622 231 -268 131 -132 133 -134 131 -134 131 -66 99 -100 231 -66 167 -336 165 -98 197 -100 97 -264 321 -98 521 -132 163 -130 129 -294 297 -134 101 -102 265 -168 497 -68 197 -68 499 -134 269 -398 267 -130 203 -302 65 -498 271 -136 465 -292 131 -294 163 -198 329 -96 129 -98 193 -130 391 -330 165 -134 167 -170 297 -102 133 -136 135 -366 199 -132 423 -132 395 -168 65 -166 401 -98 229 -98 329 -98 99 -130 129 -228 261 -160 127 -426 389 -162 193 -132 131 -100 231 -168 67 -304 201 -68 765 -132 161 -162 193 -64 195 -64 295 -130 787 -98 419 -528 429 -66 363 -134 131 -100 133 -200 331 -98 -RAW_Data: 431 -66 1167 -68 937 -68 1003 -66 99 -132 941 -134 65 -66 365 -274 165 -236 367 -96 557 -134 675 -66 261 -164 127 -96 391 -164 161 -98 391 -292 163 -98 519 -196 165 -98 523 -66 195 -160 3343 -66 661 -100 2589 -136 307 -100 629 -136 639 -100 133 -168 405 -100 267 -66 465 -132 1171 -64 749 -64 165 -98 983 -100 163 -202 537 -66 327 -100 669 -100 401 -236 2885 -164 439 -134 97 -426 1931 -66 1385 -98 715 -98 519 -66 289 -162 97 -360 297 -166 163 -66 289 -66 555 -334 167 -230 429 -102 267 -132 943 -136 401 -68 929 -130 193 -68 467 -198 335 -66 963 -100 597 -132 197 -260 523 -232 1115 -102 1935 -66 1395 -134 305 -100 99 -66 199 -66 1071 -66 2357 -66 367 -498 769 -234 163 -130 191 -64 1211 -200 133 -102 201 -100 561 -366 361 -98 195 -100 537 -64 165 -196 1041 -332 133 -102 441 -230 4217 -66 1033 -66 167 -66 933 -100 565 -66 331 -164 673 -104 441 -66 533 -66 2095 -164 525 -66 297 -170 965 -198 421 -100 663 -832 65 -100 331 -164 231 -166 135 -168 237 -466 761 -134 891 -196 791 -198 257 -160 161 -98 293 -66 1081 -98 229 -130 327 -66 1301 -200 331 -166 101 -66 461 -100 2619 -132 1663 -98 1609 -134 499 -332 165 -370 67 -264 97 -96 259 -98 701 -402 197 -128 527 -236 233 -102 167 -134 303 -134 99 -166 299 -132 165 -200 467 -68 305 -168 207 -102 465 -102 729 -136 101 -374 327 -96 259 -98 467 -202 65 -66 673 -98 335 -404 135 -66 339 -204 99 -366 233 -68 365 -166 133 -102 867 -198 163 -162 163 -294 463 -332 165 -68 269 -268 331 -100 131 -166 299 -132 231 -400 263 -164 131 -266 267 -264 367 -66 371 -134 229 -104 267 -232 67 -466 265 -100 101 -100 165 -200 65 -200 301 -66 199 -168 233 -98 267 -66 67 -134 261 -196 261 -234 427 -294 65 -194 193 -66 259 -132 849 -96 63 -198 167 -294 95 -98 361 -164 261 -196 131 -132 437 -100 597 -262 327 -162 295 -98 295 -164 259 -196 425 -230 321 -66 195 -66 261 -496 99 -200 529 -132 133 -966 133 -132 165 -66 63 -128 491 -402 65 -262 299 -66 299 -202 265 -100 99 -668 97 -134 65 -100 101 -66 65 -266 691 -66 431 -166 167 -134 199 -370 899 -134 99 -100 1093 -166 163 -166 399 -98 327 -100 99 -168 135 -200 133 -202 429 -98 65 -98 197 -556 65 -66 97 -326 331 -166 333 -200 135 -100 235 -234 265 -98 65 -68 135 -66 335 -66 133 -298 99 -66 233 -164 435 -232 97 -132 97 -392 -RAW_Data: 99 -198 819 -66 1235 -98 321 -132 1091 -66 1307 -98 3059 -164 3305 -64 227 -98 591 -98 129 -66 229 -98 2143 -98 939 -68 563 -100 361 -232 945 -164 257 -96 229 -230 387 -64 195 -130 981 -294 587 -162 193 -98 1337 -66 293 -98 2665 -66 297 -98 647 -66 459 -132 491 -164 489 -96 595 -66 899 -66 837 -64 1151 -196 259 -98 357 -164 891 -132 1359 -134 197 -98 97 -98 261 -64 229 -96 461 -136 693 -100 201 -98 865 -66 599 -100 517 -132 709 -66 293 -298 655 -66 197 -130 129 -66 197 -98 4291 -66 673 -66 667 -132 1473 -132 133 -104 99 -66 163 -168 333 -134 1743 -132 1097 -132 99 -68 167 -602 1323 -352 99 -166 753 -98 423 -98 97 -66 1317 -228 1309 -98 1849 -66 1939 -132 601 -100 665 -100 1875 -66 695 -132 425 -66 425 -66 263 -134 165 -134 99 -98 829 -66 601 -166 131 -102 565 -66 301 -100 1099 -100 601 -138 533 -66 667 -234 561 -66 99 -68 2741 -98 199 -100 531 -168 101 -434 1027 -68 431 -66 403 -132 99 -98 565 -132 135 -100 399 -166 271 -236 233 -166 197 -366 99 -66 99 -168 503 -66 199 -170 207 -100 673 -368 99 -66 263 -168 133 -98 397 -268 337 -66 131 -132 231 -132 501 -134 99 -168 567 -138 103 -136 267 -298 231 -134 197 -160 321 -332 231 -98 131 -164 257 -64 163 -328 395 -66 331 -202 65 -168 133 -68 167 -100 233 -102 335 -66 197 -326 1101 -132 589 -100 811 -132 399 -136 269 -102 497 -66 559 -100 129 -98 855 -68 637 -102 65 -200 875 -68 233 -166 167 -66 529 -202 235 -102 231 -66 1237 -66 733 -98 1723 -132 101 -100 297 -66 829 -232 197 -100 367 -134 169 -166 167 -434 633 -100 235 -200 131 -134 233 -100 131 -100 331 -134 495 -432 65 -528 161 -130 295 -132 337 -136 133 -166 165 -100 269 -240 201 -336 133 -166 165 -238 199 -202 431 -434 99 -134 501 -166 231 -96 559 -202 167 -66 717 -98 987 -198 65 -64 163 -64 227 -98 555 -164 199 -64 361 -66 163 -98 129 -162 97 -130 161 -460 197 -230 681 -98 197 -98 329 -100 267 -266 291 -264 65 -100 329 -100 459 -200 363 -98 165 -134 231 -134 301 -134 231 -302 99 -132 101 -134 267 -136 233 -68 393 -422 163 -166 361 -166 99 -134 365 -134 133 -336 401 -66 495 -132 401 -168 133 -402 501 -136 1093 -862 165 -132 293 -300 289 -66 131 -164 391 -134 99 -360 359 -130 323 -200 423 -98 195 -162 295 -132 161 -98 129 -782 131 -426 227 -64 259 -166 63 -160 323 -98 261 -230 -RAW_Data: 231 -66 921 -66 355 -64 1019 -98 227 -258 163 -66 597 -232 1313 -132 163 -404 467 -236 901 -164 483 -98 195 -96 489 -134 103 -238 169 -66 67 -68 299 -100 497 -68 65 -134 1635 -304 1153 -100 539 -168 265 -200 499 -166 535 -100 397 -168 931 -100 131 -66 631 -134 897 -270 1233 -100 65 -132 131 -334 663 -66 163 -66 131 -132 705 -98 571 -200 433 -100 237 -234 229 -132 1627 -66 569 -100 715 -66 1863 -272 265 -68 301 -98 465 -68 97 -134 99 -66 395 -136 1405 -66 529 -132 63 -196 579 -132 413 -260 129 -136 101 -166 1201 -134 833 -134 393 -66 335 -172 201 -68 1027 -96 753 -64 815 -66 97 -64 1341 -132 289 -160 127 -66 99 -228 1083 -96 163 -66 259 -64 159 -98 2409 -168 767 -200 367 -66 1675 -66 1067 -98 3407 -200 99 -66 1403 -166 99 -134 439 -200 329 -136 599 -66 637 -66 835 -66 1099 -98 99 -66 463 -166 165 -100 461 -164 3037 -66 655 -66 97 -98 229 -130 355 -132 1443 -66 527 -98 881 -98 229 -162 127 -96 583 -64 65 -162 489 -166 885 -194 257 -98 1539 -66 293 -166 229 -132 655 -98 757 -49522 271 -758 689 -1264 737 -670 293 -1152 811 -1144 341 -664 773 -678 327 -1118 807 -1144 835 -1146 781 -1126 873 -1096 347 -622 877 -624 321 -1106 843 -1098 871 -1098 843 -1106 379 -610 841 -584 381 -1122 365 -602 845 -1116 837 -610 381 -1056 889 -1078 383 -614 827 -1110 877 -592 353 -1108 845 -1120 839 -1120 347 -602 849 -1110 865 -612 361 -1072 869 -1114 351 -618 861 -618 343 -1090 853 -1106 387 -618 797 -674 347 -1084 389 -574 867 -584 381 -1114 841 -1102 845 -1116 839 -1112 843 -1098 875 -1086 383 -584 865 -588 375 -1100 861 -1112 851 -1084 853 -1108 847 -1106 381 -584 857 -610 383 -1080 357 -602 871 -602 385 -1084 383 -616 823 -610 373 -1086 381 -590 871 -1084 839 -628 353 -1102 875 -1100 349 -9404 875 -1060 871 -1086 887 -1088 879 -1058 863 -1086 855 -1132 845 -1078 871 -1076 857 -1098 881 -1082 861 -1088 843 -1120 853 -1074 879 -1074 879 -1068 889 -614 341 -1090 387 -616 863 -624 345 -1088 391 -590 857 -612 385 -1058 393 -596 843 -1088 889 -1078 879 -578 387 -1082 875 -1076 415 -550 881 -1070 877 -592 391 -1114 821 -1104 373 -620 821 -624 361 -1072 903 -1086 855 -1092 843 -1086 905 -1054 387 -614 863 -618 347 -1088 853 -1114 845 -1090 867 -1070 381 -610 885 -584 385 -1052 407 -578 877 -1052 899 -600 389 -1048 907 -1074 383 -586 877 -1072 877 -594 359 -1076 875 -1082 891 -1088 363 -616 855 -1084 857 -592 381 -1088 883 -1086 385 -572 -RAW_Data: 889 -624 353 -1082 853 -1096 379 -594 853 -624 353 -1092 417 -582 847 -612 385 -1076 847 -1080 883 -1052 913 -1044 907 -1076 849 -1088 383 -602 867 -616 361 -1068 901 -1072 865 -1104 831 -1080 879 -1098 397 -586 855 -626 355 -1084 381 -592 873 -616 351 -1084 385 -624 821 -620 359 -1086 387 -584 883 -1086 877 -592 355 -1106 853 -1086 387 -69570 97 -100 99 -2620 131 -636 333 -102 235 -236 67 -68 363 -66 201 -100 567 -102 267 -164 101 -134 65 -68 197 -68 297 -166 671 -100 469 -336 165 -100 201 -66 169 -230 169 -204 329 -624 67 -98 265 -232 193 -168 299 -100 235 -138 101 -370 165 -294 333 -622 231 -130 129 -130 353 -132 195 -162 359 -164 67 -68 333 -100 133 -688 235 -236 497 -198 293 -98 129 -296 293 -164 229 -128 229 -132 193 -400 165 -66 163 -98 361 -164 355 -196 587 -164 131 -98 263 -554 99 -130 129 -130 191 -464 99 -132 67 -100 167 -604 329 -66 199 -68 133 -102 163 -66 2971 -132 785 -66 329 -96 323 -100 201 -136 301 -66 1959 -166 867 -134 467 -66 297 -100 835 -100 753 -166 165 -64 67 -370 335 -66 559 -232 165 -334 65 -162 129 -354 163 -64 131 -134 265 -300 263 -132 267 -296 327 -198 99 -132 535 -132 469 -866 231 -860 99 -232 503 -134 99 -198 233 -134 267 -200 97 -358 297 -164 259 -98 227 -166 135 -66 323 -100 97 -294 131 -164 129 -98 295 -96 129 -426 299 -100 67 -102 623 -100 163 -194 127 -360 563 -134 199 -428 493 -98 229 -130 257 -64 165 -100 131 -98 163 -692 357 -64 161 -98 321 -64 389 -230 65 -692 227 -130 261 -132 231 -162 287 -298 97 -460 393 -130 301 -168 331 -100 269 -202 101 -134 201 -102 99 -132 199 -204 235 -664 65 -562 133 -328 463 -100 291 -194 159 -162 227 -98 293 -328 165 -128 227 -574 535 -332 197 -168 65 -300 131 -66 389 -1078 131 -64 259 -64 223 -98 257 -164 63 -328 433 -134 65 -602 131 -68 333 -136 369 -66 297 -264 427 -66 97 -130 429 -102 133 -136 203 -240 167 -236 329 -526 67 -132 133 -168 331 -360 65 -66 331 -296 267 -134 469 -132 595 -230 661 -662 299 -100 265 -200 203 -168 801 -100 133 -68 399 -132 99 -100 161 -390 65 -298 65 -98 261 -130 161 -128 257 -66 67 -134 621 -98 227 -328 99 -230 129 -294 193 -96 195 -318 425 -526 129 -196 163 -162 65 -132 293 -130 63 -66 325 -128 63 -130 293 -66 199 -200 269 -206 133 -198 325 -98 163 -100 97 -98 261 -164 67 -98 167 -430 131 -494 131 -164 -RAW_Data: 97 -98 861 -66 1199 -166 231 -100 651 -166 197 -104 439 -98 131 -64 493 -98 883 -96 99 -98 3327 -66 131 -264 733 -134 2133 -166 131 -102 303 -136 535 -134 701 -98 355 -228 131 -202 99 -134 99 -100 791 -166 169 -202 671 -100 741 -100 263 -66 165 -68 935 -132 197 -198 673 -100 605 -66 1457 -98 1195 -166 2347 -134 505 -100 1469 -66 391 -100 229 -100 1171 -98 939 -100 459 -170 369 -134 231 -162 127 -98 95 -66 195 -98 195 -66 299 -100 331 -98 65 -232 369 -132 201 -68 167 -166 1481 -102 501 -160 1257 -66 2307 -64 623 -164 2079 -66 1101 -98 423 -64 659 -68 431 -136 99 -100 435 -130 167 -168 835 -200 135 -104 133 -100 503 -68 1437 -232 821 -132 357 -96 463 -66 263 -64 683 -132 165 -96 655 -166 3939 -100 1169 -132 2443 -98 197 -132 425 -234 233 -162 1043 -66 197 -100 2793 -134 167 -104 675 -100 197 -134 1367 -102 763 -132 265 -230 133 -102 365 -100 167 -66 1069 -66 837 -100 295 -160 97 -64 129 -132 617 -164 197 -100 133 -136 337 -172 133 -66 557 -98 951 -66 263 -130 587 -66 729 -196 335 -166 933 -432 369 -100 199 -296 225 -98 355 -66 129 -64 557 -98 289 -66 355 -128 193 -162 267 -134 299 -98 165 -170 303 -640 1031 -134 99 -66 135 -68 771 -166 171 -104 201 -134 131 -68 635 -428 661 -292 749 -430 1161 -100 905 -98 65 -98 657 -262 2837 -132 67 -66 265 -132 631 -66 1037 -296 97 -98 1703 -302 367 -100 505 -232 497 -362 333 -134 591 -100 755 -232 67 -130 587 -66 231 -168 65 -332 99 -66 267 -232 393 -134 65 -132 131 -428 133 -200 165 -202 199 -168 165 -102 269 -100 333 -852 201 -134 233 -202 65 -200 563 -768 265 -136 169 -102 169 -598 333 -202 267 -134 267 -328 163 -130 625 -500 199 -200 99 -270 65 -134 65 -198 65 -100 99 -596 493 -66 99 -66 331 -232 103 -136 373 -168 831 -170 65 -672 163 -102 133 -136 331 -100 333 -234 101 -100 99 -200 99 -100 201 -302 199 -600 301 -202 135 -134 705 -166 435 -530 97 -198 131 -198 195 -66 163 -392 293 -66 295 -370 229 -198 65 -100 405 -134 165 -134 133 -170 337 -236 205 -274 267 -134 329 -132 195 -132 503 -132 133 -136 133 -334 197 -196 299 -168 101 -100 233 -100 439 -134 301 -332 331 -298 433 -406 433 -68 167 -100 203 -100 101 -102 99 -328 397 -234 205 -168 133 -364 63 -202 397 -198 95 -394 267 -134 569 -66 201 -102 133 -136 101 -102 99 -132 99 -196 197 -498 197 -102 135 -170 -RAW_Data: 331 -164 63 -162 1267 -66 163 -130 129 -66 725 -164 231 -64 853 -66 101 -134 199 -102 99 -68 365 -66 357 -130 815 -64 357 -98 97 -98 97 -66 65 -466 231 -172 3749 -66 849 -130 917 -64 327 -64 1013 -98 555 -332 795 -100 571 -132 769 -132 401 -134 1297 -134 377 -138 435 -100 401 -100 667 -100 1761 -66 667 -66 1533 -236 233 -98 885 -130 457 -66 999 -66 165 -66 833 -134 695 -166 501 -66 499 -200 329 -64 197 -134 441 -100 2099 -98 491 -134 197 -130 2225 -132 65 -100 689 -64 193 -160 159 -96 195 -98 323 -164 259 -98 535 -472 771 -66 665 -270 665 -66 595 -266 2191 -64 643 -98 1287 -98 741 -100 233 -200 569 -194 261 -68 637 -100 97 -66 491 -158 395 -138 1017 -66 627 -262 559 -64 327 -98 263 -134 99 -102 201 -102 337 -66 167 -68 679 -100 471 -134 195 -66 133 -202 693 -96 197 -98 391 -164 99 -98 3883 -194 461 -100 237 -168 1891 -68 301 -68 969 -166 1439 -294 551 -130 389 -98 99 -196 167 -102 505 -66 569 -234 901 -98 407 -136 469 -66 769 -98 769 -166 1263 -266 297 -98 1701 -200 203 -168 329 -232 65 -100 329 -164 803 -100 135 -200 233 -166 135 -272 265 -134 197 -100 133 -134 539 -232 197 -396 165 -366 263 -68 233 -102 365 -132 233 -100 135 -266 199 -234 167 -232 97 -524 127 -128 389 -98 305 -364 261 -130 257 -162 589 -464 361 -66 229 -134 161 -100 203 -432 265 -66 199 -66 199 -366 229 -236 99 -134 99 -100 131 -168 133 -100 131 -236 267 -132 297 -264 291 -132 167 -234 65 -100 199 -66 333 -730 237 -440 365 -102 99 -100 99 -132 99 -100 1429 -134 427 -100 97 -100 131 -164 799 -170 1077 -100 431 -66 133 -168 737 -134 197 -230 65 -102 803 -132 491 -98 429 -198 471 -134 365 -66 299 -236 65 -66 2837 -102 399 -64 585 -64 523 -196 97 -98 295 -196 555 -160 261 -500 299 -396 333 -236 133 -68 327 -100 199 -204 699 -66 701 -100 65 -164 65 -370 195 -196 97 -66 193 -130 129 -360 195 -130 231 -96 291 -64 455 -228 293 -196 291 -162 97 -194 621 -130 847 -66 395 -66 161 -128 193 -130 293 -98 231 -170 67 -134 297 -360 167 -266 263 -526 263 -132 229 -98 191 -160 159 -100 721 -234 101 -100 99 -130 259 -258 265 -632 687 -164 133 -134 631 -100 199 -102 165 -560 299 -200 265 -332 431 -870 99 -266 503 -364 135 -66 269 -68 499 -100 265 -102 263 -102 569 -234 719 -132 99 -196 419 -262 163 -688 95 -66 165 -128 95 -66 -RAW_Data: 295 -98 987 -196 517 -100 489 -66 355 -132 563 -198 867 -134 1413 -134 541 -134 767 -100 193 -98 1799 -102 467 -134 299 -96 323 -66 261 -100 259 -66 229 -96 851 -66 369 -266 469 -66 101 -98 163 -136 267 -432 859 -130 523 -66 197 -134 1027 -132 227 -194 393 -98 807 -166 235 -100 133 -66 165 -102 133 -136 371 -162 1411 -132 865 -200 471 -100 133 -68 299 -66 633 -98 329 -234 401 -98 1505 -132 133 -134 331 -262 163 -66 261 -98 289 -64 201 -68 1055 -96 391 -66 951 -298 265 -202 297 -66 401 -68 131 -100 1733 -98 941 -66 803 -98 847 -64 3701 -100 721 -160 357 -166 1799 -66 329 -100 99 -102 363 -198 167 -136 197 -66 567 -66 199 -236 1247 -166 2455 -68 1107 -200 235 -100 2355 -130 913 -98 877 -98 163 -196 97 -66 427 -100 801 -134 867 -98 263 -68 441 -134 561 -98 1671 -134 865 -68 935 -132 163 -102 975 -66 1343 -132 1339 -134 369 -100 1107 -66 1167 -168 631 -232 835 -66 1027 -132 333 -166 265 -98 1207 -98 223 -98 455 -64 2095 -134 933 -136 233 -68 335 -136 305 -100 1737 -66 427 -100 263 -130 323 -66 227 -66 717 -100 265 -100 65 -128 355 -66 367 -132 95 -230 229 -100 131 -64 493 -132 291 -396 393 -130 259 -196 227 -288 397 -68 229 -430 99 -302 237 -700 65 -66 65 -100 133 -200 101 -336 133 -166 237 -202 67 -302 67 -68 333 -132 263 -102 267 -296 163 -166 233 -168 363 -64 295 -298 537 -166 431 -200 431 -166 63 -258 363 -164 563 -234 199 -68 299 -100 325 -754 295 -196 65 -98 165 -132 301 -134 131 -134 97 -68 405 -68 233 -134 271 -134 67 -168 101 -136 133 -366 99 -132 67 -132 265 -200 233 -100 201 -136 101 -66 263 -132 129 -66 293 -582 263 -132 1103 -134 203 -168 97 -66 197 -264 131 -168 133 -132 65 -134 199 -134 101 -100 131 -436 99 -232 97 -398 231 -362 65 -202 301 -396 297 -98 199 -134 265 -164 101 -168 267 -102 405 -170 99 -102 397 -132 97 -98 295 -98 1179 -100 135 -136 131 -134 765 -134 465 -168 439 -232 403 -100 65 -134 931 -100 169 -136 237 -68 231 -234 199 -68 401 -134 541 -166 429 -166 1607 -368 533 -66 363 -66 133 -134 433 -166 297 -238 201 -100 201 -170 199 -134 273 -136 99 -134 167 -238 133 -66 265 -134 165 -132 165 -132 97 -228 723 -198 415 -64 491 -298 257 -66 231 -192 225 -96 227 -98 193 -96 521 -198 65 -66 231 -166 163 -98 465 -66 133 -132 195 -130 225 -162 521 -130 63 -66 199 -228 -RAW_Data: 817 -162 449 -160 719 -198 469 -68 133 -68 1101 -132 593 -230 1105 -100 131 -134 231 -66 329 -196 685 -96 557 -68 1263 -68 101 -68 397 -100 65 -66 625 -66 97 -132 1099 -66 493 -66 757 -98 1151 -66 303 -134 1901 -66 99 -100 665 -262 991 -98 791 -66 1925 -168 865 -232 835 -98 505 -102 99 -100 535 -100 169 -134 427 -132 863 -68 167 -134 975 -100 133 -268 1339 -100 1453 -66 1445 -162 195 -64 3623 -66 237 -68 1063 -308 1449 -98 1111 -132 167 -102 855 -270 199 -134 297 -134 267 -168 863 -234 637 -66 567 -230 99 -200 3325 -198 845 -66 289 -66 131 -66 815 -130 1093 -100 167 -100 429 -98 1703 -166 195 -64 971 -98 163 -192 195 -168 439 -132 329 -132 67 -134 67 -134 1591 -168 407 -100 867 -68 399 -134 661 -100 663 -66 237 -136 395 -232 131 -66 695 -100 627 -264 913 -66 1083 -98 287 -66 199 -132 335 -100 1031 -68 99 -100 3815 -98 165 -66 129 -98 163 -128 563 -98 779 -96 223 -64 161 -164 2025 -66 1741 -172 101 -136 203 -102 665 -100 475 -64 167 -100 637 -98 997 -170 1207 -136 233 -166 233 -168 635 -132 199 -100 235 -270 199 -98 131 -102 169 -170 293 -98 323 -164 427 -334 233 -168 267 -68 369 -100 263 -368 101 -66 665 -98 265 -100 133 -100 99 -168 133 -66 133 -132 133 -66 269 -134 435 -68 267 -136 271 -500 163 -100 163 -166 355 -132 97 -98 323 -194 63 -688 463 -130 97 -396 65 -100 357 -194 461 -98 161 -130 223 -162 165 -352 461 -300 267 -166 233 -464 329 -100 293 -362 163 -228 289 -66 229 -66 195 -162 325 -66 261 -98 127 -424 299 -302 367 -68 265 -272 429 -98 161 -98 393 -296 65 -130 161 -196 261 -66 473 -234 97 -98 263 -160 323 -98 67 -132 697 -298 99 -134 233 -202 97 -134 301 -200 307 -100 101 -134 865 -166 231 -202 233 -100 301 -170 169 -102 169 -200 65 -98 595 -166 231 -234 661 -66 473 -334 165 -304 365 -266 97 -502 363 -134 133 -236 65 -100 99 -134 99 -170 235 -66 333 -100 195 -100 133 -300 133 -102 301 -304 65 -100 99 -100 131 -202 135 -134 65 -200 363 -66 263 -498 67 -68 295 -194 321 -368 435 -100 97 -664 99 -100 569 -66 133 -66 67 -134 199 -136 101 -68 301 -68 405 -198 133 -132 581 -132 165 -98 159 -98 197 -66 229 -130 131 -294 133 -96 423 -100 427 -300 357 -132 291 -64 95 -194 455 -98 263 -100 359 -196 65 -162 227 -162 157 -96 157 -230 589 -132 325 -134 535 -66 267 -100 135 -302 -RAW_Data: 131 -134 599 -166 393 -98 369 -236 197 -100 401 -232 569 -134 135 -70 337 -134 101 -136 135 -100 1895 -66 401 -170 503 -66 1633 -66 601 -66 355 -96 683 -100 729 -68 133 -132 433 -68 569 -100 133 -68 201 -132 835 -100 465 -68 527 -98 193 -200 1129 -166 535 -100 199 -98 259 -132 227 -64 1597 -98 261 -192 753 -100 911 -66 667 -298 131 -100 263 -66 1051 -230 787 -66 935 -66 233 -98 885 -236 431 -66 197 -162 521 -68 167 -196 263 -96 589 -98 517 -66 1439 -64 777 -66 3219 -132 679 -134 205 -68 507 -198 749 -200 199 -168 167 -100 133 -134 201 -68 731 -66 495 -198 737 -66 237 -68 135 -100 167 -234 1535 -68 873 -66 373 -66 67 -232 297 -68 65 -66 1095 -68 327 -130 63 -132 1715 -66 2261 -100 321 -132 197 -164 457 -232 1291 -132 405 -68 1001 -68 1133 -272 471 -66 99 -134 1403 -68 167 -68 1091 -336 933 -134 1207 -132 265 -68 267 -66 99 -366 265 -66 1469 -258 367 -168 429 -132 129 -66 491 -132 343 -100 65 -100 263 -136 199 -164 273 -204 791 -100 901 -66 167 -98 165 -64 559 -132 619 -132 1087 -128 2283 -398 1467 -164 259 -130 1927 -130 421 -98 1085 -66 705 -68 1843 -168 875 -170 203 -136 341 -640 199 -66 133 -554 161 -196 63 -66 521 -292 163 -160 95 -158 127 -192 197 -100 587 -130 397 -662 261 -66 193 -130 259 -66 361 -64 459 -98 197 -560 655 -130 389 -66 1135 -100 133 -130 131 -98 1011 -100 561 -66 685 -164 457 -132 2469 -200 609 -66 665 -66 67 -132 327 -200 1657 -134 919 -132 651 -100 327 -230 191 -130 263 -358 95 -130 549 -98 99 -68 299 -100 461 -132 99 -472 165 -134 99 -66 99 -132 399 -102 169 -102 697 -166 233 -132 333 -632 197 -164 865 -266 101 -68 533 -166 299 -100 163 -228 259 -66 327 -200 65 -66 229 -100 363 -230 197 -336 165 -102 893 -300 65 -132 231 -370 265 -230 99 -98 229 -518 199 -100 401 -724 225 -98 63 -96 231 -64 291 -292 65 -98 131 -98 159 -158 127 -194 161 -292 65 -98 133 -66 297 -66 303 -168 97 -168 231 -234 269 -532 135 -168 99 -168 301 -528 99 -506 199 -368 399 -132 329 -372 99 -68 133 -264 197 -100 201 -200 67 -134 131 -270 133 -134 133 -198 327 -200 65 -100 331 -262 161 -166 469 -534 167 -738 131 -100 367 -232 101 -100 265 -604 65 -170 99 -166 299 -102 169 -132 99 -398 229 -330 197 -166 335 -366 97 -98 131 -200 269 -100 199 -168 131 -134 537 -98 265 -100 335 -236 99 -366 -RAW_Data: 459 -100 453 -130 419 -130 519 -96 63 -130 2077 -66 767 -64 127 -134 1961 -296 529 -202 637 -134 527 -100 201 -68 633 -66 163 -360 1029 -68 765 -100 867 -66 503 -100 131 -66 841 -98 165 -68 237 -66 509 -100 501 -302 235 -66 99 -164 227 -130 551 -196 327 -66 1571 -132 99 -68 867 -66 163 -96 161 -130 129 -130 549 -130 487 -166 1801 -66 229 -66 197 -232 325 -66 425 -198 131 -64 295 -166 735 -66 533 -98 227 -130 129 -262 425 -100 263 -66 129 -132 97 -168 971 -170 405 -68 199 -134 475 -202 297 -98 1445 -98 395 -196 161 -66 225 -134 1803 -100 473 -102 1499 -66 199 -100 701 -132 165 -68 133 -102 303 -98 735 -102 805 -100 827 -100 235 -100 65 -266 637 -68 693 -66 1383 -228 819 -66 233 -304 435 -198 203 -136 1135 -270 1709 -64 227 -64 581 -134 505 -66 2203 -64 293 -64 753 -66 551 -132 747 -64 1303 -64 463 -66 229 -102 1877 -266 871 -166 1357 -64 819 -66 465 -198 693 -68 165 -64 95 -128 3785 -132 1465 -100 299 -102 329 -164 595 -134 1029 -66 299 -168 1263 -166 331 -68 967 -100 101 -102 603 -260 165 -132 467 -66 233 -66 235 -102 475 -100 135 -68 301 -134 297 -98 131 -102 269 -466 99 -134 237 -166 135 -168 203 -102 265 -68 503 -66 233 -66 637 -134 101 -200 199 -166 293 -554 361 -328 367 -264 533 -238 167 -68 135 -170 99 -300 591 -298 133 -236 299 -66 231 -368 263 -232 435 -136 133 -102 133 -200 133 -134 163 -134 167 -168 299 -66 265 -100 133 -240 135 -132 263 -170 269 -200 501 -396 263 -98 227 -132 129 -292 427 -66 165 -102 627 -602 99 -66 301 -168 199 -100 563 -330 165 -134 233 -136 65 -332 499 -100 131 -232 325 -96 65 -132 195 -98 393 -624 323 -68 133 -98 195 -162 231 -100 263 -132 231 -102 133 -236 99 -236 231 -166 65 -102 133 -268 101 -102 299 -136 267 -164 493 -64 229 -258 291 -326 263 -198 391 -134 167 -202 365 -594 133 -102 201 -134 503 -396 429 -204 169 -400 197 -170 267 -132 403 -466 297 -98 469 -234 395 -132 233 -100 165 -100 165 -66 197 -68 297 -166 501 -134 133 -100 65 -166 631 -68 297 -134 199 -100 165 -68 299 -266 133 -66 165 -100 231 -490 557 -134 371 -164 299 -170 733 -164 239 -334 335 -66 299 -300 199 -170 103 -100 233 -102 641 -168 65 -100 995 -66 265 -160 259 -130 129 -226 425 -100 355 -726 97 -688 99 -66 233 -266 299 -942 167 -102 167 -166 65 -100 367 -136 99 -134 199 -134 267 -164 -RAW_Data: 67 -68 233 -66 899 -66 163 -96 485 -98 355 -130 943 -100 235 -168 499 -104 1367 -98 297 -100 635 -68 1169 -100 67 -134 835 -264 959 -164 129 -98 419 -196 589 -66 421 -66 1717 -100 133 -100 265 -134 227 -356 455 -166 163 -66 1055 -100 1455 -134 463 -98 2191 -132 295 -132 335 -66 709 -64 619 -98 959 -68 835 -170 603 -134 1033 -134 635 -168 759 -232 397 -198 397 -164 1267 -166 257 -198 1295 -100 239 -104 563 -204 335 -198 203 -68 901 -68 1255 -134 1697 -66 793 -66 1691 -68 201 -100 765 -66 165 -132 131 -230 131 -66 917 -66 335 -338 231 -170 827 -98 199 -136 301 -196 65 -98 199 -200 765 -134 403 -98 333 -68 1691 -132 2565 -64 569 -170 1255 -264 65 -132 1243 -132 2527 -66 259 -66 1739 -100 1309 -198 167 -238 337 -66 131 -68 1973 -362 299 -100 1387 -96 129 -164 423 -230 3875 -96 4283 -98 165 -98 515 -134 469 -68 171 -102 1163 -100 65 -298 461 -66 367 -136 205 -168 371 -98 491 -164 161 -262 1093 -100 299 -100 269 -334 1205 -98 63 -98 261 -64 457 -98 diff --git a/lib/subghz/protocols/oregon2.h b/lib/subghz/protocols/oregon2.h deleted file mode 100644 index 981b2599..00000000 --- a/lib/subghz/protocols/oregon2.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "base.h" -#define SUBGHZ_PROTOCOL_OREGON2_NAME "Oregon2" -extern const SubGhzProtocol subghz_protocol_oregon2; diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 65ba0328..ebf9b93e 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -12,7 +12,8 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec, &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, &subghz_protocol_honeywell_wdb, &subghz_protocol_magellan, &subghz_protocol_intertechno_v3, - &subghz_protocol_clemsa, &subghz_protocol_oregon2}; + &subghz_protocol_clemsa, +}; const SubGhzProtocolRegistry subghz_protocol_registry = { .items = subghz_protocol_registry_items, diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 9c187e79..23aa71be 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -35,6 +35,5 @@ #include "magellan.h" #include "intertechno_v3.h" #include "clemsa.h" -#include "oregon2.h" extern const SubGhzProtocolRegistry subghz_protocol_registry; From 984d89c6d072a9c4c1955299b46f793ddf69cdd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 24 Oct 2022 19:50:34 +0900 Subject: [PATCH 19/42] Furi: smaller crash routine (#1912) * Furi: smaller crash routine * Furi: small fixes * Furi: cleanup check routines, more assembly code, force inline of __furi_halt_mcu * SubGhz: cleanup residual line mess * Documentation * Dap-link: fix help * Furi: replace __furi_halt_mcu with HALT_MCU macros * Furi: disable IRQ earlier in crash handler * Furi: properly handle masked mode when detecting ISR * Ble: allow 0 length feed in rpc_session_feed * Format sources * Furi: better crash logic explanation. * Furi: some grammar in check.h Co-authored-by: SG --- .../dap_link/gui/scenes/dap_scene_help.c | 8 +-- applications/services/rpc/rpc.c | 3 +- firmware/targets/f7/api_symbols.csv | 3 +- furi/core/check.c | 65 +++++++++---------- furi/core/check.h | 60 ++++++++++------- 5 files changed, 74 insertions(+), 65 deletions(-) diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_help.c b/applications/plugins/dap_link/gui/scenes/dap_scene_help.c index 7193f4f4..d8d70e7f 100644 --- a/applications/plugins/dap_link/gui/scenes/dap_scene_help.c +++ b/applications/plugins/dap_link/gui/scenes/dap_scene_help.c @@ -72,13 +72,13 @@ void dap_scene_help_on_enter(void* context) { if(config->uart_swap == DapUartTXRXNormal) { furi_string_cat( string, - " TX: 15 [С1]\r\n" - " RX: 16 [С0]\r\n"); + " TX: 15 [C1]\r\n" + " RX: 16 [C0]\r\n"); } else { furi_string_cat( string, - " RX: 15 [С1]\r\n" - " TX: 16 [С0]\r\n"); + " RX: 15 [C1]\r\n" + " TX: 16 [C0]\r\n"); } break; default: diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 06c05173..73eaadfb 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -148,7 +148,8 @@ size_t rpc_session_feed(RpcSession* session, uint8_t* encoded_bytes, size_t size, TickType_t timeout) { furi_assert(session); furi_assert(encoded_bytes); - furi_assert(size > 0); + + if(!size) return 0; size_t bytes_sent = furi_stream_buffer_send(session->stream, encoded_bytes, size, timeout); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 188cd748..4c48d9ab 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,4.0,, +Version,+,5.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2843,7 +2843,6 @@ Variable,+,I_update_10px,const Icon, Variable,-,MSIRangeTable,const uint32_t[16], Variable,-,SmpsPrescalerTable,const uint32_t[4][6], Variable,+,SystemCoreClock,uint32_t, -Variable,+,__furi_check_message,const char*, Variable,+,_ctype_,const char[], Variable,+,_global_impure_ptr,_reent*, Variable,+,_impure_ptr,_reent*, diff --git a/furi/core/check.c b/furi/core/check.c index ed38038a..00c20575 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -14,6 +14,28 @@ PLACE_IN_SECTION("MB_MEM2") const char* __furi_check_message = NULL; PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0}; +/** Load r12 value to __furi_check_message and store registers to __furi_check_registers */ +#define GET_MESSAGE_AND_STORE_REGISTERS() \ + asm volatile("ldr r11, =__furi_check_message \n" \ + "str r12, [r11] \n" \ + "ldr r12, =__furi_check_registers \n" \ + "stm r12, {r0-r11} \n" \ + : \ + : \ + : "memory"); + +// Restore registers and halt MCU +#define RESTORE_REGISTERS_AND_HALT_MCU() \ + asm volatile("ldr r12, =__furi_check_registers \n" \ + "ldm r12, {r0-r11} \n" \ + "loop%=: \n" \ + "bkpt 0x00 \n" \ + "wfi \n" \ + "b loop%= \n" \ + : \ + : \ + : "memory"); + extern size_t xPortGetTotalHeapSize(void); extern size_t xPortGetFreeHeapSize(void); extern size_t xPortGetMinimumEverFreeHeapSize(void); @@ -55,32 +77,11 @@ static void __furi_print_name(bool isr) { } } -static FURI_NORETURN void __furi_halt_mcu() { - register const void* r12 asm ("r12") = (void*)__furi_check_registers; - asm volatile( - "ldm r12, {r0-r11} \n" -#ifdef FURI_DEBUG - "bkpt 0x00 \n" -#endif - "loop%=: \n" - "wfi \n" - "b loop%= \n" - : - : "r" (r12) - : "memory"); - __builtin_unreachable(); -} - FURI_NORETURN void __furi_crash() { - register const void* r12 asm ("r12") = (void*)__furi_check_registers; - asm volatile( - "stm r12, {r0-r11} \n" - : - : "r" (r12) - : "memory"); - - bool isr = FURI_IS_ISR(); __disable_irq(); + GET_MESSAGE_AND_STORE_REGISTERS(); + + bool isr = FURI_IS_IRQ_MODE(); if(__furi_check_message == NULL) { __furi_check_message = "Fatal Error"; @@ -98,7 +99,7 @@ FURI_NORETURN void __furi_crash() { #ifdef FURI_DEBUG furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); furi_hal_console_puts("\033[0m\r\n"); - __furi_halt_mcu(); + RESTORE_REGISTERS_AND_HALT_MCU(); #else furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); furi_hal_console_puts("\r\nRebooting system.\r\n"); @@ -109,15 +110,10 @@ FURI_NORETURN void __furi_crash() { } FURI_NORETURN void __furi_halt() { - register const void* r12 asm ("r12") = (void*)__furi_check_registers; - asm volatile( - "stm r12, {r0-r11} \n" - : - : "r" (r12) - : "memory"); - - bool isr = FURI_IS_ISR(); __disable_irq(); + GET_MESSAGE_AND_STORE_REGISTERS(); + + bool isr = FURI_IS_IRQ_MODE(); if(__furi_check_message == NULL) { __furi_check_message = "System halt requested."; @@ -128,5 +124,6 @@ FURI_NORETURN void __furi_halt() { furi_hal_console_puts(__furi_check_message); furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n"); furi_hal_console_puts("\033[0m\r\n"); - __furi_halt_mcu(); + RESTORE_REGISTERS_AND_HALT_MCU(); + __builtin_unreachable(); } diff --git a/furi/core/check.h b/furi/core/check.h index e77891f7..78efc145 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -1,3 +1,16 @@ +/** + * @file check.h + * + * Furi crash and assert functions. + * + * The main problem with crashing is that you can't do anything without disturbing registers, + * and if you disturb registers, you won't be able to see the correct register values in the debugger. + * + * Current solution works around it by passing the message through r12 and doing some magic with registers in crash function. + * r0-r10 are stored in the ram2 on crash routine start and restored at the end. + * The only register that is going to be lost is r11. + * + */ #pragma once #ifdef __cplusplus @@ -8,9 +21,6 @@ extern "C" { #define FURI_NORETURN noreturn #endif -/** Pointer to pass message to __furi_crash and __furi_halt */ -extern const char* __furi_check_message; - /** Crash system */ FURI_NORETURN void __furi_crash(); @@ -18,39 +28,41 @@ FURI_NORETURN void __furi_crash(); FURI_NORETURN void __furi_halt(); /** Crash system with message. Show message after reboot. */ -#define furi_crash(message) \ - do { \ - __furi_check_message = message; \ - __furi_crash(); \ +#define furi_crash(message) \ + do { \ + register const void* r12 asm("r12") = (void*)message; \ + asm volatile("sukima%=:" : : "r"(r12)); \ + __furi_crash(); \ } while(0) /** Halt system with message. */ -#define furi_halt(message) \ - do { \ - __furi_check_message = message; \ - __furi_halt(); \ +#define furi_halt(message) \ + do { \ + register const void* r12 asm("r12") = (void*)message; \ + asm volatile("sukima%=:" : : "r"(r12)); \ + __furi_halt(); \ } while(0) /** Check condition and crash if check failed */ -#define furi_check(__e) \ - do { \ - if ((__e) == 0) { \ - furi_crash("furi_check failed\r\n"); \ - } \ +#define furi_check(__e) \ + do { \ + if((__e) == 0) { \ + furi_crash("furi_check failed\r\n"); \ + } \ } while(0) /** Only in debug build: Assert condition and crash if assert failed */ #ifdef FURI_DEBUG -#define furi_assert(__e) \ - do { \ - if ((__e) == 0) { \ - furi_crash("furi_assert failed\r\n"); \ - } \ +#define furi_assert(__e) \ + do { \ + if((__e) == 0) { \ + furi_crash("furi_assert failed\r\n"); \ + } \ } while(0) #else -#define furi_assert(__e) \ - do { \ - ((void)(__e)); \ +#define furi_assert(__e) \ + do { \ + ((void)(__e)); \ } while(0) #endif From 0adad32418decc8d19c39e80ce5c42795d141b1e Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 26 Oct 2022 02:15:02 +0400 Subject: [PATCH 20/42] fbt fixes pt4 (#1899) * fbt: fixed py scripts for gdb * fbt: removed compiled dolphin assets from tracked files; resolved cached dependency issues by globally disabling deps cache; changed dependency tracking for dolphin assets * fbt: fix for "resources" node lookup * toolchain: bump to v.16 with scons + x64 win binaries * fbt: using scons from toolchain * vscode: fixed paths for 64-bit Windows toolchain * fbt: added colors! * fbt: moved import validator to ansi lib coloring * fbt: moved COMSTR vars to tools * fbt: custom action for fap dist * fbt: added OPENOCD_ADAPTER_SERIAL configuration var for openocd operations * fbt: added get_stlink target * docs: details on libs for faps * vscode: added DAP config for using Flipper as a debugger for a 2nd Flipper * fbt: blind deps fix for sdk_origin * fbt: sdk: moved deployment actions to pure python * Github: disable disableLicenseExpirationCheck option for pvs Co-authored-by: Aleksandr Kutuzov --- .github/workflows/pvs_studio.yml | 1 - .gitmodules | 3 - .vscode/example/c_cpp_properties.json | 2 +- .vscode/example/launch.json | 19 +++ .vscode/example/settings.json | 6 +- SConstruct | 16 ++- assets/.gitignore | 3 +- assets/SConscript | 6 +- .../dolphin/L1_Boxing_128x64/frame_0.bm | Bin 481 -> 0 bytes .../dolphin/L1_Boxing_128x64/frame_1.bm | Bin 461 -> 0 bytes .../dolphin/L1_Boxing_128x64/frame_2.bm | Bin 531 -> 0 bytes .../dolphin/L1_Boxing_128x64/frame_3.bm | Bin 437 -> 0 bytes .../dolphin/L1_Boxing_128x64/frame_4.bm | Bin 459 -> 0 bytes .../dolphin/L1_Boxing_128x64/frame_5.bm | Bin 450 -> 0 bytes .../dolphin/L1_Boxing_128x64/frame_6.bm | Bin 442 -> 0 bytes .../dolphin/L1_Boxing_128x64/meta.txt | 32 ----- .../dolphin/L1_Cry_128x64/frame_0.bm | Bin 889 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_1.bm | Bin 911 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_2.bm | Bin 910 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_3.bm | Bin 923 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_4.bm | Bin 894 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_5.bm | Bin 940 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_6.bm | Bin 915 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_7.bm | Bin 934 -> 0 bytes .../resources/dolphin/L1_Cry_128x64/meta.txt | 41 ------ .../dolphin/L1_Furippa1_128x64/frame_0.bm | Bin 294 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_1.bm | Bin 325 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_10.bm | Bin 465 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_11.bm | Bin 698 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_12.bm | Bin 541 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_13.bm | Bin 584 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_14.bm | Bin 610 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_15.bm | Bin 719 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_16.bm | Bin 458 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_17.bm | Bin 400 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_18.bm | Bin 333 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_2.bm | Bin 351 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_3.bm | Bin 324 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_4.bm | Bin 387 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_5.bm | Bin 390 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_6.bm | Bin 407 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_7.bm | Bin 294 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_8.bm | Bin 283 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_9.bm | Bin 312 -> 0 bytes .../dolphin/L1_Furippa1_128x64/meta.txt | 14 -- .../dolphin/L1_Laptop_128x51/frame_0.bm | Bin 555 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_1.bm | Bin 557 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_2.bm | Bin 560 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_3.bm | Bin 556 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_4.bm | Bin 560 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_5.bm | Bin 554 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_6.bm | Bin 553 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_7.bm | Bin 560 -> 0 bytes .../dolphin/L1_Laptop_128x51/meta.txt | 32 ----- .../dolphin/L1_Leaving_sad_128x64/frame_0.bm | Bin 514 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_1.bm | Bin 526 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_10.bm | Bin 316 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_11.bm | Bin 294 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_12.bm | Bin 322 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_2.bm | Bin 542 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_3.bm | Bin 557 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_4.bm | Bin 488 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_5.bm | Bin 469 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_6.bm | Bin 499 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_7.bm | Bin 486 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_8.bm | Bin 403 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_9.bm | Bin 317 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/meta.txt | 32 ----- .../dolphin/L1_Mad_fist_128x64/frame_0.bm | Bin 520 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_1.bm | Bin 540 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_10.bm | Bin 542 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_11.bm | Bin 505 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_12.bm | Bin 501 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_13.bm | Bin 500 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_2.bm | Bin 515 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_3.bm | Bin 538 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_4.bm | Bin 512 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_5.bm | Bin 519 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_6.bm | Bin 524 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_7.bm | Bin 515 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_8.bm | Bin 517 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_9.bm | Bin 526 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/meta.txt | 23 ---- .../dolphin/L1_Painting_128x64/frame_0.bm | Bin 763 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_1.bm | Bin 764 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_10.bm | Bin 772 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_11.bm | Bin 767 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_2.bm | Bin 762 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_3.bm | Bin 759 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_4.bm | Bin 759 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_5.bm | Bin 757 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_6.bm | Bin 785 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_7.bm | Bin 803 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_8.bm | Bin 797 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_9.bm | Bin 777 -> 0 bytes .../dolphin/L1_Painting_128x64/meta.txt | 32 ----- .../dolphin/L1_Read_books_128x64/frame_0.bm | Bin 653 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_1.bm | Bin 653 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_2.bm | Bin 650 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_3.bm | Bin 646 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_4.bm | Bin 650 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_5.bm | Bin 652 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_6.bm | Bin 646 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_7.bm | Bin 647 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_8.bm | Bin 643 -> 0 bytes .../dolphin/L1_Read_books_128x64/meta.txt | 23 ---- .../dolphin/L1_Recording_128x51/frame_0.bm | Bin 663 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_1.bm | Bin 657 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_10.bm | Bin 629 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_11.bm | Bin 659 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_2.bm | Bin 628 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_3.bm | Bin 654 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_4.bm | Bin 662 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_5.bm | Bin 622 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_6.bm | Bin 664 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_7.bm | Bin 626 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_8.bm | Bin 663 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_9.bm | Bin 661 -> 0 bytes .../dolphin/L1_Recording_128x51/meta.txt | 14 -- .../dolphin/L1_Sleep_128x64/frame_0.bm | Bin 580 -> 0 bytes .../dolphin/L1_Sleep_128x64/frame_1.bm | Bin 589 -> 0 bytes .../dolphin/L1_Sleep_128x64/frame_2.bm | Bin 582 -> 0 bytes .../dolphin/L1_Sleep_128x64/frame_3.bm | Bin 597 -> 0 bytes .../dolphin/L1_Sleep_128x64/meta.txt | 41 ------ .../dolphin/L1_Waves_128x50/frame_0.bm | Bin 443 -> 0 bytes .../dolphin/L1_Waves_128x50/frame_1.bm | Bin 448 -> 0 bytes .../dolphin/L1_Waves_128x50/frame_2.bm | Bin 463 -> 0 bytes .../dolphin/L1_Waves_128x50/frame_3.bm | Bin 472 -> 0 bytes .../dolphin/L1_Waves_128x50/meta.txt | 50 -------- .../dolphin/L2_Furippa2_128x64/frame_0.bm | Bin 350 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_1.bm | Bin 385 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_10.bm | Bin 465 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_11.bm | Bin 698 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_12.bm | Bin 541 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_13.bm | Bin 584 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_14.bm | Bin 610 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_15.bm | Bin 740 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_16.bm | Bin 533 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_17.bm | Bin 451 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_18.bm | Bin 397 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_2.bm | Bin 402 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_3.bm | Bin 374 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_4.bm | Bin 440 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_5.bm | Bin 449 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_6.bm | Bin 466 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_7.bm | Bin 350 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_8.bm | Bin 319 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_9.bm | Bin 317 -> 0 bytes .../dolphin/L2_Furippa2_128x64/meta.txt | 14 -- .../dolphin/L2_Hacking_pc_128x64/frame_0.bm | Bin 543 -> 0 bytes .../dolphin/L2_Hacking_pc_128x64/frame_1.bm | Bin 545 -> 0 bytes .../dolphin/L2_Hacking_pc_128x64/frame_2.bm | Bin 548 -> 0 bytes .../dolphin/L2_Hacking_pc_128x64/frame_3.bm | Bin 608 -> 0 bytes .../dolphin/L2_Hacking_pc_128x64/frame_4.bm | Bin 609 -> 0 bytes .../dolphin/L2_Hacking_pc_128x64/meta.txt | 32 ----- .../dolphin/L2_Soldering_128x64/frame_0.bm | Bin 699 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_1.bm | Bin 688 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_10.bm | Bin 699 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_2.bm | Bin 689 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_3.bm | Bin 689 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_4.bm | Bin 693 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_5.bm | Bin 696 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_6.bm | Bin 712 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_7.bm | Bin 732 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_8.bm | Bin 705 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_9.bm | Bin 698 -> 0 bytes .../dolphin/L2_Soldering_128x64/meta.txt | 23 ---- .../dolphin/L3_Furippa3_128x64/frame_0.bm | Bin 398 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_1.bm | Bin 438 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_10.bm | Bin 559 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_11.bm | Bin 728 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_12.bm | Bin 541 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_13.bm | Bin 584 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_14.bm | Bin 610 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_15.bm | Bin 741 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_16.bm | Bin 559 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_17.bm | Bin 492 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_18.bm | Bin 445 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_2.bm | Bin 463 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_3.bm | Bin 424 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_4.bm | Bin 499 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_5.bm | Bin 504 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_6.bm | Bin 521 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_7.bm | Bin 398 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_8.bm | Bin 419 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_9.bm | Bin 435 -> 0 bytes .../dolphin/L3_Furippa3_128x64/meta.txt | 14 -- .../dolphin/L3_Hijack_radio_128x64/frame_0.bm | Bin 524 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_1.bm | Bin 527 -> 0 bytes .../L3_Hijack_radio_128x64/frame_10.bm | Bin 550 -> 0 bytes .../L3_Hijack_radio_128x64/frame_11.bm | Bin 572 -> 0 bytes .../L3_Hijack_radio_128x64/frame_12.bm | Bin 539 -> 0 bytes .../L3_Hijack_radio_128x64/frame_13.bm | Bin 579 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_2.bm | Bin 526 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_3.bm | Bin 529 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_4.bm | Bin 571 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_5.bm | Bin 574 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_6.bm | Bin 524 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_7.bm | Bin 655 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_8.bm | Bin 645 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_9.bm | Bin 611 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/meta.txt | 14 -- .../dolphin/L3_Lab_research_128x54/frame_0.bm | Bin 611 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_1.bm | Bin 614 -> 0 bytes .../L3_Lab_research_128x54/frame_10.bm | Bin 576 -> 0 bytes .../L3_Lab_research_128x54/frame_11.bm | Bin 585 -> 0 bytes .../L3_Lab_research_128x54/frame_12.bm | Bin 571 -> 0 bytes .../L3_Lab_research_128x54/frame_13.bm | Bin 615 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_2.bm | Bin 618 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_3.bm | Bin 608 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_4.bm | Bin 615 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_5.bm | Bin 618 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_6.bm | Bin 615 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_7.bm | Bin 585 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_8.bm | Bin 578 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_9.bm | Bin 581 -> 0 bytes .../dolphin/L3_Lab_research_128x54/meta.txt | 59 --------- assets/resources/dolphin/manifest.txt | 121 ------------------ documentation/AppManifests.md | 4 +- documentation/fbt.md | 1 + fbt | 3 +- fbt.cmd | 4 +- fbt_options.py | 2 - firmware.scons | 38 +----- lib/scons | 1 - scripts/fbt/sdk.py | 25 ++-- scripts/fbt_tools/fbt_assets.py | 30 +++-- scripts/fbt_tools/fbt_debugopts.py | 35 +++++ scripts/fbt_tools/fbt_extapps.py | 44 ++++++- scripts/fbt_tools/fbt_sdk.py | 72 +++++++---- scripts/fbt_tools/fbt_tweaks.py | 43 +++++++ scripts/fbt_tools/fwbin.py | 8 ++ scripts/fbt_tools/gdb.py | 2 - scripts/fwsize.py | 5 +- scripts/sconsdist.py | 7 +- scripts/toolchain/fbtenv.cmd | 4 +- scripts/toolchain/fbtenv.sh | 2 +- .../toolchain/windows-toolchain-download.ps1 | 12 +- site_scons/commandline.scons | 5 + site_scons/environ.scons | 4 +- site_scons/extapps.scons | 3 + site_scons/fbt_extra/util.py | 5 +- site_scons/site_init.py | 16 +-- 243 files changed, 302 insertions(+), 740 deletions(-) delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_13.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_14.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_15.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_16.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_17.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_18.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/meta.txt delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_13.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_10.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_11.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_8.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_9.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/meta.txt delete mode 100644 assets/resources/dolphin/L1_Sleep_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Sleep_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Sleep_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Sleep_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Sleep_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Waves_128x50/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Waves_128x50/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Waves_128x50/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Waves_128x50/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Waves_128x50/meta.txt delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_13.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_14.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_15.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_16.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_17.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_18.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_13.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_14.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_15.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_16.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_17.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_18.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_13.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_0.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_1.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_10.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_11.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_12.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_13.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_2.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_3.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_4.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_5.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_6.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_7.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_8.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_9.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/meta.txt delete mode 100644 assets/resources/dolphin/manifest.txt delete mode 160000 lib/scons create mode 100644 scripts/fbt_tools/fbt_tweaks.py diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index e3d5fc13..f28fad20 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -65,7 +65,6 @@ jobs: pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} pvs-studio-analyzer analyze \ @.pvsoptions \ - --disableLicenseExpirationCheck \ -j$(grep -c processor /proc/cpuinfo) \ -f build/f7-firmware-DC/compile_commands.json \ -o PVS-Studio.log diff --git a/.gitmodules b/.gitmodules index 308d60fd..a97e0933 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,9 +22,6 @@ [submodule "lib/microtar"] path = lib/microtar url = https://github.com/amachronic/microtar.git -[submodule "lib/scons"] - path = lib/scons - url = https://github.com/SCons/scons.git [submodule "lib/mbedtls"] path = lib/mbedtls url = https://github.com/Mbed-TLS/mbedtls.git diff --git a/.vscode/example/c_cpp_properties.json b/.vscode/example/c_cpp_properties.json index db08320c..d1cac63e 100644 --- a/.vscode/example/c_cpp_properties.json +++ b/.vscode/example/c_cpp_properties.json @@ -2,7 +2,7 @@ "configurations": [ { "name": "Win32", - "compilerPath": "${workspaceFolder}/toolchain/i686-windows/bin/arm-none-eabi-gcc.exe", + "compilerPath": "${workspaceFolder}/toolchain/x86_64-windows/bin/arm-none-eabi-gcc.exe", "intelliSenseMode": "gcc-arm", "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", "configurationProvider": "ms-vscode.cpptools", diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json index f4df9659..c8b0c601 100644 --- a/.vscode/example/launch.json +++ b/.vscode/example/launch.json @@ -79,6 +79,25 @@ ] // "showDevDebugOutput": "raw", }, + { + "name": "Attach FW (DAP)", + "cwd": "${workspaceFolder}", + "executable": "./build/latest/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "cmsis-dap", + "svdFile": "./debug/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "configFiles": [ + "interface/cmsis-dap.cfg", + "./debug/stm32wbx.cfg", + ], + "postAttachCommands": [ + "source debug/flipperapps.py", + ], + // "showDevDebugOutput": "raw", + }, { "name": "fbt debug", "type": "python", diff --git a/.vscode/example/settings.json b/.vscode/example/settings.json index d84707e0..19a03b69 100644 --- a/.vscode/example/settings.json +++ b/.vscode/example/settings.json @@ -6,13 +6,13 @@ "cortex-debug.enableTelemetry": false, "cortex-debug.variableUseNaturalFormat": true, "cortex-debug.showRTOS": true, - "cortex-debug.armToolchainPath.windows": "${workspaceFolder}/toolchain/i686-windows/bin", + "cortex-debug.armToolchainPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/bin", "cortex-debug.armToolchainPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin", "cortex-debug.armToolchainPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin", - "cortex-debug.openocdPath.windows": "${workspaceFolder}/toolchain/i686-windows/openocd/bin/openocd.exe", + "cortex-debug.openocdPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/openocd/bin/openocd.exe", "cortex-debug.openocdPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/openocd/bin/openocd", "cortex-debug.openocdPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/openocd/bin/openocd", - "cortex-debug.gdbPath.windows": "${workspaceFolder}/toolchain/i686-windows/bin/arm-none-eabi-gdb-py.bat", + "cortex-debug.gdbPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/bin/arm-none-eabi-gdb-py.bat", "cortex-debug.gdbPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin/arm-none-eabi-gdb-py", "cortex-debug.gdbPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gdb-py", "editor.formatOnSave": true, diff --git a/SConstruct b/SConstruct index 74fa5667..448df971 100644 --- a/SConstruct +++ b/SConstruct @@ -156,11 +156,9 @@ Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values()) Alias("fap_dist", fap_dist) # distenv.Default(fap_dist) -plugin_resources_dist = list( - distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1]) - for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values() +distenv.Depends( + firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"]["resources_dist"] ) -distenv.Depends(firmware_env["FW_RESOURCES"], plugin_resources_dist) # Target for bundling core2 package for qFlipper @@ -291,6 +289,16 @@ distenv.PhonyTarget( "@echo $( ${BLACKMAGIC_ADDR} $)", ) + +# Find STLink probe ids +distenv.PhonyTarget( + "get_stlink", + distenv.Action( + lambda **kw: distenv.GetDevices(), + None, + ), +) + # Prepare vscode environment vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*")) distenv.Precious(vscode_dist) diff --git a/assets/.gitignore b/assets/.gitignore index 9bc0bdc0..26957704 100644 --- a/assets/.gitignore +++ b/assets/.gitignore @@ -1,3 +1,4 @@ /core2_firmware /resources/Manifest -/resources/apps/* \ No newline at end of file +/resources/apps/* +/resources/dolphin/* diff --git a/assets/SConscript b/assets/SConscript index e1bf546c..63141829 100644 --- a/assets/SConscript +++ b/assets/SConscript @@ -68,15 +68,17 @@ if assetsenv["IS_BASE_FIRMWARE"]: assetsenv.Dir("#/assets/dolphin"), DOLPHIN_RES_TYPE="external", ) - assetsenv.NoClean(dolphin_external) if assetsenv["FORCE"]: assetsenv.AlwaysBuild(dolphin_external) assetsenv.Alias("dolphin_ext", dolphin_external) + assetsenv.Clean(dolphin_external, assetsenv.Dir("#/assets/resources/dolphin")) # Resources manifest resources = assetsenv.Command( "#/assets/resources/Manifest", - assetsenv.GlobRecursive("*", "resources", exclude="Manifest"), + assetsenv.GlobRecursive( + "*", assetsenv.Dir("resources").srcnode(), exclude="Manifest" + ), action=Action( '${PYTHON3} "${ASSETS_COMPILER}" manifest "${TARGET.dir.posix}" --timestamp=${GIT_UNIX_TIMESTAMP}', "${RESMANIFESTCOMSTR}", diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_0.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_0.bm deleted file mode 100644 index 46079c3728bbc787dac3b6bd912534c3cb26c2dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 481 zcmV<70UrJV0Nnuq41gct{-6W=AJ_nYgZv-l{|EQLAJWJOKV)zscEkbuK!`yZ+7IZ2 zA`k^H{0tv403h%n5x{%GBY^UdKr8^Whl&a-KGuK#@pCZ$fTREYfXmDOv4I8_7$4|8 zWM9wy{zW2%1H=e2Efj7e5vqyGsa1l5#-&xN^>_NMRwDg?{NgbPBEkVj5nrNFDx-1l zhs9(-K@X&ej6;zJl%Rf5@%aPDK9qp^!U4m790)wJ1NH}yWI^RnAHn>AY(wU59C%i$P0j1>yyZb2eSZ&<<EXw3P3+Y`9bg> zGw`qs{?L2IGCmF}0sTSa4~A5K;qqV!{Q=`2l1RPCF<=n=r}ZkY=Heb41yFw9{7xhI zFDwB4@9UBO1B(J5LHS?bB2PRR{qOIQ2c8r@kbrq&0D=!9D38oEA@{_G&~zj7CzJxB z|4{mWm<1(&sQl034`;q07 diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_1.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_1.bm deleted file mode 100644 index e12d70796299cef4674cbc2e84e90f9f89cdf652..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 461 zcmV;;0W$sp0LcLW41gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znW0m%@IofB=C?8Ndg{z#eBYLYzDVKl;Doq5uEy!2|jNkN@@qEZ_g1{{Pp(1NLHZ5`cK(00m#7Q7XS*{roa9 zP>mvRO#zP{B1I8dfDER%u^N0_S{9+ME27rFS^biz)e|IKu$OrfJzlYp@m=A^i z13({@Y<|qu!2#onAFWhAQ3xC$9wqgWREOoTfP>0NKU-)#WTfGU z2i*Rg2mm~v^OfMhED%ZsA%8#U20{r$lt1U=0Kh=#f%SiFb)nte>GVQX8}N{kLpDxphzMk`jYS_5(oPa(oq11 z=rM!(lE5Gm0saHX2qW|VhsXpn@ObC|;s8K;MZx|L9)LMu1M+~tA^`CL5CH-H5Ac5g D_X*BS diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_2.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_2.bm deleted file mode 100644 index b416740f2772d3a057f2fd7817d1b16adc40af47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 531 zcmV+u0_^<(01pBH41gct{-6W=AJ_nYgZv-l{|EQLAJWJOKV)zscEkbuK!`yZ+7IZ2 zA`k^H^c){C0wF+S0QZDP0pybz04(9)A^*i^+Ry*~UJ(!=fCu&iFE9Sa1Q@u)f57#T ze?Ry66pAD$83+_~QMimosuwDyRuT^yr~rDq{Z^|He!zZl7=w{wfTM`7(I}N)@Bh9Z z7$`hyLG_Ulh;k|-k`Ky0KOlJr#iT$%52PR*H}SxO$b3We2asfNK=G&#;Qm@60K^CI zA1_o5fFt{N6@Y$KvHOF|KwuI3io_mN1^}7x9D%Rn9wc*+m!`v1q^8X)k4#Xu+Y4~RT;0Q1NX)2sad=j0k7 zaDWCve^7ZuGEfLWJcsCiJcrH!4+o6|{`>pHGlCB@`lHGpQCE}$15`htJfxB+0svwd z5&O^ABUOPy0pcG)`Qk?-1cXR1Blo|)MymygNFZzV2b72aGx@y&3n3r1{5(Bg1R{Y6 zU#L8X11Nw*Xb}=HkIJeaNU6AJ8B+R4hv^D{NTeDU`iIgI4;;aiNAlho790vA_Wy=N zK0XWpePmzYc#p;<0FqDuqCY?QJb*(NfJdkTf&dsjCHM#kKP&VI06m}p@_;}v5)gQS VL;#<_c)&s99)L&z_y-VwgV=lG-g*E4 diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_3.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_3.bm deleted file mode 100644 index b7e15ddeec980a2775b4df50901a2996a7991609..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 437 zcmV;m0ZRS>0I>l841gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znW0m%=`-fB=C?8DIy+z#ecc0u1NLHZ5`cK(00m#7Q7XSuV1`CY z4;p}dWM9E}$YfC2*hpfPa4=c@OXF0}r_UegN^% z56ZSbf5?6Z=>q-_)qejQ`Mi1+LHxrffbqZ|-TF0H6yP2(0sBV>RDt862aJFoGC}&=L+8+l2i*Q}m=6*VKNtN# zd{AKc(iigvh_l84D1Xf4Vj}Sms{J?+L-`NuDi0_HKz}+YI5=bx0sQ4bu z1P4KoAI(-jfESHKe=;lefF3as{MB|b@OZ!2ev*QC=rM!(lAnlwav*r0gnpm!`2dDq f4<3LVKnM@;dPTwU03VaEVh!?X5CFmc5Ac5gJVnKO diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_4.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_4.bm deleted file mode 100644 index 202ad6e37bf56c5959bb6ca06bfe101de96248f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 459 zcmV;+0W|&r0LK9U41gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znW0m-wj?004;!0}Kbnz#dcx1i%(>@DTs%|B8qI|1SuL5I_U_0hVw7&;Nho;}HS~ zJtSYC8Azf)@d6BsMH`63YQNBU)oQSjz<~y<)!*v1SdHWl*@?s;hzKA7uhA%#U$6fD z85k%$YC-jpe+AokJ_ZaE5rzZL-w|V$E$z-5DKJ! zoPXj14;BGG-Ua9f3$0RibCfPNrA{TK*8!OC5Y B#Yg}E diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_5.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_5.bm deleted file mode 100644 index cdc0a2a343182740de054af8ad6e8a42848e616b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmV;z0X_Z!0KNeL41gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znX0XaGQi!2g5h;14N;0O*kL5dZ4`iiiLI3xIt9f&d@b46}d!fBXLhfO-M#BK-i# zBvAk$$h1+oj7F>d2aQ&$4;p|FYQ0_lt5t~JK>e7QLr8QOfB{$NluEDH|9=dKJY)g( zk$(l_A<-p>esS^pWnd5*0R6+|3BXV)Gy(gE%ZraLjs~bdnU163^}>(p6OjCW@Zt6! zoEJb3A^FzF>jDRu{(^{EsSrG-1O1RM=zd`FU&wr5PzwNkhw2X% skiXadA0QBe#zDjX0t5hH_RvoO$`6hhIbZ|wE{qWVE+GIYKf(SF;Hdb~#sB~S diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_6.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_6.bm deleted file mode 100644 index e8ea3aa683605683a11be4c74d8946e2c7f720d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmV;r0Y&}+0JZ@D41gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znX0XaGQi!2g5h;14N;0O*kL5dZ4`iiiLI3xIt9f&d@b46}d!fBXLhfO-M#BK-i# zBvAk$$h1+oj7F>d2aQ&$4;p|FYQ0_lt5t~JK>e7QLr8QOfB{$NluEDH|9=dKJY)g( zk$(l_A<kmk_ZF^z(e$o2g*laz(8aN z_Oye?{SlyG8UjCRz&v2k{s#g9fFHF~9zU3V0>V%c`&5DB@du6k9x@U7+d<={hrlQ1 z0jVG2QwV@Zpb$J${v@;nc|k+!zd8+o9#D7u)oK9pfUoBhNC%b$e&O<~gNFe_f6RwK z4=LdOvY_&)kLwT*DZu`N=kOoah#oWvfc~nm5O}~B^NEB5&3_^LAEb%}06`1=f8p{0 k2s~sQKky(x1`lln@EoA{;e(a{KO$(s3FH7^e}nuVz*X;M1& diff --git a/assets/resources/dolphin/L1_Boxing_128x64/meta.txt b/assets/resources/dolphin/L1_Boxing_128x64/meta.txt deleted file mode 100644 index c66998e7..00000000 --- a/assets/resources/dolphin/L1_Boxing_128x64/meta.txt +++ /dev/null @@ -1,32 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 9 -Active frames: 7 -Frames order: 0 1 2 1 3 1 2 3 1 4 5 6 5 6 5 4 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 2 - -Slot: 0 -X: 78 -Y: 16 -Text: F*&K!!! -AlignH: Left -AlignV: Bottom -StartFrame: 2 -EndFrame: 4 - -Slot: 1 -X: 78 -Y: 16 -Text: What ya\nlookin at?! -AlignH: Left -AlignV: Center -StartFrame: 10 -EndFrame: 15 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_0.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_0.bm deleted file mode 100644 index 9147714c1e291e52f40294d265c696fc6ae5c708..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 889 zcmV-<1BUzo0CfYDU~v!(1pp2(7#2ba0isYMQizTMiKK)^720#ulnk15?AeiDP5lAF}9xx0=5)*)eL;*vIpiChW z2vinH0iZ(RGDw921%bdwu@VA^!67(^gd$0#2aG2o4-<&cAdtL-LlH72#WvB=6^$ zQ4eDvkYafN;s=b1k(9{N4`V=qLP6;57zC8|VIja=LFk?n*oQ|+K#)c(0rfqO2M~K0 zLcBrgfS|x-GYE)0a1;oDJz%jH=$^&_5eJOHQYR5Y3=H-;3J z;z8oY7x91w7JEuWz-l1((jLbHfPrEcLJv$`nD!u1$iSfx51=%mkPI2@tdRit)S(|j z=))uM2pR0k!v={`0p0qmg=NQEQeV1R?l2Z%kE5OOgnjNpVn!Fb7!WeAc$ z1f!Ci1qa|V88Pf!BLt8DgdQNl02G+^G6UF@MnQu3AJPpFJ)KZ6R{(lZk7QmNq$-fb z5c;0Q7EYlDjAR%wz!1ZaX#~JRBoNTZ*umpY&^ko+GK>x)fGHaQC}0`G*q8?;2qK8& zqX6?gh@e5^EWpA6;sF>G18D?3jFJVBghWB(z(^j_0Z;-Ah@3DOh1zfsJ0dJZgCxkx zBv>4DAMhV5A-G`&u@;PjAq;>|!R5p~i~txiC=7!EIPg0}9$AS2XlM{9j0y$u|HHxM zgo#2j7?=dAD@2h4#s&{F*vT*;@sUtu7a#e&;DGUjA4EDN0kK~j=L9v1ET|GFO%lZK PKzQIK5;%cCMPd(Nc|%A2 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_1.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_1.bm deleted file mode 100644 index 789273d9da32277651ffe8631e3a657068af1b5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 911 zcmV;A191ES0E+{N=4y}%1Og=ih+t@t4n&eg0Fhv70YQRPq!LI-7;z9u1TsYts7N6& zNR z$o4WBCPEN{DF6geBP212?4l5f4nX!&3>F~_FbN^-b_y{ZiR@&UCTm-JP96%9yJ9qKr|GH z6vS{)br8r8V*q^)hY(Aq0kl${A!ui1e~BN=8CK2M3m7kc%A! zN|87@PGy}NWfDhP$O924kJkioWNEfk>LZ!5Ro1U96{xXJ()mZ83-379(Dvl z9?48N5cq?L9xxmLSadvsXyjl+Q2-YLLnMO4G#QEnN+Sih1JV+EAn3s&QYadmV_R1v{}fpA@PAsAdmpy;2~$R%tRnq7ckPkiSeHU z#tT3_o+gt3p-ctm0E5y8r2>IMXcZuOz}4dt*p5b%K*~-7>U$ZKjtdf`VltnAX+t0w zv)W9uSO9pJMj0O$j6(o>7zBeQ0NQ*3-lnVhs$3g!A^0FS%5hBEJ4 l45L(JRvY7d;D@rr(jpWI1Om$MMTnlp5kSylWRMF8JwP+AOc?+G diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_2.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_2.bm deleted file mode 100644 index a3c87e0a6de7d598766d94945ad530339de2d96f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 910 zcmWO3QA`tO007{-UTg2VlWR{l4p!RhabVLjJ?1UZrRd-G=&p`Qkv=S$$tsjd+zY5# zOlnxK6n0muwjFDVS+F2;WG|z-B|aEaLWKikfIckx))b6;=n~i*F-tb*>-YM>Py$hf z1*6D16uKlZ+#2hY7#aLxoMIV_jt6P(W}6dc<>Hu^O~T6vgj_qoJB^|`!{gT(Hyndk zu*s5}hDPLaKNSO&Z~#Bm2MZ1R4LUI<*f|E27LK91wpMox%#&iA1w6Q{y0@V5O`R8& z;bj;oo*zuc-=`nzpv28JiEd7M`G{R>fK3WhD$^tBuvEQBrG|!s-N1Yypcw5J^aUBT znp&PcazMLt!icV#>h91NNZHjtnVZwZK5N2Ld%@`C4g5Crfg*&U-5lsV@$c37;rHJl zmr~feKIp&K&tHnRT7losYBnNNauj~}iBh{;e~o2Qge6`F@7R=Lh;ry|9}rgsm(W}B zxT}$N9bLzrkE<3AZKfYM&&nR#QB>#Q2|I7_L#d3<1gn;1Ghq+m-!7$9bizL~rdi`k zX1EsS(5w%m52LR2K|I~;^ZGZDC*r|->HaY3sMBbO_u>f&?@Y!JTN(_5zjnm?`n!HS z+hwXE2{jYv`uW}ZLm1RW9;@Q z2Q4h62Pot!f1d1FeDD*zD}zBJ+Qqm*>WuU}745z8#Xd@B*!Y;)%V)E)l}Z{$lTE==&kxd&ed~{#*&6c=+@X{`Af>@ zOGD93Ti-4170XD=v*KJ(Y*>0t=(8kfrf{D!TLrGqUr}$Cp@ePuExPdalNZ`dsD-r8 in?r|oA0o2lG%nSK^SznFf@KnUs?wiQ+WepN-~R)iPDSnj diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_3.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_3.bm deleted file mode 100644 index ba3012b7e5a476c4d58f100bc9a8ff16e28a31ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 923 zcmWN~-%lEM008j22iDH-bR?NuJW+c(CdW#W3JB1r=X;5%peN8vT34J+c05U-O*d;@yWx$5HozO?*(cecOtOb9P@p&`EO#ew@na zA3d|`+nnDFC=`LL@xGU#Q-aOc{WA1oLf2WZ0v+KD>BoGBmG*$@B-G^>o+sRlhn%c{ zB^Meq;rJcjFILI+SDWBn^`8+ebdPSo+!hr*%m5LwJ)tE#`=PJXX$c48%ScpWPsb@f zQVOwuJ!D@Cj&!+_o`4ceeu1?yH)k!-89iw`q$E4l-1|DzJP2Nt^&32BeE%E#Oxm$s z2ieoNC7@>%U1)NK2wxW(F;-!UygG|yqe6E=PHvkjw^S!ms{tzvl`b#BH>=s;DvesW z{{MF@+^631wT~~yA0y#YoZ0b*qoA)z2q9qZ0aqRHZuk_+>-$d*SBIEt3sFJQGYJFC zWNhCR8}l*DsiLtQeQtQ{C|7UnYb)}6(q+_1^G3L(P2WWTeSIFt>8UO995IS0!gRcK z11cncz;~e&6BZKJnWu9|^$5=M^u$mWD!a{DofF%g8yP?NIZlzXTkCs6_Ky+biB99| z>Mu_IIu|64`K>-zp*U7DU3^I?_GJVOb{mU~0g5BVauYn)C2d1Sm1#R(F|j!|x)0L} z$d#~l7V9&{xNnK5Me$?o9W7D3_R(tZ;}CfaMXJm6jE^03Ve)Rgh1WLt7(e$Gx#lqr zH9pCYq##jW<02w^PfxVq_dp}hKLDj-3H0=|&O+C^3_&TkO u!B>y&mLZ0pg#?B-%Q>X*d-qc`wX#V$vsWyO$N#~c`oU33;av~mpZ^Cg14kzS diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_4.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_4.bm deleted file mode 100644 index 1ce28c7adb733388b90d31ce7bc1d5e8a5998ebd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 894 zcmV-^1A+Vj0D1#}QXrB{1tLKaqDU~1NDLxH1(HIc0P%!K!Q(>2$RZR721bbxq)8@` z08rs5fbq~&q!=1RRsjSSh}dBuA);uaPzV$z3jm-|P9nz~L_$Pm91uKUFjy2IL^!V}{~{_Hpxj^)EDQpmbsSgo z|BsPCQDe+Vl!7HlfWf2i4fGlXM37=i0zDXoCjrEPz&;RIhcNa~DO46E(13XeL%^cM zp)^PbAoe2xfMO}W=V}K8gLXqMJ5Il1yu>guJWSJm(@FFL)gveqLix52pBLIaS%2ATcSpn!e6j;Fr zqbQJMECBPsNKs=7J(Vs2kPoH;14w|L%4bqzjL>o?5f6?e_Hrr|l1KSZjQAc|0*Hvk z{xAs?+g=G8r&DRN@aDL`WVW!2lE(9w)Mhf(H@!KcpD8G72RMBpWM;Jt)BP zhEX6=7$iz!A7m)vL4ye*3}hHFz!1Z*IKx;|QifF+lO)Bo63%I7J2t2!!3I0Ryrg$^jCf zz#&webR9w>p|T#v0!0w`(IF=BpNq?gXc$rKau|pt26^C*gUd*kc%7yW>EmNf6W9Z91RfmQV4{nAz{7?M0O{Eq`@%&afrxd UkMj?d2=*|_Op+oi5P6M82x}2S2LJ#7 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_5.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_5.bm deleted file mode 100644 index 4cbc4968951c8b4391c7da7a5a90cd8bacf4b245..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 940 zcmV~$T}&DW007|Q?l?|B?+4tlrFdKqi%ys0T07Q2oE;wbWtuP&H<;dtx?j^-axY%(8X+J`L~)QX_U8Z9ud{ z$>+pFOveYBQn$Ug3U1KaRUQgZ(uYiM9tjX|W|ir+Fo9Y)A60z&614IFo~{;ZsB}8G z>lV(_4p<4=ne-g*jG*{J0+%vKmTWQl?=Wl73gna`S8yez4&Y5;6QHw*o+PKQCFA&v z>Pn8iUM(WsXR1McYhBpj5T?Ofj+i(w^$Gb{Ekwr=Is_ETi|tEL&i{bU`kj`qnA@X* zX=_^{16!p&47ymo~9}?nvBU)V5th52&`>CH?B68DV_ue1Z0Y|}sd=m`$>P;`B9+>0-W?$EqiBJTcng%6I^rVw7^)5R$I52k`b+v zsW|0TBE|14D?*WL1Uy4#3HSV72k*TeC^mH(2tjQ@ZSG*QJXb`2{^qI`xVFX0<8b;% zmP)O-#cf^Acbh{U8iCEDUN^R0h|ETfH%SLxgf?)!MN4N%HAqu`sbLpJEQee!=Y(jx z!pn)ifOOH=!N*2@#*^ZFd65lMo;VrOJ-3~%ToWkP`jF1zqr$1gpYtjm$<0xHG;zZM z41JE?rLW?8qYcC^yFGDEsz&U3u`j)=J7EwK?pjGbD?WV#l@A+cr3z)HQeU^<#l+Y? z83vi*2yFRRvr{knUi6z4wRpkx{Bko}zj<9_C`Bl<74CpCUyj%z`yJJ;uFFy3Sx93n z!EI6w?8KE2M@^r|^$bJ!ZueAwv+R3o43$Q@c59@yIB<}S(QgCS`Ah+`4pKN*C3`>G z0X8>G&wvTT=3%QIDWs|SP*p$BhI*1@^CH-6} z8sRvjnX=J@Zeneb(+ySPWJmCLTJss|d8A#+48_@t=YLJ&c;0kzj4P5zIS<^Ydb;0CV@8DJs>ST3f;!oz4Qq#njP8&mm z4|j#EhrP~gN6cL2($%IhK9#u#qYoY{?dYOrGj2g+A}O6SW-+iX4O`;o_kqBt3O?#Z zy{|zO%#%O^lPe|j2@k}h@-3jD)Eb66(Rs>kxy;_2fAg?}U|V56H2x`~b>r`_QP_da$rUj{+@SF>ERH?Ar$xD$G(q!Iu3& z=AhSa?sDtyU3N5nnF?^=HMz#d(oZh_^?y;GZ^^YCx=uUhb z*rJtC_V9yegYZ@iTzB(ns?pDdzih%yBL3cf8-4BqxkJ_++E*{}i9*;dVIpiD1mvdE z7c&Gv=0mgY!jG2}o8T!fF{&x$yNAduRwusRC`s&?xnPMca21^r2;Bl6PuVQ>DsI!# znEg$zz1fjM`hf1?8vwSUP?n3h=8o#pPa$u3W)0G_ZgYb!n{ZWFLE1pI%<$1kyQGe%XP&cIJ^jLqS-rA;12@yr8SeWpszx|iGk7$a@z0+K>dJOmfhpA^% zp`zMER+wYtqd7%o@3dATQi(Z<_=sxZB7EJih4VkW(?%qFfF-MZ1NfD~P3uoAnWu1Q=4FIwVrhUItE9sJ{Sg>VWYLoBNZiP&N0vi{VmU zlo6L93Ggo*x&0_h+v`^S;Zz!yC`rNTB(T^K%_YPMq-) zLHj8>d8J~H_}h~|L5QoC+jNmv$*&xR0`YM68=;J;7$Ad_T{PAmg|^g1?JA<;o3aAwlK%8M1$Hce;#> n8<&_P_`=$s9S@Xh?bL)CyiFcD^mvAzRDm-T{+8Av+L!(ZB@#`e diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_7.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_7.bm deleted file mode 100644 index da28419fd279dac8ad18145dc9a1385df41ce7c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 934 zcmV;X16lk50HOnsz-kZ(M1mp+97qUo5J?0QMUkjTDKZEx3I&0PKp`+d(7=O31fa+R zBLalLU{HW~;-FFtKon{cAQ;53p}~MeF;oZ^A}JDzNH~-MaL6i9ff$3v6Cr_7BpBKt z;9!voNZe?YD9})NluS(mB`Si%xCF7F4h91dz|kNai6n@6FalXYDpCm~B=#VYkV6c@ z1rwOWs1giFWN;_4kYJz^oJ@jCN(Kq-AyP~fLQw#qz-U4tg1{pOvVuV(GMJSH2|}bq zFbA=KKygr+Bt|k1875I^FcaA=&O*l;4ImyzvaDb{pZE-lg#gGJ#(^Z2Kuu!+{(tH6 zD1?(B#Suuo#z27>`~v+Vkw_vCi4H*aoQNC);R4V=HWdg16i;X16ORH4C=-~%G(eF( zl)wrw2pp1;B!IMqqBy8Tk}?k{H4qLl3LJuxe-qflLFJ63LM)mXlRQWx37|p2D$)hA@jDQ|FpU6CaMgfJ$u_5tN3?zy_5P3yl5jYkGK>(#mBQ7Dh z1IFYZ7?^x%FbWg`Bn%(KA_3%xz#`Bn5Ugrc2o#0@k1xQeV$eol&@zyK_`xOuAr_1R zO$CWkF#z$ALZCk-aYG;wWw_+90Pz$FFjwXe7zBeQ0NN=9sxtt3z`^ARh{-8f2*5od z$FVFH0*M604#WU->J5l@XG35XnzfWlGC6#_vJpg<@v2vclh5FmMFi(|1VjRFZo z2Z)3~12i>^kU%mF4k8H$;0uTzSc*WHWEltq67M+R1Io!1h|)9=C_n^YnCL&?9&&(q z*su_c6e3V>8TdT7hq1&_Gy(C5$|oKN=!4DxtPqgNA`%~`{vHo3B?>_iqF6G3L6G=A z@GyCz#vz2H90ns0uz$_x1O*HR6F|sHBvmT~@qTba*ubz92!sX&5VJGV2zeP6BY^^- Iq{he(P&v{|ZU6uP diff --git a/assets/resources/dolphin/L1_Cry_128x64/meta.txt b/assets/resources/dolphin/L1_Cry_128x64/meta.txt deleted file mode 100644 index 1b7d13dd..00000000 --- a/assets/resources/dolphin/L1_Cry_128x64/meta.txt +++ /dev/null @@ -1,41 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 4 -Frames order: 0 1 2 3 4 2 3 4 5 6 7 6 -Active cycles: 2 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 2 - -Slot: 0 -X: 22 -Y: 40 -Text: I miss you -AlignH: Right -AlignV: Bottom -StartFrame: 9 -EndFrame: 11 - -Slot: 0 -X: 17 -Y: 40 -Text: my friend... -AlignH: Right -AlignV: Bottom -StartFrame: 12 -EndFrame: 15 - -Slot: 1 -X: 2 -Y: 29 -Text: Why are you\nalways away? -AlignH: Right -AlignV: Bottom -StartFrame: 9 -EndFrame: 15 diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_0.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_0.bm deleted file mode 100644 index 8558f0f5bd29c5b76d6c222d7c85d52ab284e154..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 294 zcmZQ%P-0}LU_Kzn@PWO-hVcPEGKca1{|5=LKR6@^KBzs%;QWF8js=SaV=L1IH3#+w zjC=Wa$Lk9CK2LLbCB`HWC7-b4J-I(2#5*^aR#jFO37OA^}lG0 z+|rGb0(J=^42cT^7&dV~QQ$D>eXOc_{ROn zF@fr@3~l#CYqA>JnHt;}--j?xV@OjHWH4R8=5nZ+`OXCc$HW5%-!TYew6i2RaBQex qY<|G*k(2y_nIWe@{^$!y!>R_h^8##l82@^(f12R{$K;eQ(miEr(rzy}T-4@vmuCID3D#ndPOCK}D<0GEJ3z=sR83SEjo^TZX!IK-3> ziNINn5C^BCGGK6e{VeAfmnYNr-w(X}2yp-9oF4V?A^-pJuzS}*>FzTkF3V*x;2tB9J0MLWRxxj&o+Jp}~{F*?nWid2Jyde1>pZ*FDh=qV2UziX+ zED^wYUSK5g2etzS1TFe?zy&~B9{?XR02xcc=YWHh6b4apN=5XjI9KX?h!Y8XA|2#F`05eA_3Di@J)Nb`;Ym%t`9dA(X8 z2xA!h!RrWO7=TX<4>Yid1Rr#W1wpVVVUYvoKo7<-s>ojD@ogA9WF`fO0g{*xBzr)@ zLIoR`JYp*P;9np(2!R2B2Oz(L#EyUig8+xIdw|dZ_vE;P#t1?X1b+8pA;ANu0sP;Y H1;juD{u;iw diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_11.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_11.bm deleted file mode 100644 index c91ed2fd23efc317a9fec7f9244f5ecf26bfdaf6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 698 zcmV;r0!94+0JZ}EfFeKmfrIjX59|Np9uLp{LL3k1U_W#*kbcNuF#vwR<^cS;@vIO) zd(Z#CF}ve9B@qX-JRV&5#tfn=$M69PXF`dC-_IZZ0Sk>X2aoI?k-rCw2tNFDh(7RG z1aJsF{OCR-a1Q{$x3&;_+0i069sK5s4--KE3qB7Yqr`7u@&FI;)>rrp1JI!=ED16{ zi|2eOP$&mL2sL^*JbV9r{(!)9X&8`x@E~~0{Q&eFj|1S45O~kx-2i$rMgb51`=XFY z{7@c~_&7X%AB}K5FZveQ1B3uPee_r!fBx`1X5jOjOTpql6S(+u|M*Be_!7x95WXpK zPLKW!4>$%49$ov|$bZxR2|R!U30bH0tOFAfIQ$(2*5D`?RtQVL>}ORfY^cfK@wo4#6MrnKs|tmU<0WTLH;XP z27sI(^o9V%@FvkNl$ajh@M$c>{x_Jwy*v;P#4Z6m2#gaC7YC$sfA@mGI8b;bBL5Hv zl>>p}ulg3lJ^Tm<|BIl5$ALfoSM3lU2l@jagnjXN6H#zK_4KgJKlR@P9)Acv7K8uq zuhJkK&-_qkJ@}dkx*z}k{!f7M-}}L#m&pO;={q_P6AHB35AhFN`%)TJ2HpaDrD0~`qk(=2CEZ z0f)!)C5Sy^VDfDd6n=P6c!CNF4@>zJeV5sIJm5g`(0}*gLcxQ{tS}II8iWXe@eB|E zXaM&oh#-OHDi9%W;4^FixNpGtzyG;6_}9XI3LFReE(ABI%&-txX8^(=_6Q6zF#`F| z%pwXHSUm6OQH)?em-v`H|E9r!(1@c42fM^ULUj;%Vgc))|K1Fy>Kb?a9=!ZL{EAT6 z1w0;~ugGj@5C#axa1?NQe1YPw7#HjRdiZJo@l^SIcmMtl`T!3dm;X)wiUZI8-@skq zAn}sVy5IRje7^$Wz<=Pt@uKiS==&f4;{o%GgT|Nt_kZdD@qLIqwsLTI)$jj*{(v4o zPrv8!!7vCsb@%_@{(}R60SDkA5P1JeqyPQ^#q2GC2cG?kAOw2~a6#jN_us+G|A+7a f<$u2eTmSC}k$89X5PN_N?%5stv_aF0B#1shQS}MQ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_13.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_13.bm deleted file mode 100644 index aa5353e988d02785f45759a024931abe7d580086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 584 zcmV-O0=NAE07L@+fII=<4*+-rz#ahb2Y@^IANT|F;Rk?x!S}#`z&(Kbz(C*~0DZs# zKZbz<@kP z-_ZGk-W5OxmVe$YUN}5vRseX=FaHn!_#S((c=Kc+^8fh5iJ!HD#(O>ojswBL|KI=9 zyXAl$Ey0L`%fG>Z-}v0Uoqw17zfWUvCftXMrUwwc* zFdOIv=nzr}54)K^iv!95wSa!G^7S~tJCHd1P#$^^c)jswmy;2(?+ml*DY%6Tpaje?1R4~z?+C+R@JB71OJ%^j$i~g#Q}fdP!0e4@$U+bI{^s# z3(Nlv;0TA07lYt1>XG_?f57p%{4ekc#4b7D#lRpxNFUrDAR+mp#tHC`{fFbU28aM| z{|{g!U}Yi1z%rjedF|ox@OZ&E5DPI#e_na`JbB{LzW^w(J#=94pcIrmEe`+y diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_14.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_14.bm deleted file mode 100644 index 837c6c71defd8be280776e7881817fc68276b5d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 610 zcmV-o0-gN<0A2$B|KVUk{Q*P)Fi?2?0rvt2kDwoLFnIg{_W=j^KOjHgdBMT)0R98a zDj@jq1I#KQ_}~M|;Q;Id;{Xp%fIlVx`hWxc9=`B>kL(_GhKz#+o><3>gVG5B{Tt>8 z4;U5;!}CwXJVamY_&z)lfe}FLAMe0Dt_H!Nasoqr`-9r~&_@VJU>l>0Krj6W95O)w zgJlpe;FN#haR>y*&>wsRUrhppDF-nZ{sYsG|A@(;VDX@2Js+@oPGA%6%nc+tE0kSi zuxJH>Lj(tm3qcDYD|B!X2tYh=AoBP#)&UWmL9h@2c-lSS_KA8RbO;895vb?`+9^1J zKqL|g{UbmRX+$ywAO;Ttt3U_0LJkiYg@OTE2b}yKQ3wz`aF!;BgU_B1DFg(5A@ZVV zdEoJzgVn$w5#o2S;PHin%RrqGp+G;L5P3mC<-mYJ58@9VCwyLBV0pmdiSdjD2SN@Q z8NBEa$N2-wgZ=&g0qGDH4O%!v8W;Zm|NDc@SRg@@1ImB)|6{@DKq!m?&N3Yo|NZ%| zgUlq?4N0o0xY&yEj|qzoQt^FED>06iEa;GRJCuV4t{^!eXG#1C2(0)+vGdoW=5 z-AFJnC>}#T4`yhz^{KA$Q(_^4>({vevs<0996^* wNHeH?5Gg=DU|{j6KxP1O`UB1b0N_Ay5Dfv)f$A(Dg8+f^cz{o!766F@+?Nvri2wiq diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_15.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_15.bm deleted file mode 100644 index 4cb6e53325ca82bc1ba52fafee1ac42a1860a50b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 719 zcmV;=0x2edpMFaQvP#()oKH^LrM60rl^I503^{{iLsHy|)^ z0P@TO$c~T*6avV`F91BQ2NQrnAQ7N>U;*j-2b3ZK1#_7gbPDB+U69S%_nXL) zC-VRZJnv)jfk2!FPbpLAzn>Mrz?udRAJnuSUypGxMd<{tLA* zrXFq&{(Uec2t8nMZU^Um&;LikL=Pb#gU11WpyB_e!8i^9U?f3-fro8?!+z!9@{kxt z1Au`*;1$q#@xkK^fn+M+^1?tt<^J!N#5gpGLDXAf4;s)scmMbcfMkji;6cIZe>Vs` zWDyAj0>L;3j_JTF@t8;f0&ac3Iqm7QxJHZ1_AZpe1D;lp}-`8@&O7E zf4~2Td|(6-v^gM1u%*MM5rYZhlmIgItVWU zM*v6&Tz~M)@I2@cw)t=ZxC7z zA2bOE00<&62h@N_MMD3>!RtVd0SE*!_y@oqya2gi1HuX-s!%Wq5(kL{%Bg_BK>)`F z0hGWVuplXB7K?0mW9)pqY1KH}Ok%5B<9%eKGis(y(512t1 zV1o5Im^_lA7$AJa0isACd4S9wN1+GifZ8p>2hD&2D1rsA;Y>{dhy%&&jsbR%2w?#C za0n!fqF@vd!9nLh_()r-01$a420;v=I8aJZka|FG^IL|R3GfZ_(Gk1Pbd4kST{ zMsfi11Bg7X2ZO{(1R}5@neYJv=m)TLP6L3zL7)(Ddpk-XLm%(`ia3bCJkmrDD-itP z9{_m_j2T6gp61a(gZ{ZM1zPgjQn6h8mzDgPw@^3 zz}F@lG7%UECQ4EVmjV2M^?I5VRtF3sqyij97+5^uQ9ycvN8SY{0Kl$5kq4*%+69tk z2m(_@KO~R>1LeVjgXn4{VF%kx+rka7e&8S}(IJomV8H&oCqy1_6AdOzA27gr0I%$+ AWB>pF diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_17.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_17.bm deleted file mode 100644 index 02af0bcf325201270bdfadedbec283ef88d23ca3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 400 zcmV;B0dM{R0E__u6hI%*fPX>)+z)6lKcGPUz(7I#PwD_arU3pz{4gKTMmK;k_{2d3 zDe-;_GJ(K9|L_=t*8Igk0EA=6#lS<%$2b$10XzUQ!9pRyf*1$@!z2$7S_H`=&>jM< z!s7x5oq?9d6#|P{0)!VvvyBn0Ld9!N0es2xXIKD?hHNkcf>bJ^nHs1)f5le^scc3> z4<_k=4fG(wLZAU;7sx;mVIRmiJ>nnuh!AlQc;G$qANZIMe+_j29wYxF0t`96e+YOy z`5!qud`_ZP5CH#(h7U6`01AE&KlnW>;6u0r4{JaeiG>Chtc7q; zL13H+9^e56h;T+EH$XAiJOEMf2b8WMQ;(nw0EJZm6mSf&0DG5>a9{?&7mP;0180j7 u0|XCSfDp`V06IWm_y=K9vXGzv!6FDmf$)kW1cS%`P~bR%6du3>^KMRlm@CSfA0hhpbq?iBy zfrHw>1U~>!W5JH5eJR~-y#2rfd}y7oJamf1Q>IQc^^4Dd`_ZWV0xQm9$)Z!SHb(hAEy8x69Iam zOBw%{f(jT9T;K?EQ}fZl!RiVqqNK+He{2K-RC^G)$P4e(0vmi)@u~tG3IP}gy-;dn zF%XCWtpGA66d7V;Ljw#DSa2}=fCL&L!5EMNEFJ)0_yN)z82IHt7aRxi1V9;pFHiv4 f;sD!#KdAuzRg>(&`v8E*0s8e2eMA64*bo4KNFID{ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_2.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_2.bm deleted file mode 100644 index 94357802fc84c61ff65389b3e8e592c882f7dbf8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmV-l0igZ?09ye7cmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 z2i!OWI01zM&?Z0!-~kRW51RlG<^cHxAW;AcDnX(E78q1Oe8BN!R}iTkwCZr_kd6iL=zu`d>--(%?dhyEu35Rdu>4}3@dMx-zKoNNH~<27iUx!LC*lA+s1!%z z06$O$N8q_n<%^kOb)bFk~5wa1*0=zys#M xtsVp)fDe)2|J%Yg5C*>#0W3lQWN;tNQvg>1!5{>@3%Md74DbuO_)Y8hDgXv`km3LU diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_3.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_3.bm deleted file mode 100644 index b0d0e691407afc3067f1adb44437407fb75e695b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324 zcmV-K0lWSI06+l%cmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 z2i!OWI01zM&?Z0!-~kRW51RlG<^cHxAW;AcDnX(E78q1Oe8BCSxu9Tz!-0d`03gv02*iL9VDJM6zz&eu$Iu1<*x)~l&@y86 W01ciX4Y&jPkPqb`gZv-h{{T3j6M%aF diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_4.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_4.bm deleted file mode 100644 index 3413e507294a89aad208259baae4b7e14c2fed36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 387 zcmV-}0et=e0Dl1hcmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 z2i!OWI01zM&?Z0!-~kRW51RlG<^cHxAW;AcDnX(E78q1O89{;T1P7!C`45Bx)GV+V zJmMe^SP&IBa2WtSlS^`dND1uC5CO)4_5N5S{2t}8859eE4|oLt2{}X@+NBsi_q~snrkNk~DU-LNF0ppx47bE8< zU>E)9K=FoP%)$>&1^fm#3k0eihX3V`WjVd+2l)@g z1Jtar7(C)2rw{?afT_cP$N}t{TakIiA_Yj$KEKNZpM%`CBO-xt0q%z*kc7wK9|ydG z`3MP@;vNi1zzAX=_=^y~kbedK%>c|wLF17B#NYxE|3Ja%IR}m-|07Zt{LVH2c;^d6 z$oa|G1^;>wJYkqKFoV;9e*ulc!77Jg!R7x4mw^0Ji-f8S-5`1&jy*V#LDb?8Q}K8q z@#usO7z!L`{$2zV@Poz&4u%8IH9tm)_?6E9iUx!LC*lA+s1!%z06$O$N8q_n<%^kOb)bFk~5wa1*0=zys#MtsVp)fDe)2|J%Yg k5C+%*{KOyzM*;lRFa>ZN5&%oUyOJUR&j7oxgxI3xgWa$Lk9CK2LLbCB`HWC7-b4J-I(2#5*^aR#jFO37OA^}lG0 z+|rGb0(J=^42cT^7&dV~QQ$D>eXOc_{ROn zF@fr@3~l#CYqA>JnHt;}--j?xV@OjHWH4R8=5nZ+`OXCc$HW5%-!TYew6i2RaBQex qY<|G*k(2y_nIWe@{^$!y!>R_h^8##l82@^(f12R85=KBE9Kzz`}J1UMKzV8BQKC}{+U0724`4BWs}hy&t)1xbKDEC5uH1K<*& zRTR5KMyg4E8C8uffcQXg0QiI$i}?cw%4DP;=sp7je+RS>9*_@-1=%GK{KMc8_mjYZ z>eUyM1JC{s5HN$-3_$!F5IoR=C?I?Pf%_N-od6!d0-8@2g)j%qq7d{bKt5`UGE0a) zbq1q=o7g^JqF)NVgXRh;0{y@RP&u1@&}{^;1L=Bm0DZv)qLLs4#wQR6U_Sg1ba;d9 hyh!N)A-cc^a0l^w00j;Q@jIEc5A$+^{2$%`d;q4lY*+vQ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_9.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_9.bm deleted file mode 100644 index 114b26391f8643130725f03b5209d97ec8e791d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 312 zcmV-80muFU05kyrcmu#50PqKZJOSVj0C)qy9{}J$VSx>WL;?FSkOOc32Y^r@AJ6zg zZx9F%f(HPHgW~}KfkyzNa2Lb^4S){_RDtggh*$vU0tdhTK^Oy^5FZeDFdX3k_{WZp z;FNgzz<>q#kb~H^GXOFDBxnd6r_zQGf^u~z?r{gO9X^zK;9Vc&1y3g)261O<@i6EY zJS-vXAoYj=mJsj|Ldq8l&;blEKCOi!lqlUm^1wnt34j}{f)QXGEdl`)1Ej(@IC8r1 zAYk=-xgo*uNEd996aa-)W57N~3{_FYl~hqM61xf{{eTGI0o&ki(y)of90xeSK4K_) z2dQviVEsC|Jjdk(up4#=_JONI0s9aqI03v1&Y%PP0L{2Vx5I>AjsawYgnx+xAb><* KfO)#k4i8}640;Rz diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/meta.txt b/assets/resources/dolphin/L1_Furippa1_128x64/meta.txt deleted file mode 100644 index c21027e4..00000000 --- a/assets/resources/dolphin/L1_Furippa1_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 11 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_0.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_0.bm deleted file mode 100644 index 5eb2bdd05d20de1a4abcae2b7f79aa7c3277ec5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 555 zcmV+`0@VEh04D+fYy=2+2pBoRC4hr~h6E3)J|YABhlAdM1b|v86Q~@(9`Xo81L05S zKsW&Rh(sVCdVIJJB?p8^AQxN2cn%|B5sV&r0RDgf_xrsr92X!E*dY-xvCu9Qh%nrZ zMUV;)&^|Tr_@77~Pzs1WL*fr2Fn|W6EfD&w$R2e-xP#8?NC%Yw9$tVrhtrV&z5~y} zc4QCIKky%jJa9ns%zJJi@$>V6=K+9&$|3vSKz^US^?f2Z3xo>*QY*ZA2lW2mtKzZ1 zdI9G^w`0ZQ{{L5l<@|y0A?fk_Q^DvTAb89a1^1i4E}#GPb_Nd{cE zZ!j1bJbWJ*_&qK_@&9-h1%UUE#2(1;fKQZz{#T9C|NO4}s3`KlNMIa}KRyR^-~NN+ zLS>bM_{kgn-VJo`{ek0p2b5v`@_c_Y z>ix=8J|d8T2jPDQi+q9S7zd>N{($os2n@d+_jt$t0P&3i%YY9*`8-aeU;nLyfM5as ze}&=?C|X8AGkb1r2w8lz+(spoKN5nRZ&C%<-m;OAi(pI1I7&?Bph>K1tKx1 tM}R3V1b?6kAON-q4wL@`ygXn6KZ$@qLm-enL;$`3`V9~P;12+J0DzyW_2~cr diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_1.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_1.bm deleted file mode 100644 index 210f0c918cea285c191659adaf8d55e740f72e12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 557 zcmV+|0@D2f04V|hYy=2+2pBoRC4hr~h6E3)J|YABhlAdM1b|v86Q~@(9`Xo81L05S zKsW&Rh(sVCdVIJJB?p8^AQxN2cn%|B5sV&r0RDgf_xrsr92X!E*dY-xvCu9Qh%nrZ zMUV;)&^|Tr_@77~Pzs1WL*fr2Fn|W6EfD&w$R2e-xP#8?NC%Yw9$tVrhtrV&z5~y} zc4QCIKky%jJa9ns%zJJi@$>V6=K+9&$|3vSKz^US^?f2Z3xo>*QY*ZA2lW2mtKzZ1 zdI9G^w`0ZQ{{L5l<@|y0A?fk_Q^DvTAb89a1^1i4E}#GPb_Nd{cE zZ!j1bJbWJ*_&qK_@&9-h1%UUE#2(1;fKQZz{#T9C|NO4}s3`KlNMIa}KRyR^-~NN+ zLS>bM_{kgn-VJo`{ek0p2b5v`@_c_Y z>ix=8J|d8T2jPDQi+q9S7zd>N{($os2n@d+w|K|?0P&3i%YY9*={!!OU;nLyfM5as ze?{UCCL3O12hu;lFCYWJ9su|N)o=Bl diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_2.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_2.bm deleted file mode 100644 index ff2851c280846f4470944424c7acfaee9acc2d6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 560 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 diff --git a/assets/resources/dolphin/L1_Laptop_128x51/meta.txt b/assets/resources/dolphin/L1_Laptop_128x51/meta.txt deleted file mode 100644 index 90cdc5ce..00000000 --- a/assets/resources/dolphin/L1_Laptop_128x51/meta.txt +++ /dev/null @@ -1,32 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 51 -Passive frames: 6 -Active frames: 2 -Frames order: 0 1 2 3 4 5 6 7 -Active cycles: 4 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 60 -Y: 23 -Text: I have to rest -AlignH: Left -AlignV: Bottom -StartFrame: 7 -EndFrame: 10 - -Slot: 0 -X: 60 -Y: 23 -Text: but not today -AlignH: Left -AlignV: Bottom -StartFrame: 11 -EndFrame: 13 diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_0.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_0.bm deleted file mode 100644 index d4cf85bada7670d1b6d1fccf281fcfbea3533e1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 514 zcmV+d0{#5~0R900HUJ;s{|ER#!Tt~MGXFo|{elPhAK?E7_&#_a|NJ~3bO0E_0r~&_ zLc#fgf+_1S0_hxEt6Wya)gUQR*)_1V+FO z7z#dS009CF48J-Eq+kdzV0?#$7!SetgUT2Wj0DDM1_$24#ZZHT@8NXYz`)}eH^;O4 zln0Ff9!T>Cm&*HI2dBgT|Br*uiEkH{03I;_V1S4oIgOnh9#a7PNI!D&c}#EsF#s4m zXdk)wKRg0)2jqdcKp~j}{2rI#`GeAE4@?L>Fc9#1l1u)|5J30??Qjs2sGv81IbH$f z7z30R03sOR9J>JWrT~WkKM=5p$R2=lpaaN!a14QHU=%PA$N}YmfP>co!TVQZpz$$? zJmMHIIHpPw{5g^&!Q(`-KaPz=2dsk)nViA^!vf9bU>pE(LIL9Kf8&^R9y0LUa6k1=GY_X{KMX`JK=Ot&20jlPVfbvp^#_wQv1j4L E03aFJ-~a#s diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_1.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_1.bm deleted file mode 100644 index f1f0e89f0bf92ab9618b0b7416579212bc000e05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 526 zcmV+p0`dI;015&CHUJ;s{|ER#!Tt~MGXFo|{elPhAK?E7_&#_a|NJ~3bO0E_0r~&_ zLc#fgffO%jd@DIoS1`k?5Jg5g@jzA#h^#`;X0Srf|yyOuW z1P%chN6f-ufGCIzOYxwJ1N1&T03g@_@;?xMU?ww55FB^__}Eyg7f^ltE}MHq0vrz` zdwV~*Nz?)(fWV{B9$zc%dTI|phyVW{2cHt(x13-k0P%=1v0-uyo>ePBWB90!&h3?!2O!Q0{p7_dCC zpkcTON(=%R2tDHf@`AtwMH2^>WEM2Q5a0*m79IdSg=IhomxF&H2aW?^2pSj&WB~FK z2t9BdAGLNm4-+9s0px5X$wEJX;sgu4ycA0#_~_I?d*){_fOx>OWpW>gJhl=j(JZhs zIS_oPAn~AVIgrQ@WOy9TXNTbjqrf6G4CF=*s2T-=A_*D<3CuAb20zRwDgDCDW&;)c z9#}s>cwiRH#v>4rD*!yO6Tj~B_v4;S=3w&RKp|EP2fGDB;&>?ZN+2|6;Pj2xeUtqL zWB?^WkORgEVDNb)aR_9BL;w?@$HC%4AAww;egp9H90pn$gh3`M(U>2GBG52-z$4)C QwjYMfA8E#S?_px7MM3xQx^3kK`}Tiwm85(F%jJEqWm+YG_<#TL@Okkq`+Gn<;s7xL5Il1m zIy_WAau3|UL_ahkgU*5bpV)!-;1i7eA94kW1N?u6bO-o8K?J8De83Tc1R#9>;NT$- zD*zy&ZL*Z{%g2S5Vf!RJH(Fg^1i1{$C69#S9!3=ifHE?@z?KL|d% zm=7q30jLxR6_aoeD=-0!D4-k$AOOf@fdj^70APEc-T}a0h&<2nK#&8_{@{_1gV6rt O_dmJ4&+Z;K;UD0o(vidf diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_11.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_11.bm deleted file mode 100644 index fe93787f2dbaa45e6546910e547ae553947e3827..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 294 zcmV+>0oncm03rbZHUJ;s{|ER#!Tt~MGXFo|{elPhAK?E7_&#_a|NJ~3bO0E_0r~&_ zLc#fgf2W^ zzlsC&KlAbX1LA1{SLOPl6%XYHZzwnC|36wsz&$_tR*7If0CA6l+TIU1fDAxH4@mfb zwd3Hq#h!Rdepj07Hd7$hhjgMtU=fF3X~en5Nx{D^;}2uMGSqz}6Q z6oL3I-A zU;%;e5dbi}_5kv;02n@jLBL`F3=cTO2p%&q0|VUt@Hjv{Pw)>IKs?Xz!Vr2N;1(eC UKf$bFAA@-x-vpF diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_2.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_2.bm deleted file mode 100644 index 3050ba38f22ac4c8f1ccac17830bfd3455abaa1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 542 zcmV+(0^$7u02%@SHUJ;s{|ER#!Tt~MGXFo|{elPhAK?E7_&#_a|NJ~3bO0E_0r~&_ zLc#fgfqEK2tVN7p!VtSN3MM3xQx^49f z64)Ex+5O6Pf#{Dgd3>+6?yMXhUl0HOJ`X-6zi()A7Q!F{5D^2%F|(u1K?EO(2ku`Q z510&q1P>Yq?tcyh7GNI!0XUP0A2I+StBHOf`cD)CI1M}S{K4z82c85Tm;~`aJU}5N zktnbL_27Vp;07X5KzIOhzySsw7z30R03r~1zyO0grUi(8FbKdr;vNhIgjQ4ld3Z17 zQuuH994Z(rWB~GjSO8_<&)6`WAGLNm4-*)N#sm`_1pknkC`a(-Q}Ym$hXWkAKaPz= z2a*U&1BZmnApl{4X7eCHm@J9`{)fg1Wr2~%gXB0U2jV{rB8pUboX-!!Za5?fLI|Tk zutZ`s3Ixb-K=SwsO5tX+0Z1N^i9if(nT$pu9#Ft}&p4aU-;Q}RnS;_`1s{JBhs5wv z=#)fI-{HV9dJJyI?4Rf}AOR{5f*u_i_v6Vwh(jb2AOM{QKN~+1+`sTY0r+`^qX(2< g0F@XN!2B@~xkuJylMpDtt%u>W2h<)+(#4;L5FzQ{VgLXD diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_3.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_3.bm deleted file mode 100644 index 0c0c832385caef6fc7bcbcfb485699100bc5df3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 557 zcmV+|0@D2f04V|hHUJ;s{|ER#!Tt~MGXFo|{elPhAK?E7_&#_a|NJ~3bO0E_0r~&_ zLc#DX932A#1cd|R;s3{j^YQ=BgYt0z&jaQHJ{~{+LJv#8Jg^aQaR3H_4>~|Rs0U$k zhyZEk^9Q&19#!;ti_Sq2fCEG%H}reVCIIn|kcR$WeFRV*Fm#Cn+&*7890R})f%xD) zC?W^G0%J76d|D@A(0t2hK6TgA2?39|xQ;9$pFj8IWHH9v~i2 zM@g=T0Ca$QtxgaRI$@B%fYJw%0N@m0c~Pj8KzWQ3h7T;(XEF&a8l;b7BL|d-03q_> zhmXM`LKi!k%<%jm{xEso1LY8j#9}lG1PkU5Kk%TZ_Iov$3_$bXd&q?>#NraA0PhEm zcm3Xe{Btk>g3Fk$E&sa(L*jTSr|}ew|9~zm3~syZpXd+5e;NdU9)1rbjv)*`njmA~ v^Y|&xz}PCWh|3 diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_4.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_4.bm deleted file mode 100644 index 5e74ea12ae20161d809bc90a813fe9be78929a4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 488 zcmV<%yeeI8=-kVJ3*^S`6sVK7#T|)H4`lBX0Km`YpD=k) eBhms0%n#(BICwm~5%75159Hf0d4Y-ri$9QvY}0N4 diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_5.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_5.bm deleted file mode 100644 index 5c556cfdfd3f46173b2f6eb279797637000bb792..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 469 zcmV;`0V@6h0MP*eHUJ;s{|ER#!Tt~MGXFo|{elPhAK?E7&*B~r-}nFi5g>enh5-l% z@AeiCt^hn>@SuH&L?4q6{r(6Kvhhd3?Exn!fI;JU2bKaZ4=(^bzu@tt1ImDQAbOj` zA44Jsh`i(x5P8qaM2}g7z@BmN82K!NwL zu~Z_U`}kcp@_>0y3GeLwR;45T*k7|G~gQ4?ejxO?ej~aA`OqCi4>)!L2OvL?oMYhjxF|&aL-?6Ppb`W58`K_LC<{bSXD1+1Da2WmI9#{ytJ%6C_|I$I_K!h9E093Gir^bE) z8~8k=5fFP5NBsu}l)_*SVz~bS=b(xY*a$y}7=BCQ1RsP1e>4gR5Ml`Oz)WV4AbspC zRS2j(EFiH5pTg<4lmpU!Ul4iwKe$!mPh|GQy|NKM%GztU?$+!oVnoUsPU@Z1)F@+QZ zfW%rd0CC0QF$n~L1IA}_h&D!LGns?j@-TpS(SV}>;4j1;d78*Pvq$ovNCD|0@eYbc pg)9Vo9*2KBl!o7_$TtjQ9y%<0*Hd~*c<== diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_7.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_7.bm deleted file mode 100644 index e992d75c7a000c28168c9a683b2cd8a00694f195..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 486 zcmVMe+nUkfkr_Q z54bo%=P;NL-~7T&1W+FkKg4`LkPiSnhwcv>)B}Jo90bN`0tdAy)+X`TSgH|Fef%z) zc|twG=j{IFD@gXFKtbj5zSpv?62N>9gMSa765qG91I{3MC=fie8#+8xKGX2|$Uk!Z z5d6SoA`p2{KXdyKKKuf62h@RLK=zC)#2=7|CIEgya|g5tAF#*{3?3kYgag|h0KrE< zqKpurgV!_(8N37k2LT8@xq=M)6-f(h@=Z)VyHzy^BjPDE}MBlzG09LXZI;uN5DM3 zSK9VfqF4`y|NkEclnaS(+u8x=5CMn?ppXp4&W{xj%o0H${mb-2^FkN^aFQAa?tfwj z%@RT26O8;HGC_E(NFOsme@{P%ax;)U?2$lz(IS9?Q;@3zR|h04abzmBIiZc>&M>KfoDZ!RJH(ClbI9e8>TG15^G3%0vJ^7ruTp zKbSnZfCH9ZJRTo}A6?7`ltch?8H9p?K!I5|0P?c{5&>{BqJVH1fC2J9`1CSBf#Wj( xFg?%e02l`WejxKdwq^l9kOR>EfC2=Jd>)7N01*Ap=l~;opVb0D0)7$wKm>gitj+)c diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_9.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_9.bm deleted file mode 100644 index 6ebc55c16edc741797d5b69ba3482afcaa5896cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 317 zcmV-D0mA+P0678wfENA-U;~5vAK?E7_&>qz12^~VKfwP7_&>q(KV=WVAA9f^{lWPF zf&Bsbe*OJH{txhfgV+2IoH#tM{(yY#!R5a@5xv3ok1=`o{mA|2^UM2R^@H?3|t=KG1jt$I{^1IinM(At_Me}}*z9sqgH@)V4?JpJG?0T4WF z^v%bpKYjQoLb^XRA%o9<1mI^u_a76F03Utzi39w9hI9w`K0yShAbkJPf&?IZz&`*X z59gkL|NaB+K8_!lKEMUfAJ_oF#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{IV1C`3Kw{ zUl05g9z*sAmGglI59B@p9uM9E2LPN0@K^H&_&`JApQtn<4FLW@)AI)6Pr>6456ln9z!-F3K7E?FeBggb ze*yzA52n1nVDe{=z`kMfUz88=7g!G5KH&0zLLd|k#UrhCo{!8Pa7LKyDITrVxIW+k zh6Bg#eV>Qa`YiIyQWir${QLjq^5KJrfdKqd!ROciuf!HB;WvyhKB@AsKpy%7)(4cp9x(y|1Jm3;5P4vawqu603MGK z|6k#NL4jZmA`fqbd>-Io2bch0!1@uK93Gem2tBa^^AIC!Y KBj9xacmu%J8sK#R diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_1.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_1.bm deleted file mode 100644 index 0ec761cbfd5c88d482ea64236928813bae064c6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 540 zcmV+%0^|Jw02l&*&>#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{IV1C`3Kw{ zUl05g9z*sAmGglI59B@p9uM9E2LPN0@K^H&_&`JApQtn<4FLW@)AI)6Pr>6456ln9z!-F3K7E?FeBggb ze*yzA52n1nVDe{=z`kMfUz88=7g!G5KH&0zLLd|k#UrhCo{!8Pa7LKyDITrVxIW+k zh6Bg#eV>Qa`YiIyQWir${QLjq^5KJrfdKqd!ROciuf!HB;WvyhKB@AsKpy%7)(4cp9x(y|1Jm3;5P4vaHi0=0J@+7-~-X(KkNSj8vub|4I&R`2-so3^#zDL#v`MJgXBhjU_gV*0RacJA{Y-v e_?bi=kq_a4U|?mBlmq~f598r{5%4;IJOSX1q3;R+ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_10.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_10.bm deleted file mode 100644 index d4207c95d87c1178737332cfb846c4f3ef65a217..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 542 zcmV+(0^$7u02%^-P#_Tq1QG!O12ljT;Q#!8zzIJGykL5>0DH;->nI1mAYQU?GJpo2 z94H6~e3%FXf&mDCK(G{mddUIt{wOd$EyYNc9`7=z}mRhrgny-@$j3-BLbA^g9e z_$g>Z?hh#c0uB!$`vb>;1As?^_uxW|;6HN!c-RB-Ss3^{cm?@Cj4&QhH2p&$&hZ0)zhG90flh@xLfXo*%pP z1HsS_r)I7nJPi5)5by`rUSBu(m&Jf3^AD2zNrC_v9f1C!@&}j*LFLxE&qwADGWg5^ z3T8KacwU zA0+t`2Luloa6EI@dEMu%4@eXS0SyO~{2o5x_`qWn2owVVFD3}bY5JCa~EC@Xy7wBi=0q_SD{2suugQ`W&Fc|@q zLFh0X3=SL*pa9rlkZ^uPV36>6nFJoth$r~Id;omRAP_7B03I-aVG2YZQGkDc0p_m| gdqg0f2sk{I;tL>pL4jfbI2aWG5da#91Hc{tz=$g6mH+?% diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_11.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_11.bm deleted file mode 100644 index 35955bc2021298f14e78677f1134681ff57ed248..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 505 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 JKpp__1AyX#(Xjvk diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_6.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_6.bm deleted file mode 100644 index 887a4b866414750fa353f434e7890755aabf34e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 524 zcmV+n0`vU=00;tr&>#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{Js-G)4;UJLV1F1nd|=7=;6Q&cJAsfx@DDg}JcH%gtB2G4 zKOY1%KAQ6RgGLDeh<}UbA0_zk4Wj}V4lq98^AL;-9zAQU^nPITfPNqtYDcSeE)Tx~ zeh)9T_I@8(%$N z{(tG<^C^n_VUh)K%psTxV%;DJ@q>641suR-f$Jh%ns01ONh O00d);od6yH@B{!JY}+dU diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_7.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_7.bm deleted file mode 100644 index 8e9a34e971bd15694ea1fbbb480a712b73950009..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 515 zcmV+e0{r~}0RI7i&>#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{Js-G)4;UJLV1F1nd|=7=;6Q&cJAsfx@DDg}JcH%gtB2G5 z30Mp?KAQ6RlST;uh!_U0A4T}^5u`30XW%v=^aq$kv5?N?)_OlMdVC-lXgA|^E)Tu} zeh)8&_I@8(;Ko{FBlFX2vacnC(6M9!7GNr z0paF6^PA6D9#a5#lty6z!{@=}?jMLeph^*rU~f6V`$i}bC^P~efq?%9>;H#?#sR^Q@$WSZ*-3{j$2f^%sWabcl!NKfz5P88k5HNc$#L6J@{1=!2FfdF25&`^kpaZ}j F0Dyyp+Bg6J diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_8.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_8.bm deleted file mode 100644 index a430e480a6fff0f3e6a619747de0e6cb40819722..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 517 zcmV+g0{Z;{009Dk&>#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{Js; zhw2Q#90dJ;P&@Pj;qYJ75As4E0)DW6$OI37pPmK>{lGX32k;+$1qS^vfOFtK>bQJ+ zHE{WZ#a=xK3?Eh>EqQ$KGw1|CfG`pH2g!aT!H<{6fq;PP1L_YYaaaN4+`7+4<_|OI zgUi{vmj~Z50GK#O`F)><)_DM;1Iiix|IhzE|M`5la0wIx@&90mJi7n&_>0Da5`acH zG7$PF%E17kLk@|F2ed#QqOkxRowgwLLkIyS z0QCWgJ*FS{2t8Ni089uynj!#6_&w=>GJ+UJKo5E_J-8rp2b~NbxC#h8zu@$Qcni({ zQ;0np10YHi3jntudS`+11`q)9FyQ_EU_tKp5P8o50uN>QnM6KC=Ku^06(9o*3!y+B H0Pq8VP0`#< diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_9.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_9.bm deleted file mode 100644 index 487f50b354b087e2a65878b92ca3e653f1b244da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 526 zcmV+p0`dI;015(t&>#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{Jss;s6yGFo;0{j~F+A3NQ#jJYazLhy&9B85Rftr0uZ> ztOLv)AOOk%>H{XZ019FMfP>k7AOygJ-M%0LpM&Bs0~jHMWB~Yv1J{BJFd3c5A7*n0 zpZp$&@h><0>>%~LNC1Tbz#)hpU;&SV;Z{h}gM;!3{D1@70Rab}^L`I?_?bi=Mb-cm Q3>6>)4GDPw9suwJ09n4>asU7T diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/meta.txt b/assets/resources/dolphin/L1_Mad_fist_128x64/meta.txt deleted file mode 100644 index 93e59e49..00000000 --- a/assets/resources/dolphin/L1_Mad_fist_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 7 -Active frames: 13 -Frames order: 0 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 12 13 12 13 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 67 -Y: 24 -Text: Am I a joke\nto you?!?! -AlignH: Left -AlignV: Center -StartFrame: 15 -EndFrame: 19 diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_0.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_0.bm deleted file mode 100644 index 2694219ef9eefda1c8d52e3ce99cd1284d0d8d31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 763 zcmVrBnnlSRlM?kSPVi2@upQgaA>f5hQr9 zSqB0O5bPdE05MQ_z-+K%nDOE|fOpudH`hRdR4*Wh)9!NbZ zIh_adpMUy@#sNqNo&#Y~)W$gb{ZIfw<%qwoMxiOILU335r~rTTb_l@W@(qL~OfVgI zeCQ0(z(yk#h%A5}HsJXK%7K8eLA8=uh6f3v53GX$#DLYLaheS-${$vcoX1}goXBPx zfcn5D6RZVhBl(EHZ3Xi-3W>5=-f&+BIKrB^N0FX!kSqp_gX>^~C7P% z5DX>*Z>qx=#&i!H0)Su~h%)?Z6x76{hvh|tRbb%t t!9;K;@{=FN1`k*l`~Wxltd|IW5PF(K20Q@x(4WHt2Oa_t4=MnBbU+(OFu?!- diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_1.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_1.bm deleted file mode 100644 index 3c9623d4c79a19e74bfe403f8819221e10322d62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 764 zcmVrBnnlSRlM?kSPVi2@upQgaA>f5hQr9 zSqB0O5bPdE05MQ_z-+K%nDOE|fOpudH`hRdR4*Wh)9!NbZ zIhhCZpMUy@#sNqNo&#Y~l*Tyw{ZIfw<%qwoMxiOILNHhQr~rTTb_l@W@(qM0fI1y` ze8A)2z(yk#h$uX*!SV-{0|8)zYb3Y~1`|XdSq1}%0jo&xGzwjmKCK}+j=m#7kjym! z^?*z#SPINX!x4bm3+8MX2Lcb^*g$j`sD{KoTB}mbpce}S2VsPOSgNW4AdmpFoCpp= zlK{|ap->H1s2+Iy1O_n)fLJQO(D?O1#el=WAajU_e4xSg_)+H&Ys7>;P*?zcKhxSI zGx!D&`XEGHG4|NPF3j?jV2LDx(;Sa(OSRfd<0r8I}kAEZeiLFwq0vN0iUOd2f&LEKuLdZY`8i5i< z8v%oO0pr*_kN{$!@qoc#5PSy~Bd7)KEAWA;h5;di+#nj8@I05|qab+9VBk@$&;A4gii65A0QiH;RX?~G2Yw$Y3?v?W z3f^Wx{O8~PqA`FZL>^KNbw*Pda1O3<3vNf`iK3A0T-E2Y~?9$#579CWt@GV27^@! zWC(ox191n5U`V6=53lfSAn|+%d|<)!;BUj?(;)dlU;*>^LG^3#6A*py6WYC2(gqmt zec&E3_~dXreF&6b*bP+Z9#PN#RslkkP%eS-yj5nP^0+}Ya0+Ci0k98@5I&p;7yJJ+ zm`5rB@qtxms`G%uiK7^ihsp!0mn+Tzb2prd zf#MJ4CW20Z<$#35A@S%p^W2$+0l7vTBA0pp;6LMRac`2Roj0Qq47 z<52KG_y_TUf4}oE`B;O4D2PEY3-N=OM*bfd6h;v+jzS^$500-KFCX}Pagq23LI;E~ z41T|V`~$|r75G5M!vNw(I1n9W_#R8~QII@@g8;5i{saPDhyZQ?^wdx81_9rP$^$6} zrGGOZ{&VmDQ5e7_0Q10VDl(YIAAhO<2t2tH2nB+%DXT&-SNf;`fAV%{i-kTgngHl^ z;q#CvL>@58t@94-g323+F!{kVJJ*7zQdKu@9E2)DJtwR311;1&XSm8m-hGd*Jy}AQ}x+DgYl$ z1zbKzVX#$yq46+?0Dc<1R1HQU@`DH0{vQ}Qpo8NDfCtZkAJ%H2p<)lj0=ZT6j0QX( zZ~*=_|NQSCLPZ!h16Aq=ox}O~xXLIOK=`5ust-7XBjNazz%~K#f(O$94}^aw^El-| zJ}@N^tBwhb@xbHKF!?}rTcCMEpkK&@5(xkglm@Gv7Z?5m3h==2m<_(G3||~@JY(<( z4-^U^@vK#Fw+gNtJT?pj0&))+H2{7zXhy3BFDeEByfFI^{HU;wt^}S`6M*0$0f7&U xnhEL^hyY~V355d@|Nr0WvRoz9R8R#_@F*NdIS0mtItQFUqBt8E7-#|U(EtcKLwx`M diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_2.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_2.bm deleted file mode 100644 index 13916806fd62d4aed731cb58265eab34adb4b755..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 762 zcmVrBnnlSRlM?kSPVi2@upQgaA>f5hQr9 zSqB0O5bPdE05MQ_z-+K%nDOE|fOpudH`hRdR4*Wh)9!NbZ zIhhCZpMUy@#sNqNo&#Y~l*Tyw{ZIfw<%qwoMxiOILNHhQr~rTTb_l@W@(qM0fI1y` ze8A)2z(yk#h$uX*!SV-{0|8)zYb3Y~1`|XdSq1}%0jo&xGzwjmKCK}+j=m!Skjym! z^?*z#SPIBIKqF``nXq6S2tR{i0f1tn8xZ+wtxGb1Tr2?b!ayulRREAk09noeJW~Up z)k2^euTVVk_yBmqAQlR*^gcaMabPg;2pX6}3?1VOfZOV@#qpg3 z%71{MVLr?8tW|Kg3aoSm5U~%+iwLU0$L9u+&*K;q s`ALuC0|%TV!$ALk|M&W=mk53o`bKaNGbsngg#Hjg6b26fD;RuqKoWI0G5`Po diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_3.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_3.bm deleted file mode 100644 index 751fbc3efb282916cc38d5999018e804cd4c0a37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 759 zcmVrBnnlSRlM?kSPVi2@upQgaA>f5hQr9 zSqB0O5bPdE05MQ_z-+K%nDOE|fOpudH`hRdR4*Wh)9!NbZ zIhhCZpMUy@#sNqNo&#Y~l*Tyw{ZIfw<%qwoMxiOILNHhQr~rTTb_l@W@(qM0fI1y` ze8A)2z(yk#h$uX*!SV-{0|8)zYb3Y~1`|XdSq1}%0jo&xGzwjmKCK}+j=m!Skjym! z^?*z#SPIBIKqF``nXq6S2tR{i0f1tn8xZ+wtxGb1Tr2?b!ayulRREAk09noeJW~Up z)k2^euTVVk_yBmqAQlR*^gcaMabPg;2pX6}ciuJ510UA7%7K_Pe8f{#_%5zxU5Qv;P^?P8vyvh z1L6-k1mfXnnnx-D@qq`{!2pj*Ba$%rKu_WiItKznA~lG7pfy{lJfqwRg#v?zz-{$d zV))L1PFMTAvg;PWVy pM*@E-G5la)^`3vg1AnT?aEIXstOyKT0Qk_K!vhDPSm*)q(Ew_YJf;8u diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_4.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_4.bm deleted file mode 100644 index c1135b467cc5fe2e313984e30a04ded3dd814e7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 759 zcmVrBnnlSRlM?kSPVi2@upQgaA>f5hQr9 zSqB0O5bPdE05MQ_z-+K%nDOE|fOpudH`hRdR4*Wh)9!NbZ zIhhCZpMUy@#sNqNo&#Y~l*Tyw{ZIfw<%qwoMxiOILNHhQr~rTTb_l@W@(qM0fI1y` ze8A)2z(yk#h$uX*!SV-{0|8)zYb3Y~1`|XdSq1}%0jo&xGzwjmKCK}+j=m!Skjym! z^?*z#SPIBIKqF``nXq6S2tR{i0f1tn8xZ+wtxGb1Tr2?b!ayulRREAk09noi2jNM8 zXf;r%2CLK$JbnTL_=UhM6<_FldZ6OKVc-xyq)a|gVEX(h^N2T!P9G>N06w4T?GhRB z4TyaZA}$#FY;hNeBzQj%2*wJrDB@^!Ve!BR%m6VA6!J+bpj`vwcn^vsRwYE?)1`0? zfP7$q@duoPae(;DW0e5-z=P{xYK{gmBM+1W{vh(cP$mHJf8L-2#t(j-z3jS2iPFmd1^0P>&*$3*xWIm`e6 diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_5.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_5.bm deleted file mode 100644 index a4681af98e85a780faca27f632565d56a90abee8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 757 zcmVB0PzBVP#_wh1V*4h^Q%=r4;6re#y}sidI#q!KOp_)f#@Ib|JA;~|NVSF zaiA#b1NRjIjbK0F{}1|oq7Vqjg1#Sx_{cBtNru2bFs=pzg&nX(e-Qe|)~{6o3|1Ks zpV(mf2-Q+6sFiCqKp~AmBoql03&9`$N9z+>rBnnlSRlM?kSPVi2@upQgaA>f5hQr9 zSqB0O5bPdE05MQ_z-+K%nDOE|fONp1LOSv&;#X!fI=t` z5Wt_t1^)lc!{uTJj)MUKz%RxQT^sm(U{M%EBB20>U_3g!aJ+xv@y18T<2XbV?!ABf z1IEJ@_(0&F2sk{U`pfV8P)bMOCA7{DnI3j{I&fY?-JF^)d}Q~(fpOcVuBl+~daEB#aeKl(capz;lbCV)B} zcznR(Ef~aNu>}W}xIRGiutBwwTm}OPq7T0k16GmXXcW6BeW3?2*TiTt8HS)f(+SoB zvk~ybU^asJ-@)U+gZMTO9R?~Pu@9E2)UzlB!odO9VIUSNs(?r&04(PM1CXS^G#aQ> z16Aq=o<9Krj6zofRsM&^stzm$9svWKL__5U53jfcU^cr6ZnJ5c|hPO1_=NUlm@GH2bT|nFn~aCm<_(G z3||@0yz$V81RMl4imn#nRgQztV0airGJroCG$U0G2b;Kj5epFfsIZEx93Ef+$V6}_ n@{=FN1`k+A`~Wxltd|IW5PHD?#lR1Z3H&fHdP0HaKo5?HvfnzS diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_6.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_6.bm deleted file mode 100644 index 36f2d084fe3277d555dda68b3a9675916ce237c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 785 zcmV+s1Md6*01X3xP#_wh1V*4h^Q%=r4;6re#y}sidI#q!KOp_)f#@Ib|JA;~|NVSF zaiA#b1NRjIjbK0F{}1|oq7Vr#7-N!1m0(_h@t{H>K=F(N$51I5i%Xy{O8~P zqA?GT_#QAVR;r^ZjB)qiVbxF|ANYBQYV}!7S`mW3)j$J(fPY{Z2nMfJ2@DJeULQue zU;^U>2bH)!Kw*qG$AA|ZE&~CC(Fe&^Fo*;~z!w=F27ybmhsbsy@q}yw;}L+!W*UI` z1H^7HYQS7U;sF~#e81rF!(erRae%-vQ4NTEy+HB2U@F0J2aXZ}VydVIst*_k0PsNZ zq(C$ps8j$DK=H4J2(Ct9{)faKRsi_Nuo3uxe4xSfoL9cEIP}6mA12U;&V96I0|$?S0TQeMhJ65_C&nR!3=9In;187I{2w?*5(45cgiJm^&;0;CSWF}W@c@W^ z1Lgkz%){kk#y~K_LH;ks4qY4gd|*a_fIwhSPxE|n!twuy#~B~EKrb*MSii>q_y>)K zEAWBQFdxVy0{$Nx@I05|TN(xf%#jdy)+hf00YyRUfS-rPe{e7k{61712lyT#p|9p- zAI^RM>IfPH1OE@;YxP>HjHWTi-+_lz4JrmYCUU%fg3b0>gwY4f01qf9^08Hs;Aj-PD13q902~~DQL2c* zWHSILht2`PPZd-iAQ7|&%l;1&0Khexs0;%Y5ZH&y)Gr_a)vBQJ!ayulRRHxt0stNc ztUgqT27^@!fNHQH^1Kbi9wu?HRez!J2cGzDAn}L?$_yVK2qb(9@#>YvKp!Y91FOM< zz(37_pWpz(A82F)@qg-r2KfMw2hOWOXO07ZKAe0w9DNCtVAu^+4mcYh1P+Y|z{+SB zK=_(R2ObBF0yrfY46XsN4~!F3%oKS@r``wz$~jOEj0>v+pM%E$BPaxFMjt2+st0Tb zmjVU^64-~z1J!|Y0p&4*cpx}j2H#bNz-P#_wh1V*4h^Q%=r4;6re#y}sidI#q!KOp_)f#@Ib|JA;~|NVSF zaiA#b1NRjIjbK0F{}0FF1|O98#2^spFn(9#Aiu!jf%=7TI}jK^e18!7$JVb^0Ss0d z5cl9e`uNpSE2x!gH9#SaK_nN81P}2Q{zvN*TBTG3F<2)8VbFkJBXCHDp=2NejXI17Q$+fWtn3P!r=2!UhHbV7^4d<2b(u&Jl!wgU5l6hzG~{|DXrU350-N0pkNN zj0^q$nTN{7jDTUFJO?1wAm!1&hsFgFtUw?zDFgxqsK*P({vRAVa1LJ-NlKe|!K)`vDBM5<3C;tKgMM3QZ4~+icU>*2;s5lSsJWYdN z%*a2S`~TENGzbU&9%3C{s-r24argS50E5T-0Kh;h^;u0?5rV(fKm&hco&Y{Dkifuo z;q;6*$AA|YC_Jsf@>Pr?0TA#7#!G;}VKhPV9f&+(7XY}(@H7ftls-W56O0>h7Z{8N zLon0_(ZE%}xP!z3Hh}qm!Q*&5czt6;s6@>TtVZ6fLN-k0qTMV0l+*E z7--To62B0rGm~YybuYLa-bg=7gh&92aW(nPzcnFK2RN04%iPa1Pllz zu@967s{-T$%3}oZKybJXzN-y@^+B=?5`@Ac7$x}DD!5yR*NqVslrCWq;X^0`@jDHw z4I)wu0u?5RSZqV`qQWk)N+Q6a(jNrD&>``YK_^g3MED**LxBE2|NH$`ON5@mG8_n=;>2_ diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_9.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_9.bm deleted file mode 100644 index 99ed507179a3dc7a64a99f871d18c55796a97cb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 777 zcmV+k1NQs@00jepP#_wh1V*4h^Q%=r4;6re#y}sidI#q!KOp_)f#@Ib|JA;~|NVSF zaiA#b1NRjIjbK0F{}0djKPmBu1I7ne55oLp7ysl0KTxg)0_;C8#6Ge0tJOe56^2A7 z@;C?jH&lx1C0fl;2xCx51p-X~fkpq3`oz|$RRIiE2rnAoJY$eZhM{C20*ye4BWHj> zZUFJ@9!LN&P$bQ$M&E2Yw$Y3=|%4 ziq2+1{O8~PqA`F$0pkY(4!)x)jB)q+pa6n~0WlJc3acrrLNHhQr~rTA!G}OF7#&@`ss42EH- z53BqiZEy(eDD+a z33bwjuI>)nnlDkWeER*d`kRx77g0 zRTmbF4nZJ-SYVLI1!Aj(xOHM{frdmRAB+SJ5kMb}8WHMI|3CNotd|LO2b2N}C@_p71Q!ta(5F)Xc|fHc0C>Pq H!XF(FZihX= diff --git a/assets/resources/dolphin/L1_Painting_128x64/meta.txt b/assets/resources/dolphin/L1_Painting_128x64/meta.txt deleted file mode 100644 index e5f5fc0a..00000000 --- a/assets/resources/dolphin/L1_Painting_128x64/meta.txt +++ /dev/null @@ -1,32 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 9 -Active frames: 13 -Frames order: 0 1 2 3 4 5 2 3 4 10 6 7 8 7 8 7 8 7 8 9 10 11 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 57 -Y: 24 -Text: No mistakes, -AlignH: Left -AlignV: Center -StartFrame: 11 -EndFrame: 14 - -Slot: 0 -X: 57 -Y: 21 -Text: only happy\n accidents -AlignH: Left -AlignV: Center -StartFrame: 15 -EndFrame: 18 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm deleted file mode 100644 index 1169e42d690a46a34ea8a8e1910843a4d46556f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 653 zcmV;80&@KU0Eq&sfICzG;QxpGKjHrm_%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9hgxfsM+m>fXw~f#LNB$6cx5CFg?0rHaBWj;#3R%jnca6D@B-!7-gtkq8g=fEHq zx5|D+_=m=;Kj0WfvJYVSz<+`P)d%Yb{}+u*N)JgmeE-+*c^7!R>aI*NP#nfa zF(wC$mqm)zTV-@8L^=f!%SJo^@q+8H!=d$4Cm0AF!a)K5K>PvoMU6v|e}P~mei{5T n@fo3FwjuGSOfUt3o`2wf3;BI$91Hjmd=v5T$N$Im3ZMYM>|;S8 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm deleted file mode 100644 index 80e2f39bb69255c5c17ab52708c536680e2ad7dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 653 zcmV;80&@KU0Eq&sfICzG;QxpGKjHrm_%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9hgxfsM+m>fXw~f#LNB$6cx5CFg?0rHaBWj;#3R%jnca6D@B-!7-gtkq8g=fEHq zx5|D+_=m=;Kj0WfvJYVSz<+`P)d%Yb{}+u*N)JgmeE-+*c^7!R>aI*NP#nfa zF(wC$mqm)zTV-@8L^=f!%SJo^@q+8H!=d$4Cm0AF!a)K5K>PvoMU6v|e}P~mei{5T n@fo3FwjuGSOfUt3o`2wf3;BI$91Hjmd=v5T$N$Im3ZMYMr(i() diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm deleted file mode 100644 index 959b02556d64ec11a58378b58d291bb1843394bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 650 zcmV;50(JcX0EPmpfICzG;QxpGKjHrm_%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9I8tTOt4|-EOM`51*@hD*gwD)E^yosFHfQ?UsAw z4vf@3HD0M)>au+-zsMgMczi$u0GJ2LOJ$V#D*aiYeIdZ{tIvG8pC+?aJP)4$fLh-v z`4{3J8m#|-VH(IigXaPM2nSRjtRMVdH7zJTB;oV_U%}&~-m{WSA1mxP{|A6sG@$hl zmm3cQk3cyM<&%fUs~@v?Bk&4D9cpq9z zTd5g>ScBRwA1rvhLGeEilzS)mg@eSLU;*HN|70=9Mk3&WjMK2%nMbO*FvUP~85qQv z9x`1PD^+ck(4i3M6hkc-@B_vRuEP$8)l8gVAa@A_2mJ%^2hA2W4n_V2fRXrT@Xy3% kg^JjR#+@+076y6$f&4G!^`LMs;6dfevkzfcae_rL=k*0*)>*ndAk^n*Y(dar-io8a%~B(-0te4sE2rB+|<=li$$$v(C; z8XZ&u0jSkWzPz7T!#O0X!)n3t2aP~JnPB-swVJ6=Jahr`@MHt$c&g1o<3Os*zr2w7 zNeAPE)y9G2zz>g1u%NAwcpEpQt{2SOsDm0KNy*9$Ma116V&C z5`ahV{eV@YqVa$S%pQ8(Fla;L|A6ZM|KIok>b2YrR6aNO3XE8S06k{QgR1R7FQNIb z*k8ggDz2&WEs_G3%T#|NAiasQKmo1R>abJzm&et;m45@n>JN^)RRRy3_RBr;2S#ci z5FnOLt0&US{DJY8hr|FTbJfXgvY#bit27UnRsk>vjb3}@)cG}p3LB^1iZ04*;@hLFykaHXa8a zfN~qlCl8c<&ESu~DG+rj$Ugz{@x~*`0mw-N2QP%x1LFjKjldo(&ipZfTGpwzy^3J*XBuiQR1t92tW1Dpfe zE*~DQI9?^O2ekbDQT(6bUNi!6fCqv9{gB5Y7>9xeGfu;5Wge>J!xaI{WMdOxc*%5F ztyQ*HLWD!0Q4F+Wzz-NMy9_!XRWfmaf!rhzAM_8vA2e9hIT!dA0!QJW!#@$47As;O g8g#<|SQ+R32k^g_)`7skfd|1q9}Iu|e^9Cb3>pbCT>t<8 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm deleted file mode 100644 index 389d2a8ef52e63d603a3d5c6357d14886acd4518..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 650 zcmV;50(JcX0EPmpfICzG;QxpGKjHrm_fevkzfcae_rL=k*0*)>*ndAk^n*Y(dar-io8a%~B(-0te4sE2rB+|<=li$$$v(C; z8XZ&u0jSkWzPz7T!#O0X!)n3t2aP~JnPB-swVJ6=Jahr`@MHt$c&g1o<3Os*zr2w7 zNeAPE)y9G2zz>g1u%NAwcpEpQt{2SOsDm0KNy*9$Ma116V&C z5`ahV{eV@Yun7DFK4A0K>4QQa8Tw_B>gPvZcp`nR&L;COvO@z-jgL~_;7Y_s1W zbY`LP0tsa5vVAPS$R8Pad_V$cJzSQ{De_hNvq1TEU=sj%)#tulPm@`yo(Iw#4;^om z{EP7qmjHlOkb4Ktf53sjH9`3=kMKY>Ehs%C;q$?N#p9&jvyx07E9)d+@CzoC9-;E% zVc>D-2O+$&aQR2<-U$2xkq1(ogYX|O9AZ3>9E6ZSa`;V9J}^)G2H+1FJCr~JDEx=U zRS1$yJ_3=Fhtje?8Lgs8a6k-ze!GDAVJRPv6b?XMPyKv8P-WpHj6=Z#8K+^jGLKbqVTyp}GBJs; zJY>2oR;t@8p+X_hD27@w;0KHsU4|VGs+l;zK<*L<5Bdk-51K4$9Ep%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9hgxfsM+m>fXw~f#LNB$6cx5CFg?0rHaBWj;#3R%jnca6D@B-!7-gtkq8g=fEHq zx5|D+_=m>7Kj0WfvJYVSXkb6V0P2JB)$m~d;_<0zLFp$CmV^I)2ac0^&PgzQr;zXb z9sy+1gVa7;d@K$<0OU88PG2bfyTKoTQXuP7kbVQ@;*3X>1CWvk4qpkXH;exPxC6#d z1rL;?@*gNPl41N5jFdi=k@;Czb>0qge< zm8y}LC5Szu;qu0d#2*vz`9`vTh*&&H#sD4%|Mo*1gkmlT7|lBksg!!FlMGY`A(4zp zf#W67VzpM;T?!EnfkZOVj{rPiy6iCMeN@TD0taxAK!4Cb0DSdQs)r)~0>DW8Gx%rX mGeX5|L*q`EU<(60|G@qi^7_y?7w{nXC*$Fd|BvbwKmmYdgFQk3 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm deleted file mode 100644 index 451c80a26af36c0bfe1cdbfa307f6e6cbb98b292..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 646 zcmV;10(t!b0D=OlfICzG;QxpGKjHrm_%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9hgxfsM+m>fXw~f#LNB$6cx5CFg?0rHaBWj;#3R%jnca6D@B-!7-gtkq8g=fEHq zx5|D+_=m=;Kj0WfvJYVSz<+`P)d%Yat{4SNN)JgmeE-lec?1niA#9RT5*kRE6sgsNZ4&fkx|Db*V`Rb)r4n_V2fRXrT@Xy3%g^JjR g#+@+076y6$f&4G!^`LMs;6d%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRaqeZ4{R+MdKdPyqS^%UjA#h*vf#{r`Xtt6jj=d$0~X2CD#S2mtk)E(`czAMpIw>?$FE ze~06$e2Zj20tFqGsto|X5e4iuR>*(~w_B>f%jfFe%D;i(^#{jYswAGSdu5*a1EVz$ zjaRByI;@{dFY*V*ULOzuz$O9mlG$ZGO21ZUA4qUKYV+SNr^&3j(cAjY~=oNjQA}*YJ4hH>~872g>^m|H0rEO(;D><;KIn zc{Nf2>b$(2U?ti@E;pjk})1o4njyEIeaFmTbjZ@@Ed?UWbjb=N5PVO=IkGrcvsyOfgU##zrwF2aK0R ziq%_XbSOkR1rW#)P2^;0Jp2pz&f0slb!0rN$TLy>=hU?hGS{4?%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRaqeZ4{R+MdKdPyqS^%UjA#h*vf#{r`Xtt6ji|i;n@Sz#4)8J!Z>;s*k`w;rXxFTgCn# zj;Zo3kpKu3c1_~G5e4iuR>*(~w_6AKa@I`*nv(c0QLKy%H2rJ62u|N9}1LNONv3}&5%)XF_o$%ZNen8?N?!10pkv0AHa zu7wDPK%yCF$ABI%U3M6BKB{En0Ry;5AV26IfIeuksB$mxECi3kKZbrIG%QxcJ~Ziu d0I)O9{14%OFRcTCe*zDJem)rg`2L|(02rJOIJW=* diff --git a/assets/resources/dolphin/L1_Read_books_128x64/meta.txt b/assets/resources/dolphin/L1_Read_books_128x64/meta.txt deleted file mode 100644 index 7432507c..00000000 --- a/assets/resources/dolphin/L1_Read_books_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 13 -Active frames: 2 -Frames order: 0 1 0 2 3 3 4 0 1 5 6 1 1 7 8 -Active cycles: 2 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 1 - -Slot: 0 -X: 5 -Y: 28 -Text: Predictable twist -AlignH: Right -AlignV: Bottom -StartFrame: 14 -EndFrame: 16 diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_0.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_0.bm deleted file mode 100644 index a278e3a9d693124ef13784e913883e49b5c1e994..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 663 zcmV;I0%-jK0FwfMU_apf@PFW7LAU`#0MHM{0uB!zWPn=m3FHDD0I*P`Fc0vUzYy?v z!@tn@2Lgj3z(8Su@q!62UqK0wYO^1xgqQlG|x3FPo#@?i5q4L?O>f=ppCD2)?I zb_bj?!J_&=uR!sIN;H{fDcfQ?PoxI;4;Veb9^d`{(&7&;J;WYggowR_9yJhoQNiP$ zfO#L|{twQ60rGc@3IIQh{2!zbGWfvn!10GpG?9q-e7;Z}2-Iib@sCdd<30zJI|U&0 zkcJ5){zvjr%fLKbV?>a0ary9g&LP0@xOk!Shy@}+lE>5FKv98WVcrkWykHoK1dAUh z2c#Rp1cNXJA19(?A}}0;hlPaWcpy+n1;Pl!~zK*X^uP|KZtldZUK>S3@C`x)A3%I)5G({}5=Ei{mcTwbXka~Ifb;MZ z#xoJXMnUFK5GnuT@R*hY5(zE8k$iMl zV0ptV8ZV>|{0|s(<3*NYowg&S`ao}h@q_#{AoU&((&GdNj~*kS2an+*FJT9bL>^Rd zc;}!5k#&fEbMOz7ykJlP{Ab|(AbFR@1Yamod}9&t`Fx-{8zd0^Q4{#aC&2QDV5AeD z!hC$51o+1<0P%5+5OfU!WpgkJ2< zfSxg!jsh|dGJt_k{~v_Jun>?*Z~^K77%;q1A_gD8JW`|pKzZOl;PHchzzmrr0KrmY z0uQh}?+``^!{iX7b rwNoj#XR&PAKo{o(Rt~#VA7FaQ2ksAPBsjjn_K_#v0SE`41HzwhY5ozm diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_10.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_10.bm deleted file mode 100644 index 7684384fdfb09f1dc5e2dde7b898fa11630b8d79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 629 zcmV-*0*d_s0C56nmLGlK12bKciBJ}_K0P+4{@?i5q4L?QbgUn$vDD@Lb zb_bj?!J_;???CZ~N;H{h2f!dU*p8Fo0lq?bPDR|M&0-*KRNgZ&|WYo0RA)Zev}*@Mfi+I{!sV_$^)T4ykdR&2aG!fAWyt< z@dk!C#>pUGymH}ifKDJ3i35G%-UwpEu@VRS!Mp{*U<^J_ME*fwF#rhRINkv9d?6Ez zL+&#AMkjO1OU)L$$zM!`3Ifihz}TagTdp+1$^CSV+fPOF#cm;xmA%K5^#rTJV#(4mS zKsXc`4gvxO7z7$%4;Xj)J|!9)hQR=s0fqy>2ag^Da0CpxLlFN^;1mIP;BXT#v0x{X z7(@pN0FXf<7%T)dY*YxuhY9?Iz+wRL!f+r!@q|z!5*Qc$4+4Y(#}UAQgT^61hDhK> z@duBh7l`m73PC`@$f7^W3=#?OgX9e202&4Y;Ubj&G!RcGg9nobqynF!vOy*=n3P6| zq_Zs!8DP0>@-h%iQ7?0o{KFBCMi{k^o1I8UV(q_Wk})3# zm&*en^pJ)LB>qS8QHl5Z7l4P*q#T@nd>%81c1k&TgF`M53M7LikEg(?5(pM1IdF3c zz;O}@7Cuf7NOyuTfG9+siHwNAauOaE6OG`4FbW?y#6j`!2?2a?Qg0csSVIVqL>{C8 z7zPF{4+8-f^l%}*2sk`JV~qdL;L{8Qd-1^dXW%f&1Oxq#2an<( zfH#i<04o@fJYX2F{r&*~2c&WVAqSQbVmttO42A+w5PA9o<3ND}#Bxv$1q2>j0Ql*l zm+lNhU-HKY)0T1OU)L zc;G+uaQuVF;51Np!=xS$9zP$)&%lA@c#;FgBmemQ`~ClZ{sJjDrUOB)kX)|)zQwzD t06(PzL8{eErrn;!vtw9d@Wb!1W)%J!A#=2f6*=5P*5a6Y#Yx0w4eY diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_2.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_2.bm deleted file mode 100644 index f5dbbc71c2ddd18c7644ca48385fa4066c1f6daa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 628 zcmV-)0*n0t0B{0;U_apf@PFW7LAU`#0MHM{0uB!zWPn=m3FHDD0I*P`Fc0upzYy?v z!@tn@2Lgj3z(8Su@qnmLGlK12bKciBJ}_K0P+4{@?i5q4L?QbgUn$vDD@Lb zb_bj?!J_;???CZ~N;H{fDcfQ?PlN{f3E@HR0QUdy;1>{hz_^zXc?w1V_7Hf~LFGgT zj(P#)bPxDHIrs<3-Y_Tt{xk4?kUY!r7>|d`oO7;G?R0K??;PvjN@5CD!7jo^VnAQuQk;}HSEKqL@o zh6+vNUz9o#Ac%jca0& z5C|lJra16;i^Lu`0LZuo1P>SnEB}9hKtbX*AMh+-Ao9XJ1yCUIh!@L%Y7ij$1LHt} zU?V`1Ef5s80rAsA0qYD0pMah*n2rK64>Ev(PyZi;#IO*MNpJz;g&+Yzh2n`2FujBl zFff8BgdSK2P@u>8{{SjvkOKu+i;zR=4?DyWVlenegU64@@$>4F4h$Os5&!&t{r>;I zeyKPHG0-~bMau8n>|3{Il)6AP8m&~yZQ1NwHc$omlLuk0)d$!fvVr@9+DQ&Cusx(_ O!62UqK0wYO^1xgqQlG|x3FPo#@?i5q4L?O>f=ppCD2)?I zb_bj?!J_&=uR!sIN;H{h2f)HM*p8Fw0lovq4{!&RG7JwN{{QK52bUh=41ZZ#DDn*fPA1jA0!@r4;b|D9y9Os zJ^>EFNIfJWf=NG-{FHL>28KAsi6G?T^WgEELxJOPYYgH6NRVW)^!N}IU|5)QfZ!O3 z1dAUh2c#GtcnEx+iHwNAauOaE6OG_*i~`^?97G=<0FW2Q1t#%>${h%hL?9FZ2|zG0 zZzKW&Ul4fMAQJ(wf=2-!{6HYn$N)S+V~qdL;L{8N{9p?Lf=C);j|Y$99uFIXK>_0c z#eeVPp$CZAKm`E@mJwnkfd`3TKm))v2oQY%@t{Dk5uiyHhzeT(`01g5^@an_z)u*= zM*$fJoE#o-5Bz=;62L-1CBO%$0AE9ZM2HxF0P#wQ0YT@0|AWR2{{S*%lS2hoj0ir! z^SnVBqYsdHJb3(nA3muy@WwC^AOFYi-|zeP>XSc=AAzorT(14T#k+QCPox7us?|)U o-JZp>WdL8C5m-9yP$qK1KLENcmyCGcn=DFz&rO3ng9R* diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_4.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_4.bm deleted file mode 100644 index e0db66ffe303381a4cee87351807509ea9dfe0b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 662 zcmV;H0%`pL0FnZLU_apf@PFW7LAU`#0MHM{0uB!zWPn=m3FHDD0I*P`Fc0upzYy?v z!@tn@2Lgj3z(8Su@q8k$iMl zV0ptV8ZV>|{0|s(<3*N$d<-LPi0M9%8{j-({|yK{VUS>Wj|b^-f&<5o5zqs~08e2D zjYJ+)aCql|=l&1Qd>(Kp0RA)Zf5QYp;$IjMe4$10j7R^Fcn8V@p|U{_iwf&=_tj1%Dx2aUo72%JD0CH`-WBhdrI!<+{Ie2feJ z{}@CQ0f>VG&jAmU!9TGW3_=M&!g0I}(STeALkNK5AQA{9!v!YsgUTHUkVHS!I0Xao z55UE|kO&EULE~V6Oa{UU90Ysu0E15;0PzKmGygw>OfU!WfGi3LAZd;~9wPAvjlrOR z@ql8#_wmq!#B3k}fP>2j^b$aW#vl$Z0jNNO=nsto1%QnLNVGsw*ayc=3<>G{5-|XW!aN>4em{?&RG4sJ*a(mRnmLGlK12bKciBJ}_K0P+4{@?i5q4L?QbgUn$vDD@Lb zb_bj?!J_;???CZ~N;H{fDcfQ?PlN{f3E@HR0QUdy;1>{hz_^zXc?w1V_7Hf~LFGgT zj(P#)bPxDHIrs<3-Y_Tt{xk4?kUY!r7>|d`oO7;G?R0K??;PvjN@5CD!7jo^VnAQuQk;}HSEKqL@o zh6+vNUz9o#Ac%jca0& z5C|lJra16;i^Lu`0LZuo1P>SnEB}9hKtbX*AMh+-Ao9XJ1yCUIh!@L%Y7ij$1LHt} zU?V`1Ef5s80rAsA0qYD0pMah*n2rK64>Ev(PyZi;#IO*MNpJz`02na5Q6dHxuz-Xf zilPUW0n{Op|9}}XNCASR#snW=dEOw55r@b;9z1?OkDpYU_+uCdkN@NM@Av)t^+}(` z55U(*E?0ivV%@tmr_uqS)oP|wZqH)bvVbqn2&^4;s6N2;ln>k<(nxWAf$btsyaEsp IJO_n7;4e4@;Q#;t diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_6.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_6.bm deleted file mode 100644 index b2676b7ce2fc232e1fcfb5069feb32a5271de2bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 664 zcmV;J0%!dJ0F(lNU_apf@PFW7LAU`#0MHM{0uB!zWPn=m3FHDD0I*P`Fc0upzYy?v z!@tn@2Lgj3z(8Su@q8k$iMl zV0ptV8ZV>|{0|s(<3*N$d;$Y)i0M9%8{j-({|yK{U^Qt zJZd2Fql3phHb^1-q9^f; zPruN(1Um&FocQ4dLVe1cY+wP zC&<9R@9~5|Fc^q8fVeCHhsofd*o+1t1fXF!-T?7@Arpv12N?j6K_(a}H;f)r_(Xys z{-MAqABcViE#%-p2tp4V1Oi|-7!Lyh9{g}1amW}vL1T>n&*0Mx0sMF$&Kv?6AZd;~ z9wPAvjlmisk%1$^CSV+fPOF#cm;xmA%K5?!V4I9JZF#y zWCMXgkl-L7VSquV0P%-^qvBDa$ZQY^fEZvr0C@4>HvmA(q%jZm4go+Hjt2oV3l;)- zA;ds%kO>4DA%eg|M#VskNNAtPECwI}947(<4;V!PAt8}};P5O&0wIXtKtbaWphF~i zBrw3K9td6|z=SCU0|z4H|KKo5C*lu~Gl&3a7z>1p)Bo@T$N7WFgVF&{(Rv{B7)**i zMABK7hYYZ2zYsgnJYmv}CD^Q?d4LEWVfN3x1_uC9i2ecN?1F>HzZr->`VtQ<3H{>{@OgZ&G6eg^C*SB^ z0v|$2HjY!(p0BoPnw4emdPeg-Y%z$eCh4;usmU?vz30|6HFa3Q`3I6OgPjQ`Kz z(+mWA@xb_J;4sMq1O1N&h`ewH@!&uOV-g390~PjzAq@Vps@BB;erm01K!= z>A=I_0qV&l$bkp&4-w#i8VC;@2mX#9ka-*iiVqlcgTdp+_2g)d$!fqxc7`fWH9u MKfD4E4>*E;0O;od%>V!Z diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_8.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_8.bm deleted file mode 100644 index 05f98d639bbddc8653ccabef2808aeb876a4b0c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 663 zcmV;I0%-jK0FwfMU_apf@PFW7LAU`#0MHM{0uB!zWPn=m3FHDD0I*P`Fc0vUzYy?v z!@tn@2Lgj3z(8Su@q!62UqK0wYO^1xgqQlG|x3FPo#@?i5q4L?O>f=ppCD2)?I zb_bj?!J_&=uR!sIN;H{h2f!dU*p8Fw0lovq4{!&R0RlGP{{QK52bUh=4>%+N>>%-| zgUXH%9P^N2F0l{JegW(kj0ylhjQk({1qYFQV0Ykn!>1ZZ#DDxRACw0}%81a6E1o2n6B*NRVW)^!N}IU|5)U zf*7$ZM1n<+lY`O?;4TXQVe)z=G9v-VNO)LIH-J1}2t?x%2gkr91@Xa2ykPR5!Xyz0 z1poq23=CVzz<>~h9ySOBz-%xc1_C|!;6US$FnEH;8ULTbrWgbG@IRb51TsL=9C$o` z5b${15uzy=kUU@*ul@c30SAbPU}zDLgUbl95YU6fus`S@03LpT_|PC&2+$;pLqP(T zz&<)?U_D`g^Y9bKGZDZ>LFWg+flvP*gv78AkV$X>>Hrte;1MDQAI2X54_3(o%K`rf zi10uS1Pqt@iXV`9-Y9_ahe$jgJbpiqpMeHWNDml~|Ks=X_x=0$2t4AL4Ft9+V9Rt5q_ac6%1hlmUJq0fGa^U8)bTJxA~lSpj|l?tgd$ARci9`~W@N5<~z1 diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_9.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_9.bm deleted file mode 100644 index 65b723203c3cba419975bb816eceab751c8021f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 661 zcmV;G0&4vM0FeR!d;1$^CSV+fPOF#cm;xmA%K5?!V4I9JZF#y zWCMXgkl-L7VSquV0P%-^qvBDa$ZQY^fEZvr0C@4>HvmA(q%jBu00K}94gzKtECljH zh=b$c5(xD31%QT)ih&rA(J_%23_?Rf!f+r!@q|zTBO=Gi;8=+U3lYG8gT^61hDh>Q z`g{tJAc10h2ttrhFmf(VKR*PLP9fPNIDiI$fVfD#KmGVTf06Kdz$yAKBp-hWi5JI3 zW?CFF!J_&=|G@EwPBd3wdD|j7PoxI;4;VkgLzaMi3?pqG57OfV2ag^jpa+aH4FG!x zJZd2Fql3%d7R?fd}vp5#WFt z2oD?w{*E7zc^n3c4;XZV!Q;o{`1$w{Jg*W!c*KAIAHRRU@87^hCltVFHPQ=}-?!Me vZr}&>plCH(sg&EZ*tTq-5de8X0pqUK2iP8?_y??jzX10?yaEspID&ovpRo_7 diff --git a/assets/resources/dolphin/L1_Recording_128x51/meta.txt b/assets/resources/dolphin/L1_Recording_128x51/meta.txt deleted file mode 100644 index de37d5b2..00000000 --- a/assets/resources/dolphin/L1_Recording_128x51/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 51 -Passive frames: 6 -Active frames: 6 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_Sleep_128x64/frame_0.bm b/assets/resources/dolphin/L1_Sleep_128x64/frame_0.bm deleted file mode 100644 index 9560e1f41551cbb914ece9b331178782165c8e38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 580 zcmV-K0=xYI06+r&fII=<4*+-rz#ahb2Y@^Q;12+J1Hcat2lW5^46XPGZ~wslgXVt# z6&OALfBrxJ@(+LLMDH*UIX%JY4uEG3?SJ?I4hI0Z5}ZDJ_zVMpTX1>z;1CP}P(TxJ z2aE&k52?63xq$kMgUWEUU;)O#<=~D$3PT?UtTY~24p^N(^}X!1MtZ;9#?Gc=!Q>KmiVO=5A1U+yR3i0S%9w4@g)%OM?KI z9ESlpKPWt7!10I-OUyt>Fg!p77#NmdfRG@1LIflJ|MSzw0|f{Gp^y%Q2Z20K90VSM zL7Wsv!ayzfsDK2-5(vOB-?ecTh_+BD1@MSoV0hQhq971>z~C4_5X?J^;($nClnVp_ zoG?O=&?W%!m*6N6Dg=W74;T`NyeBaSjsgZ3902fOTnq>Q2mNmdC}1)Oz&!95@|c(g zE)EzBVmSUJaGb|6_Xm~)_%P@3KuqNz_dkXKf+h?cz%UOC1fVny!}wqnD*;4XgMf}9 zO98Nd3<8N5@DxN8DB(I4hyMi!j1u6P5Miwgh5nAfF%U`f45$UsdKMG=H-Ka?aQ5L) z8{`!m$L)SH5Af($WMU<-<^JyRpm+fz4g!e)s2Ax6+kXMTOhMu?69|w8;Q^p~FYs_- zZ~%f25&*-)VvrF~!h-@2I|t#WT@U>30X=|!!Qz1tAweJ;AOF4q>i?KX)-&P&v_{4M S-@)pYUZ^~NANU9~LW4vded_Z7 diff --git a/assets/resources/dolphin/L1_Sleep_128x64/frame_1.bm b/assets/resources/dolphin/L1_Sleep_128x64/frame_1.bm deleted file mode 100644 index 238b50a2e7b89ec4d08249e43b1fc9ed16fff178..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 589 zcmV-T0fII=<4*+-rz#ahb2Y@^Q;12+J1Hcat244IGGym{Dqu?RG{{#9D zf7qm80;31N$N$Iw{z3E~8_WaF2O@fZLMIM@XAST86dVo#a4Wcc_wX180M~u^1Oos> z5Cq%7;{f{u>TVA%U_PSY@|*+eHV-cZasW~lp#TmV4=e|d|HB5a-v0&%lmdJK6Ps_~ z;PZ|^`~o3AED;dI3AF& zc$WqNFgXqaa(+;F$ARMz7?+rUkYIR#3NSG&!2uvZ^n?gR)I9KUU*KS&02DF-(17qK ziQ|BS&`2|ai1IQh5E5L4gOIfNm`@=zr&M3G4&@4-^QA3JCz%|NZa}SO5HB b6IjoP0?`{6|9=OnReGTD_TVA% zU_PSY@|-Og0CBhk`T^jMKng=22A|*m4S-Ne1LObj!|UU}!LS$sfQW{Cb6>vx4?ut* zMmYoU2!sGjLGQsR5x{u-07D*XExaC_0g(O%1|J!?JqBXbADOv9;y)<>JZya6dP3lZ z^Ozim0XYYxHVA(x1VCb5V~Rn6;(QncU}9N<0ziS{04P9&OGD2%oD38o0){|35FP~a zsBjQ^2?lUc9|-`r;-UZ)7=lm?H|!og^3fK`1pvMg3(OB1`E*1A4;UN+2m%?0a2_`J z1cY`r1qne4LqM1V#$SM-3dTWz2aE|sUK5yu#{mP7Oco^q;9x)aKkIlwLjjCN0q20f zl*GU>aB#q2k;m~PgyuPqxIC~W!G}MJ0%s`)x%@B;5U^n80f2a5B>|vzAHx8lSPCRt z7~&ML8wc>fD3K2Y1RhA?Iu(ci1qX~G;F%C%tqX={O$ogfPcZ_fe|4=AR8b5z5(k0m`K($;sCTp U#sA;I>XlxoJboYe2sA>2M3T4c#sB~S diff --git a/assets/resources/dolphin/L1_Sleep_128x64/frame_3.bm b/assets/resources/dolphin/L1_Sleep_128x64/frame_3.bm deleted file mode 100644 index 72fedc5e13cf89e0b10e68e27ddbb31f86f17de5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 597 zcmV-b0;>H108s+}fII=<4*+-rz#ahb2Y@^Q;12+J1Hcat244IGGym{Dqu?RG{{#9D zf7qm80;31N$N$Iw{z32k51r-#=LHcx0ptJ}bOShVYyZFqa5x3PuHp0Fz+fB$TZ7NP z0Dxc!f&iO%JYXMSeNDmT%m>t59#e%Q01h_*pFlhj$N@-Lga9~f0)kKA)Ee;9#?GdJM&=KQnTJ z#C}o$c-Z;C^o79-=P)@A0&;#>A^e~b0f~8t2?hs=@L&;viDn210tbiyp#l*#4?G-K z_!uZa1q^_6AUp};c;F!P5)9y?J`w?M#Y6xoF$AC(Z`!zvL|Z5n0{BEPFg$DJ(GUnc zU~mi|2xc9?c-!C-5!l!iB?KuA0$>jregc3i83q6zFeMRqPGS!n1P(zkSdFa>1~C{1o&x?;69C1*!vTgzAHtXS|eir@8I=HuT&mC5BvlgAwi-5Z;a;$ diff --git a/assets/resources/dolphin/L1_Sleep_128x64/meta.txt b/assets/resources/dolphin/L1_Sleep_128x64/meta.txt deleted file mode 100644 index ffd845e8..00000000 --- a/assets/resources/dolphin/L1_Sleep_128x64/meta.txt +++ /dev/null @@ -1,41 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 2 -Active frames: 4 -Frames order: 0 1 2 3 2 3 -Active cycles: 2 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 2 - -Slot: 0 -X: 53 -Y: 20 -Text: In a lucid dream,\nI could walk... -AlignH: Left -AlignV: Bottom -StartFrame: 3 -EndFrame: 9 - -Slot: 1 -X: 53 -Y: 20 -Text: OH MY GOD! -AlignH: Left -AlignV: Bottom -StartFrame: 3 -EndFrame: 5 - -Slot: 1 -X: 53 -Y: 31 -Text: Just a dream... -AlignH: Left -AlignV: Bottom -StartFrame: 6 -EndFrame: 9 diff --git a/assets/resources/dolphin/L1_Waves_128x50/frame_0.bm b/assets/resources/dolphin/L1_Waves_128x50/frame_0.bm deleted file mode 100644 index aa7454666240b57190099b0e7bee4a1c218982cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 443 zcmV;s0Yv@*0Ji}EKZDN$0uSIHI1l_hC_yp+Kl(TXAbno|W$=T8=l=m8m;8S@@YzQ{ z;|Rb;N#%r(j3a>=gaHbAFNy?V2y=iTh|nIN3xE)^1%dz&JnkF-$%E8XcZ>i(fI>Vd z5Iw(>@OfT==l=hI7xD}qsDNN!`ltZ@uzv!$wNvmY>VOIQ1`hro;Hv-Y;lrv`YALe5 zeQW-p>Eg9gpdi1?-~|!V01Nw9XO{x6RUN1Rf7eYZ_2fsd!ydOzcpaw8M zz&!VwudVz(omdZnYYP17oUI#WkCQB3=l9tQ9sTB z_zw-&fPEOiJl|;86}W>&zzHHE01m(n8~{AW<_|#0-wKKkNCXH7Kj4_H2mqb}@t+C| z4`RT=0)!nHa6O3wFai)fVSx4=4FDcUK?j$D{tq~DfWhe;z(>L593>i%&_u;71nvM( zAbH#ah5Uiu0K!lrc+Lr{{DYtcwm1k-J=-Y*pv6RnfN>y)$a-J}C5!@q9}s+;1b%-2 le1E_Khb)7A|KLM6j6fQf$OQZjhS3ME06J$wkB{VRvG5NIyQu&G diff --git a/assets/resources/dolphin/L1_Waves_128x50/frame_1.bm b/assets/resources/dolphin/L1_Waves_128x50/frame_1.bm deleted file mode 100644 index a23d250b969fd3914a84d4ad0b4e0594ed714ae5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 448 zcmV;x0YCl$0K5SJKZDN$0uSIHI1l_hC_yp+Kl(TXAbno|W$=T8=l=m8m;8S@@YzQ{ z;|Rb;N#%r(j3a>=gaHbAFNy?V2y=iTh|nIN3xE)^1%dz&JnkF-$%E8XcZ>i&|Iz!v z7)ONy2d@MF{|A-m9)I9afC>lj{euUpAQ%_^ssKCdjDh2-pZGuepaMsMgT9COD!=;p zaO#y>ioCBMTK}jzc&${c{i`gOgIB5@AOOGpcF}Mu^-NYQ0RcN*?e&e_#Fnt92cSJnt0{@k4=QueBotUVi}i(R!tVJP0&c$6z0m1cU?c zK8!F9UIF`e qCxBGs1LOJs|C)vY2p>QFFjxrqJ-!eh;n9Qb{#z^p|A-&J*>B)^bG`)t diff --git a/assets/resources/dolphin/L1_Waves_128x50/frame_2.bm b/assets/resources/dolphin/L1_Waves_128x50/frame_2.bm deleted file mode 100644 index cd39b17ec8969063183447fd5463a0ae27cf0fc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 463 zcmV;=0Wkgn0LuXYKZDN$0uSIHI1l_hC_yp+Kl(TXAbno|W$=T8=l=m8m;8S@@YzQ{ z;|Rb;N#%r(j3a>=gaHbAFNy?V2y=iTh|nIN3xE)^1%dz&JnkF-$%E8XcZ>i&|B(B@ z7)ONy2de~c;PSl#&;9=Z91r371`kv~FfaX70C(3J1IJZA@PG9{0s;(p7(3{Hf~)_p zhYzX4sjJHI^{@Jar;62jpc>-IcmYB>Kmj_%v|IoF&)nla2hZT(ZsAHG0OJS|F_1J; zh7Uq0g9qRZ5PQr90ty}gKCBu9An*V~1_!l+0YQh8L>^oMIRFUIIB4)Z;gg5~;6Mau z7{d^H0P-5700b~U1^NM?CX8_waDRXhVaL&cR1QK9Fo2ST5MK-qf&_;ZgWLc^eyBlU z1mOTTfCfSG2gd=DQ~>z?0q%Ps0rCOvUJwBY#t;sF@r;Th4`h(Kh-h#y`yfmJf5=Gw F2F$Z&xby%3 diff --git a/assets/resources/dolphin/L1_Waves_128x50/frame_3.bm b/assets/resources/dolphin/L1_Waves_128x50/frame_3.bm deleted file mode 100644 index 2d5452d7c931fbcc2cb933335456f4f647e40ed1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 472 zcmV;}0Vn=gaHbAFNy?V2y=iTh|nHi$bZ7%2`qtNfCU&xjsD@l446GH(}dvs zFE{{vaOZb}7~jC+JSY*k;4|fUUw}h;2cP@?m>s}Yx~e#<`w ztE$Z+sDKB0%MeL$Dzl)ehzbsX7zFqpAaQmb@~Ryl@OVN)fU1LxEGW@{e*xqIAJ{zr z2l!u)zyvQs2e{;1MAR?={4f6ic>sj^AO-9Ygj6^He8M1l2>3kVfcKtI0prjIzxw|O z;AqjKpeBrP{E!>~aDRy*s2{{0lfV$p5@mtVK%fzTNnj3zU<(!cp#=tsOomDXAo+d< zq5x2_&;#T6S2+xT2gnD%fM95H2f0we_W*|j_u>Bl2*`o&{%(o@fsFSbfb|Gv1PCYK OI$#3+K!3#e8!QB;G|RjDQco7?di^51Rl;u)Gj_%m726q2vr=0C>O%Y7!X&24R8n zh)|%w_<{$@5kvxz0fQxIgkl7w0B8|NsF;XAJ_#fLl;I)@34o*qEd+qX!Sf*hMj;#& zDFfXWF*roRK%kmL0!a{t1L{e9q$h?!{DXtuDgUUHhd~E0#!LT`0uSM?UKtQA-7L0R1hYVN712hFV6ap|Gd#F&xVj&O%T7YCsC@?+hkO&~K){|ERc09Xc%RjDQco7?di^51Rl;u)Gj_%m726q2vr=0C>O%Y7!X&24R8n zh)|%w_<{$@5kvxz0fQxIgkl7w0B8|NsF;XAJ_#fLl;I)@34o*qEd+qX!Sf*hMj;#- zhvFU&bXdgU6A1)8g%pSck|7N6Kvv`cStat2o)xIj0q+$5)Jj5s8V7>@4_uf3CUraT zJ@H@t8^mB33H#*02g>>B-i6?ggY0}i1Nhtl?f=u@2gCe>zz{4CAjyA%LmmgftZ*Ol z2&`Kd|CfOWfNUQCgg^&?$xq9oP8t32KJijWfCur9ql23PCV+uU{r_LbGzf9{;63me zpdoND@G$&cKxN>#t6Ts!pc!5v&W~N`v`S)hAOpq;!UQk`SUd+J7>EKe0pf8G f13UojemDXL)Ive`i@Ey%2ET*(n*auOz(3#s%S5EX diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_10.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_10.bm deleted file mode 100644 index c5312e5e5a820aa4379d24bfda9c4e3a6c296f8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 465 zcmV;?0WSUl0L=ma|KJ!1JOBb92k?KvV|ai<1QYz9hz1J~2hIcX0~p1CgIV|t0%5=@ zSun5w`-VUukO3HgE-(-8gboCi*f7LHlA#oMki-Dc9wdEcFgU`3ATxj=M-Bu3hlAJe z1_Bj>;eQ(miEr(rzy}T-4@vmuCID3D#ndPOCK}D<0GEJ3z=sR83SEjo^TZX!IK-3> ziNINn5C^BCGGK6e{VeAfmnYNr-w(X}2yp-9oF4V?A^-pJuzS}*>FzTkF3V*x;2tB9J0MLWRxxj&o+Jp}~{F*?nWid2Jyde1>pZ*FDh=qV2UziX+ zED^wYUSK5g2etzS1TFe?zy&~B9{?XR02xcc=YWHh6b4apN=5XjI9KX?h!Y8XA|2#F`05eA_3Di@J)Nb`;Ym%t`9dA(X8 z2xA!h!RrWO7=TX<4>Yid1Rr#W1wpVVVUYvoKo7<-s>ojD@ogA9WF`fO0g{*xBzr)@ zLIoR`JYp*P;9np(2!R2B2Oz(L#EyUig8+xIdw|dZ_vE;P#t1?X1b+8pA;ANu0sP;Y H1;juD{u;iw diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_11.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_11.bm deleted file mode 100644 index c91ed2fd23efc317a9fec7f9244f5ecf26bfdaf6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 698 zcmV;r0!94+0JZ}EfFeKmfrIjX59|Np9uLp{LL3k1U_W#*kbcNuF#vwR<^cS;@vIO) zd(Z#CF}ve9B@qX-JRV&5#tfn=$M69PXF`dC-_IZZ0Sk>X2aoI?k-rCw2tNFDh(7RG z1aJsF{OCR-a1Q{$x3&;_+0i069sK5s4--KE3qB7Yqr`7u@&FI;)>rrp1JI!=ED16{ zi|2eOP$&mL2sL^*JbV9r{(!)9X&8`x@E~~0{Q&eFj|1S45O~kx-2i$rMgb51`=XFY z{7@c~_&7X%AB}K5FZveQ1B3uPee_r!fBx`1X5jOjOTpql6S(+u|M*Be_!7x95WXpK zPLKW!4>$%49$ov|$bZxR2|R!U30bH0tOFAfIQ$(2*5D`?RtQVL>}ORfY^cfK@wo4#6MrnKs|tmU<0WTLH;XP z27sI(^o9V%@FvkNl$ajh@M$c>{x_Jwy*v;P#4Z6m2#gaC7YC$sfA@mGI8b;bBL5Hv zl>>p}ulg3lJ^Tm<|BIl5$ALfoSM3lU2l@jagnjXN6H#zK_4KgJKlR@P9)Acv7K8uq zuhJkK&-_qkJ@}dkx*z}k{!f7M-}}L#m&pO;={q_P6AHB35AhFN`%)TJ2HpaDrD0~`qk(=2CEZ z0f)!)C5Sy^VDfDd6n=P6c!CNF4@>zJeV5sIJm5g`(0}*gLcxQ{tS}II8iWXe@eB|E zXaM&oh#-OHDi9%W;4^FixNpGtzyG;6_}9XI3LFReE(ABI%&-txX8^(=_6Q6zF#`F| z%pwXHSUm6OQH)?em-v`H|E9r!(1@c42fM^ULUj;%Vgc))|K1Fy>Kb?a9=!ZL{EAT6 z1w0;~ugGj@5C#axa1?NQe1YPw7#HjRdiZJo@l^SIcmMtl`T!3dm;X)wiUZI8-@skq zAn}sVy5IRje7^$Wz<=Pt@uKiS==&f4;{o%GgT|Nt_kZdD@qLIqwsLTI)$jj*{(v4o zPrv8!!7vCsb@%_@{(}R60SDkA5P1JeqyPQ^#q2GC2cG?kAOw2~a6#jN_us+G|A+7a f<$u2eTmSC}k$89X5PN_N?%5stv_aF0B#1shQS}MQ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_13.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_13.bm deleted file mode 100644 index aa5353e988d02785f45759a024931abe7d580086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 584 zcmV-O0=NAE07L@+fII=<4*+-rz#ahb2Y@^IANT|F;Rk?x!S}#`z&(Kbz(C*~0DZs# zKZbz<@kP z-_ZGk-W5OxmVe$YUN}5vRseX=FaHn!_#S((c=Kc+^8fh5iJ!HD#(O>ojswBL|KI=9 zyXAl$Ey0L`%fG>Z-}v0Uoqw17zfWUvCftXMrUwwc* zFdOIv=nzr}54)K^iv!95wSa!G^7S~tJCHd1P#$^^c)jswmy;2(?+ml*DY%6Tpaje?1R4~z?+C+R@JB71OJ%^j$i~g#Q}fdP!0e4@$U+bI{^s# z3(Nlv;0TA07lYt1>XG_?f57p%{4ekc#4b7D#lRpxNFUrDAR+mp#tHC`{fFbU28aM| z{|{g!U}Yi1z%rjedF|ox@OZ&E5DPI#e_na`JbB{LzW^w(J#=94pcIrmEe`+y diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_14.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_14.bm deleted file mode 100644 index 837c6c71defd8be280776e7881817fc68276b5d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 610 zcmV-o0-gN<0A2$B|KVUk{Q*P)Fi?2?0rvt2kDwoLFnIg{_W=j^KOjHgdBMT)0R98a zDj@jq1I#KQ_}~M|;Q;Id;{Xp%fIlVx`hWxc9=`B>kL(_GhKz#+o><3>gVG5B{Tt>8 z4;U5;!}CwXJVamY_&z)lfe}FLAMe0Dt_H!Nasoqr`-9r~&_@VJU>l>0Krj6W95O)w zgJlpe;FN#haR>y*&>wsRUrhppDF-nZ{sYsG|A@(;VDX@2Js+@oPGA%6%nc+tE0kSi zuxJH>Lj(tm3qcDYD|B!X2tYh=AoBP#)&UWmL9h@2c-lSS_KA8RbO;895vb?`+9^1J zKqL|g{UbmRX+$ywAO;Ttt3U_0LJkiYg@OTE2b}yKQ3wz`aF!;BgU_B1DFg(5A@ZVV zdEoJzgVn$w5#o2S;PHin%RrqGp+G;L5P3mC<-mYJ58@9VCwyLBV0pmdiSdjD2SN@Q z8NBEa$N2-wgZ=&g0qGDH4O%!v8W;Zm|NDc@SRg@@1ImB)|6{@DKq!m?&N3Yo|NZ%| zgUlq?4N0o0xY&yEj|qzoQt^FED>06iEa;GRJCuV4t{^!eXG#1C2(0)+vGdoW=5 z-AFJnC>}#T4`yhz^{KA$Q(_^4>({vevs<0996^* wNHeH?5Gg=DU|{j6KxP1O`UB1b0N_Ay5Dfv)f$A(Dg8+f^cz{o!766F@+?Nvri2wiq diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_15.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_15.bm deleted file mode 100644 index 0000a886313b8c265743efe166ccfd156492d830..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 740 zcmV2edpMFaQvP#()oKH^LrM60rl^I503^{{iLsHy|)^ z0P@TO$c~T*6avV`F91BQ2NQrnAQ7N>U;*j-2b3ZK1#_7gbPDB+UsoNaWG5dPb%aa%Ma{d z|DEDsz@&x(4~zyiF!OMK^XY*gSOo$Hj1CRJ{O`H{==f-1o&bS^#{qtz;s2(wI1VBV zR2T#eE<0=n8}}~2CKq!|)qA>9R0!Kjw;7H&PBItkNnc#WQA#nkC@B-!W2amiSaeM(VZUFM|55zur z3vdiz5rDaP0mKg|26UD%2yi^UB;p606xKKhI6S}qaC+Xa&=0^Es2%ZpVYol}I2f=O W5qn`E9|>4I?g03JC zA2bOE00<&62h@N_MMD3>!RtVd0SE*!_y@oqya2gi1HuI$kb}V0E5g61P?e66wYH9giI33j52{(AdrB`gVf+cal;Nwlp<4z0LlXagupw1_IjTN zArTdbFoEVNECQfnMiC(pJ>mfYBMcB;p<UWSb^&U9gP8?HUvQ}Q-S!Aj0Rwr5P2)idN68af)NZK2tD!;9ESo92@?RI zh6)clN5Vq^MF;@_GKB&s2`okA)ew0}AXG>YEJP^P6xl@}IEWEAK;n-a1iZRX z2$W|b1X>6jLFI4qM!wUzT z3Mdau03?0jVqgpk5>2tL|o-Vkkr_W=P; Xi41@f1_$-wIw13am}x#>`Gy0~KbO0F diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_17.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_17.bm deleted file mode 100644 index edccc7396ec68d5dd2a861017a681b2763c1a4c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 451 zcmV;!0X+Tz0KWkM6hI%*fPX>)+z)6lKcGPUz(7I#PwD_arU3pz{4gKTMmK;k_{2d3 zDe-;_GJ(K9|L_=t*8Igk0EA=6#lS<%$2b#|96X)?8DOCw>-+=J90#Cef#ZOI;2`rr zNu*W-zzdWr%n2f(z=7vr0gnR~(2t^UO zA(FI0F%QWy6vBy!gahH8Qk*0~K(B;I3|a{RiG$`r{)|F6C{hQyEMjnpgn_!C1EfGC zkqBTsfDa_TQWL`<{z1X-6#vvpL!g70V9{3ED{Pb`z z{6$QLp%MGwB+w8k2!*2@&>_PW@qo<%P6Ysrd;zFN8>kq@Vj&QFT7bk%C@{HXFbbps z2rLtU1Ka>0(GCd2hUf-62Y?DM5-X@w>^KMRlm@CSfA0hhpbq?iBy zfrHw>1U~>!W5l;g-C!L zl0W%Q5+I;30>DxO7J@)xVEK@LqY#b?6oKxG7@Q(uAZRcEsSpVyLKqJ)14%EGgz(5e zkZ^m&KlKuj=pg18$$#=-LHsq$0JyLIj0iCIxqugy^V7gQ1=t6u@PYIH)8HS)UkBB| z3OoV&8U!AM(Ju(SP^F9i%fST<2d;7ffXPqKM*|0_D59#990~of7zs24E{z!Gfesk2 zj0R{5a3};|KKD?ejKo492DJdlm{4VjjSi^*f(s2~KnDhha7H8miwA%hFA@Mo0zNrV r1;+vWK@bLD3)BEMcz`zG59&Zam1IO9e?TBI0DiqhA5j1hwgdnmDfpDF diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_2.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_2.bm deleted file mode 100644 index ade67d1016ab61cb52596c9cc607f61b30ea100a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 402 zcmV;D0d4*P0FD6wcmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 zgM;h+e}H-;fa$RjDQco7?di^51Rl;u)Gj_%m726q2vr=0C>O%Y7!X&24R8n zh)|%w_<{$@5k!K2A>j55m7)=dhC$9x0x1;}5y*hZ0qjX1{HF;JzCZ!*0Z0s52?33R z=0X09LO3Q-pM%{NF*roR1;7Wq0+9fcL@|&bgnSO2M@{~-Pg z`;~HE{G0+20RKS2?~4EE)P?_Wjes7!ub!O9I1l(BLGqFw1^f^<3jjTT`f_mi55+jR z1Km0&ija90cgzFaY_m wD@TC`afymcg5DYcK17NS<{sClcm>^jCiVOk07yZoxc~qF diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_3.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_3.bm deleted file mode 100644 index d05e8ae2b4715a5e6ec362ef63a96ac6b1ce0895..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 374 zcmV-+0g3(r0CE8Ucmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 zgM;h+e}H-;fa$RjDQco7?di^51Rl;u)Gj_%m726q2vr=0C>O%Y7!X&24R8n zh)|%w_<{$@5kvxz0fQxIgkl7w0B8|NsF;XAJ_#fLl;I)@34o*qEd+qX!Sf*hMj;#& zDFfXWF*roRK%kmL0!a{t1L{e9q$h?!{DXtuDgUUHhd~F9crWf%$$#=-LHr>E9{8{R zj0iCIxmW?~%K7Qw9sn@V0rs9Cc-#a+4^RG|0RAfc0&#E$xFEOz!H-S>?oAT#&^!?M z@I5hq`FIqefboIDfyaQ!PuYh9Cx`)}s*_NOaYN-MfPqLvEg0s34j2(2`(}Wr0)R#X z?{x|o%tRspYfub{g$4(`QUL@O8pwbS4G`drNC6fP05D!80E`5E0ALM{1NglICNEF` U+2R1(fIq1K{-6W=AK?E4z^3n-*Z=?k diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_4.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_4.bm deleted file mode 100644 index 5aef127622c6a77c35d1a33e111aca2f49d2cd7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 440 zcmV;p0Z0A;0JH%Bcmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 zgM;h+e}H-;fa$RjDQco7?di^51Rl;u)Gj_%m726q2vr=0C>O%Y7!X&24R8n zh)|%wQ2`JD;DPdlQ6Qhld>|g7Vzfdr5Xd~@AP-m(NT`^OL^FU;NF#G0!h2Z~y z1R(cN8C*Xf{P;cXKr)^X2ObZ4qyh;5PLIO|QJBX8IyZ~}K5Pon;6dDCVv^vuhJX)| iB7^yeKn#up`Kn+F;5Z-vw}E#gL;;=wcV7v;e+58OX1XQ- diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_5.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_5.bm deleted file mode 100644 index 3be1790d74ca3b2fce974f633aaa7e01de7b81dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 449 zcmV;y0Y3f#0KEYKcmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 zgM;h+e}H-;fa$RjDQco7?di^2q-W;Yyl#|@IeI_2duyZItm^@#xMy0JwnA$ zkjM}vKnJWaK2Zt~@UX!3f(ObGM1p@I_<(wbO3?_!Lm=~rfee5J0x1;}5y*hZ0qjX1 z{HF;Jz89VW0Z0s52?33R=0X09LO3Q-pM%{NF*roR1;7Wq0+9fcL@|&bgnSA&^yK02ABu5sl|h@N4$1{oLzFaIwB3HU+d1BXKa<$%dg$HIOkbHE~@p#TZ^fDM3?0*L%z2j+mt z{9*Ua0Sm$Z00=?upfb3AKl$)`+JI#|APzhp^+*H~0G%I(45KlQ0(5T}0DRaLqrii> r#Kk4SZw&w+B1H%D5P%sR2lG_G6~J&n0dE5CNQeVG0`9&Odj1N48I-`J diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_6.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_6.bm deleted file mode 100644 index c457e78771463e34c9357d1c8dd27ddc2984d33c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 466 zcmV;@0WJOk0L}pbcmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iRsfe-*w zfDrs3;Bv47+x>rldLw{xzzM_vcnBT>4>S>w0AN7$;0#I?W&{)(2oFKAa4@_OK}G@V zFaZvNhmakVf)Ec#zzAv*83F{T0QH6k${|970Y?oG2cXod5Q-!d`47Yc)G}6xMj{yp zoJ1lZ^g5KniHPJtWB~RgkN#7Hh+hlBu4oECV$etoY#%ZY^kNagGK~Bl=&^~yCJ-(F zJ>V3G1d<_)fczui_mW>J5(*px@ec+gprW7pj{%53$UlPqrmA>d!Z<8ZJ_q1bSFfAr+x@E?kC zaFs!uqz@$MpN>5^kU`GLIxQa1~30F0txs*;{%670p)fI+{{RRR diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_7.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_7.bm deleted file mode 100644 index 7e83e14a5ae80fed0dfe4163d65bc5bb431613e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 350 zcmV-k0ipf@09pY6cmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 zgM;h+e}H-;fa$RjDQco7?di^51Rl;u)Gj_%m726q2vr=0C>O%Y7!X&24R8n zh)|%w_<{$@5kvxz0fQxIgkl7w0B8|NsF;XAJ_#fLl;I)@34o*qEd+qX!Sf*hMj;#& zDFfXWF*roRK%kmL0!a{t1L{e9q$h?!{DXtuDgUUHhd~E0#!LT`0uSM?UKtQA-7L0R1hYVN712hFV6ap|Gd#F&xVj&O%T7YCsC@?+hkO&~K){|ERc09Xc%0uQ)60v!(r zR{%IO3BW$C0V41N>M#Q=83LhzGJ-(C^9BKwAP8Uv4IDx~4vYZk^aVmUI1ua{fP7E@ zhakcO;=ltb^g{%2i2(?>Fo=jgCIcd#L*yt3hzN^^2B5%|)sXl>LGuU!4I}vrfdk=u zQjhdr1%S2iy=^F@Sx>ClCl=KKu}Lc!TT!I0STn5Zzz{xC8jTfC7gD R_?^t!2l=@{{txc}J^=C5d%XYv diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_9.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_9.bm deleted file mode 100644 index 269e5b1d860d851c52cc46fe88b260d4ba0f9a9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 317 zcmV-D0mA+P0675wcmu#50PqKZJOSVj0C)qy9{}J$VSx>WL;?FSkOOc32Y^r@AJ6zg zZx9F%f(HPHgW~}KfkyzNa2Lb^4S){_RDtggh*$vU1_!_XK^QuqJ|N+vyhMTVj~yS< zRu5?Tz<>q%l!Mr|GXOFDX=n(91Jb4sgGzO3?r{gO9X`5w;sv+>RPy2AV{`xuwRpaA zI)DZs1xQicLF*6$EGgh1g_J=6$94!I?6c8+M zaHrubuL1@SQ}U7|9|VDR$ss@pRaQI$8=1_lq)tCP%rP)h-~V2@}Tv@jpB0&{>Hz`W`JKd=njggbmVMfl(rNH|CM PkTM7aMhFL+tl;4G_Y8T; diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/meta.txt b/assets/resources/dolphin/L2_Furippa2_128x64/meta.txt deleted file mode 100644 index c21027e4..00000000 --- a/assets/resources/dolphin/L2_Furippa2_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 11 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm deleted file mode 100644 index 3ff70a91699daba73b1e15ad953597404f21fd73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 543 zcmV+)0^t1t02>1TfII=<4*+-rz#ahb2Y@^Q;12+J0B`Jbzys%g6hLdw|K0cqe5e5T z@Be+^{ICCh;P->@0qgMt6&wa%nfN_FVDqCCi~Yd<1JYxS;(zF%kh%paaHZP5cASF948q4a5Hr#4N}<5C6Ua`VWl;A`dt!&`zW8hy%nv!0~`!f5G&n zWP#@b2aE++H9-6W$_4okq)X%vEBp)|gZKxW6XYLBC-@&f|JZ%E;Prrm#$y!ZgW3_| z2bg_v;Pdkb*T@eqyaXQM_5Fj?o!c9`#aq|Apyq2d!Ju$TE2Y z(jhqNLO%fV_)uUL0C{jf^9sN`RpSeXBSap5&;5e&4ERJ~5FvPj&j3dr2R;wr4~z_6 zzW{iR4gwb-0E5U}Ao2K5@DLxxRKm7gw!T;ge!Qv$-0kBDfIJLkx z_$%1iFc1$N{sDOZ4TcAPFBp8ImR@_1@VA>6y~llIsgt14`B1)1DF5+ diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm deleted file mode 100644 index ed11583f8f8149118f44b8da3aa791c579bffa72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 545 zcmV++0^a=r038DVfII=<4*+-rz#ahb2Y@^Q;12+J0B`Jbzys%g6hLdw|K0cqe5e5T z@Be+^{ICCh;P->@0qgMt6&wa%nfN_Fa~y9I|3)bn`+@uipnT|{AI4rM3b1N{@`-$b?E?IV=Kli+p#0>Y;Cn$nLH!4p|Mnjkrz9R*@Or>O;vZ0X!aP9p z508)@SnzrIgY)-*gWNtpka>Q=>Q5m0&q3u#2Z_9g=S2sIIA9u%H@{W-{fEwt|BnO8 zQOom(#2$k2jRVv69}>a9A0rk5lw+0afPK945PDOA>D$$MzhLo|KnLSK2b4ppyMTJV zV)ZYWJcHxgClhpk_ptl!|MU0<`#=A?gU_)$fcmdt^WTHss!t#Ay)EGNt9n@mPat|k zCml#f;2vKJ3250rBO1N>HKun6_R8bk9J3=_fg1CN3p4_|}^4G7=@#fE+W jd6S9=SST1i2jn~unFHUzUl<4D{~rgExRvGrM(KmhGyew= diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm deleted file mode 100644 index 41850505b54db86c1a371b051d9d4d1a5eee03da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 548 zcmV+<0^9uo03ZVYfII=<4*+-rz#ahb2Y@^Q;12+J0B`Jbzys%g6hLdw|K0cqe5e5T z@Be+^{ICCh;P->@0qgMt6&wa%nfN_FVDqCCi~Yd<1JYwn{D0`6kh%paaHZL5PFSF948q4a5Hr#4N}<5C6Ua`VWmOBo8<$&`zW8hy%nv!0~`!f5G&L ze1Ycz2aE++H9-6W$_4okq?7y)EBp)|gZKxW6XYLArz9Ug|JZ%E;Prrm#6CVidqO-w z^AD~(9)4i@{z2vUfP>sVzp#3f$bPfXc~Sx5Zy@>6LE;V=2BVGd)qcNw5P4C*@!)w% zIeu{X{>SSY2dC^lC4+!IMl1y=$1B$W`+4Rd^rr*Ux2pAi!Q(1`55{~CD2G#b0QGvs z>R&K<2gkNfCg}g~VfWqt=kO2qfB$y}pJI0b^09*q9fII=<4*+-rz#ahb2Y@^Q;12+J0B`Jbzys%g6hLdw|K0cqe5e5T z@Be+^{ICCh;Q#o6ijF!s2lyU;uzAsdWyk-5NFGxhZxjDT2bAUk&Ih0LUhr?^{AM%& zc+6?PfO+5WFg^kBf8qGw&+%42{=M-4c!%2G5C@OYK9sDGJo@mE zc=|o{K>R}C!13q@(k1c-oEH%f2ax_`w+Vy;Kp#mW+(G3Mh*81i|NV#C2NLAw0p$S) zjK(R+2bK3FD9}BG9%1#tVEmF82t5Pi9${8@q z2*e&H@(-OCe-PjU%K`7ze!pPzqi@@zuiwBh9&q@B(4I6H2k;(Wu=xGpEO9&y{{sNv zSHa`!fPK945PARLL1B}F$G58We!=4^fKi*n0)R(>Kwd=sMpFN#Ee^9sN`RpSeXQ-Omt0T1*4V7x;<5f}m}XaK=BksNp&_&&aA;IDQrw8%>hrtZOCMWe~5Kj4f5%sw&91P}4qAq=a*2S$ke#lr;fe8!`HkEck! ua2PZrfe#7~;P5^Gd6S6{KljlC++%42{=M-4c!%2G5C@OYK9sDGJo@mE zc=|o{K>R}C!13q@(k1c-oEH%f2ax_`w+Vy;Kp#mW+(G3Mh*81i|NV#C2NLAw0p$S) zjK(R+2bK3FD9}BG9%1#tVEmF82t5Pi|pA|Cm+*;;$H7I-Cp{;0S-8`vu||@QA=5Lh%L(w20%t=fV5| z@qvrR;14f=JV<50dWnFs;zAb)Jbn}W1PAd|tJPtUFnFE{0S*rj|3Clyf8p>$Fo}u) z1rWH-HGU0LVlXyL1Ovx^fL=erp^x|@0P_!wa{&YVc1S}i@Ild{KQVB@JRdQr-{a}h vFPsJq2;f7)gZMlTfF5MxL=XLTK=&V%0zC)^&w#!n!JmW8TuSo*9{?WUP=735 diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt b/assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt deleted file mode 100644 index 8ad8d42a..00000000 --- a/assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt +++ /dev/null @@ -1,32 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 3 -Active frames: 2 -Frames order: 0 1 2 3 4 -Active cycles: 4 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 22 -Y: 25 -Text: Mess with\nthe best, -AlignH: Right -AlignV: Center -StartFrame: 4 -EndFrame: 7 - -Slot: 0 -X: 31 -Y: 25 -Text: die like\nthe rest. -AlignH: Right -AlignV: Center -StartFrame: 8 -EndFrame: 10 diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_0.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_0.bm deleted file mode 100644 index 3fc3644065c7118acc02181bf59501908732b125..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 699 zcmV;s0z~}*0Jj4FfCfGf$p7FUupbCNU>qQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`*b3CAG{16 zU9fxV^-+412Js*Nhw|~C|NKYyIQ@ghI{@Hc+wuSXugYP76pcVikN|*`h5-zKLxb^u z2b2l{6bGRIV1M}j;2tm-1dtw{{rZrHD$M^G($N}S! z2Z?aK!1Dk7!RHWwhui=$_=f@>42~UJqSM`&-CydE|Od3pbU@dk(pe)D*Gq;Medn)&zO^^3>E140LmD=|NxemDa1 zh0p8|37|YdBfpq3igEt3^j~*Kuh<)aUfs$XY5Gs&7yaCY%odWRyQSg+4w=1yg|qkID<<)vNDd9{|YR;1AKy{v_k^4~~`r2*5zqBLet&|I!k9 z(O?iy_x^nVfW$YDaCp!G<3@l$;Lzy50s#>GK>R=Z_~>-x(YAXca{ze!ATtx0KqHa( h24lnIhl9ivFpznG2ski+AcM*d4`jHA#Oh?hfB?*oKqQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`*b3CAG{16 zU9fxV^-+412Js*Nhw|~C|NKYyIQ@ghI{@Hc+wuSXugYP76pcVikN|*`h5-zKLxb^u z2b2l{6bGRIV1M}j;2tm-1dtw{{rZrHD$M^G($N}S! z2Z?aK!1Dk7!RHWwhui=$_=f@>42~UJqS^Z)ja(BB|=SRLi({sY7sAR+tD#AATKgT`y;--Fga9uN%(9yqMT{(bo13(6lp zus|k&@dS?keQ@9k$K?;d1I8HuN0fSavJiPf|K0$3bOGa^2b5;V@`C?7FbR-6U^oc) zJhZt_loRpefN+812U3sVh9L9!ao{j0A3{I)v&_rlFB_B!G0cF#VT<#hm&_h8d|*6g z4)c%~&Sm0&XCQDI0DS@C1R1zUW!UwIhvH}g8ZG*9fd`*}Iw14FQXm>g1x5fbBKY@Y z`$z=?;8YNJ@F21IL410(edw6yVbP!G{}OTdhsR3*h+rUb>;wbh=l@7a=S6@*Ki~L! zh+-SaI6P&}?)rI1eAh55xbzj)zVo8)x(90|$@712H+20yvc%#pVbH7=Q?2 WAoBnez=R<5gM--MG4LS#6Ndmx6F-&! diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_10.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_10.bm deleted file mode 100644 index f808193580805a0d405d7316ec33cc3708428222..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 699 zcmV;s0z~}*0Jj4FfCfGf$p7FUupbCNU>qQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`Fbr z;9&=kY#vwm;5@Z@sJ%)9c#r?V6@05Tu}|7a%==MeNb|NeYDLMlT4_&@L(06a;9 zz~}F(?t#L8)58DL5rOJGRqKzH{e7q3%Ty&^-wM;?FZLh`erCEF2UbFva=L z%ja^jU?BkUm^;l1Ao7_K1`-M$0DT=L|C_*Q5#ZTWL+>;PjN#%iDv&(90nrDXg5dz< zKx&Rb7m0j(vVEiiA>e>{AQeso7C$I2k5;d}M~Gx@@CWGU{}OTdhsR3*f?yzOk%4?X z|LF-l=%Athf9KE$3`2Pb2aNz8G;9O~+A($j#2CZj|KG<$r!I}N!4q%{L*W6KoXPqQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`*b3CAG{16 zU9fxV^-+412Js*Nhw|~C|NKYyIQ@ghI{@Hc+wuSXugYP76pcVikN|*`h5-zKLxb^u z2b2l{6bGRIV1M}j;2tm-1dtw{{rZrHD$M^G($N}S! z2Z?aK!1Dk7!RHWwhui=$_=f@>42~UJqS^Z)ja(BC0=z$^~(^Zxje?GWy2V?S!{{iC+fFsI0C=hwX|DF(ebOGa^2b5;V@`V08a0!q+U^oc) zJhZt_lq2EifN+812U3sVh9L5ScscMG6c3>v{8{E@@fVHC2{`OPU@*n`&&%g>fkCqr z7!1MQasv6xxKJI%4g)|Bpgcf>Hwg_hp0N=8O#nlR|4uL<^Y8~m9+@8ok^xbG3&_4b z**?%#J`0V=9y|yveo$W?tzUXXIoNb(`TxY66e01_k&15~z(5qE{*aT-ivWm!zwq}E z#5a&|c+dgkMs5rnc?*sM$MFO3|L^0W)4*VM&&SRN4qQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`*b3CAG{16 zU9fxV^-+412Js*Nhw|~C|NKYyIQ@ghI{@Hc+wuSXugYP76pcVikN|*`h5-zKLxb^u z2b2l{6bGRIV1M}j;2tm-1dtw{{rZrHD$M^G($N}S! z2Z?aK!1Dk7!RHWwhui=$_=f@>42~UJqS^Z)ja(BB|=SRLi({sY7sAR+tD#AATKgT`y;--Fga9)Ar89yqMT{(bo13(6lp zutFw)@dS?keQ@9k$K?;dSRs(e0z9MB#gK!_7yj@A%b*V&_&lRFKa>~w;f73s;{m`& z!R4jOe4w9?95aLu7&?@H1ThDn!;c+-K>88?#hzwg5qR97QI2GG0}NlB{JvoEgX1qS z8H2p!1@oDBpc%^?27n(xc!36P5*c^BVj=jN0EUZxoM1uc;0}m9@Dzy#k^xbG3&_4b z**?+%K=>5|9y|yveo$W?tzUX3IoNb(`TxY6ej)MFyGH>7k6<7l4?p@sPdY3D5&r+f z>;X~ULBZod2aOs527_avalm-~AbubH{B$~T7~4OeI2b&B5E+Tgpb^BV<}WZnIK%)$ X2?v+}rUW4eq#Pc{0gr(P;G8%BxBEH0 diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_4.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_4.bm deleted file mode 100644 index a961f4c0a17681a1c74e2d84e79a148aad724c00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 693 zcmV;m0!sY>0I>r9fCfGf$p7FUupbCNU>qQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`*b3CAG{16 zU9fxV^-+412Js*Nhw|~C|NKYyIQ@ghI{@Hc+wuSXugYP76pcVikN|*`h5-zKLxb^u z2b2l{6bGRIV1M}j;2tm-1dtw{{rZrHD$M^G($N}S! z2Z?aK!1Dk7!RHWwhui=$_=f@>42~UJqS2Y|N1+?Jbr-k z{z(Djpa0MQ+B-vhh2sFQJIl}f2Z%I4L-)VHpdSnfJZ8Rq_&sOy$OPhq4;)rve?I(h z1?4ZkSRoTYc!EcNKDclPWAcmtED%U!0UlA|K!eUF{P2Uzpbs4QJfk*0lqd1yf=q$q z0l-JW<)zAep&t)CBZLnaKY}0lA&5Mno(_Bl1q0|u{}y?fd`07Of=)XS7z{CfbMpDz zpipeUJZ28_kQdHn!hr4|a2f!80pbK1xJYT3_yzz!6F?B+uhWbOJp2LC2c|{=q<~al z0`f19c2BewkAeeo2af^^ACwozt5@EU4t5HgXpn2anqQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`*b3CAG{16 zU9fxV^-+412Js*Nhw|~C|NKYyIQ@ghI{@Hc+wuSXugYP76pcVikN|*`h5-zKLxb^u z2b2nd6bGRIV1M}j;2tm=1dtw{{rZrHP-5s;UI*I%px2mpVO z`b791w0H%49$)BuqF)3Dk01ZfhloW;KoQhW0pv^`2S0sRfWR<-@$>+M9*gn^j}!j` z^iSsi@oz8YPvr-l0s{YU&0HQZ77*Z5=pSegf*%P7uaEFQA1C}uIC(%k@BjTB;2u9f zc}FCG@z4L~|Lq;2zCiM@JIl}f2Z%I4L-(JE#{q!{jMvY<2dsWPAQ})naaoD{`|-dR zlsy+^C*#Ke;RD7Fr60izLFe$}z+g~5gn#j8nU};~Hz*WinE`;q7w11Om^@(k zzvVEiif$)HNAQeso7C$I2k5;d}6CK_F{T%<|PCpR%>0l8I1PxL!FNdG~At#*{ z0SNzp=gBM7f{zT>g@%TVyCo+IX5~G;> e`61x(1q>t}U;+*dAP6AxgM--MG4LS#6Ndn7H99^3 diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_6.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_6.bm deleted file mode 100644 index 2f030833a27a95c21247e158074d5fe721ef4e51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 712 zcmV;(0yq5u0K@|SfCfGf$p7FUupbCNU>qQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf50nsIf%KjO&KOTXdB@{R^9P(y zBlm%W%eD_Vz&y2jsJ%)9c#r?V=Qt0&<3IoSgUaAPdB^M?G1vzJ{@;)P?S4}afTU^& zRDc8oq$T0FLxb^u2b2oo6bGRIV1M}j;2tm;gpeMd{rZrHD$M^G($RH4mfILfu?gy9u?hhygLINLf0LS7S2zWe@LZBde$RZGk5IpJl586z} zqb~=pzgPqi0RJKMiGV$5@Cx`mztH(af(Q>DKmVT(5Q>n1Bj^X2FnApO^0En z0)um)Fc@O|=jHRcc%V4oc+4H=ATONA1UYCN2Y??yc!36P4;dajHZ>6Y%?$%2zg!?y zAbEHLq7OO+;sMBj)gS`$FOPOlv=a(^e2@yK0t+9M7ssnt-oQQqk-NYjqo4dq$KoFy zECLaLfvQFY@bmwqB=e%cAfNC2`T+rmZy@0DpaaH@0D-}w(S8I1A^3s#fA{gw>B*yP u_C)3Y@%TVyCo+IXBk&Bzhsh5Ih$vwo^8gTVVE{n~lpG$(aS@5s$%6o#P)TY4 diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_7.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_7.bm deleted file mode 100644 index 4519819ea5c7e9d0127fbaf538f5aa276c881d4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 732 zcmV<20weta0N4WmfCfGV7(hN_|A2W$0rLUygZ2T!2b6#WKLN=A3V#6N1J|GrP+;Z( z<=H`&OaarjzAtI!uJEq|Mv%+LINLf0LS7S z2zWe@LZBde$RZGk5IpJl586!O83}kjb^5>{fCu>xq)Y+pM}Sws<^G4tCGbFa@&Ekz zc!X4h0Ubo}9z?<5bNAJCP~idN=m7{l7vv8fC;kWMpUwf|-e1h0$`3jO1^(ZfxIADi zA;725KF}WoJ`xXKAK-pIPxzE@@_>2Y|N1+?Jbsz*c*i7w@z4L~|Lq;2zFzQn*d68P z{sY7sAR+tB;p&mVgT`y;--FgK9}o=)9yqMT{(bo13(6Nius|k&@dS?keQ@9t$K?aR zgT@&EN0fSavJiPe|K0$3bOGa^2b5;V@`C?7FbR-6U^oc)JhZt_loRpefN+812U3sV zh9L5RPB_pQ6c3>v{8{E@@fVHC1qSCpU@*n`&&%g>@j!9F@t8Z#KwmkK2y)Ok4*))Z z@d6Co9x^<5Y-%C*ni>X4ez-uYK=SYhL>_bt!~>B5sz3$gUmonAXeJc+`5+Ze1QtIi zFOOEQy?}fJBX@v5M?d(JkHkJYSOg;h15}I);phKIN#{j?K|kO5^a2AA-a*0RKnIN) z0Rw|WqWlO1L-7Of|L^0W)00No?1{_)Pl0{-8R|LuNL3;?8R z0#twm1f(zsWC9!?i}*aCPzazs2m=HE$M*p7fWRbx^!M-7gg+br*l7Utm+#?!{1`lF z0P(~J*d9N>oPIzL9DqDah3*HJ|LzYsgakg|0guEu5b${*g+M^{kVGL6AbHd9AGDdm zG7|85>-B&^01xsXNSFiGj{vWO%l!|OOW=U<G{Jc)zA=kKcOp~3^l z&;k&8FUTG|Py7$jKb!-_yuX=0lpb^l3;n+}aCpF2LxE4AeV{%Fd?X&eKfwHapYbT+ zMw89FhaaKmVWqw04I13&sIpcbA{|4-ja8hwp!ZDn|kj8Lyvy4_W;1 zU^F0j;e*`fHloP?vfWV-A2>;^GGcSm|Zcs_b zVgmt&FV22nJCq6ym3igEt8cGaefnh<)aOA;n*=5Gs&7 zyaCY%rbYqCfYl%Z@-L5ePqc22f&=7$RX7k>{Gh%)TE6s%cX$KzbN`7rC`03=BNQD{ zFeNDeq$KmAz#<>-{Q3a_h;Ja^@t_07jNBMBaxcINkKza6|KG<$r+~oipNO2m9zO^S n#O6>4=lTJd`1v8=@dXSd9$*3v3?K*~@`HofE-~;R{1b-&_Jm6b diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_9.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_9.bm deleted file mode 100644 index 1339c607e63f0d52de7d04b4076e94a820e1a774..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 698 zcmV;r0!94+0JZ}EfCfGf$p7FUupbCNU>qQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`Fbr z;9&=kY#vwm;5@Z@sJ%)9c#r?V6@05Tu}|7a%==MeNb|NeYDLMlT4_&@L(06a;9 zz~}F(?t#L8)58D(8>|NlpL2anUflp_fAGynPj`$uSRm%Pj-aR-l|_zw_hfQRq*hrm7pVdzbK`|x`8 z*MguBc;d4Y`S;_1FDzXBgdp++j{bdcG>ik~2Tl#*1dllM@bWl001;PbV2bX|4AoGx1ARLGd zQHUb(FOPOlw17lB5Dz2*slbBA0l5{1PxL!FNdG~ zAt#*_6hH6$`T+rmZy@0DpaaH@fPlfE5q1E?7{lTJ-^W9zE{(In6L1Ve;Q^SO$^jgQ gz%w2nAP`~zp;M3n1A@UIc|pPKml*gEehI^X01SRc?f?J) diff --git a/assets/resources/dolphin/L2_Soldering_128x64/meta.txt b/assets/resources/dolphin/L2_Soldering_128x64/meta.txt deleted file mode 100644 index b705bf62..00000000 --- a/assets/resources/dolphin/L2_Soldering_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 9 -Active frames: 5 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 9 10 9 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 71 -Y: 28 -Text: I am busy rn -AlignH: Left -AlignV: Center -StartFrame: 10 -EndFrame: 13 diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_0.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_0.bm deleted file mode 100644 index 07a63d6424edfe232cc55faf2756cca9485384c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 398 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 K0l^!}zz-0n&QtRM diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_12.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_12.bm deleted file mode 100644 index 392905a559e718552e9db176de5b7cc96aee167a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 541 zcmV+&0^2HpaDrD0~`qk(=2CEZ z0f)!)C5Sy^VDfDd6n=P6c!CNF4@>zJeV5sIJm5g`(0}*gLcxQ{tS}II8iWXe@eB|E zXaM&oh#-OHDi9%W;4^FixNpGtzyG;6_}9XI3LFReE(ABI%&-txX8^(=_6Q6zF#`F| z%pwXHSUm6OQH)?em-v`H|E9r!(1@c42fM^ULUj;%Vgc))|K1Fy>Kb?a9=!ZL{EAT6 z1w0;~ugGj@5C#axa1?NQe1YPw7#HjRdiZJo@l^SIcmMtl`T!3dm;X)wiUZI8-@skq zAn}sVy5IRje7^$Wz<=Pt@uKiS==&f4;{o%GgT|Nt_kZdD@qLIqwsLTI)$jj*{(v4o zPrv8!!7vCsb@%_@{(}R60SDkA5P1JeqyPQ^#q2GC2cG?kAOw2~a6#jN_us+G|A+7a f<$u2eTmSC}k$89X5PN_N?%5stv_aF0B#1shQS}MQ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_13.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_13.bm deleted file mode 100644 index aa5353e988d02785f45759a024931abe7d580086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 584 zcmV-O0=NAE07L@+fII=<4*+-rz#ahb2Y@^IANT|F;Rk?x!S}#`z&(Kbz(C*~0DZs# zKZbz<@kP z-_ZGk-W5OxmVe$YUN}5vRseX=FaHn!_#S((c=Kc+^8fh5iJ!HD#(O>ojswBL|KI=9 zyXAl$Ey0L`%fG>Z-}v0Uoqw17zfWUvCftXMrUwwc* zFdOIv=nzr}54)K^iv!95wSa!G^7S~tJCHd1P#$^^c)jswmy;2(?+ml*DY%6Tpaje?1R4~z?+C+R@JB71OJ%^j$i~g#Q}fdP!0e4@$U+bI{^s# z3(Nlv;0TA07lYt1>XG_?f57p%{4ekc#4b7D#lRpxNFUrDAR+mp#tHC`{fFbU28aM| z{|{g!U}Yi1z%rjedF|ox@OZ&E5DPI#e_na`JbB{LzW^w(J#=94pcIrmEe`+y diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_14.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_14.bm deleted file mode 100644 index 837c6c71defd8be280776e7881817fc68276b5d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 610 zcmV-o0-gN<0A2$B|KVUk{Q*P)Fi?2?0rvt2kDwoLFnIg{_W=j^KOjHgdBMT)0R98a zDj@jq1I#KQ_}~M|;Q;Id;{Xp%fIlVx`hWxc9=`B>kL(_GhKz#+o><3>gVG5B{Tt>8 z4;U5;!}CwXJVamY_&z)lfe}FLAMe0Dt_H!Nasoqr`-9r~&_@VJU>l>0Krj6W95O)w zgJlpe;FN#haR>y*&>wsRUrhppDF-nZ{sYsG|A@(;VDX@2Js+@oPGA%6%nc+tE0kSi zuxJH>Lj(tm3qcDYD|B!X2tYh=AoBP#)&UWmL9h@2c-lSS_KA8RbO;895vb?`+9^1J zKqL|g{UbmRX+$ywAO;Ttt3U_0LJkiYg@OTE2b}yKQ3wz`aF!;BgU_B1DFg(5A@ZVV zdEoJzgVn$w5#o2S;PHin%RrqGp+G;L5P3mC<-mYJ58@9VCwyLBV0pmdiSdjD2SN@Q z8NBEa$N2-wgZ=&g0qGDH4O%!v8W;Zm|NDc@SRg@@1ImB)|6{@DKq!m?&N3Yo|NZ%| zgUlq?4N0o0xY&yEj|qzoQt^FED>06iEa;GRJCuV4t{^!eXG#1C2(0)+vGdoW=5 z-AFJnC>}#T4`yhz^{KA$Q(_^4>({vevs<0996^* wNHeH?5Gg=DU|{j6KxP1O`UB1b0N_Ay5Dfv)f$A(Dg8+f^cz{o!766F@+?Nvri2wiq diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_15.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_15.bm deleted file mode 100644 index 9ff56a5b6bd83d39c6585dafb084113387bf309d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 741 zcmV2edpMFaQvP#()oKH^LrM60rl^I503^{{iLsHy|)^ z0P@TO$c~T*6avV`F91BQ2NQrnAQ7N>U;*j-2b3ZK1#_7gbPDB+U69Vy z2d})|M3D~)2*KxjACw9NtPlv3%2fI<=fqYpC`JMNAOZbLLFM@O69xpR5TL;;kZ&wM zv48$|iGu=y7zjQv7}Uef!T-;u1c9OhgeEXZ92#R14<0x?VUR4PTpn0R2t2>t^7yL9kti^WY(e8% z2af;$0dNeKDo`E-93GeRaKs{72$D!_6M%T`oC3cYgo1+sguF|DVj0x`W)B_(0t2Li zLmEV1AFl)B{S1u`0c1CcR7V2<{{Q|V@qlR{6sUUupb^dk^nd z`{45EK&FvLi4;m6ARtKSAiN120pwi|{4+ccIs`5tFCG9~z98}UgU&C2CJn$IUIF-r z&jD@$i~=whF90}!d04o9jcmu#50PqKZJOMC(e@*~H;Gcu)Kp-FI{sIrE!~!3HfrIK{fDVi> zA2bOE00<&6fJcGxAQBVFfe%V z2?Po(1P}^v0C>zG2xA9~2ufh`6Nn_h0*o^N!C(S$6qr3?0P+rtY{w!sFnNW<3O&Gk zC9wT8f5ItaOfd_~*OhgYk2s}k& z0iYO!9@H2I{!R!IaNrw+4_FxNU{(lyfXFOW;C>`v@t9B|0P2CSOq13hXmkeVx$Jlghm1hk|1&b^56rE9-u*b zk`z`43?iih97Y&eJm674dC)Kbk@t^KHL{N x0tncHk_Vv>zzCfd@UEO1VEw>AQ=&RRdj|v`*MLBS&H_(>K!T0}e_#N^0q7Yl$LIh6 diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_17.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_17.bm deleted file mode 100644 index 80cb06fd3cbd753e5bf6f00f85a67bee5a0590ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 492 zcmV)+z)6lKcGPUz(7I#PwD_arU3pz{4gKTMmK;S{sc4Q z5e5Umrk^9=vnU*SF-SfRq7N02#{ta&$w0Dd;runk9{8*#h#oL? zpn`|t4+otp@uYxyFYqOpfCv0UAb8RN;~c<20p|dpgVzBAfI@5F^SA(p1TP>la);yq zKzQLakl--Rh3F4FUyEM>0}MDG_)L^!Tu=xYJ>Zd5NxcMq_(?FdlcdpjPc#Z}#e8CO zMO1)BJ^*vTP$E}QF^t4QAoY-1fW%BFFu7zj3ZaNaf^Z;vQUGEN5a5hRZjc-Rw7dYp z@GDp(AySW^3;=~uQ?zglu?Pg@5T)Z>8J(~N;}N1AS>nV%!5)YJPa`p_olv4@E&zNWkwRctC_R7$4g-w>^KMRlm@CSfA05|Yd-vDL6 zrh}m*mk*cyGWGx=$00Bbd_3^slEc9uABn*Va1<7}%{%~tpfJ~gKoN{-w5JrATcm{vup{D1Q;0w-&W_W-d nPOl>XE1giJZ<8y6l#+*4fXD!a1qaL2M1)X4c|-t0*bo4Ks86b< diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_2.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_2.bm deleted file mode 100644 index c014858502e8d153d61a39604e4128198513b5df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 463 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 diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_6.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_6.bm deleted file mode 100644 index f46aabadf6b1ae21e3d61fe50b3691c7d6c063cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 521 zcmV+k0`~m@00ja7cmu#50PqKZJOSVj0C)qy9suwMfCl~!-UT!tw-1;80uQ+4CIOF! zfyz7&Y&;SH_?!XCzz$pm1_c0`cmW1AUIhRSF`!xn!~k{xc|%|jFhIeE;s7}SJa7s*c2Y|^a2<$=eApaMJ1^j@| z!R>*_Kw@C=fpCHE0HnYqkPIjP=^qEYn)yh8Q6L}Kcrhml0>RW^6G#X858%Jqpc#ot zXmU_2n)tuaFnUfwJ=1rhkb4GHFf2>f6M#O8p7;QxRG9?>hP z5UwAO{(K(N3s4ZJgaOBc+JRIt3;{Yn3>ZDB03nQU6Qg)(hyg(5U{;R;55Nb=@PF;$ zpby**&oKZjYfqw_UW%0@E diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_7.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_7.bm deleted file mode 100644 index 07a63d6424edfe232cc55faf2756cca9485384c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 398 zcmV;90df8T0Ez(scmu#50PqKZJOSVj0C)qy9suwMfCl~!-UT!tw-1;80uQ+4CIOF! zgX<3jfPNA+B6Pzk4iom2tn#;^n#umj2)0D*(zKo1-Q8UO(h1Iz*AKn{saKo1xP zj)D&Wh99IJJP5=B5D$o;JZusHD1+iHh=9~65D0)iBuBB7U;*_bz<=hjC=9?K6dnWu zJph1^L?03l@i1UO`M~5LF))3=DKH5lctQ0hzET0@{85TQ@O2P)tcExaXbwsRlUEPn zt|0ftVKhMTgRKM}^sB~_0qDQLmSPWjKs;lZ2p~T25I6)Tz7J>+ysiLY@6g zO6mkG0L(-p09gg72t>kz1KNR9F$l2MLI7Go2tlG85s3g704*;7Fnj>!2c7~xfHC`l smN*Z2$Qet{2y6j)(Cp6;1F6;IUcmu#50PqKZJOSVj0C)qy9suwMfCmBpkAw5U0GI>vco7%~KIHHT z1UwyF2?#uA0Q$H{0pdas1jYg55Woy789)b&0yYnrFg&mmpnSl=;|QQxQ?mgFj3+_D zH%LT7<$*}RbV22y8i+mb03m?St`HayJ>`HQ!0=-bC@?+bz#*YeA@E2bJ>)?_LF)kn zj#5D@pd-aX!1M3}$K?YDxMXsX30DFi3Q$SHKg+Wz(67Z8-P9y0uM6tm<%3p;CwCweq`t<5GWL?94Aeas{f2wnmn4S)O| z0YKn1MNcxw0z(|^5sxL?E_DWKtbc>!Jr~=4-}B)a6tGph%)1~B0Iz@!4=gh~K|3LmQUL&XqM4gg-Ku?-MBWDtu45FU!Lnhgj>GYI4e z{Gs!Z0Y`xY2dQ*Ny* dI7Rs27DoVFD1bfW41$AzguwvwjseIfKm^${pj`j} diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/meta.txt b/assets/resources/dolphin/L3_Furippa3_128x64/meta.txt deleted file mode 100644 index c21027e4..00000000 --- a/assets/resources/dolphin/L3_Furippa3_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 11 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_0.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_0.bm deleted file mode 100644 index cf2120ff470ed140a0d47f8adcfedd64a8d20cd8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 524 zcmV+n0`vU=00;vAfII=<4*+-rz#ahb2Y@^Q;12+J1Hc^RUH~ZK7yu!Ef~mg&l>gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moyY2a$|_{Be1B z_1@fzk3(N>7S?zlxT`9#A0ogVTq9g@ew41d(XP=VSg~;Hj|y3A{t&2LqOX zBrqU(%pwvm*nHFC4;V-A;4y&t56?soFT@N!Fvf^Kd8K*sdIr~590(U zs62`y4-ou9;1~gjhQ)p~0s7tW2a<#X=NDfII=<4*+-rz#ahb2Y@^Q;12+J1Hc^RUH~ZK7yu!Ef~mg&l>gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moydAvUe z4CbAH(1Y5=_NBlv@yzD~LFxymuRHCPz&0?<#{z&4SzY_JCBSe2<6sB2t&TES06g#j z_w~xK7yu)fFdrfL=z-<Ggk>LU;sg2?ePEr diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_10.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_10.bm deleted file mode 100644 index 1354c78f2ce9d20f8e2b08bad75be024c8b4018e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 550 zcmV+>0@?im03rhafIt8H0hjbC;12+J1Hc{t@CSfA0pJe+cmu!?uZ39z`kHnw)XbGA?pLmwE%~} zy@2_@_h9ho7eF67`E+st0xO6;`d`8WiKJjOAoj6;sc;N`6C=RTdV%Td&i`d_4UCf|z@P)xSAP9Ta2yHddcY5D zU;pbRfN%rjV88?4*DAnZ2jegY$6OvT0{Fay9`gBW6i?!y5PR3_um}%|P7f3M=-eY# z4~*V0FA#gz^x&a{#3T>}`sy9SPT;0k$7k=NpgZVC3@91cn6=9t;9u5Rrbv=ARIF!Y9FT7>Rtoa!<%S{|EAhPy^%- zU-Ud~P-+4FLFENYoC^Sd7$H4DLu*4rY^Z^6M{0Si0fBpab3Vf�Q4{f5&!@I diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_11.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_11.bm deleted file mode 100644 index c15289b5ec4e1bf2622a9d4e63dabf35ff48e539..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 572 zcmV-C0>k|Q05}5wfII=<4*+-rz#ahb2Y@^Q;12+J1Hc^RUH~ZK7yu!Ef~mg&l>gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moyM;G#;+THpei>dh@^8Tmxey$#5tZuD#Z~-@3~0->EJGfjrMx z0ps8L-+TXkU;pbRfN%rjV89E+9y5$^JbiMk1^|9D0C@Gmn5fFIDpmcnf zp7Y|Lf8wRFCXWIH9}s$Q|M0MQ#tGvDk!Z!|WBz~Osj-6yNIo!fd1wMd0*DU=0WgS2 zzhU!Fh&*8v;JA!LzF#>fgs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moyM;G#;ROdh@^8Tmxey$#5tD^_AbhQd|cDd7iKX+gJbk$zU7+ z_}DN2_w~xK7yDIo{EcjsC^iG+9t zG5_(!<>TT1B|@gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moyM;G#;+k_jh%>`t!fpTmxey$#5tZuYH%D-P_9V->EJGfjrMx z0poe!-}is*U;pbRfN%rjV8D8}`g!-iRu3Oss{w!?jKCf~ukD||x(AP39xwv#QwTB2-kz-2Z#&A9`*hBC}S|m1Oa}! zhj8DxG5-V(!2oZpv;9-P&)-ErfIrv4{kCslF9AsiJ>$PR(fmQ=V;}zDAj zh=ayO`{U>Qm!A~-{}nBWM0gM&`2){~|Am9Z`ZNe2e1+v>{(s=9v4%hm;vX0}9JB!; zfkZG6F$lyYP(C7k0pke21;#KRDn5EoNznt#@P8;}0!8BoxB4D8C^Z27qVj^J&IN!U zi9LbjVGwwS;uf9}h`|@-K*>)zAo9E54gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moydAvUe z3?`9)(1Y5={-wY%@JxpTLFxymuRHCPz&0TApaa%de*H;s8~}LO0qv_}jFtcoJODkj z{<&5I01p{}J@5Tsc)$zd@)3K><)~3Vihx1yTdu$$De(u1{d8^-uLs5t5EqC&>HDxy z#$l2O0{wLk;lFTW{s`lo%LzKVeWf3Ji4Y~H|L0+JAW$9{FA_=CvCKmItp zynH?6s8%5JkwEDADJ3VxKL5o_Vh<<~d_n2M|H8rNK!QlLV)L>8Kk!u8fCSzl@q>ZO zKoS@bJmwJz7wkT1@du0}_;47&e23?v2bbao9~fl-J|Oo0L&fC=pdZv;P*l0Vum|yi z6Vx6>5eJBVA#e-;#6x1g8UX!n_yfs80rQUm34lT8z!$CjJ|q-(p*+9)`S=`IVh^MN Q1IPRcAXxy-TqLo801PJV%>V!Z diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_3.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_3.bm deleted file mode 100644 index dc0fb9b79cc78fac3cda74455bf92526b8504378..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 529 zcmV+s0`C0*01X2FfII=<4*+-rz#ahb2Y@^Q;12+J1Hc^RUH~ZK7yu!Ef~mg&l>gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moyx?2gVN&7l=LU z`|wc4VUh>}{dErEzi?yz2pxg|-&tq+r+uHkih%%suY>z+-oRc0k`Q~xes!bxgUH4| z{y4n6d_Ux zgMrIH5*QFX<`D@O>^^Dn2aF^5a2UXRhv%XPm*NH=7-axHAol-5#pMQ|AJkq@RJp*g z2l0Xv)E-3<2Z(+la0~#%Lt?)g0R3+G1Ia=G^N#@ufI;WL7p?q0Boud{Jiq(-_#9Yb T52OJD$NUK(SpdykB(Z=1M>Oxj diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_4.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_4.bm deleted file mode 100644 index 025477a7a943d7eac253ddddafe16f07fea0a0a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 571 zcmV-B0>u3R05<~vfII=<4*+-rz#ahb2Y@^Q;12+J1Hc^RUH~ZK7yu!Ef~mg&l>gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moy#QwTB2-kz-2Z#&A9`*hBC}S|m1Oa}!hj8Dx zG5-V(!2oZpv;9-P&)-ErfIrv4{kCslF9AsiJ>$PR(fmQ=V;}zDAjdC0$f zeE*X3;-7!wrLhN;2tGjb;s4=a^PoWk9JtssDFT@N!Fv JGgk>LU;r>`30MFC diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_5.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_5.bm deleted file mode 100644 index 89a4cd6acf85afc12c608940bc379d5b1464e61c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 574 zcmV-E0>S+O06GHyfII=<4*+-rz#ahb2Y@^Q;12+J1Hc^RUH~ZK7yu!Ef~mg&l>gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moyEJGfDao0 zJbV7%r~kLB|NUgJ0D0goL&5v*|KRcU%CHy#_{;(0wcGc8b?|ug!Q%igi^zRn|F`}> z_Xmu=T7?t%s0Y>D?$P#p(0JGDum}o#LE?X18-#1Y@q@$#;tzWM{1h>mWP$*{T|>BU z+!+6Y2Vj6V)>;0k-)HZlpg@lx1>$^;)8I6U}&_*gvX5J?w|UUom{{tBBA0Gq@?gHR9Z4=5^J;8+P4?4H2#D2O~m z@e6=p1|tMtl>;T`r09X;cfcM>5D%Pq2uuPGJ^;OM;qf4&y$R+2-_O9}!w`KS2p&J+ MNdm|QYT+e}07|gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moygT??~7m$PAUoAq3{8R!Ddi{0*0Z)iLPwS&_jd(sVc!0b??_b}7 zhBFM1Ko{$%cMbc4AMilz5C;0oKh-TT1B|@ZO zKoS@bJmwJz6c31>KzPDGhXITS$bNbtd43>a@rF<&UNC!qq2ls`P!H-aC@NgwSONHx z*d9d@2Z(+la0~#%V2kpg57zI1Jd_|GIPegd1Ri_=df&t1K}UKM%m2ThfyIU(`alpo Of54IjkPOwrOBeu5#pKBV diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_7.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_7.bm deleted file mode 100644 index fb6d9bd292c739951e00a03612b86833b5222191..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 655 zcmV;A0&x8S0E+_ufIt852lspgKhS;iz(e>4-y8%#zgs<0+a|ong+SfFdhJW`Tz5N zG!8y5l&I41Fni(o&u{_Gwd0_X$Y z;r`#tqmT#@TtV!i9sF8g4;lbH%EMoU*a72!{{z^>Ao1V>%lHpousn6}Tm%I;Keha8 z!Gqy}z&`8v2b5|V0QG~`KqeZz1_1_$1IIKkPhxmM<>HaRIxwL6N<3fuygmU@qTo6p zfdCwjJl-FK2XjuqXhH2_|5D%>|E6Dop!IgYySuI5*PZ^#;2RlcYk@$ud+faK?%r2^ z{Yh{f38q@W4;#+@|GWQc|NmJm1AreJ1_RZ<)6c#7xnKX+D!^a|<1h!0tNUl~?xlau zxIAD5@p%ucy{-Q-23Po(%TS_!6#)9KcYfWnpR4>=>#zt4d_m%WT^w(JUJr~OATJPm zcdN6rHKZgN~2g{H1l72zw_<@JU89)z^J%7;gc|oWL^#_y{ zE^sUX{9uIj2a!ZU;vZQboc=Hj0K`LLzZwR5d3lj=zxR9rof1`a$*CIJVZ0A6_y p{rn^(6nCV8VdMYr=iqV&z2mSC6afRr{4F2=@9*sa->Za{FaVQOM?U}n diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_8.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_8.bm deleted file mode 100644 index a0377f635fe25d6ae38494e2e28ec168af6f1f3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 645 zcmV;00($)c0D%Jk|NZ~_|M&_Z3G;);_z%CBJbnQF`UCIo0uXceiGk!D-3L^J| z&NuYD3?IUn5PMnjAN{TZ8v=R)3!o2ykjtZx z2oan?^A?x`#()o+{4T%`90&LxTmX500q^tI!Eg{1;QrU~uLci>2LSu4*dQSCjYA+F zuzJV@!&iX7AkhGL=7s6({)5lOBY<>aLG+Y(zxjB40;5O3bU^|DIUaleOZY%_G`t3c z9@a1QE&-43Wq2A7R`StyE@tgPm*Gn$0|I`PJ1Jg=*53Be4-PiB> zxcs78g&U0(KsK)bH`m+QhsJ?LfIw5?4-@+6|M(h^d|>ebc!S=rxCIPm86bc!*HG>o z_Xa=Uf!H7o^_G9CciH>ss1OJG_&>JI>;>Q{AqTv7=UP9AJd9)i1Ro%I@c;0zc%Mdr1P_qBtbfn^6*e%)0lY)w2P2k)zyEwE%~}y@2@7+&i#%bPJ#liT>hU9DqQI;tz5V#iju9pabT=3$O#n z0saTq03Kh!d~4vi2nuk2XZY6x2g3t^ebw==1dM7K0QG~`KqeZz1_1_$1IIKkPiOQV zekmLSqY4kCqs9Nr!@+|^z;rScHu4_9LwW0+&T zdEe}=0kM{5xD*RlUh7@&-DP+0)RzIko@cB8@$da_z5l+i|Milhm&hJ9dh7xMpAdMT*GK=s z(}Uv&hzrCX^l|X`zcBd(0e-rNaNIaC_yj`10B@|b{Zqcr-$g)xKi9$iwr^lB0Z9lw zuABcR@;tv=__%1Pk`El}cen>q32l9qc zBw8?gf1%@YgHR9Z4=5^J;8+P4?4G{xc^E_jf@01)tm2VkEkh=8RH9=ZSk diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/meta.txt b/assets/resources/dolphin/L3_Hijack_radio_128x64/meta.txt deleted file mode 100644 index 1d415b4b..00000000 --- a/assets/resources/dolphin/L3_Hijack_radio_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 8 -Frames order: 0 1 2 3 4 5 4 6 7 8 9 10 11 12 11 13 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_0.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_0.bm deleted file mode 100644 index db283e81fbb46514ec3a63dedb87bba98daeb086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 611 zcmV-p0-XH;0AB(C9v}oX06F|W|M~y;20#3`JpT|Kgajs8?>{Feln0y|4Y0(-C|H1b ze#7Yjgbl!hQGMmP^FfkZ{(4=*JIk|}|Q#BCT6`h2W`agbO8 zUe7-Od}#U4@H~1P4h3P}!192K27NKCgT@U8C!TZU4=M~g;13_bAXDh;6iC5=_{D9tfb60|+AxcxrGc1{wqGfrH#I zFh$UV$NuRUAk1~a@(CBh2dw^-#2!@PD1**04;T;f05=#8I0hgCg9FQe4>)8L9C!=} z{9rZ!dP0r`0JvZvKI!m1W`WXx!-Zq+;mlAa0)}!0#r}Z!AaVFSz#fwWuz&~_F!*F( z&`?Nc0vHcJ<_;Ga2dn}n4>*6q3viYd#bPmN1h5=j4j=vtFyjpbZXoffD~B~v1BMS0!Vg3qc>D(j1F-*q3?7~ge*j`T1L%MXMh`v)KESLHh=bRFH!b|) xaRBTC(n22@;1qe0==1{c4`bMf2Qi0(z%7CJ5D(+ROd0S!garY-UjV~52cXVC@<9Lq diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_1.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_1.bm deleted file mode 100644 index c600743700f179ba45f3713af0d427024c0b683c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 614 zcmV-s0-5~*0Ad0F9v}oX06F|W|M~y;20#3`JpT|Kgajs8FFofaln0y|4Y0(-C|H1b zeuL=&gblVi*Vs{7vFNSUds*4@7v6$R3bLgaSUV zF#4v(5Rn;sJp2Lj&VPa9(BN<@4)+I)1W+^Sjbt7$XfQo8K=Po&t^o1;0t9}2Cs^b# z5MX%#2x1@s2bDts1^HjW=ji{x1RkOF03G-r)({Yz#6QwFEXW@hfDq_6kWYm?3^Cvk z;1_}qJp>E*UmSqstPzL>unH*g!9eZ-fnbXWT4E0>2l(zh5kV*h5JnpC)ZkDIGzZxO z2e@Ehi=hXP{n9W&nCpY&5-)@gS^X)9JgLG_2b^FYFdyUqZZI5h3_u442bTaIaL6b) z@E8&Jz-$2Yg&Yb2aKJ!))8Kl|1Em3n3dh~Un4n4p4CD)o{Q>YmT9()Xafmk6C2d@Bb zTlvJ|0oVtmgg!ICDDxxH=mp>&$FUF&V-E*_TLbVQAIF54GvIp&3Ilk)0fuf5K-I|j AUjP6A diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_10.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_10.bm deleted file mode 100644 index 694302a9de7918f0009e738f417aee84929cc94a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 576 zcmV-G0>AwM06YQ!9v}oX06F|W|M~y;20#3`JpT|Kgajs8FFofaln0y|4Y0(-C|H1a z{(s=~fWiji4`F%WAQ1rjUtk&Vc?bqT4?=ju_#g0jNIn4(1Ld6mKx2@~WCOWRcd$IXkvtp&@n2DjkU{6tfMm3Sv7B52LevrY zgU1zU6^t^#@(=)wL;xW1;?zOo0|%$0|NozZ#!FIw90UR&2ERZjwde=N0uPG_eBrP! z@P0?-|HA*l?pz#HpjbYjc}IXlfP;g}B_y0cKUeTrjz$4sj6g3C1o(wV{Xc*(NrWvi ztUxmYvj0ch#Gn{Mlud(R1c^Xl;P>4E2^v4~ki;MmC;`4`{>`4n793gXT2=jDH}1^mv2X86<#s1EcsD2sA<>MHmz^P);C_ z@!(+hKM_1<_3cxEV0<=bAA8&^YMpFP7%pt(_jsnU6)DW1% zh3SCDumk|1^+<{YKnn{Z68!)T3&0ix064&W*c=DE2gD~q4FZ>7A20}z6te*NkV^oi O7ze{pY5-D3m;pc_5AB-( diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_11.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_11.bm deleted file mode 100644 index 246b955cf93c8b6abccaef6f44dd1b8fcf01e296..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 585 zcmV-P0=E4D07U`-9v}oX06F|W|M~y;20#3`JpT|Kgajs8?Qy>+`d;`bH6T!eM0P%w<1Ri}T21`kR2cO}nAoays z1!4~%0SrU{Ao1eVLF4$pgU`|b|IfkWC8+{O@m~j+0PoNV?RY*h%fLPCA@jh%>;d^7 zmBs*~5Cazn6)3=ef#)6p4gwAjER>Uh0fGMkVmTNEf-wLj3VeT`_-76P!4P=FBngp# zWDVEGEIQ0!9He2gc|SKmmpW#vuTKKp=xL2Z$g*0)HTTR-hpA4v0Ki zg9Hea2Rs810L%a+4`{>`4p8U;;6d*i1d2RL0uOj(kXAFnh{1uR5fHe*p^}1VqsIZm zfrH%qa1Q_^jv}!rAU$s%2pB!Z;s7b|2t*?wR*1|)>42l~90EraAeP1)4?l(-AV2|! zgvuN*KlUF0V-Ny|&w>9BkK_ITSXmIDAIxBNfCCG_76bqw`mi_;cn^q9f*J)bz&>CR XASq@6^B|T1N&FxOY7IaNNYel)t_A1i diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_12.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_12.bm deleted file mode 100644 index b6fb1130bb895889494bb5ade77a02bde7d64d3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 571 zcmV-B0>u3R05<{v9v}oX06F|W|M~y;20#3`JpT|Kgajs8?>{Feln0y|4Y0(-C|H1b zf5GVigbl{$PW`(gUcXM5qN{j$Y?L&5Mnlr2>m`&KzT+3=VWyFD^hl;o$Q4Kxh#Hc>DwBo`b-F<2Zqz;2w~8Fg*W?^nruNfdGyx%sy0bd3hl7 z2n4a7egOE<^PlJpav4m3cPZ}m2cMEBgMfZ3>M>FYJhPuD21`gQ8O6XPEkRGD0~Kf$ zj55IT5CDus03h+=)Is9|2dAU||DS`#OHzOw1Ogxizd$Fo=m*9E4~qzV;jl08en;j1 z!vDeUTpU!OSU#Y6M}R|sgM-T@B%DA$SMXSlMgd@qKravk_=QLPKW_?DSVGep!~-xZ zFZ6x9N&$p9MA$X}NR$R14}H)ek)!_^3_<|{fI$Xg4-i1WJ|FlPJ?l^qd4E6RvIdAm zpgG_ekBFo|_KZP5KREVHN0I;$lFVFzcya8Z91B?gFfxvsfd_r^(&?$BS^8kqfOE3?a1h5KGfP6Iu JpamppfE4a4{Feln0y|4Y0(-C|H1b zf5GVigblZNCreNFnGtz;#ePmJcH%p#C)WX z@VLR?5QvR~01sUxE&&9G1Ir*$5qN{k$Y&TQ9>x3~evkkDfAWB6JP|>8fE)Y|7=%=KG%$PcLE|5b#6J}9{zM^_zb^tK2MT9Le2aW^z$AG~B z;`StforZh`8I0jlDDeliXbLdlK-vT25P(3S5J8ycfTRx?z&;~TP&onw9xTBNh$LV! z=+Hdia{znBB#A=cKmqeuI0yhC~6P5ffOzqZ9xjyjs2bT_F4@nFI7?mIP z{sA9@mq;F$2y_GS!!3+4h#>XC3JV8-g~kEz0H-gD;RU!A$R3bNfD~Zg$SlGP4?xk! B`X>MY diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_2.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_2.bm deleted file mode 100644 index 1025137e462b44c2341e193471c8b457d4451c1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 618 zcmV-w0+sy%0A>OJ9v}oX06F|W|M~y;20#3`JpT|Kgajs8?>u;K^nyrYW-I1e0-Lm*#YJs^FyKMwk_VLr9dHMa;1D45={mx!Dc}Cya0zmyn=iw;9-vd zhXDr%rh*0hFOL9nRtUrb*aZ}L;GlO12ZI0!7=(ZxbPw^|cp&kBs0I*58t~NMPz*E& zVFL%dXkd$>2ao;IFhQ8>gX9t~gb#84DTq9&!chmDU>-1URk2L^%fAlv|X z!yusJz+hlI$PIuVkfVV>E*J<7+I|mNpmd-x;aKgt^AKo+Mj6N#7y1L>f!yPh0D4Rg z!T=yx&gKz=K|vv$2w*(_m^o`OLFoXAgU%oDi|{3daafF60W1d>1Bd^Ma5%$38;Cq= z3gdudj6(*8h6o-aQh;wT6fk&}5PBf<$KWU`;s1aP9-a(;0^tMTA4C9BFnRDX_5~QI zgdU@F-_AS&K?kISJ~O~5^CQ=Y12{qIdl3NUAo1rIhu}azj0_$DI0vwRpf`_!a7M%c EAd|oRBLDyZ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_3.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_3.bm deleted file mode 100644 index e623a1c0fa5cba36a8a2f364e19c96d49a644e13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 608 zcmV-m0-yZ>09*n99v}oX06F|W|M~y;20#3`JpT|Kgajs8?>{Feln0y|4Y0(-C|H1b ze#7Yjgbl!hQGMmP^FfkZ{(4=*JIk|}|Q#BCT6`h2W`agbO8 zUe7-Od}#U4@H~1P4h3P}!192K27NKCgT@U8C!TZU4=M~g;13_bAXDh;6iC5=tAQr$VqsIjUxHpA@EFo!#Jg6VzxbR>SMhK(?j5Xn@z@Qjt54r{qaKOPA zLje*Cq+o+F*9XWXUkDuRra#0%CzE6f=-6FZ2h%Ul;_y9+LyGfCv^pvlyBR z2@K#v0q6X|@!{b0fJDLP4@eEpSXUK@#h?7QS@!$>1e>g3O uJ!B#Ao&iUh9*jUOfc8CzfO8OdbOOvDfdKw6Fn9#m9>M~E-aG}tnS;Vi*Vs{7vFNSUds*4@7v6$R3bLgaSUV zF#4v(5Rn;sJp2Lj&VPa9(BN<@4)+I)1W+^Sjbt7$XfQo8K=Po&t^o1;0t9}2Cs^b# z5MX%#2x1@s2bDts1^HjW=ji{x1RkOF03G-r)({Yz#6QwFEXW@hfDq_6kWYm?3^Cvk z;1_}qJp>E*UmO7BtPzL>unH*g!9eZ>QUr`arXcd5e~#n9fKC`9kPsM1NMJPVh<{CltJehf$JbY$N=16IN%t74h#=206gK4 zP;uZeBk_RP0qF`j6awLZfZM0Q^_mAt0}d6ByMZx4lnNQh7Z>^i;DP(#a{ziw4#EH+ zSjXV;gF!(doCsh%|Cl&@Fdnc7m^|VC2wmb>R~3lGpc24wa5#VXEW?a65x9fKpsqLu zI36%)cwm9zB`5~-0mCl|VF#iQJbnX%1K5AS1`kgLKY{Ro><^*OJ9v}oX06F|W|M~y;20#3`JpT|Kgajs8?>u;K^nyrYW-I1e0-Lm*#YJs^FyKMwk_VLr9dHMa;1D45={mEOrk1|zUOhybKu^WbCb3c(17 zJ$M6h-_9ox4!}JmA@QC8N0}avKraCHJ&1sF7{Feln0y|4Y0(-C|H1b zf5GVigblZNCreNFnGtz;#ePmJcH%p#C)WX z@VLR?5QvR~01sUxE&&9G1Ir*$5qN{k$Y&TQ9>x3~evkkDfAWB6JP|>8fE)Y|7=%=KG%$PcLE|5b#6J}9{zM^_zb^tK2MT9Le2aW^z$AG~B z;`StforZh`8I0jlDDeliXbLdlK-vT25P(3S5J8ycfTRx?z&;~TP&onw9xTBNh$LV! z=+Hdia{znBB#A=cKmqeuI0yhC~6P5ffOzqZ9xjyjs2bT_F4@nFI7?mIP z{sA9@mq;F$2y_GS!!3+4h#>XC3JV8-g~kEz0H-gD;RU!A$R3bNfD~Zg$SlGP4?xk! B`X>MY diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_7.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_7.bm deleted file mode 100644 index c9b99a0144864d530c76fb9864ad23ebd5351253..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 585 zcmV-P0=E4D07U`-9v}oX06F|W|M~y;20#3`JpT|Kgajs8FFofaln0y|4Y0(-C|H1a z{(s=~fWiji4`F%WAQ1rjUtk&Vc?bqT4?=ju_#g0jNIn4(1Ld6mKx2@~WCFPFaCvzmcsK=M9x!DfgU_V^$!P^*4==+|N9GS4 zRiIWN@&FLTKmrdQEkqtai}*bKAOHOP9x_^xB!3m~d4LZ60G`)_;~cyL-ohU|3=Y5_ zk@;L;3LyY7aB)(M2lyUw;1J*-;PS~yI1m^g@D?MHfLJ3C0zjw7`TvG+;0zLB3ruSe z49ETl0O^aw6e02zP;f$bQAfyx~KJP1AGK#@m@Ktb;ek_yInF&Hp3f+80f6f#gv6nNk` za4>tHjsf5Vk;GOd1P86-;R6S_yg&s$0SJU-3eg#eeJ~V$1As{4gc8`pf#>kUqzC{o z@R>t}=l;Xs3}OIK`S3sC@%(?l3kxC?1Nn>&umE9r0>FR-A65qe?*Z`%&_h6_*ayr4 XBn2$MK4cQWDL;e&?LnvkNg7}UlQy?A6d%eNqrfDR ze?Q`~28cwUIp7$Nh@?RFj6p!-;A;pzV^9dk@(2G%h&`c_NC$vAKY@UQL?R?nfkPz( z;t3BP1`l)b6UKhh5m>|)B?JeeM4({z7mxs~0{Feln0y|4Y0(-C|H1b zf5GVigbl{$PW`(gUcXM5qN{j$Y?L&5Mnlr2>m`&KzT+3=VWyFD^hl;o$Q4Kxh#Hc>DwBo`b-F<2Zqz;2w~8Fg*W?^nruNfdGyx%sy0bd3hl7 z2n4a7egOE<^PlJpav4m3R~_sRKO|2F0IUPX45SiyXFgC2mXKB;^87UgK9CGmpjIIA z01(7L0uLT7L>@nj_&og||NQ(OGFp%%e--d~fDZitp4Wrp9J~YG!XG>g4!|Fg`CMQM zApkLOaZ-#2_#ScK5a1x-^2td!5Eviu79){>SR)VuK&Qv~|Auhe!j%>fw8pUj%zxl; z4w$?_LZBEzSWSaq1dIY`4~@_ufCCH%j6wkdfI$Xg4-i0r1pYwwtw2HK9T0f41_%)- z4tNG20hj9HGzyz=Pg22^4sg1Rn6oAgpJD5rYFrA|Y{sLnQ>!M~(xB0|&YI z;2r=;97SSKKziOj5HNd-!~j#^5Qs)Vtr3`q(*Z}|I0TL;K`o3p9)AovK!5`e36wZq zf9yU0#vla`p9B6MAIJOvu(BaQKbXMl00tL;EC>KW^4} flash pages (last page {last_page_state:.02f}% full)" + fg.yellow( + f"{os.path.basename(self.args.binname):<11}: {pages:>4} flash pages (last page {last_page_state:.02f}% full)" + ) ) return 0 diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index cb4f8f5a..4c042789 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -7,6 +7,7 @@ from update import Main as UpdateMain import shutil import zipfile import tarfile +from ansi.color import fg class ProjectDir: @@ -126,7 +127,7 @@ class Main(App): self.copy_single_project(project) self.logger.info( - f"Firmware binaries can be found at:\n\t{self.output_dir_path}" + fg.green(f"Firmware binaries can be found at:\n\t{self.output_dir_path}") ) if self.args.version: @@ -162,7 +163,9 @@ class Main(App): if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0: self.logger.info( - f"Use this directory to self-update your Flipper:\n\t{bundle_dir}" + fg.green( + f"Use this directory to self-update your Flipper:\n\t{bundle_dir}" + ) ) # Create tgz archive diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 5eaf16f9..a11a3ccd 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,8 +13,8 @@ if not [%FBT_NOENV%] == [] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=15" -set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\i686-windows" +set "FLIPPER_TOOLCHAIN_VERSION=16" +set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows" if not exist "%FBT_TOOLCHAIN_ROOT%" ( diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 7c501803..15f29e4d 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -5,7 +5,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"15"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"16"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; fbtenv_show_usage() diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 index bcb8f998..370f1a14 100644 --- a/scripts/toolchain/windows-toolchain-download.ps1 +++ b/scripts/toolchain/windows-toolchain-download.ps1 @@ -3,13 +3,13 @@ $ErrorActionPreference = "Stop" [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" $repo_root = (Get-Item "$PSScriptRoot\..\..").FullName $toolchain_version = $args[0] -$toolchain_url = "https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-i686-windows-flipper-$toolchain_version.zip" -$toolchain_zip = "gcc-arm-none-eabi-10.3-i686-windows-flipper-$toolchain_version.zip" -$toolchain_dir = "gcc-arm-none-eabi-10.3-i686-windows-flipper" +$toolchain_url = "https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-windows-flipper-$toolchain_version.zip" +$toolchain_zip = "gcc-arm-none-eabi-10.3-x86_64-windows-flipper-$toolchain_version.zip" +$toolchain_dir = "gcc-arm-none-eabi-10.3-x86_64-windows-flipper" -if (Test-Path -LiteralPath "$repo_root\toolchain\i686-windows") { +if (Test-Path -LiteralPath "$repo_root\toolchain\x86_64-windows") { Write-Host -NoNewline "Removing old Windows toolchain.." - Remove-Item -LiteralPath "$repo_root\toolchain\i686-windows" -Force -Recurse + Remove-Item -LiteralPath "$repo_root\toolchain\x86_64-windows" -Force -Recurse Write-Host "done!" } if (!(Test-Path -Path "$repo_root\$toolchain_zip" -PathType Leaf)) { @@ -26,7 +26,7 @@ if (!(Test-Path -LiteralPath "$repo_root\toolchain")) { Write-Host -NoNewline "Unziping Windows toolchain.." Add-Type -Assembly "System.IO.Compression.Filesystem" [System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip", "$repo_root\") -Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\i686-windows" +Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\x86_64-windows" Write-Host "done!" Write-Host -NoNewline "Clearing temporary files.." diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 324dfe33..cadb417f 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -171,6 +171,11 @@ vars.AddVariables( "Blackmagic probe location", "auto", ), + ( + "OPENOCD_ADAPTER_SERIAL", + "OpenOCD adapter serial number", + "auto", + ), ( "UPDATE_SPLASH", "Directory name with slideshow frames to render after installing update package", diff --git a/site_scons/environ.scons b/site_scons/environ.scons index 2d082838..94705dad 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -35,6 +35,7 @@ for env_value_name in variables_to_forward: coreenv = VAR_ENV.Clone( tools=[ + "fbt_tweaks", ( "crosscc", { @@ -80,8 +81,9 @@ if not coreenv["VERBOSE"]: # Default value for commandline options SetOption("num_jobs", multiprocessing.cpu_count()) +## NB - disabled both caches since they seem to do more harm then good in our case # Avoiding re-scan of all sources on every startup -SetOption("implicit_cache", True) +# SetOption("implicit_cache", True) # SetOption("implicit_deps_unchanged", True) # More aggressive caching SetOption("max_drift", 1) diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 9edc6b5c..ee317be3 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -65,6 +65,7 @@ extapps = appenv["_extapps"] = { "debug": {}, "validators": {}, "dist": {}, + "resources_dist": None, } @@ -108,6 +109,8 @@ appenv.PhonyTarget("firmware_extapps", appenv.Action(legacy_app_build_stub, None Alias("faps", extapps["compact"].values()) Alias("faps", extapps["validators"].values()) +extapps["resources_dist"] = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) + if appsrc := appenv.subst("$APPSRC"): app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc) appenv.PhonyTarget( diff --git a/site_scons/fbt_extra/util.py b/site_scons/fbt_extra/util.py index aa3d50b6..c670c01d 100644 --- a/site_scons/fbt_extra/util.py +++ b/site_scons/fbt_extra/util.py @@ -1,10 +1,11 @@ from fbt.util import link_dir +from ansi.color import fg def link_elf_dir_as_latest(env, elf_node): elf_dir = elf_node.Dir(".") latest_dir = env.Dir("#build/latest") - print(f"Setting {elf_dir} as latest built dir (./build/latest/)") + print(fg.green(f"Linking {elf_dir} as latest built dir (./build/latest/)")) return link_dir(latest_dir.abspath, elf_dir.abspath, env["PLATFORM"] == "win32") @@ -12,7 +13,7 @@ def should_gen_cdb_and_link_dir(env, requested_targets): explicitly_building_updater = False # Hacky way to check if updater-related targets were requested for build_target in requested_targets: - if "updater" in str(build_target): + if "updater" in str(build_target) and "package" not in str(build_target): explicitly_building_updater = True is_updater = not env["IS_BASE_FIRMWARE"] diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 2d83d881..b763a3a7 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -3,6 +3,7 @@ from SCons.Script import GetBuildFailures import sys import os import atexit +from ansi.color import fg, fx sys.path.insert(0, os.path.join(os.getcwd(), "scripts")) sys.path.insert(0, os.path.join(os.getcwd(), "lib/cxxheaderparser")) @@ -16,12 +17,12 @@ def bf_to_str(bf): if bf is None: # unknown targets product None in list return "(unknown tgt)" elif isinstance(bf, SCons.Errors.StopError): - return str(bf) + return fg.yellow(str(bf)) elif bf.node: - return str(bf.node) + ": " + bf.errstr + return fg.yellow(str(bf.node)) + ": " + bf.errstr elif bf.filename: - return bf.filename + ": " + bf.errstr - return "unknown failure: " + bf.errstr + return fg.yellow(bf.filename) + ": " + bf.errstr + return fg.yellow("unknown failure: ") + bf.errstr def display_build_status(): @@ -31,10 +32,9 @@ def display_build_status(): if bf: # bf is normally a list of build failures; if an element is None, # it's because of a target that scons doesn't know anything about. - failures_message = "\n".join( - ["Failed building %s" % bf_to_str(x) for x in bf if x is not None] - ) - print("*" * 10, "ERRORS", "*" * 10) + failures_message = "\n".join([bf_to_str(x) for x in bf if x is not None]) + print() + print(fg.brightred(fx.bold("*" * 10 + " FBT ERRORS " + "*" * 10))) print(failures_message) From 30f10dce80300a2a308e323e70cb1961f86b60cc Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Wed, 26 Oct 2022 01:44:27 +0300 Subject: [PATCH 21/42] Fix a typo in the factory reset screen "setting" -> "settings" #1917 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../scenes/storage_settings_scene_factory_reset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c b/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c index 64d2b96b..865ee48d 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c @@ -24,7 +24,7 @@ void storage_settings_scene_factory_reset_on_enter(void* context) { dialog_ex_set_header(dialog_ex, "Confirm Factory Reset", 64, 10, AlignCenter, AlignCenter); dialog_ex_set_text( dialog_ex, - "Internal storage will be erased\r\nData and setting will be lost!", + "Internal storage will be erased\r\nData and settings will be lost!", 64, 32, AlignCenter, From d530238faeae6cf0b9ac2229ed7bead090ee1d09 Mon Sep 17 00:00:00 2001 From: Anna Prosvetova Date: Wed, 26 Oct 2022 00:48:33 +0200 Subject: [PATCH 22/42] CI: Update web updater domain (#1919) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7d616295..ed5ee6bd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -127,7 +127,7 @@ jobs: **Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:** - [📦 Update package](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz) - [📥 DFU file](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-full-${{steps.names.outputs.suffix}}.dfu) - - [☁️ Web/App updater](https://my.flipp.dev/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz&channel=${{steps.names.outputs.branch_name}}&version=${{steps.names.outputs.commit_sha}}) + - [☁️ Web/App updater](https://lab.flipper.net/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz&channel=${{steps.names.outputs.branch_name}}&version=${{steps.names.outputs.commit_sha}}) edit-mode: replace compact: From 7d2d2b3dd9e04ca61a88781a5dd229974f1de58d Mon Sep 17 00:00:00 2001 From: gornekich Date: Wed, 26 Oct 2022 02:57:06 +0400 Subject: [PATCH 23/42] [FL-2932] TikTok: reset cursor after enter and reconnect #1921 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../plugins/bt_hid_app/views/bt_hid_tiktok.c | 179 ++++++++++-------- 1 file changed, 101 insertions(+), 78 deletions(-) diff --git a/applications/plugins/bt_hid_app/views/bt_hid_tiktok.c b/applications/plugins/bt_hid_app/views/bt_hid_tiktok.c index 9af00157..fe1005b2 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_tiktok.c +++ b/applications/plugins/bt_hid_app/views/bt_hid_tiktok.c @@ -17,6 +17,7 @@ typedef struct { bool down_pressed; bool ok_pressed; bool connected; + bool is_cursor_set; } BtHidTikTokModel; static void bt_hid_tiktok_draw_callback(Canvas* canvas, void* context) { @@ -88,48 +89,48 @@ static void bt_hid_tiktok_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); } -static void bt_hid_tiktok_process_press(BtHidTikTok* bt_hid_tiktok, InputEvent* event) { - with_view_model( - bt_hid_tiktok->view, - BtHidTikTokModel * model, - { - if(event->key == InputKeyUp) { - model->up_pressed = true; - } else if(event->key == InputKeyDown) { - model->down_pressed = true; - } else if(event->key == InputKeyLeft) { - model->left_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT); - } else if(event->key == InputKeyRight) { - model->right_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); - } else if(event->key == InputKeyOk) { - model->ok_pressed = true; - } - }, - true); +static void bt_hid_tiktok_reset_cursor() { + // Set cursor to the phone's left up corner + // Delays to guarantee one packet per connection interval + for(size_t i = 0; i < 8; i++) { + furi_hal_bt_hid_mouse_move(-127, -127); + furi_delay_ms(50); + } + // Move cursor from the corner + furi_hal_bt_hid_mouse_move(20, 120); + furi_delay_ms(50); } -static void bt_hid_tiktok_process_release(BtHidTikTok* bt_hid_tiktok, InputEvent* event) { - with_view_model( - bt_hid_tiktok->view, - BtHidTikTokModel * model, - { - if(event->key == InputKeyUp) { - model->up_pressed = false; - } else if(event->key == InputKeyDown) { - model->down_pressed = false; - } else if(event->key == InputKeyLeft) { - model->left_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT); - } else if(event->key == InputKeyRight) { - model->right_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); - } else if(event->key == InputKeyOk) { - model->ok_pressed = false; - } - }, - true); +static void bt_hid_tiktok_process_press(BtHidTikTokModel* model, InputEvent* event) { + if(event->key == InputKeyUp) { + model->up_pressed = true; + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + } +} + +static void bt_hid_tiktok_process_release(BtHidTikTokModel* model, InputEvent* event) { + if(event->key == InputKeyUp) { + model->up_pressed = false; + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + } } static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) { @@ -137,43 +138,59 @@ static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) { BtHidTikTok* bt_hid_tiktok = context; bool consumed = false; - if(event->type == InputTypePress) { - bt_hid_tiktok_process_press(bt_hid_tiktok, event); - consumed = true; - } else if(event->type == InputTypeRelease) { - bt_hid_tiktok_process_release(bt_hid_tiktok, event); - consumed = true; - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyOk) { - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); - consumed = true; - } else if(event->key == InputKeyUp) { - // Emulate up swipe - furi_hal_bt_hid_mouse_scroll(-6); - furi_hal_bt_hid_mouse_scroll(-12); - furi_hal_bt_hid_mouse_scroll(-19); - furi_hal_bt_hid_mouse_scroll(-12); - furi_hal_bt_hid_mouse_scroll(-6); - consumed = true; - } else if(event->key == InputKeyDown) { - // Emulate down swipe - furi_hal_bt_hid_mouse_scroll(6); - furi_hal_bt_hid_mouse_scroll(12); - furi_hal_bt_hid_mouse_scroll(19); - furi_hal_bt_hid_mouse_scroll(12); - furi_hal_bt_hid_mouse_scroll(6); - consumed = true; - } else if(event->key == InputKeyBack) { - furi_hal_bt_hid_consumer_key_release_all(); - consumed = true; - } - } + with_view_model( + bt_hid_tiktok->view, + BtHidTikTokModel * model, + { + if(event->type == InputTypePress) { + bt_hid_tiktok_process_press(model, event); + if(model->connected && !model->is_cursor_set) { + bt_hid_tiktok_reset_cursor(); + model->is_cursor_set = true; + } + consumed = true; + } else if(event->type == InputTypeRelease) { + bt_hid_tiktok_process_release(model, event); + consumed = true; + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); + consumed = true; + } else if(event->key == InputKeyUp) { + // Emulate up swipe + furi_hal_bt_hid_mouse_scroll(-6); + furi_hal_bt_hid_mouse_scroll(-12); + furi_hal_bt_hid_mouse_scroll(-19); + furi_hal_bt_hid_mouse_scroll(-12); + furi_hal_bt_hid_mouse_scroll(-6); + consumed = true; + } else if(event->key == InputKeyDown) { + // Emulate down swipe + furi_hal_bt_hid_mouse_scroll(6); + furi_hal_bt_hid_mouse_scroll(12); + furi_hal_bt_hid_mouse_scroll(19); + furi_hal_bt_hid_mouse_scroll(12); + furi_hal_bt_hid_mouse_scroll(6); + consumed = true; + } else if(event->key == InputKeyBack) { + furi_hal_bt_hid_consumer_key_release_all(); + consumed = true; + } + } else if(event->type == InputTypeLong) { + if(event->key == InputKeyBack) { + furi_hal_bt_hid_consumer_key_release_all(); + model->is_cursor_set = false; + consumed = false; + } + } + }, + true); return consumed; } @@ -203,5 +220,11 @@ View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok) { void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected) { furi_assert(bt_hid_tiktok); with_view_model( - bt_hid_tiktok->view, BtHidTikTokModel * model, { model->connected = connected; }, true); + bt_hid_tiktok->view, + BtHidTikTokModel * model, + { + model->connected = connected; + model->is_cursor_set = false; + }, + true); } From 406d830fb652bb0a8791efd28357bb50e1f9640a Mon Sep 17 00:00:00 2001 From: Max Lapan Date: Wed, 26 Oct 2022 15:37:58 +0200 Subject: [PATCH 24/42] Oregon2 extra (#1924) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add support for temp-humid sensor 0x1D20 * Fix protocol type and flags, humidity decoding Co-authored-by: あく --- .../weather_station/protocols/oregon2.c | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/applications/plugins/weather_station/protocols/oregon2.c b/applications/plugins/weather_station/protocols/oregon2.c index c4e4471a..76bc3f0a 100644 --- a/applications/plugins/weather_station/protocols/oregon2.c +++ b/applications/plugins/weather_station/protocols/oregon2.c @@ -19,7 +19,7 @@ static const SubGhzBlockConst ws_oregon2_const = { }; #define OREGON2_PREAMBLE_BITS 19 -#define OREGON2_PREAMBLE_MASK ((1 << (OREGON2_PREAMBLE_BITS + 1)) - 1) +#define OREGON2_PREAMBLE_MASK 0b1111111111111111111 #define OREGON2_SENSOR_ID(d) (((d) >> 16) & 0xFFFF) #define OREGON2_CHECKSUM_BITS 8 @@ -103,6 +103,7 @@ static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration // From sensor id code return amount of bits in variable section static uint8_t oregon2_sensor_id_var_bits(uint16_t sensor_id) { if(sensor_id == 0xEC40) return 16; + if(sensor_id == 0x1D20) return 24; return 0; } @@ -119,18 +120,30 @@ static void ws_oregon2_decode_const_data(WSBlockGeneric* ws_block) { ws_block->battery_low = (ws_block->data & OREGON2_FLAG_BAT_LOW) ? 1 : 0; } -static void - ws_oregon2_decode_var_data(WSBlockGeneric* ws_block, uint16_t sensor_id, uint32_t var_data) { - int16_t temp_val; - if(sensor_id == 0xEC40) { - temp_val = ((var_data >> 4) & 0xF) * 10 + ((var_data >> 8) & 0xF); - temp_val *= 10; - temp_val += (var_data >> 12) & 0xF; - if(var_data & 0xF) temp_val = -temp_val; - } else - return; +uint16_t bcd_decode_short(uint32_t data) { + return (data & 0xF) * 10 + ((data >> 4) & 0xF); +} - ws_block->temp = (float)temp_val / 10.0; +static float ws_oregon2_decode_temp(uint32_t data) { + int32_t temp_val; + temp_val = bcd_decode_short(data >> 4); + temp_val *= 10; + temp_val += (data >> 12) & 0xF; + if(data & 0xF) temp_val = -temp_val; + return (float)temp_val / 10.0; +} + +static void ws_oregon2_decode_var_data(WSBlockGeneric* ws_b, uint16_t sensor_id, uint32_t data) { + switch(sensor_id) { + case 0xEC40: + ws_b->temp = ws_oregon2_decode_temp(data); + ws_b->humidity = WS_NO_HUMIDITY; + break; + case 0x1D20: + ws_b->humidity = bcd_decode_short(data); + ws_b->temp = ws_oregon2_decode_temp(data >> 8); + break; + } } void ws_protocol_decoder_oregon2_feed(void* context, bool level, uint32_t duration) { @@ -346,9 +359,8 @@ const SubGhzProtocolDecoder ws_protocol_oregon2_decoder = { const SubGhzProtocol ws_protocol_oregon2 = { .name = WS_PROTOCOL_OREGON2_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, .decoder = &ws_protocol_oregon2_decoder, }; From 378bf050684b9e56d71c84fd2e44a57caa507ee2 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 26 Oct 2022 18:56:54 +0400 Subject: [PATCH 25/42] [FL-2934] WS: add protocol Acurite 592TXR (#1916) * WS: add protocol Acurite 592TXR * WS: fix decoder Acurite_592TXR * SubGhz: proper types in math Co-authored-by: Aleksandr Kutuzov --- .../helpers/weather_station_types.h | 2 +- .../protocols/acurite_592txr.c | 307 ++++++++++++++++++ .../protocols/acurite_592txr.h | 79 +++++ .../protocols/protocol_items.c | 1 + .../protocols/protocol_items.h | 1 + firmware/targets/f7/api_symbols.csv | 6 +- lib/subghz/blocks/math.c | 30 ++ lib/subghz/blocks/math.h | 31 ++ 8 files changed, 455 insertions(+), 2 deletions(-) create mode 100644 applications/plugins/weather_station/protocols/acurite_592txr.c create mode 100644 applications/plugins/weather_station/protocols/acurite_592txr.h diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h index 275d2332..479638b9 100644 --- a/applications/plugins/weather_station/helpers/weather_station_types.h +++ b/applications/plugins/weather_station/helpers/weather_station_types.h @@ -3,7 +3,7 @@ #include #include -#define WS_VERSION_APP "0.2" +#define WS_VERSION_APP "0.3" #define WS_DEVELOPED "SkorP" #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" diff --git a/applications/plugins/weather_station/protocols/acurite_592txr.c b/applications/plugins/weather_station/protocols/acurite_592txr.c new file mode 100644 index 00000000..db05af09 --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_592txr.c @@ -0,0 +1,307 @@ +#include "acurite_592txr.h" + +#define TAG "WSProtocolAcurite_592TXR" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c + * + * Acurite 592TXR Temperature Humidity sensor decoder + * Message Type 0x04, 7 bytes + * | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | + * | --------- | --------- | --------- | --------- | --------- | --------- | --------- | + * | CCII IIII | IIII IIII | pB00 0100 | pHHH HHHH | p??T TTTT | pTTT TTTT | KKKK KKKK | + * - C: Channel 00: C, 10: B, 11: A, (01 is invalid) + * - I: Device ID (14 bits) + * - B: Battery, 1 is battery OK, 0 is battery low + * - M: Message type (6 bits), 0x04 + * - T: Temperature Celsius (11 - 14 bits?), + 1000 * 10 + * - H: Relative Humidity (%) (7 bits) + * - K: Checksum (8 bits) + * - p: Parity bit + * Notes: + * - Temperature + * - Encoded as Celsius + 1000 * 10 + * - only 11 bits needed for specified range -40 C to 70 C (-40 F - 158 F) + * - However 14 bits available for temperature, giving possible range of -100 C to 1538.4 C + * - @todo - check if high 3 bits ever used for anything else + * + */ + +static const SubGhzBlockConst ws_protocol_acurite_592txr_const = { + .te_short = 200, + .te_long = 400, + .te_delta = 90, + .min_count_bit_for_found = 56, +}; + +struct WSProtocolDecoderAcurite_592TXR { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderAcurite_592TXR { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + Acurite_592TXRDecoderStepReset = 0, + Acurite_592TXRDecoderStepCheckPreambule, + Acurite_592TXRDecoderStepSaveDuration, + Acurite_592TXRDecoderStepCheckDuration, +} Acurite_592TXRDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_acurite_592txr_decoder = { + .alloc = ws_protocol_decoder_acurite_592txr_alloc, + .free = ws_protocol_decoder_acurite_592txr_free, + + .feed = ws_protocol_decoder_acurite_592txr_feed, + .reset = ws_protocol_decoder_acurite_592txr_reset, + + .get_hash_data = ws_protocol_decoder_acurite_592txr_get_hash_data, + .serialize = ws_protocol_decoder_acurite_592txr_serialize, + .deserialize = ws_protocol_decoder_acurite_592txr_deserialize, + .get_string = ws_protocol_decoder_acurite_592txr_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_acurite_592txr_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_acurite_592txr = { + .name = WS_PROTOCOL_ACURITE_592TXR_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_acurite_592txr_decoder, + .encoder = &ws_protocol_acurite_592txr_encoder, +}; + +void* ws_protocol_decoder_acurite_592txr_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderAcurite_592TXR* instance = malloc(sizeof(WSProtocolDecoderAcurite_592TXR)); + instance->base.protocol = &ws_protocol_acurite_592txr; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_acurite_592txr_free(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + free(instance); +} + +void ws_protocol_decoder_acurite_592txr_reset(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; +} + +static bool ws_protocol_acurite_592txr_check_crc(WSProtocolDecoderAcurite_592TXR* instance) { + uint8_t msg[] = { + instance->decoder.decode_data >> 48, + instance->decoder.decode_data >> 40, + instance->decoder.decode_data >> 32, + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8}; + + if((subghz_protocol_blocks_add_bytes(msg, 6) == + (uint8_t)(instance->decoder.decode_data & 0xFF)) && + (!subghz_protocol_blocks_parity_bytes(&msg[2], 4))) { + return true; + } else { + return false; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_acurite_592txr_remote_controller(WSBlockGeneric* instance) { + uint8_t channel[] = {3, 0, 2, 1}; + uint8_t channel_raw = ((instance->data >> 54) & 0x03); + instance->channel = channel[channel_raw]; + instance->id = (instance->data >> 40) & 0x3FFF; + instance->battery_low = !((instance->data >> 38) & 1); + instance->humidity = (instance->data >> 24) & 0x7F; + + uint16_t temp_raw = ((instance->data >> 9) & 0xF80) | ((instance->data >> 8) & 0x7F); + instance->temp = ((float)(temp_raw)-1000) / 10.0f; + + instance->btn = WS_NO_BTN; +} + +void ws_protocol_decoder_acurite_592txr_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + + switch(instance->decoder.parser_step) { + case Acurite_592TXRDecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_short * 3) < + ws_protocol_acurite_592txr_const.te_delta * 2)) { + instance->decoder.parser_step = Acurite_592TXRDecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case Acurite_592TXRDecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF( + instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_short * 3) < + ws_protocol_acurite_592txr_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_short * 3) < + ws_protocol_acurite_592txr_const.te_delta * 2)) { + //Found preambule + instance->header_count++; + } else if((instance->header_count > 2) && (instance->header_count < 5)) { + if((DURATION_DIFF( + instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_short) < + ws_protocol_acurite_592txr_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_long) < + ws_protocol_acurite_592txr_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Acurite_592TXRDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_long) < + ws_protocol_acurite_592txr_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_short) < + ws_protocol_acurite_592txr_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Acurite_592TXRDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; + } + } else { + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; + } + } + break; + + case Acurite_592TXRDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Acurite_592TXRDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; + } + break; + + case Acurite_592TXRDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)ws_protocol_acurite_592txr_const.te_short * 5)) { + if((instance->decoder.decode_count_bit == + ws_protocol_acurite_592txr_const.min_count_bit_for_found) && + ws_protocol_acurite_592txr_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_acurite_592txr_remote_controller(&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 = Acurite_592TXRDecoderStepReset; + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_short) < + ws_protocol_acurite_592txr_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_long) < + ws_protocol_acurite_592txr_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Acurite_592TXRDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_long) < + ws_protocol_acurite_592txr_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_short) < + ws_protocol_acurite_592txr_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Acurite_592TXRDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; + } + } else { + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_acurite_592txr_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_acurite_592txr_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_acurite_592txr_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_acurite_592txr_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%d.%d C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (int16_t)instance->generic.temp, + abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/acurite_592txr.h b/applications/plugins/weather_station/protocols/acurite_592txr.h new file mode 100644 index 00000000..ac0371d8 --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_592txr.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_ACURITE_592TXR_NAME "Acurite 592TXR" + +typedef struct WSProtocolDecoderAcurite_592TXR WSProtocolDecoderAcurite_592TXR; +typedef struct WSProtocolEncoderAcurite_592TXR WSProtocolEncoderAcurite_592TXR; + +extern const SubGhzProtocolDecoder ws_protocol_acurite_592txr_decoder; +extern const SubGhzProtocolEncoder ws_protocol_acurite_592txr_encoder; +extern const SubGhzProtocol ws_protocol_acurite_592txr; + +/** + * Allocate WSProtocolDecoderAcurite_592TXR. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderAcurite_592TXR* pointer to a WSProtocolDecoderAcurite_592TXR instance + */ +void* ws_protocol_decoder_acurite_592txr_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderAcurite_592TXR. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + */ +void ws_protocol_decoder_acurite_592txr_free(void* context); + +/** + * Reset decoder WSProtocolDecoderAcurite_592TXR. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + */ +void ws_protocol_decoder_acurite_592txr_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_acurite_592txr_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_acurite_592txr_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderAcurite_592TXR. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_acurite_592txr_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderAcurite_592TXR. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_acurite_592txr_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + * @param output Resulting text + */ +void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/plugins/weather_station/protocols/protocol_items.c index 0740691a..3ec9e995 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.c +++ b/applications/plugins/weather_station/protocols/protocol_items.c @@ -8,6 +8,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_acurite_606tx, &ws_protocol_lacrosse_tx141thbv2, &ws_protocol_oregon2, + &ws_protocol_acurite_592txr, }; const SubGhzProtocolRegistry weather_station_protocol_registry = { diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/plugins/weather_station/protocols/protocol_items.h index edc078cd..8f3eb53d 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.h +++ b/applications/plugins/weather_station/protocols/protocol_items.h @@ -8,5 +8,6 @@ #include "acurite_606tx.h" #include "lacrosse_tx141thbv2.h" #include "oregon2.h" +#include "acurite_592txr.h" extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 4c48d9ab..17a8a675 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,5.0,, +Version,+,5.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2271,6 +2271,7 @@ Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, u Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" +Function,+,subghz_protocol_blocks_add_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_crc16,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" Function,+,subghz_protocol_blocks_crc16lsb,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" Function,+,subghz_protocol_blocks_crc4,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" @@ -2284,8 +2285,11 @@ Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDur Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], int, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_parity8,int,uint8_t +Function,+,subghz_protocol_blocks_parity_bytes,int,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" +Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" Function,-,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" diff --git a/lib/subghz/blocks/math.c b/lib/subghz/blocks/math.c index 588d4eff..d2b8e3d1 100644 --- a/lib/subghz/blocks/math.c +++ b/lib/subghz/blocks/math.c @@ -216,4 +216,34 @@ uint16_t subghz_protocol_blocks_lfsr_digest16( } } return sum; +} + +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t num_bytes) { + int result = 0; + for(size_t i = 0; i < num_bytes; ++i) { + result += message[i]; + } + return (uint8_t)result; +} + +int subghz_protocol_blocks_parity8(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x6996 >> byte) & 1; +} + +int subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t num_bytes) { + int result = 0; + for(size_t i = 0; i < num_bytes; ++i) { + result ^= subghz_protocol_blocks_parity8(message[i]); + } + return result; +} + +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t num_bytes) { + uint8_t result = 0; + for(size_t i = 0; i < num_bytes; ++i) { + result ^= message[i]; + } + return result; } \ No newline at end of file diff --git a/lib/subghz/blocks/math.h b/lib/subghz/blocks/math.h index 27588948..8cddf4c0 100644 --- a/lib/subghz/blocks/math.h +++ b/lib/subghz/blocks/math.h @@ -161,6 +161,37 @@ uint16_t subghz_protocol_blocks_lfsr_digest16( uint16_t gen, uint16_t key); +/** + * Compute Addition of a number of bytes. + * @param message bytes of message data + * @param num_bytes number of bytes to sum + * @return summation value + **/ +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t num_bytes); + +/** + * Compute bit parity of a single byte (8 bits). + * @param byte single byte to check + * @return 1 odd parity, 0 even parity + **/ +int subghz_protocol_blocks_parity8(uint8_t byte); + +/** + * Compute bit parity of a number of bytes. + * @param message bytes of message data + * @param num_bytes number of bytes to sum + * @return 1 odd parity, 0 even parity + **/ +int subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t num_bytes); + +/** + * Compute XOR (byte-wide parity) of a number of bytes. + * @param message bytes of message data + * @param num_bytes number of bytes to sum + * @return summation value, per bit-position 1 odd parity, 0 even parity + **/ +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t num_bytes); + #ifdef __cplusplus } #endif From 5c8df66b7c2d10c4e17c831ebf9852d5690205fc Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 26 Oct 2022 19:13:00 +0400 Subject: [PATCH 26/42] [FL-2927] SubGhz: add RAW Read threshold rssi (#1911) * SubGhz: add RAW Read threshold rssi * SubGhz: update indicator RSSI * SubGhz: fix record file Co-authored-by: Aleksandr Kutuzov --- .../subghz/scenes/subghz_scene_read_raw.c | 50 ++++++++++-- .../scenes/subghz_scene_receiver_config.c | 52 +++++++++++++ applications/main/subghz/subghz.c | 1 + applications/main/subghz/subghz_i.h | 3 + .../main/subghz/views/subghz_read_raw.c | 77 ++++++++++++++++--- .../main/subghz/views/subghz_read_raw.h | 7 +- firmware/targets/f7/api_symbols.csv | 1 + lib/subghz/protocols/raw.c | 12 ++- lib/subghz/protocols/raw.h | 7 ++ 9 files changed, 192 insertions(+), 18 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 5dacefd6..8ac9bf5b 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -6,6 +6,7 @@ #define RAW_FILE_NAME "Raw_signal_" #define TAG "SubGhzSceneReadRAW" +#define RAW_THRESHOLD_RSSI_LOW_COUNT 10 bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { bool ret = false; @@ -72,24 +73,33 @@ void subghz_scene_read_raw_on_enter(void* context) { switch(subghz->txrx->rx_key_state) { case SubGhzRxKeyStateBack: - subghz_read_raw_set_status(subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, ""); + subghz_read_raw_set_status( + subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", subghz->txrx->raw_threshold_rssi); break; case SubGhzRxKeyStateRAWLoad: path_extract_filename(subghz->file_path, file_name, true); subghz_read_raw_set_status( subghz->subghz_read_raw, SubGhzReadRAWStatusLoadKeyTX, - furi_string_get_cstr(file_name)); + furi_string_get_cstr(file_name), + subghz->txrx->raw_threshold_rssi); subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; break; case SubGhzRxKeyStateRAWSave: path_extract_filename(subghz->file_path, file_name, true); subghz_read_raw_set_status( - subghz->subghz_read_raw, SubGhzReadRAWStatusSaveKey, furi_string_get_cstr(file_name)); + subghz->subghz_read_raw, + SubGhzReadRAWStatusSaveKey, + furi_string_get_cstr(file_name), + subghz->txrx->raw_threshold_rssi); subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; break; default: - subghz_read_raw_set_status(subghz->subghz_read_raw, SubGhzReadRAWStatusStart, ""); + subghz_read_raw_set_status( + subghz->subghz_read_raw, + SubGhzReadRAWStatusStart, + "", + subghz->txrx->raw_threshold_rssi); subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; break; } @@ -273,7 +283,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if(subghz->txrx->rx_key_state != SubGhzRxKeyStateIDLE) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); } else { - //subghz_get_preset_name(subghz, subghz->error_str); + subghz->txrx->raw_threshold_rssi_low_count = RAW_THRESHOLD_RSSI_LOW_COUNT; if(subghz_protocol_raw_save_to_file_init( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, RAW_FILE_NAME, @@ -319,7 +329,35 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz->subghz_read_raw, subghz_protocol_raw_get_sample_write( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result)); - subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi()); + + float rssi = furi_hal_subghz_get_rssi(); + + if(subghz->txrx->raw_threshold_rssi == SUBGHZ_RAW_TRESHOLD_MIN) { + subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true); + subghz_protocol_raw_save_to_file_pause( + (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false); + } else { + if(rssi < subghz->txrx->raw_threshold_rssi) { + subghz->txrx->raw_threshold_rssi_low_count++; + if(subghz->txrx->raw_threshold_rssi_low_count > RAW_THRESHOLD_RSSI_LOW_COUNT) { + subghz->txrx->raw_threshold_rssi_low_count = RAW_THRESHOLD_RSSI_LOW_COUNT; + } + subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false); + } else { + subghz->txrx->raw_threshold_rssi_low_count = 0; + } + + if(subghz->txrx->raw_threshold_rssi_low_count == RAW_THRESHOLD_RSSI_LOW_COUNT) { + subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false); + subghz_protocol_raw_save_to_file_pause( + (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, true); + } else { + subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true); + subghz_protocol_raw_save_to_file_pause( + (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false); + } + } + break; case SubGhzNotificationStateTx: notification_message(subghz->notifications, &sequence_blink_magenta_10); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 5f022d6e..fd42829b 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -1,10 +1,41 @@ #include "../subghz_i.h" +#include enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, SubGhzSettingIndexHopping, SubGhzSettingIndexModulation, SubGhzSettingIndexLock, + SubGhzSettingIndexRAWThesholdRSSI, +}; + +#define RAW_THRESHOLD_RSSI_COUNT 11 +const char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = { + "-----", + "-85.0", + "-80.0", + "-75.0", + "-70.0", + "-65.0", + "-60.0", + "-55.0", + "-50.0", + "-45.0", + "-40.0", + +}; +const float raw_theshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = { + -90.0f, + -85.0f, + -80.0f, + -75.0f, + -70.0f, + -65.0f, + -60.0f, + -55.0f, + -50.0f, + -45.0f, + -40.0f, }; #define HOPPING_COUNT 2 @@ -136,6 +167,14 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) subghz->txrx->hopper_state = hopping_value[index]; } +static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, raw_theshold_rssi_text[index]); + subghz->txrx->raw_threshold_rssi = raw_theshold_rssi_value[index]; +} + static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { furi_assert(context); SubGhz* subghz = context; @@ -204,6 +243,19 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz_scene_receiver_config_var_list_enter_callback, subghz); } + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == + SubGhzCustomEventManagerSet) { + item = variable_item_list_add( + subghz->variable_item_list, + "RSSI Threshold:", + RAW_THRESHOLD_RSSI_COUNT, + subghz_scene_receiver_config_set_raw_threshold_rssi, + subghz); + value_index = value_index_float( + subghz->txrx->raw_threshold_rssi, raw_theshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]); + } view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); } diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index ba70f36d..df5a7652 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -178,6 +178,7 @@ SubGhz* subghz_alloc() { subghz->txrx->txrx_state = SubGhzTxRxStateSleep; subghz->txrx->hopper_state = SubGhzHopperStateOFF; subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; + subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN; subghz->txrx->history = subghz_history_alloc(); subghz->txrx->worker = subghz_worker_alloc(); subghz->txrx->fff_data = flipper_format_string_alloc(); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 284f7ccf..09830ba0 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -54,6 +54,9 @@ struct SubGhzTxRx { uint8_t hopper_timeout; uint8_t hopper_idx_frequency; SubGhzRxKeyState rx_key_state; + + float raw_threshold_rssi; + uint8_t raw_threshold_rssi_low_count; }; typedef struct SubGhzTxRx SubGhzTxRx; diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index 2d951b11..6120a210 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -23,10 +23,12 @@ typedef struct { FuriString* sample_write; FuriString* file_name; uint8_t* rssi_history; + uint8_t rssi_curret; bool rssi_history_end; uint8_t ind_write; uint8_t ind_sin; SubGhzReadRAWStatus status; + float raw_threshold_rssi; } SubGhzReadRAWModel; void subghz_read_raw_set_callback( @@ -54,21 +56,27 @@ void subghz_read_raw_add_data_statusbar( true); } -void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi) { +void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool trace) { furi_assert(instance); uint8_t u_rssi = 0; - if(rssi < -90) { + if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { u_rssi = 0; } else { - u_rssi = (uint8_t)((rssi + 90) / 2.7); + u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7); } with_view_model( instance->view, SubGhzReadRAWModel * model, { - model->rssi_history[model->ind_write++] = u_rssi; + model->rssi_curret = u_rssi; + if(trace) { + model->rssi_history[model->ind_write++] = u_rssi; + } else { + model->rssi_history[model->ind_write] = u_rssi; + } + if(model->ind_write > SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE) { model->rssi_history_end = true; model->ind_write = 0; @@ -187,24 +195,53 @@ void subghz_read_raw_draw_scale(Canvas* canvas, SubGhzReadRAWModel* model) { void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) { int ind = 0; int base = 0; + uint8_t width = 2; if(model->rssi_history_end == false) { for(int i = model->ind_write; i >= 0; i--) { canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[i]); } + canvas_draw_line( + canvas, model->ind_write + 1, 47, model->ind_write + 1, 47 - model->rssi_curret); if(model->ind_write > 3) { - canvas_draw_line(canvas, model->ind_write, 47, model->ind_write, 13); + canvas_draw_line( + canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_curret); + + for(uint8_t i = 13; i < 47; i += width * 2) { + canvas_draw_line(canvas, model->ind_write, i, model->ind_write, i + width); + } canvas_draw_line(canvas, model->ind_write - 2, 12, model->ind_write + 2, 12); canvas_draw_line(canvas, model->ind_write - 1, 13, model->ind_write + 1, 13); } } else { + int i = 0; base = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - model->ind_write; - for(int i = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; i >= 0; i--) { + for(i = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; i > 0; i--) { ind = i - base; if(ind < 0) ind += SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[ind]); } + canvas_draw_line( - canvas, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 47, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 13); + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1, + 47, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1, + 47 - model->rssi_curret); + canvas_draw_line( + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1, + 47, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1, + 47 - model->rssi_curret); + + for(uint8_t i = 13; i < 47; i += width * 2) { + canvas_draw_line( + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, + i, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, + i + width); + } canvas_draw_line( canvas, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 2, @@ -220,6 +257,24 @@ void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) { } } +void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* model) { + uint8_t x = 118; + uint8_t y = 48; + + if(model->raw_threshold_rssi > SUBGHZ_RAW_TRESHOLD_MIN) { + uint8_t x = 118; + y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7); + + uint8_t width = 3; + for(uint8_t i = 0; i < x; i += width * 2) { + canvas_draw_line(canvas, i, y, i + width, y); + } + } + canvas_draw_line(canvas, x, y - 2, x, y + 2); + canvas_draw_line(canvas, x - 1, y - 1, x - 1, y + 1); + canvas_draw_dot(canvas, x - 2, y); +} + void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { uint8_t graphics_mode = 1; canvas_set_color(canvas, ColorBlack); @@ -278,8 +333,9 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { } else { subghz_read_raw_draw_rssi(canvas, model); subghz_read_raw_draw_scale(canvas, model); + subghz_read_raw_draw_threshold_rssi(canvas, model); canvas_set_font_direction(canvas, CanvasDirectionBottomToTop); - canvas_draw_str(canvas, 126, 40, "RSSI"); + canvas_draw_str(canvas, 128, 40, "RSSI"); canvas_set_font_direction(canvas, CanvasDirectionLeftToRight); } } @@ -433,7 +489,8 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { void subghz_read_raw_set_status( SubGhzReadRAW* instance, SubGhzReadRAWStatus status, - const char* file_name) { + const char* file_name, + float raw_threshold_rssi) { furi_assert(instance); switch(status) { @@ -447,6 +504,7 @@ void subghz_read_raw_set_status( model->ind_write = 0; furi_string_reset(model->file_name); furi_string_set(model->sample_write, "0 spl."); + model->raw_threshold_rssi = raw_threshold_rssi; }, true); break; @@ -536,6 +594,7 @@ SubGhzReadRAW* subghz_read_raw_alloc() { model->sample_write = furi_string_alloc(); model->file_name = furi_string_alloc(); model->rssi_history = malloc(SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE * sizeof(uint8_t)); + model->raw_threshold_rssi = -127.0f; }, true); diff --git a/applications/main/subghz/views/subghz_read_raw.h b/applications/main/subghz/views/subghz_read_raw.h index 1d4bb7dc..bc871192 100644 --- a/applications/main/subghz/views/subghz_read_raw.h +++ b/applications/main/subghz/views/subghz_read_raw.h @@ -3,6 +3,8 @@ #include #include "../helpers/subghz_custom_event.h" +#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f + typedef struct SubGhzReadRAW SubGhzReadRAW; typedef void (*SubGhzReadRAWCallback)(SubGhzCustomEvent event, void* context); @@ -40,11 +42,12 @@ void subghz_read_raw_stop_send(SubGhzReadRAW* instance); void subghz_read_raw_update_sin(SubGhzReadRAW* instance); -void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi); +void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool trace); void subghz_read_raw_set_status( SubGhzReadRAW* instance, SubGhzReadRAWStatus status, - const char* file_name); + const char* file_name, + float raw_threshold_rssi); View* subghz_read_raw_get_view(SubGhzReadRAW* subghz_static); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 17a8a675..8bad0b83 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2310,6 +2310,7 @@ Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhz Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzRadioPreset*" +Function,+,subghz_protocol_raw_save_to_file_pause,void,"SubGhzProtocolDecoderRAW*, _Bool" Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index b8e93c3d..b639c93b 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -32,6 +32,7 @@ struct SubGhzProtocolDecoderRAW { FuriString* file_name; size_t sample_write; bool last_level; + bool pause; }; struct SubGhzProtocolEncoderRAW { @@ -158,6 +159,7 @@ bool subghz_protocol_raw_save_to_file_init( instance->upload_raw = malloc(SUBGHZ_DOWNLOAD_MAX_SIZE * sizeof(int32_t)); instance->file_is_open = RAWFileIsOpenWrite; instance->sample_write = 0; + instance->pause = false; init = true; } while(0); @@ -199,6 +201,14 @@ void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance) { instance->file_is_open = RAWFileIsOpenClose; } +void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause) { + furi_assert(instance); + + if(instance->pause != pause) { + instance->pause = pause; + } +} + size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance) { return instance->sample_write + instance->ind_write; } @@ -234,7 +244,7 @@ void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t durati furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; - if(instance->upload_raw != NULL) { + if(!instance->pause && (instance->upload_raw != NULL)) { if(duration > subghz_protocol_raw_const.te_short) { if(instance->last_level != level) { instance->last_level = (level ? true : false); diff --git a/lib/subghz/protocols/raw.h b/lib/subghz/protocols/raw.h index 96c77553..44c7faec 100644 --- a/lib/subghz/protocols/raw.h +++ b/lib/subghz/protocols/raw.h @@ -103,6 +103,13 @@ void subghz_protocol_encoder_raw_free(void* context); */ void subghz_protocol_encoder_raw_stop(void* context); +/** + * pause writing to flash. + * @param context Pointer to a SubGhzProtocolEncoderRAW instance + * @param pause pause writing + */ +void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause); + /** * Set callback on completion of file transfer. * @param instance Pointer to a SubGhzProtocolEncoderRAW instance From a8edb41eae75c7796e4f16f31b0fa8c5d6440e01 Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 26 Oct 2022 19:18:06 +0400 Subject: [PATCH 27/42] fbt, docs: typo fixes; vscode: fixed deprecated target names (#1926) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .vscode/example/tasks.json | 4 ++-- documentation/fbt.md | 2 +- firmware.scons | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json index 3946d05c..9baaf97b 100644 --- a/.vscode/example/tasks.json +++ b/.vscode/example/tasks.json @@ -109,13 +109,13 @@ "label": "[Debug] Build FAPs", "group": "build", "type": "shell", - "command": "./fbt plugin_dist" + "command": "./fbt fap_dist" }, { "label": "[Release] Build FAPs", "group": "build", "type": "shell", - "command": "./fbt COMPACT=1 DEBUG=0 plugin_dist" + "command": "./fbt COMPACT=1 DEBUG=0 fap_dist" }, { "label": "[Debug] Launch App on Flipper", diff --git a/documentation/fbt.md b/documentation/fbt.md index 65917051..2bf9ea28 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -54,7 +54,7 @@ To run cleanup (think of `make clean`) for specified targets, add `-c` option. - `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board) - `openocd` - just start OpenOCD - `get_blackmagic` - output blackmagic address in gdb remote format. Useful for IDE integration -- `get_stlink` - output serial numbers for attached STLink probes. Ued for `OPENOCD_ADAPTER_SERIAL=...`. +- `get_stlink` - output serial numbers for attached STLink probes. Used for specifying an adapter with `OPENOCD_ADAPTER_SERIAL=...`. - `lint`, `format` - run clang-format on C source code to check and reformat it according to `.clang-format` specs - `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on Python source code, build system files & application manifests - `cli` - start Flipper CLI session over USB diff --git a/firmware.scons b/firmware.scons index d0b89a76..d501996b 100644 --- a/firmware.scons +++ b/firmware.scons @@ -292,9 +292,10 @@ Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_all", fw_artifacts) if fwenv["IS_BASE_FIRMWARE"]: sdk_source = fwenv.SDKPrebuilder( "sdk_origin", + # Deps on root SDK headers and generated files (fwenv["SDK_HEADERS"], fwenv["FW_ASSETS_HEADERS"]), ) - # Extra deps for root headers and generated files + # Extra deps on headers included in deeper levels Depends(sdk_source, fwenv.ProcessSdkDepends("sdk_origin.d")) fwenv["SDK_DIR"] = fwenv.Dir("sdk") From ebaa84b0c27548dd4df1a9584fe22ee797a68331 Mon Sep 17 00:00:00 2001 From: "Vasyl \"vk\" Kaigorodov" Date: Wed, 26 Oct 2022 20:35:49 +0200 Subject: [PATCH 28/42] Support for setting all screen orientations (#1928) * Support for setting all screen orientations * Gui: add flipped orientation to view * Gui: correct assert conditions in gui_add_view_port Co-authored-by: Aleksandr Kutuzov --- applications/services/gui/canvas.c | 35 ++++++++-- applications/services/gui/canvas.h | 2 + applications/services/gui/gui.c | 4 +- applications/services/gui/view.h | 2 + applications/services/gui/view_dispatcher.c | 10 ++- applications/services/gui/view_port.c | 74 ++++++++++++++++++--- applications/services/gui/view_port.h | 2 + 7 files changed, 112 insertions(+), 17 deletions(-) diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 1e275aaf..a2979d56 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -371,16 +371,39 @@ void canvas_set_bitmap_mode(Canvas* canvas, bool alpha) { void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation) { furi_assert(canvas); if(canvas->orientation != orientation) { - canvas->orientation = orientation; - if(canvas->orientation == CanvasOrientationHorizontal) { - FURI_SWAP(canvas->width, canvas->height); + switch(orientation) { + case CanvasOrientationHorizontal: + if(canvas->orientation == CanvasOrientationVertical || + canvas->orientation == CanvasOrientationVerticalFlip) { + FURI_SWAP(canvas->width, canvas->height); + } u8g2_SetDisplayRotation(&canvas->fb, U8G2_R0); - } else if(canvas->orientation == CanvasOrientationVertical) { - FURI_SWAP(canvas->width, canvas->height); + break; + case CanvasOrientationHorizontalFlip: + if(canvas->orientation == CanvasOrientationVertical || + canvas->orientation == CanvasOrientationVerticalFlip) { + FURI_SWAP(canvas->width, canvas->height); + } + u8g2_SetDisplayRotation(&canvas->fb, U8G2_R2); + break; + case CanvasOrientationVertical: + if(canvas->orientation == CanvasOrientationHorizontal || + canvas->orientation == CanvasOrientationHorizontalFlip) { + FURI_SWAP(canvas->width, canvas->height); + }; u8g2_SetDisplayRotation(&canvas->fb, U8G2_R3); - } else { + break; + case CanvasOrientationVerticalFlip: + if(canvas->orientation == CanvasOrientationHorizontal || + canvas->orientation == CanvasOrientationHorizontalFlip) { + FURI_SWAP(canvas->width, canvas->height); + } + u8g2_SetDisplayRotation(&canvas->fb, U8G2_R1); + break; + default: furi_assert(0); } + canvas->orientation = orientation; } } diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index 49bbd7d6..a67e5849 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -42,7 +42,9 @@ typedef enum { /** Canvas Orientation */ typedef enum { CanvasOrientationHorizontal, + CanvasOrientationHorizontalFlip, CanvasOrientationVertical, + CanvasOrientationVerticalFlip, } CanvasOrientation; /** Font Direction */ diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 8c5ed91a..42712ed9 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -322,7 +322,9 @@ void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) { furi_check(layer < GuiLayerMAX); // Only fullscreen supports Vertical orientation for now furi_assert( - (layer == GuiLayerFullscreen) || (view_port->orientation != ViewPortOrientationVertical)); + (layer == GuiLayerFullscreen) || + ((view_port->orientation != ViewPortOrientationVertical) && + (view_port->orientation != ViewPortOrientationVerticalFlip))); gui_lock(gui); // Verify that view port is not yet added diff --git a/applications/services/gui/view.h b/applications/services/gui/view.h index b40f8ded..7a2003a6 100644 --- a/applications/services/gui/view.h +++ b/applications/services/gui/view.h @@ -25,7 +25,9 @@ extern "C" { typedef enum { ViewOrientationHorizontal, + ViewOrientationHorizontalFlip, ViewOrientationVertical, + ViewOrientationVerticalFlip, } ViewOrientation; /** View, anonymous type */ diff --git a/applications/services/gui/view_dispatcher.c b/applications/services/gui/view_dispatcher.c index 6e4ce836..1736558c 100644 --- a/applications/services/gui/view_dispatcher.c +++ b/applications/services/gui/view_dispatcher.c @@ -331,10 +331,16 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie view_dispatcher->current_view = view; // Dispatch view enter event if(view_dispatcher->current_view) { - if(view->orientation == ViewOrientationVertical) + if(view->orientation == ViewOrientationVertical) { view_port_set_orientation(view_dispatcher->view_port, ViewPortOrientationVertical); - else if(view->orientation == ViewOrientationHorizontal) + } else if(view->orientation == ViewOrientationVerticalFlip) { + view_port_set_orientation(view_dispatcher->view_port, ViewPortOrientationVerticalFlip); + } else if(view->orientation == ViewOrientationHorizontal) { view_port_set_orientation(view_dispatcher->view_port, ViewPortOrientationHorizontal); + } else if(view->orientation == ViewOrientationHorizontalFlip) { + view_port_set_orientation( + view_dispatcher->view_port, ViewPortOrientationHorizontalFlip); + } view_enter(view_dispatcher->current_view); view_port_enabled_set(view_dispatcher->view_port, true); view_port_update(view_dispatcher->view_port); diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index 8069a02e..baa8f7bd 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -7,7 +7,7 @@ // TODO add mutex to view_port ops -static void view_port_rotate_buttons(InputEvent* event) { +static void view_port_remap_buttons_vertical(InputEvent* event) { switch(event->key) { case InputKeyUp: event->key = InputKeyRight; @@ -26,14 +26,61 @@ static void view_port_rotate_buttons(InputEvent* event) { } } -static void view_port_setup_canvas_orientation(const ViewPort* view_port, Canvas* canvas) { - if(view_port->orientation == ViewPortOrientationHorizontal) { - canvas_set_orientation(canvas, CanvasOrientationHorizontal); - } else if(view_port->orientation == ViewPortOrientationVertical) { - canvas_set_orientation(canvas, CanvasOrientationVertical); +static void view_port_remap_buttons_vertical_flip(InputEvent* event) { + switch(event->key) { + case InputKeyUp: + event->key = InputKeyLeft; + break; + case InputKeyDown: + event->key = InputKeyRight; + break; + case InputKeyRight: + event->key = InputKeyUp; + break; + case InputKeyLeft: + event->key = InputKeyDown; + break; + default: + break; } } +static void view_port_remap_buttons_horizontal_flip(InputEvent* event) { + switch(event->key) { + case InputKeyUp: + event->key = InputKeyDown; + break; + case InputKeyDown: + event->key = InputKeyUp; + break; + case InputKeyRight: + event->key = InputKeyLeft; + break; + case InputKeyLeft: + event->key = InputKeyRight; + break; + default: + break; + } +} + +static void view_port_setup_canvas_orientation(const ViewPort* view_port, Canvas* canvas) { + switch(view_port->orientation) { + case ViewPortOrientationHorizontalFlip: + canvas_set_orientation(canvas, CanvasOrientationHorizontalFlip); + break; + case ViewPortOrientationVertical: + canvas_set_orientation(canvas, CanvasOrientationVertical); + break; + case ViewPortOrientationVerticalFlip: + canvas_set_orientation(canvas, CanvasOrientationVerticalFlip); + break; + default: + canvas_set_orientation(canvas, CanvasOrientationHorizontal); + break; + }; +} + ViewPort* view_port_alloc() { ViewPort* view_port = malloc(sizeof(ViewPort)); view_port->orientation = ViewPortOrientationHorizontal; @@ -122,8 +169,19 @@ void view_port_input(ViewPort* view_port, InputEvent* event) { furi_check(view_port->gui); if(view_port->input_callback) { - if(view_port_get_orientation(view_port) == ViewPortOrientationVertical) { - view_port_rotate_buttons(event); + ViewPortOrientation orientation = view_port_get_orientation(view_port); + switch(orientation) { + case ViewPortOrientationHorizontalFlip: + view_port_remap_buttons_horizontal_flip(event); + break; + case ViewPortOrientationVertical: + view_port_remap_buttons_vertical(event); + break; + case ViewPortOrientationVerticalFlip: + view_port_remap_buttons_vertical_flip(event); + break; + default: + break; } view_port->input_callback(event, view_port->input_callback_context); } diff --git a/applications/services/gui/view_port.h b/applications/services/gui/view_port.h index 96f2798e..169681ac 100644 --- a/applications/services/gui/view_port.h +++ b/applications/services/gui/view_port.h @@ -16,7 +16,9 @@ typedef struct ViewPort ViewPort; typedef enum { ViewPortOrientationHorizontal, + ViewPortOrientationHorizontalFlip, ViewPortOrientationVertical, + ViewPortOrientationVerticalFlip, } ViewPortOrientation; /** ViewPort Draw callback From aff99a72e84aac45b7bc6281d3072218b83f5498 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 26 Oct 2022 23:40:13 +0400 Subject: [PATCH 29/42] SubGhz: fix variable types and CC1101 GPIO initialization optimization (#1931) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SubGhz: fix variable types * SubGhz: CC1101 GPIO initialization optimization * SubGhz: return back gpio init * SubGhz: cleanup grammar in math and format doxygen comments Co-authored-by: あく --- applications/main/subghz/subghz_i.c | 2 +- firmware/targets/f7/api_symbols.csv | 25 +-- lib/subghz/blocks/decoder.c | 10 + lib/subghz/blocks/decoder.h | 11 ++ lib/subghz/blocks/math.c | 115 ++++++------ lib/subghz/blocks/math.h | 272 +++++++++++++++------------- 6 files changed, 235 insertions(+), 200 deletions(-) diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index ac1036d0..beefd802 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -102,8 +102,8 @@ static bool subghz_tx(SubGhz* subghz, uint32_t frequency) { furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); furi_hal_subghz_idle(); furi_hal_subghz_set_frequency_and_path(frequency); + furi_hal_gpio_write(&gpio_cc1101_g0, false); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_cc1101_g0, true); bool ret = furi_hal_subghz_tx(); subghz->txrx->txrx_state = SubGhzTxRxStateTx; return ret; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 8bad0b83..fd11dffe 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,5.1,, +Version,+,6.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2272,21 +2272,22 @@ Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, si Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" Function,+,subghz_protocol_blocks_add_bytes,uint8_t,"const uint8_t[], size_t" -Function,+,subghz_protocol_blocks_crc16,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" -Function,+,subghz_protocol_blocks_crc16lsb,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" -Function,+,subghz_protocol_blocks_crc4,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" -Function,+,subghz_protocol_blocks_crc7,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" -Function,+,subghz_protocol_blocks_crc8,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" -Function,+,subghz_protocol_blocks_crc8le,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_add_to_128_bit,void,"SubGhzBlockDecoder*, uint8_t, uint64_t*" +Function,+,subghz_protocol_blocks_crc16,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" +Function,+,subghz_protocol_blocks_crc16lsb,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" +Function,+,subghz_protocol_blocks_crc4,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_crc7,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_crc8,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_crc8le,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t" Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t" Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t" -Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" -Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" -Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], int, uint8_t, uint8_t" -Function,+,subghz_protocol_blocks_parity8,int,uint8_t -Function,+,subghz_protocol_blocks_parity_bytes,int,"const uint8_t[], size_t" +Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" +Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_parity8,uint8_t,uint8_t +Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" diff --git a/lib/subghz/blocks/decoder.c b/lib/subghz/blocks/decoder.c index d2237f6c..f491c87b 100644 --- a/lib/subghz/blocks/decoder.c +++ b/lib/subghz/blocks/decoder.c @@ -7,6 +7,16 @@ void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit) { decoder->decode_count_bit++; } +void subghz_protocol_blocks_add_to_128_bit( + SubGhzBlockDecoder* decoder, + uint8_t bit, + uint64_t* head_64_bit) { + if(++decoder->decode_count_bit > 64) { + (*head_64_bit) = ((*head_64_bit) << 1) | (decoder->decode_data >> 63); + } + decoder->decode_data = decoder->decode_data << 1 | bit; +} + uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len) { uint8_t hash = 0; uint8_t* p = (uint8_t*)&decoder->decode_data; diff --git a/lib/subghz/blocks/decoder.h b/lib/subghz/blocks/decoder.h index 25549fab..a5e561e3 100644 --- a/lib/subghz/blocks/decoder.h +++ b/lib/subghz/blocks/decoder.h @@ -24,6 +24,17 @@ struct SubGhzBlockDecoder { */ void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit); +/** + * Add data to_128 bit when decoding. + * @param decoder Pointer to a SubGhzBlockDecoder instance + * @param head_64_bit Pointer to a head_64_bit + * @param bit data, 1bit + */ +void subghz_protocol_blocks_add_to_128_bit( + SubGhzBlockDecoder* decoder, + uint8_t bit, + uint64_t* head_64_bit); + /** * Getting the hash sum of the last randomly received parcel. * @param decoder Pointer to a SubGhzBlockDecoder instance diff --git a/lib/subghz/blocks/math.c b/lib/subghz/blocks/math.c index d2b8e3d1..24202ad1 100644 --- a/lib/subghz/blocks/math.c +++ b/lib/subghz/blocks/math.c @@ -1,16 +1,16 @@ #include "math.h" -uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit) { - uint64_t key_reverse = 0; - for(uint8_t i = 0; i < count_bit; i++) { - key_reverse = key_reverse << 1 | bit_read(key, i); +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) { + uint64_t reverse_key = 0; + for(uint8_t i = 0; i < bit_count; i++) { + reverse_key = reverse_key << 1 | bit_read(key, i); } - return key_reverse; + return reverse_key; } -uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit) { +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) { uint8_t parity = 0; - for(uint8_t i = 0; i < count_bit; i++) { + for(uint8_t i = 0; i < bit_count; i++) { parity += bit_read(key, i); } return parity & 0x01; @@ -18,14 +18,14 @@ uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit) { uint8_t subghz_protocol_blocks_crc4( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init) { - unsigned remainder = init << 4; // LSBs are unused - unsigned poly = polynomial << 4; - unsigned bit; + uint8_t remainder = init << 4; // LSBs are unused + uint8_t poly = polynomial << 4; + uint8_t bit; - while(nBytes--) { + while(size--) { remainder ^= *message++; for(bit = 0; bit < 8; bit++) { if(remainder & 0x80) { @@ -40,16 +40,15 @@ uint8_t subghz_protocol_blocks_crc4( uint8_t subghz_protocol_blocks_crc7( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init) { - unsigned remainder = init << 1; // LSB is unused - unsigned poly = polynomial << 1; - unsigned byte, bit; + uint8_t remainder = init << 1; // LSB is unused + uint8_t poly = polynomial << 1; - for(byte = 0; byte < nBytes; ++byte) { + for(size_t byte = 0; byte < size; ++byte) { remainder ^= message[byte]; - for(bit = 0; bit < 8; ++bit) { + for(uint8_t bit = 0; bit < 8; ++bit) { if(remainder & 0x80) { remainder = (remainder << 1) ^ poly; } else { @@ -62,15 +61,14 @@ uint8_t subghz_protocol_blocks_crc7( uint8_t subghz_protocol_blocks_crc8( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init) { uint8_t remainder = init; - unsigned byte, bit; - for(byte = 0; byte < nBytes; ++byte) { + for(size_t byte = 0; byte < size; ++byte) { remainder ^= message[byte]; - for(bit = 0; bit < 8; ++bit) { + for(uint8_t bit = 0; bit < 8; ++bit) { if(remainder & 0x80) { remainder = (remainder << 1) ^ polynomial; } else { @@ -83,16 +81,15 @@ uint8_t subghz_protocol_blocks_crc8( uint8_t subghz_protocol_blocks_crc8le( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init) { uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8); - unsigned byte, bit; polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8); - for(byte = 0; byte < nBytes; ++byte) { + for(size_t byte = 0; byte < size; ++byte) { remainder ^= message[byte]; - for(bit = 0; bit < 8; ++bit) { + for(uint8_t bit = 0; bit < 8; ++bit) { if(remainder & 1) { remainder = (remainder >> 1) ^ polynomial; } else { @@ -105,15 +102,14 @@ uint8_t subghz_protocol_blocks_crc8le( uint16_t subghz_protocol_blocks_crc16lsb( uint8_t const message[], - unsigned nBytes, + size_t size, uint16_t polynomial, uint16_t init) { uint16_t remainder = init; - unsigned byte, bit; - for(byte = 0; byte < nBytes; ++byte) { + for(size_t byte = 0; byte < size; ++byte) { remainder ^= message[byte]; - for(bit = 0; bit < 8; ++bit) { + for(uint8_t bit = 0; bit < 8; ++bit) { if(remainder & 1) { remainder = (remainder >> 1) ^ polynomial; } else { @@ -126,15 +122,14 @@ uint16_t subghz_protocol_blocks_crc16lsb( uint16_t subghz_protocol_blocks_crc16( uint8_t const message[], - unsigned nBytes, + size_t size, uint16_t polynomial, uint16_t init) { uint16_t remainder = init; - unsigned byte, bit; - for(byte = 0; byte < nBytes; ++byte) { + for(size_t byte = 0; byte < size; ++byte) { remainder ^= message[byte] << 8; - for(bit = 0; bit < 8; ++bit) { + for(uint8_t bit = 0; bit < 8; ++bit) { if(remainder & 0x8000) { remainder = (remainder << 1) ^ polynomial; } else { @@ -147,18 +142,18 @@ uint16_t subghz_protocol_blocks_crc16( uint8_t subghz_protocol_blocks_lfsr_digest8( uint8_t const message[], - unsigned bytes, + size_t size, uint8_t gen, uint8_t key) { uint8_t sum = 0; - for(unsigned k = 0; k < bytes; ++k) { - uint8_t data = message[k]; + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; for(int i = 7; i >= 0; --i) { // XOR key into sum if data bit is set if((data >> i) & 1) sum ^= key; - // roll the key right (actually the lsb is dropped here) - // and apply the gen (needs to include the dropped lsb as msb) + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) if(key & 1) key = (key >> 1) ^ gen; else @@ -170,22 +165,22 @@ uint8_t subghz_protocol_blocks_lfsr_digest8( uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( uint8_t const message[], - int bytes, + size_t size, uint8_t gen, uint8_t key) { uint8_t sum = 0; // Process message from last byte to first byte (reflected) - for(int k = bytes - 1; k >= 0; --k) { - uint8_t data = message[k]; + for(int byte = size - 1; byte >= 0; --byte) { + uint8_t data = message[byte]; // Process individual bits of each byte (reflected) - for(int i = 0; i < 8; ++i) { + for(uint8_t i = 0; i < 8; ++i) { // XOR key into sum if data bit is set if((data >> i) & 1) { sum ^= key; } - // roll the key left (actually the lsb is dropped here) - // and apply the gen (needs to include the dropped lsb as msb) + // roll the key left (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped lsb as MSB) if(key & 0x80) key = (key << 1) ^ gen; else @@ -197,18 +192,18 @@ uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( uint16_t subghz_protocol_blocks_lfsr_digest16( uint8_t const message[], - unsigned bytes, + size_t size, uint16_t gen, uint16_t key) { uint16_t sum = 0; - for(unsigned k = 0; k < bytes; ++k) { - uint8_t data = message[k]; - for(int i = 7; i >= 0; --i) { + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for(int8_t i = 7; i >= 0; --i) { // if data bit is set then xor with key if((data >> i) & 1) sum ^= key; - // roll the key right (actually the lsb is dropped here) - // and apply the gen (needs to include the dropped lsb as msb) + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) if(key & 1) key = (key >> 1) ^ gen; else @@ -218,31 +213,31 @@ uint16_t subghz_protocol_blocks_lfsr_digest16( return sum; } -uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t num_bytes) { - int result = 0; - for(size_t i = 0; i < num_bytes; ++i) { +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) { + uint32_t result = 0; + for(size_t i = 0; i < size; ++i) { result += message[i]; } return (uint8_t)result; } -int subghz_protocol_blocks_parity8(uint8_t byte) { +uint8_t subghz_protocol_blocks_parity8(uint8_t byte) { byte ^= byte >> 4; byte &= 0xf; return (0x6996 >> byte) & 1; } -int subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t num_bytes) { - int result = 0; - for(size_t i = 0; i < num_bytes; ++i) { +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++i) { result ^= subghz_protocol_blocks_parity8(message[i]); } return result; } -uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t num_bytes) { +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size) { uint8_t result = 0; - for(size_t i = 0; i < num_bytes; ++i) { + for(size_t i = 0; i < size; ++i) { result ^= message[i]; } return result; diff --git a/lib/subghz/blocks/math.h b/lib/subghz/blocks/math.h index 8cddf4c0..a4f04271 100644 --- a/lib/subghz/blocks/math.h +++ b/lib/subghz/blocks/math.h @@ -14,183 +14,201 @@ #ifdef __cplusplus extern "C" { #endif -/** - * Flip the data bitwise. - * @param key In data - * @param count_bit number of data bits - * @return Reverse data - **/ -uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit); -/** - * Get parity the data bitwise. - * @param key In data - * @param count_bit number of data bits - * @return parity - **/ -uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit); +/** Flip the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return Reverse data + */ +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count); -/** - * CRC-4. - * @param message array of bytes to check - * @param nBytes number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * @return CRC value - **/ +/** Get parity the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return parity + */ +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count); + +/** CRC-4 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ uint8_t subghz_protocol_blocks_crc4( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init); -/** - * CRC-7. - * @param message array of bytes to check - * @param nBytes number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * @return CRC value - **/ +/** CRC-7 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ uint8_t subghz_protocol_blocks_crc7( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init); -/** - * Generic Cyclic Redundancy Check CRC-8. - * Example polynomial: 0x31 = x8 + x5 + x4 + 1 (x8 is implicit) - * Example polynomial: 0x80 = x8 + x7 (a normal bit-by-bit parity XOR) - * @param message array of bytes to check - * @param nBytes number of bytes in message - * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) - * @param init starting crc value - * @return CRC value - **/ +/** Generic Cyclic Redundancy Check CRC-8. Example polynomial: 0x31 = x8 + x5 + + * x4 + 1 (x8 is implicit) Example polynomial: 0x80 = x8 + x7 (a normal + * bit-by-bit parity XOR) + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) + * @param init starting crc value + * + * @return CRC value + */ uint8_t subghz_protocol_blocks_crc8( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init); -/** - * "Little-endian" Cyclic Redundancy Check CRC-8 LE - * Input and output are reflected, i.e. least significant bit is shifted in first. - * @param message array of bytes to check - * @param nBytes number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * @return CRC value - **/ +/** "Little-endian" Cyclic Redundancy Check CRC-8 LE Input and output are + * reflected, i.e. least significant bit is shifted in first + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ uint8_t subghz_protocol_blocks_crc8le( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init); -/** - * CRC-16 LSB. - * Input and output are reflected, i.e. least significant bit is shifted in first. - * Note that poly and init already need to be reflected. - * @param message array of bytes to check - * @param nBytes number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * @return CRC value - **/ +/** CRC-16 LSB. Input and output are reflected, i.e. least significant bit is + * shifted in first. Note that poly and init already need to be reflected + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ uint16_t subghz_protocol_blocks_crc16lsb( uint8_t const message[], - unsigned nBytes, + size_t size, uint16_t polynomial, uint16_t init); -/** - * CRC-16. - * @param message array of bytes to check - * @param nBytes number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * @return CRC value - **/ +/** CRC-16 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ uint16_t subghz_protocol_blocks_crc16( uint8_t const message[], - unsigned nBytes, + size_t size, uint16_t polynomial, uint16_t init); -/** - * Digest-8 by "LFSR-based Toeplitz hash". - * @param message bytes of message data - * @param bytes number of bytes to digest - * @param gen key stream generator, needs to includes the MSB if the LFSR is rolling - * @param key initial key - * @return digest value - **/ +/** Digest-8 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ uint8_t subghz_protocol_blocks_lfsr_digest8( uint8_t const message[], - unsigned bytes, + size_t size, uint8_t gen, uint8_t key); -/** - * Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect. - * @param message bytes of message data - * @param bytes number of bytes to digest - * @param gen key stream generator, needs to includes the MSB if the LFSR is rolling - * @param key initial key - * @return digest value - **/ +/** Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( uint8_t const message[], - int bytes, + size_t size, uint8_t gen, uint8_t key); -/** - * Digest-16 by "LFSR-based Toeplitz hash". - * @param message bytes of message data - * @param bytes number of bytes to digest - * @param gen key stream generator, needs to includes the MSB if the LFSR is rolling - * @param key initial key - * @return digest value - **/ +/** Digest-16 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ uint16_t subghz_protocol_blocks_lfsr_digest16( uint8_t const message[], - unsigned bytes, + size_t size, uint16_t gen, uint16_t key); -/** - * Compute Addition of a number of bytes. - * @param message bytes of message data - * @param num_bytes number of bytes to sum - * @return summation value - **/ -uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t num_bytes); +/** Compute Addition of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value + */ +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size); -/** - * Compute bit parity of a single byte (8 bits). - * @param byte single byte to check - * @return 1 odd parity, 0 even parity - **/ -int subghz_protocol_blocks_parity8(uint8_t byte); +/** Compute bit parity of a single byte (8 bits) + * + * @param byte single byte to check + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity8(uint8_t byte); -/** - * Compute bit parity of a number of bytes. - * @param message bytes of message data - * @param num_bytes number of bytes to sum - * @return 1 odd parity, 0 even parity - **/ -int subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t num_bytes); +/** Compute bit parity of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size); -/** - * Compute XOR (byte-wide parity) of a number of bytes. - * @param message bytes of message data - * @param num_bytes number of bytes to sum - * @return summation value, per bit-position 1 odd parity, 0 even parity - **/ -uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t num_bytes); +/** Compute XOR (byte-wide parity) of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value, per bit-position 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size); #ifdef __cplusplus } From 8b7a52b97b6b537b02608517ad3d12c6c170ece5 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 27 Oct 2022 00:25:31 +0400 Subject: [PATCH 30/42] fbt: fixed linter paths (#1930) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fbt: fixed linter paths * lint: changed file permissions Co-authored-by: あく --- .../targets/f7/ble_glue/dev_info_service.c | 0 .../targets/f7/furi_hal/furi_hal_cortex.c | 3 +- .../targets/furi_hal_include/furi_hal_nfc.h | 125 +++++++++++------- furi/SConscript | 7 +- furi/core/timer.c | 2 +- furi/flipper.c | 0 lib/lfrfid/SConscript | 2 +- lib/lfrfid/protocols/protocol_awid.c | 2 +- 8 files changed, 85 insertions(+), 56 deletions(-) mode change 100755 => 100644 firmware/targets/f7/ble_glue/dev_info_service.c mode change 100755 => 100644 furi/flipper.c diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c old mode 100755 new mode 100644 diff --git a/firmware/targets/f7/furi_hal/furi_hal_cortex.c b/firmware/targets/f7/furi_hal/furi_hal_cortex.c index c2abd1b8..192b83ee 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_cortex.c +++ b/firmware/targets/f7/furi_hal/furi_hal_cortex.c @@ -36,5 +36,6 @@ bool furi_hal_cortex_timer_is_expired(FuriHalCortexTimer cortex_timer) { } void furi_hal_cortex_timer_wait(FuriHalCortexTimer cortex_timer) { - while(!furi_hal_cortex_timer_is_expired(cortex_timer)); + while(!furi_hal_cortex_timer_is_expired(cortex_timer)) + ; } \ No newline at end of file diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index 537e0abf..90d968fe 100644 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ b/firmware/targets/furi_hal_include/furi_hal_nfc.h @@ -111,7 +111,7 @@ bool furi_hal_nfc_is_busy(); * * @return true if initialized */ - bool furi_hal_nfc_is_init(); +bool furi_hal_nfc_is_init(); /** NFC field on */ @@ -234,7 +234,6 @@ void furi_hal_nfc_sleep(); void furi_hal_nfc_stop(); - /* Low level transport API, use it to implement your own transport layers */ #define furi_hal_nfc_ll_ms2fc rfalConvMsTo1fc @@ -250,7 +249,8 @@ typedef enum { FuriHalNfcReturnBusy = 2, /*!< device or resource busy */ FuriHalNfcReturnIo = 3, /*!< generic IO error */ FuriHalNfcReturnTimeout = 4, /*!< error due to timeout */ - FuriHalNfcReturnRequest = 5, /*!< invalid request or requested function can't be executed at the moment */ + FuriHalNfcReturnRequest = + 5, /*!< invalid request or requested function can't be executed at the moment */ FuriHalNfcReturnNomsg = 6, /*!< No message of desired type */ FuriHalNfcReturnParam = 7, /*!< Parameter error */ FuriHalNfcReturnSystem = 8, /*!< System error */ @@ -261,20 +261,23 @@ typedef enum { FuriHalNfcReturnAgain = 13, /*!< Call again */ FuriHalNfcReturnMemCorrupt = 14, /*!< memory corruption */ FuriHalNfcReturnNotImplemented = 15, /*!< not implemented */ - FuriHalNfcReturnPcCorrupt = 16, /*!< Program Counter has been manipulated or spike/noise trigger illegal operation */ + FuriHalNfcReturnPcCorrupt = + 16, /*!< Program Counter has been manipulated or spike/noise trigger illegal operation */ FuriHalNfcReturnSend = 17, /*!< error sending*/ FuriHalNfcReturnIgnore = 18, /*!< indicates error detected but to be ignored */ FuriHalNfcReturnSemantic = 19, /*!< indicates error in state machine (unexpected cmd) */ FuriHalNfcReturnSyntax = 20, /*!< indicates error in state machine (unknown cmd) */ FuriHalNfcReturnCrc = 21, /*!< crc error */ FuriHalNfcReturnNotfound = 22, /*!< transponder not found */ - FuriHalNfcReturnNotunique = 23, /*!< transponder not unique - more than one transponder in field */ + FuriHalNfcReturnNotunique = + 23, /*!< transponder not unique - more than one transponder in field */ FuriHalNfcReturnNotsupp = 24, /*!< requested operation not supported */ FuriHalNfcReturnWrite = 25, /*!< write error */ FuriHalNfcReturnFifo = 26, /*!< fifo over or underflow error */ FuriHalNfcReturnPar = 27, /*!< parity error */ FuriHalNfcReturnDone = 28, /*!< transfer has already finished */ - FuriHalNfcReturnRfCollision = 29, /*!< collision error (Bit Collision or during RF Collision avoidance ) */ + FuriHalNfcReturnRfCollision = + 29, /*!< collision error (Bit Collision or during RF Collision avoidance ) */ FuriHalNfcReturnHwOverrun = 30, /*!< lost one or more received bytes */ FuriHalNfcReturnReleaseReq = 31, /*!< device requested release */ FuriHalNfcReturnSleepReq = 32, /*!< device requested sleep */ @@ -282,7 +285,8 @@ typedef enum { FuriHalNfcReturnMaxReruns = 34, /*!< blocking procedure reached maximum runs */ FuriHalNfcReturnDisabled = 35, /*!< operation aborted due to disabled configuration */ FuriHalNfcReturnHwMismatch = 36, /*!< expected hw do not match */ - FuriHalNfcReturnLinkLoss = 37, /*!< Other device's field didn't behave as expected: turned off by Initiator in Passive mode, or AP2P did not turn on field */ + FuriHalNfcReturnLinkLoss = + 37, /*!< Other device's field didn't behave as expected: turned off by Initiator in Passive mode, or AP2P did not turn on field */ FuriHalNfcReturnInvalidHandle = 38, /*!< invalid or not initalized device handle */ FuriHalNfcReturnIncompleteByte = 40, /*!< Incomplete byte rcvd */ FuriHalNfcReturnIncompleteByte01 = 41, /*!< Incomplete byte rcvd - 1 bit */ @@ -295,38 +299,40 @@ typedef enum { } FuriHalNfcReturn; typedef enum { - FuriHalNfcModeNone = 0, /*!< No mode selected/defined */ - FuriHalNfcModePollNfca = 1, /*!< Mode to perform as NFCA (ISO14443A) Poller (PCD) */ - FuriHalNfcModePollNfcaT1t = 2, /*!< Mode to perform as NFCA T1T (Topaz) Poller (PCD) */ - FuriHalNfcModePollNfcb = 3, /*!< Mode to perform as NFCB (ISO14443B) Poller (PCD) */ - FuriHalNfcModePollBPrime = 4, /*!< Mode to perform as B' Calypso (Innovatron) (PCD) */ - FuriHalNfcModePollBCts = 5, /*!< Mode to perform as CTS Poller (PCD) */ - FuriHalNfcModePollNfcf = 6, /*!< Mode to perform as NFCF (FeliCa) Poller (PCD) */ - FuriHalNfcModePollNfcv = 7, /*!< Mode to perform as NFCV (ISO15963) Poller (PCD) */ - FuriHalNfcModePollPicopass = 8, /*!< Mode to perform as PicoPass / iClass Poller (PCD) */ - FuriHalNfcModePollActiveP2p = 9, /*!< Mode to perform as Active P2P (ISO18092) Initiator */ - FuriHalNfcModeListenNfca = 10, /*!< Mode to perform as NFCA (ISO14443A) Listener (PICC) */ - FuriHalNfcModeListenNfcb = 11, /*!< Mode to perform as NFCA (ISO14443B) Listener (PICC) */ - FuriHalNfcModeListenNfcf = 12, /*!< Mode to perform as NFCA (ISO15963) Listener (PICC) */ - FuriHalNfcModeListenActiveP2p = 13 /*!< Mode to perform as Active P2P (ISO18092) Target */ + FuriHalNfcModeNone = 0, /*!< No mode selected/defined */ + FuriHalNfcModePollNfca = 1, /*!< Mode to perform as NFCA (ISO14443A) Poller (PCD) */ + FuriHalNfcModePollNfcaT1t = 2, /*!< Mode to perform as NFCA T1T (Topaz) Poller (PCD) */ + FuriHalNfcModePollNfcb = 3, /*!< Mode to perform as NFCB (ISO14443B) Poller (PCD) */ + FuriHalNfcModePollBPrime = 4, /*!< Mode to perform as B' Calypso (Innovatron) (PCD) */ + FuriHalNfcModePollBCts = 5, /*!< Mode to perform as CTS Poller (PCD) */ + FuriHalNfcModePollNfcf = 6, /*!< Mode to perform as NFCF (FeliCa) Poller (PCD) */ + FuriHalNfcModePollNfcv = 7, /*!< Mode to perform as NFCV (ISO15963) Poller (PCD) */ + FuriHalNfcModePollPicopass = 8, /*!< Mode to perform as PicoPass / iClass Poller (PCD) */ + FuriHalNfcModePollActiveP2p = 9, /*!< Mode to perform as Active P2P (ISO18092) Initiator */ + FuriHalNfcModeListenNfca = 10, /*!< Mode to perform as NFCA (ISO14443A) Listener (PICC) */ + FuriHalNfcModeListenNfcb = 11, /*!< Mode to perform as NFCA (ISO14443B) Listener (PICC) */ + FuriHalNfcModeListenNfcf = 12, /*!< Mode to perform as NFCA (ISO15963) Listener (PICC) */ + FuriHalNfcModeListenActiveP2p = 13 /*!< Mode to perform as Active P2P (ISO18092) Target */ } FuriHalNfcMode; typedef enum { - FuriHalNfcBitrate106 = 0, /*!< Bit Rate 106 kbit/s (fc/128) */ - FuriHalNfcBitrate212 = 1, /*!< Bit Rate 212 kbit/s (fc/64) */ - FuriHalNfcBitrate424 = 2, /*!< Bit Rate 424 kbit/s (fc/32) */ - FuriHalNfcBitrate848 = 3, /*!< Bit Rate 848 kbit/s (fc/16) */ - FuriHalNfcBitrate1695 = 4, /*!< Bit Rate 1695 kbit/s (fc/8) */ - FuriHalNfcBitrate3390 = 5, /*!< Bit Rate 3390 kbit/s (fc/4) */ - FuriHalNfcBitrate6780 = 6, /*!< Bit Rate 6780 kbit/s (fc/2) */ - FuriHalNfcBitrate13560 = 7, /*!< Bit Rate 13560 kbit/s (fc) */ - FuriHalNfcBitrate52p97 = 0xEB, /*!< Bit Rate 52.97 kbit/s (fc/256) Fast Mode VICC->VCD */ - FuriHalNfcBitrate26p48 = 0xEC, /*!< Bit Rate 26,48 kbit/s (fc/512) NFCV VICC->VCD & VCD->VICC 1of4 */ - FuriHalNfcBitrate1p66 = 0xED, /*!< Bit Rate 1,66 kbit/s (fc/8192) NFCV VCD->VICC 1of256 */ - FuriHalNfcBitrateKeep = 0xFF /*!< Value indicating to keep the same previous bit rate */ + FuriHalNfcBitrate106 = 0, /*!< Bit Rate 106 kbit/s (fc/128) */ + FuriHalNfcBitrate212 = 1, /*!< Bit Rate 212 kbit/s (fc/64) */ + FuriHalNfcBitrate424 = 2, /*!< Bit Rate 424 kbit/s (fc/32) */ + FuriHalNfcBitrate848 = 3, /*!< Bit Rate 848 kbit/s (fc/16) */ + FuriHalNfcBitrate1695 = 4, /*!< Bit Rate 1695 kbit/s (fc/8) */ + FuriHalNfcBitrate3390 = 5, /*!< Bit Rate 3390 kbit/s (fc/4) */ + FuriHalNfcBitrate6780 = 6, /*!< Bit Rate 6780 kbit/s (fc/2) */ + FuriHalNfcBitrate13560 = 7, /*!< Bit Rate 13560 kbit/s (fc) */ + FuriHalNfcBitrate52p97 = 0xEB, /*!< Bit Rate 52.97 kbit/s (fc/256) Fast Mode VICC->VCD */ + FuriHalNfcBitrate26p48 = + 0xEC, /*!< Bit Rate 26,48 kbit/s (fc/512) NFCV VICC->VCD & VCD->VICC 1of4 */ + FuriHalNfcBitrate1p66 = 0xED, /*!< Bit Rate 1,66 kbit/s (fc/8192) NFCV VCD->VICC 1of256 */ + FuriHalNfcBitrateKeep = 0xFF /*!< Value indicating to keep the same previous bit rate */ } FuriHalNfcBitrate; -FuriHalNfcReturn furi_hal_nfc_ll_set_mode(FuriHalNfcMode mode, FuriHalNfcBitrate txBR, FuriHalNfcBitrate rxBR); +FuriHalNfcReturn + furi_hal_nfc_ll_set_mode(FuriHalNfcMode mode, FuriHalNfcBitrate txBR, FuriHalNfcBitrate rxBR); #define FURI_HAL_NFC_LL_GT_NFCA furi_hal_nfc_ll_ms2fc(5U) /*!< GTA Digital 2.0 6.10.4.1 & B.2 */ #define FURI_HAL_NFC_LL_GT_NFCB furi_hal_nfc_ll_ms2fc(5U) /*!< GTB Digital 2.0 7.9.4.1 & B.3 */ @@ -334,40 +340,57 @@ FuriHalNfcReturn furi_hal_nfc_ll_set_mode(FuriHalNfcMode mode, FuriHalNfcBitrate #define FURI_HAL_NFC_LL_GT_NFCV furi_hal_nfc_ll_ms2fc(5U) /*!< GTV Digital 2.0 9.7.5.1 & B.5 */ #define FURI_HAL_NFC_LL_GT_PICOPASS furi_hal_nfc_ll_ms2fc(1U) /*!< GT Picopass */ #define FURI_HAL_NFC_LL_GT_AP2P furi_hal_nfc_ll_ms2fc(5U) /*!< TIRFG Ecma 340 11.1.1 */ -#define FURI_HAL_NFC_LL_GT_AP2P_ADJUSTED furi_hal_nfc_ll_ms2fc(5U + 25U) /*!< Adjusted GT for greater interoperability (Sony XPERIA P, Nokia N9, Huawei P2) */ +#define FURI_HAL_NFC_LL_GT_AP2P_ADJUSTED \ + furi_hal_nfc_ll_ms2fc( \ + 5U + \ + 25U) /*!< Adjusted GT for greater interoperability (Sony XPERIA P, Nokia N9, Huawei P2) */ void furi_hal_nfc_ll_set_guard_time(uint32_t cycles); typedef enum { - FuriHalNfcErrorHandlingNone = 0, /*!< No special error handling will be performed */ - FuriHalNfcErrorHandlingNfc = 1, /*!< Error handling set to perform as NFC compliant device */ - FuriHalNfcErrorHandlingEmvco = 2 /*!< Error handling set to perform as EMVCo compliant device */ + FuriHalNfcErrorHandlingNone = 0, /*!< No special error handling will be performed */ + FuriHalNfcErrorHandlingNfc = 1, /*!< Error handling set to perform as NFC compliant device */ + FuriHalNfcErrorHandlingEmvco = + 2 /*!< Error handling set to perform as EMVCo compliant device */ } FuriHalNfcErrorHandling; void furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandling eHandling); /* RFAL Frame Delay Time (FDT) Listen default values */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER 1172U /*!< FDTA,LISTEN,MIN (n=9) Last bit: Logic "1" - tnn,min/2 Digital 1.1 6.10 ; EMV CCP Spec Book D v2.01 4.8.1.3 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCB_POLLER 1008U /*!< TR0B,MIN Digital 1.1 7.1.3 & A.3 ; EMV CCP Spec Book D v2.01 4.8.1.3 & Table A.5 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCF_POLLER 2672U /*!< TR0F,LISTEN,MIN Digital 1.1 8.7.1.1 & A.4 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCV_POLLER 4310U /*!< FDTV,LISTEN,MIN t1 min Digital 2.1 B.5 ; ISO15693-3 2009 9.1 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_PICOPASS_POLLER 3400U /*!< ISO15693 t1 min - observed adjustment */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_AP2P_POLLER 64U /*!< FDT AP2P No actual FDTListen is required as fields switch and collision avoidance */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER \ + 1172U /*!< FDTA,LISTEN,MIN (n=9) Last bit: Logic "1" - tnn,min/2 Digital 1.1 6.10 ; EMV CCP Spec Book D v2.01 4.8.1.3 */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCB_POLLER \ + 1008U /*!< TR0B,MIN Digital 1.1 7.1.3 & A.3 ; EMV CCP Spec Book D v2.01 4.8.1.3 & Table A.5 */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCF_POLLER \ + 2672U /*!< TR0F,LISTEN,MIN Digital 1.1 8.7.1.1 & A.4 */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCV_POLLER \ + 4310U /*!< FDTV,LISTEN,MIN t1 min Digital 2.1 B.5 ; ISO15693-3 2009 9.1 */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_PICOPASS_POLLER \ + 3400U /*!< ISO15693 t1 min - observed adjustment */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_AP2P_POLLER \ + 64U /*!< FDT AP2P No actual FDTListen is required as fields switch and collision avoidance */ #define FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_LISTENER 1172U /*!< FDTA,LISTEN,MIN Digital 1.1 6.10 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCB_LISTENER 1024U /*!< TR0B,MIN Digital 1.1 7.1.3 & A.3 ; EMV CCP Spec Book D v2.01 4.8.1.3 & Table A.5 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCF_LISTENER 2688U /*!< TR0F,LISTEN,MIN Digital 2.1 8.7.1.1 & B.4 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_AP2P_LISTENER 64U /*!< FDT AP2P No actual FDTListen exists as fields switch and collision avoidance */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCB_LISTENER \ + 1024U /*!< TR0B,MIN Digital 1.1 7.1.3 & A.3 ; EMV CCP Spec Book D v2.01 4.8.1.3 & Table A.5 */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCF_LISTENER \ + 2688U /*!< TR0F,LISTEN,MIN Digital 2.1 8.7.1.1 & B.4 */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_AP2P_LISTENER \ + 64U /*!< FDT AP2P No actual FDTListen exists as fields switch and collision avoidance */ void furi_hal_nfc_ll_set_fdt_listen(uint32_t cycles); /* RFAL Frame Delay Time (FDT) Poll default values */ -#define FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER 6780U /*!< FDTA,POLL,MIN Digital 1.1 6.10.3.1 & A.2 */ -#define FURI_HAL_NFC_LL_FDT_POLL_NFCA_T1T_POLLER 384U /*!< RRDDT1T,MIN,B1 Digital 1.1 10.7.1 & A.5 */ -#define FURI_HAL_NFC_LL_FDT_POLL_NFCB_POLLER 6780U /*!< FDTB,POLL,MIN = TR2B,MIN,DEFAULT Digital 1.1 7.9.3 & A.3 ; EMVCo 3.0 FDTB,PCD,MIN Table A.5 */ +#define FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER \ + 6780U /*!< FDTA,POLL,MIN Digital 1.1 6.10.3.1 & A.2 */ +#define FURI_HAL_NFC_LL_FDT_POLL_NFCA_T1T_POLLER \ + 384U /*!< RRDDT1T,MIN,B1 Digital 1.1 10.7.1 & A.5 */ +#define FURI_HAL_NFC_LL_FDT_POLL_NFCB_POLLER \ + 6780U /*!< FDTB,POLL,MIN = TR2B,MIN,DEFAULT Digital 1.1 7.9.3 & A.3 ; EMVCo 3.0 FDTB,PCD,MIN Table A.5 */ #define FURI_HAL_NFC_LL_FDT_POLL_NFCF_POLLER 6800U /*!< FDTF,POLL,MIN Digital 2.1 8.7.3 & B.4 */ #define FURI_HAL_NFC_LL_FDT_POLL_NFCV_POLLER 4192U /*!< FDTV,POLL Digital 2.1 9.7.3.1 & B.5 */ #define FURI_HAL_NFC_LL_FDT_POLL_PICOPASS_POLLER 1790U /*!< FDT Max */ -#define FURI_HAL_NFC_LL_FDT_POLL_AP2P_POLLER 0U /*!< FDT AP2P No actual FDTPoll exists as fields switch and collision avoidance */ +#define FURI_HAL_NFC_LL_FDT_POLL_AP2P_POLLER \ + 0U /*!< FDT AP2P No actual FDTPoll exists as fields switch and collision avoidance */ void furi_hal_nfc_ll_set_fdt_poll(uint32_t FDTPoll); diff --git a/furi/SConscript b/furi/SConscript index a751eb6e..f95ef13f 100644 --- a/furi/SConscript +++ b/furi/SConscript @@ -1,6 +1,11 @@ Import("env") -env.Append(LINT_SOURCES=["furi"]) +env.Append( + LINT_SOURCES=[ + "furi", + "furi/core", + ] +) libenv = env.Clone(FW_LIB_NAME="furi") diff --git a/furi/core/timer.c b/furi/core/timer.c index 462a2e89..c42b0c2a 100644 --- a/furi/core/timer.c +++ b/furi/core/timer.c @@ -86,7 +86,7 @@ void furi_timer_free(FuriTimer* instance) { furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS); - while (furi_timer_is_running(instance)) furi_delay_tick(2); + while(furi_timer_is_running(instance)) furi_delay_tick(2); if((uint32_t)callb & 1U) { /* Callback memory was allocated from dynamic pool, clear flag */ diff --git a/furi/flipper.c b/furi/flipper.c old mode 100755 new mode 100644 diff --git a/lib/lfrfid/SConscript b/lib/lfrfid/SConscript index fd29ca2e..6177a9a5 100644 --- a/lib/lfrfid/SConscript +++ b/lib/lfrfid/SConscript @@ -2,7 +2,7 @@ Import("env") env.Append( LINT_SOURCES=[ - "#/lib/lfrfid", + "lib/lfrfid", ], CPPPATH=[ "#/lib/lfrfid", diff --git a/lib/lfrfid/protocols/protocol_awid.c b/lib/lfrfid/protocols/protocol_awid.c index 38c7793b..93962772 100644 --- a/lib/lfrfid/protocols/protocol_awid.c +++ b/lib/lfrfid/protocols/protocol_awid.c @@ -207,7 +207,7 @@ bool protocol_awid_write_data(ProtocolAwid* protocol, void* data) { // Fix incorrect length byte if(protocol->data[0] != 26 && protocol->data[0] != 50 && protocol->data[0] != 37 && - protocol->data[0] != 34 && protocol->data[0] != 36 ) { + protocol->data[0] != 34 && protocol->data[0] != 36) { protocol->data[0] = 26; } From f11df494683ecadd20fe760ec27d2e77115aee90 Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Fri, 28 Oct 2022 10:18:41 +0300 Subject: [PATCH 31/42] [FL-2828] Dolphin score update take 2 (#1929) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move DolphinDeedNfcRead * Move DolphinDeedNfcReadSuccess * Move DolphinDeedNfcSave * Move DolphinDeedNfcDetectReader * Move DolphinDeedNfcEmulate * Count DolphinDeedNfcEmulate when launched from file browser * Implement most of the score accounting for NFC * Fully update Nfc icounter handling * Move DolphinDeedSubGhzFrequencyAnalyzer * Update the rest of icounter in SubGHz * Adjust SubGHz icounter handling * Adjust LFRFID icounter handling * Adjust Infrared icounter handling * Don't count renaming RFID tags as saving * Don't count renaming SubGHz signals as saving * Don't count renaming NFC tags as saving * Adjust iButton icounter handling * Minor code refactoring * Correct formatting * Account for emulating iButton keys from file manager/rpc Co-authored-by: あく --- applications/main/ibutton/ibutton.c | 3 +++ .../main/ibutton/scenes/ibutton_scene_add_value.c | 3 --- .../main/ibutton/scenes/ibutton_scene_emulate.c | 3 --- .../main/ibutton/scenes/ibutton_scene_read.c | 3 +-- .../ibutton/scenes/ibutton_scene_read_key_menu.c | 2 ++ .../main/ibutton/scenes/ibutton_scene_save_name.c | 10 ++++++++++ .../main/ibutton/scenes/ibutton_scene_save_success.c | 2 -- .../ibutton/scenes/ibutton_scene_saved_key_menu.c | 2 ++ .../main/ibutton/scenes/ibutton_scene_start.c | 2 ++ .../main/infrared/scenes/infrared_scene_learn.c | 2 ++ .../main/infrared/scenes/infrared_scene_learn_done.c | 3 --- .../scenes/infrared_scene_learn_enter_name.c | 2 ++ .../infrared/scenes/infrared_scene_learn_success.c | 3 --- applications/main/lfrfid/lfrfid.c | 5 ++++- .../main/lfrfid/scenes/lfrfid_scene_emulate.c | 3 --- .../main/lfrfid/scenes/lfrfid_scene_extra_actions.c | 3 +++ applications/main/lfrfid/scenes/lfrfid_scene_read.c | 3 +-- .../main/lfrfid/scenes/lfrfid_scene_read_key_menu.c | 2 ++ .../main/lfrfid/scenes/lfrfid_scene_save_data.c | 2 -- .../main/lfrfid/scenes/lfrfid_scene_save_name.c | 8 ++++++++ .../main/lfrfid/scenes/lfrfid_scene_save_success.c | 2 -- .../main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c | 2 ++ applications/main/lfrfid/scenes/lfrfid_scene_start.c | 2 ++ applications/main/nfc/nfc.c | 4 ++++ .../main/nfc/scenes/nfc_scene_detect_reader.c | 2 -- applications/main/nfc/scenes/nfc_scene_emulate_uid.c | 2 -- .../main/nfc/scenes/nfc_scene_emv_read_success.c | 2 -- .../nfc/scenes/nfc_scene_mf_classic_dict_attack.c | 4 ++++ .../main/nfc/scenes/nfc_scene_mf_classic_emulate.c | 2 -- .../main/nfc/scenes/nfc_scene_mf_classic_keys_add.c | 2 ++ .../main/nfc/scenes/nfc_scene_mf_classic_menu.c | 7 +++++-- .../nfc/scenes/nfc_scene_mf_classic_read_success.c | 3 --- .../main/nfc/scenes/nfc_scene_mf_desfire_menu.c | 6 ++++++ .../nfc/scenes/nfc_scene_mf_ultralight_emulate.c | 2 -- .../main/nfc/scenes/nfc_scene_mf_ultralight_menu.c | 6 ++++++ .../nfc/scenes/nfc_scene_mf_ultralight_read_auth.c | 2 -- .../nfc_scene_mf_ultralight_read_auth_result.c | 4 ---- .../scenes/nfc_scene_mf_ultralight_read_success.c | 2 -- .../nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c | 2 ++ applications/main/nfc/scenes/nfc_scene_nfca_menu.c | 6 ++++++ .../main/nfc/scenes/nfc_scene_nfca_read_success.c | 3 --- applications/main/nfc/scenes/nfc_scene_read.c | 7 ++++++- .../main/nfc/scenes/nfc_scene_read_card_success.c | 2 -- applications/main/nfc/scenes/nfc_scene_save_name.c | 8 ++++++++ .../main/nfc/scenes/nfc_scene_save_success.c | 2 -- applications/main/nfc/scenes/nfc_scene_saved_menu.c | 2 ++ applications/main/nfc/scenes/nfc_scene_set_uid.c | 2 -- applications/main/nfc/scenes/nfc_scene_start.c | 3 +++ .../subghz/scenes/subghz_scene_frequency_analyzer.c | 2 -- .../main/subghz/scenes/subghz_scene_read_raw.c | 7 ++++++- .../main/subghz/scenes/subghz_scene_receiver.c | 2 ++ .../main/subghz/scenes/subghz_scene_receiver_info.c | 2 -- .../main/subghz/scenes/subghz_scene_save_name.c | 12 ++++++++++++ .../main/subghz/scenes/subghz_scene_save_success.c | 3 --- .../main/subghz/scenes/subghz_scene_set_type.c | 2 -- applications/main/subghz/scenes/subghz_scene_start.c | 2 ++ .../main/subghz/scenes/subghz_scene_transmitter.c | 2 +- applications/services/dolphin/helpers/dolphin_deed.c | 2 +- applications/services/dolphin/helpers/dolphin_deed.h | 2 +- 59 files changed, 125 insertions(+), 72 deletions(-) diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 887fb3e7..b6d8361b 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -5,6 +5,7 @@ #include #include #include +#include #define TAG "iButtonApp" @@ -337,11 +338,13 @@ int32_t ibutton_app(void* p) { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); + DOLPHIN_DEED(DolphinDeedIbuttonEmulate); } else { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); if(key_loaded) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); + DOLPHIN_DEED(DolphinDeedIbuttonEmulate); } else { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_value.c b/applications/main/ibutton/scenes/ibutton_scene_add_value.c index b3ec11a5..ccac7612 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_add_value.c +++ b/applications/main/ibutton/scenes/ibutton_scene_add_value.c @@ -1,7 +1,5 @@ #include "../ibutton_i.h" -#include - void ibutton_scene_add_type_byte_input_callback(void* context) { iButton* ibutton = context; view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditResult); @@ -38,7 +36,6 @@ bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) { consumed = true; if(event.event == iButtonCustomEventByteEditResult) { ibutton_key_set_data(ibutton->key, new_key_data, IBUTTON_KEY_DATA_SIZE); - DOLPHIN_DEED(DolphinDeedIbuttonAdd); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName); } } diff --git a/applications/main/ibutton/scenes/ibutton_scene_emulate.c b/applications/main/ibutton/scenes/ibutton_scene_emulate.c index b3bc38ea..6f6ffcf5 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_emulate.c +++ b/applications/main/ibutton/scenes/ibutton_scene_emulate.c @@ -1,6 +1,5 @@ #include "../ibutton_i.h" #include -#include #include #define EMULATE_TIMEOUT_TICKS 10 @@ -26,8 +25,6 @@ void ibutton_scene_emulate_on_enter(void* context) { path_extract_filename(ibutton->file_path, key_name, true); } - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); - // check that stored key has name if(!furi_string_empty(key_name)) { ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name)); diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c index 05920a0a..1fe75e45 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read.c @@ -11,7 +11,6 @@ void ibutton_scene_read_on_enter(void* context) { Popup* popup = ibutton->popup; iButtonKey* key = ibutton->key; iButtonWorker* worker = ibutton->key_worker; - DOLPHIN_DEED(DolphinDeedIbuttonRead); popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom); popup_set_text(popup, "Waiting\nfor key ...", 95, 30, AlignCenter, AlignTop); @@ -54,8 +53,8 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) { if(success) { ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess); ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn); - DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); + DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); } } } diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c index 921b24fc..0a8ecfa5 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c @@ -1,4 +1,5 @@ #include "../ibutton_i.h" +#include typedef enum { SubmenuIndexSave, @@ -49,6 +50,7 @@ bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName); } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); + DOLPHIN_DEED(DolphinDeedIbuttonEmulate); } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_name.c b/applications/main/ibutton/scenes/ibutton_scene_save_name.c index 773b93e0..5f25a000 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_name.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_name.c @@ -1,6 +1,7 @@ #include "../ibutton_i.h" #include #include +#include static void ibutton_scene_save_name_text_input_callback(void* context) { iButton* ibutton = context; @@ -57,6 +58,15 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(event.event == iButtonCustomEventTextEditResult) { if(ibutton_save_key(ibutton, ibutton->text_store)) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess); + if(scene_manager_has_previous_scene( + ibutton->scene_manager, iButtonSceneSavedKeyMenu)) { + // Nothing, do not count editing as saving + } else if(scene_manager_has_previous_scene( + ibutton->scene_manager, iButtonSceneAddType)) { + DOLPHIN_DEED(DolphinDeedIbuttonAdd); + } else { + DOLPHIN_DEED(DolphinDeedIbuttonSave); + } } else { const uint32_t possible_scenes[] = { iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_success.c b/applications/main/ibutton/scenes/ibutton_scene_save_success.c index 43237f42..e0b9b3c4 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_success.c @@ -1,5 +1,4 @@ #include "../ibutton_i.h" -#include static void ibutton_scene_save_success_popup_callback(void* context) { iButton* ibutton = context; @@ -9,7 +8,6 @@ static void ibutton_scene_save_success_popup_callback(void* context) { void ibutton_scene_save_success_on_enter(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - DOLPHIN_DEED(DolphinDeedIbuttonSave); popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); diff --git a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c index 3d588dd0..e4c9c350 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c @@ -1,4 +1,5 @@ #include "../ibutton_i.h" +#include enum SubmenuIndex { SubmenuIndexEmulate, @@ -58,6 +59,7 @@ bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent even consumed = true; if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); + DOLPHIN_DEED(DolphinDeedIbuttonEmulate); } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite); } else if(event.event == SubmenuIndexEdit) { diff --git a/applications/main/ibutton/scenes/ibutton_scene_start.c b/applications/main/ibutton/scenes/ibutton_scene_start.c index dde224e1..b8f6b07d 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_start.c +++ b/applications/main/ibutton/scenes/ibutton_scene_start.c @@ -1,5 +1,6 @@ #include "../ibutton_i.h" #include "ibutton/scenes/ibutton_scene.h" +#include enum SubmenuIndex { SubmenuIndexRead, @@ -38,6 +39,7 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) { consumed = true; if(event.event == SubmenuIndexRead) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); + DOLPHIN_DEED(DolphinDeedIbuttonRead); } else if(event.event == SubmenuIndexSaved) { furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); diff --git a/applications/main/infrared/scenes/infrared_scene_learn.c b/applications/main/infrared/scenes/infrared_scene_learn.c index 37f9b3e0..48699a71 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn.c +++ b/applications/main/infrared/scenes/infrared_scene_learn.c @@ -1,4 +1,5 @@ #include "../infrared_i.h" +#include void infrared_scene_learn_on_enter(void* context) { Infrared* infrared = context; @@ -27,6 +28,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) { if(event.event == InfraredCustomEventTypeSignalReceived) { infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess); + DOLPHIN_DEED(DolphinDeedIrLearnSuccess); consumed = true; } } diff --git a/applications/main/infrared/scenes/infrared_scene_learn_done.c b/applications/main/infrared/scenes/infrared_scene_learn_done.c index 7d357171..54b7da72 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_done.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_done.c @@ -1,13 +1,10 @@ #include "../infrared_i.h" -#include - void infrared_scene_learn_done_on_enter(void* context) { Infrared* infrared = context; Popup* popup = infrared->popup; popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - DOLPHIN_DEED(DolphinDeedIrSave); if(infrared->app_state.is_learning_new_remote) { popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop); diff --git a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c index b6a7eac0..a8772a98 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c @@ -1,4 +1,5 @@ #include "../infrared_i.h" +#include void infrared_scene_learn_enter_name_on_enter(void* context) { Infrared* infrared = context; @@ -49,6 +50,7 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e if(success) { scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); + DOLPHIN_DEED(DolphinDeedIrSave); } else { dialog_message_show_storage_error(infrared->dialogs, "Failed to save file"); const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; diff --git a/applications/main/infrared/scenes/infrared_scene_learn_success.c b/applications/main/infrared/scenes/infrared_scene_learn_success.c index 46662714..469d4de9 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_success.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_success.c @@ -1,7 +1,5 @@ #include "../infrared_i.h" -#include - static void infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) { Infrared* infrared = context; @@ -13,7 +11,6 @@ void infrared_scene_learn_success_on_enter(void* context) { DialogEx* dialog_ex = infrared->dialog_ex; InfraredSignal* signal = infrared->received_signal; - DOLPHIN_DEED(DolphinDeedIrLearnSuccess); infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn); if(infrared_signal_is_raw(signal)) { diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index b0f98937..51322730 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -1,4 +1,5 @@ #include "lfrfid_i.h" +#include static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -182,12 +183,14 @@ int32_t lfrfid_app(void* p) { view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc); + DOLPHIN_DEED(DolphinDeedRfidEmulate); } else { furi_string_set(app->file_path, args); lfrfid_load_key_data(app, app->file_path, true); view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + DOLPHIN_DEED(DolphinDeedRfidEmulate); } } else { @@ -311,4 +314,4 @@ void lfrfid_widget_callback(GuiButtonType result, InputType type, void* context) void lfrfid_text_input_callback(void* context) { LfRfid* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventNext); -} \ No newline at end of file +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c index 2725982f..dc391899 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c @@ -1,12 +1,9 @@ #include "../lfrfid_i.h" -#include void lfrfid_scene_emulate_on_enter(void* context) { LfRfid* app = context; Popup* popup = app->popup; - DOLPHIN_DEED(DolphinDeedRfidEmulate); - popup_set_header(popup, "Emulating", 89, 30, AlignCenter, AlignTop); if(!furi_string_empty(app->file_name)) { popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index d7fd93e1..fac2ebce 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -1,4 +1,5 @@ #include "../lfrfid_i.h" +#include typedef enum { SubmenuIndexASK, @@ -57,10 +58,12 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) if(event.event == SubmenuIndexASK) { app->read_type = LFRFIDWorkerReadTypeASKOnly; scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexPSK) { app->read_type = LFRFIDWorkerReadTypePSKOnly; scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexRAW) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_read.c index 4bdb215d..5f195972 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read.c @@ -46,7 +46,6 @@ static void void lfrfid_scene_read_on_enter(void* context) { LfRfid* app = context; - DOLPHIN_DEED(DolphinDeedRfidRead); if(app->read_type == LFRFIDWorkerReadTypePSKOnly) { lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPskOnly); } else if(app->read_type == LFRFIDWorkerReadTypeASKOnly) { @@ -79,10 +78,10 @@ bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == LfRfidEventReadDone) { app->protocol_id = app->protocol_id_next; - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); notification_message(app->notifications, &sequence_success); furi_string_reset(app->file_name); scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess); + DOLPHIN_DEED(DolphinDeedRfidReadSuccess); consumed = true; } else if(event.event == LfRfidEventReadStartPSK) { if(app->read_type == LFRFIDWorkerReadTypeAuto) { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c index 7480304b..081c4791 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c @@ -1,4 +1,5 @@ #include "../lfrfid_i.h" +#include typedef enum { SubmenuIndexSave, @@ -43,6 +44,7 @@ bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) consumed = true; } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + DOLPHIN_DEED(DolphinDeedRfidEmulate); consumed = true; } scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c index 6c5ea2f2..11a687bd 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c @@ -1,5 +1,4 @@ #include "../lfrfid_i.h" -#include void lfrfid_scene_save_data_on_enter(void* context) { LfRfid* app = context; @@ -32,7 +31,6 @@ bool lfrfid_scene_save_data_on_event(void* context, SceneManagerEvent event) { consumed = true; size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); protocol_dict_set_data(app->dict, app->protocol_id, app->new_key_data, size); - DOLPHIN_DEED(DolphinDeedRfidAdd); scene_manager_next_scene(scene_manager, LfRfidSceneSaveName); scene_manager_set_scene_state(scene_manager, LfRfidSceneSaveData, 1); } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c index ca9a52de..87e110f1 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c @@ -1,5 +1,6 @@ #include #include "../lfrfid_i.h" +#include void lfrfid_scene_save_name_on_enter(void* context) { LfRfid* app = context; @@ -55,6 +56,13 @@ bool lfrfid_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(lfrfid_save_key(app)) { scene_manager_next_scene(scene_manager, LfRfidSceneSaveSuccess); + if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSavedKeyMenu)) { + // Nothing, do not count editing as saving + } else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) { + DOLPHIN_DEED(DolphinDeedRfidAdd); + } else { + DOLPHIN_DEED(DolphinDeedRfidSave); + } } else { scene_manager_search_and_switch_to_previous_scene( scene_manager, LfRfidSceneReadKeyMenu); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c index e91ad04e..52aefa84 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c @@ -1,5 +1,4 @@ #include "../lfrfid_i.h" -#include void lfrfid_scene_save_success_on_enter(void* context) { LfRfid* app = context; @@ -8,7 +7,6 @@ void lfrfid_scene_save_success_on_enter(void* context) { // Clear state of data enter scene scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveData, 0); - DOLPHIN_DEED(DolphinDeedRfidSave); popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); popup_set_context(popup, app); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c index 040b31f1..d3c3d389 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c @@ -1,4 +1,5 @@ #include "../lfrfid_i.h" +#include typedef enum { SubmenuIndexEmulate, @@ -42,6 +43,7 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + DOLPHIN_DEED(DolphinDeedRfidEmulate); consumed = true; } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c index d6d87f44..8e1c92db 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_start.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c @@ -1,4 +1,5 @@ #include "../lfrfid_i.h" +#include typedef enum { SubmenuIndexRead, @@ -47,6 +48,7 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexSaved) { furi_string_set(app->file_path, LFRFID_APP_FOLDER); diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 0b685f54..55c68a45 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -1,5 +1,6 @@ #include "nfc_i.h" #include "furi_hal_nfc.h" +#include bool nfc_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -275,12 +276,15 @@ int32_t nfc_app(void* p) { if(nfc_device_load(nfc->dev, p, true)) { if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } } else { // Exit app diff --git a/applications/main/nfc/scenes/nfc_scene_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_detect_reader.c index f0177f9c..abf1437d 100644 --- a/applications/main/nfc/scenes/nfc_scene_detect_reader.c +++ b/applications/main/nfc/scenes/nfc_scene_detect_reader.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include #define NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX (10U) @@ -26,7 +25,6 @@ void nfc_scene_detect_reader_callback(void* context) { void nfc_scene_detect_reader_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcDetectReader); detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc); detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX); diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c index 8bb20796..5ddb6099 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include #define NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX (200) @@ -59,7 +58,6 @@ static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) { void nfc_scene_emulate_uid_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); // Setup Widget nfc_scene_emulate_uid_widget_config(nfc, false); diff --git a/applications/main/nfc/scenes/nfc_scene_emv_read_success.c b/applications/main/nfc/scenes/nfc_scene_emv_read_success.c index 6a0b32fa..005b76cb 100644 --- a/applications/main/nfc/scenes/nfc_scene_emv_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_emv_read_success.c @@ -1,6 +1,5 @@ #include "../nfc_i.h" #include "../helpers/nfc_emv_parser.h" -#include void nfc_scene_emv_read_success_widget_callback( GuiButtonType result, @@ -15,7 +14,6 @@ void nfc_scene_emv_read_success_widget_callback( void nfc_scene_emv_read_success_on_enter(void* context) { Nfc* nfc = context; EmvData* emv_data = &nfc->dev->dev_data.emv_data; - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); // Setup Custom Widget view widget_add_button_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index 81354690..acb5b783 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include #define TAG "NfcMfClassicDictAttack" @@ -110,6 +111,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent } else { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } } else if(event.event == NfcWorkerEventAborted) { @@ -119,6 +121,8 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent } else { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); + // Counting failed attempts too + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } } else if(event.event == NfcWorkerEventCardDetected) { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c index 65639b2b..e514fa72 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include #define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL) #define NFC_MF_CLASSIC_DATA_CHANGED (1UL) @@ -15,7 +14,6 @@ bool nfc_mf_classic_emulate_worker_callback(NfcWorkerEvent event, void* context) void nfc_scene_mf_classic_emulate_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); // Setup view Popup* popup = nfc->popup; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c index 2921d21c..b122aa22 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include void nfc_scene_mf_classic_keys_add_byte_input_callback(void* context) { Nfc* nfc = context; @@ -36,6 +37,7 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + DOLPHIN_DEED(DolphinDeedNfcMfcAdd); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c index 2cba0433..a5bb10dd 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c @@ -36,8 +36,6 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexSave) { - DOLPHIN_DEED(DolphinDeedNfcMfcAdd); - scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave); nfc->dev->format = NfcDeviceSaveFormatMifareClassic; @@ -49,6 +47,11 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } consumed = true; } else if(event.event == SubmenuIndexInfo) { scene_manager_set_scene_state( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c index 0cdb8646..ae31e92c 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_mf_classic_read_success_widget_callback( GuiButtonType result, @@ -18,8 +17,6 @@ void nfc_scene_mf_classic_read_success_on_enter(void* context) { NfcDeviceData* dev_data = &nfc->dev->dev_data; MfClassicData* mf_data = &dev_data->mf_classic_data; - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - // Setup view Widget* widget = nfc->widget; widget_add_button_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c index 1e2f2d2f..bee63d77 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include enum SubmenuIndex { SubmenuIndexSave, @@ -48,6 +49,11 @@ bool nfc_scene_mf_desfire_menu_on_event(void* context, SceneManagerEvent event) consumed = true; } else if(event.event == SubmenuIndexEmulateUid) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } consumed = true; } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c index 712ddc07..e84fb392 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include #define NFC_MF_UL_DATA_NOT_CHANGED (0UL) #define NFC_MF_UL_DATA_CHANGED (1UL) @@ -15,7 +14,6 @@ bool nfc_mf_ultralight_emulate_worker_callback(NfcWorkerEvent event, void* conte void nfc_scene_mf_ultralight_emulate_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); // Setup view Popup* popup = nfc->popup; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c index ba9f2233..ab4d37b0 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include enum SubmenuIndex { SubmenuIndexUnlock, @@ -56,6 +57,11 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even consumed = true; } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } consumed = true; } else if(event.event == SubmenuIndexUnlock) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c index 25008004..5dbb0c18 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include typedef enum { NfcSceneMfUlReadStateIdle, @@ -51,7 +50,6 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcRead); nfc_device_clear(nfc->dev); // Setup view diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c index 5a690a21..178d0335 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_mf_ultralight_read_auth_result_widget_callback( GuiButtonType result, @@ -37,7 +36,6 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { widget_add_string_element( widget, 0, 17, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); if(mf_ul_data->auth_success) { - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); furi_string_printf( temp_str, "Password: %02X %02X %02X %02X", @@ -54,8 +52,6 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { config_pages->auth_data.pack.raw[1]); widget_add_string_element( widget, 0, 39, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); - } else { - DOLPHIN_DEED(DolphinDeedNfcMfulError); } furi_string_printf( temp_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c index 77034ea8..63bffbf3 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_mf_ultralight_read_success_widget_callback( GuiButtonType result, @@ -14,7 +13,6 @@ void nfc_scene_mf_ultralight_read_success_widget_callback( void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); // Setup widget view FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c index 58e081db..514cd4e9 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void* context) { Nfc* nfc = context; @@ -30,6 +31,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultCenter) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + DOLPHIN_DEED(DolphinDeedNfcRead); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfca_menu.c b/applications/main/nfc/scenes/nfc_scene_nfca_menu.c index 00d0d943..30f63945 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfca_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfca_menu.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include enum SubmenuIndex { SubmenuIndexSaveUid, @@ -41,6 +42,11 @@ bool nfc_scene_nfca_menu_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == SubmenuIndexEmulateUid) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } consumed = true; } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); diff --git a/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c b/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c index 2ea7c992..a38f31a9 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_nfca_read_success_widget_callback( GuiButtonType result, @@ -16,8 +15,6 @@ void nfc_scene_nfca_read_success_widget_callback( void nfc_scene_nfca_read_success_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - // Setup view FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; Widget* widget = nfc->widget; diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index e6df476f..1f82aef0 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -39,7 +39,6 @@ void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) { void nfc_scene_read_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcRead); nfc_device_clear(nfc->dev); // Setup view @@ -62,26 +61,32 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { (event.event == NfcWorkerEventReadUidNfcV)) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadUidNfcA) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDone) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfDesfire) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadBankCard) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_success.c b/applications/main/nfc/scenes/nfc_scene_read_card_success.c index 352cb4a7..9b2a2188 100644 --- a/applications/main/nfc/scenes/nfc_scene_read_card_success.c +++ b/applications/main/nfc/scenes/nfc_scene_read_card_success.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_read_card_success_widget_callback( GuiButtonType result, @@ -18,7 +17,6 @@ void nfc_scene_read_card_success_on_enter(void* context) { FuriString* temp_str; temp_str = furi_string_alloc(); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); // Setup view FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c index 736eab7d..791f8d99 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_name.c +++ b/applications/main/nfc/scenes/nfc_scene_save_name.c @@ -2,6 +2,7 @@ #include #include #include +#include void nfc_scene_save_name_text_input_callback(void* context) { Nfc* nfc = context; @@ -63,6 +64,13 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1); if(nfc_device_save(nfc->dev, nfc->text_store)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + // Nothing, do not count editing as saving + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddSave); + } else { + DOLPHIN_DEED(DolphinDeedNfcSave); + } consumed = true; } else { consumed = scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index dcd2519f..34919cbd 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_save_success_popup_callback(void* context) { Nfc* nfc = context; @@ -8,7 +7,6 @@ void nfc_scene_save_success_popup_callback(void* context) { void nfc_scene_save_success_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcSave); // Setup view Popup* popup = nfc->popup; diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index fe65b5b8..09d2c2d6 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include enum SubmenuIndex { SubmenuIndexEmulate, @@ -76,6 +77,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } + DOLPHIN_DEED(DolphinDeedNfcEmulate); consumed = true; } else if(event.event == SubmenuIndexRename) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index 9622ba21..1d3fb5eb 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_set_uid_byte_input_callback(void* context) { Nfc* nfc = context; @@ -30,7 +29,6 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { - DOLPHIN_DEED(DolphinDeedNfcAddSave); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; if(nfc_device_save(nfc->dev, nfc->dev->dev_name)) { diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index 1a9051df..0c4ec1cf 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include enum SubmenuIndex { SubmenuIndexRead, @@ -47,11 +48,13 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + DOLPHIN_DEED(DolphinDeedNfcRead); consumed = true; } else if(event.event == SubmenuIndexDetectReader) { bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; if(sd_exist) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + DOLPHIN_DEED(DolphinDeedNfcDetectReader); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } diff --git a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c index 9595c61b..b48a821d 100644 --- a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -1,6 +1,5 @@ #include "../subghz_i.h" #include "../views/subghz_frequency_analyzer.h" -#include void subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* context) { furi_assert(context); @@ -10,7 +9,6 @@ void subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* con void subghz_scene_frequency_analyzer_on_enter(void* context) { SubGhz* subghz = context; - DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); subghz_frequency_analyzer_set_callback( subghz->subghz_frequency_analyzer, subghz_scene_frequency_analyzer_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdFrequencyAnalyzer); diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 8ac9bf5b..ba9ce803 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -223,7 +223,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); } else { - DOLPHIN_DEED(DolphinDeedSubGhzSend); + if(scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneSaved) || + !scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + DOLPHIN_DEED(DolphinDeedSubGhzSend); + } // set callback end tx subghz_protocol_raw_file_encoder_worker_set_callback_end( (SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance( diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 77a92145..2b01e297 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -1,5 +1,6 @@ #include "../subghz_i.h" #include "../views/receiver.h" +#include static const NotificationSequence subghs_sequence_rx = { &message_green_255, @@ -181,6 +182,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz->txrx->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); + DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); consumed = true; break; case SubGhzCustomEventViewReceiverConfig: diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 03a8ebbc..bc6c8f11 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -1,6 +1,5 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -#include void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); @@ -45,7 +44,6 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { void subghz_scene_receiver_info_on_enter(void* context) { SubGhz* subghz = context; - DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); if(subghz_scene_receiver_info_update_parser(subghz)) { FuriString* frequency_str; FuriString* modulation_str; diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index dfcb6586..33846c28 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -4,6 +4,7 @@ #include "../helpers/subghz_custom_event.h" #include #include +#include #define MAX_TEXT_INPUT_LEN 22 @@ -131,6 +132,17 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { } scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); + if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSavedMenu)) { + // Nothing, do not count editing as saving + } else if(scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneMoreRAW)) { + // Ditto, for RAW signals + } else if(scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneSetType)) { + DOLPHIN_DEED(DolphinDeedSubGhzAddManually); + } else { + DOLPHIN_DEED(DolphinDeedSubGhzSave); + } return true; } else { furi_string_set(subghz->error_str, "No name file"); diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c index 3d5c16ca..d32c9271 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_success.c +++ b/applications/main/subghz/scenes/subghz_scene_save_success.c @@ -1,7 +1,5 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -#include -#include void subghz_scene_save_success_popup_callback(void* context) { SubGhz* subghz = context; @@ -10,7 +8,6 @@ void subghz_scene_save_success_popup_callback(void* context) { void subghz_scene_save_success_on_enter(void* context) { SubGhz* subghz = context; - DOLPHIN_DEED(DolphinDeedSubGhzSave); // Setup view Popup* popup = subghz->popup; diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 44fe2fc7..2ed53719 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -381,7 +380,6 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { if(generated_protocol) { subghz_file_name_clear(subghz); - DOLPHIN_DEED(DolphinDeedSubGhzAddManually); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); return true; } diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index f37ccae9..0b1c3c15 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -1,4 +1,5 @@ #include "../subghz_i.h" +#include enum SubmenuIndex { SubmenuIndexRead = 10, @@ -84,6 +85,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); + DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); return true; } else if(event.event == SubmenuIndexTest) { scene_manager_set_scene_state( diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 5da6f430..fbe954f0 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -50,7 +50,6 @@ bool subghz_scene_transmitter_update_data_show(void* context) { void subghz_scene_transmitter_on_enter(void* context) { SubGhz* subghz = context; - DOLPHIN_DEED(DolphinDeedSubGhzSend); if(!subghz_scene_transmitter_update_data_show(subghz)) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventViewTransmitterError); @@ -78,6 +77,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { } else { subghz->state_notifications = SubGhzNotificationStateTx; subghz_scene_transmitter_update_data_show(subghz); + DOLPHIN_DEED(DolphinDeedSubGhzSend); } } return true; diff --git a/applications/services/dolphin/helpers/dolphin_deed.c b/applications/services/dolphin/helpers/dolphin_deed.c index ce3e058b..51db56fd 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.c +++ b/applications/services/dolphin/helpers/dolphin_deed.c @@ -21,8 +21,8 @@ static const DolphinDeedWeight dolphin_deed_weights[] = { {1, DolphinAppNfc}, // DolphinDeedNfcDetectReader {2, DolphinAppNfc}, // DolphinDeedNfcEmulate {2, DolphinAppNfc}, // DolphinDeedNfcMfcAdd - {1, DolphinAppNfc}, // DolphinDeedNfcMfulError {1, DolphinAppNfc}, // DolphinDeedNfcAddSave + {1, DolphinAppNfc}, // DolphinDeedNfcAddEmulate {1, DolphinAppIr}, // DolphinDeedIrSend {3, DolphinAppIr}, // DolphinDeedIrLearnSuccess diff --git a/applications/services/dolphin/helpers/dolphin_deed.h b/applications/services/dolphin/helpers/dolphin_deed.h index abe027d7..c9cd18f3 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.h +++ b/applications/services/dolphin/helpers/dolphin_deed.h @@ -37,8 +37,8 @@ typedef enum { DolphinDeedNfcDetectReader, DolphinDeedNfcEmulate, DolphinDeedNfcMfcAdd, - DolphinDeedNfcMfulError, DolphinDeedNfcAddSave, + DolphinDeedNfcAddEmulate, DolphinDeedIrSend, DolphinDeedIrLearnSuccess, From 26f852839a93f82a3d863fc6a6e64c9ebd7e6f0a Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Fri, 28 Oct 2022 15:16:54 +0400 Subject: [PATCH 32/42] WS: fix Acurite-606TX protocol (#1938) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WS: fix acurite_606tx protocol * WS: update version * WeatherStation: remove break from invalid place Co-authored-by: あく --- .../helpers/weather_station_types.h | 2 +- .../weather_station/protocols/acurite_606tx.c | 64 +++++++++---------- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h index 479638b9..2976cbce 100644 --- a/applications/plugins/weather_station/helpers/weather_station_types.h +++ b/applications/plugins/weather_station/helpers/weather_station_types.h @@ -3,7 +3,7 @@ #include #include -#define WS_VERSION_APP "0.3" +#define WS_VERSION_APP "0.3.1" #define WS_DEVELOPED "SkorP" #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.c b/applications/plugins/weather_station/protocols/acurite_606tx.c index a92ec243..3c914405 100644 --- a/applications/plugins/weather_station/protocols/acurite_606tx.c +++ b/applications/plugins/weather_station/protocols/acurite_606tx.c @@ -151,41 +151,37 @@ void ws_protocol_decoder_acurite_606tx_feed(void* context, bool level, uint32_t case Acurite_606TXDecoderStepCheckDuration: if(!level) { - if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_606tx_const.te_short) < - ws_protocol_acurite_606tx_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_short) < - ws_protocol_acurite_606tx_const.te_delta)) { - //Found syncPostfix - instance->decoder.parser_step = Acurite_606TXDecoderStepReset; - if((instance->decoder.decode_count_bit == - ws_protocol_acurite_606tx_const.min_count_bit_for_found) && - ws_protocol_acurite_606tx_check(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_acurite_606tx_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); + if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_606tx_const.te_short) < + ws_protocol_acurite_606tx_const.te_delta) { + if((DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_short) < + ws_protocol_acurite_606tx_const.te_delta) || + (duration > ws_protocol_acurite_606tx_const.te_long * 3)) { + //Found syncPostfix + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_acurite_606tx_const.min_count_bit_for_found) && + ws_protocol_acurite_606tx_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_acurite_606tx_remote_controller(&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; + } else if( + DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_long) < + ws_protocol_acurite_606tx_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_long * 2) < + ws_protocol_acurite_606tx_const.te_delta * 4) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - - break; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, ws_protocol_acurite_606tx_const.te_short) < - ws_protocol_acurite_606tx_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_long) < - ws_protocol_acurite_606tx_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, ws_protocol_acurite_606tx_const.te_short) < - ws_protocol_acurite_606tx_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_long * 2) < - ws_protocol_acurite_606tx_const.te_delta * 4)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; } else { instance->decoder.parser_step = Acurite_606TXDecoderStepReset; } From be3ee9f2fe7a06fc2cd8e959c3dcb3e7fde374a7 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko <807634+merlokk@users.noreply.github.com> Date: Fri, 28 Oct 2022 16:42:59 +0300 Subject: [PATCH 33/42] Oregon2 additional sensors defines (#1933) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added a list of sensors and added several additional temperature and temperature/humidity sensor id's * now here are only sensors that have test data Co-authored-by: あく --- .../weather_station/protocols/oregon2.c | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/applications/plugins/weather_station/protocols/oregon2.c b/applications/plugins/weather_station/protocols/oregon2.c index 76bc3f0a..d294548e 100644 --- a/applications/plugins/weather_station/protocols/oregon2.c +++ b/applications/plugins/weather_station/protocols/oregon2.c @@ -29,6 +29,40 @@ static const SubGhzBlockConst ws_oregon2_const = { // bit indicating the low battery #define OREGON2_FLAG_BAT_LOW 0x4 +/// Documentation for Oregon Scientific protocols can be found here: +/// http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf +// Sensors ID +#define ID_THGR122N 0x1d20 +#define ID_THGR968 0x1d30 +#define ID_BTHR918 0x5d50 +#define ID_BHTR968 0x5d60 +#define ID_RGR968 0x2d10 +#define ID_THR228N 0xec40 +#define ID_THN132N 0xec40 // same as THR228N but different packet size +#define ID_RTGN318 0x0cc3 // warning: id is from 0x0cc3 and 0xfcc3 +#define ID_RTGN129 0x0cc3 // same as RTGN318 but different packet size +#define ID_THGR810 0xf824 // This might be ID_THGR81, but what's true is lost in (git) history +#define ID_THGR810a 0xf8b4 // unconfirmed version +#define ID_THN802 0xc844 +#define ID_PCR800 0x2914 +#define ID_PCR800a 0x2d14 // Different PCR800 ID - AU version I think +#define ID_WGR800 0x1984 +#define ID_WGR800a 0x1994 // unconfirmed version +#define ID_WGR968 0x3d00 +#define ID_UV800 0xd874 +#define ID_THN129 0xcc43 // THN129 Temp only +#define ID_RTHN129 0x0cd3 // RTHN129 Temp, clock sensors +#define ID_BTHGN129 0x5d53 // Baro, Temp, Hygro sensor +#define ID_UVR128 0xec70 +#define ID_THGR328N 0xcc23 // Temp & Hygro sensor similar to THR228N with 5 channel instead of 3 +#define ID_RTGR328N_1 0xdcc3 // RTGR328N_[1-5] RFclock(date &time)&Temp&Hygro sensor +#define ID_RTGR328N_2 0xccc3 +#define ID_RTGR328N_3 0xbcc3 +#define ID_RTGR328N_4 0xacc3 +#define ID_RTGR328N_5 0x9cc3 +#define ID_RTGR328N_6 0x8ce3 // RTGR328N_6&7 RFclock(date &time)&Temp&Hygro sensor like THGR328N +#define ID_RTGR328N_7 0x8ae3 + struct WSProtocolDecoderOregon2 { SubGhzProtocolDecoderBase base; @@ -101,9 +135,12 @@ static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration } // From sensor id code return amount of bits in variable section +// https://temofeev.ru/info/articles/o-dekodirovanii-protokola-pogodnykh-datchikov-oregon-scientific static uint8_t oregon2_sensor_id_var_bits(uint16_t sensor_id) { - if(sensor_id == 0xEC40) return 16; - if(sensor_id == 0x1D20) return 24; + if(sensor_id == ID_THR228N) return 16; + + if(sensor_id == ID_THGR122N) return 24; + return 0; } @@ -134,15 +171,16 @@ static float ws_oregon2_decode_temp(uint32_t data) { } static void ws_oregon2_decode_var_data(WSBlockGeneric* ws_b, uint16_t sensor_id, uint32_t data) { - switch(sensor_id) { - case 0xEC40: + if(sensor_id == ID_THR228N) { ws_b->temp = ws_oregon2_decode_temp(data); ws_b->humidity = WS_NO_HUMIDITY; - break; - case 0x1D20: + return; + } + + if(sensor_id == ID_THGR122N) { ws_b->humidity = bcd_decode_short(data); ws_b->temp = ws_oregon2_decode_temp(data >> 8); - break; + return; } } From 492f147568a6f04896dfea6fba06a9494dfaa65d Mon Sep 17 00:00:00 2001 From: Konstantin Volkov <72250702+doomwastaken@users.noreply.github.com> Date: Fri, 28 Oct 2022 16:59:09 +0300 Subject: [PATCH 34/42] [FL-2887] actions unit tests runner (#1920) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Konstantin Volkov Co-authored-by: あく --- .github/workflows/unit_tests.yml | 56 ++++++++++++++++++++++ scripts/flipper/storage.py | 13 ++++++ scripts/storage.py | 16 +++++++ scripts/testing/await_flipper.py | 48 +++++++++++++++++++ scripts/testing/units.py | 79 ++++++++++++++++++++++++++++++++ 5 files changed, 212 insertions(+) create mode 100644 .github/workflows/unit_tests.yml create mode 100644 scripts/testing/await_flipper.py create mode 100644 scripts/testing/units.py diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml new file mode 100644 index 00000000..8c5bac2a --- /dev/null +++ b/.github/workflows/unit_tests.yml @@ -0,0 +1,56 @@ +name: 'Unit tests' + +on: + pull_request: + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + +jobs: + main: + runs-on: [self-hosted, FlipperZeroTest] + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get flipper from device manager (mock)' + id: device + run: | + echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT + + - name: 'Compile unit tests firmware' + id: compile + continue-on-error: true + run: | + FBT_TOOLCHAIN_PATH=/opt ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 + + - name: 'Wait for flipper to finish updating' + id: connect + if: steps.compile.outcome == 'success' + continue-on-error: true + run: | + python3 ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + + - name: 'Format flipper SD card' + id: format + if: steps.connect.outcome == 'success' + continue-on-error: true + run: | + ./scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext + + - name: 'Copy unit tests to flipper' + id: copy + if: steps.format.outcome == 'success' + continue-on-error: true + run: | + ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/unit_tests /ext/unit_tests + + - name: 'Run units and validate results' + if: steps.copy.outcome == 'success' + continue-on-error: true + run: | + python3 ./scripts/testing/units.py ${{steps.device.outputs.flipper}} diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 5fa8a2c8..6b53f747 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -340,6 +340,19 @@ class FlipperStorage: else: return True + def format_ext(self): + """Create a directory on Flipper""" + self.send_and_wait_eol("storage format /ext\r") + self.send_and_wait_eol("y\r") + answer = self.read.until(self.CLI_EOL) + self.read.until(self.CLI_PROMPT) + + if self.has_error(answer): + self.last_error = self.get_error(answer) + return False + else: + return True + def remove(self, path): """Remove file or directory on Flipper""" self.send_and_wait_eol('storage remove "' + path + '"\r') diff --git a/scripts/storage.py b/scripts/storage.py index 167ba88e..ee5dabd4 100755 --- a/scripts/storage.py +++ b/scripts/storage.py @@ -21,6 +21,11 @@ class Main(App): self.parser_mkdir.add_argument("flipper_path", help="Flipper path") self.parser_mkdir.set_defaults(func=self.mkdir) + self.parser_format = self.subparsers.add_parser( + "format_ext", help="Format flash card" + ) + self.parser_format.set_defaults(func=self.format_ext) + self.parser_remove = self.subparsers.add_parser( "remove", help="Remove file/directory" ) @@ -275,6 +280,17 @@ class Main(App): storage.stop() return 0 + def format_ext(self): + if not (storage := self._get_storage()): + return 1 + + self.logger.debug("Formatting /ext SD card") + + if not storage.format_ext(): + self.logger.error(f"Error: {storage.last_error}") + storage.stop() + return 0 + def stress(self): self.logger.error("This test is wearing out flash memory.") self.logger.error("Never use it with internal storage(/int)") diff --git a/scripts/testing/await_flipper.py b/scripts/testing/await_flipper.py new file mode 100644 index 00000000..1f0d1619 --- /dev/null +++ b/scripts/testing/await_flipper.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +import sys, os, time + + +def flp_serial_by_name(flp_name): + if sys.platform == "darwin": # MacOS + flp_serial = "/dev/cu.usbmodemflip_" + flp_name + "1" + elif sys.platform == "linux": # Linux + flp_serial = ( + "/dev/serial/by-id/usb-Flipper_Devices_Inc._Flipper_" + + flp_name + + "_flip_" + + flp_name + + "-if00" + ) + + if os.path.exists(flp_serial): + return flp_serial + else: + if os.path.exists(flp_name): + return flp_name + else: + return "" + + +UPDATE_TIMEOUT = 30 + + +def main(): + flipper_name = sys.argv[1] + elapsed = 0 + flipper = flp_serial_by_name(flipper_name) + + while flipper == "" and elapsed < UPDATE_TIMEOUT: + elapsed += 1 + time.sleep(1) + flipper = flp_serial_by_name(flipper_name) + + if flipper == "": + print(f"Cannot find {flipper_name} flipper. Guess your flipper swam away") + sys.exit(1) + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/scripts/testing/units.py b/scripts/testing/units.py new file mode 100644 index 00000000..83b07899 --- /dev/null +++ b/scripts/testing/units.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +import sys, os +import serial +import re + +from await_flipper import flp_serial_by_name + + +LEAK_THRESHOLD = 3000 # added until units are fixed + + +def main(): + flp_serial = flp_serial_by_name(sys.argv[1]) + + if flp_serial == "": + print("Name or serial port is invalid") + sys.exit(1) + + with serial.Serial(flp_serial, timeout=1) as flipper: + flipper.baudrate = 230400 + flipper.flushOutput() + flipper.flushInput() + + flipper.timeout = 300 + + flipper.read_until(b">: ").decode("utf-8") + flipper.write(b"unit_tests\r") + data = flipper.read_until(b">: ").decode("utf-8") + + lines = data.split("\r\n") + + tests_re = r"Failed tests: \d{0,}" + time_re = r"Consumed: \d{0,}" + leak_re = r"Leaked: \d{0,}" + status_re = r"Status: \w{3,}" + + tests_pattern = re.compile(tests_re) + time_pattern = re.compile(time_re) + leak_pattern = re.compile(leak_re) + status_pattern = re.compile(status_re) + + tests, time, leak, status = None, None, None, None + + for line in lines: + print(line) + if not tests: + tests = re.match(tests_pattern, line) + if not time: + time = re.match(time_pattern, line) + if not leak: + leak = re.match(leak_pattern, line) + if not status: + status = re.match(status_pattern, line) + + if leak is None or time is None or leak is None or status is None: + print("Failed to get data. Or output is corrupt") + sys.exit(1) + + leak = int(re.findall(r"[- ]\d+", leak.group(0))[0]) + status = re.findall(r"\w+", status.group(0))[1] + tests = int(re.findall(r"\d+", tests.group(0))[0]) + time = int(re.findall(r"\d+", time.group(0))[0]) + + if tests > 0 or leak > LEAK_THRESHOLD or status != "PASSED": + print(f"Got {tests} failed tests.") + print(f"Leaked {leak} bytes.") + print(f"Status by flipper: {status}") + print(f"Time elapsed {time/1000} seconds.") + sys.exit(1) + + print( + f"Tests ran successfully! Time elapsed {time/1000} seconds. Passed {tests} tests." + ) + sys.exit(0) + + +if __name__ == "__main__": + main() From 3434305630b4e76f20d703e119c9f3ad1df9cf4b Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Sat, 29 Oct 2022 00:08:50 +1000 Subject: [PATCH 35/42] [FL-2937] Remove resources from API to prevent frequent API version increase (#1935) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove all icons from API * Music player: icons * Signal generator: icons * Bt hid: icons * Weather station: icons * Picopass: icons * File browser test: icons * Example images: documentation * Remove global assets header * Fix picopass Co-authored-by: あく --- .../debug/file_browser_test/application.fam | 1 + .../file_browser_test/file_browser_app.c | 2 +- .../file_browser_test/icons/badusb_10px.png | Bin 0 -> 576 bytes .../examples/example_images/ReadMe.md | 24 +++ applications/main/bad_usb/bad_usb_app_i.h | 1 + .../main/bad_usb/views/bad_usb_view.c | 1 + applications/main/fap_loader/fap_loader_app.c | 1 + applications/main/gpio/gpio_app_i.h | 1 + applications/main/ibutton/ibutton_i.h | 1 + applications/main/infrared/infrared_i.h | 1 + applications/main/lfrfid/lfrfid_i.h | 1 + .../main/lfrfid/views/lfrfid_view_read.c | 1 + applications/main/nfc/nfc_i.h | 1 + applications/main/nfc/views/detect_reader.c | 2 +- applications/main/subghz/subghz_i.h | 1 + applications/main/u2f/u2f_app_i.h | 1 + applications/main/u2f/views/u2f_view.c | 1 + .../bt_hid_app/assets/ButtonDown_7x4.png | Bin 0 -> 102 bytes .../bt_hid_app/assets/ButtonLeft_4x7.png | Bin 0 -> 1415 bytes .../bt_hid_app/assets/ButtonRight_4x7.png | Bin 0 -> 1839 bytes .../bt_hid_app/assets/ButtonUp_7x4.png | Bin 0 -> 102 bytes .../plugins/bt_hid_app/assets/Ok_btn_9x9.png | Bin 0 -> 3605 bytes .../bt_hid_app/assets/Pin_arrow_down_7x9.png | Bin 0 -> 3607 bytes .../bt_hid_app/assets/Pin_arrow_left_9x7.png | Bin 0 -> 3603 bytes .../bt_hid_app/assets/Pin_arrow_right_9x7.png | Bin 0 -> 3602 bytes .../bt_hid_app/assets/Pin_arrow_up_7x9.png | Bin 0 -> 3603 bytes .../bt_hid_app/assets/Pin_back_arrow_10x8.png | Bin 0 -> 3606 bytes .../plugins/music_player/application.fam | 3 +- .../plugins/music_player/icons/music_10px.png | Bin 0 -> 142 bytes .../plugins/music_player/music_player.c | 2 +- applications/plugins/picopass/application.fam | 1 + .../picopass/icons/DolphinMafia_115x62.png | Bin 0 -> 2504 bytes .../picopass/icons/DolphinNice_96x59.png | Bin 0 -> 2459 bytes .../plugins/picopass/icons/Nfc_10px.png | Bin 0 -> 304 bytes .../icons/RFIDDolphinReceive_97x61.png | Bin 0 -> 1421 bytes .../picopass/icons/RFIDDolphinSend_97x61.png | Bin 0 -> 1418 bytes .../plugins/picopass/picopass_device.c | 1 + applications/plugins/picopass/picopass_i.h | 1 + .../plugins/signal_generator/application.fam | 1 + .../icons/SmallArrowDown_3x5.png | Bin 0 -> 3592 bytes .../icons/SmallArrowUp_3x5.png | Bin 0 -> 7976 bytes .../signal_generator/views/signal_gen_pwm.c | 1 + .../weather_station/images/Lock_7x8.png | Bin 0 -> 3597 bytes .../images/Pin_back_arrow_10x8.png | Bin 0 -> 3606 bytes .../weather_station/images/Quest_7x8.png | Bin 0 -> 3675 bytes .../images/Scanning_123x52.png | Bin 0 -> 1690 bytes .../weather_station/images/Unlock_7x8.png | Bin 0 -> 3598 bytes .../images/WarningDolphin_45x42.png | Bin 0 -> 1139 bytes .../views/weather_station_receiver.c | 3 +- applications/services/bt/bt_service/bt.c | 1 + .../desktop/views/desktop_view_lock_menu.c | 1 + .../desktop/views/desktop_view_locked.c | 1 + .../desktop/views/desktop_view_pin_input.c | 1 + applications/services/dialogs/dialogs_api.c | 1 + applications/services/gui/canvas.h | 2 +- applications/services/gui/gui.c | 1 + applications/services/gui/icon_animation.h | 2 +- .../services/gui/modules/button_menu.c | 1 + .../services/gui/modules/byte_input.c | 5 +- applications/services/gui/modules/menu.c | 1 + .../services/gui/modules/text_input.c | 1 + .../services/power/power_service/power_i.h | 1 + .../power/power_service/views/power_off.c | 1 + .../power_service/views/power_unplug_usb.c | 1 + applications/services/storage/storage.c | 1 + applications/settings/about/about.c | 1 + .../bt_settings_app/bt_settings_app.h | 1 + .../desktop_settings/desktop_settings_app.h | 1 + .../power_settings_app/power_settings_app.h | 1 + .../power_settings_app/views/battery_info.c | 1 + .../storage_settings/storage_settings.h | 1 + .../system/updater/views/updater_main.c | 1 + firmware/targets/f7/api_symbols.csv | 186 +----------------- 73 files changed, 74 insertions(+), 195 deletions(-) create mode 100644 applications/debug/file_browser_test/icons/badusb_10px.png create mode 100644 applications/examples/example_images/ReadMe.md create mode 100644 applications/plugins/bt_hid_app/assets/ButtonDown_7x4.png create mode 100644 applications/plugins/bt_hid_app/assets/ButtonLeft_4x7.png create mode 100644 applications/plugins/bt_hid_app/assets/ButtonRight_4x7.png create mode 100644 applications/plugins/bt_hid_app/assets/ButtonUp_7x4.png create mode 100644 applications/plugins/bt_hid_app/assets/Ok_btn_9x9.png create mode 100644 applications/plugins/bt_hid_app/assets/Pin_arrow_down_7x9.png create mode 100644 applications/plugins/bt_hid_app/assets/Pin_arrow_left_9x7.png create mode 100644 applications/plugins/bt_hid_app/assets/Pin_arrow_right_9x7.png create mode 100644 applications/plugins/bt_hid_app/assets/Pin_arrow_up_7x9.png create mode 100644 applications/plugins/bt_hid_app/assets/Pin_back_arrow_10x8.png create mode 100644 applications/plugins/music_player/icons/music_10px.png create mode 100644 applications/plugins/picopass/icons/DolphinMafia_115x62.png create mode 100644 applications/plugins/picopass/icons/DolphinNice_96x59.png create mode 100644 applications/plugins/picopass/icons/Nfc_10px.png create mode 100644 applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png create mode 100644 applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png create mode 100644 applications/plugins/signal_generator/icons/SmallArrowDown_3x5.png create mode 100644 applications/plugins/signal_generator/icons/SmallArrowUp_3x5.png create mode 100644 applications/plugins/weather_station/images/Lock_7x8.png create mode 100644 applications/plugins/weather_station/images/Pin_back_arrow_10x8.png create mode 100644 applications/plugins/weather_station/images/Quest_7x8.png create mode 100644 applications/plugins/weather_station/images/Scanning_123x52.png create mode 100644 applications/plugins/weather_station/images/Unlock_7x8.png create mode 100644 applications/plugins/weather_station/images/WarningDolphin_45x42.png diff --git a/applications/debug/file_browser_test/application.fam b/applications/debug/file_browser_test/application.fam index 5e4c7f46..4a401a64 100644 --- a/applications/debug/file_browser_test/application.fam +++ b/applications/debug/file_browser_test/application.fam @@ -8,4 +8,5 @@ App( stack_size=2 * 1024, order=150, fap_category="Debug", + fap_icon_assets="icons", ) diff --git a/applications/debug/file_browser_test/file_browser_app.c b/applications/debug/file_browser_test/file_browser_app.c index 5c7b93bc..6cb50d38 100644 --- a/applications/debug/file_browser_test/file_browser_app.c +++ b/applications/debug/file_browser_test/file_browser_app.c @@ -1,4 +1,4 @@ -#include "assets_icons.h" +#include #include "file_browser_app_i.h" #include "gui/modules/file_browser.h" #include diff --git a/applications/debug/file_browser_test/icons/badusb_10px.png b/applications/debug/file_browser_test/icons/badusb_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..037474aa3bc9c2e1aca79a68483e69980432bcf5 GIT binary patch literal 576 zcmV-G0>AxEX>4Tx04R}tkv&MmKpe$i(`rSk4t5Z6$WWau6cusQDionYs1;guFuC*#nlvOW zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?JWDYS_3;J6>}?mh0_0Yan9G%FATG`(u3 z5^*t;T@{0`5D-8=V(6BcWz0!Z5}xDh9zMR_MR}I@xj#prnzI<-6NzV;VOEJZh^IHJ z2Iqa^Fe}O`@j3ChNf#u3C`-Nm{=@yu+qV-Xlle$#1U1~DPPFA zta9Gstd(o5bx;1nP)=W2<~q$0B(R7jND!f*h7!uCB1)@HiiH&I$36VRj$a~|Laq`R zITlcX2HEk0|H1EWt^DMKn-q!zT`#u%F$x5Cfo9#dzmILZc>?&Kfh)c3uQY&}Ptxmc zEph}5Yy%h9ZB5w&E_Z;TCqp)6NAlAY@_FF>jJ_!g4Bi60Yi@6?eVjf3Y3eF@0~{Oz zV+G1y_jq?tXK(+WY4!I5C=YUpXXIhH00006VoOIv0RI600RN!9r;`8x010qNS#tmY z3ljhU3ljkVnw%H_000McNliru<^lu`HWXkp{t5s906j@WK~xyijZi@f05Awj>HlAL zr$MwDdI>{Qf+U53tOUR#xOeyy)jcQo#JNRv)7r6DVVK|+*(cmT+R+EbO(O#X#REG4 O0000 +#include #include #include #include diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index 6c6a1584..e5c5d92a 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -1,6 +1,7 @@ #include "bad_usb_view.h" #include "../bad_usb_script.h" #include +#include #define MAX_NAME_LEN 64 diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index faf8eefc..9b4c9233 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/applications/main/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h index fff35c95..85c5c332 100644 --- a/applications/main/gpio/gpio_app_i.h +++ b/applications/main/gpio/gpio_app_i.h @@ -15,6 +15,7 @@ #include #include "views/gpio_test.h" #include "views/gpio_usb_uart.h" +#include struct GpioApp { Gui* gui; diff --git a/applications/main/ibutton/ibutton_i.h b/applications/main/ibutton/ibutton_i.h index a9515195..0a809935 100644 --- a/applications/main/ibutton/ibutton_i.h +++ b/applications/main/ibutton/ibutton_i.h @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/applications/main/infrared/infrared_i.h b/applications/main/infrared/infrared_i.h index 6d25d160..5b555e4b 100644 --- a/applications/main/infrared/infrared_i.h +++ b/applications/main/infrared/infrared_i.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index 71917c0c..72b06193 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/applications/main/lfrfid/views/lfrfid_view_read.c b/applications/main/lfrfid/views/lfrfid_view_read.c index 0d4db617..094afb61 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.c +++ b/applications/main/lfrfid/views/lfrfid_view_read.c @@ -1,5 +1,6 @@ #include "lfrfid_view_read.h" #include +#include #define TEMP_STR_LEN 128 diff --git a/applications/main/nfc/nfc_i.h b/applications/main/nfc/nfc_i.h index fa5b54ed..e9b36a3e 100644 --- a/applications/main/nfc/nfc_i.h +++ b/applications/main/nfc/nfc_i.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index 2dbb4338..91537868 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -1,5 +1,5 @@ #include "detect_reader.h" - +#include #include struct DetectReader { diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 09830ba0..46768cf0 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -13,6 +13,7 @@ #include "views/subghz_test_packet.h" #include +#include #include #include #include diff --git a/applications/main/u2f/u2f_app_i.h b/applications/main/u2f/u2f_app_i.h index 53647859..2896684c 100644 --- a/applications/main/u2f/u2f_app_i.h +++ b/applications/main/u2f/u2f_app_i.h @@ -4,6 +4,7 @@ #include "scenes/u2f_scene.h" #include +#include #include #include #include diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index fa3d6cc2..181c495d 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -1,5 +1,6 @@ #include "u2f_view.h" #include +#include struct U2fView { View* view; diff --git a/applications/plugins/bt_hid_app/assets/ButtonDown_7x4.png b/applications/plugins/bt_hid_app/assets/ButtonDown_7x4.png new file mode 100644 index 0000000000000000000000000000000000000000..2954bb6a67d1c23c0bb5d765e8d2aa04b9b5adec GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J9|(o>FH3<^BV2haYO z-y5_sM4;GPjq%Ck6>60csmUj6EiNa>ORduPH4*)h!w|e3sE@(Z)z4*}Q$iC10Gods AV*mgE literal 0 HcmV?d00001 diff --git a/applications/plugins/bt_hid_app/assets/ButtonLeft_4x7.png b/applications/plugins/bt_hid_app/assets/ButtonLeft_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..0b4655d43247083aa705620e9836ac415b42ca46 GIT binary patch literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a literal 0 HcmV?d00001 diff --git a/applications/plugins/bt_hid_app/assets/ButtonRight_4x7.png b/applications/plugins/bt_hid_app/assets/ButtonRight_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1c74c1c0038ea55172f19ac875003fc80c2d06 GIT binary patch literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA literal 0 HcmV?d00001 diff --git a/applications/plugins/bt_hid_app/assets/ButtonUp_7x4.png b/applications/plugins/bt_hid_app/assets/ButtonUp_7x4.png new file mode 100644 index 0000000000000000000000000000000000000000..1be79328b40a93297a5609756328406565c437c0 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- literal 0 HcmV?d00001 diff --git a/applications/plugins/bt_hid_app/assets/Ok_btn_9x9.png b/applications/plugins/bt_hid_app/assets/Ok_btn_9x9.png new file mode 100644 index 0000000000000000000000000000000000000000..9a1539da2049f12f7b25f96b11a9c40cd8227302 GIT binary patch literal 3605 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 QX&eA7&1_6djNPvM5BL~PlmGw# literal 0 HcmV?d00001 diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_left_9x7.png b/applications/plugins/bt_hid_app/assets/Pin_arrow_left_9x7.png new file mode 100644 index 0000000000000000000000000000000000000000..fb4ded78fde8d1bf4f053ba0b353e7acf14f031a GIT binary patch literal 3603 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 literal 0 HcmV?d00001 diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_up_7x9.png b/applications/plugins/bt_hid_app/assets/Pin_arrow_up_7x9.png new file mode 100644 index 0000000000000000000000000000000000000000..a91a6fd5e99a72112e28865cd8a004c7d1933fff GIT binary patch literal 3603 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 TFC+3}9cE|kU{!4CefEC<&8td2 literal 0 HcmV?d00001 diff --git a/applications/plugins/music_player/application.fam b/applications/plugins/music_player/application.fam index 76787e09..a3698898 100644 --- a/applications/plugins/music_player/application.fam +++ b/applications/plugins/music_player/application.fam @@ -11,8 +11,9 @@ App( provides=["music_player_start"], stack_size=2 * 1024, order=20, - fap_icon="../../../assets/icons/Archive/music_10px.png", + fap_icon="icons/music_10px.png", fap_category="Misc", + fap_icon_assets="icons", ) App( diff --git a/applications/plugins/music_player/icons/music_10px.png b/applications/plugins/music_player/icons/music_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..d41eb0db8c822c60be6c097393b3682680b81a6c GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIasA%~WHIdey2}7aaTa() z7Bet#3xhBt!>l_x9Asc&(0a_=e)W&n8K5!-Pgg&ebxsLQ0Ao%f>i_@% literal 0 HcmV?d00001 diff --git a/applications/plugins/music_player/music_player.c b/applications/plugins/music_player/music_player.c index 192500c2..28872284 100644 --- a/applications/plugins/music_player/music_player.c +++ b/applications/plugins/music_player/music_player.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include diff --git a/applications/plugins/picopass/application.fam b/applications/plugins/picopass/application.fam index bbe37e06..88460b4f 100644 --- a/applications/plugins/picopass/application.fam +++ b/applications/plugins/picopass/application.fam @@ -17,4 +17,5 @@ App( name="loclass", ), ], + fap_icon_assets="icons", ) diff --git a/applications/plugins/picopass/icons/DolphinMafia_115x62.png b/applications/plugins/picopass/icons/DolphinMafia_115x62.png new file mode 100644 index 0000000000000000000000000000000000000000..66fdb40ff2651916faed4a2ae1d564cafdbf7bcb GIT binary patch literal 2504 zcmbVO3se(V8ji5Kg5ZmV3I#ewZHbsZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj literal 0 HcmV?d00001 diff --git a/applications/plugins/picopass/icons/DolphinNice_96x59.png b/applications/plugins/picopass/icons/DolphinNice_96x59.png new file mode 100644 index 0000000000000000000000000000000000000000..a299d3630239b4486e249cc501872bed5996df3b GIT binary patch literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!Q<>&pI=m5)b(dHL6nbwD9yPZ!4!j_b)k${QZ;XFmLn zjqNsD+YPq1J8W%_*aXBie!OR3*tC!PwU_7Q9H4U564!{5l*E!$tK_0oAjM#0U}UIk zV5)0q5@Kj%Wo%$&Y@uynU}a#izM}XGiiX_$l+3hBs0L%8o)7~QD^p9LQiz6svOM}g O4Gf;HelF{r5}E+GUQp8j literal 0 HcmV?d00001 diff --git a/applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png b/applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png new file mode 100644 index 0000000000000000000000000000000000000000..e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083 GIT binary patch literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ literal 0 HcmV?d00001 diff --git a/applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png b/applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png new file mode 100644 index 0000000000000000000000000000000000000000..380a970d9004cba5520560fd9aa24aa42924e2a1 GIT binary patch literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ literal 0 HcmV?d00001 diff --git a/applications/plugins/picopass/picopass_device.c b/applications/plugins/picopass/picopass_device.c index b6e69cc2..199b79e9 100644 --- a/applications/plugins/picopass/picopass_device.c +++ b/applications/plugins/picopass/picopass_device.c @@ -2,6 +2,7 @@ #include #include +#include #define TAG "PicopassDevice" diff --git a/applications/plugins/picopass/picopass_i.h b/applications/plugins/picopass/picopass_i.h index 8e011f22..469a672b 100644 --- a/applications/plugins/picopass/picopass_i.h +++ b/applications/plugins/picopass/picopass_i.h @@ -24,6 +24,7 @@ #include #include +#include #define PICOPASS_TEXT_STORE_SIZE 128 diff --git a/applications/plugins/signal_generator/application.fam b/applications/plugins/signal_generator/application.fam index 7794ee49..de915733 100644 --- a/applications/plugins/signal_generator/application.fam +++ b/applications/plugins/signal_generator/application.fam @@ -9,4 +9,5 @@ App( order=50, fap_icon="signal_gen_10px.png", fap_category="Tools", + fap_icon_assets="icons", ) diff --git a/applications/plugins/signal_generator/icons/SmallArrowDown_3x5.png b/applications/plugins/signal_generator/icons/SmallArrowDown_3x5.png new file mode 100644 index 0000000000000000000000000000000000000000..1912e5d246268d75a20984bdc8b996d503f3d166 GIT binary patch literal 3592 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 BDRuw= literal 0 HcmV?d00001 diff --git a/applications/plugins/signal_generator/icons/SmallArrowUp_3x5.png b/applications/plugins/signal_generator/icons/SmallArrowUp_3x5.png new file mode 100644 index 0000000000000000000000000000000000000000..9c6242078d3bcc9a93eb55b6b5f358720ee6662c GIT binary patch literal 7976 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 +#include typedef enum { LineIndexChannel, diff --git a/applications/plugins/weather_station/images/Lock_7x8.png b/applications/plugins/weather_station/images/Lock_7x8.png new file mode 100644 index 0000000000000000000000000000000000000000..f7c9ca2c702f1b93d7d06bd12ae708655c79d7c8 GIT binary patch literal 3597 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 a9|QyVQ~@!E@+ey_4zMz}H7hmoyzn0`d`!~- literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/images/Pin_back_arrow_10x8.png b/applications/plugins/weather_station/images/Pin_back_arrow_10x8.png new file mode 100644 index 0000000000000000000000000000000000000000..3bafabd144864b575144c75b592e5eaf53974566 GIT binary patch literal 3606 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 TFC+3}9cE|kU{!4CefEC<&8td2 literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/images/Quest_7x8.png b/applications/plugins/weather_station/images/Quest_7x8.png new file mode 100644 index 0000000000000000000000000000000000000000..6825247fbeaf98b4d0d9f8aa15d4f2c5f45dcf3c GIT binary patch literal 3675 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 N!3~Mzlm%~0{|8*@s)qmo literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/images/Unlock_7x8.png b/applications/plugins/weather_station/images/Unlock_7x8.png new file mode 100644 index 0000000000000000000000000000000000000000..9d82b4daf3c722a5244f537f18b147833c2fb4ee GIT binary patch literal 3598 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 MrJY5ondim-0h+K&wEzGB literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/images/WarningDolphin_45x42.png b/applications/plugins/weather_station/images/WarningDolphin_45x42.png new file mode 100644 index 0000000000000000000000000000000000000000..d766ffbb444db1739f2ccd030e506e8bada11ee8 GIT binary patch literal 1139 zcmaJ=TWAz#6rQ4%BpO*okt7zz3Bkm=bK9L=XV}qhW_HceY#KHzP4Z$UGyf(-b}pUy z<8ESW=_MtCk6tj|`k)VrCbo(SMH0n`eW+KIU`wG5O`$IeMg)Vzf7adDhv+af=lqBB zo%5Z`zqhqzdu2s+1%_dji6%LPq#u2o%9fzN?;7Dlq6)^^VVjkKImH23RI|DPo-mXi zkOGP}@Wrnnf?-SQ^>jOIPc{pxWsr*JL*@+|p)oA7EpIDoAAoo_=+RA)c=F3Qf$N$` ze9k55q%DD7y=l+^ZG$aob+Aw6HDcRVJdzhs00Te;&l_3O74jlch$|r7GgAa!aDjay z@rG1;vK5ys2jF3n@vAgV<6)izn!k6Qhd>D(EhD7l zcrhJ1i9|1iwm?z2T#n2INXzM=7@p@Tnx$CQk39VDfC-hn-*jtB5oF-1j&4KUGI1}W z(rxuakw9eMRAJc3%8 zlBq3$QTyJX$a6$&1ldyi4Pe5AEE32Sg@vDzY~7qR?1u@oXh zd9(fBtV<@eK%Tm=yy&p7{=h^#@1W)WFFSe!U5pP~o71uR`FW)7xc*=d5_b}EG@XBZ z@<7MRp-;+|o}SzJvMyfx>37Y4etBizf%0H*zaIF?2ObZt?$D;{o|esJ z*PgAOIO^*8tL%z2)|}k$DP5kN5}8qo*wNa8y?R2=%wO{gCD(`sHt}TzWQuK zMDIJap(nb2=7*ns*oDcRBbC23yy`kvKYV`ovU`a=EmxNv-90;;5r6+l8hQTp^u`Hn XX6*+%8gHC`h)Tl}u@-r>vFqE{F~5F& literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/views/weather_station_receiver.c b/applications/plugins/weather_station/views/weather_station_receiver.c index d30b7926..61b15260 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver.c +++ b/applications/plugins/weather_station/views/weather_station_receiver.c @@ -1,11 +1,10 @@ #include "weather_station_receiver.h" #include "../weather_station_app_i.h" -#include "weather_station_icons.h" +#include #include #include #include -#include #include #define FRAME_HEIGHT 12 diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index c003013e..62b5ab10 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -4,6 +4,7 @@ #include #include +#include #define TAG "BtSrv" diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 8cb8a7a1..486be23b 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -1,5 +1,6 @@ #include #include +#include #include "../desktop_i.h" #include "desktop_view_lock_menu.h" diff --git a/applications/services/desktop/views/desktop_view_locked.c b/applications/services/desktop/views/desktop_view_locked.c index 915b2610..d18ed6c9 100644 --- a/applications/services/desktop/views/desktop_view_locked.c +++ b/applications/services/desktop/views/desktop_view_locked.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include diff --git a/applications/services/desktop/views/desktop_view_pin_input.c b/applications/services/desktop/views/desktop_view_pin_input.c index bf05f06b..b86bf292 100644 --- a/applications/services/desktop/views/desktop_view_pin_input.c +++ b/applications/services/desktop/views/desktop_view_pin_input.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include diff --git a/applications/services/dialogs/dialogs_api.c b/applications/services/dialogs/dialogs_api.c index 6fd51782..ee959a33 100644 --- a/applications/services/dialogs/dialogs_api.c +++ b/applications/services/dialogs/dialogs_api.c @@ -1,6 +1,7 @@ #include "dialogs/dialogs_message.h" #include "dialogs_i.h" #include "dialogs_api_lock.h" +#include /****************** File browser ******************/ diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index a67e5849..a3df5adc 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -7,7 +7,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 42712ed9..2d06d70c 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -1,5 +1,6 @@ #include "gui/canvas.h" #include "gui_i.h" +#include #define TAG "GuiSrv" diff --git a/applications/services/gui/icon_animation.h b/applications/services/gui/icon_animation.h index dab9d996..68479035 100644 --- a/applications/services/gui/icon_animation.h +++ b/applications/services/gui/icon_animation.h @@ -7,7 +7,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/applications/services/gui/modules/button_menu.c b/applications/services/gui/modules/button_menu.c index 37a04326..ff12a931 100644 --- a/applications/services/gui/modules/button_menu.c +++ b/applications/services/gui/modules/button_menu.c @@ -5,6 +5,7 @@ #include #include #include +#include #define ITEM_FIRST_OFFSET 17 #define ITEM_NEXT_OFFSET 4 diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index 8d7e7fd4..bc19f0ee 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -1,6 +1,7 @@ -#include "byte_input.h" -#include #include +#include +#include +#include "byte_input.h" struct ByteInput { View* view; diff --git a/applications/services/gui/modules/menu.c b/applications/services/gui/modules/menu.c index db0717f7..6983e010 100644 --- a/applications/services/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -2,6 +2,7 @@ #include #include +#include #include struct Menu { diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 79fa8772..540e4b7c 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -1,5 +1,6 @@ #include "text_input.h" #include +#include #include struct TextInput { diff --git a/applications/services/power/power_service/power_i.h b/applications/services/power/power_service/power_i.h index 66ced885..8cb5140d 100644 --- a/applications/services/power/power_service/power_i.h +++ b/applications/services/power/power_service/power_i.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "views/power_off.h" diff --git a/applications/services/power/power_service/views/power_off.c b/applications/services/power/power_service/views/power_off.c index b0046325..f14a18d7 100644 --- a/applications/services/power/power_service/views/power_off.c +++ b/applications/services/power/power_service/views/power_off.c @@ -1,6 +1,7 @@ #include "power_off.h" #include #include +#include struct PowerOff { View* view; diff --git a/applications/services/power/power_service/views/power_unplug_usb.c b/applications/services/power/power_service/views/power_unplug_usb.c index 5632cd8b..c2d61139 100644 --- a/applications/services/power/power_service/views/power_unplug_usb.c +++ b/applications/services/power/power_service/views/power_unplug_usb.c @@ -1,6 +1,7 @@ #include "power_unplug_usb.h" #include #include +#include struct PowerUnplugUsb { View* view; diff --git a/applications/services/storage/storage.c b/applications/services/storage/storage.c index 9079a95e..700408c9 100644 --- a/applications/services/storage/storage.c +++ b/applications/services/storage/storage.c @@ -5,6 +5,7 @@ #include "storage/storage_glue.h" #include "storages/storage_int.h" #include "storages/storage_ext.h" +#include #define STORAGE_TICK 1000 diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index a42969b2..1719e188 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/applications/settings/bt_settings_app/bt_settings_app.h b/applications/settings/bt_settings_app/bt_settings_app.h index c45ff3db..b79e3695 100644 --- a/applications/settings/bt_settings_app/bt_settings_app.h +++ b/applications/settings/bt_settings_app/bt_settings_app.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/applications/settings/desktop_settings/desktop_settings_app.h b/applications/settings/desktop_settings/desktop_settings_app.h index fc56c325..6f97564c 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include diff --git a/applications/settings/power_settings_app/power_settings_app.h b/applications/settings/power_settings_app/power_settings_app.h index 8429b54b..cd05846c 100644 --- a/applications/settings/power_settings_app/power_settings_app.h +++ b/applications/settings/power_settings_app/power_settings_app.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "views/battery_info.h" #include diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index 1a8bc71e..e1b7adb4 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -1,6 +1,7 @@ #include "battery_info.h" #include #include +#include struct BatteryInfo { View* view; diff --git a/applications/settings/storage_settings/storage_settings.h b/applications/settings/storage_settings/storage_settings.h index 4cf185e0..664e74c8 100644 --- a/applications/settings/storage_settings/storage_settings.h +++ b/applications/settings/storage_settings/storage_settings.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include diff --git a/applications/system/updater/views/updater_main.c b/applications/system/updater/views/updater_main.c index 5ed3c70a..1199cc88 100644 --- a/applications/system/updater/views/updater_main.c +++ b/applications/system/updater/views/updater_main.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index fd11dffe..00ba30c2 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,6.0,, +Version,+,7.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2661,191 +2661,7 @@ Function,-,yn,double,"int, double" Function,-,ynf,float,"int, float" Variable,-,AHBPrescTable,const uint32_t[16], Variable,-,APBPrescTable,const uint32_t[8], -Variable,+,A_125khz_14,const Icon, -Variable,+,A_BadUsb_14,const Icon, -Variable,+,A_Debug_14,const Icon, -Variable,+,A_FileManager_14,const Icon, -Variable,+,A_GPIO_14,const Icon, -Variable,+,A_Infrared_14,const Icon, -Variable,+,A_Levelup1_128x64,const Icon, -Variable,+,A_Levelup2_128x64,const Icon, -Variable,+,A_Loading_24,const Icon, -Variable,+,A_NFC_14,const Icon, -Variable,+,A_Plugins_14,const Icon, -Variable,+,A_Round_loader_8x8,const Icon, -Variable,+,A_Settings_14,const Icon, -Variable,+,A_Sub1ghz_14,const Icon, -Variable,+,A_U2F_14,const Icon, -Variable,+,A_iButton_14,const Icon, Variable,-,ITM_RxBuffer,volatile int32_t, -Variable,+,I_125_10px,const Icon, -Variable,+,I_ActiveConnection_50x64,const Icon, -Variable,+,I_Alert_9x8,const Icon, -Variable,+,I_ArrowC_1_36x36,const Icon, -Variable,+,I_ArrowDownEmpty_14x15,const Icon, -Variable,+,I_ArrowDownFilled_14x15,const Icon, -Variable,+,I_ArrowUpEmpty_14x15,const Icon, -Variable,+,I_ArrowUpFilled_14x15,const Icon, -Variable,+,I_Attention_5x8,const Icon, -Variable,+,I_Auth_62x31,const Icon, -Variable,+,I_BLE_Pairing_128x64,const Icon, -Variable,+,I_Background_128x11,const Icon, -Variable,+,I_BatteryBody_52x28,const Icon, -Variable,+,I_Battery_16x16,const Icon, -Variable,+,I_Battery_26x8,const Icon, -Variable,+,I_Ble_connected_15x15,const Icon, -Variable,+,I_Ble_disconnected_15x15,const Icon, -Variable,+,I_Bluetooth_Connected_16x8,const Icon, -Variable,+,I_Bluetooth_Idle_5x8,const Icon, -Variable,+,I_ButtonCenter_7x7,const Icon, -Variable,+,I_ButtonDown_7x4,const Icon, -Variable,+,I_ButtonLeftSmall_3x5,const Icon, -Variable,+,I_ButtonLeft_4x7,const Icon, -Variable,+,I_ButtonRightSmall_3x5,const Icon, -Variable,+,I_ButtonRight_4x7,const Icon, -Variable,+,I_ButtonUp_7x4,const Icon, -Variable,+,I_Button_18x18,const Icon, -Variable,+,I_Certification1_103x56,const Icon, -Variable,+,I_Certification2_98x33,const Icon, -Variable,+,I_Charging_lightning_9x10,const Icon, -Variable,+,I_Charging_lightning_mask_9x10,const Icon, -Variable,+,I_Circles_47x47,const Icon, -Variable,+,I_Clock_18x18,const Icon, -Variable,+,I_Connect_me_62x31,const Icon, -Variable,+,I_Connected_62x31,const Icon, -Variable,+,I_CoolHi_25x27,const Icon, -Variable,+,I_CoolHi_hvr_25x27,const Icon, -Variable,+,I_CoolLo_25x27,const Icon, -Variable,+,I_CoolLo_hvr_25x27,const Icon, -Variable,+,I_Cry_dolph_55x52,const Icon, -Variable,+,I_DFU_128x50,const Icon, -Variable,+,I_Dehumidify_25x27,const Icon, -Variable,+,I_Dehumidify_hvr_25x27,const Icon, -Variable,+,I_Detailed_chip_17x13,const Icon, -Variable,+,I_DolphinCommon_56x48,const Icon, -Variable,+,I_DolphinMafia_115x62,const Icon, -Variable,+,I_DolphinNice_96x59,const Icon, -Variable,+,I_DolphinReadingSuccess_59x63,const Icon, -Variable,+,I_DolphinWait_61x59,const Icon, -Variable,+,I_DoorLeft_70x55,const Icon, -Variable,+,I_DoorRight_70x55,const Icon, -Variable,+,I_Down_25x27,const Icon, -Variable,+,I_Down_hvr_25x27,const Icon, -Variable,+,I_Drive_112x35,const Icon, -Variable,+,I_Error_18x18,const Icon, -Variable,+,I_Error_62x31,const Icon, -Variable,+,I_EviSmile1_18x21,const Icon, -Variable,+,I_EviSmile2_18x21,const Icon, -Variable,+,I_EviWaiting1_18x21,const Icon, -Variable,+,I_EviWaiting2_18x21,const Icon, -Variable,+,I_FaceCharging_29x14,const Icon, -Variable,+,I_FaceConfused_29x14,const Icon, -Variable,+,I_FaceNopower_29x14,const Icon, -Variable,+,I_FaceNormal_29x14,const Icon, -Variable,+,I_GameMode_11x8,const Icon, -Variable,+,I_Health_16x16,const Icon, -Variable,+,I_HeatHi_25x27,const Icon, -Variable,+,I_HeatHi_hvr_25x27,const Icon, -Variable,+,I_HeatLo_25x27,const Icon, -Variable,+,I_HeatLo_hvr_25x27,const Icon, -Variable,+,I_Hidden_window_9x8,const Icon, -Variable,+,I_InfraredArrowDown_4x8,const Icon, -Variable,+,I_InfraredArrowUp_4x8,const Icon, -Variable,+,I_InfraredLearnShort_128x31,const Icon, -Variable,+,I_KeyBackspaceSelected_16x9,const Icon, -Variable,+,I_KeyBackspace_16x9,const Icon, -Variable,+,I_KeySaveSelected_24x11,const Icon, -Variable,+,I_KeySave_24x11,const Icon, -Variable,+,I_Keychain_39x36,const Icon, -Variable,+,I_Left_mouse_icon_9x9,const Icon, -Variable,+,I_Lock_7x8,const Icon, -Variable,+,I_Lock_8x8,const Icon, -Variable,+,I_MHz_25x11,const Icon, -Variable,+,I_Medium_chip_22x21,const Icon, -Variable,+,I_Modern_reader_18x34,const Icon, -Variable,+,I_Move_flipper_26x39,const Icon, -Variable,+,I_Mute_25x27,const Icon, -Variable,+,I_Mute_hvr_25x27,const Icon, -Variable,+,I_NFC_manual_60x50,const Icon, -Variable,+,I_Nfc_10px,const Icon, -Variable,+,I_Off_25x27,const Icon, -Variable,+,I_Off_hvr_25x27,const Icon, -Variable,+,I_Ok_btn_9x9,const Icon, -Variable,+,I_Ok_btn_pressed_13x13,const Icon, -Variable,+,I_Percent_10x14,const Icon, -Variable,+,I_Pin_arrow_down_7x9,const Icon, -Variable,+,I_Pin_arrow_left_9x7,const Icon, -Variable,+,I_Pin_arrow_right_9x7,const Icon, -Variable,+,I_Pin_arrow_up_7x9,const Icon, -Variable,+,I_Pin_attention_dpad_29x29,const Icon, -Variable,+,I_Pin_back_arrow_10x8,const Icon, -Variable,+,I_Pin_back_full_40x8,const Icon, -Variable,+,I_Pin_pointer_5x3,const Icon, -Variable,+,I_Pin_star_7x7,const Icon, -Variable,+,I_Power_25x27,const Icon, -Variable,+,I_Power_hvr_25x27,const Icon, -Variable,+,I_Pressed_Button_13x13,const Icon, -Variable,+,I_Quest_7x8,const Icon, -Variable,+,I_RFIDBigChip_37x36,const Icon, -Variable,+,I_RFIDDolphinReceive_97x61,const Icon, -Variable,+,I_RFIDDolphinSend_97x61,const Icon, -Variable,+,I_RFIDDolphinSuccess_108x57,const Icon, -Variable,+,I_Reader_detect_43x40,const Icon, -Variable,+,I_Release_arrow_18x15,const Icon, -Variable,+,I_Restoring_38x32,const Icon, -Variable,+,I_Right_mouse_icon_9x9,const Icon, -Variable,+,I_SDQuestion_35x43,const Icon, -Variable,+,I_SDcardFail_11x8,const Icon, -Variable,+,I_SDcardMounted_11x8,const Icon, -Variable,+,I_Scanning_123x52,const Icon, -Variable,+,I_SmallArrowDown_3x5,const Icon, -Variable,+,I_SmallArrowDown_4x7,const Icon, -Variable,+,I_SmallArrowUp_3x5,const Icon, -Variable,+,I_SmallArrowUp_4x7,const Icon, -Variable,+,I_Smile_18x18,const Icon, -Variable,+,I_Space_65x18,const Icon, -Variable,+,I_Tap_reader_36x38,const Icon, -Variable,+,I_Temperature_16x16,const Icon, -Variable,+,I_Unlock_7x8,const Icon, -Variable,+,I_Unplug_bg_bottom_128x10,const Icon, -Variable,+,I_Unplug_bg_top_128x14,const Icon, -Variable,+,I_Up_25x27,const Icon, -Variable,+,I_Up_hvr_25x27,const Icon, -Variable,+,I_Updating_32x40,const Icon, -Variable,+,I_UsbTree_48x22,const Icon, -Variable,+,I_Vol_down_25x27,const Icon, -Variable,+,I_Vol_down_hvr_25x27,const Icon, -Variable,+,I_Vol_up_25x27,const Icon, -Variable,+,I_Vol_up_hvr_25x27,const Icon, -Variable,+,I_Voldwn_6x6,const Icon, -Variable,+,I_Voltage_16x16,const Icon, -Variable,+,I_Volup_8x6,const Icon, -Variable,+,I_WarningDolphin_45x42,const Icon, -Variable,+,I_Warning_30x23,const Icon, -Variable,+,I_back_10px,const Icon, -Variable,+,I_badusb_10px,const Icon, -Variable,+,I_dir_10px,const Icon, -Variable,+,I_iButtonDolphinVerySuccess_108x52,const Icon, -Variable,+,I_iButtonKey_49x44,const Icon, -Variable,+,I_ibutt_10px,const Icon, -Variable,+,I_ir_10px,const Icon, -Variable,+,I_loading_10px,const Icon, -Variable,+,I_music_10px,const Icon, -Variable,+,I_passport_bad1_46x49,const Icon, -Variable,+,I_passport_bad2_46x49,const Icon, -Variable,+,I_passport_bad3_46x49,const Icon, -Variable,+,I_passport_bottom_128x18,const Icon, -Variable,+,I_passport_happy1_46x49,const Icon, -Variable,+,I_passport_happy2_46x49,const Icon, -Variable,+,I_passport_happy3_46x49,const Icon, -Variable,+,I_passport_left_6x46,const Icon, -Variable,+,I_passport_okay1_46x49,const Icon, -Variable,+,I_passport_okay2_46x49,const Icon, -Variable,+,I_passport_okay3_46x49,const Icon, -Variable,+,I_sub1_10px,const Icon, -Variable,+,I_u2f_10px,const Icon, -Variable,+,I_unknown_10px,const Icon, -Variable,+,I_update_10px,const Icon, Variable,-,MSIRangeTable,const uint32_t[16], Variable,-,SmpsPrescalerTable,const uint32_t[4][6], Variable,+,SystemCoreClock,uint32_t, From 9cd0592aafb826ea08d180c857d399acdf607706 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Fri, 28 Oct 2022 18:31:41 +0400 Subject: [PATCH 36/42] SubGhz: add keeloq potocol JCM_Tech (#1939) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SubGhz: add keeloq potocol JCM_Tech * SubGhz: add new metod decoder Co-authored-by: あく --- assets/resources/subghz/assets/keeloq_mfcodes | 99 ++++++++++--------- lib/subghz/protocols/keeloq.c | 18 ++++ lib/subghz/protocols/keeloq_common.c | 30 +++++- lib/subghz/protocols/keeloq_common.h | 18 ++++ 4 files changed, 117 insertions(+), 48 deletions(-) diff --git a/assets/resources/subghz/assets/keeloq_mfcodes b/assets/resources/subghz/assets/keeloq_mfcodes index b8fc3690..1b27bfb0 100644 --- a/assets/resources/subghz/assets/keeloq_mfcodes +++ b/assets/resources/subghz/assets/keeloq_mfcodes @@ -1,50 +1,55 @@ Filetype: Flipper SubGhz Keystore File Version: 0 Encryption: 1 -IV: 2A 34 F1 5A AF 6F F5 1A 83 A6 1E DA DE B7 3D F1 -06B63DF24AE073A2F2B19C55CA9E8364FBECD26E49C551990153F6513BDE5267 -6139C78C74C341EB7474085CF1D047BD6FB005F80A72AF3EF3F89D58EF5DF500 -D85F11689020ECA47FBE9C2B67EE41A81E1F06DE2A35AF958965E3ECE29EA701 -1AE9073A42FE0E439544FE6945F6B33CF15A7A4A279020B5E0B3BE33FD189A7E -E161F007854BB33E0056FA09A2E2DEE66789B5C87C8D6D3DE2C8C1BD2B48983EB9D1C5697CA6E95996918F7C47B761B0 -59AE4644DCB3D720C38B5115F230DA58E7BE0A697907F6174BB05AB7886ACDB1 -634DF0BCC185C4C1F7E1B1594B4438D051ABAE092433078963063B51D961D08C -1EBEBCB49E498B9BE977D53EC21B9A546155B627737BD0AA832D496035729346 -4DFA93E639197772D57E8ACE04512CEFC045B8CC965C175A25ED525B630CBB63 -C2D5235D1014A319B249EAE8A5EE350F18D5AB8A498EF222704BD4EB1435F388 -F66D1937160E1392197F463A52E87FCE938A92070892113443C348D7553327A5715CF615CE2F2C96284F47759E043419 -841D29E7CBE040188E2283BFBA9F26EF2F65CCB085B56C3515E8C46C3F20BD75BAA963550869435FDAF509CEEE66A2C4 -7D87E24487D307635E7A17B989B8547EE11F3BF3468D055F0B44633B631BA42C -B4916043973501B95A82B329196D6EBA69FBBC3AF8FD914583104E0E18CE82F6 -E4649F9C2A5465D2EA6F3E9724DD06CD6962FE2BAEB14F1453C14D1559232AE1 -96E15D890DF7FD348441F5E429A875754C6BF0520A787F8E9D8C5415674783CC -CB52005EDED47B57F795BC92FB0522EAB18D23EE028B8D10ED57828C250EB285BFEC6E4A4BE8DABCE0D57ECAA20D90C3 -8E5A50C7D5C374445E88752301D20F0B3D6E4988B61D90FD63779B0EDEF9C60D -49D6CB276A0E5FF134A38062503F01351F44CD6455708B50B5F07D03FC477C33 -CB45B56613DF208E79E4E10A6510F07DC1AA49210C7B94E8BBAECD2C35EC6ABC99FB10FD7C96DD6BB6A6685E9FAD93FB -0743F3CC51200F763C242F1956B4D775C092ADF1A5C19ACAE96EB60C2990CF214F8FEA8FC6749286F6BDAB67657C479A -E5608B28A058787D64A145F0362DEFD98CAE0B5A0F22C6DA7C6D278C7B5F95E3 -D4C113D43E7FB6D2EFA9E87471AA76A61B26872607B4AF5B87F9D72113835CE6 -2DC502800BFD21B76126390CA64A08C5432A2254E822F214CDE1EA11430084C5 -CA22C73010B0F1CB8009601BE2AF0B3674D83D5880E4A26C2A3FF0EA0A098CEA -E53B2B102FDB000E9BB747F957156976E5A0C0E3898AA844C13AE8A9CEE7013B -95CF1A46FFC252BE92919531C92BF6A3AA1B16C170DF4461EC54BE07A55C2387 -2EC7E24090F6DFFF6F2F2D8874D2F36AA769995F31F29FBE3B0EA6A16C3EE833 -C1145B1D9AC70761EA902B86455C1BE1BB1153552A1F7327411DECABE538827B -18D596CADD2EE544200A58716C7A4690B658E58CC2B97334740F70894A6C90FA -6A2F8859DFF01E13AC6C5300AD4A2218810FC91A6FB64A560E99FE6C99226AD2 -48D2EB5A08E35AF89A3B7A1CFDEE829FC0C2DDD2E965F4E3D043B0B14CB7825E -91039325D53CDD0236D1CD13047973A013C14B45A32DE0784A73BFABCEAFBCD1 -51B4EAC87C4DC49B007F40D38B8166C388A1AF25E8D2FF6598E8EDE8726E6E14AD88443114D2A0F5E7721E304F3870DA -3A179DDF65B9868CD84C7C04931F40D5D204C97B20DCBF1A70C241E59BFD7F14 -AF538FD16104DCAF03F4DDF05026D6741898DFC247E48A8F72E652DDF2DFD289 -E67F16AEC9D84B6C06F77B806CA6FBC7618BFBECD0D7A04EC3AE1D1DD06BEC5B -FA4D9F8920EBF2F4293C6D4E99083AA4A71A9DDFFDB07EEBDC552DACEC4DA24A -5BF23E630AC81E2CD533803E225BCB3C481B8D650A9858CF2B5219BAE1CDA01A -17B57E8C1032481E69247EA9A0C9EA41F6C0EA9B3F11170CA69C0842423F0455 -96EA848B8527A647DC9DACDB16C5D92B0081EB1CD77B99B47F56C2E249190BD3BE4306333F37487133DD3AD8E57F3092 -B0E9411274D799BE5989D52E74E00DE310CCA2BD47D7A8FA554D66BB04CD787A -D0D28476E3D8832975653D93F545C35278EC1F0B7AD70CA2F36EB476CC207937 -933195E37014619F997B73F5CF4C0110865A822CA8CB0ED1D977D49A1B06A37F -E790CAC2A26452BF941A9E1BABF0A85598EA1CC8F8CFED637C9B40D5E027B518 -49C1F179ABA5BD4F2C45257A33701730E9CC4728677EFF07808ABE31D3CE6FD5C805F43EA5ABB7261B220C82F0794092 +IV: AA FF DE 54 A1 BB F1 21 83 46 FE 2A 1E B7 3D 33 +95B8CD65BBAC95EACE67CA94F679B82877A921396D461ECB479722F8A369454A +61065C41297B9FF8F8168814F49A03D1FE7B4CB79DFFCBBF0402AAA6A2211E84 +A1557AC139188FF105D1081A4B688C5CA440FB5DA7F40901B541120AD08A544F +AF0A6056D7F0D97DAD6C16C4E63204E4B3B1C5A20AC82B983B516F4F718EE29F +6861BFAE46A1AADB1DB2D6DFAA7E39D21D5B3E46A41BD50F4F2828879EB328EF0A406F2B9C79A031AB361257E6D69756 +0DDB3DAC53678541981CC46C22CED245CBA314C9BBE1BA9383B8505B75AC5E40 +99AB5D9404934F2D257ED04D9F8CCEE06D00F38157B121AFD63101E4E5C08268 +5114A6C42B342C7D933A76F9052FF963C2047E85EA524497C21B4C35C38EF6E7 +88CA2A1907D94B972FF93DBB9B88CB576F3E1BB0FE8F85A5B2CCA7D44B00374D +349C4153FE7CA8AE044E9F75F77D9694304474CE3F127CF968662B5F78A7F421 +62AA02E20CA7E691EFC0B55CA41C9BDF889FB23868289284241CD31AA1A0E499AE2A770B6B5AB3170CDCCDB8A246D36C +97901B5EB76228ADF8E5073F1BAB1502878DEFF1C4EBF12A43D105556CB7E80F947A8BD7831666BD838C57CDF64A6F3F +B05959D210B500943A93BDFAF783D9DB215FC84503B152EAFBCFB5B6237E3888 +B393DE4489BCAFD5DB80592A12E329E18913E185D2042580048029A8C4C3A257 +B4B30492A5F0C3C763E2F43C02D1451A5B9CFB468CFE62BE85B1F56FF49DAB9A +CE5D57C0EE3D717FC717EB725970A9F25D211546EE7AC5C237950CEA323D85D4 +4E9028944813FD40A17AF6DF5A97E76179B48EE79265BBD38B07E3A270587A813DADB51B3367479AC5644F754B5613F8 +3B3C3000B9D1361711ECE3DB77C90A059576F738CB167679DA36DD3D128B27A1 +997023148148DE7B9CBA47D3FD48DEF73AA1715FF4BC1E7A1DBA6D52A0DCB2C0 +C8428D18E69FB92486434FCE470F1FF37D40507F27D824679C132A70D516530367277F02DDB5C464D03450FF6B425A24 +3701200DF5DA7235971FD95844056E74C7D61A8EB12A8772E04F52037C63D50B6229A7F905F3E6F84C565FCC7632870C +BB392A464CDC0D5D923AA9EF8ECC3C6F020D0AD82165462DF0DE7C5025AAAAAC +999C82209B30638506E5D708471676D2CBB4A432E5AF86ABD61179111EDAE636 +FDE2A452A6B47261338117EC20FC57731DA492562ECD21BBC61F098A5442CF20 +D923BABB5C4DFB48E3F763898B2796C7830D3EE9A91DF904AC2223A0F4736507 +0987DDAC695DD5E4607048DF1D4EF96599E17ED52F41785E676AA048AB7213FE +26CB3E6CFA10338A8DDD99BFF6957C53DEF435CB0FF977B71B5164ADFE11292A +097908FD07A0A093CA80E6FF59524707C1A11169D0CB6F8E4967D8DAA725FE7A +8C629E70A5CC6FCB039DFA1A6AC58CB7B7E92C85BDA66266AB49E6B1285FC7A6 +39A2052350CD446EDC1B9AD0C2DD51C78B2E5F3A76AAD0EC200F74B40ACD4AC5 +A1685CF8C4A5401F2CA0C8172CB5B4B5726C61CE68A72AE834B0A472CEB2F3DE +1F5ED5793DB381D1B501BA8A4DF3E74FB11FC1A922DDC8AE62E5BA8934C37EA8 +D80EF661BF36E2F6C179E253CE5BC3732684ACBC7C65E526A628442A2EBF8FAA +7785BF721F21E19A8CFBFBBB56BD76B96A4E8EF9F8A2344009B14AB385909598F834A5533B648DA7D62BD6D4314A43A5 +C8F6F943DE615B5827569B283577344C0455B3279C73634FC4E0E9A8088DF633 +FB4F4C786FC51BBDA679A212B4A05EF120AC62F7EBFFE8263BD50A4D9BC9C6E0 +16EEC35CF69BA86DB3BE999CDF9B39F5736F3727B2AA2C5AB9141A48F176D831 +AD1AD6DE813E7710DA3AF546D4F9EA085831E6B3FA17B64F1B8765F48134EA54 +345D743BC35B4A8614632ADD11E809C0D1E6C78F9469256B9A738DA0B648B2B8 +7C876CECAC839EBB4609C3996966C3EC454F51C8ABCC51097E405370C4B6F086 +0F857C031FD3047607647148C534F969567F207FF1691D8D06DCDF4C2514695D +EC0630EDC82241C1952F49B6B1B0C1A954A7DDD6BDB1326ACC54AD449D1BF985 +286EF9F7FD0D09F2604CCE867C52144CD0C4773A3D8183066C61B8BF9860AE7C +EA55424097A08722A66966E3177E09DE91AC65175E5C68CB47B6153E6585DF85 +D54FCDF9EA4BD1FE4F316DB6D5CE4A2675F2D0144772865EDC781FBA7DFD23E4 +7A2F5C5CA9F97FE9527BAA760E64B930C407A27DE036476737E6BDD9422F4056A5F1F414F12F0982109FD7C30E8CC1CB +06BAD9B4EEEEB1BCF8C97672D271534FE84D772282EE9642698788D3842D7641 +101C1B2DBD963E23777294C22E553D145D5B40838F91355CA86D571A0CEFF68F +1B148C2B502B3E0A5BD40858E019C513DD4CCAF2A114CBB29C59BFB018079285 +8DF4D07EC20FF873EA989ACEF4AF96E9787FE6E0F71965858B4186C3AF302A31 +2317DC8C098CD60F3467B3644A19CCE887339708820CD37F6F5277D6648F837512F70CE90E23D7339CDDE002BD8D83DB diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index ae6588e7..eef1d093 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -529,6 +529,24 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( return 1; } break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2: + man = subghz_protocol_keeloq_common_magic_serial_type2_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3: + man = subghz_protocol_keeloq_common_magic_serial_type3_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + return 1; + } + break; case KEELOQ_LEARNING_UNKNOWN: // Simple Learning decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); diff --git a/lib/subghz/protocols/keeloq_common.c b/lib/subghz/protocols/keeloq_common.c index 6c9bc461..ddbf1c91 100644 --- a/lib/subghz/protocols/keeloq_common.c +++ b/lib/subghz/protocols/keeloq_common.c @@ -94,6 +94,34 @@ inline uint64_t inline uint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) { - return man | ((uint64_t)data << 40) | + return (man & 0xFFFFFFFF) | ((uint64_t)data << 40) | ((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32); } + +/** Magic_serial_type2 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man) { + uint8_t* p = (uint8_t*)&data; + uint8_t* m = (uint8_t*)&man; + m[7] = p[0]; + m[6] = p[1]; + m[5] = p[2]; + m[4] = p[3]; + return man; +} + +/** Magic_serial_type3 Learning + * @param data - serial number (24bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) { + return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF); +} diff --git a/lib/subghz/protocols/keeloq_common.h b/lib/subghz/protocols/keeloq_common.h index 448388f0..df3d0dbf 100644 --- a/lib/subghz/protocols/keeloq_common.h +++ b/lib/subghz/protocols/keeloq_common.h @@ -22,6 +22,8 @@ #define KEELOQ_LEARNING_SECURE 3u #define KEELOQ_LEARNING_MAGIC_XOR_TYPE_1 4u #define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1 5u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 6u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 7u /** * Simple Learning Encrypt @@ -72,3 +74,19 @@ uint64_t subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, u */ uint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man); + +/** Magic_serial_type2 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man); + +/** Magic_serial_type3 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man); From 4b921803cbad19829d0b08e92bab89f29080ffd7 Mon Sep 17 00:00:00 2001 From: hedger Date: Fri, 28 Oct 2022 19:32:06 +0400 Subject: [PATCH 37/42] fbt: fixes for ufbt compat (#1940) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fbt: split sdk management code * scripts: fixed import handling * fbt: sdk: reformatted paths * scrips: dist: bundling libs as a build artifact * fbt: sdk: better path management * typo fix * fbt: sdk: minor path handling fixes * toolchain: fixed windows toolchain download Co-authored-by: あく --- firmware.scons | 17 -- scripts/fbt/sdk/__init__.py | 44 +++ scripts/fbt/{sdk.py => sdk/cache.py} | 280 +----------------- scripts/fbt/sdk/collector.py | 238 +++++++++++++++ scripts/fbt_tools/fbt_extapps.py | 2 +- scripts/fbt_tools/fbt_sdk.py | 49 ++- scripts/sconsdist.py | 66 +++-- .../toolchain/windows-toolchain-download.ps1 | 6 +- site_scons/extapps.scons | 2 +- site_scons/firmwareopts.scons | 20 +- 10 files changed, 383 insertions(+), 341 deletions(-) create mode 100644 scripts/fbt/sdk/__init__.py rename scripts/fbt/{sdk.py => sdk/cache.py} (52%) create mode 100644 scripts/fbt/sdk/collector.py diff --git a/firmware.scons b/firmware.scons index d501996b..63a1aa3f 100644 --- a/firmware.scons +++ b/firmware.scons @@ -178,23 +178,6 @@ sources.extend( ) ) - -fwenv.AppendUnique( - LINKFLAGS=[ - "-specs=nano.specs", - "-specs=nosys.specs", - "-Wl,--gc-sections", - "-Wl,--undefined=uxTopUsedPriority", - "-Wl,--wrap,_malloc_r", - "-Wl,--wrap,_free_r", - "-Wl,--wrap,_calloc_r", - "-Wl,--wrap,_realloc_r", - "-n", - "-Xlinker", - "-Map=${TARGET}.map", - ], -) - # Debug # print(fwenv.Dump()) diff --git a/scripts/fbt/sdk/__init__.py b/scripts/fbt/sdk/__init__.py new file mode 100644 index 00000000..27da5f7c --- /dev/null +++ b/scripts/fbt/sdk/__init__.py @@ -0,0 +1,44 @@ +from typing import Set, ClassVar +from dataclasses import dataclass, field + + +@dataclass(frozen=True) +class ApiEntryFunction: + name: str + returns: str + params: str + + csv_type: ClassVar[str] = "Function" + + def dictify(self): + return dict(name=self.name, type=self.returns, params=self.params) + + +@dataclass(frozen=True) +class ApiEntryVariable: + name: str + var_type: str + + csv_type: ClassVar[str] = "Variable" + + def dictify(self): + return dict(name=self.name, type=self.var_type, params=None) + + +@dataclass(frozen=True) +class ApiHeader: + name: str + + csv_type: ClassVar[str] = "Header" + + def dictify(self): + return dict(name=self.name, type=None, params=None) + + +@dataclass +class ApiEntries: + # These are sets, to avoid creating duplicates when we have multiple + # declarations with same signature + functions: Set[ApiEntryFunction] = field(default_factory=set) + variables: Set[ApiEntryVariable] = field(default_factory=set) + headers: Set[ApiHeader] = field(default_factory=set) diff --git a/scripts/fbt/sdk.py b/scripts/fbt/sdk/cache.py similarity index 52% rename from scripts/fbt/sdk.py rename to scripts/fbt/sdk/cache.py index 48f935de..62d42798 100644 --- a/scripts/fbt/sdk.py +++ b/scripts/fbt/sdk/cache.py @@ -4,284 +4,18 @@ import csv import operator from enum import Enum, auto -from typing import List, Set, ClassVar, Any -from dataclasses import dataclass, field +from typing import Set, ClassVar, Any +from dataclasses import dataclass from ansi.color import fg -from cxxheaderparser.parser import CxxParser - - -# 'Fixing' complaints about typedefs -CxxParser._fundamentals.discard("wchar_t") - -from cxxheaderparser.types import ( - EnumDecl, - Field, - ForwardDecl, - FriendDecl, - Function, - Method, - Typedef, - UsingAlias, - UsingDecl, - Variable, - Pointer, - Type, - PQName, - NameSpecifier, - FundamentalSpecifier, - Parameter, - Array, - Value, - Token, - FunctionType, +from . import ( + ApiEntries, + ApiEntryFunction, + ApiEntryVariable, + ApiHeader, ) -from cxxheaderparser.parserstate import ( - State, - EmptyBlockState, - ClassBlockState, - ExternBlockState, - NamespaceBlockState, -) - - -@dataclass(frozen=True) -class ApiEntryFunction: - name: str - returns: str - params: str - - csv_type: ClassVar[str] = "Function" - - def dictify(self): - return dict(name=self.name, type=self.returns, params=self.params) - - -@dataclass(frozen=True) -class ApiEntryVariable: - name: str - var_type: str - - csv_type: ClassVar[str] = "Variable" - - def dictify(self): - return dict(name=self.name, type=self.var_type, params=None) - - -@dataclass(frozen=True) -class ApiHeader: - name: str - - csv_type: ClassVar[str] = "Header" - - def dictify(self): - return dict(name=self.name, type=None, params=None) - - -@dataclass -class ApiEntries: - # These are sets, to avoid creating duplicates when we have multiple - # declarations with same signature - functions: Set[ApiEntryFunction] = field(default_factory=set) - variables: Set[ApiEntryVariable] = field(default_factory=set) - headers: Set[ApiHeader] = field(default_factory=set) - - -class SymbolManager: - def __init__(self): - self.api = ApiEntries() - self.name_hashes = set() - - # Calculate hash of name and raise exception if it already is in the set - def _name_check(self, name: str): - name_hash = gnu_sym_hash(name) - if name_hash in self.name_hashes: - raise Exception(f"Hash collision on {name}") - self.name_hashes.add(name_hash) - - def add_function(self, function_def: ApiEntryFunction): - if function_def in self.api.functions: - return - self._name_check(function_def.name) - self.api.functions.add(function_def) - - def add_variable(self, variable_def: ApiEntryVariable): - if variable_def in self.api.variables: - return - self._name_check(variable_def.name) - self.api.variables.add(variable_def) - - def add_header(self, header: str): - self.api.headers.add(ApiHeader(header)) - - -def gnu_sym_hash(name: str): - h = 0x1505 - for c in name: - h = (h << 5) + h + ord(c) - return str(hex(h))[-8:] - - -class SdkCollector: - def __init__(self): - self.symbol_manager = SymbolManager() - - def add_header_to_sdk(self, header: str): - self.symbol_manager.add_header(header) - - def process_source_file_for_sdk(self, file_path: str): - visitor = SdkCxxVisitor(self.symbol_manager) - with open(file_path, "rt") as f: - content = f.read() - parser = CxxParser(file_path, content, visitor, None) - parser.parse() - - def get_api(self): - return self.symbol_manager.api - - -def stringify_array_dimension(size_descr): - if not size_descr: - return "" - return stringify_descr(size_descr) - - -def stringify_array_descr(type_descr): - assert isinstance(type_descr, Array) - return ( - stringify_descr(type_descr.array_of), - stringify_array_dimension(type_descr.size), - ) - - -def stringify_descr(type_descr): - if isinstance(type_descr, (NameSpecifier, FundamentalSpecifier)): - return type_descr.name - elif isinstance(type_descr, PQName): - return "::".join(map(stringify_descr, type_descr.segments)) - elif isinstance(type_descr, Pointer): - # Hack - if isinstance(type_descr.ptr_to, FunctionType): - return stringify_descr(type_descr.ptr_to) - return f"{stringify_descr(type_descr.ptr_to)}*" - elif isinstance(type_descr, Type): - return ( - f"{'const ' if type_descr.const else ''}" - f"{'volatile ' if type_descr.volatile else ''}" - f"{stringify_descr(type_descr.typename)}" - ) - elif isinstance(type_descr, Parameter): - return stringify_descr(type_descr.type) - elif isinstance(type_descr, Array): - # Hack for 2d arrays - if isinstance(type_descr.array_of, Array): - argtype, dimension = stringify_array_descr(type_descr.array_of) - return ( - f"{argtype}[{stringify_array_dimension(type_descr.size)}][{dimension}]" - ) - return f"{stringify_descr(type_descr.array_of)}[{stringify_array_dimension(type_descr.size)}]" - elif isinstance(type_descr, Value): - return " ".join(map(stringify_descr, type_descr.tokens)) - elif isinstance(type_descr, FunctionType): - return f"{stringify_descr(type_descr.return_type)} (*)({', '.join(map(stringify_descr, type_descr.parameters))})" - elif isinstance(type_descr, Token): - return type_descr.value - elif type_descr is None: - return "" - else: - raise Exception("unsupported type_descr: %s" % type_descr) - - -class SdkCxxVisitor: - def __init__(self, symbol_manager: SymbolManager): - self.api = symbol_manager - - def on_variable(self, state: State, v: Variable) -> None: - if not v.extern: - return - - self.api.add_variable( - ApiEntryVariable( - stringify_descr(v.name), - stringify_descr(v.type), - ) - ) - - def on_function(self, state: State, fn: Function) -> None: - if fn.inline or fn.has_body: - return - - self.api.add_function( - ApiEntryFunction( - stringify_descr(fn.name), - stringify_descr(fn.return_type), - ", ".join(map(stringify_descr, fn.parameters)) - + (", ..." if fn.vararg else ""), - ) - ) - - def on_define(self, state: State, content: str) -> None: - pass - - def on_pragma(self, state: State, content: str) -> None: - pass - - def on_include(self, state: State, filename: str) -> None: - pass - - def on_empty_block_start(self, state: EmptyBlockState) -> None: - pass - - def on_empty_block_end(self, state: EmptyBlockState) -> None: - pass - - def on_extern_block_start(self, state: ExternBlockState) -> None: - pass - - def on_extern_block_end(self, state: ExternBlockState) -> None: - pass - - def on_namespace_start(self, state: NamespaceBlockState) -> None: - pass - - def on_namespace_end(self, state: NamespaceBlockState) -> None: - pass - - def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None: - pass - - def on_typedef(self, state: State, typedef: Typedef) -> None: - pass - - def on_using_namespace(self, state: State, namespace: List[str]) -> None: - pass - - def on_using_alias(self, state: State, using: UsingAlias) -> None: - pass - - def on_using_declaration(self, state: State, using: UsingDecl) -> None: - pass - - def on_enum(self, state: State, enum: EnumDecl) -> None: - pass - - def on_class_start(self, state: ClassBlockState) -> None: - pass - - def on_class_field(self, state: State, f: Field) -> None: - pass - - def on_class_method(self, state: ClassBlockState, method: Method) -> None: - pass - - def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None: - pass - - def on_class_end(self, state: ClassBlockState) -> None: - pass - @dataclass(frozen=True) class SdkVersion: diff --git a/scripts/fbt/sdk/collector.py b/scripts/fbt/sdk/collector.py new file mode 100644 index 00000000..578a8c7a --- /dev/null +++ b/scripts/fbt/sdk/collector.py @@ -0,0 +1,238 @@ +from typing import List + +from cxxheaderparser.parser import CxxParser +from . import ( + ApiEntries, + ApiEntryFunction, + ApiEntryVariable, + ApiHeader, +) + + +# 'Fixing' complaints about typedefs +CxxParser._fundamentals.discard("wchar_t") + +from cxxheaderparser.types import ( + EnumDecl, + Field, + ForwardDecl, + FriendDecl, + Function, + Method, + Typedef, + UsingAlias, + UsingDecl, + Variable, + Pointer, + Type, + PQName, + NameSpecifier, + FundamentalSpecifier, + Parameter, + Array, + Value, + Token, + FunctionType, +) + +from cxxheaderparser.parserstate import ( + State, + EmptyBlockState, + ClassBlockState, + ExternBlockState, + NamespaceBlockState, +) + + +class SymbolManager: + def __init__(self): + self.api = ApiEntries() + self.name_hashes = set() + + # Calculate hash of name and raise exception if it already is in the set + def _name_check(self, name: str): + name_hash = gnu_sym_hash(name) + if name_hash in self.name_hashes: + raise Exception(f"Hash collision on {name}") + self.name_hashes.add(name_hash) + + def add_function(self, function_def: ApiEntryFunction): + if function_def in self.api.functions: + return + self._name_check(function_def.name) + self.api.functions.add(function_def) + + def add_variable(self, variable_def: ApiEntryVariable): + if variable_def in self.api.variables: + return + self._name_check(variable_def.name) + self.api.variables.add(variable_def) + + def add_header(self, header: str): + self.api.headers.add(ApiHeader(header)) + + +def gnu_sym_hash(name: str): + h = 0x1505 + for c in name: + h = (h << 5) + h + ord(c) + return str(hex(h))[-8:] + + +class SdkCollector: + def __init__(self): + self.symbol_manager = SymbolManager() + + def add_header_to_sdk(self, header: str): + self.symbol_manager.add_header(header) + + def process_source_file_for_sdk(self, file_path: str): + visitor = SdkCxxVisitor(self.symbol_manager) + with open(file_path, "rt") as f: + content = f.read() + parser = CxxParser(file_path, content, visitor, None) + parser.parse() + + def get_api(self): + return self.symbol_manager.api + + +def stringify_array_dimension(size_descr): + if not size_descr: + return "" + return stringify_descr(size_descr) + + +def stringify_array_descr(type_descr): + assert isinstance(type_descr, Array) + return ( + stringify_descr(type_descr.array_of), + stringify_array_dimension(type_descr.size), + ) + + +def stringify_descr(type_descr): + if isinstance(type_descr, (NameSpecifier, FundamentalSpecifier)): + return type_descr.name + elif isinstance(type_descr, PQName): + return "::".join(map(stringify_descr, type_descr.segments)) + elif isinstance(type_descr, Pointer): + # Hack + if isinstance(type_descr.ptr_to, FunctionType): + return stringify_descr(type_descr.ptr_to) + return f"{stringify_descr(type_descr.ptr_to)}*" + elif isinstance(type_descr, Type): + return ( + f"{'const ' if type_descr.const else ''}" + f"{'volatile ' if type_descr.volatile else ''}" + f"{stringify_descr(type_descr.typename)}" + ) + elif isinstance(type_descr, Parameter): + return stringify_descr(type_descr.type) + elif isinstance(type_descr, Array): + # Hack for 2d arrays + if isinstance(type_descr.array_of, Array): + argtype, dimension = stringify_array_descr(type_descr.array_of) + return ( + f"{argtype}[{stringify_array_dimension(type_descr.size)}][{dimension}]" + ) + return f"{stringify_descr(type_descr.array_of)}[{stringify_array_dimension(type_descr.size)}]" + elif isinstance(type_descr, Value): + return " ".join(map(stringify_descr, type_descr.tokens)) + elif isinstance(type_descr, FunctionType): + return f"{stringify_descr(type_descr.return_type)} (*)({', '.join(map(stringify_descr, type_descr.parameters))})" + elif isinstance(type_descr, Token): + return type_descr.value + elif type_descr is None: + return "" + else: + raise Exception("unsupported type_descr: %s" % type_descr) + + +class SdkCxxVisitor: + def __init__(self, symbol_manager: SymbolManager): + self.api = symbol_manager + + def on_variable(self, state: State, v: Variable) -> None: + if not v.extern: + return + + self.api.add_variable( + ApiEntryVariable( + stringify_descr(v.name), + stringify_descr(v.type), + ) + ) + + def on_function(self, state: State, fn: Function) -> None: + if fn.inline or fn.has_body: + return + + self.api.add_function( + ApiEntryFunction( + stringify_descr(fn.name), + stringify_descr(fn.return_type), + ", ".join(map(stringify_descr, fn.parameters)) + + (", ..." if fn.vararg else ""), + ) + ) + + def on_define(self, state: State, content: str) -> None: + pass + + def on_pragma(self, state: State, content: str) -> None: + pass + + def on_include(self, state: State, filename: str) -> None: + pass + + def on_empty_block_start(self, state: EmptyBlockState) -> None: + pass + + def on_empty_block_end(self, state: EmptyBlockState) -> None: + pass + + def on_extern_block_start(self, state: ExternBlockState) -> None: + pass + + def on_extern_block_end(self, state: ExternBlockState) -> None: + pass + + def on_namespace_start(self, state: NamespaceBlockState) -> None: + pass + + def on_namespace_end(self, state: NamespaceBlockState) -> None: + pass + + def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None: + pass + + def on_typedef(self, state: State, typedef: Typedef) -> None: + pass + + def on_using_namespace(self, state: State, namespace: List[str]) -> None: + pass + + def on_using_alias(self, state: State, using: UsingAlias) -> None: + pass + + def on_using_declaration(self, state: State, using: UsingDecl) -> None: + pass + + def on_enum(self, state: State, enum: EnumDecl) -> None: + pass + + def on_class_start(self, state: ClassBlockState) -> None: + pass + + def on_class_field(self, state: State, f: Field) -> None: + pass + + def on_class_method(self, state: ClassBlockState, method: Method) -> None: + pass + + def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None: + pass + + def on_class_end(self, state: ClassBlockState) -> None: + pass diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 5a5dab57..38c943cc 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -8,7 +8,7 @@ import os import pathlib from fbt.elfmanifest import assemble_manifest_data from fbt.appmanifest import FlipperApplication, FlipperManifestException -from fbt.sdk import SdkCache +from fbt.sdk.cache import SdkCache import itertools from ansi.color import fg diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py index 0b6e22de..f1f55bdb 100644 --- a/scripts/fbt_tools/fbt_sdk.py +++ b/scripts/fbt_tools/fbt_sdk.py @@ -4,7 +4,7 @@ from SCons.Action import Action from SCons.Errors import UserError # from SCons.Scanner import C -from SCons.Script import Mkdir, Copy, Delete, Entry +from SCons.Script import Entry from SCons.Util import LogicalLines import os.path @@ -12,7 +12,8 @@ import posixpath import pathlib import json -from fbt.sdk import SdkCollector, SdkCache +from fbt.sdk.collector import SdkCollector +from fbt.sdk.cache import SdkCache def ProcessSdkDepends(env, filename): @@ -49,15 +50,19 @@ def prebuild_sdk_create_origin_file(target, source, env): class SdkMeta: - def __init__(self, env): + def __init__(self, env, tree_builder: "SdkTreeBuilder"): self.env = env + self.treebuilder = tree_builder def save_to(self, json_manifest_path: str): meta_contents = { - "sdk_symbols": self.env["SDK_DEFINITION"].name, + "sdk_symbols": self.treebuilder.build_sdk_file_path( + self.env["SDK_DEFINITION"].path + ), "cc_args": self._wrap_scons_vars("$CCFLAGS $_CCCOMCOM"), "cpp_args": self._wrap_scons_vars("$CXXFLAGS $CCFLAGS $_CCCOMCOM"), "linker_args": self._wrap_scons_vars("$LINKFLAGS"), + "linker_script": self.env.subst("${LINKER_SCRIPT_PATH}"), } with open(json_manifest_path, "wt") as f: json.dump(meta_contents, f, indent=4) @@ -68,6 +73,8 @@ class SdkMeta: class SdkTreeBuilder: + SDK_DIR_SUBST = "SDK_ROOT_DIR" + def __init__(self, env, target, source) -> None: self.env = env self.target = target @@ -88,6 +95,8 @@ class SdkTreeBuilder: self.header_depends = list( filter(lambda fname: fname.endswith(".h"), depends.split()), ) + self.header_depends.append(self.env.subst("${LINKER_SCRIPT_PATH}")) + self.header_depends.append(self.env.subst("${SDK_DEFINITION}")) self.header_dirs = sorted( set(map(os.path.normpath, map(os.path.dirname, self.header_depends))) ) @@ -102,17 +111,33 @@ class SdkTreeBuilder: ) sdk_dirs = ", ".join(f"'{dir}'" for dir in self.header_dirs) - for dir in full_fw_paths: - if dir in sdk_dirs: - filtered_paths.append( - posixpath.normpath(posixpath.join(self.target_sdk_dir_name, dir)) - ) + filtered_paths.extend( + map( + self.build_sdk_file_path, + filter(lambda path: path in sdk_dirs, full_fw_paths), + ) + ) sdk_env = self.env.Clone() - sdk_env.Replace(CPPPATH=filtered_paths) - meta = SdkMeta(sdk_env) + sdk_env.Replace( + CPPPATH=filtered_paths, + LINKER_SCRIPT=self.env.subst("${APP_LINKER_SCRIPT}"), + ORIG_LINKER_SCRIPT_PATH=self.env["LINKER_SCRIPT_PATH"], + LINKER_SCRIPT_PATH=self.build_sdk_file_path("${ORIG_LINKER_SCRIPT_PATH}"), + ) + + meta = SdkMeta(sdk_env, self) meta.save_to(self.target[0].path) + def build_sdk_file_path(self, orig_path: str) -> str: + return posixpath.normpath( + posixpath.join( + self.SDK_DIR_SUBST, + self.target_sdk_dir_name, + orig_path, + ) + ).replace("\\", "/") + def emitter(self, target, source, env): target_folder = target[0] target = [target_folder.File("sdk.opts")] @@ -128,8 +153,6 @@ class SdkTreeBuilder: for sdkdir in dirs_to_create: os.makedirs(sdkdir, exist_ok=True) - shutil.copy2(self.env["SDK_DEFINITION"].path, self.sdk_root_dir.path) - for header in self.header_depends: shutil.copy2(header, self.sdk_deploy_dir.File(header).path) diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index 4c042789..7636c87b 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -48,48 +48,52 @@ class Main(App): ) self.parser_copy.set_defaults(func=self.copy) - def get_project_filename(self, project, filetype): + def get_project_file_name(self, project: ProjectDir, filetype: str) -> str: # Temporary fix project_name = project.project - if project_name == "firmware": - if filetype == "zip": - project_name = "sdk" - elif filetype != "elf": - project_name = "full" + if project_name == "firmware" and filetype != "elf": + project_name = "full" - return f"{self.DIST_FILE_PREFIX}{self.target}-{project_name}-{self.args.suffix}.{filetype}" + return self.get_dist_file_name(project_name, filetype) - def get_dist_filepath(self, filename): + def get_dist_file_name(self, dist_artifact_type: str, filetype: str) -> str: + return f"{self.DIST_FILE_PREFIX}{self.target}-{dist_artifact_type}-{self.args.suffix}.{filetype}" + + def get_dist_file_path(self, filename: str) -> str: return join(self.output_dir_path, filename) - def copy_single_project(self, project): + def copy_single_project(self, project: ProjectDir) -> None: obj_directory = join("build", project.dir) for filetype in ("elf", "bin", "dfu", "json"): if exists(src_file := join(obj_directory, f"{project.project}.{filetype}")): shutil.copyfile( src_file, - self.get_dist_filepath( - self.get_project_filename(project, filetype) + self.get_dist_file_path( + self.get_project_file_name(project, filetype) ), ) - if exists(sdk_folder := join(obj_directory, "sdk")): - with zipfile.ZipFile( - self.get_dist_filepath(self.get_project_filename(project, "zip")), - "w", - zipfile.ZIP_DEFLATED, - ) as zf: - for root, dirs, files in walk(sdk_folder): - for file in files: - zf.write( - join(root, file), - relpath( - join(root, file), - sdk_folder, - ), - ) + for foldertype in ("sdk", "lib"): + if exists(sdk_folder := join(obj_directory, foldertype)): + self.package_zip(foldertype, sdk_folder) - def copy(self): + def package_zip(self, foldertype, sdk_folder): + with zipfile.ZipFile( + self.get_dist_file_path(self.get_dist_file_name(foldertype, "zip")), + "w", + zipfile.ZIP_DEFLATED, + ) as zf: + for root, _, files in walk(sdk_folder): + for file in files: + zf.write( + join(root, file), + relpath( + join(root, file), + sdk_folder, + ), + ) + + def copy(self) -> int: self.projects = dict( map( lambda pd: (pd.project, pd), @@ -144,12 +148,12 @@ class Main(App): "-t", self.target, "--dfu", - self.get_dist_filepath( - self.get_project_filename(self.projects["firmware"], "dfu") + self.get_dist_file_path( + self.get_project_file_name(self.projects["firmware"], "dfu") ), "--stage", - self.get_dist_filepath( - self.get_project_filename(self.projects["updater"], "bin") + self.get_dist_file_path( + self.get_project_file_name(self.projects["updater"], "bin") ), ] if self.args.resources: diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 index 370f1a14..aaed8985 100644 --- a/scripts/toolchain/windows-toolchain-download.ps1 +++ b/scripts/toolchain/windows-toolchain-download.ps1 @@ -23,12 +23,12 @@ if (!(Test-Path -LiteralPath "$repo_root\toolchain")) { New-Item "$repo_root\toolchain" -ItemType Directory } -Write-Host -NoNewline "Unziping Windows toolchain.." +Write-Host -NoNewline "Extracting Windows toolchain.." Add-Type -Assembly "System.IO.Compression.Filesystem" -[System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip", "$repo_root\") +[System.IO.Compression.ZipFile]::ExtractToDirectory("$repo_root\$toolchain_zip", "$repo_root\") Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\x86_64-windows" Write-Host "done!" -Write-Host -NoNewline "Clearing temporary files.." +Write-Host -NoNewline "Cleaning up temporary files.." Remove-Item -LiteralPath "$repo_root\$toolchain_zip" -Force Write-Host "done!" diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index ee317be3..90d228e5 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -21,7 +21,7 @@ appenv = ENV.Clone( ) appenv.Replace( - LINKER_SCRIPT="application_ext", + LINKER_SCRIPT=appenv.subst("$APP_LINKER_SCRIPT"), ) appenv.AppendUnique( diff --git a/site_scons/firmwareopts.scons b/site_scons/firmwareopts.scons index f04b55cd..9f707b4d 100644 --- a/site_scons/firmwareopts.scons +++ b/site_scons/firmwareopts.scons @@ -32,12 +32,27 @@ else: ], ) -ENV.Append( +ENV.AppendUnique( LINKFLAGS=[ - "-Tfirmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld", + "-specs=nano.specs", + "-specs=nosys.specs", + "-Wl,--gc-sections", + "-Wl,--undefined=uxTopUsedPriority", + "-Wl,--wrap,_malloc_r", + "-Wl,--wrap,_free_r", + "-Wl,--wrap,_calloc_r", + "-Wl,--wrap,_realloc_r", + "-n", + "-Xlinker", + "-Map=${TARGET}.map", + "-T${LINKER_SCRIPT_PATH}", ], ) +ENV.SetDefault( + LINKER_SCRIPT_PATH="firmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld", +) + if ENV["FIRMWARE_BUILD_CFG"] == "updater": ENV.Append( IMAGE_BASE_ADDRESS="0x20000000", @@ -47,4 +62,5 @@ else: ENV.Append( IMAGE_BASE_ADDRESS="0x8000000", LINKER_SCRIPT="stm32wb55xx_flash", + APP_LINKER_SCRIPT="application_ext", ) From 09b622d4ae01ef6d3ffcfd0eefabfa5cf890ffdd Mon Sep 17 00:00:00 2001 From: Konstantin Volkov <72250702+doomwastaken@users.noreply.github.com> Date: Fri, 28 Oct 2022 18:45:22 +0300 Subject: [PATCH 38/42] UnitTests: removed all continue-on-error lines (#1946) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * removed all continue-on-error lines * Github: add assets deployment after format Co-authored-by: Konstantin Volkov Co-authored-by: あく --- .github/workflows/unit_tests.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 8c5bac2a..b5bf1000 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -24,33 +24,29 @@ jobs: - name: 'Compile unit tests firmware' id: compile - continue-on-error: true run: | FBT_TOOLCHAIN_PATH=/opt ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 - name: 'Wait for flipper to finish updating' id: connect if: steps.compile.outcome == 'success' - continue-on-error: true run: | python3 ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - name: 'Format flipper SD card' id: format if: steps.connect.outcome == 'success' - continue-on-error: true run: | ./scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext - - name: 'Copy unit tests to flipper' + - name: 'Copy assets and unit tests data to flipper' id: copy if: steps.format.outcome == 'success' - continue-on-error: true run: | + ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/resources /ext ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/unit_tests /ext/unit_tests - name: 'Run units and validate results' if: steps.copy.outcome == 'success' - continue-on-error: true run: | python3 ./scripts/testing/units.py ${{steps.device.outputs.flipper}} From 93a6e17ce57222fa7b92a930dd4f9aaee3a146bf Mon Sep 17 00:00:00 2001 From: gornekich Date: Fri, 28 Oct 2022 20:10:16 +0400 Subject: [PATCH 39/42] [FL-2933] Mf Classic initial write, update, detect reader (#1941) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc: introduce nfc write * nfc: add write logic * nfc worker: add write state * nfc: add mfc update logic * nfc: add update success logic * nfc: add custom card for detect reader * nfc: update write logic * nfc: add halt command, add notifications * nfc: add write fail scene * nfc: fixes and clean up * nfc: fix navigation ad notifications * nfc: fix detect reader nfc data setter Co-authored-by: あく --- .../main/nfc/scenes/nfc_scene_config.h | 6 + .../main/nfc/scenes/nfc_scene_detect_reader.c | 5 + .../nfc/scenes/nfc_scene_mf_classic_update.c | 98 +++++++ .../nfc_scene_mf_classic_update_success.c | 44 +++ .../nfc/scenes/nfc_scene_mf_classic_write.c | 92 ++++++ .../scenes/nfc_scene_mf_classic_write_fail.c | 58 ++++ .../nfc_scene_mf_classic_write_success.c | 44 +++ .../scenes/nfc_scene_mf_classic_wrong_card.c | 53 ++++ .../main/nfc/scenes/nfc_scene_saved_menu.c | 34 +++ .../main/nfc/scenes/nfc_scene_start.c | 1 + applications/main/nfc/views/detect_reader.c | 36 +++ applications/main/nfc/views/detect_reader.h | 2 + firmware/targets/f7/furi_hal/furi_hal_nfc.c | 10 +- lib/nfc/helpers/reader_analyzer.c | 9 +- lib/nfc/helpers/reader_analyzer.h | 2 + lib/nfc/nfc_device.c | 7 + lib/nfc/nfc_worker.c | 150 +++++++++- lib/nfc/nfc_worker.h | 7 +- lib/nfc/nfc_worker_i.h | 4 + lib/nfc/protocols/crypto1.c | 52 ++++ lib/nfc/protocols/crypto1.h | 14 + lib/nfc/protocols/mifare_classic.c | 271 ++++++++++++------ lib/nfc/protocols/mifare_classic.h | 43 +++ 23 files changed, 949 insertions(+), 93 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_update.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a25850c8..9b922add 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -36,6 +36,12 @@ ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete) ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate) ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) +ADD_SCENE(nfc, mf_classic_write, MfClassicWrite) +ADD_SCENE(nfc, mf_classic_write_success, MfClassicWriteSuccess) +ADD_SCENE(nfc, mf_classic_write_fail, MfClassicWriteFail) +ADD_SCENE(nfc, mf_classic_update, MfClassicUpdate) +ADD_SCENE(nfc, mf_classic_update_success, MfClassicUpdateSuccess) +ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard) ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) ADD_SCENE(nfc, emv_menu, EmvMenu) ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) diff --git a/applications/main/nfc/scenes/nfc_scene_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_detect_reader.c index abf1437d..74594615 100644 --- a/applications/main/nfc/scenes/nfc_scene_detect_reader.c +++ b/applications/main/nfc/scenes/nfc_scene_detect_reader.c @@ -28,6 +28,11 @@ void nfc_scene_detect_reader_on_enter(void* context) { detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc); detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX); + NfcDeviceData* dev_data = &nfc->dev->dev_data; + if(dev_data->nfc_data.uid_len) { + detect_reader_set_uid( + nfc->detect_reader, dev_data->nfc_data.uid, dev_data->nfc_data.uid_len); + } // Store number of collected nonces in scene state scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDetectReader, 0); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c new file mode 100644 index 00000000..dd3a6f7d --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c @@ -0,0 +1,98 @@ +#include "../nfc_i.h" +#include + +enum { + NfcSceneMfClassicUpdateStateCardSearch, + NfcSceneMfClassicUpdateStateCardFound, +}; + +bool nfc_mf_classic_update_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + + return true; +} + +static void nfc_scene_mf_classic_update_setup_view(Nfc* nfc) { + Popup* popup = nfc->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicUpdate); + + if(state == NfcSceneMfClassicUpdateStateCardSearch) { + popup_set_text( + nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Updating\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_update_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); + nfc_scene_mf_classic_update_setup_view(nfc); + + // Setup and start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfClassicUpdate, + &nfc->dev->dev_data, + nfc_mf_classic_update_worker_callback, + nfc); + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_mf_classic_update_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventSuccess) { + nfc_worker_stop(nfc->worker); + if(nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdateSuccess); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); + } + consumed = true; + } else if(event.event == NfcWorkerEventWrongCard) { + nfc_worker_stop(nfc->worker); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, + NfcSceneMfClassicUpdate, + NfcSceneMfClassicUpdateStateCardFound); + nfc_scene_mf_classic_update_setup_view(nfc); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, + NfcSceneMfClassicUpdate, + NfcSceneMfClassicUpdateStateCardSearch); + nfc_scene_mf_classic_update_setup_view(nfc); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_classic_update_on_exit(void* context) { + Nfc* nfc = context; + nfc_worker_stop(nfc->worker); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); + // Clear view + popup_reset(nfc->popup); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c new file mode 100644 index 00000000..fef8fd5e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_classic_update_success_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_update_success_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcSave); + + notification_message(nfc->notifications, &sequence_success); + + Popup* popup = nfc->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_mf_classic_update_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_update_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } + return consumed; +} + +void nfc_scene_mf_classic_update_success_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c new file mode 100644 index 00000000..3543cbc5 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c @@ -0,0 +1,92 @@ +#include "../nfc_i.h" +#include + +enum { + NfcSceneMfClassicWriteStateCardSearch, + NfcSceneMfClassicWriteStateCardFound, +}; + +bool nfc_mf_classic_write_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + + return true; +} + +static void nfc_scene_mf_classic_write_setup_view(Nfc* nfc) { + Popup* popup = nfc->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicWrite); + + if(state == NfcSceneMfClassicWriteStateCardSearch) { + popup_set_text( + nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_write_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); + nfc_scene_mf_classic_write_setup_view(nfc); + + // Setup and start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfClassicWrite, + &nfc->dev->dev_data, + nfc_mf_classic_write_worker_callback, + nfc); + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_mf_classic_write_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventSuccess) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventFail) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteFail); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCard) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardFound); + nfc_scene_mf_classic_write_setup_view(nfc); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); + nfc_scene_mf_classic_write_setup_view(nfc); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_classic_write_on_exit(void* context) { + Nfc* nfc = context; + + nfc_worker_stop(nfc->worker); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); + // Clear view + popup_reset(nfc->popup); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c new file mode 100644 index 00000000..aeea6eef --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c @@ -0,0 +1,58 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_write_fail_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_write_fail_on_enter(void* context) { + Nfc* nfc = context; + Widget* widget = nfc->widget; + + notification_message(nfc->notifications, &sequence_error); + + widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); + widget_add_string_multiline_element( + widget, + 7, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Not all sectors\nwere written\ncorrectly."); + + widget_add_button_element( + widget, GuiButtonTypeLeft, "Finish", nfc_scene_mf_classic_write_fail_widget_callback, nfc); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_write_fail_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneSavedMenu); + } + return consumed; +} + +void nfc_scene_mf_classic_write_fail_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c new file mode 100644 index 00000000..2f2a3beb --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_classic_write_success_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_write_success_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcSave); + + notification_message(nfc->notifications, &sequence_success); + + Popup* popup = nfc->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_mf_classic_write_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_write_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } + return consumed; +} + +void nfc_scene_mf_classic_write_success_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c new file mode 100644 index 00000000..2c56270e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c @@ -0,0 +1,53 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_wrong_card_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_wrong_card_on_enter(void* context) { + Nfc* nfc = context; + Widget* widget = nfc->widget; + + notification_message(nfc->notifications, &sequence_error); + + widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); + widget_add_string_multiline_element( + widget, + 4, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Data management\nis only possible\nwith initial card"); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_classic_wrong_card_widget_callback, nfc); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_wrong_card_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + } + return consumed; +} + +void nfc_scene_mf_classic_wrong_card_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 09d2c2d6..231f1208 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -4,6 +4,9 @@ enum SubmenuIndex { SubmenuIndexEmulate, SubmenuIndexEditUid, + SubmenuIndexDetectReader, + SubmenuIndexWrite, + SubmenuIndexUpdate, SubmenuIndexRename, SubmenuIndexDelete, SubmenuIndexInfo, @@ -42,6 +45,28 @@ void nfc_scene_saved_menu_on_enter(void* context) { submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); } + if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { + if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) { + submenu_add_item( + submenu, + "Detect reader", + SubmenuIndexDetectReader, + nfc_scene_saved_menu_submenu_callback, + nfc); + } + submenu_add_item( + submenu, + "Write To Initial Card", + SubmenuIndexWrite, + nfc_scene_saved_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Update From Initial Card", + SubmenuIndexUpdate, + nfc_scene_saved_menu_submenu_callback, + nfc); + } submenu_add_item( submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); if(nfc->dev->shadow_file_exist) { @@ -79,6 +104,15 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } DOLPHIN_DEED(DolphinDeedNfcEmulate); consumed = true; + } else if(event.event == SubmenuIndexDetectReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + consumed = true; + } else if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); + consumed = true; + } else if(event.event == SubmenuIndexUpdate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdate); + consumed = true; } else if(event.event == SubmenuIndexRename) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index 0c4ec1cf..028f85ae 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -53,6 +53,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexDetectReader) { bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; if(sd_exist) { + nfc_device_data_clear(&nfc->dev->dev_data); scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); DOLPHIN_DEED(DolphinDeedNfcDetectReader); } else { diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index 91537868..e5951beb 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -2,6 +2,8 @@ #include #include +#define DETECT_READER_UID_MAX_LEN (10) + struct DetectReader { View* view; DetectReaderDoneCallback callback; @@ -12,6 +14,7 @@ typedef struct { uint16_t nonces; uint16_t nonces_max; DetectReaderState state; + FuriString* uid_str; } DetectReaderViewModel; static void detect_reader_draw_callback(Canvas* canvas, void* model) { @@ -23,6 +26,10 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) { if(m->state == DetectReaderStateStart) { snprintf(text, sizeof(text), "Touch the reader"); canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39); + if(furi_string_size(m->uid_str)) { + elements_multiline_text_aligned( + canvas, 64, 64, AlignCenter, AlignBottom, furi_string_get_cstr(m->uid_str)); + } } else if(m->state == DetectReaderStateReaderDetected) { snprintf(text, sizeof(text), "Move the Flipper away"); canvas_draw_icon(canvas, 24, 25, &I_Release_arrow_18x15); @@ -86,12 +93,24 @@ DetectReader* detect_reader_alloc() { view_set_input_callback(detect_reader->view, detect_reader_input_callback); view_set_context(detect_reader->view, detect_reader); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { model->uid_str = furi_string_alloc(); }, + false); + return detect_reader; } void detect_reader_free(DetectReader* detect_reader) { furi_assert(detect_reader); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { furi_string_free(model->uid_str); }, + false); + view_free(detect_reader->view); free(detect_reader); } @@ -106,6 +125,7 @@ void detect_reader_reset(DetectReader* detect_reader) { model->nonces = 0; model->nonces_max = 0; model->state = DetectReaderStateStart; + furi_string_reset(model->uid_str); }, false); } @@ -152,3 +172,19 @@ void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState stat with_view_model( detect_reader->view, DetectReaderViewModel * model, { model->state = state; }, true); } + +void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len) { + furi_assert(detect_reader); + furi_assert(uid); + furi_assert(uid_len < DETECT_READER_UID_MAX_LEN); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { + furi_string_set_str(model->uid_str, "UID:"); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(model->uid_str, " %02X", uid[i]); + } + }, + true); +} diff --git a/applications/main/nfc/views/detect_reader.h b/applications/main/nfc/views/detect_reader.h index aabdd7c8..6481216b 100644 --- a/applications/main/nfc/views/detect_reader.h +++ b/applications/main/nfc/views/detect_reader.h @@ -32,3 +32,5 @@ void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_m void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected); void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state); + +void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len); diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 069ac4ea..3ebf4f82 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -620,6 +620,10 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity( uint16_t in_buff_bits, uint8_t* out_data, uint8_t* out_parity) { + if(in_buff_bits < 8) { + out_data[0] = in_buff[0]; + return in_buff_bits; + } if(in_buff_bits % 9 != 0) { return 0; } @@ -635,7 +639,7 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity( bit_processed += 9; curr_byte++; } - return curr_byte; + return curr_byte * 8; } bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { @@ -692,8 +696,8 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw || tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRxRaw) { - tx_rx->rx_bits = 8 * furi_hal_nfc_bitstream_to_data_and_parity( - temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity); + tx_rx->rx_bits = furi_hal_nfc_bitstream_to_data_and_parity( + temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity); } else { memcpy(tx_rx->rx_data, temp_rx_buff, MIN(*temp_rx_bits / 8, FURI_HAL_NFC_DATA_BUFF_SIZE)); tx_rx->rx_bits = *temp_rx_bits; diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/helpers/reader_analyzer.c index 0ba657a2..7fed9c6f 100644 --- a/lib/nfc/helpers/reader_analyzer.c +++ b/lib/nfc/helpers/reader_analyzer.c @@ -201,10 +201,17 @@ NfcProtocol FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) { furi_assert(instance); - + instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic]; return &instance->nfc_data; } +void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data) { + furi_assert(instance); + furi_assert(nfc_data); + + memcpy(&instance->nfc_data, nfc_data, sizeof(FuriHalNfcDevData)); +} + static void reader_analyzer_write( ReaderAnalyzer* instance, uint8_t* data, diff --git a/lib/nfc/helpers/reader_analyzer.h b/lib/nfc/helpers/reader_analyzer.h index cc501f5a..13bf4d77 100644 --- a/lib/nfc/helpers/reader_analyzer.h +++ b/lib/nfc/helpers/reader_analyzer.h @@ -35,6 +35,8 @@ NfcProtocol FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance); +void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data); + void reader_analyzer_prepare_tx_rx( ReaderAnalyzer* instance, FuriHalNfcTxRxContext* tx_rx, diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 740cfae5..a5e3fc14 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -1122,6 +1122,13 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; + // Load CUID + uint8_t* cuid_start = data->uid; + if(data->uid_len == 7) { + cuid_start = &data->uid[3]; + } + data->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | + (cuid_start[3]); // Parse other data if(dev->format == NfcDeviceSaveFormatMifareUl) { if(!nfc_device_load_mifare_ul_data(file, dev)) break; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index ebe20390..e1e379a0 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -99,6 +99,10 @@ int32_t nfc_worker_task(void* context) { nfc_worker_emulate_mf_ultralight(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { nfc_worker_emulate_mf_classic(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateMfClassicWrite) { + nfc_worker_write_mf_classic(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateMfClassicUpdate) { + nfc_worker_update_mf_classic(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { nfc_worker_mf_ultralight_read_auth(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { @@ -666,6 +670,144 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { rfal_platform_spi_release(); } +void nfc_worker_write_mf_classic(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + bool card_found_notified = false; + FuriHalNfcDevData nfc_data = {}; + MfClassicData* src_data = &nfc_worker->dev_data->mf_classic_data; + MfClassicData dest_data = *src_data; + + while(nfc_worker->state == NfcWorkerStateMfClassicWrite) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(!card_found_notified) { + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + card_found_notified = true; + } + furi_hal_nfc_sleep(); + + FURI_LOG_I(TAG, "Check low level nfc data"); + if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) { + FURI_LOG_E(TAG, "Wrong card"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + FURI_LOG_I(TAG, "Check mf classic type"); + MfClassicType type = + mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak); + if(type != nfc_worker->dev_data->mf_classic_data.type) { + FURI_LOG_E(TAG, "Wrong mf classic type"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + // Set blocks not read + mf_classic_set_sector_data_not_read(&dest_data); + FURI_LOG_I(TAG, "Updating card sectors"); + uint8_t total_sectors = mf_classic_get_total_sectors_num(type); + bool write_success = true; + for(uint8_t i = 0; i < total_sectors; i++) { + FURI_LOG_I(TAG, "Reading sector %d", i); + mf_classic_read_sector(&tx_rx, &dest_data, i); + bool old_data_read = mf_classic_is_sector_data_read(src_data, i); + bool new_data_read = mf_classic_is_sector_data_read(&dest_data, i); + if(old_data_read != new_data_read) { + FURI_LOG_E(TAG, "Failed to update sector %d", i); + write_success = false; + break; + } + if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break; + if(!mf_classic_write_sector(&tx_rx, &dest_data, src_data, i)) { + FURI_LOG_E(TAG, "Failed to write %d sector", i); + write_success = false; + break; + } + } + if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break; + if(write_success) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + break; + } else { + nfc_worker->callback(NfcWorkerEventFail, nfc_worker->context); + break; + } + + } else { + if(card_found_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_found_notified = false; + } + } + furi_delay_ms(300); + } +} + +void nfc_worker_update_mf_classic(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + bool card_found_notified = false; + FuriHalNfcDevData nfc_data = {}; + MfClassicData* old_data = &nfc_worker->dev_data->mf_classic_data; + MfClassicData new_data = *old_data; + + while(nfc_worker->state == NfcWorkerStateMfClassicUpdate) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(!card_found_notified) { + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + card_found_notified = true; + } + furi_hal_nfc_sleep(); + + FURI_LOG_I(TAG, "Check low level nfc data"); + if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) { + FURI_LOG_E(TAG, "Low level nfc data mismatch"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + FURI_LOG_I(TAG, "Check MF classic type"); + MfClassicType type = + mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak); + if(type != nfc_worker->dev_data->mf_classic_data.type) { + FURI_LOG_E(TAG, "MF classic type mismatch"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + // Set blocks not read + mf_classic_set_sector_data_not_read(&new_data); + FURI_LOG_I(TAG, "Updating card sectors"); + uint8_t total_sectors = mf_classic_get_total_sectors_num(type); + bool update_success = true; + for(uint8_t i = 0; i < total_sectors; i++) { + FURI_LOG_I(TAG, "Reading sector %d", i); + mf_classic_read_sector(&tx_rx, &new_data, i); + bool old_data_read = mf_classic_is_sector_data_read(old_data, i); + bool new_data_read = mf_classic_is_sector_data_read(&new_data, i); + if(old_data_read != new_data_read) { + FURI_LOG_E(TAG, "Failed to update sector %d", i); + update_success = false; + break; + } + if(nfc_worker->state != NfcWorkerStateMfClassicUpdate) break; + } + if(nfc_worker->state != NfcWorkerStateMfClassicUpdate) break; + + // Check updated data + if(update_success) { + *old_data = new_data; + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + break; + } + } else { + if(card_found_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_found_notified = false; + } + } + furi_delay_ms(300); + } +} + void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); @@ -758,7 +900,13 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer; - FuriHalNfcDevData* nfc_data = reader_analyzer_get_nfc_data(reader_analyzer); + FuriHalNfcDevData* nfc_data = NULL; + if(nfc_worker->dev_data->protocol == NfcDeviceProtocolMifareClassic) { + nfc_data = &nfc_worker->dev_data->nfc_data; + reader_analyzer_set_nfc_data(reader_analyzer, nfc_data); + } else { + nfc_data = reader_analyzer_get_nfc_data(reader_analyzer); + } MfClassicEmulator emulator = { .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), .data = nfc_worker->dev_data->mf_classic_data, diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 84615f5d..ce3a1824 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -14,6 +14,8 @@ typedef enum { NfcWorkerStateUidEmulate, NfcWorkerStateMfUltralightEmulate, NfcWorkerStateMfClassicEmulate, + NfcWorkerStateMfClassicWrite, + NfcWorkerStateMfClassicUpdate, NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, @@ -48,13 +50,16 @@ typedef enum { NfcWorkerEventNoCardDetected, NfcWorkerEventWrongCardDetected, - // Mifare Classic events + // Read Mifare Classic events NfcWorkerEventNoDictFound, NfcWorkerEventNewSector, NfcWorkerEventNewDictKeyBatch, NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, + // Write Mifare Classic events + NfcWorkerEventWrongCard, + // Detect Reader events NfcWorkerEventDetectReaderDetected, NfcWorkerEventDetectReaderLost, diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index 526182f9..b9f69e62 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -41,6 +41,10 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker); +void nfc_worker_write_mf_classic(NfcWorker* nfc_worker); + +void nfc_worker_update_mf_classic(NfcWorker* nfc_worker); + void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker); void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker); diff --git a/lib/nfc/protocols/crypto1.c b/lib/nfc/protocols/crypto1.c index f08164ba..2ac0ff08 100644 --- a/lib/nfc/protocols/crypto1.c +++ b/lib/nfc/protocols/crypto1.c @@ -73,3 +73,55 @@ uint32_t prng_successor(uint32_t x, uint32_t n) { return SWAPENDIAN(x); } + +void crypto1_decrypt( + Crypto1* crypto, + uint8_t* encrypted_data, + uint16_t encrypted_data_bits, + uint8_t* decrypted_data) { + furi_assert(crypto); + furi_assert(encrypted_data); + furi_assert(decrypted_data); + + if(encrypted_data_bits < 8) { + uint8_t decrypted_byte = 0; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3; + decrypted_data[0] = decrypted_byte; + } else { + for(size_t i = 0; i < encrypted_data_bits / 8; i++) { + decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; + } + } +} + +void crypto1_encrypt( + Crypto1* crypto, + uint8_t* keystream, + uint8_t* plain_data, + uint16_t plain_data_bits, + uint8_t* encrypted_data, + uint8_t* encrypted_parity) { + furi_assert(crypto); + furi_assert(plain_data); + furi_assert(encrypted_data); + furi_assert(encrypted_parity); + + if(plain_data_bits < 8) { + encrypted_data[0] = 0; + for(size_t i = 0; i < plain_data_bits; i++) { + encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i; + } + } else { + memset(encrypted_parity, 0, plain_data_bits / 8 + 1); + for(uint8_t i = 0; i < plain_data_bits / 8; i++) { + encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ + plain_data[i]; + encrypted_parity[i / 8] |= + (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01) + << (7 - (i & 0x0007))); + } + } +} diff --git a/lib/nfc/protocols/crypto1.h b/lib/nfc/protocols/crypto1.h index 07b39c22..450d1534 100644 --- a/lib/nfc/protocols/crypto1.h +++ b/lib/nfc/protocols/crypto1.h @@ -21,3 +21,17 @@ uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted); uint32_t crypto1_filter(uint32_t in); uint32_t prng_successor(uint32_t x, uint32_t n); + +void crypto1_decrypt( + Crypto1* crypto, + uint8_t* encrypted_data, + uint16_t encrypted_data_bits, + uint8_t* decrypted_data); + +void crypto1_encrypt( + Crypto1* crypto, + uint8_t* keystream, + uint8_t* plain_data, + uint16_t plain_data_bits, + uint8_t* encrypted_data, + uint8_t* encrypted_parity); diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index e879ff4e..7b0e1797 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -9,21 +9,8 @@ #define MF_CLASSIC_AUTH_KEY_A_CMD (0x60U) #define MF_CLASSIC_AUTH_KEY_B_CMD (0x61U) -#define MF_CLASSIC_READ_SECT_CMD (0x30) - -typedef enum { - MfClassicActionDataRead, - MfClassicActionDataWrite, - MfClassicActionDataInc, - MfClassicActionDataDec, - - MfClassicActionKeyARead, - MfClassicActionKeyAWrite, - MfClassicActionKeyBRead, - MfClassicActionKeyBWrite, - MfClassicActionACRead, - MfClassicActionACWrite, -} MfClassicAction; +#define MF_CLASSIC_READ_BLOCK_CMD (0x30) +#define MF_CLASSIC_WRITE_BLOCK_CMD (0xA0) const char* mf_classic_get_type_str(MfClassicType type) { if(type == MfClassicType1k) { @@ -122,6 +109,24 @@ void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassic FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32); } +bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num) { + furi_assert(data); + + uint8_t first_block = mf_classic_get_first_block_num_of_sector(sector_num); + uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num); + bool data_read = true; + for(size_t i = first_block; i < first_block + total_blocks; i++) { + data_read &= mf_classic_is_block_read(data, i); + } + + return data_read; +} + +void mf_classic_set_sector_data_not_read(MfClassicData* data) { + furi_assert(data); + memset(data->block_read_mask, 0, sizeof(data->block_read_mask)); +} + bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) { furi_assert(data); @@ -190,6 +195,9 @@ void mf_classic_get_read_sectors_and_keys( uint8_t* sectors_read, uint8_t* keys_found) { furi_assert(data); + furi_assert(sectors_read); + furi_assert(keys_found); + *sectors_read = 0; *keys_found = 0; uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); @@ -225,12 +233,12 @@ bool mf_classic_is_card_read(MfClassicData* data) { return card_read; } -static bool mf_classic_is_allowed_access_sector_trailer( - MfClassicEmulator* emulator, +bool mf_classic_is_allowed_access_sector_trailer( + MfClassicData* data, uint8_t block_num, MfClassicKey key, MfClassicAction action) { - uint8_t* sector_trailer = emulator->data.block[block_num].value; + uint8_t* sector_trailer = data->block[block_num].value; uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) | ((sector_trailer[8] >> 7) & 0x01); switch(action) { @@ -266,13 +274,13 @@ static bool mf_classic_is_allowed_access_sector_trailer( return true; } -static bool mf_classic_is_allowed_access_data_block( - MfClassicEmulator* emulator, +bool mf_classic_is_allowed_access_data_block( + MfClassicData* data, uint8_t block_num, MfClassicKey key, MfClassicAction action) { uint8_t* sector_trailer = - emulator->data.block[mf_classic_get_sector_trailer_num_by_block(block_num)].value; + data->block[mf_classic_get_sector_trailer_num_by_block(block_num)].value; uint8_t sector_block; if(block_num <= 128) { @@ -336,9 +344,10 @@ static bool mf_classic_is_allowed_access( MfClassicKey key, MfClassicAction action) { if(mf_classic_is_sector_trailer(block_num)) { - return mf_classic_is_allowed_access_sector_trailer(emulator, block_num, key, action); + return mf_classic_is_allowed_access_sector_trailer( + &emulator->data, block_num, key, action); } else { - return mf_classic_is_allowed_access_data_block(emulator, block_num, key, action); + return mf_classic_is_allowed_access_data_block(&emulator->data, block_num, key, action); } } @@ -514,25 +523,17 @@ bool mf_classic_read_block( furi_assert(block); bool read_block_success = false; - uint8_t plain_cmd[4] = {MF_CLASSIC_READ_SECT_CMD, block_num, 0x00, 0x00}; + uint8_t plain_cmd[4] = {MF_CLASSIC_READ_BLOCK_CMD, block_num, 0x00, 0x00}; nfca_append_crc16(plain_cmd, 2); - memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data)); - memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity)); - for(uint8_t i = 0; i < 4; i++) { - tx_rx->tx_data[i] = crypto1_byte(crypto, 0x00, 0) ^ plain_cmd[i]; - tx_rx->tx_parity[0] |= - ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_cmd[i])) & 0x01) << (7 - i); - } + crypto1_encrypt(crypto, NULL, plain_cmd, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = 4 * 9; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 8 * (MF_CLASSIC_BLOCK_SIZE + 2)) { uint8_t block_received[MF_CLASSIC_BLOCK_SIZE + 2]; - for(uint8_t i = 0; i < MF_CLASSIC_BLOCK_SIZE + 2; i++) { - block_received[i] = crypto1_byte(crypto, 0, 0) ^ tx_rx->rx_data[i]; - } + crypto1_decrypt(crypto, tx_rx->rx_data, tx_rx->rx_bits, block_received); uint16_t crc_calc = nfca_get_crc16(block_received, MF_CLASSIC_BLOCK_SIZE); uint16_t crc_received = (block_received[MF_CLASSIC_BLOCK_SIZE + 1] << 8) | block_received[MF_CLASSIC_BLOCK_SIZE]; @@ -754,49 +755,6 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data return sectors_read; } -void mf_crypto1_decrypt( - Crypto1* crypto, - uint8_t* encrypted_data, - uint16_t encrypted_data_bits, - uint8_t* decrypted_data) { - if(encrypted_data_bits < 8) { - uint8_t decrypted_byte = 0; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3; - decrypted_data[0] = decrypted_byte; - } else { - for(size_t i = 0; i < encrypted_data_bits / 8; i++) { - decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; - } - } -} - -void mf_crypto1_encrypt( - Crypto1* crypto, - uint8_t* keystream, - uint8_t* plain_data, - uint16_t plain_data_bits, - uint8_t* encrypted_data, - uint8_t* encrypted_parity) { - if(plain_data_bits < 8) { - encrypted_data[0] = 0; - for(size_t i = 0; i < plain_data_bits; i++) { - encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i; - } - } else { - memset(encrypted_parity, 0, plain_data_bits / 8 + 1); - for(uint8_t i = 0; i < plain_data_bits / 8; i++) { - encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ - plain_data[i]; - encrypted_parity[i / 8] |= - (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01) - << (7 - (i & 0x0007))); - } - } -} - bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx) { furi_assert(emulator); furi_assert(tx_rx); @@ -819,7 +777,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->rx_bits); break; } - mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); } if(plain_data[0] == 0x50 && plain_data[1] == 0x00) { @@ -857,7 +815,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->tx_bits = sizeof(nt) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; } else { - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, nt_keystream, nt, @@ -904,7 +862,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ uint32_t ans = prng_successor(nonce, 96); uint8_t responce[4] = {}; nfc_util_num2bytes(ans, 4, responce); - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, NULL, responce, @@ -938,7 +896,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // Send NACK uint8_t nack = 0x04; if(is_encrypted) { - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); } else { tx_rx->tx_data[0] = nack; @@ -951,7 +909,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } nfca_append_crc16(block_data, 16); - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, NULL, block_data, @@ -967,14 +925,14 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } // Send ACK uint8_t ack = 0x0A; - mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; if(tx_rx->rx_bits != 18 * 8) break; - mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); uint8_t block_data[16] = {}; memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); if(mf_classic_is_sector_trailer(block)) { @@ -1002,7 +960,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } // Send ACK ack = 0x0A; - mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; } else { @@ -1015,8 +973,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // Send NACK uint8_t nack = 0x04; if(is_encrypted) { - mf_crypto1_encrypt( - &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); + crypto1_encrypt(&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); } else { tx_rx->tx_data[0] = nack; } @@ -1027,3 +984,143 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ return true; } + +bool mf_classic_write_block( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key) { + furi_assert(tx_rx); + furi_assert(src_block); + + Crypto1 crypto = {}; + uint8_t plain_data[18] = {}; + uint8_t resp = 0; + bool write_success = false; + + do { + furi_hal_nfc_sleep(); + if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto)) { + FURI_LOG_D(TAG, "Auth fail"); + break; + } + // Send write command + plain_data[0] = MF_CLASSIC_WRITE_BLOCK_CMD; + plain_data[1] = block_num; + nfca_append_crc16(plain_data, 2); + crypto1_encrypt(&crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = 4 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp); + break; + } + } else { + FURI_LOG_D(TAG, "Not ACK received"); + break; + } + } else { + FURI_LOG_D(TAG, "Failed to send write cmd"); + break; + } + + // Send data + memcpy(plain_data, src_block->value, MF_CLASSIC_BLOCK_SIZE); + nfca_append_crc16(plain_data, MF_CLASSIC_BLOCK_SIZE); + crypto1_encrypt( + &crypto, + NULL, + plain_data, + (MF_CLASSIC_BLOCK_SIZE + 2) * 8, + tx_rx->tx_data, + tx_rx->tx_parity); + tx_rx->tx_bits = (MF_CLASSIC_BLOCK_SIZE + 2) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on sending data"); + break; + } + } else { + FURI_LOG_D(TAG, "Not ACK received"); + break; + } + } else { + FURI_LOG_D(TAG, "Failed to send data"); + break; + } + write_success = true; + + // Send Halt + plain_data[0] = 0x50; + plain_data[1] = 0x00; + nfca_append_crc16(plain_data, 2); + crypto1_encrypt(&crypto, NULL, plain_data, 2 * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = 2 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + // No response is expected + furi_hal_nfc_tx_rx(tx_rx, 50); + } while(false); + + return write_success; +} + +bool mf_classic_write_sector( + FuriHalNfcTxRxContext* tx_rx, + MfClassicData* dest_data, + MfClassicData* src_data, + uint8_t sec_num) { + furi_assert(tx_rx); + furi_assert(dest_data); + furi_assert(src_data); + + uint8_t first_block = mf_classic_get_first_block_num_of_sector(sec_num); + uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(dest_data, sec_num); + bool key_a_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyA); + bool key_b_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyB); + + bool write_success = true; + for(size_t i = first_block; i < first_block + total_blocks; i++) { + // Compare blocks + if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE)) { + bool key_a_write_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyA, MfClassicActionDataWrite); + bool key_b_write_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyB, MfClassicActionDataWrite); + + if(key_a_found && key_a_write_allowed) { + FURI_LOG_I(TAG, "Writing block %d with key A", i); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) { + FURI_LOG_E(TAG, "Failed to write block %d", i); + write_success = false; + break; + } + } else if(key_b_found && key_b_write_allowed) { + FURI_LOG_I(TAG, "Writing block %d with key A", i); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); + if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) { + FURI_LOG_E(TAG, "Failed to write block %d", i); + write_success = false; + break; + } + } else { + FURI_LOG_E(TAG, "Failed to find key with write access"); + write_success = false; + break; + } + } else { + FURI_LOG_D(TAG, "Blocks %d are equal", i); + } + } + + return write_success; +} diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index ead846e4..d5467b10 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -27,6 +27,20 @@ typedef enum { MfClassicKeyB, } MfClassicKey; +typedef enum { + MfClassicActionDataRead, + MfClassicActionDataWrite, + MfClassicActionDataInc, + MfClassicActionDataDec, + + MfClassicActionKeyARead, + MfClassicActionKeyAWrite, + MfClassicActionKeyBRead, + MfClassicActionKeyBWrite, + MfClassicActionACRead, + MfClassicActionACWrite, +} MfClassicAction; + typedef struct { uint8_t value[MF_CLASSIC_BLOCK_SIZE]; } MfClassicBlock; @@ -90,6 +104,18 @@ bool mf_classic_is_sector_trailer(uint8_t block); uint8_t mf_classic_get_sector_by_block(uint8_t block); +bool mf_classic_is_allowed_access_sector_trailer( + MfClassicData* data, + uint8_t block_num, + MfClassicKey key, + MfClassicAction action); + +bool mf_classic_is_allowed_access_data_block( + MfClassicData* data, + uint8_t block_num, + MfClassicKey key, + MfClassicAction action); + bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type); void mf_classic_set_key_found( @@ -104,6 +130,10 @@ bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num); void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data); +bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num); + +void mf_classic_set_sector_data_not_read(MfClassicData* data); + bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num); bool mf_classic_is_card_read(MfClassicData* data); @@ -145,3 +175,16 @@ uint8_t mf_classic_read_card( uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data); bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); + +bool mf_classic_write_block( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key); + +bool mf_classic_write_sector( + FuriHalNfcTxRxContext* tx_rx, + MfClassicData* dest_data, + MfClassicData* src_data, + uint8_t sec_num); From d5f791b1fa8ee34428b92f6c0c2ddac9d6fcbe0a Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Fri, 28 Oct 2022 19:43:54 +0300 Subject: [PATCH 40/42] [FL-2911] IR Universal Audio Remote (#1942) * Add Audio universal remote * Add signal library for Audio Universal Remote * Update UniversalRemotes.md * Added IR profile for Samsung K450 soundbar (#1892) * Add symbols to API file * Rearrange Audio remote buttons * Add new icons, remove old ones * Remove old signals, add new ones * Add universal audio remote to CLI, refactor code * Improve help text * Correct formatting * Update UniversalRemotes.md * Furi: restore correct api_symbols.csv version Co-authored-by: Alexei Humeniy Co-authored-by: Aleksandr Kutuzov --- applications/main/infrared/infrared_cli.c | 211 ++++++--------- .../infrared/scenes/infrared_scene_config.h | 1 + .../scenes/infrared_scene_universal.c | 8 +- .../scenes/infrared_scene_universal_audio.c | 133 ++++++++++ assets/icons/Infrared/Pause_25x27.png | Bin 0 -> 3634 bytes assets/icons/Infrared/Pause_hvr_25x27.png | Bin 0 -> 3623 bytes assets/icons/Infrared/Play_25x27.png | Bin 0 -> 3653 bytes assets/icons/Infrared/Play_hvr_25x27.png | Bin 0 -> 3643 bytes assets/icons/Infrared/TrackNext_25x27.png | Bin 0 -> 3651 bytes assets/icons/Infrared/TrackNext_hvr_25x27.png | Bin 0 -> 3639 bytes assets/icons/Infrared/TrackPrev_25x27.png | Bin 0 -> 3657 bytes assets/icons/Infrared/TrackPrev_hvr_25x27.png | Bin 0 -> 3644 bytes assets/resources/infrared/assets/audio.ir | 244 ++++++++++++++++++ documentation/UniversalRemotes.md | 16 +- 14 files changed, 486 insertions(+), 127 deletions(-) create mode 100644 applications/main/infrared/scenes/infrared_scene_universal_audio.c create mode 100644 assets/icons/Infrared/Pause_25x27.png create mode 100644 assets/icons/Infrared/Pause_hvr_25x27.png create mode 100644 assets/icons/Infrared/Play_25x27.png create mode 100644 assets/icons/Infrared/Play_hvr_25x27.png create mode 100644 assets/icons/Infrared/TrackNext_25x27.png create mode 100644 assets/icons/Infrared/TrackNext_hvr_25x27.png create mode 100644 assets/icons/Infrared/TrackPrev_25x27.png create mode 100644 assets/icons/Infrared/TrackPrev_hvr_25x27.png create mode 100644 assets/resources/infrared/assets/audio.ir diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 5a04f749..8f35a8fd 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -5,25 +5,21 @@ #include #include #include +#include #include "infrared_signal.h" #include "infrared_brute_force.h" -#include - #define INFRARED_CLI_BUF_SIZE 10 +#define INFRARED_ASSETS_FOLDER "infrared/assets" +#define INFRARED_BRUTE_FORCE_DUMMY_INDEX 0 DICT_DEF2(dict_signals, FuriString*, FURI_STRING_OPLIST, int, M_DEFAULT_OPLIST) -enum RemoteTypes { TV = 0, AC = 1 }; - static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args); static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args); static void infrared_cli_process_decode(Cli* cli, FuriString* args); static void infrared_cli_process_universal(Cli* cli, FuriString* args); -static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type); -static void - infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal); static const struct { const char* cmd; @@ -87,8 +83,10 @@ static void infrared_cli_print_usage(void) { INFRARED_MIN_FREQUENCY, INFRARED_MAX_FREQUENCY); printf("\tir decode []\r\n"); - printf("\tir universal \r\n"); - printf("\tir universal list \r\n"); + printf("\tir universal \r\n"); + printf("\tir universal list \r\n"); + // TODO: Do not hardcode universal remote names + printf("\tAvailable universal remotes: tv audio ac\r\n"); } static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { @@ -356,89 +354,31 @@ static void infrared_cli_process_decode(Cli* cli, FuriString* args) { furi_record_close(RECORD_STORAGE); } -static void infrared_cli_process_universal(Cli* cli, FuriString* args) { - enum RemoteTypes Remote; - - FuriString* command; - FuriString* remote; - FuriString* signal; - command = furi_string_alloc(); - remote = furi_string_alloc(); - signal = furi_string_alloc(); - - do { - if(!args_read_string_and_trim(args, command)) { - infrared_cli_print_usage(); - break; - } - - if(furi_string_cmp_str(command, "list") == 0) { - args_read_string_and_trim(args, remote); - if(furi_string_cmp_str(remote, "tv") == 0) { - Remote = TV; - } else if(furi_string_cmp_str(remote, "ac") == 0) { - Remote = AC; - } else { - printf("Invalid remote type.\r\n"); - break; - } - infrared_cli_list_remote_signals(Remote); - break; - } - - if(furi_string_cmp_str(command, "tv") == 0) { - Remote = TV; - } else if(furi_string_cmp_str(command, "ac") == 0) { - Remote = AC; - } else { - printf("Invalid remote type.\r\n"); - break; - } - - args_read_string_and_trim(args, signal); - if(furi_string_empty(signal)) { - printf("Must supply a valid signal for type of remote selected.\r\n"); - break; - } - - infrared_cli_brute_force_signals(cli, Remote, signal); - break; - - } while(false); - - furi_string_free(command); - furi_string_free(remote); - furi_string_free(signal); -} - -static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); - dict_signals_t signals_dict; - FuriString* key; - const char* remote_file = NULL; - bool success = false; - int max = 1; - - switch(remote_type) { - case TV: - remote_file = EXT_PATH("infrared/assets/tv.ir"); - break; - case AC: - remote_file = EXT_PATH("infrared/assets/ac.ir"); - break; - default: - break; +static void infrared_cli_list_remote_signals(FuriString* remote_name) { + if(furi_string_empty(remote_name)) { + printf("Missing remote name.\r\n"); + return; } - dict_signals_init(signals_dict); - key = furi_string_alloc(); + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + FuriString* remote_path = furi_string_alloc_printf( + "%s/%s.ir", EXT_PATH(INFRARED_ASSETS_FOLDER), furi_string_get_cstr(remote_name)); + + do { + if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(remote_path))) { + printf("Invalid remote name.\r\n"); + break; + } + + dict_signals_t signals_dict; + dict_signals_init(signals_dict); + + FuriString* key = furi_string_alloc(); + FuriString* signal_name = furi_string_alloc(); - success = flipper_format_buffered_file_open_existing(ff, remote_file); - if(success) { - FuriString* signal_name; - signal_name = furi_string_alloc(); printf("Valid signals:\r\n"); + int max = 1; while(flipper_format_read_string(ff, "name", signal_name)) { furi_string_set_str(key, furi_string_get_cstr(signal_name)); int* v = dict_signals_get(signals_dict, key); @@ -449,57 +389,57 @@ static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) { dict_signals_set_at(signals_dict, key, 1); } } + dict_signals_it_t it; for(dict_signals_it(it, signals_dict); !dict_signals_end_p(it); dict_signals_next(it)) { const struct dict_signals_pair_s* pair = dict_signals_cref(it); printf("\t%s\r\n", furi_string_get_cstr(pair->key)); } - furi_string_free(signal_name); - } - furi_string_free(key); - dict_signals_clear(signals_dict); + furi_string_free(key); + furi_string_free(signal_name); + dict_signals_clear(signals_dict); + + } while(false); + flipper_format_free(ff); + furi_string_free(remote_path); furi_record_close(RECORD_STORAGE); } static void - infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal) { + infrared_cli_brute_force_signals(Cli* cli, FuriString* remote_name, FuriString* signal_name) { InfraredBruteForce* brute_force = infrared_brute_force_alloc(); - const char* remote_file = NULL; - uint32_t i = 0; - bool success = false; + FuriString* remote_path = furi_string_alloc_printf( + "%s/%s.ir", EXT_PATH(INFRARED_ASSETS_FOLDER), furi_string_get_cstr(remote_name)); - switch(remote_type) { - case TV: - remote_file = EXT_PATH("infrared/assets/tv.ir"); - break; - case AC: - remote_file = EXT_PATH("infrared/assets/ac.ir"); - break; - default: - break; - } + infrared_brute_force_set_db_filename(brute_force, furi_string_get_cstr(remote_path)); + infrared_brute_force_add_record( + brute_force, INFRARED_BRUTE_FORCE_DUMMY_INDEX, furi_string_get_cstr(signal_name)); - infrared_brute_force_set_db_filename(brute_force, remote_file); - infrared_brute_force_add_record(brute_force, i++, furi_string_get_cstr(signal)); - - success = infrared_brute_force_calculate_messages(brute_force); - if(success) { - uint32_t record_count; - uint32_t index = 0; - int records_sent = 0; - bool running = false; - - running = infrared_brute_force_start(brute_force, index, &record_count); - if(record_count <= 0) { - printf("Invalid signal.\n"); - infrared_brute_force_reset(brute_force); - return; + do { + if(furi_string_empty(signal_name)) { + printf("Missing signal name.\r\n"); + break; + } + if(!infrared_brute_force_calculate_messages(brute_force)) { + printf("Invalid remote name.\r\n"); + break; } - printf("Sending %ld codes to the tv.\r\n", record_count); + uint32_t record_count; + bool running = infrared_brute_force_start( + brute_force, INFRARED_BRUTE_FORCE_DUMMY_INDEX, &record_count); + + if(record_count <= 0) { + printf("Invalid signal name.\r\n"); + break; + } + + printf("Sending %ld signal(s)...\r\n", record_count); printf("Press Ctrl-C to stop.\r\n"); + + int records_sent = 0; while(running) { running = infrared_brute_force_send_next(brute_force); @@ -510,14 +450,35 @@ static void } infrared_brute_force_stop(brute_force); - } else { - printf("Invalid signal.\r\n"); - } + } while(false); + furi_string_free(remote_path); infrared_brute_force_reset(brute_force); infrared_brute_force_free(brute_force); } +static void infrared_cli_process_universal(Cli* cli, FuriString* args) { + FuriString* arg1 = furi_string_alloc(); + FuriString* arg2 = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, arg1)) break; + if(!args_read_string_and_trim(args, arg2)) break; + } while(false); + + if(furi_string_empty(arg1)) { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + } else if(furi_string_equal_str(arg1, "list")) { + infrared_cli_list_remote_signals(arg2); + } else { + infrared_cli_brute_force_signals(cli, arg1, arg2); + } + + furi_string_free(arg1); + furi_string_free(arg2); +} + static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) { UNUSED(context); if(furi_hal_infrared_is_busy()) { diff --git a/applications/main/infrared/scenes/infrared_scene_config.h b/applications/main/infrared/scenes/infrared_scene_config.h index 22125fb7..111fd2d3 100644 --- a/applications/main/infrared/scenes/infrared_scene_config.h +++ b/applications/main/infrared/scenes/infrared_scene_config.h @@ -16,6 +16,7 @@ ADD_SCENE(infrared, remote_list, RemoteList) ADD_SCENE(infrared, universal, Universal) ADD_SCENE(infrared, universal_tv, UniversalTV) ADD_SCENE(infrared, universal_ac, UniversalAC) +ADD_SCENE(infrared, universal_audio, UniversalAudio) ADD_SCENE(infrared, debug, Debug) ADD_SCENE(infrared, error_databases, ErrorDatabases) ADD_SCENE(infrared, rpc, Rpc) diff --git a/applications/main/infrared/scenes/infrared_scene_universal.c b/applications/main/infrared/scenes/infrared_scene_universal.c index 2bd7082c..914360d7 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal.c +++ b/applications/main/infrared/scenes/infrared_scene_universal.c @@ -21,6 +21,12 @@ void infrared_scene_universal_on_enter(void* context) { SubmenuIndexUniversalTV, infrared_scene_universal_submenu_callback, context); + submenu_add_item( + submenu, + "Audio Players", + SubmenuIndexUniversalAudio, + infrared_scene_universal_submenu_callback, + context); submenu_add_item( submenu, "Air Conditioners", @@ -45,7 +51,7 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC); consumed = true; } else if(event.event == SubmenuIndexUniversalAudio) { - //TODO Implement Audio universal remote + scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio); consumed = true; } } diff --git a/applications/main/infrared/scenes/infrared_scene_universal_audio.c b/applications/main/infrared/scenes/infrared_scene_universal_audio.c new file mode 100644 index 00000000..00c86fff --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_universal_audio.c @@ -0,0 +1,133 @@ +#include "../infrared_i.h" + +#include "common/infrared_scene_universal_common.h" + +void infrared_scene_universal_audio_on_enter(void* context) { + infrared_scene_universal_common_on_enter(context); + + Infrared* infrared = context; + ButtonPanel* button_panel = infrared->button_panel; + InfraredBruteForce* brute_force = infrared->brute_force; + + infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/audio.ir")); + + button_panel_reserve(button_panel, 2, 4); + uint32_t i = 0; + button_panel_add_item( + button_panel, + i, + 0, + 0, + 3, + 11, + &I_Power_25x27, + &I_Power_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Power"); + button_panel_add_item( + button_panel, + i, + 1, + 0, + 36, + 11, + &I_Mute_25x27, + &I_Mute_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Mute"); + button_panel_add_item( + button_panel, + i, + 0, + 1, + 3, + 41, + &I_Play_25x27, + &I_Play_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Play"); + button_panel_add_item( + button_panel, + i, + 1, + 1, + 36, + 41, + &I_Pause_25x27, + &I_Pause_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Pause"); + button_panel_add_item( + button_panel, + i, + 0, + 2, + 3, + 71, + &I_TrackPrev_25x27, + &I_TrackPrev_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Prev"); + button_panel_add_item( + button_panel, + i, + 1, + 2, + 36, + 71, + &I_TrackNext_25x27, + &I_TrackNext_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Next"); + button_panel_add_item( + button_panel, + i, + 0, + 3, + 3, + 101, + &I_Vol_down_25x27, + &I_Vol_down_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_dn"); + button_panel_add_item( + button_panel, + i, + 1, + 3, + 36, + 101, + &I_Vol_up_25x27, + &I_Vol_up_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_up"); + + button_panel_add_label(button_panel, 1, 8, FontPrimary, "Mus. remote"); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + + infrared_show_loading_popup(infrared, true); + bool success = infrared_brute_force_calculate_messages(brute_force); + infrared_show_loading_popup(infrared, false); + + if(!success) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); + } +} + +bool infrared_scene_universal_audio_on_event(void* context, SceneManagerEvent event) { + return infrared_scene_universal_common_on_event(context, event); +} + +void infrared_scene_universal_audio_on_exit(void* context) { + infrared_scene_universal_common_on_exit(context); +} diff --git a/assets/icons/Infrared/Pause_25x27.png b/assets/icons/Infrared/Pause_25x27.png new file mode 100644 index 0000000000000000000000000000000000000000..a371ba81718e864efe908e61f34bc842079cc20d GIT binary patch literal 3634 zcmaJ@XH-+!7QP75n@AB6Cj_Jk2?@;vLPUm^m4|45Dl3Fj~`j@p=5LtDgz-m;+D zi*bYO(ea_8$@0oFJi_KNGWo+|cFl*3j5wq^^J3T&6GIck>{R&Uct3E>$lOhgxEB-m zYU@+bJ@0o78=rf2pS;(bD__m2?&E6W=1((Kx6=&eFF_wa^f98Nt^Lys#2}1Ujs^&G zS9{3#?Z~nLn<2WoC&5iz&jB|7K|XGv$tt@^?O61l&{uTkS+>yYY)y>hQx@EzqJTkQ zBDxSTFlGzQ$&hyd@;Ct3Zg;n7z*ZG-Rk-$f5D3lL%nV`!TyDvTvE?NLpu@x%Ea0UB zl=q#EGXn5xfT*WM8v^*C3aFVmd71(bp8`$2!hBT#H$R|e7Za%ja0CIaowBn2!1YW( z)Of{7_xF>P!gI>3N@Z5**2*D_!d$pjeYu>RAjcJZ%_L5WY7q_)vJ4;04vMFF5zWZf?`NvwdYW0|5BM2jipO{JG72KGFrCRMiB^(HlehZptOf|6B>&$+XIvrrJmGn%G00AQt_ z+Wc0Ln?2Mk;!_`UZ&`oGB<}S=b<7XZ#<u9;Q7PK&$*CX^8-BqbP9IY7D^H5sZ75dgdTBFI%D=LL12x)PACWxX5 zeJ60|HY+xS@o*S+avvthjKr|H#o=WWxg|0qH)WblIYi>+KwUASc3_KSO;ebC91i4Y zD!qcDA3#K(HLgq6=>*{6+ffZBuv=kOcBr@fPcXH`+DES&-{pJb!GL8YiRWd%p+7!~ zO3=!mdsF5mG?Ju;=}>F>a)e90?UEX#y%qiFlnPIZd-o%7Ie%IE(TAtY+3RE1-TNLf zIYh#Yns~H0m}n5;xS=WD5^w#%v>0?uPUFxBk2Vkcb-NY?a7wYoWBIy6f3zKOgTtcn zrYf@UM3N3eg@a-+ZQ61ou^6~Q?TrIwkM83JLO^7ZlO({tgYFO67h-bY^)6&!M zWu|3zWhM)aT9u1MIfacz_0C&if`%RD3TG8eNJ+g1bLJd|9mb1zi^Q!^$n{D{sds@? zem$1?!l!5{Blr3F&|FJu(L_Mw-1lR_&>h?k<$KY(2|u2*nqQ7{l)v|g?n_Nys;)G& zWAt}B%(+$rOaUR4kpAgaYjfE1(?PsUurYY@|_IvW2@-p%kR$r}_vh1yD z3zZ6WEOjh9dS3V3?Rl4}nT>+IhtFltWxvm!eCm3}|BOmaam_QLS=G#$Lg%HL2A{|? z27O;=6HQ^|+3A2>%VYbZ6r1d^Ks z>FLYL)}@rjl;FDHUw2Sk0@1^QWzuJ)L;N1oMUkhG6Is2tm-K^QuBFXGN%%hDz7Oit zHHW*E+Q>N$*@Gq|2~w?J#A-}@tVMV?BwY`ZE!95W**Ig)Sob~mDR5vtC%ZbkWwChl z!IIVzc`17A&TEZ3O1aMJes5YkF(2(_`}O(mq^fyOmWSA2Y{E!S=47Gn&}65I_Ya>I zFiSTG%MyCu^yqh{^`>>TC*Tv#7hY>OJ?(tjZPQB4y%0HxxhA{ku@|`44-|!-U?Z`| zh8c#r9N5|nyejh|Q6D)<{8lx*Xqb>!Yba)z6kZWN+gu^z)%n|v3Ym?$jNas4vS6fb z$d`9-xCoyK@vR~J3X#!~PEq=av>5!+eptFDvwsZZH;Mg@O~X!PlVCQ82dd_p%6g@c zi@GD)bsBa0?GR7r*F*RmyxAp-V+e?HrIyd7=abuutI<k1_|8Y~4Y$Fq^S;#pTf6gUdm2#dIxe8U%ADr1#WL;6bk?0KcT zoETZP`_d==DfD7Vd7EY?t_|J{y7ZFvuz5%1W(_#ltMxEv?*L@aOqf8mHDO+?( zuMBCF547?QJKy{&y!i>6_X3|I?`&l7!r1%8b2fOW^W+o4_oy5xJ+auhO3_h?bg^q6 z6vzB$rJ|{?USy8ldR$W0R_oE{Ip8_}c|tYUMKG;2{d2mYkHGNAV}Z%jj~Ca!8I3~I zdlX0OBWf_U_g?5eYakYN_4erKRky^|M(sLOT2j86kbd+~ER4LZGNSDrCeszzrIJ3VvCdVst@`O5gJyPGm-(@}rB zKC80!tat5FVB?b@&y9JhTv!$_vWXf}O8T3z82; z+gsX?KkxEahn-(Ly|viP9Aio^-M0#?22R@o`JWrM7mQc5W>C< z#GgWAp#eZw-=9Ws_a-txZbTB<2L)cPe*p%OJy2jLO)E7k8iwdaHVvc`9RjU!?t$L! z2oJEn9!S?8$s(W-83d3&<&uvd(jNu>lNZU7_tj7^=uZ=dHwye$PG3hX^%Dj+a2qK z0(&tSG$a(tWHKR4bqJMCg2E681XK+Ug~L@@7OH*$J`94ts*j)S4+bpJ&z(-DG00RO z&^{xzkA6g&3zbnP68PuOZgTf$c`&s=kw6gmDrWDFQ)_x3o;(y}( zpTvH+02&c$PxPZ+rn|Fx=PA4IiiX6{i3A3fj-yg9{V1fZ7nMQv^PUTRSs~4Q{1^luccK{<1!j>z$Yc*B_LLd|W~`>Ifx(=D!HlqIm^KE1);y&S z(>B)BP)Gd0VyW(zDMTN}53I+3u%~{C-5&@FjpZ3jq?4}@J&fs83h2+Qk>sDpqWM$4 zzp)-ak45XJSSTwP=zeeitJgnNtO42={~2D^;h*s*`mko6&Khp?bb=l0VsP48;*9q8 z_E@50%U}p=Lz!U>asDH2uB_F@!S4zb*aET;o`;H8cy62`NX9&&b(;`5Gx^PY<76Y% m;I8T6+;I4mbsYW#aDX4+76INw)S>uTM1Yx*HMSD%8vbuAhB-h0 literal 0 HcmV?d00001 diff --git a/assets/icons/Infrared/Pause_hvr_25x27.png b/assets/icons/Infrared/Pause_hvr_25x27.png new file mode 100644 index 0000000000000000000000000000000000000000..472d583db0ff9adc5ef810675c8b915c6e5a899c GIT binary patch literal 3623 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 HpN{x9W3^Ap literal 0 HcmV?d00001 diff --git a/assets/icons/Infrared/Play_hvr_25x27.png b/assets/icons/Infrared/Play_hvr_25x27.png new file mode 100644 index 0000000000000000000000000000000000000000..6708dcdbf2c7ac0c656bbb1c2441fb0f4d6fc9c5 GIT binary patch literal 3643 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 SxG~3f0odCdwl1|i74;vAA8iN# literal 0 HcmV?d00001 diff --git a/assets/icons/Infrared/TrackNext_hvr_25x27.png b/assets/icons/Infrared/TrackNext_hvr_25x27.png new file mode 100644 index 0000000000000000000000000000000000000000..a4de4fc3cbe582349b8c79bbb7ec6e931e8bc792 GIT binary patch literal 3639 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 W@<`O{ko&lf02|BW7A0mL7ybo9V{1nM literal 0 HcmV?d00001 diff --git a/assets/icons/Infrared/TrackPrev_hvr_25x27.png b/assets/icons/Infrared/TrackPrev_hvr_25x27.png new file mode 100644 index 0000000000000000000000000000000000000000..838055341e568fed60b3e28a280fe839d2c9db9e GIT binary patch literal 3644 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 in order to keep the library organised. + +When done, open a pull request containing the changed file. + ## Air Conditioners ### Recording signals Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote. @@ -31,7 +45,7 @@ Finally, record the `Off` signal: 4. Save the resulting signal under the name `Off`. The resulting remote file should now contain 6 signals. Any of them can be omitted, but that will mean that this functionality will not be used. -Test the file against the actual device. Every signal must do what it's supposed to. +Test the file against the actual device. Make sure that every signal does what it's supposed to. If everything checks out, append these signals **to the end** of the [A/C universal remote file](/assets/resources/infrared/assets/ac.ir). From 85d341104f32b509c70ed8808655697d25963df3 Mon Sep 17 00:00:00 2001 From: Vladimir <74153654+touchscan@users.noreply.github.com> Date: Fri, 28 Oct 2022 19:50:07 +0300 Subject: [PATCH 41/42] Update ac.ir (#1945) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added conditioner Saturn CS-TL09CHR ir signals (Dh, Cool_hi, Cool_lo, Heat_hi, Heat_lo, Off) Co-authored-by: あく --- assets/resources/infrared/assets/ac.ir | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 97c38459..7ef95305 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -111,4 +111,41 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 9106 4398 731 499 706 500 705 502 702 504 701 505 701 505 701 1606 701 505 701 1607 701 505 701 506 700 1607 700 506 700 506 700 505 700 505 701 506 700 506 700 506 699 506 700 506 700 1607 700 506 700 506 700 506 700 505 701 506 700 506 700 1608 699 506 700 1608 699 506 700 506 700 1608 700 506 700 19941 701 1606 700 505 701 505 701 506 700 505 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 701 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 699 506 700 506 700 1608 700 1607 700 506 700 506 700 +# +# Model: Saturn CS-TL09CHR +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3014 1708 488 1059 464 1085 461 362 461 363 460 387 436 1085 462 362 461 402 436 1084 463 1083 463 364 459 1083 463 363 460 386 437 1082 518 1044 464 386 437 1108 439 1108 438 386 464 360 463 1082 464 360 490 352 459 1084 462 362 460 363 460 363 460 364 459 364 459 364 459 380 459 364 459 364 459 364 460 364 459 364 459 364 459 364 459 380 459 364 459 365 458 1088 459 364 459 365 458 1088 458 365 458 380 459 365 458 1088 459 365 458 364 459 365 459 365 458 365 458 380 458 1088 459 1088 459 1088 458 365 458 365 458 365 458 365 458 381 458 365 458 365 458 365 458 365 458 365 459 365 458 365 458 381 458 366 457 366 457 366 457 366 457 366 458 365 458 366 458 381 457 366 457 366 457 366 457 366 457 366 457 366 457 366 458 382 457 366 457 366 457 367 456 367 457 367 456 367 456 390 433 406 433 367 456 367 456 390 433 391 433 390 433 390 433 390 433 406 433 391 432 1114 432 391 432 391 432 391 432 391 432 1114 433 396 433 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3011 1710 462 1084 462 1085 486 356 467 356 444 364 484 1060 462 364 483 357 458 1085 461 1085 461 388 435 1085 461 388 435 388 435 1084 462 1099 463 387 436 1084 462 1085 461 388 461 362 461 1084 462 362 461 378 460 1087 459 365 458 365 458 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 367 456 367 456 367 456 367 456 382 457 367 456 367 456 1090 457 367 456 367 456 1090 457 367 456 382 457 1090 456 1090 457 367 456 367 456 367 456 367 456 367 456 382 457 1091 456 1091 456 1091 456 1090 456 367 456 367 456 367 457 382 457 367 456 367 456 367 456 367 456 368 456 367 456 368 455 383 456 367 456 367 456 368 455 368 455 368 455 368 456 368 455 383 456 368 455 368 455 368 455 368 455 368 455 368 455 368 455 383 456 368 455 368 455 368 455 368 455 368 455 368 455 368 455 384 455 368 455 368 455 368 455 368 455 368 455 368 455 368 455 383 456 1091 456 1091 456 368 456 1091 455 368 455 368 455 1091 456 373 456 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3012 1709 462 1083 463 1084 463 361 462 362 484 355 469 1059 463 387 436 402 437 1083 464 1083 464 362 462 1080 520 354 469 354 469 1026 521 1068 518 354 390 1108 439 1108 438 386 463 360 464 1081 465 359 464 375 463 1083 463 361 462 362 461 363 460 363 460 364 459 364 459 380 459 364 459 364 459 365 458 365 458 365 458 365 458 365 458 380 459 365 458 365 458 1089 458 365 459 365 458 1089 458 366 457 382 456 1090 457 1113 433 390 433 390 433 390 433 390 433 390 433 405 434 390 433 390 433 390 433 1113 434 390 433 390 433 390 433 405 434 390 433 390 433 390 434 390 433 390 433 390 433 390 433 405 434 390 433 390 433 390 433 390 433 390 433 390 433 390 434 405 433 390 433 390 433 390 433 390 433 390 433 390 433 390 433 406 433 390 433 390 433 390 433 390 433 390 433 391 432 391 432 406 433 390 433 391 432 391 432 391 433 390 433 390 433 391 432 406 433 391 432 391 432 1114 433 391 432 391 432 391 432 1114 433 396 433 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3011 1712 461 1087 459 1088 459 364 459 389 434 366 458 1086 461 388 435 404 435 1086 460 1085 461 388 435 1085 461 365 458 388 435 1084 462 1098 464 387 436 1084 462 1084 462 388 461 362 461 1084 462 362 461 378 460 1086 460 364 459 365 458 365 458 365 458 366 457 365 458 381 458 366 457 366 458 366 457 366 457 366 457 366 457 365 458 381 458 366 458 366 457 1089 458 366 457 366 457 1089 458 366 457 381 458 1089 458 366 457 366 457 366 457 366 458 366 457 366 457 381 458 366 457 366 457 366 457 366 457 366 457 366 457 366 457 381 458 366 457 366 457 366 458 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 366 457 366 457 366 457 366 457 382 457 366 457 366 457 367 456 367 456 367 457 366 457 366 457 382 457 367 457 366 457 367 457 366 457 367 457 366 457 366 457 382 457 367 456 367 456 367 456 367 456 367 456 367 456 367 456 382 457 367 456 1090 457 367 456 1091 456 1090 457 1090 456 367 456 373 456 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3045 1677 493 1023 524 1024 522 354 469 354 469 354 469 1026 576 353 470 350 487 1001 492 1054 492 354 469 1054 492 355 468 354 469 1054 492 1070 491 354 468 1056 438 1109 438 386 437 385 438 1107 439 385 438 400 464 1081 465 359 464 360 463 361 462 362 461 388 435 388 435 404 434 389 434 389 434 389 434 389 434 389 434 389 434 389 434 405 434 389 434 389 434 1112 434 389 434 389 434 1113 434 389 434 405 434 1112 435 389 434 366 458 365 458 365 458 365 458 365 458 381 458 365 458 366 457 365 458 1089 457 366 457 366 457 366 457 382 456 367 433 390 433 391 432 391 432 391 432 391 432 391 432 406 433 391 432 391 432 390 433 391 432 391 432 391 432 391 432 406 433 391 432 391 433 391 432 391 432 391 432 391 432 391 432 407 432 391 432 391 432 391 432 392 431 391 433 391 432 391 432 430 409 393 430 392 431 392 431 392 432 391 457 367 432 392 431 408 431 392 431 1138 433 391 408 392 431 392 456 367 456 1113 408 421 433 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3013 1709 463 1085 462 1084 463 387 461 355 469 355 444 1084 463 387 461 378 436 1084 462 1084 487 355 469 1059 463 387 460 355 444 1083 463 1098 464 386 437 1083 463 1083 489 361 463 360 463 1082 464 361 462 376 462 1085 461 363 460 364 459 364 459 365 458 365 459 364 459 380 459 365 458 365 458 365 458 365 458 365 458 365 458 365 459 380 458 365 459 365 458 365 459 365 458 365 458 1089 458 365 458 380 459 1088 459 365 458 365 458 365 458 365 459 365 458 365 458 381 458 365 458 365 458 365 458 1089 458 365 458 365 458 365 458 381 458 365 458 365 458 365 458 365 458 365 458 365 458 365 458 381 457 366 457 366 457 366 457 366 457 366 458 365 458 366 457 381 458 366 458 366 457 366 457 366 457 366 457 366 457 366 457 381 458 366 457 366 457 366 457 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 366 457 366 457 367 457 366 457 382 457 367 456 1090 457 1090 456 1090 457 1090 457 1090 456 367 457 372 457 From 104a1998a5e2f77cea99c31311541864fb98d4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 1 Nov 2022 19:27:25 +0900 Subject: [PATCH 42/42] Furi: raise bkpt only if debug session initiated, add debug support for release builds (#1957) * Fix hard crash on some custom firmwares in RELEASE mode * Furi: anya wa erai, anya wa eleganto, raise bkpt only if debug session initiated, add debug support for release builds Co-authored-by: DerSkythe --- furi/core/check.c | 59 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/furi/core/check.c b/furi/core/check.c index 00c20575..1c2a005f 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -1,6 +1,7 @@ #include "check.h" #include "common_defines.h" +#include #include #include #include @@ -24,16 +25,30 @@ PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0}; : \ : "memory"); -// Restore registers and halt MCU -#define RESTORE_REGISTERS_AND_HALT_MCU() \ - asm volatile("ldr r12, =__furi_check_registers \n" \ - "ldm r12, {r0-r11} \n" \ +/** Restore registers and halt MCU + * + * - Always use it with GET_MESSAGE_AND_STORE_REGISTERS + * - If debugger is(was) connected this routine will raise bkpt + * - If debugger is not connected then endless loop + * + */ +#define RESTORE_REGISTERS_AND_HALT_MCU(debug) \ + register const bool r0 asm("r0") = debug; \ + asm volatile("cbnz r0, with_debugger%= \n" \ + "ldr r12, =__furi_check_registers\n" \ + "ldm r12, {r0-r11} \n" \ "loop%=: \n" \ - "bkpt 0x00 \n" \ "wfi \n" \ - "b loop%= \n" \ - : \ + "b loop%= \n" \ + "with_debugger%=: \n" \ + "ldr r12, =__furi_check_registers\n" \ + "ldm r12, {r0-r11} \n" \ + "debug_loop%=: \n" \ + "bkpt 0x00 \n" \ + "wfi \n" \ + "b debug_loop%= \n" \ : \ + : "r"(r0) \ : "memory"); extern size_t xPortGetTotalHeapSize(void); @@ -96,16 +111,19 @@ FURI_NORETURN void __furi_crash() { } __furi_print_heap_info(); -#ifdef FURI_DEBUG - furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); - furi_hal_console_puts("\033[0m\r\n"); - RESTORE_REGISTERS_AND_HALT_MCU(); -#else - furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); - furi_hal_console_puts("\r\nRebooting system.\r\n"); - furi_hal_console_puts("\033[0m\r\n"); - furi_hal_power_reset(); -#endif + // Check if debug enabled by DAP + // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en + bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk; + if(debug) { + furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); + furi_hal_console_puts("\033[0m\r\n"); + RESTORE_REGISTERS_AND_HALT_MCU(debug); + } else { + furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); + furi_hal_console_puts("\r\nRebooting system.\r\n"); + furi_hal_console_puts("\033[0m\r\n"); + furi_hal_power_reset(); + } __builtin_unreachable(); } @@ -124,6 +142,11 @@ FURI_NORETURN void __furi_halt() { furi_hal_console_puts(__furi_check_message); furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n"); furi_hal_console_puts("\033[0m\r\n"); - RESTORE_REGISTERS_AND_HALT_MCU(); + + // Check if debug enabled by DAP + // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en + bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk; + RESTORE_REGISTERS_AND_HALT_MCU(debug); + __builtin_unreachable(); }