From 8ec5527ae4a486608e4003b51e21b41045daf7fd Mon Sep 17 00:00:00 2001 From: hedger Date: Sun, 16 Oct 2022 21:11:27 +0400 Subject: [PATCH 01/30] 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 34fb942ab..9f81d4145 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/30] 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 c9e2371f4..33e289948 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/30] 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 f149508b0..8009f6a17 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 a842ed921..690bba61b 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 06d57a0c3..740cfae5e 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/30] [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 a2e6ae745..54cc18d32 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/30] 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 5ec57c757..54e3e2515 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/30] 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 d39f30333..b61ac0329 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/30] [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 54e3e2515..7723dc973 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 86b19114c..c03f180f6 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 c6617e501..26919c4f5 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/30] [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 7c037bd12..3fc61bf60 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/30] 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 000000000..455eb23c1 --- /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 000000000..e5ec8dfcb --- /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 c175f3986..34e987060 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 ed6bb4781..26f215941 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 532effd9a..2c9b1a0ce 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 9b215fb74..101d4d8f2 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 37f1db4f2..bb170ac45 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/30] 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 fa91e8f79..fff35c95a 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 653a306aa..c114d79a2 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 6e0bce736..a5caceafa 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 acd46ee4b..a9b89d63b 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 78c7769c5..db2a58993 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/30] 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 d28309d0e..5219a2f3b 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 5795e242b..2285a6f2c 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 33e289948..d3d5a9010 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 9f81d4145..e40a4efc2 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/30] [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 210d3770f..2dbf2eeda 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 e5193521d..f403c1f38 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 023fb4b6f..bd6451f24 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 2e833edd1..03a8ebbcb 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 1e7a7474b..44fe2fc76 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 71c6bc2cd..ba70f36d1 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 a1474885e..ad5d8afb0 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 0f67b4fc9..beecc6e8b 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 7bff3df5e..5b2b57d33 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 58d307170..284f7ccf5 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 cdebc6320..7aa699222 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 000000000..1074fc040 --- /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 000000000..b0486183d --- /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 000000000..a6905e828 --- /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 zcmaJ@XH-*Zw>}8cn@SN8Cjdgpu}znQ`v@@y$K!ob$f#Ui*3Wv&&iQ#GbUX6cauq3;=+b zwH4Npy9#h0NkLxjd;WZKJpc%sQ!$v6)))+k!K8apFOmTuV2I;H!8^^$pw`A#&^9rl zcWmg6(t;pIbX=%ZqKdkrkmQLN#ruQO4t4v?&H3b8vSWDT<3n#sJ7|dB5FQYiQhX2} z@i68_+s5bMhd%w)YhOCHUwky4DO%=~bqUl8il$iUIOv6n=A)17`xMdK*z|b{Vj3o_ z%-{+uBPsfCDe(a7AxPwLaIL^=fG40=L=dROW!7pPj^2^@hE6}j6MCJemX&B|BN!?L zm?Bjj-oVAb|L^eK#suz z-bO%C*Qp!k06`0o^0H}!0|T0XmbHtQ74Y;WP}?afQVIx)0$L6+k;eeOV8FdaNhtuh zo(@P^EV&?mKVBj^qt2~VdMUC(8EzitCaCEr;Nk)~qSk3Gdt6GNxQCcw3aJlFm(vc@ zmH4#$4gj(frMcNIZv}LUmvnaO$Crzr*ZlT|e+TU0F}Xe6Rmd;}fX}Ru?rjZd*`ZJ) z{!rTXgQE+4-seQJFRjISl}ebt0J3L?T$UNTwK2bct733)dTMImL?hab*yeI|n^J$i z)@AGBA0f!iwbf6rCzQjq&xTp@t$(V2w_=-fxa+pib&ruR36`5LMRqn7dclp>9u)+2 zsY!?Ze(~6ho6Fic;8^tSV{ec4?2snLH8yyS$Mt}x7mRs=6E*YBdh&j^QI#aHYA4nJ zV5y2;_d!jNH`F`ga~FGO(PYaq`zR3VWqsQZ0M22RA^5g3lV(8xz-EW3KQ)tIsXM4q z%YV3T??|1OR5Cya9)T+aT_{>@a4-gfHVt71m5R~EtWz!?q73-|{_QxrMT4SUfz&43`RxrmK zc#yM|!V-$P2OfRKqB7B_1<(%PjHfvLzdICS0OfyjFj3zm@}lb!jV z`TP*-rvCkz_l4dPLkY&1X06(<2L*H*FKR)W8qm)SHH4Bp+n<4pL<^e^Jv~*#TNS(N z+4YRgw?E9hR!EEJJhR!lk#kyt5oj$qw%1J zHY}Q8rJ>ZnKj8pWGB^g)XrR157Nf0NachtDvq$)z{XG^vzK%+>8u^*JR)>_5T8BtJ zr2_Cf8ldAXkyD(hhAEvX`6XWam%6+5BN9iCI7pFWAAFK#`&h0wPOcfRWdNH?n@N{Qr#lnW%hj() zC$O2AxK8g>z+aD8yt&)~AGK#PXEHx#j=yw29dKHsJg@u}*}8P<^kdhB z@@n76({R@ug7fLKWfsMp;-mdl#Z|fcax3hT>aSZU0kP;o@j`{u3L*Z_nNo;Th_Q^$y9*{)->#(0LMenU z$*uvN$?^m3#~P^|r_5eUiY%qVKVms1F4iWz9g=Dc$&_yzZK;_$!CLh@`#Gp*m6KVP zSwEjQ{A59Yfw~Yqa_^n)y<=IfI{xn)S}>m+rn^lvaj2DI2W9-8yFJ_dWp3p>> z;*U>X=CBLah>Nnu-;J5~CXFYN24mV|uIJww)V^$a*>2xJ&pIDDj=83^L)r=2=>~E` zkMdA>W5dkC-1cm&2VGHo6K{eTCVwv-oHx6fU126|mJnVXK3!L==-u+$tzyNsnY7Nt zPO5n1$&j!8?*)ioh;a=eqN9~J9=m%4<3Eo5fla}VWl~`F@F$ul z^wf&%CQT48*LQsc4#E1O&0#o1y+q&l;_LCv`Q_*d&V{iiUS54t^^y9Di(`p~p1xhJo7q2%Rv2E~_!mQ&R z^Y6;qhHn|%UA(t5zrTL}=iB8uQ8q4`3WP5;MHk?uNWZ{g;YsPe$D>a17a?EWC|9TT z*%!{cq?Ux#s087B!p_yTh1b2{@tG5G7M_m0Iydrh{;WL#>N@^{_#=uVZ!8^qqeN<0 zHdXrCfZ9mFw0tzZ?M?c~o#*+5jTNLWuO6@2FJqcnZsI8gsb5mXeZ>Zco{Np2dOpAU z-Fz6D+MzaF6;Y0u- z_1czk>+4}>9%o#iS08!9dZTR3q$IXrc0FZ-cDC4#<~QHW+rzshpd?=YvEoCYLJtb> zn9zTG&QiSjm)F~zMYg7xzL@i`cbg`Z7}&t6*)^f@wIgDPq02Xei#`kV{&HD?q5!>s z&REK@$aKosaPx4hw0~#Z-T!SYXw!1|7m2&NNY}s<%lKC6&}?{b5@o6DCMTJ5H3ag< zi2Lw^^57ZI&hZNp^uEA}P?Xm5c-cUNtJ7z#`ym5uS7! zgt+Si37|2!XaGQ(1Tcu6K4ccigG{0NqQHw)Z@?fb2?ci1!)f6d7_v9jDu_vT3bMm{ z2KjixNnjIW5HbM4C7_X6L{I?jqOU(900sV&7s1`{nxSCOpDrvP6!gZ5R~By$v*B1_*5(1Pl)P`vP+VGD%(tN36x) z;kYLh*qg;-AfQk-n+;*>Kg6b6UGp<3EdZEa1iho*m^FN+wU>FclblL1Ti_heET zEGpd>w982JpkHF4z+AC^WkF;7L+k7Rccr*Bg9Z>8P#8pOH>;nHINbm5N~8Vb?ay)~ z|F7QvO6-phWRRhbWPkc4rYE;|UP`;67zhlLOk~lScsl*!&qAK`rnBh&-gE{?TL%II zso{v8RNr08u|FX=9KzbypGEZbBwJ%qU@i%SN+lt5bak=1X1azt=6YH%m^s!EgE7G9 zp!Kj=Lo`Moj{b?o(mgNH$iA$fSP~ZtH?Yu!85;gOc6T6X3~ppBnMu7&CRs4)G|-<} zBdGtbMgQNm{Dmd`7cGD1Veqe5C|3-0x3~YN*FR0%0ovXEGrZixKjTmK<<2~lJKX2I z2l}`l2LDN0y!p<~4tMKZ-y6bRRIIUP_<$h~cXxLZi3IGiF!aLa%K61iCPfSCc)Vu> dgNA@TqJWq&V4(J>lEOs**5-ED6102xe*n97GqeBz 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 000000000..1492374c9 --- /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 000000000..fd9536e34 --- /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 000000000..525b55575 --- /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 000000000..2792ab987 --- /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 000000000..e40f33485 --- /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 000000000..5450f0040 --- /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 000000000..ea02ec058 --- /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 000000000..eac28a23a --- /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 000000000..9a2eacb2f --- /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 000000000..1feae58d3 --- /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 000000000..5740750a2 --- /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 000000000..e0402b818 --- /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 000000000..670c8c386 --- /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 000000000..f9306e5f4 --- /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 000000000..8cee4ee60 --- /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 000000000..df784ec96 --- /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 000000000..0ba8ec013 --- /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 000000000..fcd8f6d3e --- /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 000000000..b26661be3 --- /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 000000000..56dd6fa86 --- /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 000000000..d30b7926b --- /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 000000000..30c6516d5 --- /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 000000000..318706276 --- /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 000000000..705434a23 --- /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 000000000..052bb8533 --- /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 000000000..41e248112 --- /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 000000000..2eddf6cdc --- /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 000000000..11601fe79 --- /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 f013afa2d..0d43c5591 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 1f37a2eb5..a0cbb9a07 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 3317fd4bc..11684803d 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 d3d5a9010..b65de9a94 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 eff2f168e..e25d122c8 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 57b47d503..f32334e2f 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 339e27c15..25549fab3 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 6ad734cbd..1ff077726 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 7496aea3d..1bad5f0a3 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 300807daa..1448f0ea6 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 fca50c8f8..782076e3e 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 85b146ebc..c312b607a 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 46d0d368c..0a4b7b606 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 d4678e413..5f8fcf1f5 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 4ee7a3f8a..36f33b9a5 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 47b4e482b..1f3d3e1be 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 605a922c6..2dd39af9e 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 04bf46b56..c0ce0b7f4 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 3fc61bf60..1ac4ec053 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 d1ac76286..253c93aae 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 8c2a542c2..3f6045bea 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 aa1cffd05..3781a0d8c 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 3d5029ec9..e7eb33c42 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 aa1f0e0db..359b964da 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 d2d19af25..3650a9867 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 923a3151b..f87b64d90 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 dbee0ac94..a2cb7a18b 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 8287af234..8858c1a3b 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 5c3402144..6b31d4f27 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 c96946a13..30f1fffd0 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 b54435902..ad1867383 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 9b18f5eab..52c265d20 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 5cf3a8714..4c7c2d484 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 36eeb9a99..4bfba3597 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 230f4cfef..39e27bbf8 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 1dac783b1..252a26dc7 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 3cd62698d..3b940fc67 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 c61aa822f..828631837 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 d8438604f..cb6adaf62 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 743ad0b38..857a50041 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 6cc60bde8..31ff20e42 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 67d2b4d8f..634f6ff89 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 14e137cab..2c4e514cc 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 078d03978..ffee14b04 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 039d97a6a..ae6588e7a 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 c0575bd96..7b1aaee23 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 d46838661..997f8e1de 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 cf1869219..a9afcf149 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 582c4aaf3..2fc8b20c8 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 6111ad85c..923337ac2 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 160ec4a2c..714975254 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 000000000..a179c9cb4 --- /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 226919c8d..000000000 --- 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 a72238a41..d557c29b0 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 5fc042e77..e330ccf16 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 baa8188d3..1b871a0c6 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 0fafddb6b..e31434fa3 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 d6925d485..5fffaa19d 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 ef270342c..361da6173 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 8080bf9e4..b124b717b 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 4536e7043..ac87fb00a 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 06538db10..ecd866a72 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 dd374006b..e382e6146 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 d567ddf2c..411ceeacf 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 286d03d44..593a79157 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 561df819f..244ac58d8 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 019c3ec25..b3d6f1e98 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 b2f182796..48487535e 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 3ec35c74f..63ca78711 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 21e5dcf4a..806729f8e 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 370f33fb0..a4a0921ac 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 4e482f747..f63004db4 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 453fa747a..65ba03280 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 8eb464ada..9c187e795 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 fd234502a..b8e93c3d5 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 be03dea27..96c77553a 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 837dcf058..a9a3078e4 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 f0fe595b4..b7e84ea1f 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 cfe6bdee5..7bd0b5b79 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 ba1762f55..99480b62b 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 73bb7802b..90cc805a3 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 007609fc1..bea8cdb5d 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 cf627ef37..00bb8a8cb 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 af1df91d5..3b5950611 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 6e375bb38..362b1b07c 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 b6e58e34c..a6a9fa5b2 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 4b89bd2bf..8e5e07c1f 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 74c994101..34c251504 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 cf4085df4..fd6f1493e 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 000000000..d0c22ea8c --- /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 000000000..062cdf68f --- /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 e0322f6a9..c5ec5db75 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 0590cf499..cd82b302b 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 0dc2bacde..8507ee054 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 dd9cefb8b..6c34dc728 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/30] 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 7723dc973..5a04f7495 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 b65de9a94..6aa8591e7 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 01b26cce1..0523d3ba0 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 3fe57162f..2082e550f 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/30] 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 ba7644981..308d60fdc 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 000000000..aead0a60a --- /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 000000000..3b99d5ef3 --- /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 000000000..88b90bd34 --- /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 000000000..58d032b91 --- /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 000000000..d51726c45 --- /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 000000000..4dd986153 --- /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 000000000..3d8e6bdf9 --- /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 000000000..8b127c9d4 --- /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 000000000..59411e78c --- /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 000000000..37e235540 --- /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 000000000..6fb38da4a --- /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 000000000..8957aca06 --- /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 000000000..0974e60a7 --- /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 000000000..56b06411c --- /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 000000000..7193f4f4b --- /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 000000000..8c19bd6a5 --- /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 000000000..c5c8f9dff --- /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 000000000..1fd900452 --- /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 000000000..e7752beb5 --- /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 000000000..0c303a3ba --- /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 000000000..2a0e86056 --- /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 000000000..9c3a172dc --- /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/30] [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 6aa8591e7..89f447157 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 c9c8400a7..c2abd1b85 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 36f5230c2..6c17d6ade 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 13035161d..91596ffe3 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/30] [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 a6905e828..275d23329 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 000000000..a92ec243c --- /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 000000000..5bab3bcb7 --- /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 525b55575..b08a4e9db 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 000000000..828d49be7 --- /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 000000000..941b01058 --- /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 e40f33485..a2ab0f412 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 ea02ec058..5b753994e 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 eac28a23a..c4f4ecd9a 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 5740750a2..174531090 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 e0402b818..b2a84df8e 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 318706276..34ec122d1 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 2eddf6cdc..b37009c46 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 89f447157..d0dfcbfad 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 782076e3e..588d4effc 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 c312b607a..275889484 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/30] [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 0535b32b8..8c5ed91a9 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 zcmaJ^c|25m8$TlZmMlrSW2}i8Gqz^TSZ2n)g^`gKgE1zJ8DnZJB}?`$N%n|Jb`_Zgm>I~yYKtQ+j~CeoZs_%KF|02tiR9aoS2g~rb7IO`2heBGB?B7 zvsXU$!^_Lbejhog7zF_Q#uO~}q&XG~qBCfol#3()@E^`{Ambh8CD9w>YZ%MuklU6t zdkJ2UJti(hJW)wij!)DgS}u1;!mi+4$78^X$F;T*+!yMGsFsuV9Lqrk!?(U?Jw{fsf&>%`KBN5W`S@1v((g zTj3$+w=K^BYzCuH$HDbHuK*7JegR&f-a4~h<4Dx5$QMld8IF+a9QDk^6PCOo;(%db zJmw)_Xu=Vam8RqXzBL5`|)n@b%R*CFmBW*9;m1Jb48p;{sz zSKk0YPND=m+tqFVZsD@-Zk_l_;q&TW>bQ5oZf?^%vpvOhLjag$2KVl$K`Rc2=y8Wo zckU@Jae18+$hou;=UFad_zcLIA9h-$@72aQ_h!}5(Cp022a9Hm3$erL>JGW_e7)25 z#Xo{k-#6AjzM4`9g`W*I<=Xt%_-4&2??}%rzUywIpAt;JKNr~jF53@oHg~HcsESY9 ziTTFkGH$HG^T08fTgP52_U(F1O{i^g>Q86`k1yzR1SP5$oOS2?YN;$rVAfA<0KiH$ zt>K*tFK38X_+$@jciCXuG)EB#@if2X3jpVE3J`opg+VJX0N`>$RUaG4tTh}`tLHw_ zuz$2c;Jd5Nb&NuDBSsX%?-6o@;d-nj45Jd+^;lNb75dIlR;%95D>8{L3~6+HA&jPr zd?WH^H>lKv@^Klp@g5|~4M%gh#S-M>d8N`LHsu=3xWwWVK<&}uc3{gyZ8MmCEFR=& zCcA=W>_bGIF?pG&*9O4DzDL%W!fuC_+o9hHKEqZO=pMdqa!=r`2NRZ&Aeoc-mhtp3 z`i4&K+}mO=k>Lb=Y^z57=R-W2%@;KwElrpwC)D_o+&iCuO9YEs4nICs!dV@&?%wl+ z#U&PMT+gS&#lnDyM~%z~Rs@?5W(A-l3R(w-`E>a>uiG^$gOds@pDMjP@JI6@H#jVc zV5TNND3)*#DjF1xZPrx~i^0N`t8VTyfARp|C)Q`u=VLEFuU43;Wp-FTseRbPyPOAc zTMcx)En+5XrfEiVMrKBC#f3l1&CvJ3ro)tqea8h0=~>ZPtyykT8r;b7eun*6K`#D) zcEV{)X>O(cBfF@AgIbp|5MAMt=9YP-_mcY$dr5h1P#%3|zC19NeAFiOty5p;yy@rE z&zx&4L7_;A@YZOuige~7oX@{V#lJalM4L z0S#~PAlP|3hTFNxs>v4nz%J*>`RSS#kbt{%g}<7dw@`89rBLl=r6*lOS1zkor)|qt z$DxY~a}iig3r(jA`_u0pr*+thFQ>!yMP?g~K#?h(Q6L#Wr$>oXdQlIq}@}wEtx880Lo#Jr#@cFUEST%QXD0*u*0VAzG8-2Q zMjkEcEK!!CM(@7Sx_d0!S<~-rQycc9-E6Ocz};jG?}?INTdZv;`PrO2)E%1WRQmq$ z^E_73260(R|EoUZ?zH|iN_QMS?tK1@ZvFGN=bg4qwCwW{6WD9=yB@oNyS+euh$07> zGbYq1)Mek+X5baMx_ATR21u-S%EIj^?gZkEbz%%ycFc2k5S zP6mG-e9J@pM2u?+7F3Riig1cFh^I#r4)?-RwOPHRSicF}H(UyCJd+HwMLbfs&{owi zf?Pli>%P=_Y0v`kbbd2H$Re0uv^;`QS2&Gga%r zTfZgNXa^{~*346zt-7vUc(cYz$Z9MTnJt-d8AOGnk+rb!TZuP)F-3CLNtNU;Qqk4K*3E$Uhr#bz$`V;#pe))Oq3=@mpk;jJ`xnY?=6oRI0?a z4=SVnvocY%j=J>G+fNINo2xu}Jo`N7KaHzry9lQgrG82k_7NHyekwF^>gnS8SK|?A zYM07Lb$BJV>V6&SMGYyxy}L`#0RI5LhX01wS?U{mMtr~N)4L=SRP$Bqw}BCtnvHG! z_E#g09FEolo&%&U^R0>vgR+>S`OTCq>e*5os_$YeXCLP_kGyc@`>J;XvVCa0eZt`J z1ykYHUtaBGEwj{xbc7s#z0)!!Psat!%x~~bY#bFr4qv_zR5Hoa|I1}rvMlrhCSxVT zB-0^d%f-#*rR^L2-oY>9f!|F>ei6B&g>nwCSjD$fhUdfjlgKMQH?oqmt_DN?7-epwkPdj7P}x)Gy30sGX#K+t%tk z)fr_~XS}PH0&AZId2YS@tuwzjJ1};0JAC^b2U8rZ}toDwYZg5A0_v|FDCx~G8C!{BIMhZnP zWS`JSAf^l$+wnH62UxqL>9TNDhHEc=teW zcZ3JnKp%wiN3sd1BqkB$Prc~lhxA8-|Kvro5T^e6%@hxBnV4mkU+W+ zn7X@$h6YF%0U>!1;cl9qM0Yh1Tmue+!q~U2I!qS{*F?e)puaCL+abfl6KRh#`P&_P zhX#8wnRFx+%3`q~EKLZFL59K*2n19E4u!+j*%s=40X|Hkzq*f~{0{~k$;?=ZcQRGDt)wje)1pF8(OwNiQ0c=I2GDgW#GF7)ZsM z=uYw3(;WK~Vr`8y_wi#AecVarI5e0|0-;bmkjA=3C$!6htR?xFuEr+ zG+|g{BV7&H4=j%6eu+x*VgA5+{0D3NQ|#VAQ0Z*XI1+<$ndD)@pix18W{sr$JQmDP z`ToXw{5%%yPq9#TFwni;{#UPmsMrIvC;l_M?9D&pPx4{UJcB*lq`LYg_QBvjX@xi5 z-Q8u2j*b1n>_y2OXN313M#UNlv;XSCW_Is(C%44F6vFr6DNOS#(`|Jo(j43X7Y0!L 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 d0dfcbfad..188cd7484 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 613b52f46..ed38038a5 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 30efdf010..e77891f70 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/30] 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 2dbf2eeda..dbe03d88c 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 244ac58d8..c4e4471a0 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 000000000..cfe938e6d --- /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 5b753994e..0740691a2 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 c4f4ecd9a..edc078cd6 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 549a60db3..000000000 --- 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 7d342bb93..928838d3c 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 981b25999..000000000 --- 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 65ba03280..ebf9b93e4 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 9c187e795..23aa71be0 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/30] 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 7193f4f4b..d8d70e7ff 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 06c05173c..73eaadfb1 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 188cd7484..4c48d9abd 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 ed38038a5..00c20575a 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 e77891f70..78efc1451 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/30] 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 e3d5fc132..f28fad20d 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 308d60fdc..a97e0933a 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 db08320c9..d1cac63e9 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 f4df96592..c8b0c601d 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 d84707e07..19a03b69d 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 74fa5667b..448df9715 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 9bc0bdc0c..269577047 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 e1bf546cc..63141829e 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 c66998e7d..000000000 --- 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 1b7d13dd8..000000000 --- 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 c21027e49..000000000 --- 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 zcmV-00?+*c04xFkYy=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)fU*-=nfPl;K(|3%2;13wkEVuyk_>;uyHUIk9NCp5O z=ipu-@_;&s_8>C8PXX!wV-5M7KmosB0qMYmU@$NcdNc$catJ8!U_&$f|DbDAoxwqa z1P7RGVC_?qK;aMwKnPGn_XmhnpcBX#3}OKDiTnZTswn_GxDlL$7#?y!c*7_JgN|$f yq((pd{{Sg21b?6kAON-q4wL@`ygXn6m;i)QQV9dpKnvgxqV6=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|d8T2jP7Oi+q9S7zd=~{($os2n@d+mw3ni0P&3i%YY9*!8}f*U;nLyfM5as ze+A+XC&1OC5*(}4&^j2IrD@%l(0sL_NB&-4F*txk6Z9t=Pp zVX=d?PDuk641i`RA^U^GDo_dJ3`jupiTnZTsw@CJxDlL$7#?y!c+4OWQ5i=z1mYtY u08SzwDE~kdV6kin9Vh+?czD1BE*Jo$q!I_HfEU0YNdEx5fDZt81KV6=K+9&$|3vSKz^US^?f2Z2m}ZOB_X@Vpnp&8`o1e1 z1%MuO3wAtSKkxN;K3|9z0v?|q#XKH?^8P^P3IhAh;1^H-`nv-Mjq(KyNDl-3I|rrs zwl~-e3?4oYjC>xKAb9`03j)A<$YKv3{xLepD2BU?eaOM<1U9x^MqM z@u4!x!Te;6{_h65cmBZfy#vZH`xCg|;J;6Iy1jCx1Ii5h69C_rj@!Ir{{VQ#fn~r4pZuODQLq2j!#^Mp z08m0N5P3i%902I7#2!Im52QX|O%`x~2mOBsrvecOfIxxhKUoA7Rxn6_JikBq8r0`- zP$0qq<{KD0ROFB_P{am8f*-g%LZtwn1I7&?2b@pf4^>dYC;;-{Msg5fdC39eFpv@s yIj{ne7{CN6E(Cv|3P!;J(tqHWhl~J8@jwU&WD*CcfEU0YNdEx5fDZt81KV6=K+9&$|3vSKz^US^?f2Z2m}ZOB_X@Vpnp&8`o1e1 z1%MuO3wAtSKkxN;K3|9z0v?|q#XKH?^8P^P3IhAh;1^H-`nv-Mjq(KyNDl-3I|rrs zwl~-e3?4oYjC>xKAb9`03j)A<$YKv3{xLepD2BU?eaOM<1U9x^MqM z@u4!x!Te;6{_h65cmBZfy#vZH`xCg|;J;6Iy1jCx1Ii5h69C_rj{Cf0{{VQ#fn~r4pZuODQLq2j!ay(p zpoCr^@_;&s_8{{gkbKy}2o`XF2mD_kd2k^d0qBF!ezFKGz}h1-{QuxDKqrte7{USP6ZiwwR8atVa3eVgFg)ad@qV6=Q)6b$|3vSKz^US^?f2Z3*-v`QY*ZA2lW2mtKzZ1 zdx7Ubw`0ZQ{{L5l<^F+iA?fk_Q^DvTAb89a1^1i4E}#GPb_Nd-_`?zd!2eFc>3%JV zd|+Vl@O)$7^tl7a{oq&@1KvXrdn3jHK2i_)UN=ks^1Je&z#uRj1cm{~oxMe%;`DU?3p#-zUfO zKCj%RL*gk25Plc%c(=$Nae#VH-{=o9fPl;K-*=3E;13wkEVuyk|C7Y(HUIk9NCp5D z5R1efPzO;S#2#bv51SZ40?rTs|BK`gE(9ZhJrH^i)V6=Q)6b$|3vSKz^US^?f2Z3*-v`QY*ZA2lW2mtKzZ1 zdx7Hd8U?!^FCX{%ydN+03c!#Y2jZR&K=}j4Fn}+--T`#~|EsVtc#p;m4;}~lb`MMO zY-cza2t0fr82CLdK=J=rAQSQU`^sVuWO(ocV|@QB#_50lSAJ9&1O@}}4UZ#_&w<@H z|DgELnPvQB3EXb*Yo~wg4;#=tzwlQXU*Nw_ce=fDr2>b6|9y#oZ_DxfcY*2fzk|V^EI(Qd|iCKomd$Y!Dq1gD(#l0FUBe5D>^D2SWfKfIfpn0C)qy9snRwx%a&Q 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 90cdc5ce9..000000000 --- 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 zcmV#^A1P}~AkN5#6;P;FNmS7KgKs{vu_v8!KP6ki_)5&H6A0`3; zAb>(35G(~C9j(HD4}m{eKjZ=jzz@#?gZ|(g1U{XbxO{=*eo&7*KDzSx zgT?{i=oipFOYy*9XV3_Tfc~NKzz+sKUmgHFde>R#{K4b^7#)BfKCRTaKE=^^2t0n* z+4z0S<1h!0`~N@u`~T(gUkoAv@Gpow`v3L#@HlV?Bmyvh5c((10YL|*d(Sz%^?~JJ zfKZ4d3e;@e$0tEoTe+%kt75Kve_yG5a!yyil vz(4`<2nYB*z`_6$NQ3+y|M&#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{Js!{oma;K$43z(1&bf#d)f9e^G^YpnEsVDkY8JiS|~aDB_; zFb9v?`#%q=_+bF^|9|KIpMU(mTsQ<00r)S7Jo^9j_>0DZ4;aDxL+GC?1OkLX7+^ee z-g(XEtPdh!;DO@~2d}t(Ao77gU=Yy!#wZXd1^`||_W#4dJ>d|@L*#G} r0DptT3?KmC0Pq2T&>#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{Js1Rit9339yu*Qi z2anqOKM$(-VH5iRJb%JL{QLjq^5K(&;3e?~pI`pJ5qQvGgAqT7eG}zifKZ4d7zd7f z&pEvHf#j?l5Ikdm_4g0N9#AL@0v;cj#R3HYz(WV{nh<#yM82lMUyL$9E*Jzb{wrbx z56)DQGV0M{4{fF8l&gT%q#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{JInK`3Kw{ zOQC{;$bP``pKu`I`@jQ6JRiOSSZ4wGERFq2J_7uo#$XQ~0)FEF@xVaS^baTiJcIEJ zCWt)4@eDwW0t~;XKj0oO!C%Z82?Ga=4L>k%6n+8xgQw;P@>mQW7$2A)>JQ*L2z>iB zaQXwrI1eEC>&xc@{zBFR`!AS$m*aqM3=#n#&IjBca3OH!DIII9^nPITPmnx)Td8n; z+v5SoQa;z&_2p zgWxF&QcRLOV9&uH3@{&207*tL9D!JX4o`rAgVs<5AOT7N>oCAZ0qZdTz(MEW7N`KQ zAoNd|h+qrvzz3^DZ}7-O z0tJ9Hh&{d$@Oy!T9$*231Ly{DaC%@MAoj!y)qW;X2c^UKU|1L#W9$Zie;o_pkAc(y F;15h-#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{JInK`3Kw{ zOQC{;$bP``pKu`I`@jQ6JRiOSSZ4wGERFq2J_7uo#$XQ~0)FEF@xVaS^baTiJcIEJ zCWt)4@eDwW0t~;XKj0oO!C%Z82?Ga=4L>k%6n+8xgQw;P@>mQW7$2A)>JQ*L2z>iB zaQc3ralm;8&t6|RAMz5gAK84v2mm}*LqGic|K;-GgT&rH>Ucc*|MmFrAP^q|09WE4MEMHF z1rQ(=BR%Jw-g?0Dm;=TTj}UtMhvE+`5kQ0U7@z~na1Rmtf8q295(GhlUqfK8#u*@h z!LSf~1tCgFl1Gdg_#@$l1L)cy0gPZd01tRg%Q8K6BlFaUa9C>IE@KKv9M1Z*Ck z@Onfr1=Ro#03NLozw;rm2o?a+Aolow?Xci_!U5=V9Xu=_AT#p;1Rhuj2tA=f&>#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{Js-G)4;UJLV1F1nd|=7=;6Q&cJAsfx@DDg}JcH%gtB2Hn zJ_u-iHRbbw{-BTuhxoo>@?Vbv0X%^VhZrAlc{>F@Ao1&6XQT56n|xpxYDcSeE)To{ zU=J^~_I@8(;5<#^gQP5mfBEd>}Xl_=C@{|6ho_U@(FJz(eYvD+B@re;WWi zbKZH)=d2GRSkQ_D4_|QnLF*h3%wmCp2aG&_ANYL&1q0xJ3+ikY_`@U$;g~}(6ojz? z1^|y3H-HjA`-lQcF@WR>!~k-91PmUsfGGe9P!Czg0x%C*hyDT&KR~rW1%U^rA_MRT zEc_n12t5J-2NpQMWB~PuM}hCb1CTtg0RHe$LGAwst^mIL0D8bb*Z2e?0Rq540qP19 zc)&sI1`v6`6#@^~1Q;He0WkmsvLPXc1JG#^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)To{ zeh)9T_I@8(;Ko{FBl9G280m$r^>)?A0z_6 z4;=TNb9w6n%3%PZMj#Uadi#gs4>JJA1M?W5V1eTgAIJV5K!Fp7_+L|Cuf`c5R}8`# zfTSge5HJLI!Mp&H2i!msQH%#5Rv-hD;2>c2lmSQpQh<8iFbFXKB*Xs!2cMu?paQ^y z(-9B&1D1XdScUq40~iDl!ZHARd? zJ)jZ@WFi3qzy-t}fT0J-EP!VahXeK*0|%x80uM6Ch6E5m@?VH7f#?2UG!2yi1Vjs= 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 93e59e49b..000000000 --- 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 e5f5fc0a6..000000000 --- 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 7432507ce..000000000 --- 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 de37d5b2e..000000000 --- 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 ffd845e8c..000000000 --- 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 c21027e49..000000000 --- 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 8ad8d42a3..000000000 --- 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 b705bf623..000000000 --- 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 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;IUA+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@i1Ug55tM@IS33)9#HobQeYB9@c0gb4|z56kPjP#0OlzN!PG(HjA9Rh z{tieEN(GZw4E_av2fix_qJVNZ7)ks9g0LD@<4FK652ij3iapoOhNB}fxth| zaUlQ>1TP@Te}Y1`5PIP>kmv(YU>*gEz_ETUdtZ$I1hYIXb4;kJPaQ3E2t2>7Zqy&20>~972yIh0DDj> zh9Q7|3S%$_wIBpBf^dNh0Js1U*gOX!7>GVF0pf8G{ea8h2Q&h523O*M3JeBO^S~j5 gRUbK>htQ{I>zmL3SMYp52Pn;O0YOl-FEWAyd04oCj|KJ!1JOBb92k?KvV|ai<1QYz9hz1J~2hIcX0~p1CgIV|t0%5=@ zSun5w`-VUukO3HgE-(-8gboCi*f7LHlA#oMki-Dc9wdEcFgU~`_z!u&5Tl0y!^6Sr z_k#fn!SKJrK1LOT8I0**;V3T=33|XQiDh&XV za0p$X1|pFMmInZnNM0aJAc2Si2L%9xhty0V5eJkC0~rUWpbTL8`dP<>4=5xC-k(q3 zd_J&D7IYqP|K*$tQUH17f#?7K#=+|d!DfN!?}D?09+*tE{hoK_*yI80)z7R-f4l++ zJZJ?Xf=}cDp$Cm~fddzaJt$NFd0*tx1-S##1Au{($~F&a1Rx$G^8dj>@er^B%ku&U z#ez5wE6fC*AoQR@Fc>rWZ_fg-FIofIcA%Lx5-x3=cH0 zhzGzAszd^z48RMX2aoI?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~_&I>T8sK_g^ewUn3<3$q-$jAv|L+6FZVx%cydER*JCBDy|Ad3jfh?0j3*wg) z=>Oor@<9#^9$ov|$bZxR2|R)04hNL~7;P^6f)EcmkUYQlaCu4M5deAEq>xX(2N(YL zzl0ubA<;lX<9-K<^nvRlfk7Y`98~Lo-4J_0Ae;CF*PlPXKw<>(zywA`2!R1d6*J&w zU=jg9gWvTHfLWp(2pj@{4;YL<9%gZjCrBR`qBzI_=OCN|#1K#$5Iy+d^B{>ZMMeM| zgE(OUg8pg&?NESWAr=BUnuq{2hz24E54nJZV0r~11I`!&7r=~4Apw&E+x`m?iA*3@ zn83X}5D&yI0Xztd6Au>$q;r4wg1|UXd?Y6S5C@e5f#a|G7Q;RKKp|iJT?8IH3IFoH zXn^oP&=~wH?~A~yi-G^IrG{buuJ|DH_(AZrAOC%PxIEANP-Z>&nh3fd|Nj0@fbrk^ z!J(JQ0p;lL&+p!g|Kk5&1q{Ba4}(BH-~Rvh4}cMmJmxa}q(U)(y#FQttJutY{`eq# zq#g_Y`}Z#WVSm8^`@hG(TaZvN75|`oU|-W9)>vadt^>sQKTiGj0uLX(v>qU_&;!iA zAo9JP0KyL~{v94ALHKEZ;Nb_Se}WG`|8xdG 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 zcmV;=0Wkgn0LuXYcmu#50PqKZJOSVj0C)qy9suwMfCl~!-UT!tw-1;80uQ+4CIOF! zgX<3jfPNA+B6Pzk4iom2tn#;^n#umj2)0D*(zKo1-Q8UO(h1Iz*AKn{saKo1xP zj)D&Wh99IJJP5=B5D$o;JZusHDE|^q7Q{em6bJ-D4w88h>}41T2SNfP5CX{XANi~b z18_TtJ*Yeg1bP7hhy&(9{x1v*`2nAU+XIk*#KGeN;RD_QNq|Wp7*GGwJ`Z^{@{s_d zKtHGVNDdC80GdEQ(0>K~%nmpXN(GZ&7y1Sdd{z@g?l1fX0bmEMD)FR%K+=K<5PQ-z zit*rI!6R_61Kbck1MyB4f&eHGyp#j*KNNYyfbqg<3B&k54}u=Y5P9PKTJqTcAmH(V z!-407$wo%xeiZY;BB7xG3HX2wgp&e@{9p!z^FV}tFau(9Ktk|;zyc3wmDC7V56AyL z4`~Ic2vfoUA+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)tcE$@zuAGu0m(qJ zYT^7f#2)ypCWsy|b)dj30QIF_G>{KP{sglSd(r{p9Kb;V<8ToOJ+Kfs1SY=#oLm9t z0vC`Vxk15?P66wL(nEm5I-qzV>~RO4FU7BbfrcCp7#LW91BA&&M&|=3hyh@cRY|=B zxPkMN3rRXK28+UZpi_nfNC3E;(N!Q3fO|x)phCb5#6lnikXnF*Oeiots1-vHiw$HT z1*8Ck8X>_LkO6Q4((nTZzz$$};3Mb*AGjG~fcKn%l)T`Ez!#kk%<%v^onA%&S303c S-zHfA86^Sr1qbc`pn&=5kDA+B6Pzk4iom2tn#;^n#umj2)0D*(zKo1-Q8UO(h1Iz*AKn{saKo1xP zj)D&Wh99IJJP5=B5C{>IMFHbrkO@cm4+#(e;@F4{LV*B?LFW+wddQDsD8N8E5E%eH zq<9bf)&&8$9mJm09s~kC0Dy-C9}*Aocwk@14E!G09E1iY4;U8+9`FiG0!aYEfBup1 zd&#eqhy@Y>{fC1RaF8q=MgcT{f1v&g{h9%ol$M7j0?Dt7{R0Q2TI3&}t~ z1Mx?v5(qpv9#~BvRDTEI@I&L#2pOPYSics$wm--?JYaC>U_7vyD9f-<#IATGR5Ty~ zKM(=1l3-CEi~!J{Xb_La0BlZZ2wo5P072~%x`7Je`2Xj@?I5)Q3V1*qcs-~ULlD3d zqwv9l+K>Vm#{oJwhM0g94ps$d@F4sEe2)kI-WmY?!0hu70?t6~Faxu!KnolLvxGnj p)i43Z0L#E|IUTPI_d~KoKo0`~AAnro0fK=J_zsK#K=>E%d;lscmu#50PqKZJOSVj0C)qy9suwMfCl~!-UT!tw-1;80uQ+4CIOF! zgX<3jfPNA+B6Pzk4iom2tn#;^n#umj2)0D*!A3=dfVJa7P^1{7c(vjBL|1ENz9 zz=PH>4;=&^0Sp8U0Q|u5;0lHi2pljyVu10m3WhQKhvEV27Q{em0-=aO=Me*%$d6+v zz(6_>82~+`cn|#61p&Aho&iDNKqJry3_u?;5Ak?lU&svn9@rd&1||;}7YH8k3QPh? z0K$L%k??!Tuat-d5&`{(gA#C%EFDGxG=P7g{tNw@0hpAQhb02ZuZ#Ty2c+a4IIJd# z++Xx)*a735bc_a-c+x;1X+azaJfWC5_&jMEMR@Qp;PJRvB~a`*JuDCp0r;mE2~-$x zFd=y;2jG4v^x{DWhXczAqzaGV{5}YLdLaWe3=0?H*Otfm2M3G}9SjGS6D1jT3HX)I z1d4`)04L%AHWEwfI+{{RR*qE}EMTt6TD_&uZ+pdn8P1CIx_ z0;pmb0(5>DFndw}Lm1#EM)1=R0)fiFtsVp)fDe)2|J%brAGjT!VgOml9i{+wb%+6D ufOc?*0a~U2IKUZr4hJK(;hyMrNQeR8U_RSffG8gV{ttjq_rDAP 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 c21027e49..000000000 --- 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 1d415b4b8..000000000 --- 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 cb4f8f5a5..4c0427894 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 5eaf16f94..a11a3ccd5 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 7c501803f..15f29e4dc 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 bcb8f998f..370f1a14a 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 324dfe332..cadb417f8 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 2d082838a..94705dada 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 9edc6b5c1..ee317be3b 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 aa3d50b64..c670c01d4 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 2d83d8816..b763a3a7c 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/30] 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 64d2b96b1..865ee48d4 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/30] 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 7d6162954..ed5ee6bd7 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/30] [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 9af00157d..fe1005b2d 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/30] 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 c4e4471a0..76bc3f0a1 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/30] [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 275d23329..479638b98 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 000000000..db05af095 --- /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 000000000..ac0371d89 --- /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 0740691a2..3ec9e995a 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 edc078cd6..8f3eb53d7 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 4c48d9abd..17a8a675a 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 588d4effc..d2b8e3d11 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 275889484..8cddf4c0b 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/30] [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 5dacefd66..8ac9bf5ba 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 5f022d6e1..fd42829b5 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 ba70f36d1..df5a76525 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 284f7ccf5..09830ba05 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 2d951b11a..6120a210b 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 1d4bb7dc0..bc8711923 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 17a8a675a..8bad0b830 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 b8e93c3d5..b639c93b9 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 96c77553a..44c7faec5 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/30] 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 3946d05cc..9baaf97b4 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 659170519..2bf9ea28e 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 d0b89a763..d501996b3 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/30] 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 1e275aafb..a2979d56b 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 49bbd7d68..a67e58494 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 8c5ed91a9..42712ed90 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 b40f8ded5..7a2003a63 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 6e4ce8360..1736558cb 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 8069a02e5..baa8f7bd2 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 96f2798e2..169681ac0 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/30] 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 ac1036d05..beefd8024 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 8bad0b830..fd11dffe0 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 d2237f6c4..f491c87bf 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 25549fab3..a5e561e35 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 d2b8e3d11..24202ad1c 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 8cddf4c0b..a4f04271a 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/30] 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 c2abd1b85..192b83ee4 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 537e0abf0..90d968fea 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 a751eb6e1..f95ef13f9 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 462a2e89e..c42b0c2ac 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 fd29ca2ef..6177a9a50 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 38c7793b8..939627723 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; }