diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c9b8ff3f5..6b77482c6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -18,7 +18,7 @@ /applications/main/gpio/ @skotopes @DrZlo13 @hedger @nminaylov /applications/main/ibutton/ @skotopes @DrZlo13 @hedger @gsurkov /applications/main/infrared/ @skotopes @DrZlo13 @hedger @gsurkov -/applications/main/nfc/ @skotopes @DrZlo13 @hedger @gornekich +/applications/main/nfc/ @skotopes @DrZlo13 @hedger @gornekich @Astrrra /applications/main/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm /applications/main/u2f/ @skotopes @DrZlo13 @hedger @nminaylov @@ -40,6 +40,8 @@ /applications/system/storage_move_to_sd/ @skotopes @DrZlo13 @hedger @nminaylov +/applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @nminaylov @gornekich @Astrrra @gsurkov @Skorpionm + # Documentation /documentation/ @skotopes @DrZlo13 @hedger @drunkbatya /scripts/toolchain/ @skotopes @DrZlo13 @hedger @drunkbatya @@ -54,6 +56,9 @@ /lib/mbedtls/ @skotopes @DrZlo13 @hedger @nminaylov /lib/micro-ecc/ @skotopes @DrZlo13 @hedger @nminaylov /lib/nanopb/ @skotopes @DrZlo13 @hedger @nminaylov -/lib/nfc/ @skotopes @DrZlo13 @hedger @gornekich +/lib/nfc/ @skotopes @DrZlo13 @hedger @gornekich @Astrrra /lib/one_wire/ @skotopes @DrZlo13 @hedger @gsurkov /lib/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm + +# CI/CD +/.github/workflows/ @skotopes @DrZlo13 @hedger @drunkbatya diff --git a/.github/workflows/amap_analyse.yml b/.github/workflows/amap_analyse.yml index a50c5436f..cfb1eab14 100644 --- a/.github/workflows/amap_analyse.yml +++ b/.github/workflows/amap_analyse.yml @@ -91,7 +91,7 @@ jobs: export RODATA_SIZE="$(get_size ".rodata")" export DATA_SIZE="$(get_size ".data")" export FREE_FLASH_SIZE="$(get_size ".free_flash")" - python3 -m pip install mariadb + python3 -m pip install mariadb==1.1.4 python3 scripts/amap_mariadb_insert.py \ ${{ secrets.AMAP_MARIADB_USER }} \ ${{ secrets.AMAP_MARIADB_PASSWORD }} \ diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index f28fad20d..9de493a44 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -57,7 +57,7 @@ jobs: - name: 'Generate compile_comands.json' run: | - FBT_TOOLCHAIN_PATH=/runner/_work ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking + FBT_TOOLCHAIN_PATH=/runner/_work ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking _fap_icons - name: 'Static code analysis' run: | diff --git a/.gitignore b/.gitignore index 38a31bf01..61c594ed1 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,5 @@ openocd.log # PVS Studio temporary files .PVS-Studio/ PVS-Studio.log + +.gdbinit diff --git a/.pvsconfig b/.pvsconfig index d17eaa5a0..5f1ffb7cb 100644 --- a/.pvsconfig +++ b/.pvsconfig @@ -5,6 +5,7 @@ //-V:BPTREE_DEF2:779,1086,557,773,512 //-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685 //-V:ALGO_DEF:1048,747,1044 +//-V:TUPLE_DEF2:524,590,1001,760 # Non-severe malloc/null pointer deref warnings //-V::522:2,3 diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json index c8b0c601d..5c46d3979 100644 --- a/.vscode/example/launch.json +++ b/.vscode/example/launch.json @@ -38,6 +38,7 @@ "postAttachCommands": [ // "compare-sections", "source debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", // "source debug/FreeRTOS/FreeRTOS.py", // "svd_load debug/STM32WB55_CM4.svd" ] @@ -59,6 +60,7 @@ "set confirm off", "set mem inaccessible-by-default off", "source debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", // "compare-sections", ] // "showDevDebugOutput": "raw", @@ -76,6 +78,7 @@ "rtos": "FreeRTOS", "postAttachCommands": [ "source debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", ] // "showDevDebugOutput": "raw", }, @@ -95,6 +98,7 @@ ], "postAttachCommands": [ "source debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", ], // "showDevDebugOutput": "raw", }, diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00a0191e5..dc1d41e27 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ Thank you for investing your time in contributing to our project! -Read our [Code of Coduct](CODE_OF_CONDUCT.md) to keep our community approachable and respectable. +Read our [Code of Conduct](CODE_OF_CONDUCT.md) to keep our community approachable and respectable. In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR. @@ -17,12 +17,12 @@ See the [ReadMe](ReadMe.md) to get an overview of the project. Here are some hel ## Getting started -Before writing code and creating PR make sure that it aligns with our mission and guidlines: +Before writing code and creating PR make sure that it aligns with our mission and guidelines: - All our devices are intended for research and education. - PR that contains code intended to commit crimes is not going to be accepted. - Your PR must comply with our [Coding Style](CODING_STYLE.md) -- Your PR must contain code compatiable with project [LICENSE](LICENSE). +- Your PR must contain code compatible with project [LICENSE](LICENSE). - PR will only be merged if it pass CI/CD. - PR will only be merged if it pass review by code owner. diff --git a/SConstruct b/SConstruct index 13a698c81..474175c14 100644 --- a/SConstruct +++ b/SConstruct @@ -7,6 +7,7 @@ # construction of certain targets behind command-line options. import os +from fbt.util import path_as_posix DefaultEnvironment(tools=[]) @@ -43,6 +44,7 @@ distenv = coreenv.Clone( "jflash", ], ENV=os.environ, + UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}", ) firmware_env = distenv.AddFwProject( @@ -140,21 +142,28 @@ distenv.Default(basic_dist) dist_dir = distenv.GetProjetDirName() fap_dist = [ distenv.Install( - f"#/dist/{dist_dir}/apps/debug_elf", - firmware_env["FW_EXTAPPS"]["debug"].values(), + distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"), + list( + app_artifact.debug + for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() + ), ), - *( - distenv.Install(f"#/dist/{dist_dir}/apps/{dist_entry[0]}", dist_entry[1]) - for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values() + distenv.Install( + f"#/dist/{dist_dir}/apps", + "#/assets/resources/apps", ), ] -Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values()) +Depends( + fap_dist, + list( + app_artifact.validator + for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() + ), +) Alias("fap_dist", fap_dist) # distenv.Default(fap_dist) -distenv.Depends( - firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"]["resources_dist"] -) +distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist) # Target for bundling core2 package for qFlipper @@ -192,6 +201,7 @@ firmware_debug = distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", + FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), ) distenv.Depends(firmware_debug, firmware_flash) @@ -201,6 +211,7 @@ distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", GDBREMOTE="${BLACKMAGIC_ADDR}", + FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), ) # Debug alien elf @@ -209,7 +220,7 @@ distenv.PhonyTarget( "${GDBPYCOM}", GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", - GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ', + GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ', ) distenv.PhonyTarget( @@ -230,13 +241,13 @@ distenv.PhonyTarget( distenv.PhonyTarget( "lint", "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}", - LINT_SOURCES=firmware_env["LINT_SOURCES"], + LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], ) distenv.PhonyTarget( "format", "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}", - LINT_SOURCES=firmware_env["LINT_SOURCES"], + LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], ) # PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests diff --git a/applications/debug/accessor/helpers/wiegand.cpp b/applications/debug/accessor/helpers/wiegand.cpp index 79c9f723b..bb2885549 100644 --- a/applications/debug/accessor/helpers/wiegand.cpp +++ b/applications/debug/accessor/helpers/wiegand.cpp @@ -71,7 +71,7 @@ void WIEGAND::end() { } void WIEGAND::ReadD0() { - _bitCount++; // Increament bit count for Interrupt connected to D0 + _bitCount++; // Increment bit count for Interrupt connected to D0 if(_bitCount > 31) // If bit count more than 31, process high bits { _cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits diff --git a/applications/debug/bt_debug_app/bt_debug_app.c b/applications/debug/bt_debug_app/bt_debug_app.c index ac442de0a..405051a4a 100644 --- a/applications/debug/bt_debug_app/bt_debug_app.c +++ b/applications/debug/bt_debug_app/bt_debug_app.c @@ -98,7 +98,7 @@ void bt_debug_app_free(BtDebugApp* app) { int32_t bt_debug_app(void* p) { UNUSED(p); if(!furi_hal_bt_is_testing_supported()) { - FURI_LOG_E(TAG, "Incorrect radio stack: radio testing fetures are absent."); + FURI_LOG_E(TAG, "Incorrect radio stack: radio testing features are absent."); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack"); return 255; diff --git a/applications/debug/display_test/display_test.c b/applications/debug/display_test/display_test.c index e7f366cbb..5b46d2b41 100644 --- a/applications/debug/display_test/display_test.c +++ b/applications/debug/display_test/display_test.c @@ -145,7 +145,7 @@ DisplayTest* display_test_alloc() { view_set_previous_callback(view, display_test_previous_callback); view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view); - // Configurtion items + // Configuration items VariableItem* item; instance->config_bias = false; instance->config_contrast = 32; diff --git a/applications/debug/file_browser_test/file_browser_app.c b/applications/debug/file_browser_test/file_browser_app.c index 6cb50d385..996cb2bd2 100644 --- a/applications/debug/file_browser_test/file_browser_app.c +++ b/applications/debug/file_browser_test/file_browser_app.c @@ -48,7 +48,7 @@ FileBrowserApp* file_browser_app_alloc(char* arg) { app->file_path = furi_string_alloc(); app->file_browser = file_browser_alloc(app->file_path); - file_browser_configure(app->file_browser, "*", true, &I_badusb_10px, true); + file_browser_configure(app->file_browser, "*", NULL, true, &I_badusb_10px, true); view_dispatcher_add_view( app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget)); diff --git a/applications/debug/rpc_debug_app/application.fam b/applications/debug/rpc_debug_app/application.fam new file mode 100644 index 000000000..d71065afa --- /dev/null +++ b/applications/debug/rpc_debug_app/application.fam @@ -0,0 +1,10 @@ +App( + appid="rpc_debug", + name="RPC Debug", + apptype=FlipperAppType.DEBUG, + entry_point="rpc_debug_app", + requires=["gui", "rpc_start", "notification"], + stack_size=2 * 1024, + order=10, + fap_category="Debug", +) diff --git a/applications/debug/rpc_debug_app/rpc_debug_app.c b/applications/debug/rpc_debug_app/rpc_debug_app.c new file mode 100644 index 000000000..314be5e72 --- /dev/null +++ b/applications/debug/rpc_debug_app/rpc_debug_app.c @@ -0,0 +1,138 @@ +#include "rpc_debug_app.h" +#include + +#include + +static bool rpc_debug_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + RpcDebugApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool rpc_debug_app_back_event_callback(void* context) { + furi_assert(context); + RpcDebugApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void rpc_debug_app_tick_event_callback(void* context) { + furi_assert(context); + RpcDebugApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +static void rpc_debug_app_rpc_command_callback(RpcAppSystemEvent event, void* context) { + furi_assert(context); + RpcDebugApp* app = context; + furi_assert(app->rpc); + + if(event == RpcAppEventSessionClose) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + rpc_system_app_set_callback(app->rpc, NULL, NULL); + app->rpc = NULL; + } else if(event == RpcAppEventAppExit) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + rpc_system_app_confirm(app->rpc, RpcAppEventAppExit, true); + } else { + rpc_system_app_confirm(app->rpc, event, false); + } +} + +static bool rpc_debug_app_rpc_init_rpc(RpcDebugApp* app, const char* args) { + bool ret = false; + if(args && strlen(args)) { + uint32_t rpc = 0; + if(sscanf(args, "RPC %lX", &rpc) == 1) { + app->rpc = (RpcAppSystem*)rpc; + rpc_system_app_set_callback(app->rpc, rpc_debug_app_rpc_command_callback, app); + rpc_system_app_send_started(app->rpc); + ret = true; + } + } + return ret; +} + +static RpcDebugApp* rpc_debug_app_alloc() { + RpcDebugApp* app = malloc(sizeof(RpcDebugApp)); + + app->gui = furi_record_open(RECORD_GUI); + app->notifications = furi_record_open(RECORD_NOTIFICATION); + app->scene_manager = scene_manager_alloc(&rpc_debug_app_scene_handlers, app); + app->view_dispatcher = view_dispatcher_alloc(); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, rpc_debug_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, rpc_debug_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, rpc_debug_app_tick_event_callback, 100); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, RpcDebugAppViewWidget, widget_get_view(app->widget)); + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, RpcDebugAppViewSubmenu, submenu_get_view(app->submenu)); + app->text_box = text_box_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, RpcDebugAppViewTextBox, text_box_get_view(app->text_box)); + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, RpcDebugAppViewTextInput, text_input_get_view(app->text_input)); + app->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, RpcDebugAppViewByteInput, byte_input_get_view(app->byte_input)); + + return app; +} + +static void rpc_debug_app_free(RpcDebugApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewByteInput); + view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextInput); + view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextBox); + view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewSubmenu); + view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewWidget); + + free(app->byte_input); + free(app->text_input); + free(app->text_box); + free(app->submenu); + free(app->widget); + + free(app->scene_manager); + free(app->view_dispatcher); + + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + furi_record_close(RECORD_GUI); + app->gui = NULL; + + if(app->rpc) { + rpc_system_app_set_callback(app->rpc, NULL, NULL); + rpc_system_app_send_exited(app->rpc); + app->rpc = NULL; + } + + free(app); +} + +int32_t rpc_debug_app(void* args) { + RpcDebugApp* app = rpc_debug_app_alloc(); + + if(rpc_debug_app_rpc_init_rpc(app, args)) { + notification_message(app->notifications, &sequence_display_backlight_on); + scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStart); + } else { + scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStartDummy); + } + + view_dispatcher_run(app->view_dispatcher); + + rpc_debug_app_free(app); + return 0; +} diff --git a/applications/debug/rpc_debug_app/rpc_debug_app.h b/applications/debug/rpc_debug_app/rpc_debug_app.h new file mode 100644 index 000000000..100fc124c --- /dev/null +++ b/applications/debug/rpc_debug_app/rpc_debug_app.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "scenes/rpc_debug_app_scene.h" + +#define DATA_STORE_SIZE 64U +#define TEXT_STORE_SIZE 64U + +typedef struct { + Gui* gui; + RpcAppSystem* rpc; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + NotificationApp* notifications; + + Widget* widget; + Submenu* submenu; + TextBox* text_box; + TextInput* text_input; + ByteInput* byte_input; + + char text_store[TEXT_STORE_SIZE]; + uint8_t data_store[DATA_STORE_SIZE]; +} RpcDebugApp; + +typedef enum { + RpcDebugAppViewWidget, + RpcDebugAppViewSubmenu, + RpcDebugAppViewTextBox, + RpcDebugAppViewTextInput, + RpcDebugAppViewByteInput, +} RpcDebugAppView; + +typedef enum { + // Reserve first 100 events for button types and indexes, starting from 0 + RpcDebugAppCustomEventInputErrorCode = 100, + RpcDebugAppCustomEventInputErrorText, + RpcDebugAppCustomEventInputDataExchange, + RpcDebugAppCustomEventRpcDataExchange, +} RpcDebugAppCustomEvent; diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.c new file mode 100644 index 000000000..2c593cdeb --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.c @@ -0,0 +1,30 @@ +#include "rpc_debug_app_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const rpc_debug_app_on_enter_handlers[])(void*) = { +#include "rpc_debug_app_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 rpc_debug_app_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "rpc_debug_app_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 rpc_debug_app_on_exit_handlers[])(void* context) = { +#include "rpc_debug_app_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers rpc_debug_app_scene_handlers = { + .on_enter_handlers = rpc_debug_app_on_enter_handlers, + .on_event_handlers = rpc_debug_app_on_event_handlers, + .on_exit_handlers = rpc_debug_app_on_exit_handlers, + .scene_num = RpcDebugAppSceneNum, +}; diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.h b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.h new file mode 100644 index 000000000..eb3424fb1 --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) RpcDebugAppScene##id, +typedef enum { +#include "rpc_debug_app_scene_config.h" + RpcDebugAppSceneNum, +} RpcDebugAppScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers rpc_debug_app_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "rpc_debug_app_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 "rpc_debug_app_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 "rpc_debug_app_scene_config.h" +#undef ADD_SCENE diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_config.h b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_config.h new file mode 100644 index 000000000..8f163253d --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_config.h @@ -0,0 +1,8 @@ +ADD_SCENE(rpc_debug_app, start, Start) +ADD_SCENE(rpc_debug_app, start_dummy, StartDummy) +ADD_SCENE(rpc_debug_app, test_app_error, TestAppError) +ADD_SCENE(rpc_debug_app, test_data_exchange, TestDataExchange) +ADD_SCENE(rpc_debug_app, input_error_code, InputErrorCode) +ADD_SCENE(rpc_debug_app, input_error_text, InputErrorText) +ADD_SCENE(rpc_debug_app, input_data_exchange, InputDataExchange) +ADD_SCENE(rpc_debug_app, receive_data_exchange, ReceiveDataExchange) diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_data_exchange.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_data_exchange.c new file mode 100644 index 000000000..3ae2ba44d --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_data_exchange.c @@ -0,0 +1,40 @@ +#include "../rpc_debug_app.h" + +static void rpc_debug_app_scene_input_data_exchange_result_callback(void* context) { + RpcDebugApp* app = context; + view_dispatcher_send_custom_event( + app->view_dispatcher, RpcDebugAppCustomEventInputDataExchange); +} + +void rpc_debug_app_scene_input_data_exchange_on_enter(void* context) { + RpcDebugApp* app = context; + byte_input_set_header_text(app->byte_input, "Enter data to exchange"); + byte_input_set_result_callback( + app->byte_input, + rpc_debug_app_scene_input_data_exchange_result_callback, + NULL, + app, + app->data_store, + DATA_STORE_SIZE); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewByteInput); +} + +bool rpc_debug_app_scene_input_data_exchange_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == RpcDebugAppCustomEventInputDataExchange) { + rpc_system_app_exchange_data(app->rpc, app->data_store, DATA_STORE_SIZE); + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_input_data_exchange_on_exit(void* context) { + RpcDebugApp* app = context; + UNUSED(app); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c new file mode 100644 index 000000000..eae12e6ee --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c @@ -0,0 +1,60 @@ +#include "../rpc_debug_app.h" + +static bool rpc_debug_app_scene_input_error_code_validator_callback( + const char* text, + FuriString* error, + void* context) { + UNUSED(context); + + for(; *text; ++text) { + const char c = *text; + if(c < '0' || c > '9') { + furi_string_printf(error, "%s", "Please enter\na number!"); + return false; + } + } + + return true; +} + +static void rpc_debug_app_scene_input_error_code_result_callback(void* context) { + RpcDebugApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorCode); +} + +void rpc_debug_app_scene_input_error_code_on_enter(void* context) { + RpcDebugApp* app = context; + strncpy(app->text_store, "666", TEXT_STORE_SIZE); + text_input_set_header_text(app->text_input, "Enter error code"); + text_input_set_validator( + app->text_input, rpc_debug_app_scene_input_error_code_validator_callback, NULL); + text_input_set_result_callback( + app->text_input, + rpc_debug_app_scene_input_error_code_result_callback, + app, + app->text_store, + TEXT_STORE_SIZE, + true); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput); +} + +bool rpc_debug_app_scene_input_error_code_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == RpcDebugAppCustomEventInputErrorCode) { + rpc_system_app_set_error_code(app->rpc, (uint32_t)atol(app->text_store)); + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_input_error_code_on_exit(void* context) { + RpcDebugApp* app = context; + text_input_reset(app->text_input); + text_input_set_validator(app->text_input, NULL, NULL); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_text.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_text.c new file mode 100644 index 000000000..b07f8f4af --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_text.c @@ -0,0 +1,40 @@ +#include "../rpc_debug_app.h" + +static void rpc_debug_app_scene_input_error_text_result_callback(void* context) { + RpcDebugApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorText); +} + +void rpc_debug_app_scene_input_error_text_on_enter(void* context) { + RpcDebugApp* app = context; + strncpy(app->text_store, "I'm a scary error message!", TEXT_STORE_SIZE); + text_input_set_header_text(app->text_input, "Enter error text"); + text_input_set_result_callback( + app->text_input, + rpc_debug_app_scene_input_error_text_result_callback, + app, + app->text_store, + TEXT_STORE_SIZE, + true); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput); +} + +bool rpc_debug_app_scene_input_error_text_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == RpcDebugAppCustomEventInputErrorText) { + rpc_system_app_set_error_text(app->rpc, app->text_store); + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_input_error_text_on_exit(void* context) { + RpcDebugApp* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c new file mode 100644 index 000000000..98bafff8a --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c @@ -0,0 +1,70 @@ +#include "../rpc_debug_app.h" + +static void rpc_debug_app_scene_start_format_hex( + const uint8_t* data, + size_t data_size, + char* buf, + size_t buf_size) { + furi_assert(data); + furi_assert(buf); + + const size_t byte_width = 3; + const size_t line_width = 7; + + data_size = MIN(data_size, buf_size / (byte_width + 1)); + + for(size_t i = 0; i < data_size; ++i) { + char* p = buf + (i * byte_width); + char sep = !((i + 1) % line_width) ? '\n' : ' '; + snprintf(p, byte_width + 1, "%02X%c", data[i], sep); + } + + buf[buf_size - 1] = '\0'; +} + +static void rpc_debug_app_scene_receive_data_exchange_callback( + const uint8_t* data, + size_t data_size, + void* context) { + RpcDebugApp* app = context; + if(data) { + rpc_debug_app_scene_start_format_hex(data, data_size, app->text_store, TEXT_STORE_SIZE); + } else { + strncpy(app->text_store, "", TEXT_STORE_SIZE); + } + view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange); +} + +void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) { + RpcDebugApp* app = context; + strncpy(app->text_store, "Received data will appear here...", TEXT_STORE_SIZE); + + text_box_set_text(app->text_box, app->text_store); + text_box_set_font(app->text_box, TextBoxFontHex); + + rpc_system_app_set_data_exchange_callback( + app->rpc, rpc_debug_app_scene_receive_data_exchange_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextBox); +} + +bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == RpcDebugAppCustomEventRpcDataExchange) { + notification_message(app->notifications, &sequence_blink_cyan_100); + notification_message(app->notifications, &sequence_display_backlight_on); + text_box_set_text(app->text_box, app->text_store); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_receive_data_exchange_on_exit(void* context) { + RpcDebugApp* app = context; + text_box_reset(app->text_box); + rpc_system_app_set_data_exchange_callback(app->rpc, NULL, NULL); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start.c new file mode 100644 index 000000000..9461649ba --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start.c @@ -0,0 +1,57 @@ +#include "../rpc_debug_app.h" + +enum SubmenuIndex { + SubmenuIndexTestAppError, + SubmenuIndexTestDataExchange, +}; + +static void rpc_debug_app_scene_start_submenu_callback(void* context, uint32_t index) { + RpcDebugApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void rpc_debug_app_scene_start_on_enter(void* context) { + RpcDebugApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Test App Error", + SubmenuIndexTestAppError, + rpc_debug_app_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "Test Data Exchange", + SubmenuIndexTestDataExchange, + rpc_debug_app_scene_start_submenu_callback, + app); + + submenu_set_selected_item(submenu, SubmenuIndexTestAppError); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu); +} + +bool rpc_debug_app_scene_start_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + SceneManager* scene_manager = app->scene_manager; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const uint32_t submenu_index = event.event; + if(submenu_index == SubmenuIndexTestAppError) { + scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestAppError); + consumed = true; + } else if(submenu_index == SubmenuIndexTestDataExchange) { + scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestDataExchange); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_start_on_exit(void* context) { + RpcDebugApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start_dummy.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start_dummy.c new file mode 100644 index 000000000..5a30a079a --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start_dummy.c @@ -0,0 +1,30 @@ +#include "../rpc_debug_app.h" + +void rpc_debug_app_scene_start_dummy_on_enter(void* context) { + RpcDebugApp* app = context; + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 64, + AlignCenter, + AlignCenter, + "This application\nis meant to be run\nin \e#RPC\e# mode.", + false); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewWidget); +} + +bool rpc_debug_app_scene_start_dummy_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + UNUSED(app); + UNUSED(event); + + bool consumed = false; + return consumed; +} + +void rpc_debug_app_scene_start_dummy_on_exit(void* context) { + RpcDebugApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_app_error.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_app_error.c new file mode 100644 index 000000000..865d475ae --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_app_error.c @@ -0,0 +1,57 @@ +#include "../rpc_debug_app.h" + +typedef enum { + SubmenuIndexSetErrorCode, + SubmenuIndexSetErrorText, +} SubmenuIndex; + +static void rpc_debug_app_scene_test_app_error_submenu_callback(void* context, uint32_t index) { + RpcDebugApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void rpc_debug_app_scene_test_app_error_on_enter(void* context) { + RpcDebugApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Set Error Code", + SubmenuIndexSetErrorCode, + rpc_debug_app_scene_test_app_error_submenu_callback, + app); + submenu_add_item( + submenu, + "Set Error Text", + SubmenuIndexSetErrorText, + rpc_debug_app_scene_test_app_error_submenu_callback, + app); + + submenu_set_selected_item(submenu, SubmenuIndexSetErrorCode); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu); +} + +bool rpc_debug_app_scene_test_app_error_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + SceneManager* scene_manager = app->scene_manager; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const uint32_t submenu_index = event.event; + if(submenu_index == SubmenuIndexSetErrorCode) { + scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorCode); + consumed = true; + } else if(submenu_index == SubmenuIndexSetErrorText) { + scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorText); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_test_app_error_on_exit(void* context) { + RpcDebugApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_data_exchange.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_data_exchange.c new file mode 100644 index 000000000..f90f480c2 --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_data_exchange.c @@ -0,0 +1,58 @@ +#include "../rpc_debug_app.h" + +typedef enum { + SubmenuIndexSendData, + SubmenuIndexReceiveData, +} SubmenuIndex; + +static void + rpc_debug_app_scene_test_data_exchange_submenu_callback(void* context, uint32_t index) { + RpcDebugApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void rpc_debug_app_scene_test_data_exchange_on_enter(void* context) { + RpcDebugApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Send Data", + SubmenuIndexSendData, + rpc_debug_app_scene_test_data_exchange_submenu_callback, + app); + submenu_add_item( + submenu, + "Receive Data", + SubmenuIndexReceiveData, + rpc_debug_app_scene_test_data_exchange_submenu_callback, + app); + + submenu_set_selected_item(submenu, SubmenuIndexSendData); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu); +} + +bool rpc_debug_app_scene_test_data_exchange_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + SceneManager* scene_manager = app->scene_manager; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const uint32_t submenu_index = event.event; + if(submenu_index == SubmenuIndexSendData) { + scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputDataExchange); + consumed = true; + } else if(submenu_index == SubmenuIndexReceiveData) { + scene_manager_next_scene(scene_manager, RpcDebugAppSceneReceiveDataExchange); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_test_data_exchange_on_exit(void* context) { + RpcDebugApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/debug/uart_echo/uart_echo.c b/applications/debug/uart_echo/uart_echo.c index 701e5325a..dc1327529 100644 --- a/applications/debug/uart_echo/uart_echo.c +++ b/applications/debug/uart_echo/uart_echo.c @@ -215,30 +215,26 @@ static UartEchoApp* uart_echo_app_alloc() { view_dispatcher_add_view(app->view_dispatcher, 0, app->view); view_dispatcher_switch_to_view(app->view_dispatcher, 0); + app->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 1024, uart_echo_worker, app); + furi_thread_start(app->worker_thread); + // Enable uart listener furi_hal_console_disable(); furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200); furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app); - app->worker_thread = furi_thread_alloc(); - furi_thread_set_name(app->worker_thread, "UsbUartWorker"); - furi_thread_set_stack_size(app->worker_thread, 1024); - furi_thread_set_context(app->worker_thread, app); - furi_thread_set_callback(app->worker_thread, uart_echo_worker); - furi_thread_start(app->worker_thread); - return app; } static void uart_echo_app_free(UartEchoApp* app) { furi_assert(app); + furi_hal_console_enable(); // this will also clear IRQ callback so thread is no longer referenced + furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop); furi_thread_join(app->worker_thread); furi_thread_free(app->worker_thread); - furi_hal_console_enable(); - // Free views view_dispatcher_remove_view(app->view_dispatcher, 0); diff --git a/applications/debug/unit_tests/furi/furi_memmgr_test.c b/applications/debug/unit_tests/furi/furi_memmgr_test.c index b0fd060cf..cf3848747 100644 --- a/applications/debug/unit_tests/furi/furi_memmgr_test.c +++ b/applications/debug/unit_tests/furi/furi_memmgr_test.c @@ -11,7 +11,7 @@ extern size_t memmgr_get_free_heap(void); extern size_t memmgr_get_minimum_free_heap(void); -// current heap managment realization consume: +// current heap management realization consume: // X bytes after allocate and 0 bytes after allocate and free, // where X = sizeof(void*) + sizeof(size_t), look to BlockLink_t const size_t heap_overhead_max_size = sizeof(void*) + sizeof(size_t); @@ -21,7 +21,7 @@ bool heap_equal(size_t heap_size, size_t heap_size_old) { const size_t heap_low = heap_size_old - heap_overhead_max_size; const size_t heap_high = heap_size_old + heap_overhead_max_size; - // not extact, so we must test it against bigger numbers than "overhead size" + // not exact, so we must test it against bigger numbers than "overhead size" const bool result = ((heap_size >= heap_low) && (heap_size <= heap_high)); // debug allocation info diff --git a/applications/debug/unit_tests/furi_hal/furi_hal_tests.c b/applications/debug/unit_tests/furi_hal/furi_hal_tests.c new file mode 100644 index 000000000..e942c5933 --- /dev/null +++ b/applications/debug/unit_tests/furi_hal/furi_hal_tests.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include "../minunit.h" + +#define DATA_SIZE 4 + +static void furi_hal_i2c_int_setup() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); +} + +static void furi_hal_i2c_int_teardown() { + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} + +MU_TEST(furi_hal_i2c_int_1b) { + bool ret = false; + uint8_t data_one = 0; + + // 1 byte: read, write, read + ret = furi_hal_i2c_read_reg_8( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + LP5562_CHANNEL_BLUE_CURRENT_REGISTER, + &data_one, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "0 read_reg_8 failed"); + mu_assert(data_one != 0, "0 invalid data"); + ret = furi_hal_i2c_write_reg_8( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + LP5562_CHANNEL_BLUE_CURRENT_REGISTER, + data_one, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "1 write_reg_8 failed"); + ret = furi_hal_i2c_read_reg_8( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + LP5562_CHANNEL_BLUE_CURRENT_REGISTER, + &data_one, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "2 read_reg_8 failed"); + mu_assert(data_one != 0, "2 invalid data"); +} + +MU_TEST(furi_hal_i2c_int_3b) { + bool ret = false; + uint8_t data_many[DATA_SIZE] = {0}; + + // 3 byte: read, write, read + data_many[0] = LP5562_CHANNEL_BLUE_CURRENT_REGISTER; + ret = furi_hal_i2c_tx( + &furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, 1, LP5562_I2C_TIMEOUT); + mu_assert(ret, "3 tx failed"); + ret = furi_hal_i2c_rx( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + data_many + 1, + DATA_SIZE - 1, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "4 rx failed"); + for(size_t i = 0; i < DATA_SIZE; i++) mu_assert(data_many[i] != 0, "4 invalid data_many"); + + ret = furi_hal_i2c_tx( + &furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, DATA_SIZE, LP5562_I2C_TIMEOUT); + mu_assert(ret, "5 tx failed"); + + ret = furi_hal_i2c_tx( + &furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, 1, LP5562_I2C_TIMEOUT); + mu_assert(ret, "6 tx failed"); + ret = furi_hal_i2c_rx( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + data_many + 1, + DATA_SIZE - 1, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "7 rx failed"); + for(size_t i = 0; i < DATA_SIZE; i++) mu_assert(data_many[i] != 0, "7 invalid data_many"); +} + +MU_TEST(furi_hal_i2c_int_1b_fail) { + bool ret = false; + uint8_t data_one = 0; + + // 1 byte: fail, read, fail, write, fail, read + data_one = 0; + ret = furi_hal_i2c_read_reg_8( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS + 0x10, + LP5562_CHANNEL_BLUE_CURRENT_REGISTER, + &data_one, + LP5562_I2C_TIMEOUT); + mu_assert(!ret, "8 read_reg_8 failed"); + mu_assert(data_one == 0, "8 invalid data"); + ret = furi_hal_i2c_read_reg_8( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + LP5562_CHANNEL_BLUE_CURRENT_REGISTER, + &data_one, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "9 read_reg_8 failed"); + mu_assert(data_one != 0, "9 invalid data"); +} + +MU_TEST_SUITE(furi_hal_i2c_int_suite) { + MU_SUITE_CONFIGURE(&furi_hal_i2c_int_setup, &furi_hal_i2c_int_teardown); + MU_RUN_TEST(furi_hal_i2c_int_1b); + MU_RUN_TEST(furi_hal_i2c_int_3b); + MU_RUN_TEST(furi_hal_i2c_int_1b_fail); +} + +int run_minunit_test_furi_hal() { + MU_RUN_SUITE(furi_hal_i2c_int_suite); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/infrared/infrared_test.c b/applications/debug/unit_tests/infrared/infrared_test.c index 8879c8fc8..2bcb95da8 100644 --- a/applications/debug/unit_tests/infrared/infrared_test.c +++ b/applications/debug/unit_tests/infrared/infrared_test.c @@ -424,6 +424,7 @@ MU_TEST(infrared_test_decoder_mixed) { infrared_test_run_decoder(InfraredProtocolRC5, 5); infrared_test_run_decoder(InfraredProtocolSamsung32, 1); infrared_test_run_decoder(InfraredProtocolSIRC, 3); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 1); } MU_TEST(infrared_test_decoder_nec) { @@ -489,6 +490,15 @@ MU_TEST(infrared_test_encoder_rc6) { infrared_test_run_encoder(InfraredProtocolRC6, 1); } +MU_TEST(infrared_test_decoder_kaseikyo) { + infrared_test_run_decoder(InfraredProtocolKaseikyo, 1); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 2); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 3); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 4); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 5); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 6); +} + MU_TEST(infrared_test_encoder_decoder_all) { infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1); infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1); @@ -498,6 +508,7 @@ MU_TEST(infrared_test_encoder_decoder_all) { infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1); infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1); infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1); + infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1); } MU_TEST_SUITE(infrared_test) { @@ -515,6 +526,7 @@ MU_TEST_SUITE(infrared_test) { MU_RUN_TEST(infrared_test_decoder_nec); MU_RUN_TEST(infrared_test_decoder_samsung32); MU_RUN_TEST(infrared_test_decoder_necext1); + MU_RUN_TEST(infrared_test_decoder_kaseikyo); MU_RUN_TEST(infrared_test_decoder_mixed); MU_RUN_TEST(infrared_test_encoder_decoder_all); } diff --git a/applications/debug/unit_tests/minunit.h b/applications/debug/unit_tests/minunit.h index 17eb7b3f1..69bfba6d9 100644 --- a/applications/debug/unit_tests/minunit.h +++ b/applications/debug/unit_tests/minunit.h @@ -316,7 +316,7 @@ void minunit_print_fail(const char* error); MU__SAFE_BLOCK( \ double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \ minunit_tmp_r = (result); \ - if(fabs(minunit_tmp_e - minunit_tmp_r) > MINUNIT_EPSILON) { \ + if(fabs(minunit_tmp_e - minunit_tmp_r) > (double)MINUNIT_EPSILON) { \ int minunit_significant_figures = 1 - log10(MINUNIT_EPSILON); \ snprintf( \ minunit_last_message, \ diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 8009f6a17..4218482c7 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include @@ -17,6 +19,7 @@ #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") +#define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_dev_test.nfc") static const char* nfc_test_file_type = "Flipper NFC test"; static const uint32_t nfc_test_file_version = 1; @@ -133,7 +136,7 @@ static bool nfc_test_digital_signal_test_encode( ref_timings_sum += ref[i]; if(timings_diff > timing_tolerance) { FURI_LOG_E( - TAG, "Too big differece in %d timings. Ref: %ld, DUT: %ld", i, ref[i], dut[i]); + TAG, "Too big difference in %d timings. Ref: %ld, DUT: %ld", i, ref[i], dut[i]); timing_check_success = false; break; } @@ -287,9 +290,203 @@ MU_TEST(mf_classic_dict_load_test) { furi_record_close(RECORD_STORAGE); } +MU_TEST(nfca_file_test) { + NfcDevice* nfc = nfc_device_alloc(); + mu_assert(nfc != NULL, "nfc_device_data != NULL assert failed\r\n"); + nfc->format = NfcDeviceSaveFormatUid; + + // Fill the UID, sak, ATQA and type + uint8_t uid[7] = {0x04, 0x01, 0x23, 0x45, 0x67, 0x89, 0x00}; + memcpy(nfc->dev_data.nfc_data.uid, uid, 7); + nfc->dev_data.nfc_data.uid_len = 7; + + nfc->dev_data.nfc_data.sak = 0x08; + nfc->dev_data.nfc_data.atqa[0] = 0x00; + nfc->dev_data.nfc_data.atqa[1] = 0x04; + nfc->dev_data.nfc_data.type = FuriHalNfcTypeA; + + // Save the NFC device data to the file + mu_assert( + nfc_device_save(nfc, NFC_TEST_NFC_DEV_PATH), "nfc_device_save == true assert failed\r\n"); + nfc_device_free(nfc); + + // Load the NFC device data from the file + NfcDevice* nfc_validate = nfc_device_alloc(); + mu_assert( + nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, true), + "nfc_device_load == true assert failed\r\n"); + + // Check the UID, sak, ATQA and type + mu_assert(memcmp(nfc_validate->dev_data.nfc_data.uid, uid, 7) == 0, "uid assert failed\r\n"); + mu_assert(nfc_validate->dev_data.nfc_data.sak == 0x08, "sak == 0x08 assert failed\r\n"); + mu_assert( + nfc_validate->dev_data.nfc_data.atqa[0] == 0x00, "atqa[0] == 0x00 assert failed\r\n"); + mu_assert( + nfc_validate->dev_data.nfc_data.atqa[1] == 0x04, "atqa[1] == 0x04 assert failed\r\n"); + mu_assert( + nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA, + "type == FuriHalNfcTypeA assert failed\r\n"); + nfc_device_free(nfc_validate); +} + +static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) { + NfcDevice* nfc_dev = nfc_device_alloc(); + mu_assert(nfc_dev != NULL, "nfc_device_data != NULL assert failed\r\n"); + nfc_dev->format = NfcDeviceSaveFormatMifareClassic; + + // Create a test file + nfc_generate_mf_classic(&nfc_dev->dev_data, uid_len, type); + + // Get the uid from generated MFC + uint8_t uid[7] = {0}; + memcpy(uid, nfc_dev->dev_data.nfc_data.uid, uid_len); + uint8_t sak = nfc_dev->dev_data.nfc_data.sak; + uint8_t atqa[2] = {}; + memcpy(atqa, nfc_dev->dev_data.nfc_data.atqa, 2); + + MfClassicData* mf_data = &nfc_dev->dev_data.mf_classic_data; + // Check the manufacturer block (should be uid[uid_len] + 0xFF[rest]) + uint8_t manufacturer_block[16] = {0}; + memcpy(manufacturer_block, nfc_dev->dev_data.mf_classic_data.block[0].value, 16); + mu_assert( + memcmp(manufacturer_block, uid, uid_len) == 0, + "manufacturer_block uid doesn't match the file\r\n"); + for(uint8_t i = uid_len; i < 16; i++) { + mu_assert( + manufacturer_block[i] == 0xFF, "manufacturer_block[i] == 0xFF assert failed\r\n"); + } + + // Reference sector trailers (should be 0xFF[6] + 0xFF + 0x07 + 0x80 + 0x69 + 0xFF[6]) + uint8_t sector_trailer[16] = { + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x07, + 0x80, + 0x69, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF}; + // Reference block data + uint8_t block_data[16] = {}; + memset(block_data, 0xff, sizeof(block_data)); + uint16_t total_blocks = mf_classic_get_total_block_num(type); + for(size_t i = 1; i < total_blocks; i++) { + if(mf_classic_is_sector_trailer(i)) { + mu_assert( + memcmp(mf_data->block[i].value, sector_trailer, 16) == 0, + "Failed sector trailer compare"); + } else { + mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare"); + } + } + // Save the NFC device data to the file + mu_assert( + nfc_device_save(nfc_dev, NFC_TEST_NFC_DEV_PATH), + "nfc_device_save == true assert failed\r\n"); + // Verify that key cache is saved + FuriString* key_cache_name = furi_string_alloc(); + furi_string_set_str(key_cache_name, "/ext/nfc/.cache/"); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(key_cache_name, "%02X", uid[i]); + } + furi_string_cat_printf(key_cache_name, ".keys"); + mu_assert( + storage_common_stat(nfc_dev->storage, furi_string_get_cstr(key_cache_name), NULL) == + FSE_OK, + "Key cache file save failed"); + nfc_device_free(nfc_dev); + + // Load the NFC device data from the file + NfcDevice* nfc_validate = nfc_device_alloc(); + mu_assert(nfc_validate, "Nfc device alloc assert"); + mu_assert( + nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, false), + "nfc_device_load == true assert failed\r\n"); + + // Check the UID, sak, ATQA and type + mu_assert( + memcmp(nfc_validate->dev_data.nfc_data.uid, uid, uid_len) == 0, + "uid compare assert failed\r\n"); + mu_assert(nfc_validate->dev_data.nfc_data.sak == sak, "sak compare assert failed\r\n"); + mu_assert( + memcmp(nfc_validate->dev_data.nfc_data.atqa, atqa, 2) == 0, + "atqa compare assert failed\r\n"); + mu_assert( + nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA, + "type == FuriHalNfcTypeA assert failed\r\n"); + + // Check the manufacturer block + mu_assert( + memcmp(nfc_validate->dev_data.mf_classic_data.block[0].value, manufacturer_block, 16) == 0, + "manufacturer_block assert failed\r\n"); + // Check other blocks + for(size_t i = 1; i < total_blocks; i++) { + if(mf_classic_is_sector_trailer(i)) { + mu_assert( + memcmp(mf_data->block[i].value, sector_trailer, 16) == 0, + "Failed sector trailer compare"); + } else { + mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare"); + } + } + nfc_device_free(nfc_validate); + + // Check saved key cache + NfcDevice* nfc_keys = nfc_device_alloc(); + mu_assert(nfc_validate, "Nfc device alloc assert"); + nfc_keys->dev_data.nfc_data.uid_len = uid_len; + memcpy(nfc_keys->dev_data.nfc_data.uid, uid, uid_len); + mu_assert(nfc_device_load_key_cache(nfc_keys), "Failed to load key cache"); + uint8_t total_sec = mf_classic_get_total_sectors_num(type); + uint8_t default_key[6] = {}; + memset(default_key, 0xff, 6); + for(size_t i = 0; i < total_sec; i++) { + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(&nfc_keys->dev_data.mf_classic_data, i); + mu_assert(memcmp(sec_tr->key_a, default_key, 6) == 0, "Failed key compare"); + mu_assert(memcmp(sec_tr->key_b, default_key, 6) == 0, "Failed key compare"); + } + + // Delete key cache file + mu_assert( + storage_common_remove(nfc_keys->storage, furi_string_get_cstr(key_cache_name)) == FSE_OK, + "Failed to remove key cache file"); + furi_string_free(key_cache_name); + nfc_device_free(nfc_keys); +} + +MU_TEST(mf_classic_1k_4b_file_test) { + mf_classic_generator_test(4, MfClassicType1k); +} + +MU_TEST(mf_classic_4k_4b_file_test) { + mf_classic_generator_test(4, MfClassicType4k); +} + +MU_TEST(mf_classic_1k_7b_file_test) { + mf_classic_generator_test(7, MfClassicType1k); +} + +MU_TEST(mf_classic_4k_7b_file_test) { + mf_classic_generator_test(7, MfClassicType4k); +} + MU_TEST_SUITE(nfc) { nfc_test_alloc(); + MU_RUN_TEST(nfca_file_test); + MU_RUN_TEST(mf_classic_1k_4b_file_test); + MU_RUN_TEST(mf_classic_4k_4b_file_test); + MU_RUN_TEST(mf_classic_1k_7b_file_test); + MU_RUN_TEST(mf_classic_4k_7b_file_test); MU_RUN_TEST(nfc_digital_signal_test); MU_RUN_TEST(mf_classic_dict_test); MU_RUN_TEST(mf_classic_dict_load_test); diff --git a/applications/debug/unit_tests/power/power_test.c b/applications/debug/unit_tests/power/power_test.c new file mode 100644 index 000000000..ce2c7aad7 --- /dev/null +++ b/applications/debug/unit_tests/power/power_test.c @@ -0,0 +1,62 @@ +#include +#include +#include "../minunit.h" + +static void power_test_deinit(void) { + // Try to reset to default charging voltage + furi_hal_power_set_battery_charging_voltage(4.208f); +} + +MU_TEST(test_power_charge_voltage_exact) { + // Power of 16mV charge voltages get applied exactly + // (bq25896 charge controller works in 16mV increments) + // + // This test may need adapted if other charge controllers are used in the future. + for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) { + float charge_volt = (float)charge_mv / 1000.0f; + furi_hal_power_set_battery_charging_voltage(charge_volt); + mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charging_voltage()); + } +} + +MU_TEST(test_power_charge_voltage_floating_imprecision) { + // 4.016f should act as 4.016 V, even with floating point imprecision + furi_hal_power_set_battery_charging_voltage(4.016f); + mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charging_voltage()); +} + +MU_TEST(test_power_charge_voltage_inexact) { + // Charge voltages that are not power of 16mV get truncated down + furi_hal_power_set_battery_charging_voltage(3.841f); + mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage()); + + furi_hal_power_set_battery_charging_voltage(3.900f); + mu_assert_double_eq(3.888, furi_hal_power_get_battery_charging_voltage()); + + furi_hal_power_set_battery_charging_voltage(4.200f); + mu_assert_double_eq(4.192, furi_hal_power_get_battery_charging_voltage()); +} + +MU_TEST(test_power_charge_voltage_invalid_clamped) { + // Out-of-range charge voltages get clamped to 3.840 V and 4.208 V + furi_hal_power_set_battery_charging_voltage(3.808f); + mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage()); + + // NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an + // unhappy battery if this fails. + furi_hal_power_set_battery_charging_voltage(4.240f); + mu_assert_double_eq(4.208, furi_hal_power_get_battery_charging_voltage()); +} + +MU_TEST_SUITE(test_power_suite) { + MU_RUN_TEST(test_power_charge_voltage_exact); + MU_RUN_TEST(test_power_charge_voltage_floating_imprecision); + MU_RUN_TEST(test_power_charge_voltage_inexact); + MU_RUN_TEST(test_power_charge_voltage_invalid_clamped); + power_test_deinit(); +} + +int run_minunit_test_power() { + MU_RUN_SUITE(test_power_suite); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/storage/storage_test.c index 7c1c669ff..115009701 100644 --- a/applications/debug/unit_tests/storage/storage_test.c +++ b/applications/debug/unit_tests/storage/storage_test.c @@ -43,11 +43,8 @@ MU_TEST(storage_file_open_lock) { File* file = storage_file_alloc(storage); // file_locker thread start - FuriThread* locker_thread = furi_thread_alloc(); - furi_thread_set_name(locker_thread, "StorageFileLocker"); - furi_thread_set_stack_size(locker_thread, 2048); - furi_thread_set_context(locker_thread, semaphore); - furi_thread_set_callback(locker_thread, storage_file_locker); + FuriThread* locker_thread = + furi_thread_alloc_ex("StorageFileLocker", 2048, storage_file_locker, semaphore); furi_thread_start(locker_thread); // wait for file lock @@ -133,11 +130,8 @@ MU_TEST(storage_dir_open_lock) { File* file = storage_file_alloc(storage); // file_locker thread start - FuriThread* locker_thread = furi_thread_alloc(); - furi_thread_set_name(locker_thread, "StorageDirLocker"); - furi_thread_set_stack_size(locker_thread, 2048); - furi_thread_set_context(locker_thread, semaphore); - furi_thread_set_callback(locker_thread, storage_dir_locker); + FuriThread* locker_thread = + furi_thread_alloc_ex("StorageDirLocker", 2048, storage_dir_locker, semaphore); furi_thread_start(locker_thread); // wait for dir lock diff --git a/applications/debug/unit_tests/stream/stream_test.c b/applications/debug/unit_tests/stream/stream_test.c index c04e119c6..802e34025 100644 --- a/applications/debug/unit_tests/stream/stream_test.c +++ b/applications/debug/unit_tests/stream/stream_test.c @@ -219,21 +219,21 @@ MU_TEST_1(stream_composite_subtest, Stream* stream) { mu_check(stream_eof(stream)); mu_assert_int_eq(0, stream_tell(stream)); - // insert formated string at the end of stream + // insert formatted string at the end of stream // "" -> "dio666" mu_check(stream_insert_format(stream, "%s%d", "dio", 666)); mu_assert_int_eq(6, stream_size(stream)); mu_check(stream_eof(stream)); mu_assert_int_eq(6, stream_tell(stream)); - // insert formated string at the end of stream + // insert formatted string at the end of stream // "dio666" -> "dio666zlo555" mu_check(stream_insert_format(stream, "%s%d", "zlo", 555)); mu_assert_int_eq(12, stream_size(stream)); mu_check(stream_eof(stream)); mu_assert_int_eq(12, stream_tell(stream)); - // insert formated string at the 6 pos + // insert formatted string at the 6 pos // "dio666" -> "dio666baba13zlo555" mu_check(stream_seek(stream, 6, StreamOffsetFromStart)); mu_check(stream_insert_format(stream, "%s%d", "baba", 13)); diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index dbe03d88c..fe834c606 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 232 +#define TEST_RANDOM_COUNT_PARSE 244 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -209,6 +209,149 @@ MU_TEST(subghz_keystore_test) { "Test keystore error"); } +typedef enum { + SubGhzHalAsyncTxTestTypeNormal, + SubGhzHalAsyncTxTestTypeInvalidStart, + SubGhzHalAsyncTxTestTypeInvalidMid, + SubGhzHalAsyncTxTestTypeInvalidEnd, + SubGhzHalAsyncTxTestTypeResetStart, + SubGhzHalAsyncTxTestTypeResetMid, + SubGhzHalAsyncTxTestTypeResetEnd, +} SubGhzHalAsyncTxTestType; + +typedef struct { + SubGhzHalAsyncTxTestType type; + size_t pos; +} SubGhzHalAsyncTxTest; + +#define SUBGHZ_HAL_TEST_DURATION 1 + +static LevelDuration subghz_hal_async_tx_test_yield(void* context) { + SubGhzHalAsyncTxTest* test = context; + bool is_odd = test->pos % 2; + + if(test->type == SubGhzHalAsyncTxTestTypeNormal) { + if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidStart) { + if(test->pos == 0) { + test->pos++; + return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) { + if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { + test->pos++; + return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) { + if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { + test->pos++; + return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else if(test->type == SubGhzHalAsyncTxTestTypeResetStart) { + if(test->pos == 0) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) { + if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { + test->pos++; + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) { + if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { + test->pos++; + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else { + furi_crash("Programming error"); + } +} + +bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) { + SubGhzHalAsyncTxTest test = {0}; + test.type = type; + furi_hal_subghz_reset(); + furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_set_frequency_and_path(433920000); + + furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test); + while(!furi_hal_subghz_is_async_tx_complete()) { + furi_delay_ms(10); + } + furi_hal_subghz_stop_async_tx(); + furi_hal_subghz_sleep(); + + return true; +} + +MU_TEST(subghz_hal_async_tx_test) { + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeNormal), + "Test furi_hal_async_tx normal"); + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidStart), + "Test furi_hal_async_tx invalid start"); + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidMid), + "Test furi_hal_async_tx invalid mid"); + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidEnd), + "Test furi_hal_async_tx invalid end"); + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetStart), + "Test furi_hal_async_tx reset start"); + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetMid), + "Test furi_hal_async_tx reset mid"); + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetEnd), + "Test furi_hal_async_tx reset end"); +} + //test decoders MU_TEST(subghz_decoder_came_atomo_test) { mu_assert( @@ -437,6 +580,13 @@ MU_TEST(subghz_decoder_clemsa_test) { "Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n"); } +MU_TEST(subghz_decoder_ansonic_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/ansonic_raw.sub"), SUBGHZ_PROTOCOL_ANSONIC_NAME), + "Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -558,6 +708,12 @@ MU_TEST(subghz_encoder_clemsa_test) { "Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n"); } +MU_TEST(subghz_encoder_ansonic_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/ansonic.sub")), + "Test encoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n"); +} + MU_TEST(subghz_random_test) { mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); } @@ -566,6 +722,8 @@ MU_TEST_SUITE(subghz) { subghz_test_init(); MU_RUN_TEST(subghz_keystore_test); + MU_RUN_TEST(subghz_hal_async_tx_test); + MU_RUN_TEST(subghz_decoder_came_atomo_test); MU_RUN_TEST(subghz_decoder_came_test); MU_RUN_TEST(subghz_decoder_came_twee_test); @@ -598,6 +756,7 @@ 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_ansonic_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); @@ -619,6 +778,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_magellan_test); MU_RUN_TEST(subghz_encoder_intertechno_v3_test); MU_RUN_TEST(subghz_encoder_clemsa_test); + MU_RUN_TEST(subghz_encoder_ansonic_test); MU_RUN_TEST(subghz_random_test); subghz_test_deinit(); diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index 2009d4a5b..65fa23c01 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -9,6 +9,7 @@ #define TAG "UnitTests" int run_minunit_test_furi(); +int run_minunit_test_furi_hal(); int run_minunit_test_furi_string(); int run_minunit_test_infrared(); int run_minunit_test_rpc(); @@ -18,6 +19,7 @@ int run_minunit_test_stream(); int run_minunit_test_storage(); int run_minunit_test_subghz(); int run_minunit_test_dirwalk(); +int run_minunit_test_power(); int run_minunit_test_protocol_dict(); int run_minunit_test_lfrfid_protocols(); int run_minunit_test_nfc(); @@ -32,6 +34,7 @@ typedef struct { const UnitTest unit_tests[] = { {.name = "furi", .entry = run_minunit_test_furi}, + {.name = "furi_hal", .entry = run_minunit_test_furi_hal}, {.name = "furi_string", .entry = run_minunit_test_furi_string}, {.name = "storage", .entry = run_minunit_test_storage}, {.name = "stream", .entry = run_minunit_test_stream}, @@ -42,6 +45,7 @@ const UnitTest unit_tests[] = { {.name = "subghz", .entry = run_minunit_test_subghz}, {.name = "infrared", .entry = run_minunit_test_infrared}, {.name = "nfc", .entry = run_minunit_test_nfc}, + {.name = "power", .entry = run_minunit_test_power}, {.name = "protocol_dict", .entry = run_minunit_test_protocol_dict}, {.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols}, {.name = "bit_lib", .entry = run_minunit_test_bit_lib}, diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 9689454ba..6c8c9633a 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -80,10 +80,12 @@ static void archive_file_browser_set_path( ArchiveBrowserView* browser, FuriString* path, const char* filter_ext, - bool skip_assets) { + bool skip_assets, + bool hide_dot_files) { furi_assert(browser); if(!browser->worker_running) { - browser->worker = file_browser_worker_alloc(path, filter_ext, skip_assets); + browser->worker = + file_browser_worker_alloc(path, NULL, filter_ext, skip_assets, hide_dot_files); file_browser_worker_set_callback_context(browser->worker, browser); file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb); file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb); @@ -92,7 +94,8 @@ static void archive_file_browser_set_path( browser->worker_running = true; } else { furi_assert(browser->worker); - file_browser_worker_set_config(browser->worker, path, filter_ext, skip_assets); + file_browser_worker_set_config( + browser->worker, path, filter_ext, skip_assets, hide_dot_files); } } @@ -265,8 +268,7 @@ void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) { offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1; } if(offset_new > 0) { - offset_new = - CLAMP(offset_new, (int32_t)model->item_cnt - FILE_LIST_BUF_LEN, 0); + offset_new = CLAMP(offset_new, (int32_t)model->item_cnt, 0); } else { offset_new = 0; } @@ -473,8 +475,10 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { tab = archive_get_tab(browser); if(archive_is_dir_exists(browser->path)) { bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true; + // Hide dot files everywhere except Browser + bool hide_dot_files = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true; archive_file_browser_set_path( - browser, browser->path, archive_get_tab_ext(tab), skip_assets); + browser, browser->path, archive_get_tab_ext(tab), skip_assets, hide_dot_files); tab_empty = false; // Empty check will be performed later } } diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 519a34a2d..09ffea1f9 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -3,9 +3,9 @@ #include "../archive_i.h" #include -#define TAB_RIGHT InputKeyRight // Default tab swith direction +#define TAB_RIGHT InputKeyRight // Default tab switch direction #define TAB_DEFAULT ArchiveTabFavorites // Start tab -#define FILE_LIST_BUF_LEN 100 +#define FILE_LIST_BUF_LEN 50 static const char* tab_default_paths[] = { [ArchiveTabFavorites] = "/app:favorites", diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index 9dc671617..04f4dcc3a 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -133,7 +133,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { case ArchiveBrowserEventFileMenuRename: if(favorites) { browser->callback(ArchiveBrowserEventEnterFavMove, browser->context); - } else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) { + } else if(selected->is_app == false) { archive_show_file_menu(browser, false); scene_manager_set_scene_state( archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); diff --git a/applications/main/archive/scenes/archive_scene_rename.c b/applications/main/archive/scenes/archive_scene_rename.c index 1451428bd..37f860a9a 100644 --- a/applications/main/archive/scenes/archive_scene_rename.c +++ b/applications/main/archive/scenes/archive_scene_rename.c @@ -57,9 +57,11 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { ArchiveFile_t* file = archive_get_current_file(archive->browser); FuriString* path_dst; + path_dst = furi_string_alloc(); path_extract_dirname(path_src, path_dst); - furi_string_cat_printf(path_dst, "/%s%s", archive->text_store, known_ext[file->type]); + furi_string_cat_printf( + path_dst, "/%s%s", archive->text_store, archive->file_extension); storage_common_rename(fs_api, path_src, furi_string_get_cstr(path_dst)); furi_record_close(RECORD_STORAGE); diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index a2e219b95..65be42135 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -65,7 +65,6 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { if(!archive_is_known_app(selected->type)) { furi_string_set(menu[0], "---"); furi_string_set(menu[1], "---"); - furi_string_set(menu[2], "---"); } else { if(model->tab_idx == ArchiveTabFavorites) { furi_string_set(menu[2], "Move"); diff --git a/applications/main/bad_usb/application.fam b/applications/main/bad_usb/application.fam index 4da34f0de..2442dd3aa 100644 --- a/applications/main/bad_usb/application.fam +++ b/applications/main/bad_usb/application.fam @@ -11,4 +11,5 @@ App( stack_size=2 * 1024, icon="A_BadUsb_14", order=70, + fap_libs=["assets"], ) diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 33b3f5030..aa465351e 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -237,12 +237,8 @@ static int32_t const char* line_tmp = furi_string_get_cstr(line); bool state = false; - for(uint32_t i = 0; i < line_len; i++) { - if((line_tmp[i] != ' ') && (line_tmp[i] != '\t') && (line_tmp[i] != '\n')) { - line_tmp = &line_tmp[i]; - break; // Skip spaces and tabs - } - if(i == line_len - 1) return SCRIPT_STATE_NEXT_LINE; // Skip empty lines + if(line_len == 0) { + return SCRIPT_STATE_NEXT_LINE; // Skip empty lines } FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); @@ -338,10 +334,6 @@ static int32_t furi_hal_hid_kb_release(key); return (0); } - if(error != NULL) { - strncpy(error, "Unknown error", error_len); - } - return SCRIPT_STATE_ERROR; } static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) { @@ -454,10 +446,12 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil bad_usb->st.line_cur++; bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1); bad_usb->buf_start = i + 1; + furi_string_trim(bad_usb->line); delay_val = ducky_parse_line( bad_usb, bad_usb->line, bad_usb->st.error, sizeof(bad_usb->st.error)); - - if(delay_val < 0) { + if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line + return 0; + } else if(delay_val < 0) { bad_usb->st.error_line = bad_usb->st.line_cur; FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur); return SCRIPT_STATE_ERROR; @@ -656,19 +650,14 @@ static int32_t bad_usb_worker(void* context) { BadUsbScript* bad_usb_script_open(FuriString* file_path) { furi_assert(file_path); - BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); + BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); //-V773 bad_usb->file_path = furi_string_alloc(); furi_string_set(bad_usb->file_path, file_path); bad_usb->st.state = BadUsbStateInit; bad_usb->st.error[0] = '\0'; - bad_usb->thread = furi_thread_alloc(); - furi_thread_set_name(bad_usb->thread, "BadUsbWorker"); - furi_thread_set_stack_size(bad_usb->thread, 2048); - furi_thread_set_context(bad_usb->thread, bad_usb); - furi_thread_set_callback(bad_usb->thread, bad_usb_worker); - + bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb); furi_thread_start(bad_usb->thread); return bad_usb; } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c index c562fc2de..9264eb976 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -1,12 +1,14 @@ #include "../bad_usb_app_i.h" #include "furi_hal_power.h" #include "furi_hal_usb.h" +#include static bool bad_usb_file_select(BadUsbApp* bad_usb) { furi_assert(bad_usb); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, BAD_USB_APP_EXTENSION, &I_badusb_10px); + browser_options.base_path = BAD_USB_APP_PATH_FOLDER; // Input events and views are managed by file_browser bool res = dialog_file_browser_show( diff --git a/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp b/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp index f8adbf9d8..e845ed008 100644 --- a/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp +++ b/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp @@ -31,7 +31,7 @@ bool elf_resolve_from_hashtable(const char* name, Elf32_Addr* address) { auto find_res = std::lower_bound(elf_api_table.cbegin(), elf_api_table.cend(), key); if((find_res == elf_api_table.cend() || (find_res->hash != gnu_sym_hash))) { - FURI_LOG_W(TAG, "Cant find symbol '%s' (hash %lx)!", name, gnu_sym_hash); + FURI_LOG_W(TAG, "Can't find symbol '%s' (hash %lx)!", name, gnu_sym_hash); result = false; } else { result = true; diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index 9b4c92335..901866746 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -148,6 +148,7 @@ static bool fap_loader_select_app(FapLoader* loader) { .hide_ext = true, .item_loader_callback = fap_loader_item_callback, .item_loader_context = loader, + .base_path = EXT_PATH("apps"), }; return dialog_file_browser_show( @@ -155,7 +156,7 @@ static bool fap_loader_select_app(FapLoader* loader) { } static FapLoader* fap_loader_alloc(const char* path) { - FapLoader* loader = malloc(sizeof(FapLoader)); + FapLoader* loader = malloc(sizeof(FapLoader)); //-V773 loader->fap_path = furi_string_alloc_set(path); loader->storage = furi_record_open(RECORD_STORAGE); loader->dialogs = furi_record_open(RECORD_DIALOGS); diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index 64f8db5b0..efeb8b6fe 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -8,4 +8,5 @@ App( stack_size=1 * 1024, icon="A_GPIO_14", order=50, + fap_libs=["assets"], ) diff --git a/applications/main/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h index 85c5c332e..8f805891b 100644 --- a/applications/main/gpio/gpio_app_i.h +++ b/applications/main/gpio/gpio_app_i.h @@ -29,6 +29,7 @@ struct GpioApp { GpioTest* gpio_test; GpioUsbUart* gpio_usb_uart; UsbUartBridge* usb_uart_bridge; + UsbUartConfig* usb_uart_cfg; }; typedef enum { diff --git a/applications/main/gpio/gpio_custom_event.h b/applications/main/gpio/gpio_custom_event.h index 2bf3e5a8b..72b8feccd 100644 --- a/applications/main/gpio/gpio_custom_event.h +++ b/applications/main/gpio/gpio_custom_event.h @@ -9,4 +9,5 @@ typedef enum { GpioCustomEventErrorBack, GpioUsbUartEventConfig, + GpioUsbUartEventConfigSet, } GpioCustomEvent; 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 c114d79a2..55b04ed67 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c @@ -9,8 +9,6 @@ typedef enum { UsbUartLineIndexFlow, } LineIndex; -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", "16,15"}; @@ -28,8 +26,14 @@ static const uint32_t baudrate_list[] = { }; bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); + GpioApp* app = context; + furi_assert(app); + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GpioUsbUartEventConfigSet) { + usb_uart_set_config(app->usb_uart_bridge, app->usb_uart_cfg); + return true; + } + } return false; } @@ -38,55 +42,59 @@ void line_ensure_flow_invariant(GpioApp* app) { // 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; + uint8_t available_flow_pins = app->usb_uart_cfg->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); + if(app->usb_uart_cfg->flow_pins >= available_flow_pins) { + app->usb_uart_cfg->flow_pins = 0; - variable_item_set_current_value_index(item, cfg_set->flow_pins); - variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); + variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins); + variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]); } } static void line_vcp_cb(VariableItem* item) { GpioApp* app = variable_item_get_context(item); + furi_assert(app); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, vcp_ch[index]); - cfg_set->vcp_ch = index; - usb_uart_set_config(app->usb_uart_bridge, cfg_set); + app->usb_uart_cfg->vcp_ch = index; + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); } static void line_port_cb(VariableItem* item) { GpioApp* app = variable_item_get_context(item); + furi_assert(app); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, uart_ch[index]); if(index == 0) - cfg_set->uart_ch = FuriHalUartIdUSART1; + app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1; else if(index == 1) - cfg_set->uart_ch = FuriHalUartIdLPUART1; - usb_uart_set_config(app->usb_uart_bridge, cfg_set); + app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1; + line_ensure_flow_invariant(app); + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); } static void line_flow_cb(VariableItem* item) { GpioApp* app = variable_item_get_context(item); + furi_assert(app); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, flow_pins[index]); - cfg_set->flow_pins = index; - usb_uart_set_config(app->usb_uart_bridge, cfg_set); + app->usb_uart_cfg->flow_pins = index; + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); } static void line_baudrate_cb(VariableItem* item) { GpioApp* app = variable_item_get_context(item); + furi_assert(app); uint8_t index = variable_item_get_current_value_index(item); char br_text[8]; @@ -94,28 +102,29 @@ static void line_baudrate_cb(VariableItem* item) { if(index > 0) { snprintf(br_text, 7, "%lu", baudrate_list[index - 1]); variable_item_set_current_value_text(item, br_text); - cfg_set->baudrate = baudrate_list[index - 1]; + app->usb_uart_cfg->baudrate = baudrate_list[index - 1]; } else { variable_item_set_current_value_text(item, baudrate_mode[index]); - cfg_set->baudrate = 0; + app->usb_uart_cfg->baudrate = 0; } - cfg_set->baudrate_mode = index; - usb_uart_set_config(app->usb_uart_bridge, cfg_set); + app->usb_uart_cfg->baudrate_mode = index; + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); } void gpio_scene_usb_uart_cfg_on_enter(void* context) { GpioApp* app = context; + furi_assert(app); VariableItemList* var_item_list = app->var_item_list; - cfg_set = malloc(sizeof(UsbUartConfig)); - usb_uart_get_config(app->usb_uart_bridge, cfg_set); + app->usb_uart_cfg = malloc(sizeof(UsbUartConfig)); + usb_uart_get_config(app->usb_uart_bridge, app->usb_uart_cfg); VariableItem* item; char br_text[8]; item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app); - variable_item_set_current_value_index(item, cfg_set->vcp_ch); - variable_item_set_current_value_text(item, vcp_ch[cfg_set->vcp_ch]); + variable_item_set_current_value_index(item, app->usb_uart_cfg->vcp_ch); + variable_item_set_current_value_text(item, vcp_ch[app->usb_uart_cfg->vcp_ch]); item = variable_item_list_add( var_item_list, @@ -123,22 +132,23 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) { sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1, line_baudrate_cb, app); - variable_item_set_current_value_index(item, cfg_set->baudrate_mode); - if(cfg_set->baudrate_mode > 0) { - snprintf(br_text, 7, "%lu", baudrate_list[cfg_set->baudrate_mode - 1]); + variable_item_set_current_value_index(item, app->usb_uart_cfg->baudrate_mode); + if(app->usb_uart_cfg->baudrate_mode > 0) { + snprintf(br_text, 7, "%lu", baudrate_list[app->usb_uart_cfg->baudrate_mode - 1]); variable_item_set_current_value_text(item, br_text); } else { - variable_item_set_current_value_text(item, baudrate_mode[cfg_set->baudrate_mode]); + variable_item_set_current_value_text( + item, baudrate_mode[app->usb_uart_cfg->baudrate_mode]); } item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app); - variable_item_set_current_value_index(item, cfg_set->uart_ch); - variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]); + variable_item_set_current_value_index(item, app->usb_uart_cfg->uart_ch); + variable_item_set_current_value_text(item, uart_ch[app->usb_uart_cfg->uart_ch]); 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]); + variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins); + variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]); app->var_item_flow = item; line_ensure_flow_invariant(app); @@ -155,5 +165,5 @@ void gpio_scene_usb_uart_cfg_on_exit(void* context) { GpioAppViewUsbUartCfg, variable_item_list_get_selected_item_index(app->var_item_list)); variable_item_list_reset(app->var_item_list); - free(cfg_set); + free(app->usb_uart_cfg); } diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c index a5caceafa..1a82dbdc2 100644 --- a/applications/main/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -3,6 +3,7 @@ #include #include "usb_cdc.h" #include "cli/cli_vcp.h" +#include #include "cli/cli.h" #define USB_CDC_PKT_LEN CDC_DATA_SZ @@ -51,6 +52,8 @@ struct UsbUartBridge { UsbUartState st; + FuriApiLock cfg_lock; + uint8_t rx_buf[USB_CDC_PKT_LEN]; }; @@ -159,11 +162,8 @@ static int32_t usb_uart_worker(void* context) { usb_uart->tx_sem = furi_semaphore_alloc(1, 1); usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - usb_uart->tx_thread = furi_thread_alloc(); - furi_thread_set_name(usb_uart->tx_thread, "UsbUartTxWorker"); - furi_thread_set_stack_size(usb_uart->tx_thread, 512); - furi_thread_set_context(usb_uart->tx_thread, usb_uart); - furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread); + usb_uart->tx_thread = + furi_thread_alloc_ex("UsbUartTxWorker", 512, usb_uart_tx_thread, usb_uart); usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch); usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch); @@ -184,7 +184,7 @@ static int32_t usb_uart_worker(void* context) { while(1) { uint32_t events = furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); - furi_check((events & FuriFlagError) == 0); + furi_check(!(events & FuriFlagError)); if(events & WorkerEvtStop) break; if(events & WorkerEvtRxDone) { size_t len = furi_stream_buffer_receive( @@ -247,6 +247,7 @@ static int32_t usb_uart_worker(void* context) { usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins; events |= WorkerEvtCtrlLineSet; } + api_lock_unlock(usb_uart->cfg_lock); } if(events & WorkerEvtLineCfgSet) { if(usb_uart->cfg.baudrate == 0) @@ -288,7 +289,7 @@ static int32_t usb_uart_tx_thread(void* context) { while(1) { uint32_t events = furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever); - furi_check((events & FuriFlagError) == 0); + furi_check(!(events & FuriFlagError)); if(events & WorkerEvtTxStop) break; if(events & WorkerEvtCdcRx) { furi_check(furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk); @@ -338,11 +339,7 @@ UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) { memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); - usb_uart->thread = furi_thread_alloc(); - furi_thread_set_name(usb_uart->thread, "UsbUartWorker"); - furi_thread_set_stack_size(usb_uart->thread, 1024); - furi_thread_set_context(usb_uart->thread, usb_uart); - furi_thread_set_callback(usb_uart->thread, usb_uart_worker); + usb_uart->thread = furi_thread_alloc_ex("UsbUartWorker", 1024, usb_uart_worker, usb_uart); furi_thread_start(usb_uart->thread); return usb_uart; @@ -359,8 +356,10 @@ void usb_uart_disable(UsbUartBridge* usb_uart) { void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) { furi_assert(usb_uart); furi_assert(cfg); + usb_uart->cfg_lock = api_lock_alloc_locked(); memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange); + api_lock_wait_unlock_and_free(usb_uart->cfg_lock); } void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) { diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index 0bc6f8a9b..77bb9a33c 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -12,6 +12,7 @@ App( icon="A_iButton_14", stack_size=2 * 1024, order=60, + fap_libs=["assets"], ) App( diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index b6d8361b3..b7c8223b0 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -218,6 +218,7 @@ void ibutton_free(iButton* ibutton) { bool ibutton_file_select(iButton* ibutton) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px); + browser_options.base_path = IBUTTON_APP_FOLDER; bool success = dialog_file_browser_show( ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options); diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c index 8b7d9dfc0..9ff165e4a 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c @@ -39,10 +39,5 @@ void ibutton_scene_delete_success_on_exit(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - - popup_disable_timeout(popup); - popup_set_context(popup, NULL); - popup_set_callback(popup, NULL); + popup_reset(popup); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_success.c b/applications/main/ibutton/scenes/ibutton_scene_save_success.c index e0b9b3c47..8b16d2929 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_success.c @@ -39,10 +39,5 @@ void ibutton_scene_save_success_on_exit(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - - popup_disable_timeout(popup); - popup_set_context(popup, NULL); - popup_set_callback(popup, NULL); + popup_reset(popup); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_write_success.c b/applications/main/ibutton/scenes/ibutton_scene_write_success.c index 3acb1dea0..17cd53d08 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_write_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_write_success.c @@ -43,10 +43,5 @@ void ibutton_scene_write_success_on_exit(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - - popup_disable_timeout(popup); - popup_set_context(popup, NULL); - popup_set_callback(popup, NULL); + popup_reset(popup); } diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index 6f76ed429..9c5eaf392 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -12,6 +12,7 @@ App( icon="A_Infrared_14", stack_size=3 * 1024, order=40, + fap_libs=["assets"], ) App( diff --git a/applications/main/infrared/scenes/infrared_scene_remote_list.c b/applications/main/infrared/scenes/infrared_scene_remote_list.c index 1667352d1..55f14416b 100644 --- a/applications/main/infrared/scenes/infrared_scene_remote_list.c +++ b/applications/main/infrared/scenes/infrared_scene_remote_list.c @@ -7,6 +7,7 @@ void infrared_scene_remote_list_on_enter(void* context) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px); + browser_options.base_path = INFRARED_APP_FOLDER; bool success = dialog_file_browser_show( infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options); diff --git a/applications/main/infrared/scenes/infrared_scene_universal_tv.c b/applications/main/infrared/scenes/infrared_scene_universal_tv.c index 583f21fa3..e21bf8f90 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_tv.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_tv.c @@ -24,7 +24,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { &I_Power_hvr_25x27, infrared_scene_universal_common_item_callback, context); - infrared_brute_force_add_record(brute_force, i++, "POWER"); + infrared_brute_force_add_record(brute_force, i++, "Power"); button_panel_add_item( button_panel, i, @@ -36,7 +36,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { &I_Mute_hvr_25x27, infrared_scene_universal_common_item_callback, context); - infrared_brute_force_add_record(brute_force, i++, "MUTE"); + infrared_brute_force_add_record(brute_force, i++, "Mute"); button_panel_add_item( button_panel, i, @@ -48,7 +48,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { &I_Vol_up_hvr_25x27, infrared_scene_universal_common_item_callback, context); - infrared_brute_force_add_record(brute_force, i++, "VOL+"); + infrared_brute_force_add_record(brute_force, i++, "Vol_up"); button_panel_add_item( button_panel, i, @@ -60,7 +60,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { &I_Up_hvr_25x27, infrared_scene_universal_common_item_callback, context); - infrared_brute_force_add_record(brute_force, i++, "CH+"); + infrared_brute_force_add_record(brute_force, i++, "Ch_next"); button_panel_add_item( button_panel, i, @@ -72,7 +72,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { &I_Vol_down_hvr_25x27, infrared_scene_universal_common_item_callback, context); - infrared_brute_force_add_record(brute_force, i++, "VOL-"); + infrared_brute_force_add_record(brute_force, i++, "Vol_dn"); button_panel_add_item( button_panel, i, @@ -84,7 +84,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { &I_Down_hvr_25x27, infrared_scene_universal_common_item_callback, context); - infrared_brute_force_add_record(brute_force, i++, "CH-"); + infrared_brute_force_add_record(brute_force, i++, "Ch_prev"); button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote"); button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol"); diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index 4a1498181..150a6f3db 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -14,6 +14,7 @@ App( icon="A_125khz_14", stack_size=2 * 1024, order=20, + fap_libs=["assets"], ) App( diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index 513227306..2207e7e07 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -32,7 +32,7 @@ static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { } static LfRfid* lfrfid_alloc() { - LfRfid* lfrfid = malloc(sizeof(LfRfid)); + LfRfid* lfrfid = malloc(sizeof(LfRfid)); //-V773 lfrfid->storage = furi_record_open(RECORD_STORAGE); lfrfid->dialogs = furi_record_open(RECORD_DIALOGS); @@ -230,6 +230,7 @@ bool lfrfid_load_key_from_file_select(LfRfid* app) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, LFRFID_APP_EXTENSION, &I_125_10px); + browser_options.base_path = LFRFID_APP_FOLDER; // Input events and views are managed by file_browser bool result = diff --git a/applications/main/nfc/helpers/nfc_generators.c b/applications/main/nfc/helpers/nfc_generators.c index 11083b9f0..5f0527c6a 100644 --- a/applications/main/nfc/helpers/nfc_generators.c +++ b/applications/main/nfc/helpers/nfc_generators.c @@ -314,7 +314,7 @@ static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) { mful->version.storage_size = 0x15; } -static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { +void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { nfc_generate_common_start(data); nfc_generate_mf_classic_common(data, uid_len, type); @@ -337,6 +337,9 @@ static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClas } mf_classic_set_block_read(mfc, i, &mfc->block[i]); } + // Set SAK to 18 + data->nfc_data.sak = 0x18; + } else if(type == MfClassicType1k) { // Set every block to 0xFF for(uint16_t i = 1; i < MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4; i += 1) { @@ -347,6 +350,8 @@ static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClas } mf_classic_set_block_read(mfc, i, &mfc->block[i]); } + // Set SAK to 08 + data->nfc_data.sak = 0x08; } mfc->type = type; diff --git a/applications/main/nfc/helpers/nfc_generators.h b/applications/main/nfc/helpers/nfc_generators.h index 10a05591b..362a19b1e 100644 --- a/applications/main/nfc/helpers/nfc_generators.h +++ b/applications/main/nfc/helpers/nfc_generators.h @@ -11,3 +11,5 @@ struct NfcGenerator { }; extern const NfcGenerator* const nfc_generators[]; + +void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type); diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 55c68a450..0dd071bc4 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -116,7 +116,9 @@ void nfc_free(Nfc* nfc) { // Stop worker nfc_worker_stop(nfc->worker); // Save data in shadow file - nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } } if(nfc->rpc_ctx) { rpc_system_app_send_exited(nfc->rpc_ctx); @@ -218,6 +220,13 @@ void nfc_blink_stop(Nfc* nfc) { notification_message(nfc->notifications, &sequence_blink_stop); } +bool nfc_save_file(Nfc* nfc) { + furi_string_printf( + nfc->dev->load_path, "%s/%s%s", NFC_APP_FOLDER, nfc->dev->dev_name, NFC_APP_EXTENSION); + bool file_saved = nfc_device_save(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + return file_saved; +} + void nfc_show_loading_popup(void* context, bool show) { Nfc* nfc = context; TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); diff --git a/applications/main/nfc/nfc_i.h b/applications/main/nfc/nfc_i.h index e9b36a3e9..57eefbf67 100644 --- a/applications/main/nfc/nfc_i.h +++ b/applications/main/nfc/nfc_i.h @@ -114,4 +114,6 @@ void nfc_blink_detect_start(Nfc* nfc); void nfc_blink_stop(Nfc* nfc); +bool nfc_save_file(Nfc* nfc); + void nfc_show_loading_popup(void* context, bool show); diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 9b922add2..ce51d000d 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -21,6 +21,7 @@ ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate) ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth) ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) +ADD_SCENE(nfc, mf_ultralight_unlock_auto, MfUltralightUnlockAuto) ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) @@ -60,3 +61,4 @@ ADD_SCENE(nfc, detect_reader, DetectReader) ADD_SCENE(nfc, mfkey_nonces_info, MfkeyNoncesInfo) ADD_SCENE(nfc, mfkey_complete, MfkeyComplete) ADD_SCENE(nfc, nfc_data_info, NfcDataInfo) +ADD_SCENE(nfc, read_card_type, ReadCardType) diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c index 5ddb60992..f90197679 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c @@ -37,8 +37,8 @@ static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) { FuriString* info_str; info_str = furi_string_alloc(); - widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); - widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating UID"); + widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); + widget_add_string_element(widget, 57, 13, AlignLeft, AlignTop, FontPrimary, "Emulating UID"); if(strcmp(nfc->dev->dev_name, "")) { furi_string_printf(info_str, "%s", nfc->dev->dev_name); } else { @@ -48,7 +48,7 @@ static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) { } furi_string_trim(info_str); widget_add_text_box_element( - widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + widget, 57, 28, 67, 25, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); furi_string_free(info_str); if(data_received) { widget_add_button_element( diff --git a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c index b22dd42a1..3ce4f6de8 100644 --- a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c @@ -29,8 +29,13 @@ bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { if(event.event == DialogExResultRight) { consumed = scene_manager_previous_scene(nfc->scene_manager); } else if(event.event == DialogExResultLeft) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadCardType)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneReadCardType); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } } } else if(event.type == SceneManagerEventTypeBack) { consumed = true; diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index e888e9d35..fc6021d73 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -1,6 +1,7 @@ #include "../nfc_i.h" enum SubmenuIndex { + SubmenuIndexReadCardType, SubmenuIndexMfClassicKeys, SubmenuIndexMfUltralightUnlock, }; @@ -15,6 +16,12 @@ void nfc_scene_extra_actions_on_enter(void* context) { Nfc* nfc = context; Submenu* submenu = nfc->submenu; + submenu_add_item( + submenu, + "Read Specific Card Type", + SubmenuIndexReadCardType, + nfc_scene_extra_actions_submenu_callback, + nfc); submenu_add_item( submenu, "Mifare Classic Keys", @@ -44,9 +51,15 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == SubmenuIndexMfUltralightUnlock) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; + } else if(event.event == SubmenuIndexReadCardType) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); + scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); + consumed = true; } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); } + return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_file_select.c b/applications/main/nfc/scenes/nfc_scene_file_select.c index 693fdec20..374a933d1 100644 --- a/applications/main/nfc/scenes/nfc_scene_file_select.c +++ b/applications/main/nfc/scenes/nfc_scene_file_select.c @@ -5,6 +5,9 @@ void nfc_scene_file_select_on_enter(void* context) { Nfc* nfc = context; // Process file_select return nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc); + if(!furi_string_size(nfc->dev->load_path)) { + furi_string_set_str(nfc->dev->load_path, NFC_APP_FOLDER); + } if(nfc_file_select(nfc->dev)) { scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, 0); scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c index e514fa728..1bd9a85a8 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c @@ -17,13 +17,14 @@ void nfc_scene_mf_classic_emulate_on_enter(void* context) { // Setup view Popup* popup = nfc->popup; + popup_set_header(popup, "Emulating", 67, 13, AlignLeft, AlignTop); if(strcmp(nfc->dev->dev_name, "")) { - nfc_text_store_set(nfc, "Emulating\n%s", nfc->dev->dev_name); + nfc_text_store_set(nfc, "%s", nfc->dev->dev_name); } else { - nfc_text_store_set(nfc, "Emulating\nMf Classic", nfc->dev->dev_name); + nfc_text_store_set(nfc, "MIFARE\nClassic"); } - popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); - popup_set_header(popup, nfc->text_store, 56, 31, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 3, &I_NFC_dolphin_emulation_47x61); + popup_set_text(popup, nfc->text_store, 90, 28, AlignCenter, AlignTop); // Setup and start worker view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); @@ -48,7 +49,10 @@ bool nfc_scene_mf_classic_emulate_on_event(void* context, SceneManagerEvent even NFC_MF_CLASSIC_DATA_CHANGED) { scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfClassicEmulate, NFC_MF_CLASSIC_DATA_NOT_CHANGED); - nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); + // Save shadow file + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } } consumed = false; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c index a5bb10ddf..5fbdabe30 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c @@ -4,6 +4,7 @@ enum SubmenuIndex { SubmenuIndexSave, SubmenuIndexEmulate, + SubmenuIndexDetectReader, SubmenuIndexInfo, }; @@ -21,6 +22,14 @@ void nfc_scene_mf_classic_menu_on_enter(void* context) { submenu, "Save", SubmenuIndexSave, nfc_scene_mf_classic_menu_submenu_callback, nfc); submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mf_classic_menu_submenu_callback, nfc); + if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) { + submenu_add_item( + submenu, + "Detect reader", + SubmenuIndexDetectReader, + nfc_scene_mf_classic_menu_submenu_callback, + nfc); + } submenu_add_item( submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_classic_menu_submenu_callback, nfc); @@ -35,17 +44,14 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfClassicMenu, event.event); if(event.event == SubmenuIndexSave) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave); nfc->dev->format = NfcDeviceSaveFormatMifareClassic; // Clear device name nfc_device_set_name(nfc->dev, ""); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; } else if(event.event == SubmenuIndexEmulate) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { DOLPHIN_DEED(DolphinDeedNfcAddEmulate); @@ -53,9 +59,11 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) DOLPHIN_DEED(DolphinDeedNfcEmulate); } consumed = true; + } else if(event.event == SubmenuIndexDetectReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + DOLPHIN_DEED(DolphinDeedNfcDetectReader); + consumed = true; } else if(event.event == SubmenuIndexInfo) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexInfo); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); consumed = true; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c index ae31e92cc..444c9a540 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c @@ -24,7 +24,7 @@ void nfc_scene_mf_classic_read_success_on_enter(void* context) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc); - FuriString* temp_str; + FuriString* temp_str = NULL; if(furi_string_size(nfc->dev->dev_data.parsed_data)) { temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data); } else { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c index dd3a6f7d5..aacf77f77 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c @@ -57,7 +57,7 @@ bool nfc_scene_mf_classic_update_on_event(void* context, SceneManagerEvent event if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcWorkerEventSuccess) { nfc_worker_stop(nfc->worker); - if(nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name)) { + if(nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path))) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdateSuccess); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c index e84fb3927..c9c617cbe 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c @@ -16,14 +16,20 @@ void nfc_scene_mf_ultralight_emulate_on_enter(void* context) { Nfc* nfc = context; // Setup view + MfUltralightType type = nfc->dev->dev_data.mf_ul_data.type; + bool is_ultralight = (type == MfUltralightTypeUL11) || (type == MfUltralightTypeUL21) || + (type == MfUltralightTypeUnknown); Popup* popup = nfc->popup; + popup_set_header(popup, "Emulating", 67, 13, AlignLeft, AlignTop); if(strcmp(nfc->dev->dev_name, "")) { - nfc_text_store_set(nfc, "Emulating\n%s", nfc->dev->dev_name); + nfc_text_store_set(nfc, "%s", nfc->dev->dev_name); + } else if(is_ultralight) { + nfc_text_store_set(nfc, "MIFARE\nUltralight"); } else { - nfc_text_store_set(nfc, "Emulating\nMf Ultralight", nfc->dev->dev_name); + nfc_text_store_set(nfc, "MIFARE\nNTAG"); } - popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); - popup_set_header(popup, nfc->text_store, 56, 31, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 3, &I_NFC_dolphin_emulation_47x61); + popup_set_text(popup, nfc->text_store, 90, 28, AlignCenter, AlignTop); // Setup and start worker view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); @@ -48,7 +54,10 @@ bool nfc_scene_mf_ultralight_emulate_on_event(void* context, SceneManagerEvent e NFC_MF_UL_DATA_CHANGED) { scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_NOT_CHANGED); - nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); + // Save shadow file + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } } consumed = false; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c index ab4d37b09..c511e9dcb 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c @@ -19,10 +19,10 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) { Submenu* submenu = nfc->submenu; MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data; - if(data->data_read != data->data_size) { + if(!mf_ul_is_full_capture(data)) { submenu_add_item( submenu, - "Unlock With Password", + "Unlock", SubmenuIndexUnlock, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c index 5dbb0c18a..2ab5e3f3f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c @@ -24,25 +24,29 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState if(curr_state != state) { if(state == NfcSceneMfUlReadStateDetecting) { popup_reset(nfc->popup); - popup_set_text( - nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_text(nfc->popup, "Apply the\ntarget card", 97, 24, AlignCenter, AlignTop); popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); + nfc_blink_read_start(nfc); } else if(state == NfcSceneMfUlReadStateReading) { popup_reset(nfc->popup); popup_set_header( nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); popup_set_icon(nfc->popup, 12, 23, &A_Loading_24); + nfc_blink_detect_start(nfc); } else if(state == NfcSceneMfUlReadStateNotSupportedCard) { popup_reset(nfc->popup); popup_set_header(nfc->popup, "Wrong type of card!", 64, 3, AlignCenter, AlignTop); popup_set_text( nfc->popup, - "Only MIFARE\nUltralight & NTAG\n are supported", + "Only MIFARE\nUltralight & NTAG\nare supported", 4, 22, AlignLeft, AlignTop); popup_set_icon(nfc->popup, 73, 20, &I_DolphinCommon_56x48); + nfc_blink_stop(nfc); + notification_message(nfc->notifications, &sequence_error); + notification_message(nfc->notifications, &sequence_set_red_255); } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth, state); } @@ -62,8 +66,6 @@ void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) { &nfc->dev->dev_data, nfc_scene_mf_ultralight_read_auth_worker_callback, nfc); - - nfc_blink_read_start(nfc); } bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent event) { @@ -86,8 +88,17 @@ bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent nfc, NfcSceneMfUlReadStateNotSupportedCard); } } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; + NfcScene next_scene; + if(mf_ul_data->auth_method == MfUltralightAuthMethodManual) { + next_scene = NfcSceneMfUltralightKeyInput; + } else if(mf_ul_data->auth_method == MfUltralightAuthMethodAuto) { + next_scene = NfcSceneMfUltralightUnlockAuto; + } else { + next_scene = NfcSceneMfUltralightUnlockMenu; + } + consumed = + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, next_scene); } return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c index 178d03351..b125e9991 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c @@ -19,16 +19,20 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data); Widget* widget = nfc->widget; + const char* title; FuriString* temp_str; temp_str = furi_string_alloc(); if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) { - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "All pages are unlocked!"); + if(mf_ul_data->auth_success) { + title = "All pages are unlocked!"; + } else { + title = "All unlocked but failed auth!"; + } } else { - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!"); + title = "Not all pages unlocked!"; } + widget_add_string_element(widget, 64, 0, AlignCenter, AlignTop, FontPrimary, title); furi_string_set(temp_str, "UID:"); for(size_t i = 0; i < nfc_data->uid_len; i++) { furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); @@ -65,6 +69,7 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { nfc); furi_string_free(temp_str); + notification_message(nfc->notifications, &sequence_set_green_255); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } @@ -81,8 +86,21 @@ bool nfc_scene_mf_ultralight_read_auth_result_on_event(void* context, SceneManag consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; + if(mf_ul_data->auth_method == MfUltralightAuthMethodManual || + mf_ul_data->auth_method == MfUltralightAuthMethodAuto) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else { + NfcScene next_scene; + if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) { + next_scene = NfcSceneMfUltralightMenu; + } else { + next_scene = NfcSceneMfUltralightUnlockMenu; + } + + consumed = + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, next_scene); + } } return consumed; @@ -93,4 +111,6 @@ void nfc_scene_mf_ultralight_read_auth_result_on_exit(void* context) { // Clean views widget_reset(nfc->widget); + + notification_message_block(nfc->notifications, &sequence_reset_green); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c index 63bffbf36..cb5ccd6e8 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c @@ -31,7 +31,7 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { nfc_scene_mf_ultralight_read_success_widget_callback, nfc); - FuriString* temp_str; + FuriString* temp_str = NULL; if(furi_string_size(nfc->dev->dev_data.parsed_data)) { temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data); } else { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c new file mode 100644 index 000000000..c59fe3a7d --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c @@ -0,0 +1,64 @@ +#include "../nfc_i.h" + +bool nfc_scene_mf_ultralight_unlock_auto_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + return true; +} + +void nfc_scene_mf_ultralight_unlock_auto_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + widget_add_string_multiline_element( + nfc->widget, + 54, + 30, + AlignLeft, + AlignCenter, + FontPrimary, + "Touch the\nreader to get\npassword..."); + widget_add_icon_element(nfc->widget, 0, 15, &I_Modern_reader_18x34); + widget_add_icon_element(nfc->widget, 20, 12, &I_Move_flipper_26x39); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfUltralightEmulate, + &nfc->dev->dev_data, + nfc_scene_mf_ultralight_unlock_auto_worker_callback, + nfc); + + nfc_blink_read_start(nfc); +} + +bool nfc_scene_mf_ultralight_unlock_auto_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if((event.event == NfcWorkerEventMfUltralightPwdAuth)) { + MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth; + memcpy(nfc->byte_input_store, auth->pwd.raw, sizeof(auth->pwd.raw)); + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAuto; + nfc_worker_stop(nfc->worker); + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_auto_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + widget_reset(nfc->widget); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c index 648aa31dc..484629b0b 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c @@ -1,9 +1,10 @@ #include "../nfc_i.h" enum SubmenuIndex { - SubmenuIndexMfUlUnlockMenuManual, + SubmenuIndexMfUlUnlockMenuAuto, SubmenuIndexMfUlUnlockMenuAmeebo, SubmenuIndexMfUlUnlockMenuXiaomi, + SubmenuIndexMfUlUnlockMenuManual, }; void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) { @@ -18,12 +19,14 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); - submenu_add_item( - submenu, - "Enter Password Manually", - SubmenuIndexMfUlUnlockMenuManual, - nfc_scene_mf_ultralight_unlock_menu_submenu_callback, - nfc); + if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexMfUlUnlockMenuAuto, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + } submenu_add_item( submenu, "Auth As Ameebo", @@ -32,10 +35,16 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { nfc); submenu_add_item( submenu, - "Auth As Xiaomi", + "Auth As Xiaomi Air Purifier", SubmenuIndexMfUlUnlockMenuXiaomi, nfc_scene_mf_ultralight_unlock_menu_submenu_callback, nfc); + submenu_add_item( + submenu, + "Enter Password Manually", + SubmenuIndexMfUlUnlockMenuManual, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); submenu_set_selected_item(submenu, state); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } @@ -57,8 +66,12 @@ bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEve nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi; scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuAuto) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + consumed = true; } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu, event.event); } return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c index 514cd4e98..16efae9de 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -10,15 +10,43 @@ void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { Nfc* nfc = context; DialogEx* dialog_ex = nfc->dialog_ex; + MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method; dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback); - dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); - dialog_ex_set_text( - dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); - dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48); - dialog_ex_set_center_button_text(dialog_ex, "OK"); + if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) { + // Build dialog text + MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth; + FuriString* password_str = + furi_string_alloc_set_str("Try to unlock the card with\npassword: "); + for(size_t i = 0; i < sizeof(auth->pwd.raw); ++i) { + furi_string_cat_printf(password_str, "%02X ", nfc->byte_input_store[i]); + } + furi_string_cat_str(password_str, "?\nCaution, a wrong password\ncan block the card!"); + nfc_text_store_set(nfc, furi_string_get_cstr(password_str)); + furi_string_free(password_str); + + dialog_ex_set_header( + dialog_ex, + auth_method == MfUltralightAuthMethodAuto ? "Password captured!" : "Risky function!", + 64, + 0, + AlignCenter, + AlignTop); + dialog_ex_set_text(dialog_ex, nfc->text_store, 64, 12, AlignCenter, AlignTop); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Continue"); + + if(auth_method == MfUltralightAuthMethodAuto) + notification_message(nfc->notifications, &sequence_set_green_255); + } else { + dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); + dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48); + dialog_ex_set_center_button_text(dialog_ex, "OK"); + } view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); } @@ -28,12 +56,33 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultCenter) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); - DOLPHIN_DEED(DolphinDeedNfcRead); + MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method; + if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } else if(event.event == DialogExResultLeft) { + if(auth_method == MfUltralightAuthMethodAuto) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } else { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + } + } else if(event.type == SceneManagerEventTypeBack) { + // Cannot press back consumed = true; } + } else { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultCenter) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + } } return consumed; @@ -43,5 +92,7 @@ void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) { Nfc* nfc = context; dialog_ex_reset(nfc->dialog_ex); - submenu_reset(nfc->submenu); + nfc_text_store_clear(nfc); + + notification_message_block(nfc->notifications, &sequence_reset_green); } diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 8f33972e0..d1767a458 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -87,6 +87,22 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4); if(data->data_size > data->data_read) { furi_string_cat_printf(temp_str, "\nPassword-protected"); + } else if(data->auth_success) { + MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data); + if(config_pages) { + furi_string_cat_printf( + temp_str, + "\nPassword: %02X %02X %02X %02X", + config_pages->auth_data.pwd.raw[0], + config_pages->auth_data.pwd.raw[1], + config_pages->auth_data.pwd.raw[2], + config_pages->auth_data.pwd.raw[3]); + furi_string_cat_printf( + temp_str, + "\nPACK: %02X %02X", + config_pages->auth_data.pack.raw[0], + config_pages->auth_data.pack.raw[1]); + } } } else if(protocol == NfcDeviceProtocolMifareClassic) { MfClassicData* data = &dev_data->mf_classic_data; @@ -115,7 +131,7 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == GuiButtonTypeRight) { if(protocol == NfcDeviceProtocolMifareDesfire) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData); consumed = true; } else if(protocol == NfcDeviceProtocolMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData); diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 1f82aef08..a64d4d00d 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -70,6 +70,8 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); + // Set unlock password input to 0xFFFFFFFF only on fresh read + memset(nfc->byte_input_store, 0xFF, 4); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_type.c b/applications/main/nfc/scenes/nfc_scene_read_card_type.c new file mode 100644 index 000000000..94262aa1e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_read_card_type.c @@ -0,0 +1,97 @@ +#include "../nfc_i.h" +#include "nfc_worker_i.h" + +enum SubmenuIndex { + SubmenuIndexReadMifareClassic, + SubmenuIndexReadMifareDesfire, + SubmenuIndexReadMfUltralight, + SubmenuIndexReadEMV, + SubmenuIndexReadNFCA, +}; + +void nfc_scene_read_card_type_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_read_card_type_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, + "Read Mifare Classic", + SubmenuIndexReadMifareClassic, + nfc_scene_read_card_type_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Read Mifare DESFire", + SubmenuIndexReadMifareDesfire, + nfc_scene_read_card_type_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Read NTAG/Ultralight", + SubmenuIndexReadMfUltralight, + nfc_scene_read_card_type_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Read EMV card", + SubmenuIndexReadEMV, + nfc_scene_read_card_type_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Read NFC-A data", + SubmenuIndexReadNFCA, + nfc_scene_read_card_type_submenu_callback, + nfc); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadCardType); + submenu_set_selected_item(submenu, state); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_read_card_type_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexReadMifareClassic) { + nfc->dev->dev_data.read_mode = NfcReadModeMfClassic; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } + if(event.event == SubmenuIndexReadMifareDesfire) { + nfc->dev->dev_data.read_mode = NfcReadModeMfDesfire; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } + if(event.event == SubmenuIndexReadMfUltralight) { + nfc->dev->dev_data.read_mode = NfcReadModeMfUltralight; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } + if(event.event == SubmenuIndexReadEMV) { + nfc->dev->dev_data.read_mode = NfcReadModeEMV; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } + if(event.event == SubmenuIndexReadNFCA) { + nfc->dev->dev_data.read_mode = NfcReadModeNFCA; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, event.event); + } + return consumed; +} + +void nfc_scene_read_card_type_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_rpc.c b/applications/main/nfc/scenes/nfc_scene_rpc.c index e5128a52f..60d01a30d 100644 --- a/applications/main/nfc/scenes/nfc_scene_rpc.c +++ b/applications/main/nfc/scenes/nfc_scene_rpc.c @@ -7,7 +7,7 @@ void nfc_scene_rpc_on_enter(void* context) { popup_set_header(popup, "NFC", 89, 42, AlignCenter, AlignBottom); popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); + popup_set_icon(popup, 0, 12, &I_NFC_dolphin_emulation_47x61); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c index 791f8d99b..ca4b1a350 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_name.c +++ b/applications/main/nfc/scenes/nfc_scene_save_name.c @@ -62,7 +62,7 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; } strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1); - if(nfc_device_save(nfc->dev, nfc->text_store)) { + if(nfc_save_file(nfc)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { // Nothing, do not count editing as saving diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 231f12089..04c686fbe 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -11,6 +11,8 @@ enum SubmenuIndex { SubmenuIndexDelete, SubmenuIndexInfo, SubmenuIndexRestoreOriginal, + SubmenuIndexMfUlUnlockByReader, + SubmenuIndexMfUlUnlockByPassword, }; void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) { @@ -69,6 +71,21 @@ void nfc_scene_saved_menu_on_enter(void* context) { } submenu_add_item( submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); + if(nfc->dev->format == NfcDeviceSaveFormatMifareUl && + !mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexMfUlUnlockByReader, + nfc_scene_saved_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Unlock With Password", + SubmenuIndexMfUlUnlockByPassword, + nfc_scene_saved_menu_submenu_callback, + nfc); + } if(nfc->dev->shadow_file_exist) { submenu_add_item( submenu, @@ -106,6 +123,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == SubmenuIndexDetectReader) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + DOLPHIN_DEED(DolphinDeedNfcDetectReader); consumed = true; } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); @@ -141,6 +159,12 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexRestoreOriginal) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm); consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockByReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockByPassword) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index 1d3fb5eb9..5f0f52f6e 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -31,7 +31,7 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { if(event.event == NfcCustomEventByteInputDone) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; - if(nfc_device_save(nfc->dev, nfc->dev->dev_name)) { + if(nfc_save_file(nfc)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); consumed = true; } @@ -41,6 +41,7 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { } } } + return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index 028f85ae0..f8b9f52c6 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include "nfc_worker_i.h" #include enum SubmenuIndex { @@ -47,6 +48,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { + nfc->dev->dev_data.read_mode = NfcReadModeAuto; scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); DOLPHIN_DEED(DolphinDeedNfcRead); consumed = true; diff --git a/applications/main/subghz/helpers/subghz_chat.c b/applications/main/subghz/helpers/subghz_chat.c index f821feaa9..dbf34c970 100644 --- a/applications/main/subghz/helpers/subghz_chat.c +++ b/applications/main/subghz/helpers/subghz_chat.c @@ -59,11 +59,8 @@ SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) { instance->cli = cli; - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzChat"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_chat_worker_thread); + instance->thread = + furi_thread_alloc_ex("SubGhzChat", 2048, subghz_chat_worker_thread, instance); instance->subghz_txrx = subghz_tx_rx_worker_alloc(); instance->event_queue = furi_message_queue_alloc(80, sizeof(SubGhzChatEvent)); return instance; diff --git a/applications/main/subghz/helpers/subghz_error_type.h b/applications/main/subghz/helpers/subghz_error_type.h new file mode 100644 index 000000000..e481aa4be --- /dev/null +++ b/applications/main/subghz/helpers/subghz_error_type.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +/** SubGhzErrorType */ +typedef enum { + SubGhzErrorTypeNoError = 0, /** There are no errors */ + SubGhzErrorTypeParseFile = + 1, /** File parsing error, or wrong file structure, or missing required parameters. more accurate data can be obtained through the debug port */ + SubGhzErrorTypeOnlyRX = + 2, /** Transmission on this frequency is blocked by regional settings */ +} SubGhzErrorType; diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h index eaf53b663..b94ebe380 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h @@ -25,7 +25,7 @@ TUPLE_DEF2( (frequency, uint32_t), (count, uint8_t), (rssi_max, uint8_t)) -/* Register globaly the oplist */ +/* Register globally the oplist */ #define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \ TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_POD_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST) diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 0341990d8..7463cfaf9 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -5,8 +5,6 @@ #define TAG "SubghzFrequencyAnalyzerWorker" -#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -95.0f - static const uint8_t subghz_preset_ook_58khz[][2] = { {CC1101_MDMCFG4, 0b11110111}, // Rx BW filter is 58.035714kHz /* End */ @@ -71,7 +69,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { .frequency_coarse = 0, .rssi_coarse = 0, .frequency_fine = 0, .rssi_fine = 0}; float rssi = 0; uint32_t frequency = 0; - float rssi_temp = 0; + float rssi_temp = -127.0f; uint32_t frequency_temp = 0; CC1101Status status; @@ -196,7 +194,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { TAG, "=:%lu:%f", frequency_rssi.frequency_fine, (double)frequency_rssi.rssi_fine); instance->sample_hold_counter = 20; - rssi_temp = frequency_rssi.rssi_fine; + rssi_temp = (rssi_temp + frequency_rssi.rssi_fine) / 2; frequency_temp = frequency_rssi.frequency_fine; if(instance->filVal) { @@ -207,10 +205,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { // Deliver callback if(instance->pair_callback) { instance->pair_callback( - instance->context, - frequency_rssi.frequency_fine, - frequency_rssi.rssi_fine, - true); + instance->context, frequency_rssi.frequency_fine, rssi_temp, true); } } else if( // Deliver results coarse (frequency_rssi.rssi_coarse > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) && @@ -222,7 +217,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { (double)frequency_rssi.rssi_coarse); instance->sample_hold_counter = 20; - rssi_temp = frequency_rssi.rssi_coarse; + rssi_temp = (rssi_temp + frequency_rssi.rssi_coarse) / 2; frequency_temp = frequency_rssi.frequency_coarse; if(instance->filVal) { frequency_rssi.frequency_coarse = @@ -232,15 +227,12 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { // Deliver callback if(instance->pair_callback) { instance->pair_callback( - instance->context, - frequency_rssi.frequency_coarse, - frequency_rssi.rssi_coarse, - true); + instance->context, frequency_rssi.frequency_coarse, rssi_temp, true); } } else { if(instance->sample_hold_counter > 0) { instance->sample_hold_counter--; - if(instance->sample_hold_counter == 18) { + if(instance->sample_hold_counter == 15) { if(instance->pair_callback) { instance->pair_callback( instance->context, frequency_temp, rssi_temp, false); @@ -248,8 +240,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { } } else { instance->filVal = 0; - if(instance->pair_callback) - instance->pair_callback(instance->context, 0, 0, false); + rssi_temp = -127.0f; + instance->pair_callback(instance->context, 0, 0, false); } } } @@ -265,12 +257,8 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* cont furi_assert(context); SubGhzFrequencyAnalyzerWorker* instance = malloc(sizeof(SubGhzFrequencyAnalyzerWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzFAWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_frequency_analyzer_worker_thread); - + instance->thread = furi_thread_alloc_ex( + "SubGhzFAWorker", 2048, subghz_frequency_analyzer_worker_thread, instance); SubGhz* subghz = context; instance->setting = subghz->setting; return instance; diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h index 8bd1addc4..ed5bd9644 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h @@ -3,6 +3,8 @@ #include #include "../subghz_i.h" +#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -93.0f + typedef struct SubGhzFrequencyAnalyzerWorker SubGhzFrequencyAnalyzerWorker; typedef void (*SubGhzFrequencyAnalyzerWorkerPairCallback)( diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index d6e1f8dd4..4f98b6a39 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -44,14 +44,7 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event void subghz_scene_delete_success_on_exit(void* context) { SubGhz* subghz = context; - - // Clear view Popup* popup = subghz->popup; - popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - popup_set_callback(popup, NULL); - popup_set_context(popup, NULL); - popup_set_timeout(popup, 0); - popup_disable_timeout(popup); + + popup_reset(popup); } diff --git a/applications/main/subghz/scenes/subghz_scene_more_raw.c b/applications/main/subghz/scenes/subghz_scene_more_raw.c index d75ab13c7..864be1ed1 100644 --- a/applications/main/subghz/scenes/subghz_scene_more_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_more_raw.c @@ -38,18 +38,34 @@ bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexDelete) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDelete); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW); - return true; + if(subghz_file_available(subghz)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDelete); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW); + return true; + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } + } } else if(event.event == SubmenuIndexEdit) { - furi_string_reset(subghz->file_path_tmp); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); - return true; + if(subghz_file_available(subghz)) { + furi_string_reset(subghz->file_path_tmp); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + return true; + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } + } } } return false; diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index ba9ce803b..2e5ba0966 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -198,20 +198,28 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { break; case SubGhzCustomEventViewReadRAWMore: - if(subghz_scene_read_raw_update_filename(subghz)) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); - subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW); - consumed = true; + if(subghz_file_available(subghz)) { + if(subghz_scene_read_raw_update_filename(subghz)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); + subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW); + consumed = true; + } else { + furi_crash("SubGhz: RAW file name update error."); + } } else { - furi_crash("SubGhz: RAW file name update error."); + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } } break; case SubGhzCustomEventViewReadRAWSendStart: - if(subghz_scene_read_raw_update_filename(subghz)) { + if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { //start send subghz->state_notifications = SubGhzNotificationStateIDLE; if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { @@ -238,6 +246,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz->state_notifications = SubGhzNotificationStateTx; } } + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } } consumed = true; break; @@ -314,11 +328,17 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { break; case SubGhzCustomEventViewReadRAWSave: - if(subghz_scene_read_raw_update_filename(subghz)) { + if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW); subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } } consumed = true; break; diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index 79295aaa6..a1c0e41fd 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -43,6 +43,12 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { result = subghz_tx_start(subghz, subghz->txrx->fff_data); if(result) subghz_blink_start(subghz); } + if(!result) { + rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeOnlyRX); + rpc_system_app_set_error_text( + subghz->rpc_ctx, + "Transmission on this frequency is restricted in your region"); + } rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { bool result = false; @@ -74,6 +80,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { popup_set_text(popup, subghz->file_name_tmp, 89, 44, AlignCenter, AlignTop); furi_string_free(file_name); + } else { + rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeParseFile); + rpc_system_app_set_error_text(subghz->rpc_ctx, "Cannot parse file"); } } rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventLoadFile, result); diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c index d32c9271a..2977975f7 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_success.c +++ b/applications/main/subghz/scenes/subghz_scene_save_success.c @@ -44,14 +44,7 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) void subghz_scene_save_success_on_exit(void* context) { SubGhz* subghz = context; - - // Clear view Popup* popup = subghz->popup; - popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - popup_set_callback(popup, NULL); - popup_set_context(popup, NULL); - popup_set_timeout(popup, 0); - popup_disable_timeout(popup); + + popup_reset(popup); } diff --git a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c index 2720b2b94..113e7ae74 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c +++ b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c @@ -36,16 +36,10 @@ bool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event void subghz_scene_show_error_sub_on_exit(void* context) { SubGhz* subghz = context; - - // Clear view Popup* popup = subghz->popup; - popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - popup_set_callback(popup, NULL); - popup_set_context(popup, NULL); - popup_set_timeout(popup, 0); - popup_disable_timeout(popup); + + popup_reset(popup); + furi_string_reset(subghz->error_str); notification_message(subghz->notifications, &sequence_reset_rgb); diff --git a/applications/main/subghz/scenes/subghz_scene_show_only_rx.c b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c index 3bc08e5b4..1907c4192 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_only_rx.c +++ b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c @@ -43,14 +43,7 @@ bool subghz_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) void subghz_scene_show_only_rx_on_exit(void* context) { SubGhz* subghz = context; - - // Clear view Popup* popup = subghz->popup; - popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - popup_set_callback(popup, NULL); - popup_set_context(popup, NULL); - popup_set_timeout(popup, 0); - popup_disable_timeout(popup); + + popup_reset(popup); } diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index ad5d8afb0..b6471b33c 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -300,7 +300,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { furi_hal_power_suppress_charge_exit(); - printf("\r\nPackets recieved %u\r\n", instance->packet_count); + printf("\r\nPackets received %u\r\n", instance->packet_count); // Cleanup subghz_receiver_free(receiver); @@ -408,7 +408,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { } } - printf("\r\nPackets recieved \033[0;32m%u\033[0m\r\n", instance->packet_count); + printf("\r\nPackets received \033[0;32m%u\033[0m\r\n", instance->packet_count); // Cleanup subghz_receiver_free(receiver); @@ -438,7 +438,7 @@ static void subghz_cli_command_print_usage() { printf("\r\n"); printf(" debug cmd:\r\n"); printf("\ttx_carrier \t - Transmit carrier\r\n"); - printf("\trx_carrier \t - Receiv carrier\r\n"); + printf("\trx_carrier \t - Receive carrier\r\n"); printf( "\tencrypt_keeloq \t - Encrypt keeloq manufacture keys\r\n"); printf( diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index beefd8024..736bcf360 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -454,6 +454,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px); + browser_options.base_path = SUBGHZ_APP_FOLDER; // Input events and views are managed by file_select bool res = dialog_file_browser_show( @@ -490,6 +491,23 @@ bool subghz_rename_file(SubGhz* subghz) { return ret; } +bool subghz_file_available(SubGhz* subghz) { + furi_assert(subghz); + bool ret = true; + Storage* storage = furi_record_open(RECORD_STORAGE); + + FS_Error fs_result = + storage_common_stat(storage, furi_string_get_cstr(subghz->file_path), NULL); + + if(fs_result != FSE_OK) { + dialog_message_show_storage_error(subghz->dialogs, "File not available\n file/directory"); + ret = false; + } + + furi_record_close(RECORD_STORAGE); + return ret; +} + bool subghz_delete_file(SubGhz* subghz) { furi_assert(subghz); @@ -513,12 +531,6 @@ bool subghz_path_is_file(FuriString* path) { } uint32_t subghz_random_serial(void) { - static bool rand_generator_inited = false; - - if(!rand_generator_inited) { - srand(DWT->CYCCNT); - rand_generator_inited = true; - } return (uint32_t)rand(); } diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 46768cf02..234365155 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 "helpers/subghz_error_type.h" #include #include "subghz.h" #include "views/receiver.h" @@ -124,6 +125,7 @@ bool subghz_save_protocol_to_file( const char* dev_file_name); bool subghz_load_protocol_from_file(SubGhz* subghz); bool subghz_rename_file(SubGhz* subghz); +bool subghz_file_available(SubGhz* subghz); bool subghz_delete_file(SubGhz* subghz); void subghz_file_name_clear(SubGhz* subghz); bool subghz_path_is_file(FuriString* path); diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index e980bd970..129898400 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -13,8 +13,6 @@ #include #define LOG_FREQUENCY_MAX_ITEMS 60 // uint8_t (limited by 'seq' of SubGhzFrequencyAnalyzerLogItem) -#define RSSI_OFFSET 74 -#define RSSI_MAX 53 // 127 - RSSI_OFFSET #define SNPRINTF_FREQUENCY(buff, freq) \ snprintf(buff, sizeof(buff), "%03ld.%03ld", freq / 1000000 % 1000, freq / 1000 % 1000); @@ -49,7 +47,7 @@ typedef struct { } SubGhzFrequencyAnalyzerModel; static inline uint8_t rssi_sanitize(float rssi) { - return (rssi * -1.0f) - RSSI_OFFSET; + return (rssi ? (uint8_t)(rssi - SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) : 0); } void subghz_frequency_analyzer_set_callback( @@ -65,12 +63,25 @@ void subghz_frequency_analyzer_set_callback( void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) { uint8_t column_number = 0; if(rssi) { - rssi = rssi / 3; + rssi = rssi / 3 + 2; + if(rssi > 20) rssi = 20; for(uint8_t i = 1; i < rssi; i++) { - if(i > 20) break; if(i % 4) { column_number++; - canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, 4 + column_number); + canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, column_number); + } + } + } +} + +void subghz_frequency_analyzer_draw_log_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) { + uint8_t column_height = 6; + if(rssi) { + //rssi = rssi + if(rssi > 54) rssi = 54; + for(uint8_t i = 1; i < rssi; i++) { + if(i % 5) { + canvas_draw_box(canvas, x + i, y - column_height, 1, column_height); } } } @@ -86,9 +97,9 @@ static void subghz_frequency_analyzer_log_frequency_draw( const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); if(items_count == 0) { - canvas_draw_rframe(canvas, offset_x + 27u, offset_y - 3u, 73u, 16u, 5u); + canvas_draw_rframe(canvas, offset_x + 27, offset_y - 3, 73, 16, 5); canvas_draw_str_aligned( - canvas, offset_x + 64u, offset_y + 8u, AlignCenter, AlignBottom, "No records"); + canvas, offset_x + 64, offset_y + 8, AlignCenter, AlignBottom, "No records"); return; } else if(items_count > 3) { elements_scrollbar_pos( @@ -117,7 +128,7 @@ static void subghz_frequency_analyzer_log_frequency_draw( canvas_draw_str(canvas, offset_x + 48, offset_y + i * 10, buffer); // Max RSSI - subghz_frequency_analyzer_draw_rssi( + subghz_frequency_analyzer_draw_log_rssi( canvas, (*log_frequency_item)->rssi_max, offset_x + 69, (offset_y + i * 10)); } @@ -167,25 +178,20 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel } else { canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); canvas_draw_str(canvas, 0, 64, "RSSI"); - subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20u, 64u); + subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20, 64); subghz_frequency_analyzer_history_frequency_draw(canvas, model); } // Frequency canvas_set_font(canvas, FontBigNumbers); - snprintf( - buffer, - sizeof(buffer), - "%03ld.%03ld", - model->frequency / 1000000 % 1000, - model->frequency / 1000 % 1000); + SNPRINTF_FREQUENCY(buffer, model->frequency); if(model->signal) { - canvas_draw_box(canvas, 4, 12, 121, 22); + canvas_draw_box(canvas, 4, 11, 121, 22); canvas_set_color(canvas, ColorWhite); } - canvas_draw_str(canvas, 8, 30, buffer); - canvas_draw_icon(canvas, 96, 19, &I_MHz_25x11); + canvas_draw_str(canvas, 8, 29, buffer); + canvas_draw_icon(canvas, 96, 18, &I_MHz_25x11); } static void subghz_frequency_analyzer_log_frequency_sort(SubGhzFrequencyAnalyzerModel* model) { @@ -292,7 +298,7 @@ static bool subghz_frequency_analyzer_log_frequency_insert(SubGhzFrequencyAnalyz return false; } (*item)->frequency = model->frequency; - (*item)->count = 1u; + (*item)->count = 1; (*item)->rssi_max = model->rssi; (*item)->seq = items_count; return true; @@ -394,9 +400,9 @@ void subghz_frequency_analyzer_enter(void* context) { model->frequency = 0; model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain; model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc; - model->log_frequency_scroll_offset = 0u; + model->log_frequency_scroll_offset = 0; model->history_frequency[0] = model->history_frequency[1] = - model->history_frequency[2] = 0u; + model->history_frequency[2] = 0; SubGhzFrequencyAnalyzerLogItemArray_init(model->log_frequency); }, true); @@ -416,13 +422,13 @@ void subghz_frequency_analyzer_exit(void* context) { instance->view, SubGhzFrequencyAnalyzerModel * model, { - model->rssi = 0u; + model->rssi = 0; model->frequency = 0; model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain; model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc; - model->log_frequency_scroll_offset = 0u; + model->log_frequency_scroll_offset = 0; model->history_frequency[0] = model->history_frequency[1] = - model->history_frequency[2] = 0u; + model->history_frequency[2] = 0; SubGhzFrequencyAnalyzerLogItemArray_clear(model->log_frequency); }, true); diff --git a/applications/main/u2f/application.fam b/applications/main/u2f/application.fam index 6b32e0225..82010ffb4 100644 --- a/applications/main/u2f/application.fam +++ b/applications/main/u2f/application.fam @@ -11,4 +11,5 @@ App( stack_size=2 * 1024, icon="A_U2F_14", order=80, + fap_libs=["assets"], ) diff --git a/applications/main/u2f/u2f_hid.c b/applications/main/u2f/u2f_hid.c index 4922d6a5a..6e1a51f33 100644 --- a/applications/main/u2f/u2f_hid.c +++ b/applications/main/u2f/u2f_hid.c @@ -58,13 +58,13 @@ struct U2fHid_packet { struct U2fHid { FuriThread* thread; FuriTimer* lock_timer; - struct U2fHid_packet packet; uint8_t seq_id_last; uint16_t req_buf_ptr; uint32_t req_len_left; uint32_t lock_cid; bool lock; U2fData* u2f_instance; + struct U2fHid_packet packet; }; static void u2f_hid_event_callback(HidU2fEvent ev, void* context) { @@ -203,7 +203,7 @@ static int32_t u2f_hid_worker(void* context) { WorkerEvtStop | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtRequest, FuriFlagWaitAny, FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); + furi_check(!(flags & FuriFlagError)); if(flags & WorkerEvtStop) break; if(flags & WorkerEvtConnect) { u2f_set_state(u2f_hid->u2f_instance, 1); @@ -215,10 +215,21 @@ static int32_t u2f_hid_worker(void* context) { } if(flags & WorkerEvtRequest) { uint32_t len_cur = furi_hal_hid_u2f_get_request(packet_buf); - if(len_cur > 0) { + do { + if(len_cur == 0) { + break; + } if((packet_buf[4] & U2F_HID_TYPE_MASK) == U2F_HID_TYPE_INIT) { + if(len_cur < 7) { + u2f_hid->req_len_left = 0; + break; // Wrong chunk len + } // Init packet u2f_hid->packet.len = (packet_buf[5] << 8) | (packet_buf[6]); + if(u2f_hid->packet.len > U2F_HID_MAX_PAYLOAD_LEN) { + u2f_hid->req_len_left = 0; + break; // Wrong packet len + } if(u2f_hid->packet.len > (len_cur - 7)) { u2f_hid->req_len_left = u2f_hid->packet.len - (len_cur - 7); len_cur = len_cur - 7; @@ -232,6 +243,10 @@ static int32_t u2f_hid_worker(void* context) { u2f_hid->req_buf_ptr = len_cur; if(len_cur > 0) memcpy(u2f_hid->packet.payload, &packet_buf[7], len_cur); } else { + if(len_cur < 5) { + u2f_hid->req_len_left = 0; + break; // Wrong chunk len + } // Continuation packet if(u2f_hid->req_len_left > 0) { uint32_t cid_temp = 0; @@ -260,7 +275,7 @@ static int32_t u2f_hid_worker(void* context) { u2f_hid_send_error(u2f_hid, U2F_HID_ERR_INVALID_CMD); } } - } + } while(0); } if(flags & WorkerEvtUnlock) { u2f_hid->lock = false; @@ -282,11 +297,7 @@ U2fHid* u2f_hid_start(U2fData* u2f_inst) { u2f_hid->u2f_instance = u2f_inst; - u2f_hid->thread = furi_thread_alloc(); - furi_thread_set_name(u2f_hid->thread, "U2fHidWorker"); - furi_thread_set_stack_size(u2f_hid->thread, 2048); - furi_thread_set_context(u2f_hid->thread, u2f_hid); - furi_thread_set_callback(u2f_hid->thread, u2f_hid_worker); + u2f_hid->thread = furi_thread_alloc_ex("U2fHidWorker", 2048, u2f_hid_worker, u2f_hid); furi_thread_start(u2f_hid->thread); return u2f_hid; } diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index 181c495d0..bf220ca2c 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -37,10 +37,10 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } else if(model->display_msg == U2fMsgSuccess) { canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31); canvas_draw_str_aligned( - canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successfull!"); + canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successful!"); } else if(model->display_msg == U2fMsgError) { canvas_draw_icon(canvas, 22, 15, &I_Error_62x31); - canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Ceritficate error"); + canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Certificate error"); } } diff --git a/applications/plugins/bt_hid_app/application.fam b/applications/plugins/bt_hid_app/application.fam deleted file mode 100644 index 2712fded7..000000000 --- a/applications/plugins/bt_hid_app/application.fam +++ /dev/null @@ -1,10 +0,0 @@ -App( - appid="bt_hid", - name="Bluetooth Remote", - apptype=FlipperAppType.EXTERNAL, - entry_point="bt_hid_app", - stack_size=1 * 1024, - fap_category="Tools", - fap_icon="bt_remote_10px.png", - fap_icon_assets="assets", -) diff --git a/applications/plugins/bt_hid_app/bt_hid.c b/applications/plugins/bt_hid_app/bt_hid.c deleted file mode 100644 index 4a77a2490..000000000 --- a/applications/plugins/bt_hid_app/bt_hid.c +++ /dev/null @@ -1,216 +0,0 @@ -#include "bt_hid.h" -#include -#include -#include - -#define TAG "BtHidApp" - -enum BtDebugSubmenuIndex { - BtHidSubmenuIndexKeynote, - BtHidSubmenuIndexKeyboard, - BtHidSubmenuIndexMedia, - BtHidSubmenuIndexTikTok, - BtHidSubmenuIndexMouse, -}; - -void bt_hid_submenu_callback(void* context, uint32_t index) { - furi_assert(context); - BtHid* app = context; - if(index == BtHidSubmenuIndexKeynote) { - app->view_id = BtHidViewKeynote; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote); - } else if(index == BtHidSubmenuIndexKeyboard) { - app->view_id = BtHidViewKeyboard; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeyboard); - } else if(index == BtHidSubmenuIndexMedia) { - app->view_id = BtHidViewMedia; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMedia); - } else if(index == BtHidSubmenuIndexMouse) { - app->view_id = BtHidViewMouse; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMouse); - } else if(index == BtHidSubmenuIndexTikTok) { - app->view_id = BtHidViewTikTok; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); - } -} - -void bt_hid_dialog_callback(DialogExResult result, void* context) { - furi_assert(context); - BtHid* app = context; - if(result == DialogExResultLeft) { - view_dispatcher_stop(app->view_dispatcher); - } else if(result == DialogExResultRight) { - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view - } else if(result == DialogExResultCenter) { - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewSubmenu); - } -} - -uint32_t bt_hid_exit_confirm_view(void* context) { - UNUSED(context); - return BtHidViewExitConfirm; -} - -uint32_t bt_hid_exit(void* context) { - UNUSED(context); - return VIEW_NONE; -} - -void bt_hid_connection_status_changed_callback(BtStatus status, void* context) { - furi_assert(context); - BtHid* bt_hid = context; - bool connected = (status == BtStatusConnected); - if(connected) { - notification_internal_message(bt_hid->notifications, &sequence_set_blue_255); - } else { - notification_internal_message(bt_hid->notifications, &sequence_reset_blue); - } - bt_hid_keynote_set_connected_status(bt_hid->bt_hid_keynote, connected); - bt_hid_keyboard_set_connected_status(bt_hid->bt_hid_keyboard, connected); - bt_hid_media_set_connected_status(bt_hid->bt_hid_media, connected); - bt_hid_mouse_set_connected_status(bt_hid->bt_hid_mouse, connected); - bt_hid_tiktok_set_connected_status(bt_hid->bt_hid_tiktok, connected); -} - -BtHid* bt_hid_app_alloc() { - BtHid* app = malloc(sizeof(BtHid)); - - // Gui - app->gui = furi_record_open(RECORD_GUI); - - // Bt - app->bt = furi_record_open(RECORD_BT); - - // Notifications - app->notifications = furi_record_open(RECORD_NOTIFICATION); - - // View dispatcher - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - // Submenu view - app->submenu = submenu_alloc(); - submenu_add_item( - app->submenu, "Keynote", BtHidSubmenuIndexKeynote, bt_hid_submenu_callback, app); - submenu_add_item( - app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app); - submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app); - submenu_add_item( - app->submenu, "TikTok Controller", BtHidSubmenuIndexTikTok, bt_hid_submenu_callback, app); - submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app); - view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewSubmenu, submenu_get_view(app->submenu)); - - // Dialog view - app->dialog = dialog_ex_alloc(); - dialog_ex_set_result_callback(app->dialog, bt_hid_dialog_callback); - dialog_ex_set_context(app->dialog, app); - dialog_ex_set_left_button_text(app->dialog, "Exit"); - dialog_ex_set_right_button_text(app->dialog, "Stay"); - dialog_ex_set_center_button_text(app->dialog, "Menu"); - dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewExitConfirm, dialog_ex_get_view(app->dialog)); - - // Keynote view - app->bt_hid_keynote = bt_hid_keynote_alloc(); - view_set_previous_callback( - bt_hid_keynote_get_view(app->bt_hid_keynote), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewKeynote, bt_hid_keynote_get_view(app->bt_hid_keynote)); - - // Keyboard view - app->bt_hid_keyboard = bt_hid_keyboard_alloc(); - view_set_previous_callback( - bt_hid_keyboard_get_view(app->bt_hid_keyboard), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewKeyboard, bt_hid_keyboard_get_view(app->bt_hid_keyboard)); - - // Media view - app->bt_hid_media = bt_hid_media_alloc(); - view_set_previous_callback(bt_hid_media_get_view(app->bt_hid_media), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media)); - - // TikTok view - app->bt_hid_tiktok = bt_hid_tiktok_alloc(); - view_set_previous_callback( - bt_hid_tiktok_get_view(app->bt_hid_tiktok), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewTikTok, bt_hid_tiktok_get_view(app->bt_hid_tiktok)); - - // Mouse view - app->bt_hid_mouse = bt_hid_mouse_alloc(); - view_set_previous_callback(bt_hid_mouse_get_view(app->bt_hid_mouse), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewMouse, bt_hid_mouse_get_view(app->bt_hid_mouse)); - - // TODO switch to menu after Media is done - app->view_id = BtHidViewSubmenu; - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); - - return app; -} - -void bt_hid_app_free(BtHid* app) { - furi_assert(app); - - // Reset notification - notification_internal_message(app->notifications, &sequence_reset_blue); - - // Free views - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewSubmenu); - submenu_free(app->submenu); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewExitConfirm); - dialog_ex_free(app->dialog); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeynote); - bt_hid_keynote_free(app->bt_hid_keynote); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeyboard); - bt_hid_keyboard_free(app->bt_hid_keyboard); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMedia); - bt_hid_media_free(app->bt_hid_media); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMouse); - bt_hid_mouse_free(app->bt_hid_mouse); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); - bt_hid_tiktok_free(app->bt_hid_tiktok); - view_dispatcher_free(app->view_dispatcher); - - // Close records - furi_record_close(RECORD_GUI); - app->gui = NULL; - furi_record_close(RECORD_NOTIFICATION); - app->notifications = NULL; - furi_record_close(RECORD_BT); - app->bt = NULL; - - // Free rest - free(app); -} - -int32_t bt_hid_app(void* p) { - UNUSED(p); - // Switch profile to Hid - BtHid* app = bt_hid_app_alloc(); - bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); - // Change profile - if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { - FURI_LOG_E(TAG, "Failed to switch profile"); - bt_hid_app_free(app); - return -1; - } - furi_hal_bt_start_advertising(); - - DOLPHIN_DEED(DolphinDeedPluginStart); - - view_dispatcher_run(app->view_dispatcher); - - bt_set_status_changed_callback(app->bt, NULL, NULL); - // Change back profile to Serial - bt_set_profile(app->bt, BtProfileSerial); - - bt_hid_app_free(app); - - return 0; -} diff --git a/applications/plugins/bt_hid_app/bt_hid.h b/applications/plugins/bt_hid_app/bt_hid.h deleted file mode 100644 index 89e8807fe..000000000 --- a/applications/plugins/bt_hid_app/bt_hid.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include "views/bt_hid_keynote.h" -#include "views/bt_hid_keyboard.h" -#include "views/bt_hid_media.h" -#include "views/bt_hid_mouse.h" -#include "views/bt_hid_tiktok.h" - -typedef struct { - Bt* bt; - Gui* gui; - NotificationApp* notifications; - ViewDispatcher* view_dispatcher; - Submenu* submenu; - DialogEx* dialog; - BtHidKeynote* bt_hid_keynote; - BtHidKeyboard* bt_hid_keyboard; - BtHidMedia* bt_hid_media; - BtHidMouse* bt_hid_mouse; - BtHidTikTok* bt_hid_tiktok; - uint32_t view_id; -} BtHid; - -typedef enum { - BtHidViewSubmenu, - BtHidViewKeynote, - BtHidViewKeyboard, - BtHidViewMedia, - BtHidViewMouse, - BtHidViewTikTok, - BtHidViewExitConfirm, -} BtHidView; diff --git a/applications/plugins/bt_hid_app/views/bt_hid_keyboard.h b/applications/plugins/bt_hid_app/views/bt_hid_keyboard.h deleted file mode 100644 index b2cc928e2..000000000 --- a/applications/plugins/bt_hid_app/views/bt_hid_keyboard.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidKeyboard BtHidKeyboard; - -BtHidKeyboard* bt_hid_keyboard_alloc(); - -void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard); - -View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard); - -void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_keynote.h b/applications/plugins/bt_hid_app/views/bt_hid_keynote.h deleted file mode 100644 index 05b121457..000000000 --- a/applications/plugins/bt_hid_app/views/bt_hid_keynote.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidKeynote BtHidKeynote; - -BtHidKeynote* bt_hid_keynote_alloc(); - -void bt_hid_keynote_free(BtHidKeynote* bt_hid_keynote); - -View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote); - -void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_media.h b/applications/plugins/bt_hid_app/views/bt_hid_media.h deleted file mode 100644 index 804239dce..000000000 --- a/applications/plugins/bt_hid_app/views/bt_hid_media.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidMedia BtHidMedia; - -BtHidMedia* bt_hid_media_alloc(); - -void bt_hid_media_free(BtHidMedia* bt_hid_media); - -View* bt_hid_media_get_view(BtHidMedia* bt_hid_media); - -void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_mouse.h b/applications/plugins/bt_hid_app/views/bt_hid_mouse.h deleted file mode 100644 index a82971d76..000000000 --- a/applications/plugins/bt_hid_app/views/bt_hid_mouse.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidMouse BtHidMouse; - -BtHidMouse* bt_hid_mouse_alloc(); - -void bt_hid_mouse_free(BtHidMouse* bt_hid_mouse); - -View* bt_hid_mouse_get_view(BtHidMouse* bt_hid_mouse); - -void bt_hid_mouse_set_connected_status(BtHidMouse* bt_hid_mouse, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_tiktok.h b/applications/plugins/bt_hid_app/views/bt_hid_tiktok.h deleted file mode 100644 index 03c9afeca..000000000 --- a/applications/plugins/bt_hid_app/views/bt_hid_tiktok.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidTikTok BtHidTikTok; - -BtHidTikTok* bt_hid_tiktok_alloc(); - -void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok); - -View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok); - -void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected); diff --git a/applications/plugins/dap_link/dap_link.c b/applications/plugins/dap_link/dap_link.c index 58d032b91..eafb435e7 100644 --- a/applications/plugins/dap_link/dap_link.c +++ b/applications/plugins/dap_link/dap_link.c @@ -13,6 +13,8 @@ #include "dap_config.h" #include "gui/dap_gui.h" #include "usb/dap_v2_usb.h" +#include +#include "dap_link_icons.h" /***************************************************************************/ /****************************** DAP COMMON *********************************/ @@ -245,7 +247,6 @@ static int32_t dap_process(void* p) { // 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; @@ -439,19 +440,6 @@ static int32_t cdc_process(void* p) { /******************************* 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); @@ -495,6 +483,24 @@ DapConfig* dap_app_get_config(DapApp* app) { int32_t dap_link_app(void* p) { UNUSED(p); + if(furi_hal_usb_is_locked()) { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop); + dialog_message_set_text( + message, + "Disconnect from\nPC or phone to\nuse this function.", + 3, + 30, + AlignLeft, + AlignTop); + dialog_message_set_icon(message, &I_ActiveConnection_50x64, 78, 0); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + return -1; + } + // alloc app DapApp* app = dap_app_alloc(); app_handle = app; diff --git a/applications/plugins/dap_link/icons/ActiveConnection_50x64.png b/applications/plugins/dap_link/icons/ActiveConnection_50x64.png new file mode 100644 index 000000000..1d7686ddd Binary files /dev/null and b/applications/plugins/dap_link/icons/ActiveConnection_50x64.png differ diff --git a/applications/plugins/dap_link/usb/dap_v2_usb.c b/applications/plugins/dap_link/usb/dap_v2_usb.c index 0c303a3ba..b42df2836 100644 --- a/applications/plugins/dap_link/usb/dap_v2_usb.c +++ b/applications/plugins/dap_link/usb/dap_v2_usb.c @@ -618,23 +618,12 @@ static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { 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; @@ -647,12 +636,6 @@ static void hid_deinit(usbd_device* dev) { 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) { diff --git a/applications/plugins/dap_link/usb/dap_v2_usb.h b/applications/plugins/dap_link/usb/dap_v2_usb.h index 2a0e86056..3f1534ffd 100644 --- a/applications/plugins/dap_link/usb/dap_v2_usb.h +++ b/applications/plugins/dap_link/usb/dap_v2_usb.h @@ -51,5 +51,3 @@ 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/hid_app/application.fam b/applications/plugins/hid_app/application.fam new file mode 100644 index 000000000..b8c13e353 --- /dev/null +++ b/applications/plugins/hid_app/application.fam @@ -0,0 +1,24 @@ +App( + appid="hid_usb", + name="USB Remote", + apptype=FlipperAppType.PLUGIN, + entry_point="hid_usb_app", + stack_size=1 * 1024, + fap_category="Tools", + fap_icon="hid_usb_10px.png", + fap_icon_assets="assets", + fap_icon_assets_symbol="hid", +) + + +App( + appid="hid_ble", + name="Bluetooth Remote", + apptype=FlipperAppType.PLUGIN, + entry_point="hid_ble_app", + stack_size=1 * 1024, + fap_category="Tools", + fap_icon="hid_ble_10px.png", + fap_icon_assets="assets", + fap_icon_assets_symbol="hid", +) diff --git a/applications/plugins/bt_hid_app/assets/Arr_dwn_7x9.png b/applications/plugins/hid_app/assets/Arr_dwn_7x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Arr_dwn_7x9.png rename to applications/plugins/hid_app/assets/Arr_dwn_7x9.png diff --git a/applications/plugins/bt_hid_app/assets/Arr_up_7x9.png b/applications/plugins/hid_app/assets/Arr_up_7x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Arr_up_7x9.png rename to applications/plugins/hid_app/assets/Arr_up_7x9.png diff --git a/applications/plugins/bt_hid_app/assets/Ble_connected_15x15.png b/applications/plugins/hid_app/assets/Ble_connected_15x15.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Ble_connected_15x15.png rename to applications/plugins/hid_app/assets/Ble_connected_15x15.png diff --git a/applications/plugins/bt_hid_app/assets/Ble_disconnected_15x15.png b/applications/plugins/hid_app/assets/Ble_disconnected_15x15.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Ble_disconnected_15x15.png rename to applications/plugins/hid_app/assets/Ble_disconnected_15x15.png diff --git a/applications/plugins/bt_hid_app/assets/ButtonDown_7x4.png b/applications/plugins/hid_app/assets/ButtonDown_7x4.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/ButtonDown_7x4.png rename to applications/plugins/hid_app/assets/ButtonDown_7x4.png diff --git a/applications/plugins/bt_hid_app/assets/ButtonLeft_4x7.png b/applications/plugins/hid_app/assets/ButtonLeft_4x7.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/ButtonLeft_4x7.png rename to applications/plugins/hid_app/assets/ButtonLeft_4x7.png diff --git a/applications/plugins/bt_hid_app/assets/ButtonRight_4x7.png b/applications/plugins/hid_app/assets/ButtonRight_4x7.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/ButtonRight_4x7.png rename to applications/plugins/hid_app/assets/ButtonRight_4x7.png diff --git a/applications/plugins/bt_hid_app/assets/ButtonUp_7x4.png b/applications/plugins/hid_app/assets/ButtonUp_7x4.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/ButtonUp_7x4.png rename to applications/plugins/hid_app/assets/ButtonUp_7x4.png diff --git a/applications/plugins/bt_hid_app/assets/Button_18x18.png b/applications/plugins/hid_app/assets/Button_18x18.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Button_18x18.png rename to applications/plugins/hid_app/assets/Button_18x18.png diff --git a/applications/plugins/bt_hid_app/assets/Circles_47x47.png b/applications/plugins/hid_app/assets/Circles_47x47.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Circles_47x47.png rename to applications/plugins/hid_app/assets/Circles_47x47.png diff --git a/applications/plugins/bt_hid_app/assets/Left_mouse_icon_9x9.png b/applications/plugins/hid_app/assets/Left_mouse_icon_9x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Left_mouse_icon_9x9.png rename to applications/plugins/hid_app/assets/Left_mouse_icon_9x9.png diff --git a/applications/plugins/bt_hid_app/assets/Like_def_11x9.png b/applications/plugins/hid_app/assets/Like_def_11x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Like_def_11x9.png rename to applications/plugins/hid_app/assets/Like_def_11x9.png diff --git a/applications/plugins/bt_hid_app/assets/Like_pressed_17x17.png b/applications/plugins/hid_app/assets/Like_pressed_17x17.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Like_pressed_17x17.png rename to applications/plugins/hid_app/assets/Like_pressed_17x17.png diff --git a/applications/plugins/bt_hid_app/assets/Ok_btn_9x9.png b/applications/plugins/hid_app/assets/Ok_btn_9x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Ok_btn_9x9.png rename to applications/plugins/hid_app/assets/Ok_btn_9x9.png diff --git a/applications/plugins/bt_hid_app/assets/Ok_btn_pressed_13x13.png b/applications/plugins/hid_app/assets/Ok_btn_pressed_13x13.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Ok_btn_pressed_13x13.png rename to applications/plugins/hid_app/assets/Ok_btn_pressed_13x13.png diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_down_7x9.png b/applications/plugins/hid_app/assets/Pin_arrow_down_7x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Pin_arrow_down_7x9.png rename to applications/plugins/hid_app/assets/Pin_arrow_down_7x9.png diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_left_9x7.png b/applications/plugins/hid_app/assets/Pin_arrow_left_9x7.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Pin_arrow_left_9x7.png rename to applications/plugins/hid_app/assets/Pin_arrow_left_9x7.png diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_right_9x7.png b/applications/plugins/hid_app/assets/Pin_arrow_right_9x7.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Pin_arrow_right_9x7.png rename to applications/plugins/hid_app/assets/Pin_arrow_right_9x7.png diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_up_7x9.png b/applications/plugins/hid_app/assets/Pin_arrow_up_7x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Pin_arrow_up_7x9.png rename to applications/plugins/hid_app/assets/Pin_arrow_up_7x9.png diff --git a/applications/plugins/bt_hid_app/assets/Pin_back_arrow_10x8.png b/applications/plugins/hid_app/assets/Pin_back_arrow_10x8.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Pin_back_arrow_10x8.png rename to applications/plugins/hid_app/assets/Pin_back_arrow_10x8.png diff --git a/applications/plugins/bt_hid_app/assets/Pressed_Button_13x13.png b/applications/plugins/hid_app/assets/Pressed_Button_13x13.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Pressed_Button_13x13.png rename to applications/plugins/hid_app/assets/Pressed_Button_13x13.png diff --git a/applications/plugins/bt_hid_app/assets/Right_mouse_icon_9x9.png b/applications/plugins/hid_app/assets/Right_mouse_icon_9x9.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Right_mouse_icon_9x9.png rename to applications/plugins/hid_app/assets/Right_mouse_icon_9x9.png diff --git a/applications/plugins/bt_hid_app/assets/Space_65x18.png b/applications/plugins/hid_app/assets/Space_65x18.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Space_65x18.png rename to applications/plugins/hid_app/assets/Space_65x18.png diff --git a/applications/plugins/bt_hid_app/assets/Voldwn_6x6.png b/applications/plugins/hid_app/assets/Voldwn_6x6.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Voldwn_6x6.png rename to applications/plugins/hid_app/assets/Voldwn_6x6.png diff --git a/applications/plugins/bt_hid_app/assets/Volup_8x6.png b/applications/plugins/hid_app/assets/Volup_8x6.png similarity index 100% rename from applications/plugins/bt_hid_app/assets/Volup_8x6.png rename to applications/plugins/hid_app/assets/Volup_8x6.png diff --git a/applications/plugins/hid_app/hid.c b/applications/plugins/hid_app/hid.c new file mode 100644 index 000000000..2a617fdea --- /dev/null +++ b/applications/plugins/hid_app/hid.c @@ -0,0 +1,365 @@ +#include "hid.h" +#include "views.h" +#include +#include + +#define TAG "HidApp" + +enum HidDebugSubmenuIndex { + HidSubmenuIndexKeynote, + HidSubmenuIndexKeyboard, + HidSubmenuIndexMedia, + BtHidSubmenuIndexTikTok, + HidSubmenuIndexMouse, +}; +typedef enum { ConnTypeSubmenuIndexBluetooth, ConnTypeSubmenuIndexUsb } ConnTypeDebugSubmenuIndex; + +static void hid_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + Hid* app = context; + if(index == HidSubmenuIndexKeynote) { + app->view_id = HidViewKeynote; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); + } else if(index == HidSubmenuIndexKeyboard) { + app->view_id = HidViewKeyboard; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard); + } else if(index == HidSubmenuIndexMedia) { + app->view_id = HidViewMedia; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia); + } else if(index == HidSubmenuIndexMouse) { + app->view_id = HidViewMouse; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse); + } else if(index == BtHidSubmenuIndexTikTok) { + app->view_id = BtHidViewTikTok; + view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); + } +} + +static void bt_hid_connection_status_changed_callback(BtStatus status, void* context) { + furi_assert(context); + Hid* hid = context; + bool connected = (status == BtStatusConnected); + if(connected) { + notification_internal_message(hid->notifications, &sequence_set_blue_255); + } else { + notification_internal_message(hid->notifications, &sequence_reset_blue); + } + hid_keynote_set_connected_status(hid->hid_keynote, connected); + hid_keyboard_set_connected_status(hid->hid_keyboard, connected); + hid_media_set_connected_status(hid->hid_media, connected); + hid_mouse_set_connected_status(hid->hid_mouse, connected); + hid_tiktok_set_connected_status(hid->hid_tiktok, connected); +} + +static void hid_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + Hid* app = context; + if(result == DialogExResultLeft) { + view_dispatcher_stop(app->view_dispatcher); + } else if(result == DialogExResultRight) { + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view + } else if(result == DialogExResultCenter) { + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewSubmenu); + } +} + +static uint32_t hid_exit_confirm_view(void* context) { + UNUSED(context); + return HidViewExitConfirm; +} + +static uint32_t hid_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +Hid* hid_alloc(HidTransport transport) { + Hid* app = malloc(sizeof(Hid)); + app->transport = transport; + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // Bt + app->bt = furi_record_open(RECORD_BT); + + // Notifications + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + // Device Type Submenu view + app->device_type_submenu = submenu_alloc(); + submenu_add_item( + app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Mouse", HidSubmenuIndexMouse, hid_submenu_callback, app); + if(app->transport == HidTransportBle) { + submenu_add_item( + app->device_type_submenu, + "TikTok Controller", + BtHidSubmenuIndexTikTok, + hid_submenu_callback, + app); + } + view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit); + view_dispatcher_add_view( + app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu)); + app->view_id = HidViewSubmenu; + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + return app; +} + +Hid* hid_app_alloc_view(void* context) { + furi_assert(context); + Hid* app = context; + // Dialog view + app->dialog = dialog_ex_alloc(); + dialog_ex_set_result_callback(app->dialog, hid_dialog_callback); + dialog_ex_set_context(app->dialog, app); + dialog_ex_set_left_button_text(app->dialog, "Exit"); + dialog_ex_set_right_button_text(app->dialog, "Stay"); + dialog_ex_set_center_button_text(app->dialog, "Menu"); + dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); + view_dispatcher_add_view( + app->view_dispatcher, HidViewExitConfirm, dialog_ex_get_view(app->dialog)); + + // Keynote view + app->hid_keynote = hid_keynote_alloc(app); + view_set_previous_callback(hid_keynote_get_view(app->hid_keynote), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote)); + + // Keyboard view + app->hid_keyboard = hid_keyboard_alloc(app); + view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard)); + + // Media view + app->hid_media = hid_media_alloc(app); + view_set_previous_callback(hid_media_get_view(app->hid_media), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media)); + + // TikTok view + app->hid_tiktok = hid_tiktok_alloc(app); + view_set_previous_callback(hid_tiktok_get_view(app->hid_tiktok), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok)); + + // Mouse view + app->hid_mouse = hid_mouse_alloc(app); + view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); + + return app; +} + +void hid_free(Hid* app) { + furi_assert(app); + + // Reset notification + notification_internal_message(app->notifications, &sequence_reset_blue); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu); + submenu_free(app->device_type_submenu); + view_dispatcher_remove_view(app->view_dispatcher, HidViewExitConfirm); + dialog_ex_free(app->dialog); + view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote); + hid_keynote_free(app->hid_keynote); + view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard); + hid_keyboard_free(app->hid_keyboard); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia); + hid_media_free(app->hid_media); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); + hid_mouse_free(app->hid_mouse); + view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); + hid_tiktok_free(app->hid_tiktok); + view_dispatcher_free(app->view_dispatcher); + + // Close records + furi_record_close(RECORD_GUI); + app->gui = NULL; + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + furi_record_close(RECORD_BT); + app->bt = NULL; + + // Free rest + free(app); +} + +void hid_hal_keyboard_press(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_kb_press(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_press(event); + } else { + furi_crash(NULL); + } +} + +void hid_hal_keyboard_release(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_kb_release(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_release(event); + } else { + furi_crash(NULL); + } +} + +void hid_hal_keyboard_release_all(Hid* instance) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_kb_release_all(); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_release_all(); + } else { + furi_crash(NULL); + } +} + +void hid_hal_consumer_key_press(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_consumer_key_press(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_consumer_key_press(event); + } else { + furi_crash(NULL); + } +} + +void hid_hal_consumer_key_release(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_consumer_key_release(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_consumer_key_release(event); + } else { + furi_crash(NULL); + } +} + +void hid_hal_consumer_key_release_all(Hid* instance) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_consumer_key_release_all(); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_release_all(); + } else { + furi_crash(NULL); + } +} + +void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_move(dx, dy); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_move(dx, dy); + } else { + furi_crash(NULL); + } +} + +void hid_hal_mouse_scroll(Hid* instance, int8_t delta) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_scroll(delta); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_scroll(delta); + } else { + furi_crash(NULL); + } +} + +void hid_hal_mouse_press(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_press(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_press(event); + } else { + furi_crash(NULL); + } +} + +void hid_hal_mouse_release(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_release(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_release(event); + } else { + furi_crash(NULL); + } +} + +void hid_hal_mouse_release_all(Hid* instance) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_release_all(); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); + furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); + } else { + furi_crash(NULL); + } +} + +int32_t hid_usb_app(void* p) { + UNUSED(p); + Hid* app = hid_alloc(HidTransportUsb); + app = hid_app_alloc_view(app); + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); + + bt_hid_connection_status_changed_callback(BtStatusConnected, app); + + DOLPHIN_DEED(DolphinDeedPluginStart); + + view_dispatcher_run(app->view_dispatcher); + + furi_hal_usb_set_config(usb_mode_prev, NULL); + + hid_free(app); + + return 0; +} + +int32_t hid_ble_app(void* p) { + UNUSED(p); + Hid* app = hid_alloc(HidTransportBle); + app = hid_app_alloc_view(app); + + if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { + FURI_LOG_E(TAG, "Failed to switch profile"); + } + furi_hal_bt_start_advertising(); + bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); + + DOLPHIN_DEED(DolphinDeedPluginStart); + + view_dispatcher_run(app->view_dispatcher); + + bt_set_status_changed_callback(app->bt, NULL, NULL); + bt_set_profile(app->bt, BtProfileSerial); + + hid_free(app); + + return 0; +} diff --git a/applications/plugins/hid_app/hid.h b/applications/plugins/hid_app/hid.h new file mode 100644 index 000000000..81ebcf566 --- /dev/null +++ b/applications/plugins/hid_app/hid.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "views/hid_keynote.h" +#include "views/hid_keyboard.h" +#include "views/hid_media.h" +#include "views/hid_mouse.h" +#include "views/hid_tiktok.h" + +typedef enum { + HidTransportUsb, + HidTransportBle, +} HidTransport; + +typedef struct Hid Hid; + +struct Hid { + Bt* bt; + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + Submenu* device_type_submenu; + DialogEx* dialog; + HidKeynote* hid_keynote; + HidKeyboard* hid_keyboard; + HidMedia* hid_media; + HidMouse* hid_mouse; + HidTikTok* hid_tiktok; + + HidTransport transport; + uint32_t view_id; +}; + +void hid_hal_keyboard_press(Hid* instance, uint16_t event); +void hid_hal_keyboard_release(Hid* instance, uint16_t event); +void hid_hal_keyboard_release_all(Hid* instance); + +void hid_hal_consumer_key_press(Hid* instance, uint16_t event); +void hid_hal_consumer_key_release(Hid* instance, uint16_t event); +void hid_hal_consumer_key_release_all(Hid* instance); + +void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy); +void hid_hal_mouse_scroll(Hid* instance, int8_t delta); +void hid_hal_mouse_press(Hid* instance, uint16_t event); +void hid_hal_mouse_release(Hid* instance, uint16_t event); +void hid_hal_mouse_release_all(Hid* instance); \ No newline at end of file diff --git a/applications/plugins/bt_hid_app/bt_remote_10px.png b/applications/plugins/hid_app/hid_ble_10px.png similarity index 100% rename from applications/plugins/bt_hid_app/bt_remote_10px.png rename to applications/plugins/hid_app/hid_ble_10px.png diff --git a/applications/plugins/hid_app/hid_usb_10px.png b/applications/plugins/hid_app/hid_usb_10px.png new file mode 100644 index 000000000..415de7d23 Binary files /dev/null and b/applications/plugins/hid_app/hid_usb_10px.png differ diff --git a/applications/plugins/hid_app/views.h b/applications/plugins/hid_app/views.h new file mode 100644 index 000000000..68a827ad6 --- /dev/null +++ b/applications/plugins/hid_app/views.h @@ -0,0 +1,9 @@ +typedef enum { + HidViewSubmenu, + HidViewKeynote, + HidViewKeyboard, + HidViewMedia, + HidViewMouse, + BtHidViewTikTok, + HidViewExitConfirm, +} HidView; \ No newline at end of file diff --git a/applications/plugins/bt_hid_app/views/bt_hid_keyboard.c b/applications/plugins/hid_app/views/hid_keyboard.c similarity index 80% rename from applications/plugins/bt_hid_app/views/bt_hid_keyboard.c rename to applications/plugins/hid_app/views/hid_keyboard.c index a1077b798..dff4a7df7 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_keyboard.c +++ b/applications/plugins/hid_app/views/hid_keyboard.c @@ -1,14 +1,15 @@ -#include "bt_hid_keyboard.h" +#include "hid_keyboard.h" #include -#include -#include #include #include +#include "../hid.h" +#include "hid_icons.h" -#include "bt_hid_icons.h" +#define TAG "HidKeyboard" -struct BtHidKeyboard { +struct HidKeyboard { View* view; + Hid* hid; }; typedef struct { @@ -24,7 +25,7 @@ typedef struct { bool back_pressed; bool connected; char key_string[5]; -} BtHidKeyboardModel; +} HidKeyboardModel; typedef struct { uint8_t width; @@ -32,13 +33,12 @@ typedef struct { const Icon* icon; char* shift_key; uint8_t value; -} BtHidKeyboardKey; +} HidKeyboardKey; typedef struct { int8_t x; int8_t y; -} BtHidKeyboardPoint; - +} HidKeyboardPoint; // 4 BY 12 #define MARGIN_TOP 0 #define MARGIN_LEFT 4 @@ -49,7 +49,7 @@ typedef struct { #define COLUMN_COUNT 12 // 0 width items are not drawn, but there value is used -const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { +const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { { {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1}, {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2}, @@ -112,7 +112,7 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { }, { {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT}, - {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA}, + {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYBOARD_COMMA}, {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT}, {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, @@ -140,19 +140,19 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { }, }; -static void bt_hid_keyboard_to_upper(char* str) { +static void hid_keyboard_to_upper(char* str) { while(*str) { *str = toupper((unsigned char)*str); str++; } } -static void bt_hid_keyboard_draw_key( +static void hid_keyboard_draw_key( Canvas* canvas, - BtHidKeyboardModel* model, + HidKeyboardModel* model, uint8_t x, uint8_t y, - BtHidKeyboardKey key, + HidKeyboardKey key, bool selected) { if(!key.width) return; @@ -190,7 +190,7 @@ static void bt_hid_keyboard_draw_key( if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) || (model->alt && key.value == HID_KEYBOARD_L_ALT) || (model->gui && key.value == HID_KEYBOARD_L_GUI)) { - bt_hid_keyboard_to_upper(model->key_string); + hid_keyboard_to_upper(model->key_string); } canvas_draw_str_aligned( canvas, @@ -202,9 +202,9 @@ static void bt_hid_keyboard_draw_key( } } -static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) { +static void hid_keyboard_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - BtHidKeyboardModel* model = context; + HidKeyboardModel* model = context; // Header if(!model->connected) { @@ -225,17 +225,17 @@ static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) { // Start shifting the all keys up if on the next row (Scrolling) uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0; for(uint8_t y = initY; y < ROW_COUNT; y++) { - const BtHidKeyboardKey* keyboardKeyRow = bt_hid_keyboard_keyset[y]; + const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y]; uint8_t x = 0; for(uint8_t i = 0; i < COLUMN_COUNT; i++) { - BtHidKeyboardKey key = keyboardKeyRow[i]; + HidKeyboardKey key = keyboardKeyRow[i]; // Select when the button is hovered // Select if the button is hovered within its width // Select if back is clicked and its the backspace key // Deselect when the button clicked or not hovered bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y; bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; - bt_hid_keyboard_draw_key( + hid_keyboard_draw_key( canvas, model, x, @@ -247,8 +247,8 @@ static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) { } } -static uint8_t bt_hid_keyboard_get_selected_key(BtHidKeyboardModel* model) { - BtHidKeyboardKey key = bt_hid_keyboard_keyset[model->y][model->x]; +static uint8_t hid_keyboard_get_selected_key(HidKeyboardModel* model) { + HidKeyboardKey key = hid_keyboard_keyset[model->y][model->x]; // Use upper case if shift is toggled bool useUppercase = model->shift; // Check if the key has an upper case version @@ -259,34 +259,34 @@ static uint8_t bt_hid_keyboard_get_selected_key(BtHidKeyboardModel* model) { return key.value; } -static void bt_hid_keyboard_get_select_key(BtHidKeyboardModel* model, BtHidKeyboardPoint delta) { +static void hid_keyboard_get_select_key(HidKeyboardModel* model, HidKeyboardPoint delta) { // Keep going until a valid spot is found, this allows for nulls and zero width keys in the map do { if(((int8_t)model->y) + delta.y < 0) model->y = ROW_COUNT - 1; else model->y = (model->y + delta.y) % ROW_COUNT; - } while(delta.y != 0 && bt_hid_keyboard_keyset[model->y][model->x].value == 0); + } while(delta.y != 0 && hid_keyboard_keyset[model->y][model->x].value == 0); do { if(((int8_t)model->x) + delta.x < 0) model->x = COLUMN_COUNT - 1; else model->x = (model->x + delta.x) % COLUMN_COUNT; - } while(delta.x != 0 && bt_hid_keyboard_keyset[model->y][model->x].width == + } while(delta.x != 0 && hid_keyboard_keyset[model->y][model->x].width == 0); // Skip zero width keys, pretend they are one key } -static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* event) { +static void hid_keyboard_process(HidKeyboard* hid_keyboard, InputEvent* event) { with_view_model( - bt_hid_keyboard->view, - BtHidKeyboardModel * model, + hid_keyboard->view, + HidKeyboardModel * model, { if(event->key == InputKeyOk) { if(event->type == InputTypePress) { model->ok_pressed = true; } else if(event->type == InputTypeLong || event->type == InputTypeShort) { - model->last_key_code = bt_hid_keyboard_get_selected_key(model); + model->last_key_code = hid_keyboard_get_selected_key(model); // Toggle the modifier key when clicked, and click the key if(model->last_key_code == HID_KEYBOARD_L_SHIFT) { @@ -314,10 +314,12 @@ static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* else model->modifier_code &= ~KEY_MOD_LEFT_GUI; } - furi_hal_bt_hid_kb_press(model->modifier_code | model->last_key_code); + hid_hal_keyboard_press( + hid_keyboard->hid, model->modifier_code | model->last_key_code); } else if(event->type == InputTypeRelease) { // Release happens after short and long presses - furi_hal_bt_hid_kb_release(model->modifier_code | model->last_key_code); + hid_hal_keyboard_release( + hid_keyboard->hid, model->modifier_code | model->last_key_code); model->ok_pressed = false; } } else if(event->key == InputKeyBack) { @@ -325,66 +327,67 @@ static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* if(event->type == InputTypePress) { model->back_pressed = true; } else if(event->type == InputTypeShort) { - furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE); - furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE); + hid_hal_keyboard_press(hid_keyboard->hid, HID_KEYBOARD_DELETE); + hid_hal_keyboard_release(hid_keyboard->hid, HID_KEYBOARD_DELETE); } else if(event->type == InputTypeRelease) { model->back_pressed = false; } } else if(event->type == InputTypePress || event->type == InputTypeRepeat) { // Cycle the selected keys if(event->key == InputKeyUp) { - bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = -1}); + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = -1}); } else if(event->key == InputKeyDown) { - bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = 1}); + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = 1}); } else if(event->key == InputKeyLeft) { - bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = -1, .y = 0}); + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = -1, .y = 0}); } else if(event->key == InputKeyRight) { - bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 1, .y = 0}); + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 1, .y = 0}); } } }, true); } -static bool bt_hid_keyboard_input_callback(InputEvent* event, void* context) { +static bool hid_keyboard_input_callback(InputEvent* event, void* context) { furi_assert(context); - BtHidKeyboard* bt_hid_keyboard = context; + HidKeyboard* hid_keyboard = context; bool consumed = false; if(event->type == InputTypeLong && event->key == InputKeyBack) { - furi_hal_bt_hid_kb_release_all(); + hid_hal_keyboard_release_all(hid_keyboard->hid); } else { - bt_hid_keyboard_process(bt_hid_keyboard, event); + hid_keyboard_process(hid_keyboard, event); consumed = true; } return consumed; } -BtHidKeyboard* bt_hid_keyboard_alloc() { - BtHidKeyboard* bt_hid_keyboard = malloc(sizeof(BtHidKeyboard)); - bt_hid_keyboard->view = view_alloc(); - view_set_context(bt_hid_keyboard->view, bt_hid_keyboard); - view_allocate_model(bt_hid_keyboard->view, ViewModelTypeLocking, sizeof(BtHidKeyboardModel)); - view_set_draw_callback(bt_hid_keyboard->view, bt_hid_keyboard_draw_callback); - view_set_input_callback(bt_hid_keyboard->view, bt_hid_keyboard_input_callback); +HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) { + HidKeyboard* hid_keyboard = malloc(sizeof(HidKeyboard)); + hid_keyboard->view = view_alloc(); + hid_keyboard->hid = bt_hid; + view_set_context(hid_keyboard->view, hid_keyboard); + view_allocate_model(hid_keyboard->view, ViewModelTypeLocking, sizeof(HidKeyboardModel)); + view_set_draw_callback(hid_keyboard->view, hid_keyboard_draw_callback); + view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback); - return bt_hid_keyboard; + return hid_keyboard; } -void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard) { - furi_assert(bt_hid_keyboard); - view_free(bt_hid_keyboard->view); - free(bt_hid_keyboard); +void hid_keyboard_free(HidKeyboard* hid_keyboard) { + furi_assert(hid_keyboard); + view_free(hid_keyboard->view); + free(hid_keyboard); } -View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard) { - furi_assert(bt_hid_keyboard); - return bt_hid_keyboard->view; +View* hid_keyboard_get_view(HidKeyboard* hid_keyboard) { + furi_assert(hid_keyboard); + return hid_keyboard->view; } -void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected) { - furi_assert(bt_hid_keyboard); +void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected) { + furi_assert(hid_keyboard); with_view_model( - bt_hid_keyboard->view, BtHidKeyboardModel * model, { model->connected = connected; }, true); + hid_keyboard->view, HidKeyboardModel * model, { model->connected = connected; }, true); } diff --git a/applications/plugins/hid_app/views/hid_keyboard.h b/applications/plugins/hid_app/views/hid_keyboard.h new file mode 100644 index 000000000..712771364 --- /dev/null +++ b/applications/plugins/hid_app/views/hid_keyboard.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidKeyboard HidKeyboard; + +HidKeyboard* hid_keyboard_alloc(Hid* bt_hid); + +void hid_keyboard_free(HidKeyboard* hid_keyboard); + +View* hid_keyboard_get_view(HidKeyboard* hid_keyboard); + +void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_keynote.c b/applications/plugins/hid_app/views/hid_keynote.c similarity index 60% rename from applications/plugins/bt_hid_app/views/bt_hid_keynote.c rename to applications/plugins/hid_app/views/hid_keynote.c index 0e81c5fa0..c95f42780 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_keynote.c +++ b/applications/plugins/hid_app/views/hid_keynote.c @@ -1,13 +1,14 @@ -#include "bt_hid_keynote.h" -#include -#include -#include +#include "hid_keynote.h" #include +#include "../hid.h" -#include "bt_hid_icons.h" +#include "hid_icons.h" -struct BtHidKeynote { +#define TAG "HidKeynote" + +struct HidKeynote { View* view; + Hid* hid; }; typedef struct { @@ -18,9 +19,9 @@ typedef struct { bool ok_pressed; bool back_pressed; bool connected; -} BtHidKeynoteModel; +} HidKeynoteModel; -static void bt_hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { +static void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { canvas_draw_triangle(canvas, x, y, 5, 3, dir); if(dir == CanvasDirectionBottomToTop) { canvas_draw_line(canvas, x, y + 6, x, y - 1); @@ -33,9 +34,9 @@ static void bt_hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, Canv } } -static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { +static void hid_keynote_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - BtHidKeynoteModel* model = context; + HidKeynoteModel* model = context; // Header if(model->connected) { @@ -56,7 +57,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, 24, 26, 13, 13); canvas_set_color(canvas, ColorWhite); } - bt_hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); + hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); canvas_set_color(canvas, ColorBlack); // Down @@ -65,7 +66,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, 24, 47, 13, 13); canvas_set_color(canvas, ColorWhite); } - bt_hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); + hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); canvas_set_color(canvas, ColorBlack); // Left @@ -74,7 +75,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, 3, 47, 13, 13); canvas_set_color(canvas, ColorWhite); } - bt_hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); + hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); canvas_set_color(canvas, ColorBlack); // Right @@ -83,7 +84,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, 45, 47, 13, 13); canvas_set_color(canvas, ColorWhite); } - bt_hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); + hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); canvas_set_color(canvas, ColorBlack); // Ok @@ -106,100 +107,101 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); } -static void bt_hid_keynote_process(BtHidKeynote* bt_hid_keynote, InputEvent* event) { +static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) { with_view_model( - bt_hid_keynote->view, - BtHidKeynoteModel * model, + hid_keynote->view, + HidKeynoteModel * model, { if(event->type == InputTypePress) { if(event->key == InputKeyUp) { model->up_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_UP_ARROW); + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_UP_ARROW); } else if(event->key == InputKeyDown) { model->down_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_DOWN_ARROW); + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW); } else if(event->key == InputKeyLeft) { model->left_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_LEFT_ARROW); + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW); } else if(event->key == InputKeyRight) { model->right_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_RIGHT_ARROW); + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW); } else if(event->key == InputKeyOk) { model->ok_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_SPACEBAR); } else if(event->key == InputKeyBack) { model->back_pressed = true; } } else if(event->type == InputTypeRelease) { if(event->key == InputKeyUp) { model->up_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_UP_ARROW); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_UP_ARROW); } else if(event->key == InputKeyDown) { model->down_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_DOWN_ARROW); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW); } else if(event->key == InputKeyLeft) { model->left_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_LEFT_ARROW); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW); } else if(event->key == InputKeyRight) { model->right_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_RIGHT_ARROW); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW); } else if(event->key == InputKeyOk) { model->ok_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_SPACEBAR); } else if(event->key == InputKeyBack) { model->back_pressed = false; } } else if(event->type == InputTypeShort) { if(event->key == InputKeyBack) { - furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE); - furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE); - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_AC_BACK); - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_AC_BACK); + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DELETE); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DELETE); + hid_hal_consumer_key_press(hid_keynote->hid, HID_CONSUMER_AC_BACK); + hid_hal_consumer_key_release(hid_keynote->hid, HID_CONSUMER_AC_BACK); } } }, true); } -static bool bt_hid_keynote_input_callback(InputEvent* event, void* context) { +static bool hid_keynote_input_callback(InputEvent* event, void* context) { furi_assert(context); - BtHidKeynote* bt_hid_keynote = context; + HidKeynote* hid_keynote = context; bool consumed = false; if(event->type == InputTypeLong && event->key == InputKeyBack) { - furi_hal_bt_hid_kb_release_all(); + hid_hal_keyboard_release_all(hid_keynote->hid); } else { - bt_hid_keynote_process(bt_hid_keynote, event); + hid_keynote_process(hid_keynote, event); consumed = true; } return consumed; } -BtHidKeynote* bt_hid_keynote_alloc() { - BtHidKeynote* bt_hid_keynote = malloc(sizeof(BtHidKeynote)); - bt_hid_keynote->view = view_alloc(); - view_set_context(bt_hid_keynote->view, bt_hid_keynote); - view_allocate_model(bt_hid_keynote->view, ViewModelTypeLocking, sizeof(BtHidKeynoteModel)); - view_set_draw_callback(bt_hid_keynote->view, bt_hid_keynote_draw_callback); - view_set_input_callback(bt_hid_keynote->view, bt_hid_keynote_input_callback); +HidKeynote* hid_keynote_alloc(Hid* hid) { + HidKeynote* hid_keynote = malloc(sizeof(HidKeynote)); + hid_keynote->view = view_alloc(); + hid_keynote->hid = hid; + view_set_context(hid_keynote->view, hid_keynote); + view_allocate_model(hid_keynote->view, ViewModelTypeLocking, sizeof(HidKeynoteModel)); + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); + view_set_input_callback(hid_keynote->view, hid_keynote_input_callback); - return bt_hid_keynote; + return hid_keynote; } -void bt_hid_keynote_free(BtHidKeynote* bt_hid_keynote) { - furi_assert(bt_hid_keynote); - view_free(bt_hid_keynote->view); - free(bt_hid_keynote); +void hid_keynote_free(HidKeynote* hid_keynote) { + furi_assert(hid_keynote); + view_free(hid_keynote->view); + free(hid_keynote); } -View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote) { - furi_assert(bt_hid_keynote); - return bt_hid_keynote->view; +View* hid_keynote_get_view(HidKeynote* hid_keynote) { + furi_assert(hid_keynote); + return hid_keynote->view; } -void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected) { - furi_assert(bt_hid_keynote); +void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) { + furi_assert(hid_keynote); with_view_model( - bt_hid_keynote->view, BtHidKeynoteModel * model, { model->connected = connected; }, true); + hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true); } diff --git a/applications/plugins/hid_app/views/hid_keynote.h b/applications/plugins/hid_app/views/hid_keynote.h new file mode 100644 index 000000000..4d4a0a9b1 --- /dev/null +++ b/applications/plugins/hid_app/views/hid_keynote.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidKeynote HidKeynote; + +HidKeynote* hid_keynote_alloc(Hid* bt_hid); + +void hid_keynote_free(HidKeynote* hid_keynote); + +View* hid_keynote_get_view(HidKeynote* hid_keynote); + +void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_media.c b/applications/plugins/hid_app/views/hid_media.c similarity index 59% rename from applications/plugins/bt_hid_app/views/bt_hid_media.c rename to applications/plugins/hid_app/views/hid_media.c index df7349a97..5d764f73a 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_media.c +++ b/applications/plugins/hid_app/views/hid_media.c @@ -1,13 +1,17 @@ -#include "bt_hid_media.h" +#include "hid_media.h" #include #include #include #include +#include "../hid.h" -#include "bt_hid_icons.h" +#include "hid_icons.h" -struct BtHidMedia { +#define TAG "HidMedia" + +struct HidMedia { View* view; + Hid* hid; }; typedef struct { @@ -17,9 +21,9 @@ typedef struct { bool down_pressed; bool ok_pressed; bool connected; -} BtHidMediaModel; +} HidMediaModel; -static void bt_hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { +static void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { canvas_draw_triangle(canvas, x, y, 5, 3, dir); if(dir == CanvasDirectionBottomToTop) { canvas_draw_dot(canvas, x, y - 1); @@ -32,9 +36,9 @@ static void bt_hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, Canvas } } -static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { +static void hid_media_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - BtHidMediaModel* model = context; + HidMediaModel* model = context; // Header if(model->connected) { @@ -76,8 +80,8 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - bt_hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); - bt_hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft); + hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); + hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft); canvas_set_color(canvas, ColorBlack); // Right @@ -87,8 +91,8 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - bt_hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); - bt_hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight); + hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); + hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight); canvas_set_color(canvas, ColorBlack); // Ok @@ -96,7 +100,7 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13); canvas_set_color(canvas, ColorWhite); } - bt_hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); + hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); canvas_draw_line(canvas, 100, 29, 100, 33); canvas_draw_line(canvas, 102, 29, 102, 33); canvas_set_color(canvas, ColorBlack); @@ -107,100 +111,101 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); } -static void bt_hid_media_process_press(BtHidMedia* bt_hid_media, InputEvent* event) { +static void hid_media_process_press(HidMedia* hid_media, InputEvent* event) { with_view_model( - bt_hid_media->view, - BtHidMediaModel * model, + hid_media->view, + HidMediaModel * model, { if(event->key == InputKeyUp) { model->up_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyDown) { model->down_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT); + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT); } else if(event->key == InputKeyLeft) { model->left_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_SCAN_PREVIOUS_TRACK); + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); } else if(event->key == InputKeyRight) { model->right_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_SCAN_NEXT_TRACK); + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK); } else if(event->key == InputKeyOk) { model->ok_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_PLAY_PAUSE); + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); } }, true); } -static void bt_hid_media_process_release(BtHidMedia* bt_hid_media, InputEvent* event) { +static void hid_media_process_release(HidMedia* hid_media, InputEvent* event) { with_view_model( - bt_hid_media->view, - BtHidMediaModel * model, + hid_media->view, + HidMediaModel * model, { if(event->key == InputKeyUp) { model->up_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyDown) { model->down_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT); + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT); } else if(event->key == InputKeyLeft) { model->left_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_SCAN_PREVIOUS_TRACK); + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); } else if(event->key == InputKeyRight) { model->right_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_SCAN_NEXT_TRACK); + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK); } else if(event->key == InputKeyOk) { model->ok_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_PLAY_PAUSE); + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); } }, true); } -static bool bt_hid_media_input_callback(InputEvent* event, void* context) { +static bool hid_media_input_callback(InputEvent* event, void* context) { furi_assert(context); - BtHidMedia* bt_hid_media = context; + HidMedia* hid_media = context; bool consumed = false; if(event->type == InputTypePress) { - bt_hid_media_process_press(bt_hid_media, event); + hid_media_process_press(hid_media, event); consumed = true; } else if(event->type == InputTypeRelease) { - bt_hid_media_process_release(bt_hid_media, event); + hid_media_process_release(hid_media, event); consumed = true; } else if(event->type == InputTypeShort) { if(event->key == InputKeyBack) { - furi_hal_bt_hid_consumer_key_release_all(); + hid_hal_consumer_key_release_all(hid_media->hid); } } return consumed; } -BtHidMedia* bt_hid_media_alloc() { - BtHidMedia* bt_hid_media = malloc(sizeof(BtHidMedia)); - bt_hid_media->view = view_alloc(); - view_set_context(bt_hid_media->view, bt_hid_media); - view_allocate_model(bt_hid_media->view, ViewModelTypeLocking, sizeof(BtHidMediaModel)); - view_set_draw_callback(bt_hid_media->view, bt_hid_media_draw_callback); - view_set_input_callback(bt_hid_media->view, bt_hid_media_input_callback); +HidMedia* hid_media_alloc(Hid* hid) { + HidMedia* hid_media = malloc(sizeof(HidMedia)); + hid_media->view = view_alloc(); + hid_media->hid = hid; + view_set_context(hid_media->view, hid_media); + view_allocate_model(hid_media->view, ViewModelTypeLocking, sizeof(HidMediaModel)); + view_set_draw_callback(hid_media->view, hid_media_draw_callback); + view_set_input_callback(hid_media->view, hid_media_input_callback); - return bt_hid_media; + return hid_media; } -void bt_hid_media_free(BtHidMedia* bt_hid_media) { - furi_assert(bt_hid_media); - view_free(bt_hid_media->view); - free(bt_hid_media); +void hid_media_free(HidMedia* hid_media) { + furi_assert(hid_media); + view_free(hid_media->view); + free(hid_media); } -View* bt_hid_media_get_view(BtHidMedia* bt_hid_media) { - furi_assert(bt_hid_media); - return bt_hid_media->view; +View* hid_media_get_view(HidMedia* hid_media) { + furi_assert(hid_media); + return hid_media->view; } -void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected) { - furi_assert(bt_hid_media); +void hid_media_set_connected_status(HidMedia* hid_media, bool connected) { + furi_assert(hid_media); with_view_model( - bt_hid_media->view, BtHidMediaModel * model, { model->connected = connected; }, true); + hid_media->view, HidMediaModel * model, { model->connected = connected; }, true); } diff --git a/applications/plugins/hid_app/views/hid_media.h b/applications/plugins/hid_app/views/hid_media.h new file mode 100644 index 000000000..4aa51dc17 --- /dev/null +++ b/applications/plugins/hid_app/views/hid_media.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef struct HidMedia HidMedia; + +HidMedia* hid_media_alloc(); + +void hid_media_free(HidMedia* hid_media); + +View* hid_media_get_view(HidMedia* hid_media); + +void hid_media_set_connected_status(HidMedia* hid_media, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_mouse.c b/applications/plugins/hid_app/views/hid_mouse.c similarity index 68% rename from applications/plugins/bt_hid_app/views/bt_hid_mouse.c rename to applications/plugins/hid_app/views/hid_mouse.c index bd48bab16..d1d76c15a 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_mouse.c +++ b/applications/plugins/hid_app/views/hid_mouse.c @@ -1,16 +1,15 @@ -#include "bt_hid_mouse.h" -#include -#include -#include +#include "hid_mouse.h" #include +#include "../hid.h" -#include "bt_hid_icons.h" +#include "hid_icons.h" -struct BtHidMouse { +#define TAG "HidMouse" + +struct HidMouse { View* view; + Hid* hid; }; -#define MOUSE_MOVE_SHORT 5 -#define MOUSE_MOVE_LONG 20 typedef struct { bool left_pressed; @@ -21,11 +20,11 @@ typedef struct { bool left_mouse_held; bool right_mouse_pressed; bool connected; -} BtHidMouseModel; +} HidMouseModel; -static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { +static void hid_mouse_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - BtHidMouseModel* model = context; + HidMouseModel* model = context; // Header if(model->connected) { @@ -103,15 +102,15 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { } } -static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { +static void hid_mouse_process(HidMouse* hid_mouse, InputEvent* event) { with_view_model( - bt_hid_mouse->view, - BtHidMouseModel * model, + hid_mouse->view, + HidMouseModel * model, { if(event->key == InputKeyBack) { if(event->type == InputTypeShort) { - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_RIGHT); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_RIGHT); + hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_RIGHT); + hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_RIGHT); } else if(event->type == InputTypePress) { model->right_mouse_pressed = true; } else if(event->type == InputTypeRelease) { @@ -120,11 +119,12 @@ static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { } else if(event->key == InputKeyOk) { if(event->type == InputTypeShort) { // Just release if it was being held before - if(!model->left_mouse_held) furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); + if(!model->left_mouse_held) + hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_LEFT); model->left_mouse_held = false; } else if(event->type == InputTypeLong) { - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); + hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT); model->left_mouse_held = true; model->left_mouse_pressed = true; } else if(event->type == InputTypePress) { @@ -133,40 +133,39 @@ static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { // Only release if it wasn't a long press if(!model->left_mouse_held) model->left_mouse_pressed = false; } - } else if(event->key == InputKeyRight) { if(event->type == InputTypePress) { model->right_pressed = true; - furi_hal_bt_hid_mouse_move(MOUSE_MOVE_SHORT, 0); + hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_SHORT, 0); } else if(event->type == InputTypeRepeat) { - furi_hal_bt_hid_mouse_move(MOUSE_MOVE_LONG, 0); + hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_LONG, 0); } else if(event->type == InputTypeRelease) { model->right_pressed = false; } } else if(event->key == InputKeyLeft) { if(event->type == InputTypePress) { model->left_pressed = true; - furi_hal_bt_hid_mouse_move(-MOUSE_MOVE_SHORT, 0); + hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_SHORT, 0); } else if(event->type == InputTypeRepeat) { - furi_hal_bt_hid_mouse_move(-MOUSE_MOVE_LONG, 0); + hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_LONG, 0); } else if(event->type == InputTypeRelease) { model->left_pressed = false; } } else if(event->key == InputKeyDown) { if(event->type == InputTypePress) { model->down_pressed = true; - furi_hal_bt_hid_mouse_move(0, MOUSE_MOVE_SHORT); + hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_SHORT); } else if(event->type == InputTypeRepeat) { - furi_hal_bt_hid_mouse_move(0, MOUSE_MOVE_LONG); + hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_LONG); } else if(event->type == InputTypeRelease) { model->down_pressed = false; } } else if(event->key == InputKeyUp) { if(event->type == InputTypePress) { model->up_pressed = true; - furi_hal_bt_hid_mouse_move(0, -MOUSE_MOVE_SHORT); + hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_SHORT); } else if(event->type == InputTypeRepeat) { - furi_hal_bt_hid_mouse_move(0, -MOUSE_MOVE_LONG); + hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_LONG); } else if(event->type == InputTypeRelease) { model->up_pressed = false; } @@ -175,45 +174,46 @@ static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { true); } -static bool bt_hid_mouse_input_callback(InputEvent* event, void* context) { +static bool hid_mouse_input_callback(InputEvent* event, void* context) { furi_assert(context); - BtHidMouse* bt_hid_mouse = context; + HidMouse* hid_mouse = context; bool consumed = false; if(event->type == InputTypeLong && event->key == InputKeyBack) { - furi_hal_bt_hid_mouse_release_all(); + hid_hal_mouse_release_all(hid_mouse->hid); } else { - bt_hid_mouse_process(bt_hid_mouse, event); + hid_mouse_process(hid_mouse, event); consumed = true; } return consumed; } -BtHidMouse* bt_hid_mouse_alloc() { - BtHidMouse* bt_hid_mouse = malloc(sizeof(BtHidMouse)); - bt_hid_mouse->view = view_alloc(); - view_set_context(bt_hid_mouse->view, bt_hid_mouse); - view_allocate_model(bt_hid_mouse->view, ViewModelTypeLocking, sizeof(BtHidMouseModel)); - view_set_draw_callback(bt_hid_mouse->view, bt_hid_mouse_draw_callback); - view_set_input_callback(bt_hid_mouse->view, bt_hid_mouse_input_callback); +HidMouse* hid_mouse_alloc(Hid* hid) { + HidMouse* hid_mouse = malloc(sizeof(HidMouse)); + hid_mouse->view = view_alloc(); + hid_mouse->hid = hid; + view_set_context(hid_mouse->view, hid_mouse); + view_allocate_model(hid_mouse->view, ViewModelTypeLocking, sizeof(HidMouseModel)); + view_set_draw_callback(hid_mouse->view, hid_mouse_draw_callback); + view_set_input_callback(hid_mouse->view, hid_mouse_input_callback); - return bt_hid_mouse; + return hid_mouse; } -void bt_hid_mouse_free(BtHidMouse* bt_hid_mouse) { - furi_assert(bt_hid_mouse); - view_free(bt_hid_mouse->view); - free(bt_hid_mouse); +void hid_mouse_free(HidMouse* hid_mouse) { + furi_assert(hid_mouse); + view_free(hid_mouse->view); + free(hid_mouse); } -View* bt_hid_mouse_get_view(BtHidMouse* bt_hid_mouse) { - furi_assert(bt_hid_mouse); - return bt_hid_mouse->view; +View* hid_mouse_get_view(HidMouse* hid_mouse) { + furi_assert(hid_mouse); + return hid_mouse->view; } -void bt_hid_mouse_set_connected_status(BtHidMouse* bt_hid_mouse, bool connected) { - furi_assert(bt_hid_mouse); +void hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected) { + furi_assert(hid_mouse); with_view_model( - bt_hid_mouse->view, BtHidMouseModel * model, { model->connected = connected; }, true); + hid_mouse->view, HidMouseModel * model, { model->connected = connected; }, true); } diff --git a/applications/plugins/hid_app/views/hid_mouse.h b/applications/plugins/hid_app/views/hid_mouse.h new file mode 100644 index 000000000..d9fb2fd88 --- /dev/null +++ b/applications/plugins/hid_app/views/hid_mouse.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#define MOUSE_MOVE_SHORT 5 +#define MOUSE_MOVE_LONG 20 + +typedef struct Hid Hid; +typedef struct HidMouse HidMouse; + +HidMouse* hid_mouse_alloc(Hid* bt_hid); + +void hid_mouse_free(HidMouse* hid_mouse); + +View* hid_mouse_get_view(HidMouse* hid_mouse); + +void hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected); diff --git a/applications/plugins/bt_hid_app/views/bt_hid_tiktok.c b/applications/plugins/hid_app/views/hid_tiktok.c similarity index 59% rename from applications/plugins/bt_hid_app/views/bt_hid_tiktok.c rename to applications/plugins/hid_app/views/hid_tiktok.c index fe1005b2d..42abf4148 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_tiktok.c +++ b/applications/plugins/hid_app/views/hid_tiktok.c @@ -1,13 +1,14 @@ -#include "bt_hid_tiktok.h" -#include -#include -#include +#include "hid_tiktok.h" +#include "../hid.h" #include -#include "bt_hid_icons.h" +#include "hid_icons.h" -struct BtHidTikTok { +#define TAG "HidTikTok" + +struct HidTikTok { View* view; + Hid* hid; }; typedef struct { @@ -18,11 +19,11 @@ typedef struct { bool ok_pressed; bool connected; bool is_cursor_set; -} BtHidTikTokModel; +} HidTikTokModel; -static void bt_hid_tiktok_draw_callback(Canvas* canvas, void* context) { +static void hid_tiktok_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - BtHidTikTokModel* model = context; + HidTikTokModel* model = context; // Header if(model->connected) { @@ -89,102 +90,104 @@ 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_reset_cursor() { +static void hid_tiktok_reset_cursor(HidTikTok* hid_tiktok) { // 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); + hid_hal_mouse_move(hid_tiktok->hid, -127, -127); furi_delay_ms(50); } // Move cursor from the corner - furi_hal_bt_hid_mouse_move(20, 120); + hid_hal_mouse_move(hid_tiktok->hid, 20, 120); furi_delay_ms(50); } -static void bt_hid_tiktok_process_press(BtHidTikTokModel* model, InputEvent* event) { +static void + hid_tiktok_process_press(HidTikTok* hid_tiktok, HidTikTokModel* 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); + hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT); } else if(event->key == InputKeyRight) { model->right_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); + hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyOk) { model->ok_pressed = true; } } -static void bt_hid_tiktok_process_release(BtHidTikTokModel* model, InputEvent* event) { +static void + hid_tiktok_process_release(HidTikTok* hid_tiktok, HidTikTokModel* 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); + hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT); } else if(event->key == InputKeyRight) { model->right_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); + hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyOk) { model->ok_pressed = false; } } -static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) { +static bool hid_tiktok_input_callback(InputEvent* event, void* context) { furi_assert(context); - BtHidTikTok* bt_hid_tiktok = context; + HidTikTok* hid_tiktok = context; bool consumed = false; with_view_model( - bt_hid_tiktok->view, - BtHidTikTokModel * model, + hid_tiktok->view, + HidTikTokModel * model, { if(event->type == InputTypePress) { - bt_hid_tiktok_process_press(model, event); + hid_tiktok_process_press(hid_tiktok, model, event); if(model->connected && !model->is_cursor_set) { - bt_hid_tiktok_reset_cursor(); + hid_tiktok_reset_cursor(hid_tiktok); model->is_cursor_set = true; } consumed = true; } else if(event->type == InputTypeRelease) { - bt_hid_tiktok_process_release(model, event); + hid_tiktok_process_release(hid_tiktok, model, event); consumed = true; } else if(event->type == InputTypeShort) { if(event->key == InputKeyOk) { - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); + hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); furi_delay_ms(50); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); furi_delay_ms(50); - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); + hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); furi_delay_ms(50); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_tiktok->hid, 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); + hid_hal_mouse_scroll(hid_tiktok->hid, -6); + hid_hal_mouse_scroll(hid_tiktok->hid, -12); + hid_hal_mouse_scroll(hid_tiktok->hid, -19); + hid_hal_mouse_scroll(hid_tiktok->hid, -12); + hid_hal_mouse_scroll(hid_tiktok->hid, -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); + hid_hal_mouse_scroll(hid_tiktok->hid, 6); + hid_hal_mouse_scroll(hid_tiktok->hid, 12); + hid_hal_mouse_scroll(hid_tiktok->hid, 19); + hid_hal_mouse_scroll(hid_tiktok->hid, 12); + hid_hal_mouse_scroll(hid_tiktok->hid, 6); consumed = true; } else if(event->key == InputKeyBack) { - furi_hal_bt_hid_consumer_key_release_all(); + hid_hal_consumer_key_release_all(hid_tiktok->hid); consumed = true; } } else if(event->type == InputTypeLong) { if(event->key == InputKeyBack) { - furi_hal_bt_hid_consumer_key_release_all(); + hid_hal_consumer_key_release_all(hid_tiktok->hid); model->is_cursor_set = false; consumed = false; } @@ -195,33 +198,34 @@ static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) { return consumed; } -BtHidTikTok* bt_hid_tiktok_alloc() { - BtHidTikTok* bt_hid_tiktok = malloc(sizeof(BtHidTikTok)); - bt_hid_tiktok->view = view_alloc(); - view_set_context(bt_hid_tiktok->view, bt_hid_tiktok); - view_allocate_model(bt_hid_tiktok->view, ViewModelTypeLocking, sizeof(BtHidTikTokModel)); - view_set_draw_callback(bt_hid_tiktok->view, bt_hid_tiktok_draw_callback); - view_set_input_callback(bt_hid_tiktok->view, bt_hid_tiktok_input_callback); +HidTikTok* hid_tiktok_alloc(Hid* bt_hid) { + HidTikTok* hid_tiktok = malloc(sizeof(HidTikTok)); + hid_tiktok->hid = bt_hid; + hid_tiktok->view = view_alloc(); + view_set_context(hid_tiktok->view, hid_tiktok); + view_allocate_model(hid_tiktok->view, ViewModelTypeLocking, sizeof(HidTikTokModel)); + view_set_draw_callback(hid_tiktok->view, hid_tiktok_draw_callback); + view_set_input_callback(hid_tiktok->view, hid_tiktok_input_callback); - return bt_hid_tiktok; + return hid_tiktok; } -void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok) { - furi_assert(bt_hid_tiktok); - view_free(bt_hid_tiktok->view); - free(bt_hid_tiktok); +void hid_tiktok_free(HidTikTok* hid_tiktok) { + furi_assert(hid_tiktok); + view_free(hid_tiktok->view); + free(hid_tiktok); } -View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok) { - furi_assert(bt_hid_tiktok); - return bt_hid_tiktok->view; +View* hid_tiktok_get_view(HidTikTok* hid_tiktok) { + furi_assert(hid_tiktok); + return hid_tiktok->view; } -void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected) { - furi_assert(bt_hid_tiktok); +void hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected) { + furi_assert(hid_tiktok); with_view_model( - bt_hid_tiktok->view, - BtHidTikTokModel * model, + hid_tiktok->view, + HidTikTokModel * model, { model->connected = connected; model->is_cursor_set = false; diff --git a/applications/plugins/hid_app/views/hid_tiktok.h b/applications/plugins/hid_app/views/hid_tiktok.h new file mode 100644 index 000000000..b2efc3692 --- /dev/null +++ b/applications/plugins/hid_app/views/hid_tiktok.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidTikTok HidTikTok; + +HidTikTok* hid_tiktok_alloc(Hid* bt_hid); + +void hid_tiktok_free(HidTikTok* hid_tiktok); + +View* hid_tiktok_get_view(HidTikTok* hid_tiktok); + +void hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected); diff --git a/applications/plugins/music_player/music_player.c b/applications/plugins/music_player/music_player.c index 28872284b..07d4e2df4 100644 --- a/applications/plugins/music_player/music_player.c +++ b/applications/plugins/music_player/music_player.c @@ -313,6 +313,7 @@ int32_t music_player_app(void* p) { dialog_file_browser_set_basic_options( &browser_options, MUSIC_PLAYER_APP_EXTENSION, &I_music_10px); browser_options.hide_ext = false; + browser_options.base_path = MUSIC_PLAYER_APP_PATH_FOLDER; DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options); diff --git a/applications/plugins/music_player/music_player_worker.c b/applications/plugins/music_player/music_player_worker.c index 3f1ac62f7..0d683f4a6 100644 --- a/applications/plugins/music_player/music_player_worker.c +++ b/applications/plugins/music_player/music_player_worker.c @@ -97,11 +97,8 @@ MusicPlayerWorker* music_player_worker_alloc() { NoteBlockArray_init(instance->notes); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "MusicPlayerWorker"); - furi_thread_set_stack_size(instance->thread, 1024); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, music_player_worker_thread_callback); + instance->thread = furi_thread_alloc_ex( + "MusicPlayerWorker", 1024, music_player_worker_thread_callback, instance); instance->volume = 1.0f; diff --git a/applications/plugins/nfc_magic/lib/magic/magic.c b/applications/plugins/nfc_magic/lib/magic/magic.c index 3cfca748b..a922bc7a8 100644 --- a/applications/plugins/nfc_magic/lib/magic/magic.c +++ b/applications/plugins/nfc_magic/lib/magic/magic.c @@ -210,5 +210,5 @@ bool magic_wipe() { void magic_deactivate() { furi_hal_nfc_ll_txrx_off(); - furi_hal_nfc_start_sleep(); + furi_hal_nfc_sleep(); } diff --git a/applications/plugins/nfc_magic/nfc_magic_worker.c b/applications/plugins/nfc_magic/nfc_magic_worker.c index 0623211e2..523c794f7 100644 --- a/applications/plugins/nfc_magic/nfc_magic_worker.c +++ b/applications/plugins/nfc_magic/nfc_magic_worker.c @@ -15,11 +15,8 @@ NfcMagicWorker* nfc_magic_worker_alloc() { NfcMagicWorker* nfc_magic_worker = malloc(sizeof(NfcMagicWorker)); // Worker thread attributes - nfc_magic_worker->thread = furi_thread_alloc(); - furi_thread_set_name(nfc_magic_worker->thread, "NfcMagicWorker"); - furi_thread_set_stack_size(nfc_magic_worker->thread, 8192); - furi_thread_set_callback(nfc_magic_worker->thread, nfc_magic_worker_task); - furi_thread_set_context(nfc_magic_worker->thread, nfc_magic_worker); + nfc_magic_worker->thread = + furi_thread_alloc_ex("NfcMagicWorker", 8192, nfc_magic_worker_task, nfc_magic_worker); nfc_magic_worker->callback = NULL; nfc_magic_worker->context = NULL; @@ -167,7 +164,6 @@ void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) { if(!magic_data_access_cmd()) continue; if(!magic_write_blk(0, &block)) continue; nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - magic_deactivate(); break; } magic_deactivate(); diff --git a/applications/plugins/picopass/picopass_device.c b/applications/plugins/picopass/picopass_device.c index 199b79e97..fd8ddbfbd 100644 --- a/applications/plugins/picopass/picopass_device.c +++ b/applications/plugins/picopass/picopass_device.c @@ -231,6 +231,7 @@ bool picopass_file_select(PicopassDevice* dev) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, PICOPASS_APP_EXTENSION, &I_Nfc_10px); + browser_options.base_path = PICOPASS_APP_FOLDER; bool res = dialog_file_browser_show( dev->dialogs, dev->load_path, picopass_app_folder, &browser_options); diff --git a/applications/plugins/picopass/picopass_worker.c b/applications/plugins/picopass/picopass_worker.c index 2c9b1a0ce..c2a32cb6a 100644 --- a/applications/plugins/picopass/picopass_worker.c +++ b/applications/plugins/picopass/picopass_worker.c @@ -25,11 +25,8 @@ PicopassWorker* picopass_worker_alloc() { PicopassWorker* picopass_worker = malloc(sizeof(PicopassWorker)); // Worker thread attributes - picopass_worker->thread = furi_thread_alloc(); - furi_thread_set_name(picopass_worker->thread, "PicopassWorker"); - furi_thread_set_stack_size(picopass_worker->thread, 8192); - furi_thread_set_callback(picopass_worker->thread, picopass_worker_task); - furi_thread_set_context(picopass_worker->thread, picopass_worker); + picopass_worker->thread = + furi_thread_alloc_ex("PicopassWorker", 8192, picopass_worker_task, picopass_worker); picopass_worker->callback = NULL; picopass_worker->context = NULL; diff --git a/applications/plugins/snake_game/snake_game.c b/applications/plugins/snake_game/snake_game.c index f9309c280..ef4ae2ee8 100644 --- a/applications/plugins/snake_game/snake_game.c +++ b/applications/plugins/snake_game/snake_game.c @@ -318,7 +318,6 @@ static void int32_t snake_game_app(void* p) { UNUSED(p); - srand(DWT->CYCCNT); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent)); diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h index 5a66dd0ce..a23540e3d 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.4" +#define WS_VERSION_APP "0.5" #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 index 4d7f59544..5384a3c91 100644 --- a/applications/plugins/weather_station/protocols/acurite_592txr.c +++ b/applications/plugins/weather_station/protocols/acurite_592txr.c @@ -293,7 +293,7 @@ void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* ou "%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%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -301,7 +301,6 @@ void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* ou 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))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.c b/applications/plugins/weather_station/protocols/acurite_606tx.c index 3c9144057..4cb5d18b8 100644 --- a/applications/plugins/weather_station/protocols/acurite_606tx.c +++ b/applications/plugins/weather_station/protocols/acurite_606tx.c @@ -234,7 +234,7 @@ void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* out "%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%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -242,7 +242,6 @@ void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* out 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))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/acurite_609txc.c b/applications/plugins/weather_station/protocols/acurite_609txc.c new file mode 100644 index 000000000..aeb0785eb --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_609txc.c @@ -0,0 +1,247 @@ +#include "acurite_609txc.h" + +#define TAG "WSProtocolAcurite_609TXC" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c#L216 + * + * 0000 1111 | 0011 0000 | 0101 1100 | 0000 0000 | 1110 0111 + * iiii iiii | buuu tttt | tttt tttt | hhhh hhhh | cccc cccc + * - i: identification; changes on battery switch + * - c: checksum (sum of previous by bytes) + * - u: unknown + * - b: battery low; flag to indicate low battery voltage + * - t: temperature; in °C * 10, 12 bit with complement + * - h: humidity + * + */ + +static const SubGhzBlockConst ws_protocol_acurite_609txc_const = { + .te_short = 500, + .te_long = 1000, + .te_delta = 150, + .min_count_bit_for_found = 40, +}; + +struct WSProtocolDecoderAcurite_609TXC { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderAcurite_609TXC { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + Acurite_609TXCDecoderStepReset = 0, + Acurite_609TXCDecoderStepSaveDuration, + Acurite_609TXCDecoderStepCheckDuration, +} Acurite_609TXCDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_acurite_609txc_decoder = { + .alloc = ws_protocol_decoder_acurite_609txc_alloc, + .free = ws_protocol_decoder_acurite_609txc_free, + + .feed = ws_protocol_decoder_acurite_609txc_feed, + .reset = ws_protocol_decoder_acurite_609txc_reset, + + .get_hash_data = ws_protocol_decoder_acurite_609txc_get_hash_data, + .serialize = ws_protocol_decoder_acurite_609txc_serialize, + .deserialize = ws_protocol_decoder_acurite_609txc_deserialize, + .get_string = ws_protocol_decoder_acurite_609txc_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_acurite_609txc_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_acurite_609txc = { + .name = WS_PROTOCOL_ACURITE_609TXC_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_acurite_609txc_decoder, + .encoder = &ws_protocol_acurite_609txc_encoder, +}; + +void* ws_protocol_decoder_acurite_609txc_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderAcurite_609TXC* instance = malloc(sizeof(WSProtocolDecoderAcurite_609TXC)); + instance->base.protocol = &ws_protocol_acurite_609txc; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_acurite_609txc_free(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + free(instance); +} + +void ws_protocol_decoder_acurite_609txc_reset(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; +} + +static bool ws_protocol_acurite_609txc_check(WSProtocolDecoderAcurite_609TXC* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t crc = (uint8_t)(instance->decoder.decode_data >> 32) + + (uint8_t)(instance->decoder.decode_data >> 24) + + (uint8_t)(instance->decoder.decode_data >> 16) + + (uint8_t)(instance->decoder.decode_data >> 8); + return (crc == (instance->decoder.decode_data & 0xFF)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_acurite_609txc_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 32) & 0xFF; + instance->battery_low = (instance->data >> 31) & 1; + + instance->channel = WS_NO_CHANNEL; + + // Temperature in Celsius is encoded as a 12 bit integer value + // multiplied by 10 using the 4th - 6th nybbles (bytes 1 & 2) + // negative values are recovered by sign extend from int16_t. + int16_t temp_raw = + (int16_t)(((instance->data >> 12) & 0xf000) | ((instance->data >> 16) << 4)); + instance->temp = (temp_raw >> 4) * 0.1f; + instance->humidity = (instance->data >> 8) & 0xff; + instance->btn = WS_NO_BTN; +} + +void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + + switch(instance->decoder.parser_step) { + case Acurite_609TXCDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_short * 17) < + ws_protocol_acurite_609txc_const.te_delta * 8)) { + //Found syncPrefix + instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case Acurite_609TXCDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Acurite_609TXCDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + } + break; + + case Acurite_609TXCDecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_609txc_const.te_short) < + ws_protocol_acurite_609txc_const.te_delta) { + if((DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_short) < + ws_protocol_acurite_609txc_const.te_delta) || + (duration > ws_protocol_acurite_609txc_const.te_long * 3)) { + //Found syncPostfix + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_acurite_609txc_const.min_count_bit_for_found) && + ws_protocol_acurite_609txc_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_acurite_609txc_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else if( + DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_long) < + ws_protocol_acurite_609txc_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_long * 2) < + ws_protocol_acurite_609txc_const.te_delta * 4) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + } + } else { + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + } + } else { + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_acurite_609txc_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* 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_609txc_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_609txc_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* 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:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 40), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/acurite_609txc.h b/applications/plugins/weather_station/protocols/acurite_609txc.h new file mode 100644 index 000000000..f87c20e9b --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_609txc.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_ACURITE_609TXC_NAME "Acurite-609TXC" + +typedef struct WSProtocolDecoderAcurite_609TXC WSProtocolDecoderAcurite_609TXC; +typedef struct WSProtocolEncoderAcurite_609TXC WSProtocolEncoderAcurite_609TXC; + +extern const SubGhzProtocolDecoder ws_protocol_acurite_609txc_decoder; +extern const SubGhzProtocolEncoder ws_protocol_acurite_609txc_encoder; +extern const SubGhzProtocol ws_protocol_acurite_609txc; + +/** + * Allocate WSProtocolDecoderAcurite_609TXC. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderAcurite_609TXC* pointer to a WSProtocolDecoderAcurite_609TXC instance + */ +void* ws_protocol_decoder_acurite_609txc_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderAcurite_609TXC. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + */ +void ws_protocol_decoder_acurite_609txc_free(void* context); + +/** + * Reset decoder WSProtocolDecoderAcurite_609TXC. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + */ +void ws_protocol_decoder_acurite_609txc_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderAcurite_609TXC. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC 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_609txc_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderAcurite_609TXC. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @param output Resulting text + */ +void ws_protocol_decoder_acurite_609txc_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/ambient_weather.c b/applications/plugins/weather_station/protocols/ambient_weather.c index 07f5330fc..5ae22b790 100644 --- a/applications/plugins/weather_station/protocols/ambient_weather.c +++ b/applications/plugins/weather_station/protocols/ambient_weather.c @@ -200,7 +200,6 @@ void ws_protocol_decoder_ambient_weather_feed(void* context, bool level, uint32_ ((instance->decoder.decode_data & AMBIENT_WEATHER_PACKET_HEADER_MASK) == AMBIENT_WEATHER_PACKET_HEADER_2)) { if(ws_protocol_ambient_weather_check_crc(instance)) { - instance->decoder.decode_data = instance->decoder.decode_data; instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = ws_protocol_ambient_weather_const.min_count_bit_for_found; @@ -264,7 +263,7 @@ void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* o "%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%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -272,7 +271,6 @@ void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* o 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))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/auriol_hg0601a.c b/applications/plugins/weather_station/protocols/auriol_hg0601a.c new file mode 100644 index 000000000..d5f89fc8b --- /dev/null +++ b/applications/plugins/weather_station/protocols/auriol_hg0601a.c @@ -0,0 +1,258 @@ +#include "auriol_hg0601a.h" + +#define TAG "WSProtocolAuriol_TH" + +/* + * +Auriol HG06061A-DCF-TX sensor. + +Data layout: + DDDDDDDD-B0-NN-TT-TTTTTTTTTT-CCCC-HHHHHHHH +Exmpl.: 11110100-10-01-00-0001001100-1111-01011101 + +- D: id, 8 bit +- B: where B is the battery status: 1=OK, 0=LOW, 1 bit +- 0: just zero :) +- N: NN is the channel: 00=CH1, 01=CH2, 11=CH3, 2bit +- T: temperature, 12 bit: 2's complement, scaled by 10 +- C: 4 bit: seems to be 0xf constantly, a separator between temp and humidity +- H: humidity sensor, humidity is 8 bits + + * The sensor sends 37 bits 10 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. + * + */ + +#define AURIOL_TH_CONST_DATA 0b1110 + +static const SubGhzBlockConst ws_protocol_auriol_th_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 37, +}; + +struct WSProtocolDecoderAuriol_TH { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderAuriol_TH { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + auriol_THDecoderStepReset = 0, + auriol_THDecoderStepSaveDuration, + auriol_THDecoderStepCheckDuration, +} auriol_THDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_auriol_th_decoder = { + .alloc = ws_protocol_decoder_auriol_th_alloc, + .free = ws_protocol_decoder_auriol_th_free, + + .feed = ws_protocol_decoder_auriol_th_feed, + .reset = ws_protocol_decoder_auriol_th_reset, + + .get_hash_data = ws_protocol_decoder_auriol_th_get_hash_data, + .serialize = ws_protocol_decoder_auriol_th_serialize, + .deserialize = ws_protocol_decoder_auriol_th_deserialize, + .get_string = ws_protocol_decoder_auriol_th_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_auriol_th_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_auriol_th = { + .name = WS_PROTOCOL_AURIOL_TH_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_auriol_th_decoder, + .encoder = &ws_protocol_auriol_th_encoder, +}; + +void* ws_protocol_decoder_auriol_th_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderAuriol_TH* instance = malloc(sizeof(WSProtocolDecoderAuriol_TH)); + instance->base.protocol = &ws_protocol_auriol_th; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_auriol_th_free(void* context) { + furi_assert(context); + WSProtocolDecoderAuriol_TH* instance = context; + free(instance); +} + +void ws_protocol_decoder_auriol_th_reset(void* context) { + furi_assert(context); + WSProtocolDecoderAuriol_TH* instance = context; + instance->decoder.parser_step = auriol_THDecoderStepReset; +} + +static bool ws_protocol_auriol_th_check(WSProtocolDecoderAuriol_TH* instance) { + uint8_t type = (instance->decoder.decode_data >> 8) & 0x0F; + + if((type == AURIOL_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_auriol_th_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 31) & 0xFF; + instance->battery_low = ((instance->data >> 30) & 1); + instance->channel = ((instance->data >> 25) & 0x03) + 1; + instance->btn = WS_NO_BTN; + if(!((instance->data >> 23) & 1)) { + instance->temp = (float)((instance->data >> 13) & 0x07FF) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 13) & 0x07FF) + 1) / -10.0f; + } + + instance->humidity = (instance->data >> 1) & 0x7F; +} + +void ws_protocol_decoder_auriol_th_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderAuriol_TH* instance = context; + + switch(instance->decoder.parser_step) { + case auriol_THDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_auriol_th_const.te_short * 8) < + ws_protocol_auriol_th_const.te_delta)) { + //Found sync + instance->decoder.parser_step = auriol_THDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case auriol_THDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = auriol_THDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = auriol_THDecoderStepReset; + } + break; + + case auriol_THDecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(duration, ws_protocol_auriol_th_const.te_short * 8) < + ws_protocol_auriol_th_const.te_delta) { + //Found sync + instance->decoder.parser_step = auriol_THDecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_auriol_th_const.min_count_bit_for_found) && + ws_protocol_auriol_th_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_auriol_th_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.parser_step = auriol_THDecoderStepCheckDuration; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_auriol_th_const.te_short) < + ws_protocol_auriol_th_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_auriol_th_const.te_short * 2) < + ws_protocol_auriol_th_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = auriol_THDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_auriol_th_const.te_short) < + ws_protocol_auriol_th_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_auriol_th_const.te_short * 4) < + ws_protocol_auriol_th_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = auriol_THDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = auriol_THDecoderStepReset; + } + } else { + instance->decoder.parser_step = auriol_THDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_auriol_th_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderAuriol_TH* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_auriol_th_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderAuriol_TH* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_auriol_th_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderAuriol_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_auriol_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_auriol_th_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderAuriol_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:%3.1f 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, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/auriol_hg0601a.h b/applications/plugins/weather_station/protocols/auriol_hg0601a.h new file mode 100644 index 000000000..c23007c1a --- /dev/null +++ b/applications/plugins/weather_station/protocols/auriol_hg0601a.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_AURIOL_TH_NAME "Auriol HG06061" //HG06061A-DCF-TX + +typedef struct WSProtocolDecoderAuriol_TH WSProtocolDecoderAuriol_TH; +typedef struct WSProtocolEncoderAuriol_TH WSProtocolEncoderAuriol_TH; + +extern const SubGhzProtocolDecoder ws_protocol_auriol_th_decoder; +extern const SubGhzProtocolEncoder ws_protocol_auriol_th_encoder; +extern const SubGhzProtocol ws_protocol_auriol_th; + +/** + * Allocate WSProtocolDecoderAuriol_TH. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderAuriol_TH* pointer to a WSProtocolDecoderAuriol_TH instance + */ +void* ws_protocol_decoder_auriol_th_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderAuriol_TH. + * @param context Pointer to a WSProtocolDecoderAuriol_TH instance + */ +void ws_protocol_decoder_auriol_th_free(void* context); + +/** + * Reset decoder WSProtocolDecoderAuriol_TH. + * @param context Pointer to a WSProtocolDecoderAuriol_TH instance + */ +void ws_protocol_decoder_auriol_th_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderAuriol_TH instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_auriol_th_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderAuriol_TH instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_auriol_th_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderAuriol_TH. + * @param context Pointer to a WSProtocolDecoderAuriol_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_auriol_th_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderAuriol_TH. + * @param context Pointer to a WSProtocolDecoderAuriol_TH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_auriol_th_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderAuriol_TH instance + * @param output Resulting text + */ +void ws_protocol_decoder_auriol_th_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/gt_wt_02.c b/applications/plugins/weather_station/protocols/gt_wt_02.c new file mode 100644 index 000000000..cbe119192 --- /dev/null +++ b/applications/plugins/weather_station/protocols/gt_wt_02.c @@ -0,0 +1,265 @@ +#include "gt_wt_02.h" + +#define TAG "WSProtocolGT_WT02" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/gt_wt_02.c + * + * GT-WT-02 sensor on 433.92MHz. + * Example and frame description provided by https://github.com/ludwich66 + * [01] {37} 34 00 ed 47 60 : 00110100 00000000 11101101 01000111 01100000 + * code, BatOK,not-man-send, Channel1, +23,7°C, 35% + * [01] {37} 34 8f 87 15 90 : 00110100 10001111 10000111 00010101 10010000 + * code, BatOK,not-man-send, Channel1,-12,1°C, 10% + * Humidity: + * - the working range is 20-90 % + * - if "LL" in display view it sends 10 % + * - if "HH" in display view it sends 110% + * SENSOR: GT-WT-02 (ALDI Globaltronics..) + * TYP IIIIIIII BMCCTTTT TTTTTTTT HHHHHHHX XXXXX + * TYPE Description: + * - I = Random Device Code, changes with battery reset + * - 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 + * - H = Humidity = 7 Bit bin2dez 00-99, Display LL=10%, Display HH=110% (Range 20-90%) + * - X = Checksum, sum modulo 64 + * A Lidl AURIO (from 12/2018) with PCB marking YJ-T12 V02 has two extra bits in front. + * +*/ + +static const SubGhzBlockConst ws_protocol_gt_wt_02_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 37, +}; + +struct WSProtocolDecoderGT_WT02 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderGT_WT02 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + GT_WT02DecoderStepReset = 0, + GT_WT02DecoderStepSaveDuration, + GT_WT02DecoderStepCheckDuration, +} GT_WT02DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_gt_wt_02_decoder = { + .alloc = ws_protocol_decoder_gt_wt_02_alloc, + .free = ws_protocol_decoder_gt_wt_02_free, + + .feed = ws_protocol_decoder_gt_wt_02_feed, + .reset = ws_protocol_decoder_gt_wt_02_reset, + + .get_hash_data = ws_protocol_decoder_gt_wt_02_get_hash_data, + .serialize = ws_protocol_decoder_gt_wt_02_serialize, + .deserialize = ws_protocol_decoder_gt_wt_02_deserialize, + .get_string = ws_protocol_decoder_gt_wt_02_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_gt_wt_02_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_gt_wt_02 = { + .name = WS_PROTOCOL_GT_WT_02_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_gt_wt_02_decoder, + .encoder = &ws_protocol_gt_wt_02_encoder, +}; + +void* ws_protocol_decoder_gt_wt_02_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderGT_WT02* instance = malloc(sizeof(WSProtocolDecoderGT_WT02)); + instance->base.protocol = &ws_protocol_gt_wt_02; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_gt_wt_02_free(void* context) { + furi_assert(context); + WSProtocolDecoderGT_WT02* instance = context; + free(instance); +} + +void ws_protocol_decoder_gt_wt_02_reset(void* context) { + furi_assert(context); + WSProtocolDecoderGT_WT02* instance = context; + instance->decoder.parser_step = GT_WT02DecoderStepReset; +} + +static bool ws_protocol_gt_wt_02_check(WSProtocolDecoderGT_WT02* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t sum = (instance->decoder.decode_data >> 5) & 0xe; + uint64_t temp_data = instance->decoder.decode_data >> 9; + for(uint8_t i = 0; i < 7; i++) { + sum += (temp_data >> (i * 4)) & 0xF; + } + return ((uint8_t)(instance->decoder.decode_data & 0x3F) == (sum & 0x3F)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_gt_wt_02_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 29) & 0xFF; + instance->battery_low = (instance->data >> 28) & 1; + instance->btn = (instance->data >> 27) & 1; + instance->channel = ((instance->data >> 25) & 0x3) + 1; + + if(!((instance->data >> 24) & 1)) { + instance->temp = (float)((instance->data >> 13) & 0x07FF) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 13) & 0x07FF) + 1) / -10.0f; + } + + instance->humidity = (instance->data >> 6) & 0x7F; + if(instance->humidity <= 10) // actually the sensors sends 10 below working range of 20% + instance->humidity = 0; + else if(instance->humidity > 90) // actually the sensors sends 110 above working range of 90% + instance->humidity = 100; +} + +void ws_protocol_decoder_gt_wt_02_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderGT_WT02* instance = context; + + switch(instance->decoder.parser_step) { + case GT_WT02DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_gt_wt_02_const.te_short * 18) < + ws_protocol_gt_wt_02_const.te_delta * 8)) { + //Found syncPrefix + instance->decoder.parser_step = GT_WT02DecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case GT_WT02DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = GT_WT02DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = GT_WT02DecoderStepReset; + } + break; + + case GT_WT02DecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_02_const.te_short) < + ws_protocol_gt_wt_02_const.te_delta) { + if(DURATION_DIFF(duration, ws_protocol_gt_wt_02_const.te_short * 18) < + ws_protocol_gt_wt_02_const.te_delta * 8) { + //Found syncPostfix + instance->decoder.parser_step = GT_WT02DecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_gt_wt_02_const.min_count_bit_for_found) && + ws_protocol_gt_wt_02_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_gt_wt_02_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } else if(instance->decoder.decode_count_bit == 1) { + instance->decoder.parser_step = GT_WT02DecoderStepSaveDuration; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else if( + DURATION_DIFF(duration, ws_protocol_gt_wt_02_const.te_long) < + ws_protocol_gt_wt_02_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = GT_WT02DecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, ws_protocol_gt_wt_02_const.te_long * 2) < + ws_protocol_gt_wt_02_const.te_delta * 4) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = GT_WT02DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = GT_WT02DecoderStepReset; + } + } else { + instance->decoder.parser_step = GT_WT02DecoderStepReset; + } + } else { + instance->decoder.parser_step = GT_WT02DecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_gt_wt_02_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderGT_WT02* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_gt_wt_02_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderGT_WT02* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_gt_wt_02_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderGT_WT02* 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_02_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_02_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderGT_WT02* 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:%3.1f 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, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/gt_wt_02.h b/applications/plugins/weather_station/protocols/gt_wt_02.h new file mode 100644 index 000000000..f17d5baa0 --- /dev/null +++ b/applications/plugins/weather_station/protocols/gt_wt_02.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_GT_WT_02_NAME "GT-WT02" + +typedef struct WSProtocolDecoderGT_WT02 WSProtocolDecoderGT_WT02; +typedef struct WSProtocolEncoderGT_WT02 WSProtocolEncoderGT_WT02; + +extern const SubGhzProtocolDecoder ws_protocol_gt_wt_02_decoder; +extern const SubGhzProtocolEncoder ws_protocol_gt_wt_02_encoder; +extern const SubGhzProtocol ws_protocol_gt_wt_02; + +/** + * Allocate WSProtocolDecoderGT_WT02. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderGT_WT02* pointer to a WSProtocolDecoderGT_WT02 instance + */ +void* ws_protocol_decoder_gt_wt_02_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderGT_WT02. + * @param context Pointer to a WSProtocolDecoderGT_WT02 instance + */ +void ws_protocol_decoder_gt_wt_02_free(void* context); + +/** + * Reset decoder WSProtocolDecoderGT_WT02. + * @param context Pointer to a WSProtocolDecoderGT_WT02 instance + */ +void ws_protocol_decoder_gt_wt_02_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderGT_WT02 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_gt_wt_02_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderGT_WT02 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_gt_wt_02_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderGT_WT02. + * @param context Pointer to a WSProtocolDecoderGT_WT02 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_02_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderGT_WT02. + * @param context Pointer to a WSProtocolDecoderGT_WT02 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_gt_wt_02_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderGT_WT02 instance + * @param output Resulting text + */ +void ws_protocol_decoder_gt_wt_02_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/gt_wt_03.c b/applications/plugins/weather_station/protocols/gt_wt_03.c index 04bca9ac1..7831cf069 100644 --- a/applications/plugins/weather_station/protocols/gt_wt_03.c +++ b/applications/plugins/weather_station/protocols/gt_wt_03.c @@ -327,7 +327,7 @@ void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* 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%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -335,7 +335,6 @@ void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output) 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))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/infactory.c b/applications/plugins/weather_station/protocols/infactory.c index b08a4e9db..2d444d981 100644 --- a/applications/plugins/weather_station/protocols/infactory.c +++ b/applications/plugins/weather_station/protocols/infactory.c @@ -283,7 +283,7 @@ void ws_protocol_decoder_infactory_get_string(void* context, FuriString* 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%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -291,7 +291,6 @@ void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output) 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))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c index d4b89be87..e4b612250 100644 --- a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c +++ b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c @@ -284,7 +284,7 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriStrin "%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%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -292,7 +292,6 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriStrin 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))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/nexus_th.c b/applications/plugins/weather_station/protocols/nexus_th.c index c3d823eda..7d4a77aea 100644 --- a/applications/plugins/weather_station/protocols/nexus_th.c +++ b/applications/plugins/weather_station/protocols/nexus_th.c @@ -247,7 +247,7 @@ void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* 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%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -255,7 +255,6 @@ void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output) 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))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/protocols/oregon2.c b/applications/plugins/weather_station/protocols/oregon2.c index d294548e6..8779e9596 100644 --- a/applications/plugins/weather_station/protocols/oregon2.c +++ b/applications/plugins/weather_station/protocols/oregon2.c @@ -52,6 +52,11 @@ static const SubGhzBlockConst ws_oregon2_const = { #define ID_UV800 0xd874 #define ID_THN129 0xcc43 // THN129 Temp only #define ID_RTHN129 0x0cd3 // RTHN129 Temp, clock sensors +#define ID_RTHN129_1 0x9cd3 +#define ID_RTHN129_2 0xacd3 +#define ID_RTHN129_3 0xbcd3 +#define ID_RTHN129_4 0xccd3 +#define ID_RTHN129_5 0xdcd3 #define ID_BTHGN129 0x5d53 // Baro, Temp, Hygro sensor #define ID_UVR128 0xec70 #define ID_THGR328N 0xcc23 // Temp & Hygro sensor similar to THR228N with 5 channel instead of 3 @@ -137,11 +142,19 @@ static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration // From sensor id code return amount of bits in variable section // https://temofeev.ru/info/articles/o-dekodirovanii-protokola-pogodnykh-datchikov-oregon-scientific static uint8_t oregon2_sensor_id_var_bits(uint16_t sensor_id) { - if(sensor_id == ID_THR228N) return 16; - - if(sensor_id == ID_THGR122N) return 24; - - return 0; + switch(sensor_id) { + case ID_THR228N: + case ID_RTHN129_1: + case ID_RTHN129_2: + case ID_RTHN129_3: + case ID_RTHN129_4: + case ID_RTHN129_5: + return 16; + case ID_THGR122N: + return 24; + default: + return 0; + } } static void ws_oregon2_decode_const_data(WSBlockGeneric* ws_block) { @@ -171,16 +184,22 @@ static float ws_oregon2_decode_temp(uint32_t data) { } static void ws_oregon2_decode_var_data(WSBlockGeneric* ws_b, uint16_t sensor_id, uint32_t data) { - if(sensor_id == ID_THR228N) { + switch(sensor_id) { + case ID_THR228N: + case ID_RTHN129_1: + case ID_RTHN129_2: + case ID_RTHN129_3: + case ID_RTHN129_4: + case ID_RTHN129_5: ws_b->temp = ws_oregon2_decode_temp(data); ws_b->humidity = WS_NO_HUMIDITY; return; - } - - if(sensor_id == ID_THGR122N) { + case ID_THGR122N: ws_b->humidity = bcd_decode_short(data); ws_b->temp = ws_oregon2_decode_temp(data >> 8); return; + default: + break; } } diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/plugins/weather_station/protocols/protocol_items.c index d7f6458ab..9ad8ce1ac 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.c +++ b/applications/plugins/weather_station/protocols/protocol_items.c @@ -4,12 +4,15 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_infactory, &ws_protocol_thermopro_tx4, &ws_protocol_nexus_th, + &ws_protocol_gt_wt_02, &ws_protocol_gt_wt_03, &ws_protocol_acurite_606tx, + &ws_protocol_acurite_609txc, &ws_protocol_lacrosse_tx141thbv2, &ws_protocol_oregon2, &ws_protocol_acurite_592txr, &ws_protocol_ambient_weather, + &ws_protocol_auriol_th, }; 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 76c085ab4..4fef89442 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.h +++ b/applications/plugins/weather_station/protocols/protocol_items.h @@ -4,11 +4,14 @@ #include "infactory.h" #include "thermopro_tx4.h" #include "nexus_th.h" +#include "gt_wt_02.h" #include "gt_wt_03.h" #include "acurite_606tx.h" +#include "acurite_609txc.h" #include "lacrosse_tx141thbv2.h" #include "oregon2.h" #include "acurite_592txr.h" #include "ambient_weather.h" +#include "auriol_hg0601a.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 index 9a2eacb2f..0882bc33d 100644 --- a/applications/plugins/weather_station/protocols/thermopro_tx4.c +++ b/applications/plugins/weather_station/protocols/thermopro_tx4.c @@ -246,7 +246,7 @@ void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* out "%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%%", + "Temp:%3.1f C Hum:%d%%", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), @@ -254,7 +254,6 @@ void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* out 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))), + (double)instance->generic.temp, instance->generic.humidity); } diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_about.c b/applications/plugins/weather_station/scenes/weather_station_scene_about.c index df784ec96..d916dc76f 100644 --- a/applications/plugins/weather_station/scenes/weather_station_scene_about.c +++ b/applications/plugins/weather_station/scenes/weather_station_scene_about.c @@ -24,7 +24,7 @@ void weather_station_scene_about_on_enter(void* context) { 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"); + temp_str, "Reading messages from\nweather stations that work\nwith SubGhz sensors\n\n"); furi_string_cat_printf(temp_str, "Supported protocols:\n"); size_t i = 0; 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 34ec122d1..49b447f10 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver_info.c +++ b/applications/plugins/weather_station/views/weather_station_receiver_info.c @@ -75,12 +75,7 @@ void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) { 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)))); + snprintf(buffer, sizeof(buffer), "%3.1f C", (double)model->generic->temp); canvas_draw_str_aligned(canvas, 63, 46, AlignRight, AlignTop, buffer); canvas_draw_circle(canvas, 55, 45, 1); } diff --git a/applications/services/applications.h b/applications/services/applications.h index 012e80ddb..acbfea312 100644 --- a/applications/services/applications.h +++ b/applications/services/applications.h @@ -56,7 +56,7 @@ extern const size_t FLIPPER_DEBUG_APPS_COUNT; extern const FlipperApplication FLIPPER_SYSTEM_APPS[]; extern const size_t FLIPPER_SYSTEM_APPS_COUNT; -/* Seperate scene app holder +/* Separate scene app holder * Spawned by loader */ extern const FlipperApplication FLIPPER_SCENE; diff --git a/applications/services/cli/cli_command_gpio.c b/applications/services/cli/cli_command_gpio.c index d072ce00c..0b29f4853 100644 --- a/applications/services/cli/cli_command_gpio.c +++ b/applications/services/cli/cli_command_gpio.c @@ -61,15 +61,20 @@ static void gpio_print_pins(void) { } } -typedef enum { OK, ERR_CMD_SYNTAX, ERR_PIN, ERR_VALUE } GpioParseError; +typedef enum { + GpioParseReturnOk, + GpioParseReturnCmdSyntaxError, + GpioParseReturnPinError, + GpioParseReturnValueError +} GpioParseReturn; -static GpioParseError gpio_command_parse(FuriString* args, size_t* pin_num, uint8_t* value) { +static GpioParseReturn gpio_command_parse(FuriString* args, size_t* pin_num, uint8_t* value) { FuriString* pin_name; pin_name = furi_string_alloc(); size_t ws = furi_string_search_char(args, ' '); if(ws == FURI_STRING_FAILURE) { - return ERR_CMD_SYNTAX; + return GpioParseReturnCmdSyntaxError; } furi_string_set_n(pin_name, args, 0, ws); @@ -78,7 +83,7 @@ static GpioParseError gpio_command_parse(FuriString* args, size_t* pin_num, uint if(!pin_name_to_int(pin_name, pin_num)) { furi_string_free(pin_name); - return ERR_PIN; + return GpioParseReturnPinError; } furi_string_free(pin_name); @@ -88,10 +93,10 @@ static GpioParseError gpio_command_parse(FuriString* args, size_t* pin_num, uint } else if(!furi_string_cmp(args, "1")) { *value = 1; } else { - return ERR_VALUE; + return GpioParseReturnValueError; } - return OK; + return GpioParseReturnOk; } void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { @@ -101,22 +106,22 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { size_t num = 0; uint8_t value = 255; - GpioParseError err = gpio_command_parse(args, &num, &value); + GpioParseReturn err = gpio_command_parse(args, &num, &value); - if(ERR_CMD_SYNTAX == err) { + if(err == GpioParseReturnCmdSyntaxError) { cli_print_usage("gpio mode", " <0|1>", furi_string_get_cstr(args)); return; - } else if(ERR_PIN == err) { + } else if(err == GpioParseReturnPinError) { gpio_print_pins(); return; - } else if(ERR_VALUE == err) { + } else if(err == GpioParseReturnValueError) { printf("Value is invalid. Enter 1 for input or 0 for output"); return; } if(cli_command_gpio_pins[num].debug) { printf( - "Changeing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); + "Changing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); char c = cli_getc(cli); if(c != 'y' && c != 'Y') { printf("Cancelled.\r\n"); @@ -161,15 +166,15 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { size_t num = 0; uint8_t value = 0; - GpioParseError err = gpio_command_parse(args, &num, &value); + GpioParseReturn err = gpio_command_parse(args, &num, &value); - if(ERR_CMD_SYNTAX == err) { + if(err == GpioParseReturnCmdSyntaxError) { cli_print_usage("gpio set", " <0|1>", furi_string_get_cstr(args)); return; - } else if(ERR_PIN == err) { + } else if(err == GpioParseReturnPinError) { gpio_print_pins(); return; - } else if(ERR_VALUE == err) { + } else if(err == GpioParseReturnValueError) { printf("Value is invalid. Enter 1 for high or 0 for low"); return; } diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 239434b7a..3534a5418 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -7,6 +7,7 @@ #include #include #include +#include // Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'` #define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d" @@ -24,7 +25,7 @@ void cli_command_device_info_callback(const char* key, const char* value, bool l void cli_command_device_info(Cli* cli, FuriString* args, void* context) { UNUSED(cli); UNUSED(args); - furi_hal_info_get(cli_command_device_info_callback, context); + furi_hal_info_get(cli_command_device_info_callback, '_', context); } void cli_command_help(Cli* cli, FuriString* args, void* context) { @@ -192,6 +193,83 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) { furi_stream_buffer_free(ring); } +void cli_command_sysctl_debug(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + if(!furi_string_cmp(args, "0")) { + furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); + loader_update_menu(); + printf("Debug disabled."); + } else if(!furi_string_cmp(args, "1")) { + furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); + loader_update_menu(); + printf("Debug enabled."); + } else { + cli_print_usage("sysctl debug", "<1|0>", furi_string_get_cstr(args)); + } +} + +void cli_command_sysctl_heap_track(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + if(!furi_string_cmp(args, "none")) { + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeNone); + printf("Heap tracking disabled"); + } else if(!furi_string_cmp(args, "main")) { + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeMain); + printf("Heap tracking enabled for application main thread"); +#if FURI_DEBUG + } else if(!furi_string_cmp(args, "tree")) { + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeTree); + printf("Heap tracking enabled for application main and child threads"); + } else if(!furi_string_cmp(args, "all")) { + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeAll); + printf("Heap tracking enabled for all threads"); +#endif + } else { + cli_print_usage("sysctl heap_track", "", furi_string_get_cstr(args)); + } +} + +void cli_command_sysctl_print_usage() { + printf("Usage:\r\n"); + printf("sysctl \r\n"); + printf("Cmd list:\r\n"); + + printf("\tdebug <0|1>\t - Enable or disable system debug\r\n"); +#if FURI_DEBUG + printf("\theap_track \t - Set heap allocation tracking mode\r\n"); +#else + printf("\theap_track \t - Set heap allocation tracking mode\r\n"); +#endif +} + +void cli_command_sysctl(Cli* cli, FuriString* args, void* context) { + FuriString* cmd; + cmd = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, cmd)) { + cli_command_sysctl_print_usage(); + break; + } + + if(furi_string_cmp_str(cmd, "debug") == 0) { + cli_command_sysctl_debug(cli, args, context); + break; + } + + if(furi_string_cmp_str(cmd, "heap_track") == 0) { + cli_command_sysctl_heap_track(cli, args, context); + break; + } + + cli_command_sysctl_print_usage(); + } while(false); + + furi_string_free(cmd); +} + void cli_command_vibro(Cli* cli, FuriString* args, void* context) { UNUSED(cli); UNUSED(context); @@ -208,22 +286,6 @@ void cli_command_vibro(Cli* cli, FuriString* args, void* context) { } } -void cli_command_debug(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); - UNUSED(context); - if(!furi_string_cmp(args, "0")) { - furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); - loader_update_menu(); - printf("Debug disabled."); - } else if(!furi_string_cmp(args, "1")) { - furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); - loader_update_menu(); - printf("Debug enabled."); - } else { - cli_print_usage("debug", "<1|0>", furi_string_get_cstr(args)); - } -} - void cli_command_led(Cli* cli, FuriString* args, void* context) { UNUSED(cli); UNUSED(context); @@ -356,7 +418,7 @@ void cli_commands_init(Cli* cli) { cli_add_command(cli, "date", CliCommandFlagParallelSafe, cli_command_date, NULL); cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL); - cli_add_command(cli, "debug", CliCommandFlagDefault, cli_command_debug, NULL); + cli_add_command(cli, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL); cli_add_command(cli, "ps", CliCommandFlagParallelSafe, cli_command_ps, NULL); cli_add_command(cli, "free", CliCommandFlagParallelSafe, cli_command_free, NULL); cli_add_command(cli, "free_blocks", CliCommandFlagParallelSafe, cli_command_free_blocks, NULL); diff --git a/applications/services/cli/cli_vcp.c b/applications/services/cli/cli_vcp.c index 1e27e185b..ad26dca47 100644 --- a/applications/services/cli/cli_vcp.c +++ b/applications/services/cli/cli_vcp.c @@ -68,10 +68,7 @@ static void cli_vcp_init() { vcp->connected = false; - vcp->thread = furi_thread_alloc(); - furi_thread_set_name(vcp->thread, "CliVcpWorker"); - furi_thread_set_stack_size(vcp->thread, 1024); - furi_thread_set_callback(vcp->thread, vcp_worker); + vcp->thread = furi_thread_alloc_ex("CliVcpWorker", 1024, vcp_worker, NULL); furi_thread_start(vcp->thread); FURI_LOG_I(TAG, "Init OK"); @@ -103,7 +100,7 @@ static int32_t vcp_worker(void* context) { while(1) { uint32_t flags = furi_thread_flags_wait(VCP_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); - furi_assert((flags & FuriFlagError) == 0); + furi_assert(!(flags & FuriFlagError)); // VCP session opened if(flags & VcpEvtConnect) { @@ -303,7 +300,7 @@ static void vcp_on_cdc_control_line(void* context, uint8_t state) { static void vcp_on_cdc_rx(void* context) { UNUSED(context); uint32_t ret = furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtRx); - furi_check((ret & FuriFlagError) == 0); + furi_check(!(ret & FuriFlagError)); } static void vcp_on_cdc_tx_complete(void* context) { diff --git a/applications/services/crypto/crypto_cli.c b/applications/services/crypto/crypto_cli.c index a64a3ad0b..1b26ba9fb 100644 --- a/applications/services/crypto/crypto_cli.c +++ b/applications/services/crypto/crypto_cli.c @@ -167,7 +167,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) { void crypto_cli_has_key(Cli* cli, FuriString* args) { UNUSED(cli); int key_slot = 0; - uint8_t iv[16]; + uint8_t iv[16] = {0}; do { if(!args_read_int_and_trim(args, &key_slot) || !(key_slot > 0 && key_slot <= 100)) { @@ -249,7 +249,7 @@ void crypto_cli_store_key(Cli* cli, FuriString* args) { } if(key_slot > 0) { - uint8_t iv[16]; + uint8_t iv[16] = {0}; if(key_slot > 1) { if(!furi_hal_crypto_store_load_key(key_slot - 1, iv)) { printf( diff --git a/applications/services/desktop/animations/animation_manager.h b/applications/services/desktop/animations/animation_manager.h index 9802c4f1f..234d20de0 100644 --- a/applications/services/desktop/animations/animation_manager.h +++ b/applications/services/desktop/animations/animation_manager.h @@ -69,7 +69,7 @@ View* animation_manager_get_animation_view(AnimationManager* animation_manager); void animation_manager_set_context(AnimationManager* animation_manager, void* context); /** - * Set callback for Animation Manager for defered calls + * Set callback for Animation Manager for deferred calls * for animation_manager_new_idle_process(). * Animation Manager doesn't have it's own thread, so main thread gives * callbacks to A.M. to call when it should perform some inner manipulations. @@ -96,7 +96,7 @@ void animation_manager_set_new_idle_callback( void animation_manager_new_idle_process(AnimationManager* animation_manager); /** - * Set callback for Animation Manager for defered calls + * Set callback for Animation Manager for deferred calls * for animation_manager_check_blocking_process(). * * @animation_manager instance @@ -115,7 +115,7 @@ void animation_manager_set_check_callback( void animation_manager_check_blocking_process(AnimationManager* animation_manager); /** - * Set callback for Animation Manager for defered calls + * Set callback for Animation Manager for deferred calls * for animation_manager_interact_process(). * * @animation_manager instance diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index 654de44d5..4f01ad5be 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -45,6 +45,13 @@ static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* fl return; } + FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); + if(mode > FuriHalRtcHeapTrackModeNone) { + furi_thread_enable_heap_trace(desktop->scene_thread); + } else { + furi_thread_disable_heap_trace(desktop->scene_thread); + } + furi_thread_set_name(desktop->scene_thread, flipper_app->name); furi_thread_set_stack_size(desktop->scene_thread, flipper_app->stack_size); furi_thread_set_callback(desktop->scene_thread, flipper_app->app); diff --git a/applications/services/dialogs/dialogs.c b/applications/services/dialogs/dialogs.c index e2db4a0f8..3908ca31b 100644 --- a/applications/services/dialogs/dialogs.c +++ b/applications/services/dialogs/dialogs.c @@ -1,6 +1,6 @@ #include "dialogs/dialogs_message.h" #include "dialogs_i.h" -#include "dialogs_api_lock.h" +#include #include "dialogs_module_file_browser.h" #include "dialogs_module_message.h" @@ -14,6 +14,7 @@ void dialog_file_browser_set_basic_options( options->hide_ext = true; options->item_loader_callback = NULL; options->item_loader_context = NULL; + options->base_path = NULL; } static DialogsApp* dialogs_app_alloc() { @@ -35,7 +36,7 @@ static void dialogs_app_process_message(DialogsApp* app, DialogsAppMessage* mess dialogs_app_process_module_message(&message->data->dialog); break; } - API_LOCK_UNLOCK(message->lock); + api_lock_unlock(message->lock); } int32_t dialogs_srv(void* p) { diff --git a/applications/services/dialogs/dialogs.h b/applications/services/dialogs/dialogs.h index 4e836e365..4c1b675a6 100644 --- a/applications/services/dialogs/dialogs.h +++ b/applications/services/dialogs/dialogs.h @@ -18,7 +18,9 @@ typedef struct DialogsApp DialogsApp; /** * File browser dialog extra options * @param extension file extension to be offered for selection + * @param base_path root folder path for navigation with back key * @param skip_assets true - do not show assets folders + * @param hide_dot_files true - hide dot files * @param icon file icon pointer, NULL for default icon * @param hide_ext true - hide extensions for files * @param item_loader_callback callback function for providing custom icon & entry name @@ -26,7 +28,9 @@ typedef struct DialogsApp DialogsApp; */ typedef struct { const char* extension; + const char* base_path; bool skip_assets; + bool hide_dot_files; const Icon* icon; bool hide_ext; FileBrowserLoadItemCallback item_loader_callback; diff --git a/applications/services/dialogs/dialogs_api.c b/applications/services/dialogs/dialogs_api.c index ee959a33c..5e2b0683e 100644 --- a/applications/services/dialogs/dialogs_api.c +++ b/applications/services/dialogs/dialogs_api.c @@ -1,6 +1,6 @@ #include "dialogs/dialogs_message.h" #include "dialogs_i.h" -#include "dialogs_api_lock.h" +#include #include /****************** File browser ******************/ @@ -10,7 +10,7 @@ bool dialog_file_browser_show( FuriString* result_path, FuriString* path, const DialogsFileBrowserOptions* options) { - FuriApiLock lock = API_LOCK_INIT_LOCKED(); + FuriApiLock lock = api_lock_alloc_locked(); furi_check(lock != NULL); DialogsAppData data = { @@ -20,9 +20,11 @@ bool dialog_file_browser_show( .file_icon = options ? options->icon : NULL, .hide_ext = options ? options->hide_ext : true, .skip_assets = options ? options->skip_assets : true, + .hide_dot_files = options ? options->hide_dot_files : true, .preselected_filename = path, .item_callback = options ? options->item_loader_callback : NULL, .item_callback_context = options ? options->item_loader_context : NULL, + .base_path = options ? options->base_path : NULL, }}; DialogsAppReturn return_data; @@ -35,7 +37,7 @@ bool dialog_file_browser_show( furi_check( furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk); - API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock); + api_lock_wait_unlock_and_free(lock); return return_data.bool_value; } @@ -43,7 +45,7 @@ bool dialog_file_browser_show( /****************** Message ******************/ DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* dialog_message) { - FuriApiLock lock = API_LOCK_INIT_LOCKED(); + FuriApiLock lock = api_lock_alloc_locked(); furi_check(lock != NULL); DialogsAppData data = { @@ -61,7 +63,7 @@ DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage furi_check( furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk); - API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock); + api_lock_wait_unlock_and_free(lock); return return_data.dialog_value; } diff --git a/applications/services/dialogs/dialogs_api_lock.h b/applications/services/dialogs/dialogs_api_lock.h deleted file mode 100644 index a165803f4..000000000 --- a/applications/services/dialogs/dialogs_api_lock.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -typedef FuriEventFlag* FuriApiLock; - -#define API_LOCK_EVENT (1U << 0) - -#define API_LOCK_INIT_LOCKED() furi_event_flag_alloc(); - -#define API_LOCK_WAIT_UNTIL_UNLOCK(_lock) \ - furi_event_flag_wait(_lock, API_LOCK_EVENT, FuriFlagWaitAny, FuriWaitForever); - -#define API_LOCK_FREE(_lock) furi_event_flag_free(_lock); - -#define API_LOCK_UNLOCK(_lock) furi_event_flag_set(_lock, API_LOCK_EVENT); - -#define API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(_lock) \ - API_LOCK_WAIT_UNTIL_UNLOCK(_lock); \ - API_LOCK_FREE(_lock); diff --git a/applications/services/dialogs/dialogs_message.h b/applications/services/dialogs/dialogs_message.h index 91e040ce4..1c8c4fb50 100644 --- a/applications/services/dialogs/dialogs_message.h +++ b/applications/services/dialogs/dialogs_message.h @@ -1,7 +1,7 @@ #pragma once #include #include "dialogs_i.h" -#include "dialogs_api_lock.h" +#include #ifdef __cplusplus extern "C" { @@ -11,11 +11,13 @@ typedef struct { const char* extension; bool skip_assets; bool hide_ext; + bool hide_dot_files; const Icon* file_icon; FuriString* result_path; FuriString* preselected_filename; FileBrowserLoadItemCallback item_callback; void* item_callback_context; + const char* base_path; } DialogsAppMessageDataFileBrowser; typedef struct { diff --git a/applications/services/dialogs/dialogs_module_file_browser.c b/applications/services/dialogs/dialogs_module_file_browser.c index 166fd1113..8d486dbaf 100644 --- a/applications/services/dialogs/dialogs_module_file_browser.c +++ b/applications/services/dialogs/dialogs_module_file_browser.c @@ -1,5 +1,5 @@ #include "dialogs_i.h" -#include "dialogs_api_lock.h" +#include #include "gui/modules/file_browser.h" typedef struct { @@ -11,14 +11,14 @@ static void dialogs_app_file_browser_back_callback(void* context) { furi_assert(context); DialogsAppFileBrowserContext* file_browser_context = context; file_browser_context->result = false; - API_LOCK_UNLOCK(file_browser_context->lock); + api_lock_unlock(file_browser_context->lock); } static void dialogs_app_file_browser_callback(void* context) { furi_assert(context); DialogsAppFileBrowserContext* file_browser_context = context; file_browser_context->result = true; - API_LOCK_UNLOCK(file_browser_context->lock); + api_lock_unlock(file_browser_context->lock); } bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data) { @@ -27,7 +27,7 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow DialogsAppFileBrowserContext* file_browser_context = malloc(sizeof(DialogsAppFileBrowserContext)); - file_browser_context->lock = API_LOCK_INIT_LOCKED(); + file_browser_context->lock = api_lock_alloc_locked(); ViewHolder* view_holder = view_holder_alloc(); view_holder_attach_to_gui(view_holder, gui); @@ -38,13 +38,19 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow file_browser_set_callback( file_browser, dialogs_app_file_browser_callback, file_browser_context); file_browser_configure( - file_browser, data->extension, data->skip_assets, data->file_icon, data->hide_ext); + file_browser, + data->extension, + data->base_path, + data->skip_assets, + data->hide_dot_files, + data->file_icon, + data->hide_ext); file_browser_set_item_callback(file_browser, data->item_callback, data->item_callback_context); file_browser_start(file_browser, data->preselected_filename); view_holder_set_view(view_holder, file_browser_get_view(file_browser)); view_holder_start(view_holder); - API_LOCK_WAIT_UNTIL_UNLOCK(file_browser_context->lock); + api_lock_wait_unlock(file_browser_context->lock); ret = file_browser_context->result; @@ -52,7 +58,7 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow view_holder_free(view_holder); file_browser_stop(file_browser); file_browser_free(file_browser); - API_LOCK_FREE(file_browser_context->lock); + api_lock_free(file_browser_context->lock); free(file_browser_context); furi_record_close(RECORD_GUI); diff --git a/applications/services/dialogs/dialogs_module_message.c b/applications/services/dialogs/dialogs_module_message.c index 8d1c3ba75..879ba9baa 100644 --- a/applications/services/dialogs/dialogs_module_message.c +++ b/applications/services/dialogs/dialogs_module_message.c @@ -1,5 +1,5 @@ #include "dialogs_i.h" -#include "dialogs_api_lock.h" +#include #include typedef struct { @@ -30,7 +30,7 @@ static void dialogs_app_message_back_callback(void* context) { furi_assert(context); DialogsAppMessageContext* message_context = context; message_context->result = DialogMessageButtonBack; - API_LOCK_UNLOCK(message_context->lock); + api_lock_unlock(message_context->lock); } static void dialogs_app_message_callback(DialogExResult result, void* context) { @@ -49,7 +49,7 @@ static void dialogs_app_message_callback(DialogExResult result, void* context) { default: break; } - API_LOCK_UNLOCK(message_context->lock); + api_lock_unlock(message_context->lock); } DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data) { @@ -57,7 +57,7 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa Gui* gui = furi_record_open(RECORD_GUI); const DialogMessage* message = data->message; DialogsAppMessageContext* message_context = malloc(sizeof(DialogsAppMessageContext)); - message_context->lock = API_LOCK_INIT_LOCKED(); + message_context->lock = api_lock_alloc_locked(); ViewHolder* view_holder = view_holder_alloc(); view_holder_attach_to_gui(view_holder, gui); @@ -87,14 +87,14 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa view_holder_set_view(view_holder, dialog_ex_get_view(dialog_ex)); view_holder_start(view_holder); - API_LOCK_WAIT_UNTIL_UNLOCK(message_context->lock); + api_lock_wait_unlock(message_context->lock); ret = message_context->result; view_holder_stop(view_holder); view_holder_free(view_holder); dialog_ex_free(dialog_ex); - API_LOCK_FREE(message_context->lock); + api_lock_free(message_context->lock); free(message_context); furi_record_close(RECORD_GUI); diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index b2d204de7..9f155402b 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -90,7 +90,7 @@ void elements_button_center(Canvas* canvas, const char* str); * * @param canvas Canvas instance * @param x, y coordinates based on align param - * @param horizontal, vertical aligment of multiline text + * @param horizontal, vertical alignment of multiline text * @param text string (possible multiline) */ void elements_multiline_text_aligned( diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 2d06d70c7..9f6ebcd76 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -436,7 +436,7 @@ void gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, vo const CanvasCallbackPair p = {callback, context}; gui_lock(gui); - furi_assert(CanvasCallbackPairArray_count(gui->canvas_callback_pair, p) == 0); + furi_assert(!CanvasCallbackPairArray_count(gui->canvas_callback_pair, p)); CanvasCallbackPairArray_push_back(gui->canvas_callback_pair, p); gui_unlock(gui); diff --git a/applications/services/gui/modules/dialog_ex.h b/applications/services/gui/modules/dialog_ex.h index 4c6094239..26a465354 100644 --- a/applications/services/gui/modules/dialog_ex.h +++ b/applications/services/gui/modules/dialog_ex.h @@ -76,8 +76,8 @@ void dialog_ex_set_context(DialogEx* dialog_ex, void* context); * @param text text to be shown, can be multiline * @param x x position * @param y y position - * @param horizontal horizontal text aligment - * @param vertical vertical text aligment + * @param horizontal horizontal text alignment + * @param vertical vertical text alignment */ void dialog_ex_set_header( DialogEx* dialog_ex, @@ -95,8 +95,8 @@ void dialog_ex_set_header( * @param text text to be shown, can be multiline * @param x x position * @param y y position - * @param horizontal horizontal text aligment - * @param vertical vertical text aligment + * @param horizontal horizontal text alignment + * @param vertical vertical text alignment */ void dialog_ex_set_text( DialogEx* dialog_ex, diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 60e78b01c..a5daa91ec 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -83,7 +83,9 @@ struct FileBrowser { View* view; BrowserWorker* worker; const char* ext_filter; + const char* base_path; bool skip_assets; + bool hide_dot_files; bool hide_ext; FileBrowserCallback callback; @@ -162,7 +164,9 @@ View* file_browser_get_view(FileBrowser* browser) { void file_browser_configure( FileBrowser* browser, const char* extension, + const char* base_path, bool skip_assets, + bool hide_dot_files, const Icon* file_icon, bool hide_ext) { furi_assert(browser); @@ -170,6 +174,8 @@ void file_browser_configure( browser->ext_filter = extension; browser->skip_assets = skip_assets; browser->hide_ext = hide_ext; + browser->base_path = base_path; + browser->hide_dot_files = hide_dot_files; with_view_model( browser->view, @@ -183,7 +189,12 @@ void file_browser_configure( void file_browser_start(FileBrowser* browser, FuriString* path) { furi_assert(browser); - browser->worker = file_browser_worker_alloc(path, browser->ext_filter, browser->skip_assets); + browser->worker = file_browser_worker_alloc( + path, + browser->base_path, + browser->ext_filter, + browser->skip_assets, + browser->hide_dot_files); file_browser_worker_set_callback_context(browser->worker, browser); file_browser_worker_set_folder_callback(browser->worker, browser_folder_open_cb); file_browser_worker_set_list_callback(browser->worker, browser_list_load_cb); @@ -232,7 +243,10 @@ static bool browser_is_item_in_array(FileBrowserModel* model, uint32_t idx) { static bool browser_is_list_load_required(FileBrowserModel* model) { size_t array_size = items_array_size(model->items); - uint32_t item_cnt = (model->is_root) ? model->item_cnt : model->item_cnt - 1; + if((array_size > 0) && (!model->is_root) && (model->array_offset == 0)) { + array_size--; + } + uint32_t item_cnt = (model->is_root) ? (model->item_cnt) : (model->item_cnt - 1); if((model->list_loading) || (array_size >= item_cnt)) { return false; @@ -524,7 +538,7 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { model->list_loading = true; int32_t load_offset = CLAMP( model->item_idx - ITEM_LIST_LEN_MAX / 4 * 3, - (int32_t)model->item_cnt - ITEM_LIST_LEN_MAX, + (int32_t)model->item_cnt, 0); file_browser_worker_load( browser->worker, load_offset, ITEM_LIST_LEN_MAX); @@ -535,7 +549,7 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { model->list_loading = true; int32_t load_offset = CLAMP( model->item_idx - ITEM_LIST_LEN_MAX / 4 * 1, - (int32_t)model->item_cnt - ITEM_LIST_LEN_MAX, + (int32_t)model->item_cnt, 0); file_browser_worker_load( browser->worker, load_offset, ITEM_LIST_LEN_MAX); @@ -590,6 +604,19 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { } consumed = true; } + } else if(event->key == InputKeyBack) { + if(event->type == InputTypeShort) { + bool is_root = false; + with_view_model( + browser->view, FileBrowserModel * model, { is_root = model->is_root; }, false); + + if(!is_root && !file_browser_worker_is_in_start_folder(browser->worker)) { + consumed = true; + if(!is_root) { + file_browser_worker_folder_exit(browser->worker); + } + } + } } return consumed; diff --git a/applications/services/gui/modules/file_browser.h b/applications/services/gui/modules/file_browser.h index c9fdddb55..879d62c4e 100644 --- a/applications/services/gui/modules/file_browser.h +++ b/applications/services/gui/modules/file_browser.h @@ -29,7 +29,9 @@ View* file_browser_get_view(FileBrowser* browser); void file_browser_configure( FileBrowser* browser, const char* extension, + const char* base_path, bool skip_assets, + bool hide_dot_files, const Icon* file_icon, bool hide_ext); diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index fdaf8273f..d8b515d03 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -35,11 +35,14 @@ struct BrowserWorker { FuriThread* thread; FuriString* filter_extension; + FuriString* path_start; + FuriString* path_current; FuriString* path_next; int32_t item_sel_idx; uint32_t load_offset; uint32_t load_count; bool skip_assets; + bool hide_dot_files; idx_last_array_t idx_last; void* cb_ctx; @@ -74,6 +77,13 @@ static bool browser_path_trim(FuriString* path) { } static bool browser_filter_by_name(BrowserWorker* browser, FuriString* name, bool is_folder) { + // Skip dot files if enabled + if(browser->hide_dot_files) { + if(furi_string_start_with_str(name, ".")) { + return false; + } + } + if(is_folder) { // Skip assets folders (if enabled) if(browser->skip_assets) { @@ -289,6 +299,7 @@ static int32_t browser_worker(void* context) { int32_t file_idx = 0; browser_folder_init(browser, path, filename, &items_cnt, &file_idx); + furi_string_set(browser->path_current, path); FURI_LOG_D( TAG, "Enter folder: %s items: %lu idx: %ld", @@ -311,6 +322,7 @@ static int32_t browser_worker(void* context) { // Pop previous selected item index from history array idx_last_array_pop_back(&file_idx, browser->idx_last); } + furi_string_set(browser->path_current, path); FURI_LOG_D( TAG, "Exit to: %s items: %lu idx: %ld", @@ -357,21 +369,29 @@ static int32_t browser_worker(void* context) { return 0; } -BrowserWorker* - file_browser_worker_alloc(FuriString* path, const char* filter_ext, bool skip_assets) { - BrowserWorker* browser = malloc(sizeof(BrowserWorker)); +BrowserWorker* file_browser_worker_alloc( + FuriString* path, + const char* base_path, + const char* filter_ext, + bool skip_assets, + bool hide_dot_files) { + BrowserWorker* browser = malloc(sizeof(BrowserWorker)); //-V773 idx_last_array_init(browser->idx_last); browser->filter_extension = furi_string_alloc_set(filter_ext); browser->skip_assets = skip_assets; + browser->hide_dot_files = hide_dot_files; + + browser->path_current = furi_string_alloc_set(path); browser->path_next = furi_string_alloc_set(path); - browser->thread = furi_thread_alloc(); - furi_thread_set_name(browser->thread, "BrowserWorker"); - furi_thread_set_stack_size(browser->thread, 2048); - furi_thread_set_context(browser->thread, browser); - furi_thread_set_callback(browser->thread, browser_worker); + browser->path_start = furi_string_alloc(); + if(base_path) { + furi_string_set_str(browser->path_start, base_path); + } + + browser->thread = furi_thread_alloc_ex("BrowserWorker", 2048, browser_worker, browser); furi_thread_start(browser->thread); return browser; @@ -386,6 +406,8 @@ void file_browser_worker_free(BrowserWorker* browser) { furi_string_free(browser->filter_extension); furi_string_free(browser->path_next); + furi_string_free(browser->path_current); + furi_string_free(browser->path_start); idx_last_array_clear(browser->idx_last); @@ -429,11 +451,13 @@ void file_browser_worker_set_config( BrowserWorker* browser, FuriString* path, const char* filter_ext, - bool skip_assets) { + bool skip_assets, + bool hide_dot_files) { furi_assert(browser); furi_string_set(browser->path_next, path); furi_string_set(browser->filter_extension, filter_ext); browser->skip_assets = skip_assets; + browser->hide_dot_files = hide_dot_files; furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); } @@ -444,6 +468,11 @@ void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); } +bool file_browser_worker_is_in_start_folder(BrowserWorker* browser) { + furi_assert(browser); + return (furi_string_cmp(browser->path_start, browser->path_current) == 0); +} + void file_browser_worker_folder_exit(BrowserWorker* browser) { furi_assert(browser); furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderExit); diff --git a/applications/services/gui/modules/file_browser_worker.h b/applications/services/gui/modules/file_browser_worker.h index 230bb5b45..3b4be6aa7 100644 --- a/applications/services/gui/modules/file_browser_worker.h +++ b/applications/services/gui/modules/file_browser_worker.h @@ -21,8 +21,12 @@ typedef void (*BrowserWorkerListItemCallback)( bool is_last); typedef void (*BrowserWorkerLongLoadCallback)(void* context); -BrowserWorker* - file_browser_worker_alloc(FuriString* path, const char* filter_ext, bool skip_assets); +BrowserWorker* file_browser_worker_alloc( + FuriString* path, + const char* base_path, + const char* filter_ext, + bool skip_assets, + bool hide_dot_files); void file_browser_worker_free(BrowserWorker* browser); @@ -48,10 +52,13 @@ void file_browser_worker_set_config( BrowserWorker* browser, FuriString* path, const char* filter_ext, - bool skip_assets); + bool skip_assets, + bool hide_dot_files); void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx); +bool file_browser_worker_is_in_start_folder(BrowserWorker* browser); + void file_browser_worker_folder_exit(BrowserWorker* browser); void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx); diff --git a/applications/services/gui/modules/popup.h b/applications/services/gui/modules/popup.h index 94f49a2ba..13371a05d 100644 --- a/applications/services/gui/modules/popup.h +++ b/applications/services/gui/modules/popup.h @@ -64,7 +64,7 @@ void popup_set_context(Popup* popup, void* context); * @param x x position * @param y y position * @param horizontal horizontal alignment - * @param vertical vertical aligment + * @param vertical vertical alignment */ void popup_set_header( Popup* popup, @@ -83,7 +83,7 @@ void popup_set_header( * @param x x position * @param y y position * @param horizontal horizontal alignment - * @param vertical vertical aligment + * @param vertical vertical alignment */ void popup_set_text( Popup* popup, diff --git a/applications/services/gui/modules/text_box.c b/applications/services/gui/modules/text_box.c index 99d7d04f1..079a1294d 100644 --- a/applications/services/gui/modules/text_box.c +++ b/applications/services/gui/modules/text_box.c @@ -43,7 +43,7 @@ static void text_box_process_up(TextBox* text_box) { model->scroll_pos--; // Reach last symbol of previous line model->text_pos--; - // Search prevous line start + // Search previous line start while((model->text_pos != model->text) && (*(--model->text_pos) != '\n')) ; if(*model->text_pos == '\n') { diff --git a/applications/services/gui/modules/text_input.h b/applications/services/gui/modules/text_input.h index 893fbd533..218df3141 100644 --- a/applications/services/gui/modules/text_input.h +++ b/applications/services/gui/modules/text_input.h @@ -1,6 +1,6 @@ /** * @file text_input.h - * GUI: TextInput keybord view module API + * GUI: TextInput keyboard view module API */ #pragma once diff --git a/applications/services/gui/modules/widget_elements/widget_element_button.c b/applications/services/gui/modules/widget_elements/widget_element_button.c index be33b1897..e3267058e 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_button.c +++ b/applications/services/gui/modules/widget_elements/widget_element_button.c @@ -60,7 +60,7 @@ WidgetElement* widget_element_button_create( ButtonCallback callback, void* context) { // Allocate and init model - GuiButtonModel* model = malloc(sizeof(GuiButtonModel)); + GuiButtonModel* model = malloc(sizeof(GuiButtonModel)); //-V773 model->button_type = button_type; model->callback = callback; model->context = context; diff --git a/applications/services/gui/view_dispatcher.c b/applications/services/gui/view_dispatcher.c index 8de452d20..046958749 100644 --- a/applications/services/gui/view_dispatcher.c +++ b/applications/services/gui/view_dispatcher.c @@ -23,7 +23,7 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) { gui_remove_view_port(view_dispatcher->gui, view_dispatcher->view_port); } // Crash if not all views were freed - furi_assert(ViewDict_size(view_dispatcher->views) == 0); + furi_assert(!ViewDict_size(view_dispatcher->views)); ViewDict_clear(view_dispatcher->views); // Free ViewPort @@ -152,12 +152,12 @@ void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_ if(view_dispatcher->current_view == view) { view_dispatcher_set_current_view(view_dispatcher, NULL); } - // Check if view is recieving input + // Check if view is receiving input if(view_dispatcher->ongoing_input_view == view) { view_dispatcher->ongoing_input_view = NULL; } // Remove view - ViewDict_erase(view_dispatcher->views, view_id); + furi_check(ViewDict_erase(view_dispatcher->views, view_id)); view_set_update_callback(view, NULL); view_set_update_callback_context(view, NULL); diff --git a/applications/services/input/input.c b/applications/services/input/input.c index d1aef9e8a..1d02df1e5 100644 --- a/applications/services/input/input.c +++ b/applications/services/input/input.c @@ -1,5 +1,7 @@ #include "input_i.h" +// #define INPUT_DEBUG + #define GPIO_Read(input_pin) (furi_hal_gpio_read(input_pin.pin->gpio) ^ (input_pin.pin->inverted)) static Input* input = NULL; @@ -72,6 +74,10 @@ int32_t input_srv(void* p) { input->event_pubsub = furi_pubsub_alloc(); furi_record_create(RECORD_INPUT_EVENTS, input->event_pubsub); +#if INPUT_DEBUG + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); +#endif + #ifdef SRV_CLI input->cli = furi_record_open(RECORD_CLI); if(input->cli) { @@ -95,10 +101,16 @@ int32_t input_srv(void* p) { bool is_changing = false; for(size_t i = 0; i < input_pins_count; i++) { bool state = GPIO_Read(input->pin_states[i]); + if(state) { + if(input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) + input->pin_states[i].debounce += 1; + } else { + if(input->pin_states[i].debounce > 0) input->pin_states[i].debounce -= 1; + } + if(input->pin_states[i].debounce > 0 && input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) { is_changing = true; - input->pin_states[i].debounce += (state ? 1 : -1); } else if(input->pin_states[i].state != state) { input->pin_states[i].state = state; @@ -129,8 +141,14 @@ int32_t input_srv(void* p) { } if(is_changing) { +#if INPUT_DEBUG + furi_hal_gpio_write(&gpio_ext_pa4, 1); +#endif furi_delay_tick(1); } else { +#if INPUT_DEBUG + furi_hal_gpio_write(&gpio_ext_pa4, 0); +#endif furi_thread_flags_wait(INPUT_THREAD_FLAG_ISR, FuriFlagWaitAny, FuriWaitForever); } } diff --git a/applications/services/input/input.h b/applications/services/input/input.h index 172b16361..ec3d09711 100644 --- a/applications/services/input/input.h +++ b/applications/services/input/input.h @@ -20,8 +20,8 @@ typedef enum { InputTypePress, /**< Press event, emitted after debounce */ InputTypeRelease, /**< Release event, emitted after debounce */ InputTypeShort, /**< Short event, emitted after InputTypeRelease done withing INPUT_LONG_PRESS interval */ - InputTypeLong, /**< Long event, emmited after INPUT_LONG_PRESS interval, asynchronouse to InputTypeRelease */ - InputTypeRepeat, /**< Repeat event, emmited with INPUT_REPEATE_PRESS period after InputTypeLong event */ + InputTypeLong, /**< Long event, emitted after INPUT_LONG_PRESS_COUNTS interval, asynchronous to InputTypeRelease */ + InputTypeRepeat, /**< Repeat event, emitted with INPUT_LONG_PRESS_COUNTS period after InputTypeLong event */ InputTypeMAX, /**< Special value for exceptional */ } InputType; diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index bc456536c..931719723 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -21,6 +21,13 @@ static bool FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name); + FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); + if(mode > FuriHalRtcHeapTrackModeNone) { + furi_thread_enable_heap_trace(loader_instance->application_thread); + } else { + furi_thread_disable_heap_trace(loader_instance->application_thread); + } + furi_thread_set_name(loader_instance->application_thread, loader_instance->application->name); furi_thread_set_stack_size( loader_instance->application_thread, loader_instance->application->stack_size); @@ -269,22 +276,18 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con event.type = LoaderEventTypeApplicationStarted; furi_pubsub_publish(loader_instance->pubsub, &event); - if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) { + if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) { furi_hal_power_insomnia_enter(); } } else if(thread_state == FuriThreadStateStopped) { - FURI_LOG_I( - TAG, - "Application thread stopped. Free heap: %d. Thread allocation balance: %d.", - memmgr_get_free_heap(), - furi_thread_get_heap_size(instance->application_thread)); + FURI_LOG_I(TAG, "Application stopped. Free heap: %d", memmgr_get_free_heap()); if(loader_instance->application_arguments) { free(loader_instance->application_arguments); loader_instance->application_arguments = NULL; } - if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) { + if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) { furi_hal_power_insomnia_exit(); } loader_unlock(instance); @@ -310,7 +313,7 @@ static Loader* loader_alloc() { Loader* instance = malloc(sizeof(Loader)); instance->application_thread = furi_thread_alloc(); - furi_thread_enable_heap_trace(instance->application_thread); + furi_thread_set_state_context(instance->application_thread, instance); furi_thread_set_state_callback(instance->application_thread, loader_thread_state_callback); diff --git a/applications/services/power/power_cli.c b/applications/services/power/power_cli.c index 5c28137e9..f4a10f0a9 100644 --- a/applications/services/power/power_cli.c +++ b/applications/services/power/power_cli.c @@ -26,7 +26,7 @@ void power_cli_reboot2dfu(Cli* cli, FuriString* args) { power_reboot(PowerBootModeDfu); } -static void power_cli_info_callback(const char* key, const char* value, bool last, void* context) { +static void power_cli_callback(const char* key, const char* value, bool last, void* context) { UNUSED(last); UNUSED(context); printf("%-24s: %s\r\n", key, value); @@ -35,13 +35,13 @@ static void power_cli_info_callback(const char* key, const char* value, bool las void power_cli_info(Cli* cli, FuriString* args) { UNUSED(cli); UNUSED(args); - furi_hal_power_info_get(power_cli_info_callback, NULL); + furi_hal_power_info_get(power_cli_callback, '_', NULL); } void power_cli_debug(Cli* cli, FuriString* args) { UNUSED(cli); UNUSED(args); - furi_hal_power_dump_state(); + furi_hal_power_debug_get(power_cli_callback, NULL); } void power_cli_5v(Cli* cli, FuriString* args) { diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index b77b6760c..f77cc4805 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -12,6 +12,20 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { if(power->info.gauge_is_ok) { canvas_draw_box(canvas, 2, 2, (power->info.charge + 4) / 5, 4); + if(power->info.voltage_battery_charging < 4.2) { + // Battery charging voltage is modified, indicate with cross pattern + canvas_invert_color(canvas); + uint8_t battery_bar_width = (power->info.charge + 4) / 5; + bool cross_odd = false; + // Start 1 further in from the battery bar's x position + for(uint8_t x = 3; x <= battery_bar_width; x++) { + // Cross pattern is from the center of the battery bar + // y = 2 + 1 (inset) + 1 (for every other) + canvas_draw_dot(canvas, x, 3 + (uint8_t)cross_odd); + cross_odd = !cross_odd; + } + canvas_invert_color(canvas); + } if(power->state == PowerStateCharging) { canvas_set_bitmap_mode(canvas, 1); canvas_set_color(canvas, ColorWhite); @@ -209,6 +223,7 @@ static bool power_update_info(Power* power) { info.capacity_full = furi_hal_power_get_battery_full_capacity(); info.current_charger = furi_hal_power_get_battery_current(FuriHalPowerICCharger); info.current_gauge = furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge); + info.voltage_battery_charging = furi_hal_power_get_battery_charging_voltage(); info.voltage_charger = furi_hal_power_get_battery_voltage(FuriHalPowerICCharger); info.voltage_gauge = furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge); info.voltage_vbus = furi_hal_power_get_usb_voltage(); diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index 3144a46e4..6469bc422 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -41,6 +41,7 @@ typedef struct { float current_charger; float current_gauge; + float voltage_battery_charging; float voltage_charger; float voltage_gauge; float voltage_vbus; @@ -94,7 +95,7 @@ FuriPubSub* power_get_settings_events_pubsub(Power* power); */ bool power_is_battery_healthy(Power* power); -/** Enable or disable battery low level notification mesage +/** Enable or disable battery low level notification message * * @param power Power instance * @param enable true - enable, false - disable diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 73eaadfb1..63a71ad06 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -52,7 +52,12 @@ static RpcSystemCallbacks rpc_systems[] = { { .alloc = rpc_system_gpio_alloc, .free = NULL, - }}; + }, + { + .alloc = rpc_system_property_alloc, + .free = NULL, + }, +}; struct RpcSession { Rpc* rpc; @@ -370,11 +375,7 @@ RpcSession* rpc_session_open(Rpc* rpc) { }; rpc_add_handler(session, PB_Main_stop_session_tag, &rpc_handler); - session->thread = furi_thread_alloc(); - furi_thread_set_name(session->thread, "RpcSessionWorker"); - furi_thread_set_stack_size(session->thread, 2048); - furi_thread_set_context(session->thread, session); - furi_thread_set_callback(session->thread, rpc_session_worker); + session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session); furi_thread_set_state_context(session->thread, session); furi_thread_set_state_callback(session->thread, rpc_session_free_callback); diff --git a/applications/services/rpc/rpc.h b/applications/services/rpc/rpc.h index 40493949d..21836d9a4 100644 --- a/applications/services/rpc/rpc.h +++ b/applications/services/rpc/rpc.h @@ -25,7 +25,7 @@ typedef void (*RpcSendBytesCallback)(void* context, uint8_t* bytes, size_t bytes typedef void (*RpcBufferIsEmptyCallback)(void* context); /** Callback to notify transport layer that close_session command * is received. Any other actions lays on transport layer. - * No destruction or session close preformed. */ + * No destruction or session close performed. */ typedef void (*RpcSessionClosedCallback)(void* context); /** Callback to notify transport layer that session was closed * and all operations were finished */ diff --git a/applications/services/rpc/rpc_app.c b/applications/services/rpc/rpc_app.c index 301055570..b96f043ac 100644 --- a/applications/services/rpc/rpc_app.c +++ b/applications/services/rpc/rpc_app.c @@ -9,9 +9,15 @@ struct RpcAppSystem { RpcSession* session; + RpcAppSystemCallback app_callback; void* app_context; + + RpcAppSystemDataExchangeCallback data_exchange_callback; + void* data_exchange_context; + PB_Main* state_msg; + PB_Main* error_msg; uint32_t last_id; char* last_data; @@ -26,6 +32,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) furi_assert(request->which_content == PB_Main_app_start_request_tag); RpcAppSystem* rpc_app = context; RpcSession* session = rpc_app->session; + rpc_system_app_error_reset(rpc_app); furi_assert(session); char args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE]; @@ -73,6 +80,7 @@ static void rpc_system_app_lock_status_process(const PB_Main* request, void* con furi_assert(request->which_content == PB_Main_app_lock_status_request_tag); RpcAppSystem* rpc_app = context; + rpc_system_app_error_reset(rpc_app); RpcSession* session = rpc_app->session; furi_assert(session); @@ -102,6 +110,7 @@ static void rpc_system_app_exit_request(const PB_Main* request, void* context) { furi_assert(request->which_content == PB_Main_app_exit_request_tag); RpcAppSystem* rpc_app = context; + rpc_system_app_error_reset(rpc_app); RpcSession* session = rpc_app->session; furi_assert(session); @@ -127,6 +136,7 @@ static void rpc_system_app_load_file(const PB_Main* request, void* context) { furi_assert(request->which_content == PB_Main_app_load_file_request_tag); RpcAppSystem* rpc_app = context; + rpc_system_app_error_reset(rpc_app); RpcSession* session = rpc_app->session; furi_assert(session); @@ -152,6 +162,7 @@ static void rpc_system_app_button_press(const PB_Main* request, void* context) { furi_assert(request->which_content == PB_Main_app_button_press_request_tag); RpcAppSystem* rpc_app = context; + rpc_system_app_error_reset(rpc_app); RpcSession* session = rpc_app->session; furi_assert(session); @@ -177,6 +188,7 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context) furi_assert(context); RpcAppSystem* rpc_app = context; + rpc_system_app_error_reset(rpc_app); RpcSession* session = rpc_app->session; furi_assert(session); @@ -195,6 +207,51 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context) } } +static void rpc_system_app_get_error_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_get_error_request_tag); + furi_assert(context); + + RpcAppSystem* rpc_app = context; + RpcSession* session = rpc_app->session; + furi_assert(session); + + rpc_app->error_msg->command_id = request->command_id; + + FURI_LOG_D(TAG, "GetError"); + rpc_send(session, rpc_app->error_msg); +} + +static void rpc_system_app_data_exchange_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_data_exchange_request_tag); + furi_assert(context); + + RpcAppSystem* rpc_app = context; + rpc_system_app_error_reset(rpc_app); + RpcSession* session = rpc_app->session; + furi_assert(session); + + PB_CommandStatus command_status; + pb_bytes_array_t* data = request->content.app_data_exchange_request.data; + + if(rpc_app->data_exchange_callback) { + uint8_t* data_bytes = NULL; + size_t data_size = 0; + if(data) { + data_bytes = data->bytes; + data_size = data->size; + } + rpc_app->data_exchange_callback(data_bytes, data_size, rpc_app->data_exchange_context); + command_status = PB_CommandStatus_OK; + } else { + command_status = PB_CommandStatus_ERROR_APP_CMD_ERROR; + } + + FURI_LOG_D(TAG, "DataExchange"); + rpc_send_and_release_empty(session, request->command_id, command_status); +} + void rpc_system_app_send_started(RpcAppSystem* rpc_app) { furi_assert(rpc_app); RpcSession* session = rpc_app->session; @@ -259,6 +316,65 @@ void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback cal rpc_app->app_context = ctx; } +void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code) { + furi_assert(rpc_app); + PB_App_GetErrorResponse* content = &rpc_app->error_msg->content.app_get_error_response; + content->code = error_code; +} + +void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text) { + furi_assert(rpc_app); + PB_App_GetErrorResponse* content = &rpc_app->error_msg->content.app_get_error_response; + + if(content->text) { + free(content->text); + } + + content->text = error_text ? strdup(error_text) : NULL; +} + +void rpc_system_app_error_reset(RpcAppSystem* rpc_app) { + furi_assert(rpc_app); + + rpc_system_app_set_error_code(rpc_app, 0); + rpc_system_app_set_error_text(rpc_app, NULL); +} + +void rpc_system_app_set_data_exchange_callback( + RpcAppSystem* rpc_app, + RpcAppSystemDataExchangeCallback callback, + void* ctx) { + furi_assert(rpc_app); + + rpc_app->data_exchange_callback = callback; + rpc_app->data_exchange_context = ctx; +} + +void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size) { + furi_assert(rpc_app); + RpcSession* session = rpc_app->session; + furi_assert(session); + + PB_Main message = { + .command_id = 0, + .command_status = PB_CommandStatus_OK, + .has_next = false, + .which_content = PB_Main_app_data_exchange_request_tag, + }; + + PB_App_DataExchangeRequest* content = &message.content.app_data_exchange_request; + + if(data && data_size) { + content->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data_size)); + content->data->size = data_size; + memcpy(content->data->bytes, data, data_size); + } else { + content->data = NULL; + } + + rpc_send_and_release(session, &message); +} + void* rpc_system_app_alloc(RpcSession* session) { furi_assert(session); @@ -270,6 +386,13 @@ void* rpc_system_app_alloc(RpcSession* session) { rpc_app->state_msg->which_content = PB_Main_app_state_response_tag; rpc_app->state_msg->command_status = PB_CommandStatus_OK; + // App error message + rpc_app->error_msg = malloc(sizeof(PB_Main)); + rpc_app->error_msg->which_content = PB_Main_app_get_error_response_tag; + rpc_app->error_msg->command_status = PB_CommandStatus_OK; + rpc_app->error_msg->content.app_get_error_response.code = 0; + rpc_app->error_msg->content.app_get_error_response.text = NULL; + RpcHandler rpc_handler = { .message_handler = NULL, .decode_submessage = NULL, @@ -294,6 +417,12 @@ void* rpc_system_app_alloc(RpcSession* session) { rpc_handler.message_handler = rpc_system_app_button_release; rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler); + rpc_handler.message_handler = rpc_system_app_get_error_process; + rpc_add_handler(session, PB_Main_app_get_error_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_app_data_exchange_process; + rpc_add_handler(session, PB_Main_app_data_exchange_request_tag, &rpc_handler); + return rpc_app; } @@ -311,8 +440,13 @@ void rpc_system_app_free(void* context) { furi_delay_tick(1); } + furi_assert(!rpc_app->data_exchange_callback); + if(rpc_app->last_data) free(rpc_app->last_data); + pb_release(&PB_Main_msg, rpc_app->error_msg); + + free(rpc_app->error_msg); free(rpc_app->state_msg); free(rpc_app); } diff --git a/applications/services/rpc/rpc_app.h b/applications/services/rpc/rpc_app.h index 635c9f8c6..d5c6fee96 100644 --- a/applications/services/rpc/rpc_app.h +++ b/applications/services/rpc/rpc_app.h @@ -14,6 +14,8 @@ typedef enum { } RpcAppSystemEvent; typedef void (*RpcAppSystemCallback)(RpcAppSystemEvent event, void* context); +typedef void ( + *RpcAppSystemDataExchangeCallback)(const uint8_t* data, size_t data_size, void* context); typedef struct RpcAppSystem RpcAppSystem; @@ -27,6 +29,19 @@ const char* rpc_system_app_get_data(RpcAppSystem* rpc_app); void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result); +void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code); + +void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text); + +void rpc_system_app_error_reset(RpcAppSystem* rpc_app); + +void rpc_system_app_set_data_exchange_callback( + RpcAppSystem* rpc_app, + RpcAppSystemDataExchangeCallback callback, + void* ctx); + +void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size); + #ifdef __cplusplus } #endif diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index 029ed0106..e66553d51 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -88,12 +88,8 @@ static void rpc_system_gui_start_screen_stream_process(const PB_Main* request, v malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(framebuffer_size)); rpc_gui->transmit_frame->content.gui_screen_frame.data->size = framebuffer_size; // Transmission thread for async TX - rpc_gui->transmit_thread = furi_thread_alloc(); - furi_thread_set_name(rpc_gui->transmit_thread, "GuiRpcWorker"); - furi_thread_set_callback( - rpc_gui->transmit_thread, rpc_system_gui_screen_stream_frame_transmit_thread); - furi_thread_set_context(rpc_gui->transmit_thread, rpc_gui); - furi_thread_set_stack_size(rpc_gui->transmit_thread, 1024); + rpc_gui->transmit_thread = furi_thread_alloc_ex( + "GuiRpcWorker", 1024, rpc_system_gui_screen_stream_frame_transmit_thread, rpc_gui); furi_thread_start(rpc_gui->transmit_thread); // GUI framebuffer callback gui_add_framebuffer_callback( diff --git a/applications/services/rpc/rpc_i.h b/applications/services/rpc/rpc_i.h index af9033f0a..91a176da8 100644 --- a/applications/services/rpc/rpc_i.h +++ b/applications/services/rpc/rpc_i.h @@ -34,6 +34,7 @@ void* rpc_system_gui_alloc(RpcSession* session); void rpc_system_gui_free(void* ctx); void* rpc_system_gpio_alloc(RpcSession* session); void rpc_system_gpio_free(void* ctx); +void* rpc_system_property_alloc(RpcSession* session); void rpc_debug_print_message(const PB_Main* message); void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size); diff --git a/applications/services/rpc/rpc_property.c b/applications/services/rpc/rpc_property.c new file mode 100644 index 000000000..ad15051eb --- /dev/null +++ b/applications/services/rpc/rpc_property.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include + +#include "rpc_i.h" + +#define TAG "RpcProperty" + +#define PROPERTY_CATEGORY_DEVICE_INFO "devinfo" +#define PROPERTY_CATEGORY_POWER_INFO "pwrinfo" +#define PROPERTY_CATEGORY_POWER_DEBUG "pwrdebug" + +typedef struct { + RpcSession* session; + PB_Main* response; + FuriString* subkey; +} RpcPropertyContext; + +static void + rpc_system_property_get_callback(const char* key, const char* value, bool last, void* context) { + furi_assert(key); + furi_assert(value); + furi_assert(context); + furi_assert(key); + furi_assert(value); + + RpcPropertyContext* ctx = context; + RpcSession* session = ctx->session; + PB_Main* response = ctx->response; + + if(!strncmp(key, furi_string_get_cstr(ctx->subkey), furi_string_size(ctx->subkey))) { + response->content.system_device_info_response.key = strdup(key); + response->content.system_device_info_response.value = strdup(value); + rpc_send_and_release(session, response); + } + + if(last) { + rpc_send_and_release_empty(session, response->command_id, PB_CommandStatus_OK); + } +} + +static void rpc_system_property_get_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_property_get_request_tag); + + FURI_LOG_D(TAG, "GetProperty"); + + RpcSession* session = (RpcSession*)context; + furi_assert(session); + + FuriString* topkey = furi_string_alloc(); + FuriString* subkey = furi_string_alloc_set_str(request->content.property_get_request.key); + + const size_t sep_idx = furi_string_search_char(subkey, '.'); + + if(sep_idx == FURI_STRING_FAILURE) { + furi_string_swap(topkey, subkey); + } else { + furi_string_set_n(topkey, subkey, 0, sep_idx); + furi_string_right(subkey, sep_idx + 1); + } + + PB_Main* response = malloc(sizeof(PB_Main)); + + response->command_id = request->command_id; + response->command_status = PB_CommandStatus_OK; + response->has_next = true; + response->which_content = PB_Main_property_get_response_tag; + + RpcPropertyContext property_context = { + .session = session, + .response = response, + .subkey = subkey, + }; + + if(!furi_string_cmp(topkey, PROPERTY_CATEGORY_DEVICE_INFO)) { + furi_hal_info_get(rpc_system_property_get_callback, '.', &property_context); + } else if(!furi_string_cmp(topkey, PROPERTY_CATEGORY_POWER_INFO)) { + furi_hal_power_info_get(rpc_system_property_get_callback, '.', &property_context); + } else if(!furi_string_cmp(topkey, PROPERTY_CATEGORY_POWER_DEBUG)) { + furi_hal_power_debug_get(rpc_system_property_get_callback, &property_context); + } else { + rpc_send_and_release_empty( + session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); + } + + furi_string_free(subkey); + furi_string_free(topkey); + + free(response); +} + +void* rpc_system_property_alloc(RpcSession* session) { + furi_assert(session); + + RpcHandler rpc_handler = { + .message_handler = NULL, + .decode_submessage = NULL, + .context = session, + }; + + rpc_handler.message_handler = rpc_system_property_get_process; + rpc_add_handler(session, PB_Main_property_get_request_tag, &rpc_handler); + return NULL; +} diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index 16e343fce..3c6ff7f94 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -330,7 +330,7 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex rpc_system_storage_reset_state(rpc_storage, session, true); - /* use same message memory to send reponse */ + /* use same message memory to send response */ PB_Main* response = malloc(sizeof(PB_Main)); const char* path = request->content.storage_read_request.path; Storage* fs_api = furi_record_open(RECORD_STORAGE); diff --git a/applications/services/rpc/rpc_system.c b/applications/services/rpc/rpc_system.c index 1681bb17e..a17be7d2d 100644 --- a/applications/services/rpc/rpc_system.c +++ b/applications/services/rpc/rpc_system.c @@ -109,7 +109,7 @@ static void rpc_system_system_device_info_process(const PB_Main* request, void* .session = session, .response = response, }; - furi_hal_info_get(rpc_system_system_device_info_callback, &device_info_context); + furi_hal_info_get(rpc_system_system_device_info_callback, '_', &device_info_context); free(response); } @@ -266,7 +266,7 @@ static void rpc_system_system_get_power_info_process(const PB_Main* request, voi .session = session, .response = response, }; - furi_hal_power_info_get(rpc_system_system_power_info_callback, &power_info_context); + furi_hal_power_info_get(rpc_system_system_power_info_callback, '_', &power_info_context); free(response); } diff --git a/applications/services/storage/filesystem_api_defines.h b/applications/services/storage/filesystem_api_defines.h index b6f1d8f13..b73e6eb33 100644 --- a/applications/services/storage/filesystem_api_defines.h +++ b/applications/services/storage/filesystem_api_defines.h @@ -25,13 +25,13 @@ typedef enum { typedef enum { FSE_OK, /**< No error */ FSE_NOT_READY, /**< FS not ready */ - FSE_EXIST, /**< File/Dir alrady exist */ + FSE_EXIST, /**< File/Dir already exist */ FSE_NOT_EXIST, /**< File/Dir does not exist */ FSE_INVALID_PARAMETER, /**< Invalid API parameter */ FSE_DENIED, /**< Access denied */ FSE_INVALID_NAME, /**< Invalid name/path */ FSE_INTERNAL, /**< Internal error */ - FSE_NOT_IMPLEMENTED, /**< Functon not implemented */ + FSE_NOT_IMPLEMENTED, /**< Function not implemented */ FSE_ALREADY_OPEN, /**< File/Dir already opened */ } FS_Error; diff --git a/applications/services/storage/filesystem_api_internal.h b/applications/services/storage/filesystem_api_internal.h index bd4bcf823..967d3bb41 100644 --- a/applications/services/storage/filesystem_api_internal.h +++ b/applications/services/storage/filesystem_api_internal.h @@ -17,7 +17,7 @@ typedef enum { struct File { uint32_t file_id; /**< File ID for internal references */ FileType type; - FS_Error error_id; /**< Standart API error from FS_Error enum */ + FS_Error error_id; /**< Standard API error from FS_Error enum */ int32_t internal_error_id; /**< Internal API error value */ void* storage; }; diff --git a/applications/services/storage/storage.h b/applications/services/storage/storage.h index 9c133e9be..e093cbe0f 100644 --- a/applications/services/storage/storage.h +++ b/applications/services/storage/storage.h @@ -255,19 +255,19 @@ FS_Error storage_common_fs_info( const char* storage_error_get_desc(FS_Error error_id); /** Retrieves the error id from the file object - * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETREIVE THE ERROR ID IF THE FILE HAS BEEN CLOSED + * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETRIEVE THE ERROR ID IF THE FILE HAS BEEN CLOSED * @return FS_Error error id */ FS_Error storage_file_get_error(File* file); /** Retrieves the internal (storage-specific) error id from the file object - * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETREIVE THE INTERNAL ERROR ID IF THE FILE HAS BEEN CLOSED + * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETRIEVE THE INTERNAL ERROR ID IF THE FILE HAS BEEN CLOSED * @return FS_Error error id */ int32_t storage_file_get_internal_error(File* file); /** Retrieves the error text from the file object - * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETREIVE THE ERROR TEXT IF THE FILE HAS BEEN CLOSED + * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETRIEVE THE ERROR TEXT IF THE FILE HAS BEEN CLOSED * @return const char* error text */ const char* storage_file_get_error_desc(File* file); diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index 0efcb5e2c..c83f16499 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -275,7 +275,7 @@ static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args uint32_t buffer_size; int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size); - if(parsed_count == EOF || parsed_count != 1) { + if(parsed_count != 1) { storage_cli_print_usage(); } else if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t file_size = storage_file_size(file); @@ -315,7 +315,7 @@ static void storage_cli_write_chunk(Cli* cli, FuriString* path, FuriString* args uint32_t buffer_size; int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size); - if(parsed_count == EOF || parsed_count != 1) { + if(parsed_count != 1) { storage_cli_print_usage(); } else { if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) { diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index 6854ef7f3..2c3a7bfc9 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -545,8 +545,8 @@ static FS_Error FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path) { FS_Error error; - const char* new_path_tmp; - FuriString* new_path_next; + const char* new_path_tmp = NULL; + FuriString* new_path_next = NULL; new_path_next = furi_string_alloc(); FileInfo fileinfo; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c index bdd9589ed..cf474c546 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -106,6 +106,7 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e .hide_ext = true, .item_loader_callback = favorite_fap_selector_item_callback, .item_loader_context = app, + .base_path = EXT_PATH("apps"), }; if(primary_favorite) { // Select favorite fap in file browser diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c b/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c index 0085c31dc..5fa38df72 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c @@ -7,6 +7,7 @@ static void power_settings_scene_battery_info_update_model(PowerSettingsApp* app .gauge_voltage = app->info.voltage_gauge, .gauge_current = app->info.current_gauge, .gauge_temperature = app->info.temperature_gauge, + .charging_voltage = app->info.voltage_battery_charging, .charge = app->info.charge, .health = app->info.health, }; diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index bbb0acb9a..d760164b9 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -68,6 +68,16 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); } else if(drain_current != 0) { snprintf(header, 20, "..."); + } else if(data->charging_voltage < 4.2) { + // Non-default battery charging limit, mention it + snprintf(emote, sizeof(emote), "Charged!"); + snprintf(header, sizeof(header), "Limited to"); + snprintf( + value, + sizeof(value), + "%ld.%ldV", + (uint32_t)(data->charging_voltage), + (uint32_t)(data->charging_voltage * 10) % 10); } else { snprintf(header, sizeof(header), "Charged!"); } diff --git a/applications/settings/power_settings_app/views/battery_info.h b/applications/settings/power_settings_app/views/battery_info.h index e62aa44fa..7bfacf69e 100644 --- a/applications/settings/power_settings_app/views/battery_info.h +++ b/applications/settings/power_settings_app/views/battery_info.h @@ -9,6 +9,7 @@ typedef struct { float gauge_voltage; float gauge_current; float gauge_temperature; + float charging_voltage; uint8_t charge; uint8_t health; } BatteryInfoModel; diff --git a/applications/settings/storage_settings/storage_settings.h b/applications/settings/storage_settings/storage_settings.h index 664e74c84..fd841623e 100644 --- a/applications/settings/storage_settings/storage_settings.h +++ b/applications/settings/storage_settings/storage_settings.h @@ -26,7 +26,7 @@ typedef struct { NotificationApp* notification; Storage* fs_api; - // view managment + // view management SceneManager* scene_manager; ViewDispatcher* view_dispatcher; diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index 7661413d7..dfce11a22 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -45,6 +45,31 @@ static void debug_changed(VariableItem* item) { loader_update_menu(); } +const char* const heap_trace_mode_text[] = { + "None", + "Main", +#if FURI_DEBUG + "Tree", + "All", +#endif +}; + +const uint32_t heap_trace_mode_value[] = { + FuriHalRtcHeapTrackModeNone, + FuriHalRtcHeapTrackModeMain, +#if FURI_DEBUG + FuriHalRtcHeapTrackModeTree, + FuriHalRtcHeapTrackModeAll, +#endif +}; + +static void heap_trace_mode_changed(VariableItem* item) { + // SystemSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, heap_trace_mode_text[index]); + furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]); +} + static uint32_t system_settings_exit(void* context) { UNUSED(context); return VIEW_NONE; @@ -79,6 +104,18 @@ SystemSettings* system_settings_alloc() { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, debug_text[value_index]); + item = variable_item_list_add( + app->var_item_list, + "Heap Trace", + COUNT_OF(heap_trace_mode_text), + heap_trace_mode_changed, + app); + value_index = value_index_uint32( + furi_hal_rtc_get_heap_track_mode(), heap_trace_mode_value, COUNT_OF(heap_trace_mode_text)); + furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[value_index]); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, heap_trace_mode_text[value_index]); + view_set_previous_callback( variable_item_list_get_view(app->var_item_list), system_settings_exit); view_dispatcher_add_view( diff --git a/applications/system/storage_move_to_sd/storage_move_to_sd.h b/applications/system/storage_move_to_sd/storage_move_to_sd.h index dc1d669b5..a62d87c1f 100644 --- a/applications/system/storage_move_to_sd/storage_move_to_sd.h +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.h @@ -30,7 +30,7 @@ typedef struct { Widget* widget; NotificationApp* notifications; - // view managment + // view management SceneManager* scene_manager; ViewDispatcher* view_dispatcher; diff --git a/applications/system/updater/cli/updater_cli.c b/applications/system/updater/cli/updater_cli.c index c3cdbb5f7..f8e21ca3e 100644 --- a/applications/system/updater/cli/updater_cli.c +++ b/applications/system/updater/cli/updater_cli.c @@ -110,11 +110,8 @@ static void updater_start_app() { * inside loader process, at startup. * So, accessing its record would cause a deadlock */ - FuriThread* thread = furi_thread_alloc(); - - furi_thread_set_name(thread, "UpdateAppSpawner"); - furi_thread_set_stack_size(thread, 768); - furi_thread_set_callback(thread, updater_spawner_thread_worker); + FuriThread* thread = + furi_thread_alloc_ex("UpdateAppSpawner", 768, updater_spawner_thread_worker, NULL); furi_thread_set_state_callback(thread, updater_spawner_thread_cleanup); furi_thread_set_state_context(thread, thread); furi_thread_start(thread); diff --git a/applications/system/updater/util/update_task.c b/applications/system/updater/util/update_task.c index de172dd49..b43a6df16 100644 --- a/applications/system/updater/util/update_task.c +++ b/applications/system/updater/util/update_task.c @@ -216,11 +216,8 @@ UpdateTask* update_task_alloc() { update_task->boot_mode = furi_hal_rtc_get_boot_mode(); update_task->update_path = furi_string_alloc(); - FuriThread* thread = update_task->thread = furi_thread_alloc(); - - furi_thread_set_name(thread, "UpdateWorker"); - furi_thread_set_stack_size(thread, 5120); - furi_thread_set_context(thread, update_task); + FuriThread* thread = update_task->thread = + furi_thread_alloc_ex("UpdateWorker", 5120, NULL, update_task); furi_thread_set_state_callback(thread, update_task_worker_thread_cb); furi_thread_set_state_context(thread, update_task); diff --git a/assets/dolphin/ReadMe.md b/assets/dolphin/ReadMe.md index 6b59d231f..643086c26 100644 --- a/assets/dolphin/ReadMe.md +++ b/assets/dolphin/ReadMe.md @@ -52,7 +52,7 @@ Version: 1 - `Active cooldown` - amount of seconds (after passive mode) to pass before entering next active mode. - `Bubble slots` - amount of bubble sequences. -- Any bubble sequence plays whole sequence during active mode. There can be many bubble sequences and bubbles inside it. Bubbles in 1 bubble sequence have to reside in 1 slot. Bubbles order in 1 bubble sequence is determined by occurance in file. As soon as frame index goes out of EndFrame index of bubble - next animation bubble is choosen. There can also be free of bubbles frames between 2 bubbles. +- Any bubble sequence plays whole sequence during active mode. There can be many bubble sequences and bubbles inside it. Bubbles in 1 bubble sequence have to reside in 1 slot. Bubbles order in 1 bubble sequence is determined by occurrence in file. As soon as frame index goes out of EndFrame index of bubble - next animation bubble is chosen. There can also be free of bubbles frames between 2 bubbles. - `Slot` - number to unite bubbles for same sequence. - `X`, `Y` - are coordinates of left top corner of bubble. diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_0.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_0.png new file mode 100644 index 000000000..912090032 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_1.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_1.png new file mode 100644 index 000000000..0b99a32ff Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_10.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_10.png new file mode 100644 index 000000000..9da72ac1d Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_11.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_11.png new file mode 100644 index 000000000..8d54da6c6 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_12.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_12.png new file mode 100644 index 000000000..84046a46c Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_13.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_13.png new file mode 100644 index 000000000..3e1c9c329 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_14.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_14.png new file mode 100644 index 000000000..f4f6ccd66 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_15.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_15.png new file mode 100644 index 000000000..5dc1a6525 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_16.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_16.png new file mode 100644 index 000000000..bec472921 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_17.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_17.png new file mode 100644 index 000000000..82e5176c8 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_18.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_18.png new file mode 100644 index 000000000..3b5e60dfd Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_19.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_19.png new file mode 100644 index 000000000..5f76c7d23 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_2.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_2.png new file mode 100644 index 000000000..84d6aaf35 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_20.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_20.png new file mode 100644 index 000000000..2f8394fd5 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_3.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_3.png new file mode 100644 index 000000000..48adde113 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_4.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_4.png new file mode 100644 index 000000000..5889835b7 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_5.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_5.png new file mode 100644 index 000000000..7f980f57f Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_6.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_6.png new file mode 100644 index 000000000..497136000 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_7.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_7.png new file mode 100644 index 000000000..03d67134a Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_8.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_8.png new file mode 100644 index 000000000..9f523cace Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_9.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_9.png new file mode 100644 index 000000000..5a565b1af Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/meta.txt b/assets/dolphin/external/L2_Wake_up_128x64/meta.txt new file mode 100644 index 000000000..06c710f03 --- /dev/null +++ b/assets/dolphin/external/L2_Wake_up_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 10 +Active frames: 18 +Frames order: 0 1 0 1 0 1 0 2 3 4 0 5 6 7 8 9 10 11 10 12 13 14 15 16 17 18 19 20 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 0 \ No newline at end of file diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index a6c7ca694..7f03d6595 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -36,19 +36,12 @@ Min level: 1 Max level: 1 Weight: 3 -Name: L2_Furippa2_128x64 +Name: L2_Wake_up_128x64 Min butthurt: 0 -Max butthurt: 6 +Max butthurt: 12 Min level: 2 -Max level: 2 -Weight: 3 - -Name: L3_Furippa3_128x64 -Min butthurt: 0 -Max butthurt: 6 -Min level: 3 Max level: 3 -Weight: 3 +Weight: 4 Name: L1_Read_books_128x64 Min butthurt: 0 @@ -57,13 +50,6 @@ Min level: 1 Max level: 1 Weight: 3 -Name: L2_Hacking_pc_128x64 -Min butthurt: 0 -Max butthurt: 8 -Min level: 2 -Max level: 2 -Weight: 3 - Name: L1_Cry_128x64 Min butthurt: 8 Max butthurt: 13 @@ -90,14 +76,49 @@ Min butthurt: 0 Max butthurt: 9 Min level: 1 Max level: 3 -Weight: 5 +Weight: 4 Name: L1_Painting_128x64 Min butthurt: 0 Max butthurt: 7 Min level: 1 Max level: 3 -Weight: 4 +Weight: 3 + +Name: L1_Leaving_sad_128x64 +Min butthurt: 14 +Max butthurt: 14 +Min level: 1 +Max level: 3 +Weight: 3 + +Name: L2_Furippa2_128x64 +Min butthurt: 0 +Max butthurt: 6 +Min level: 2 +Max level: 2 +Weight: 3 + +Name: L2_Hacking_pc_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 2 +Max level: 2 +Weight: 3 + +Name: L2_Soldering_128x64 +Min butthurt: 0 +Max butthurt: 10 +Min level: 2 +Max level: 2 +Weight: 3 + +Name: L3_Furippa3_128x64 +Min butthurt: 0 +Max butthurt: 6 +Min level: 3 +Max level: 3 +Weight: 3 Name: L3_Hijack_radio_128x64 Min butthurt: 0 @@ -112,17 +133,3 @@ Max butthurt: 10 Min level: 3 Max level: 3 Weight: 3 - -Name: L2_Soldering_128x64 -Min butthurt: 0 -Max butthurt: 10 -Min level: 2 -Max level: 2 -Weight: 3 - -Name: L1_Leaving_sad_128x64 -Min butthurt: 14 -Max butthurt: 14 -Min level: 1 -Max level: 3 -Weight: 3 diff --git a/assets/icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/icons/NFC/NFC_dolphin_emulation_47x61.png new file mode 100644 index 000000000..178353128 Binary files /dev/null and b/assets/icons/NFC/NFC_dolphin_emulation_47x61.png differ diff --git a/assets/protobuf b/assets/protobuf index e5af96e08..646066023 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit e5af96e08fea8351898f7b8c6d1e34ce5fd6cdef +Subproject commit 6460660237005d02d5c223835659b40e373bade9 diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 7ef953059..af776706d 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -148,4 +148,41 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 3013 1709 463 1085 462 1084 463 387 461 355 469 355 444 1084 463 387 461 378 436 1084 462 1084 487 355 469 1059 463 387 460 355 444 1083 463 1098 464 386 437 1083 463 1083 489 361 463 360 463 1082 464 361 462 376 462 1085 461 363 460 364 459 364 459 365 458 365 459 364 459 380 459 365 458 365 458 365 458 365 458 365 458 365 458 365 459 380 458 365 459 365 458 365 459 365 458 365 458 1089 458 365 458 380 459 1088 459 365 458 365 458 365 458 365 459 365 458 365 458 381 458 365 458 365 458 365 458 1089 458 365 458 365 458 365 458 381 458 365 458 365 458 365 458 365 458 365 458 365 458 365 458 381 457 366 457 366 457 366 457 366 457 366 458 365 458 366 457 381 458 366 458 366 457 366 457 366 457 366 457 366 457 366 457 381 458 366 457 366 457 366 457 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 366 457 366 457 367 457 366 457 382 457 367 456 1090 457 1090 456 1090 457 1090 457 1090 456 367 457 372 457 +# +# Model: Olimpia Splendid OS-SEAMH09EI +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4402 4336 561 1641 538 554 535 1616 563 1640 539 554 535 557 532 1645 534 560 539 551 538 1639 540 553 536 556 533 1644 535 1642 537 556 533 1620 558 558 531 561 538 554 535 1641 538 1639 540 1638 530 1620 559 1647 532 1643 536 1641 538 1613 566 553 536 557 532 560 539 553 536 558 531 560 529 1647 532 535 564 1613 555 537 563 1614 565 554 535 559 530 1645 534 559 530 1647 532 560 539 1612 557 562 537 1640 539 1611 557 5189 4398 4344 564 1638 530 563 537 1640 539 1639 529 563 537 530 559 1644 535 560 529 535 564 1639 529 537 563 556 533 1644 535 1643 536 557 532 1647 532 559 530 536 564 555 534 1643 536 1616 563 1640 539 1613 555 1624 565 1610 558 1619 560 1617 562 557 532 560 539 554 535 557 532 562 537 553 536 1641 538 555 534 1643 536 530 559 1644 535 558 531 564 536 1639 539 553 536 1641 537 555 534 1617 562 557 532 1645 534 1645 534 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4404 4345 563 1613 566 553 536 1641 538 1614 565 528 561 557 532 1645 534 560 539 551 538 1639 529 563 536 556 533 1618 561 1642 537 556 533 1645 534 557 532 561 538 1638 530 1646 533 1645 554 1623 556 1621 558 1596 562 1613 586 1591 588 531 537 555 534 559 530 562 537 555 534 561 538 552 537 555 534 559 530 562 537 555 534 559 530 562 537 557 532 1643 536 1615 564 1639 560 1591 588 1616 563 1588 580 1623 556 1621 537 5183 4404 4339 579 1597 581 538 530 1646 533 1619 580 539 529 563 536 1615 564 557 532 532 557 1646 533 560 539 553 536 1641 538 1639 539 554 535 1643 536 555 534 559 530 1647 532 1646 533 1644 535 1617 582 1620 559 1621 537 1612 587 1590 589 530 538 554 535 558 531 561 538 554 535 560 539 551 538 554 535 558 531 561 538 554 535 557 532 561 538 556 533 1642 537 1640 538 1613 565 1638 561 1590 589 1588 580 1623 556 1598 560 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4400 4357 561 1616 563 530 559 1619 559 1644 535 557 532 535 564 1613 555 565 534 556 533 1645 534 533 556 536 563 1614 565 1639 529 537 563 1643 536 529 560 558 531 1647 532 1645 533 1618 561 1643 536 1642 537 1642 537 1613 565 1638 530 537 563 556 533 559 530 563 536 556 533 562 538 527 562 1615 563 555 534 1643 536 531 558 561 538 554 535 560 539 1610 558 560 539 1612 556 563 537 1640 538 1639 539 1612 556 1620 558 5189 4398 4345 562 1614 564 529 560 1643 536 1616 563 531 558 534 565 1639 529 565 534 556 533 1619 559 559 530 563 537 1641 538 1640 538 554 535 1645 534 531 558 560 539 1639 529 1622 556 1621 558 1646 532 1619 559 1646 533 1617 562 1642 537 557 532 560 539 554 535 557 532 561 538 556 533 558 531 1620 559 560 539 1638 530 563 536 556 533 560 539 555 534 1642 537 556 533 1619 559 559 530 1622 557 1621 558 1620 558 1647 532 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4402 4346 561 1615 564 555 534 1644 535 1643 536 531 558 561 538 1639 539 555 534 531 558 1645 533 560 529 563 536 1641 537 1640 538 528 561 1645 533 531 558 561 538 1639 539 1638 530 1622 557 1647 532 1646 564 1590 557 1619 559 1618 561 558 531 562 537 555 534 559 530 563 536 558 531 1645 533 559 530 1648 530 1647 531 1646 532 1646 532 560 539 556 533 558 531 1621 557 561 538 555 534 558 531 562 537 1641 537 1639 539 5183 4405 4339 558 1645 533 559 530 1648 530 1647 532 561 538 555 534 1644 535 560 539 551 538 1614 564 555 534 559 530 1647 532 1620 558 561 538 1615 563 553 536 531 558 1620 558 1644 534 1643 536 1642 537 1641 537 1642 536 1613 565 1638 530 563 536 557 532 534 565 554 535 557 532 563 536 1613 565 554 535 1642 537 1641 537 1640 538 1640 538 554 535 560 539 551 538 1614 564 528 561 558 531 562 537 555 534 1644 534 1645 533 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4404 4354 564 1614 564 528 561 1617 562 1642 537 530 559 534 565 1613 555 539 560 556 533 1619 559 534 555 563 536 1642 537 1641 537 555 534 1646 533 532 557 562 537 1640 538 1613 565 1613 555 1648 531 1647 532 1622 557 1619 559 1618 560 559 530 563 536 556 533 560 539 554 535 559 530 561 538 1639 539 554 535 1616 563 1615 564 1615 563 555 534 561 538 1612 556 563 536 1641 538 529 560 533 556 563 537 1641 538 1614 564 5183 4404 4342 555 1622 557 562 537 1641 537 1640 538 529 560 533 556 1648 531 538 562 555 534 1618 560 559 530 562 537 1615 563 1640 538 529 560 1646 532 532 557 562 537 1640 538 1639 539 1639 539 1638 530 1648 530 1649 540 1611 557 1646 532 561 538 554 535 558 531 562 537 555 534 561 538 552 537 1641 537 555 534 1644 534 1643 536 1617 562 557 532 563 536 1639 539 553 536 1642 537 556 533 560 539 554 535 1642 537 1643 535 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4403 4333 563 1615 563 528 561 1618 560 1618 560 531 558 561 538 1614 564 556 533 558 541 1611 557 562 537 555 534 1619 559 1618 560 558 541 1613 565 551 538 1614 564 1614 564 1614 564 1613 565 528 561 1617 561 1619 559 1617 561 557 532 535 564 528 561 533 566 1611 557 562 537 558 531 1619 559 1619 559 1619 559 559 540 553 536 557 532 561 538 557 532 559 540 553 536 557 532 1620 558 1620 558 1620 558 1620 558 1618 560 5188 4398 4346 561 1618 560 558 531 1621 557 1621 557 561 538 555 534 1619 559 561 538 553 536 1616 562 556 533 560 539 1614 564 1613 565 554 535 1619 559 557 532 1621 557 1620 558 1620 558 1620 558 560 539 1613 565 1615 563 1613 565 553 536 557 532 535 564 554 535 1618 560 558 531 564 535 1615 563 1615 563 1614 564 555 534 559 540 552 537 530 559 536 563 528 561 532 557 562 537 1615 563 1615 563 1614 564 1614 564 1616 562 diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 79478e7eb..3965013da 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,1622 +1,1658 @@ Filetype: IR library file Version: 1 # -name: POWER +name: Power type: parsed protocol: SIRC address: 01 00 00 00 command: 15 00 00 00 # -name: POWER +name: Power type: parsed protocol: SIRC address: 10 00 00 00 command: 15 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 08 00 00 00 command: 05 00 00 00 # -name: VOL+ +name: Vol_up type: parsed protocol: NEC address: 08 00 00 00 command: 00 00 00 00 # -name: VOL- +name: Vol_dn type: parsed protocol: NEC address: 08 00 00 00 command: 01 00 00 00 # -name: CH+ +name: Ch_next type: parsed protocol: NEC address: 08 00 00 00 command: 02 00 00 00 # -name: CH- +name: Ch_prev type: parsed protocol: NEC address: 08 00 00 00 command: 03 00 00 00 # -name: MUTE +name: Mute type: parsed protocol: NEC address: 08 00 00 00 command: 0b 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 00 df 00 00 command: 1c 00 00 00 # -name: VOL+ +name: Vol_up type: parsed protocol: NECext address: 00 df 00 00 command: 4b 00 00 00 # -name: VOL- +name: Vol_dn type: parsed protocol: NECext address: 00 df 00 00 command: 4f 00 00 00 # -name: CH+ +name: Ch_next type: parsed protocol: NECext address: 00 df 00 00 command: 09 00 00 00 # -name: CH- +name: Ch_prev type: parsed protocol: NECext address: 00 df 00 00 command: 05 00 00 00 # -name: MUTE +name: Mute type: parsed protocol: NECext address: 00 df 00 00 command: 08 00 00 00 # -name: POWER +name: Power type: parsed protocol: Samsung32 address: 0e 00 00 00 command: 0c 00 00 00 # -name: MUTE +name: Mute type: parsed protocol: Samsung32 address: 0e 00 00 00 command: 0d 00 00 00 # -name: VOL+ +name: Vol_up type: parsed protocol: Samsung32 address: 0e 00 00 00 command: 14 00 00 00 # -name: VOL- +name: Vol_dn type: parsed protocol: Samsung32 address: 0e 00 00 00 command: 15 00 00 00 # -name: CH+ +name: Ch_next type: parsed protocol: Samsung32 address: 0e 00 00 00 command: 12 00 00 00 # -name: CH- +name: Ch_prev type: parsed protocol: Samsung32 address: 0e 00 00 00 command: 13 00 00 00 # -name: POWER +name: Power type: parsed protocol: RC6 address: 00 00 00 00 command: 0c 00 00 00 # -name: POWER +name: Power type: parsed protocol: Samsung32 address: 07 00 00 00 command: 02 00 00 00 # -name: POWER +name: Power type: parsed protocol: Samsung32 address: 07 00 00 00 command: E6 00 00 00 # -name: VOL+ +name: Vol_up type: parsed protocol: Samsung32 address: 07 00 00 00 command: 07 00 00 00 # -name: VOL- +name: Vol_dn type: parsed protocol: Samsung32 address: 07 00 00 00 command: 0B 00 00 00 # -name: CH+ +name: Ch_next type: parsed protocol: Samsung32 address: 07 00 00 00 command: 12 00 00 00 # -name: CH- +name: Ch_prev type: parsed protocol: Samsung32 address: 07 00 00 00 command: 10 00 00 00 # -name: MUTE +name: Mute type: parsed protocol: Samsung32 address: 07 00 00 00 command: 0F 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 50 00 00 00 command: 17 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 40 00 00 00 command: 12 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 31 49 00 00 command: 63 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: aa 00 00 00 command: 1c 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 38 00 00 00 command: 1c 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 83 7a 00 00 command: 08 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 53 00 00 00 command: 17 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 18 18 00 00 command: c0 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 38 00 00 00 command: 10 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: aa 00 00 00 command: c5 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 04 00 00 00 command: 08 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 18 00 00 00 command: 08 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 71 00 00 00 command: 08 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 80 6f 00 00 command: 0a 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 48 00 00 00 command: 00 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 80 7b 00 00 command: 13 00 00 00 # -name: POWER +name: Power type: parsed protocol: Samsung32 address: 0e 00 00 00 command: 14 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 80 7e 00 00 command: 18 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 50 00 00 00 command: 08 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 80 75 00 00 command: 0a 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 80 57 00 00 command: 0a 00 00 00 # -name: POWER +name: Power type: parsed protocol: Samsung32 address: 0b 00 00 00 command: 0a 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: aa 00 00 00 command: 1b 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 85 46 00 00 command: 12 00 00 00 # -name: POWER +name: Power type: parsed protocol: Samsung32 address: 05 00 00 00 command: 02 00 00 00 # -name: POWER +name: Power type: parsed protocol: Samsung32 address: 08 00 00 00 command: 0f 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 00 00 00 00 command: 01 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 00 00 00 00 command: 01 00 00 00 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 634 2571 505 519 479 519 479 518 480 518 480 518 480 518 480 517 481 547 481 517 481 20040 590 2555 501 1007 999 997 510 548 480 486 512 486 512 486 542 485 513 516 482 116758 593 2552 504 1004 992 1004 514 514 514 483 515 513 485 483 545 482 516 482 516 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 525 1955 449 1999 476 4545 446 4544 478 2032 443 2006 469 2011 444 4577 445 4545 447 4574 448 2002 473 4547 444 34913 447 2032 443 2007 478 4542 449 4541 471 2039 446 2004 471 2008 447 4574 448 4543 448 4572 450 2030 445 4545 446 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 2445 582 1221 603 548 602 1191 609 572 602 1191 609 542 607 544 631 1172 603 568 606 545 605 566 608 543 26263 2414 611 1192 607 544 606 1197 602 569 606 1197 602 539 611 540 635 1168 606 565 610 541 608 563 587 564 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3461 1802 439 452 444 1283 469 448 438 453 443 447 439 452 444 419 497 448 438 425 471 419 467 451 445 445 441 450 446 1281 471 420 466 425 471 446 440 424 472 445 461 456 440 451 445 445 441 450 446 1281 471 447 439 451 445 419 467 423 473 445 441 422 494 450 436 428 468 1259 493 425 471 1256 496 1259 472 1281 471 1285 467 423 473 417 469 1286 466 452 444 1283 469 1285 467 1261 491 1263 468 423 493 1260 471 74142 3578 1713 467 423 473 1281 471 420 466 452 444 419 467 424 472 418 488 429 467 451 445 445 441 450 446 444 442 449 437 1290 472 446 440 423 473 445 441 449 467 396 490 428 468 449 447 444 442 448 438 1289 473 445 441 450 446 444 442 449 437 426 470 421 495 422 464 426 470 1257 495 450 446 1254 488 1267 464 1290 472 1282 470 421 465 453 443 1284 468 450 446 1281 471 1283 469 1259 493 1261 470 448 468 1258 473 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 389 1737 280 796 253 744 295 754 274 775 274 776 273 1827 271 1828 270 805 254 1820 278 796 253 797 252 745 294 1806 302 773 245 48942 305 1821 277 798 251 746 303 747 271 778 271 1829 279 796 253 796 253 1821 277 798 251 1823 275 1824 274 1825 273 802 247 1827 271 42824 381 1745 272 804 245 752 297 753 275 773 276 774 275 1825 273 1826 272 803 246 1828 270 805 254 795 244 753 296 1804 294 781 247 48939 379 1746 271 804 245 779 270 753 275 774 275 1825 273 802 247 802 247 1827 271 804 245 1829 279 1820 278 1821 277 798 251 1823 275 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 562 1721 561 594 557 597 564 617 513 615 536 618 543 1715 566 1716 566 1692 559 594 567 588 563 618 543 611 540 615 536 618 543 1715 556 623 538 617 534 621 530 624 516 638 513 642 509 1722 560 620 541 1717 565 1692 559 1724 568 1715 556 1701 560 1723 559 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8436 4189 538 1563 566 1559 539 510 559 543 516 507 542 560 509 540 509 567 512 1586 512 1562 567 1559 539 536 533 1566 542 507 562 513 536 540 509 22102 647 1478 559 1568 540 508 541 535 534 515 534 568 511 538 511 539 540 1585 513 1560 559 1567 541 534 535 1564 534 515 534 568 511 538 511 22125 644 1482 565 1561 537 511 538 564 515 508 541 535 534 541 508 516 563 1588 510 1563 556 1570 538 510 559 1567 541 534 515 535 534 541 508 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 929 825 1711 934 797 930 791 909 822 932 789 938 793 934 797 930 791 1719 899 856 1711 907 824 90848 923 830 1706 912 819 908 823 931 790 910 822 933 788 912 819 935 796 1714 904 850 1707 939 792 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 448 2031 444 2005 480 4540 452 4539 473 2037 438 2011 474 2006 449 4571 451 4539 453 4568 444 2036 449 4541 451 34906 527 1953 451 1998 477 4543 449 4542 480 2030 445 2004 471 2009 446 4575 447 4543 449 4572 450 1999 476 4545 446 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 9021 4496 567 1664 567 1690 561 567 533 1698 563 1667 564 1693 568 1663 568 586 534 567 563 565 535 594 536 591 539 589 531 571 539 563 567 1690 541 560 560 594 536 592 538 564 536 1695 566 1664 567 1690 561 1670 561 1696 555 1675 566 562 558 596 514 562 568 559 561 594 536 565 535 593 537 591 539 1665 566 1692 559 1671 560 1697 564 1666 565 1666 565 1693 558 1672 569 23181 9013 4504 569 1689 542 1689 562 565 535 1697 564 1666 646 1610 560 1672 559 595 535 593 537 590 510 566 564 590 540 588 532 596 514 588 542 1689 542 560 560 594 536 592 538 590 510 1694 567 1664 567 1690 561 1669 562 1695 556 1675 566 561 559 596 514 588 542 585 535 593 537 591 509 593 537 591 539 1665 566 1692 559 1671 560 1697 564 1667 564 1667 564 1693 558 1672 569 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8041 3979 513 536 482 515 513 1559 509 515 514 1560 508 515 514 509 509 514 484 4000 533 1566 512 537 481 1566 512 537 481 1566 512 537 481 516 513 537 481 24150 8044 3977 505 518 510 539 479 1567 511 512 506 1568 510 539 479 543 485 538 480 3977 536 1564 514 534 484 1563 515 508 510 1563 515 508 510 540 489 534 484 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 383 2027 295 2112 271 2138 266 890 271 887 294 926 235 2114 300 887 274 915 236 2145 269 887 274 884 297 923 238 920 241 917 264 895 266 26573 384 2026 296 2111 273 2136 268 889 272 886 295 924 237 2113 301 886 275 914 237 2144 270 886 275 914 267 921 240 919 242 916 265 924 237 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 177 8474 175 5510 174 8476 173 8477 177 8504 171 5515 175 8476 178 8472 177 8473 176 5541 174 8476 173 45583 171 8481 178 5507 177 8473 176 8474 175 8506 173 5512 172 8478 176 8475 174 8476 178 5538 177 8474 175 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8044 3976 506 517 511 1563 505 517 511 538 480 517 511 538 460 563 455 568 460 3993 530 545 483 1564 514 1559 509 1564 514 509 509 540 488 535 483 513 505 24150 8043 3978 514 509 509 1564 514 509 509 540 478 519 509 540 458 565 464 559 459 3994 529 546 482 1565 513 1560 508 1565 513 510 508 541 487 536 482 541 477 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 558 2942 450 10021 482 2989 484 10015 447 3024 449 10021 472 3100 485 2986 477 2994 500 2999 475 2996 477 2994 479 2992 482 3018 476 2995 479 6464 484 36270 477 3023 450 10020 473 2999 485 10014 448 3022 452 10019 474 3098 478 2994 480 2991 503 2996 477 2994 480 2992 482 2990 484 3015 479 2992 481 6462 485 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 587 2407 476 1062 965 547 482 547 451 577 452 546 452 22108 590 2404 479 1060 967 1028 510 519 509 548 480 487 511 120791 645 2411 472 1066 961 1065 483 515 514 514 504 493 515 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 172 7439 171 7441 169 7443 177 7434 176 7462 178 4887 176 4916 177 4914 169 7469 171 4920 174 4918 175 55174 176 7436 174 7437 173 7439 171 7440 175 7463 172 4894 174 4917 171 4921 172 7465 175 4916 178 4914 169 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 589 2556 500 524 474 554 454 544 454 543 455 543 455 543 455 543 445 583 446 552 446 20046 584 2561 505 1033 485 514 963 518 480 1032 516 512 476 522 506 491 507 522 476 116758 586 2560 506 1033 484 513 964 548 450 1031 507 522 476 521 507 490 508 521 477 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 586 2407 476 1063 964 578 450 548 450 547 481 517 481 577 451 546 452 546 472 556 452 545 453 575 453 514 484 544 484 543 455 14954 510 2483 481 1058 480 548 959 552 446 1067 481 516 512 546 482 515 992 1003 535 493 515 543 486 513 475 522 506 552 446 111671 589 2405 478 1061 477 551 967 514 484 1059 479 549 479 548 480 517 990 1036 512 516 482 546 483 515 503 525 483 544 454 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8444 4180 537 1564 565 1560 538 1561 557 1568 540 1559 539 536 533 542 517 559 510 1563 535 1564 565 1561 537 512 567 1558 540 535 534 1566 542 1557 562 23122 564 1562 557 1569 539 1560 538 1587 542 1558 540 534 535 515 534 541 538 1587 511 1563 566 1560 538 536 533 1567 541 534 515 1585 533 1566 542 23166 561 1565 564 1561 537 1563 535 1590 539 1561 537 538 541 534 515 535 534 1564 534 1566 563 1563 535 540 539 1560 538 511 538 1588 541 1559 539 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 527 1923 481 1998 477 4543 449 4542 470 2040 445 2004 471 2009 446 4575 447 4543 449 4572 450 1999 476 2034 441 34899 524 1956 448 2001 474 4546 446 4545 477 2033 442 2007 478 2002 443 4578 444 4546 445 4575 447 2003 472 2037 448 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 533 1356 437 3474 427 3483 429 3455 436 1454 430 1459 405 28168 510 1379 434 3477 434 3476 425 3459 432 1457 427 1462 402 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 921 833 1714 932 789 938 793 934 797 903 818 909 822 905 816 938 793 1716 902 853 1714 905 816 90856 922 805 1742 931 790 937 794 933 788 939 792 935 796 930 791 937 794 1715 903 825 1742 904 817 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 179 7433 177 4915 178 7434 175 7436 174 7464 176 7435 175 4916 177 4915 173 4918 170 4922 171 4920 173 55174 175 7437 173 4919 174 7437 173 7439 171 7467 173 7438 172 4920 173 4919 174 4917 176 4915 178 4914 169 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 169 6731 176 6748 169 6730 177 6748 169 6755 172 4427 177 4447 178 6721 175 6749 178 4446 168 4456 169 54704 176 6723 174 6750 177 6723 173 6750 177 6747 170 4429 175 4449 176 6723 174 6751 176 4448 177 4447 178 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3506 3494 876 830 840 2576 847 2568 844 862 819 2570 842 864 816 863 818 2570 842 836 844 2572 840 866 815 865 815 2573 839 867 813 866 814 2573 850 857 813 2575 847 2568 844 834 847 2569 843 835 845 2571 872 2571 842 32654 3512 3488 872 834 847 2570 842 2573 839 867 814 2574 849 858 822 857 813 2575 848 859 821 2566 846 832 848 860 821 2567 845 833 848 860 820 2568 844 834 847 2569 843 2572 840 838 843 2574 849 829 841 2575 868 2575 837 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 560 2939 453 10018 475 2996 477 10022 450 3021 452 10018 475 3097 478 6464 483 6460 477 6466 502 6469 479 2993 480 2990 484 36274 564 2936 456 10015 478 2993 481 10020 534 2936 456 10014 479 3093 482 6461 476 6466 482 6461 476 6495 473 2999 485 2986 477 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 10726 41047 10727 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 1617 4604 1559 1537 1560 1537 1560 4661 1533 33422 1613 4607 1566 1530 1556 1540 1536 4685 1539 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 174 4972 177 4910 173 4944 170 6988 174 6984 177 6951 175 6983 174 14269 177 4969 175 4912 176 4941 178 6979 172 6986 175 6953 178 6980 171 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 174 7067 176 10544 1055 719 1053 739 1054 738 953 822 1052 2566 169 3435 171 3431 175 3446 170 3432 174 3446 170 3432 174 3428 178 3442 174 3429 177 3425 171 39320 2323 4900 178 10543 1056 719 1053 739 1054 738 953 821 1053 2566 169 3435 171 3431 175 3445 171 3432 174 3446 170 3432 174 3428 178 3442 174 3428 178 3425 171 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3506 3492 868 839 841 2575 848 858 822 2566 846 832 848 859 821 858 812 2576 847 860 820 2567 845 833 847 860 821 2568 844 862 818 2570 842 864 817 2571 841 2574 849 2567 845 861 820 2568 845 834 847 2570 873 2570 842 34395 3503 3496 874 833 847 2568 845 834 847 2570 842 864 816 835 846 862 819 2569 843 835 846 2571 841 865 816 864 816 2571 841 837 844 2573 850 857 813 2575 848 2567 845 2570 842 864 816 2572 840 838 842 2574 869 2574 838 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 170 8479 170 5516 178 8472 177 8474 175 8506 173 5513 171 8479 175 8476 178 8472 177 5540 175 8475 174 45584 177 8473 176 5509 175 8476 173 8477 176 8504 170 5516 178 8472 177 8474 175 8476 173 5543 172 8479 170 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 178 4969 170 6958 173 4944 175 6983 173 6956 174 6984 177 6980 171 16308 180 4966 173 6955 176 4941 172 6985 176 6982 169 6960 176 6982 174 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 585 2409 474 550 478 550 448 1033 994 1063 485 512 506 79916 585 2410 473 551 477 550 448 1034 993 1033 515 543 475 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 1192 1012 6649 26844 1192 1013 6648 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3134 6105 6263 82963 3134 6105 6263 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 588 1511 567 1533 565 589 531 597 513 589 541 587 533 1565 533 569 541 1533 565 562 568 1531 537 1537 561 593 537 591 539 1507 561 1539 569 22152 586 1513 565 1535 563 590 540 562 538 564 566 588 542 1557 531 597 513 1534 564 590 540 1533 535 1539 559 568 562 592 538 1509 569 1531 567 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 923 831 1715 903 818 936 795 932 789 938 793 934 797 1713 1740 932 789 911 821 934 798 930 791 90853 928 799 1737 935 796 931 790 937 795 932 789 938 793 1717 1736 936 796 932 789 911 820 934 798 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 689 1461 566 1534 564 589 531 597 513 1534 564 564 566 1533 535 620 510 1537 561 592 538 1535 543 1531 567 587 533 595 535 1511 567 1533 565 22161 588 1512 566 1534 564 564 556 598 512 1535 563 565 565 1534 534 594 536 1538 560 593 537 1536 542 1532 566 587 543 585 535 1511 567 1534 564 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 588 2558 498 526 482 515 483 546 452 545 453 545 453 545 453 545 453 544 474 554 444 20047 583 2562 504 519 479 519 479 1003 515 483 1024 548 450 1001 995 1001 506 116771 593 2552 504 520 478 551 447 1004 513 514 993 549 449 1002 994 1002 516 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 587 2559 507 517 481 517 481 547 451 516 482 546 452 546 452 546 452 545 473 525 483 20038 592 2553 503 521 477 552 446 521 477 1004 514 515 992 1034 483 514 484 513 485 116769 593 2552 504 520 478 550 448 520 478 1003 515 514 993 1032 486 513 475 522 476 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 4558 4576 558 596 565 97881 4554 4579 565 589 562 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 1039 7202 958 4713 981 4713 961 7255 956 4739 955 16077 1038 7204 956 4714 980 4715 959 7256 954 4741 954 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 506 2638 510 516 482 516 482 546 452 546 452 545 453 545 453 545 453 575 443 554 444 20048 592 2554 502 1036 481 517 481 517 511 516 482 516 961 1035 513 515 483 514 484 116757 594 2552 504 1034 484 514 484 514 504 524 484 513 964 1032 506 522 476 492 506 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 170 7441 169 4924 174 7437 193 7444 171 7441 174 4918 170 7441 174 4917 171 7440 195 7443 172 7439 171 55187 174 7437 173 4919 175 7437 173 7438 172 7466 175 4891 172 7439 171 4921 172 7439 171 7441 174 7464 171 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 179 4967 172 4915 178 6980 171 4945 169 4948 176 4912 176 4941 178 16307 176 4969 175 4912 176 6982 174 4942 171 4945 169 4919 174 4943 176 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 1042 793 898 883 869 858 924 884 878 877 875 879 873 855 928 1717 901 854 1794 878 894 887 875 1716 902 89114 985 798 903 878 874 880 902 852 900 855 897 857 905 876 896 1722 906 849 1789 883 899 855 897 1721 897 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 170 8480 169 8481 178 8472 177 8474 175 8476 173 5513 176 8474 175 8476 173 8507 178 5509 175 8505 174 45558 175 8476 173 8476 173 8477 172 8478 171 8480 169 5517 177 8473 176 8474 175 8506 174 5512 172 8509 171 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 628 2577 499 528 480 545 453 544 454 544 454 544 454 544 454 543 455 543 475 553 445 20047 593 2553 503 521 477 551 447 1004 514 515 992 519 479 1003 515 513 485 543 455 116768 585 2561 505 519 479 519 479 1002 516 513 994 548 450 1001 506 522 476 521 477 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8987 4505 568 586 534 594 536 591 509 567 563 591 539 563 567 587 513 1692 559 1672 559 1698 563 590 510 592 538 590 540 1664 567 1690 561 593 507 1699 562 1668 563 1694 567 1663 568 586 534 568 562 592 508 595 535 1695 536 592 538 1693 558 595 515 1690 561 593 517 585 535 567 563 39551 8994 4497 566 589 541 560 560 594 516 586 534 594 536 592 538 590 510 1695 566 1664 567 1690 561 593 507 595 535 566 564 1667 564 1693 558 596 534 1670 561 1670 561 1696 565 1666 565 589 541 587 533 595 515 587 533 1697 534 568 562 1695 556 572 538 1693 568 559 541 588 542 585 535 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8987 4504 569 559 561 593 537 565 535 567 563 591 539 588 532 597 513 1691 560 568 562 592 508 1697 564 589 511 565 565 1692 559 1672 559 569 561 1669 562 566 564 564 566 1665 566 588 542 586 534 1670 561 567 563 565 535 593 537 591 539 1692 539 589 541 586 534 594 536 592 508 40679 8987 4504 569 585 535 593 537 591 509 566 564 591 539 588 532 596 514 1691 560 594 536 592 508 1697 564 589 511 591 539 1692 559 1671 560 594 536 1669 562 592 538 590 540 1664 567 587 543 585 535 1670 561 593 537 565 535 593 537 591 539 1691 540 588 542 586 534 594 536 592 508 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 298 1828 270 804 245 1829 279 1820 278 797 252 771 278 1822 276 1824 274 800 249 1825 273 802 247 776 252 771 278 1822 276 799 250 48931 388 1737 280 796 253 1821 277 1822 276 798 251 1823 275 800 249 774 275 1825 273 776 273 1801 297 1828 270 1829 279 796 253 1821 277 42813 301 1825 273 801 248 1826 272 1827 271 804 245 805 244 1830 278 1821 277 798 251 1823 275 799 250 774 244 779 280 1820 278 796 253 48926 382 1744 354 696 271 1828 270 1829 279 796 253 1821 277 797 252 746 303 1823 275 774 275 1799 299 1826 272 1827 271 804 245 1829 279 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 177 5508 176 5539 176 8475 174 5542 173 8478 171 5545 170 8481 168 8482 177 8473 176 5541 174 8476 173 45573 169 5517 177 5538 177 8473 176 5541 174 8476 173 5544 171 8479 170 8481 178 8472 177 5540 175 8475 174 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 175 8474 175 8475 174 8477 172 8478 171 8480 169 8481 178 5538 177 5510 174 5542 173 5543 172 5544 171 45575 177 8472 177 8474 175 8476 173 8477 172 8478 171 8481 178 5507 177 5539 176 5540 175 5542 173 5543 172 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8050 3971 511 1562 516 1558 510 539 490 1557 511 1563 515 533 485 538 490 533 485 3983 530 1569 509 1564 514 1559 509 1565 513 1560 508 515 513 536 482 541 488 24152 8042 3979 514 1560 508 1565 513 510 508 1565 513 1560 508 515 513 536 482 541 488 3980 533 1567 511 1562 516 1557 511 1563 515 1558 510 539 489 534 484 539 490 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 175 8475 174 5512 177 8473 176 8475 174 8506 179 5508 176 8474 175 8475 174 8477 172 5544 171 8480 169 45587 176 8475 174 5511 173 8477 177 8473 176 8504 170 5516 178 8472 177 8473 176 8475 174 5542 173 8478 171 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 176 7436 174 7438 172 7439 171 7441 174 7463 172 7440 170 4921 172 4919 174 4917 176 4916 177 4914 174 55176 175 7437 173 7439 191 7446 174 7438 177 7434 176 7435 175 4917 176 4915 179 4914 174 4917 171 4946 178 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 176 7435 175 4917 177 7435 175 7436 189 7449 176 7435 175 7437 173 4918 175 7436 174 4918 175 4916 178 55171 175 7436 174 4918 170 7441 174 7438 177 7460 170 7441 174 7438 177 4914 174 7437 178 4914 169 4922 171 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8049 3973 509 1564 514 509 509 1564 514 535 483 1564 514 535 483 540 489 535 483 3980 533 1566 512 537 481 1566 512 511 507 1566 512 537 481 542 486 511 507 24149 8045 3976 516 1558 510 512 516 1557 511 512 516 1558 510 512 516 507 511 512 506 3984 539 1560 508 515 513 1560 508 541 488 1560 508 541 488 536 482 514 504 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 366 202 867 527 170 130516 343 227 863 529 168 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 176 5992 176 1372 176 1320 177 1345 172 1349 179 1370 178 1317 175 4444 176 1346 171 1351 177 1345 172 1349 179 1344 174 5963 175 65574 172 5995 178 1344 174 1348 175 1347 175 1347 170 1378 170 1325 172 4447 178 1344 173 1349 169 1353 175 1347 170 1351 177 5961 177 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 497 1478 488 511 467 1482 494 531 447 1477 499 501 466 533 445 530 468 507 471 529 438 12857 488 1485 491 509 469 1481 495 529 448 1476 490 510 468 532 445 529 469 480 498 502 465 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 982 6313 961 2662 903 2718 929 2719 907 6363 900 2722 904 6365 909 6361 903 6368 906 2742 905 46364 989 6307 906 2716 911 2712 925 2723 903 6367 907 2715 901 6369 905 6365 909 6361 903 2745 902 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 599 257 975 317 177 130649 308 228 974 319 175 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 176 4969 170 6958 173 6985 177 6981 171 6958 178 6980 177 6981 170 16308 180 4966 173 6955 176 6982 169 6988 174 6956 175 6983 173 6984 172 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 178 5990 173 1349 174 1348 175 1348 174 4444 176 1346 171 1351 177 4416 178 1344 173 1348 169 1353 175 1347 170 1351 177 9048 177 65573 173 5995 173 1348 175 1348 174 1347 176 4444 171 1351 177 1345 173 4421 173 1348 169 1352 176 1347 170 1351 177 1345 172 9053 177 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 174 4972 177 4910 173 6985 177 4940 174 6984 178 6951 175 6983 174 14269 177 4968 176 4912 176 6981 175 4941 173 6985 177 6953 178 6979 172 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 1318 225 177 130305 1609 231 171 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 585 2410 473 1065 962 581 447 519 479 580 448 549 449 579 449 518 480 548 480 517 481 517 481 577 451 516 482 576 452 545 453 14955 510 2483 481 1058 480 549 969 543 445 1037 511 547 481 546 482 516 482 545 484 545 483 514 484 544 963 1063 485 543 445 111612 626 2427 557 954 513 512 995 547 451 1031 507 521 508 551 477 520 478 550 478 549 479 488 510 548 969 1057 481 517 481 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 203 272 1215 302 171 130229 1423 275 178 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 176 7436 174 7438 192 7446 174 7437 178 7434 176 7435 175 4917 176 4915 178 4913 175 4917 197 7440 175 55130 286 7377 177 7435 175 7437 173 7438 172 7466 174 7411 178 4913 170 4921 172 4919 174 4918 175 7436 174 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 175 7437 173 4918 170 7441 174 7437 178 7460 170 7441 179 7433 177 4915 178 4913 170 4922 171 4920 173 55124 291 7372 172 4921 172 7439 171 7441 174 7463 172 7440 170 7442 178 4913 170 4922 171 4920 173 4918 175 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 172 8477 172 8478 171 8480 169 5516 179 8502 178 5509 175 8475 174 8476 173 8479 170 5545 170 8480 169 45588 176 8473 176 8474 175 8476 173 5513 177 8504 171 5515 174 8476 178 8472 177 8473 176 5541 174 8476 173 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 177 7436 174 4918 175 7436 174 4918 175 7462 178 4887 176 4916 177 4914 174 4917 171 4921 172 4919 175 55184 175 7435 175 4918 175 7436 174 4918 175 7462 178 4914 174 4917 171 4920 173 4919 174 4917 176 4915 178 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 508 2484 480 1060 967 544 485 544 454 574 454 543 455 573 445 553 445 522 506 552 446 552 446 551 477 551 447 581 447 520 478 550 478 15908 626 2427 476 1062 476 522 995 547 451 1061 477 520 508 550 478 519 479 519 999 1058 959 1037 990 582 446 1035 483 111666 590 2404 479 1059 479 549 968 543 455 1027 511 548 480 517 511 486 512 546 961 1065 962 1034 993 579 449 1032 486 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 175 8475 174 8475 174 8477 172 8478 171 8480 169 5517 178 8473 175 8475 179 8501 173 5513 176 8505 169 45562 179 8472 177 8473 176 8474 175 8476 173 8478 171 5515 174 8476 178 8473 176 8505 174 5512 172 8508 171 120769 178 93377 175 7437 173 4919 174 7437 173 7439 171 7467 173 7439 171 7441 174 4918 170 4921 172 7439 171 7467 173 55167 171 7440 170 4922 171 7440 195 7443 172 7439 171 7441 174 7438 177 4915 173 4918 195 7442 173 7439 170 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 295 1805 273 776 242 1808 270 754 244 1806 272 777 241 758 270 754 264 760 268 756 272 14149 297 1802 266 784 244 1805 263 762 246 1803 265 785 244 780 248 751 267 758 271 753 265 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 535 1723 569 585 566 615 536 619 542 586 565 616 535 1722 539 615 536 1721 561 620 541 587 564 617 534 621 540 588 563 618 543 1714 537 617 534 621 540 615 536 618 543 612 539 615 536 1722 560 594 567 1717 534 1723 569 1714 557 1700 643 1639 643 1641 559 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 502 2521 504 521 997 545 453 575 453 545 453 575 454 22097 591 2433 511 513 994 1062 476 552 476 522 476 552 476 120839 628 2455 509 515 992 1064 484 513 505 493 515 543 475 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 591 2404 479 1060 967 1029 509 519 509 549 479 518 480 79926 585 2408 556 956 989 1033 515 544 484 543 475 522 476 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 586 2560 506 518 480 548 450 548 450 517 481 517 481 517 481 547 451 546 482 516 482 20040 590 2556 500 1038 480 518 969 543 455 543 475 1006 511 517 481 517 511 486 481 116778 584 2561 505 1033 485 513 964 548 450 548 480 1001 506 522 476 522 506 521 446 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 917 206 175 186 170 21561 170 2280 175 2274 502 1929 174 2276 169 5178 170 2261 173 3724 498 1932 171 2279 176 2273 172 3709 172 2277 178 3720 171 17223 177 7619 174 2275 170 2279 176 2256 168 2280 175 5172 176 2256 168 3729 173 2276 179 2253 171 2278 177 3703 178 2271 174 3724 177 17251 170 7627 177 2272 173 2276 169 2263 171 2277 178 5169 169 2262 172 3726 175 2256 168 2280 175 2274 171 3710 171 2278 177 3720 171 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 565 233 653 313 170 130328 752 235 233 107 229 398 177 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 586 2439 505 549 968 1027 511 517 511 517 481 547 481 21583 584 2439 505 520 997 1059 479 549 479 518 480 548 480 120894 593 2432 501 522 995 1061 477 521 507 520 478 550 478 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 558 8032 474 8115 503 8116 482 8108 480 8141 477 5239 476 8114 504 8115 483 8107 481 5236 509 8111 477 45290 554 8036 481 8108 510 8110 478 8112 476 8145 473 5243 482 8107 511 8109 479 8111 477 5240 505 8115 473 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 173 7438 172 4920 173 7438 172 4920 173 7465 175 4890 173 7439 171 4920 173 4919 174 4917 176 4915 178 55179 170 7441 169 4924 174 7437 178 4913 175 7463 172 4893 170 7441 174 4918 170 4922 171 4920 173 4918 175 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 225 745 222 774 193 778 200 797 175 769 193 777 201 771 196 774 224 747 220 776 202 769 198 248 220 252 196 250 218 253 225 746 221 250 198 248 220 252 226 246 222 223 225 248 220 252 216 229 219 253 225 247 221 277 176 243 220 279 189 230 228 244 224 248 220 252 196 250 218 253 215 257 201 770 197 799 189 257 201 271 197 37716 222 749 218 778 200 771 196 801 172 772 200 771 196 774 193 777 221 750 217 780 197 773 194 251 217 255 193 279 199 273 195 749 218 254 194 252 226 245 223 275 178 242 221 277 201 245 193 253 225 247 221 250 218 254 194 252 226 272 196 223 225 248 220 251 217 255 193 253 225 247 221 251 197 773 194 803 174 297 176 244 219 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3503 2655 197 642 876 2568 844 834 846 833 848 860 821 831 839 2576 847 2569 843 2572 841 2574 849 858 823 857 813 866 815 865 815 2572 841 2575 848 2568 844 2570 842 836 844 863 818 834 847 861 820 2568 845 2571 872 2571 842 32651 3505 3495 875 2567 845 861 820 832 849 859 811 840 840 2576 847 2568 844 2571 842 2574 849 830 840 867 814 838 842 865 815 2572 840 2575 848 2568 845 2571 841 865 815 864 817 834 846 861 819 2569 843 2572 871 2572 840 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 347 677 219 252 196 276 202 742 225 798 174 770 192 778 200 771 196 775 223 748 219 777 200 770 197 249 219 253 195 251 227 271 197 747 220 252 216 229 219 253 225 273 195 277 176 244 219 253 215 230 228 244 224 248 220 252 196 250 218 280 198 247 201 245 223 249 219 253 195 251 227 245 223 248 200 797 175 795 198 248 200 246 222 37666 344 678 228 245 223 222 226 771 196 800 198 747 220 750 217 753 224 773 194 776 202 769 198 799 173 246 227 245 223 249 199 247 221 749 218 280 198 247 201 245 223 249 219 253 195 251 227 244 224 248 200 272 196 250 218 254 194 252 226 245 223 249 199 274 194 251 227 245 193 253 225 273 195 251 217 753 225 746 221 251 197 275 193 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 174 7437 173 7440 174 7437 178 7433 177 7461 169 7442 178 4914 174 4917 171 4921 172 4919 174 7463 177 55163 177 7435 174 7437 173 7438 172 7440 175 7463 172 7413 176 4915 178 4913 175 4917 171 4920 174 7438 172 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8042 3979 513 510 508 541 487 1559 509 515 513 1560 508 515 513 510 508 541 477 3981 532 1568 510 1563 515 1558 510 1564 514 1559 509 514 514 535 483 540 488 24151 8042 3979 513 536 482 541 487 1560 508 541 488 1560 508 541 487 536 482 541 477 3980 533 1566 512 1561 507 1566 512 1562 516 1557 511 538 491 533 485 538 490 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8989 4501 562 1696 565 1665 566 562 568 586 514 588 542 586 534 594 536 1667 564 591 539 1692 539 563 567 1690 561 1669 562 1669 562 1696 565 562 538 564 566 588 542 586 534 1670 561 568 562 592 538 590 510 592 538 590 540 561 539 563 567 561 569 585 535 593 507 595 535 593 537 39547 8987 4504 569 1689 562 1668 563 591 539 589 511 591 539 589 541 561 559 1671 560 568 562 1695 536 592 538 1693 558 1673 568 1663 568 1689 562 592 508 594 536 592 538 590 540 1664 567 561 569 559 561 567 543 585 535 593 537 591 509 593 537 591 539 589 541 587 513 589 541 587 533 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8988 4504 640 487 562 592 538 590 510 592 538 590 540 588 532 596 514 614 516 1689 562 1668 563 1695 536 1695 566 1664 567 1690 612 1618 643 1430 801 1613 648 481 558 570 540 589 541 587 533 595 535 592 508 594 536 592 538 1667 564 1693 558 1672 569 1688 563 1668 644 1586 563 1694 567 40630 8994 2265 557 96833 8987 2273 538 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 173 7439 171 4922 172 7439 171 4921 172 7466 174 4917 176 4915 173 4918 170 7441 174 7438 192 7445 175 55181 174 7437 173 4918 175 7437 173 4919 175 7463 177 4888 175 4917 176 4915 178 7460 170 7440 175 7437 178 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 1636 4610 1563 1533 1584 7760 1561 28769 1641 4606 1557 1539 1588 7757 1564 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 590 2404 479 1059 968 575 454 544 454 574 454 543 455 543 475 522 476 552 476 552 446 551 447 581 448 520 478 581 447 519 479 549 479 15967 587 2407 476 1062 476 522 995 547 451 1030 508 520 509 550 478 519 479 519 998 1028 999 1057 481 547 960 1066 482 111641 587 2407 476 1063 485 513 994 547 451 1031 507 551 477 551 477 520 478 550 968 1059 968 1027 511 548 959 1036 512 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 588 2406 477 1061 966 577 451 516 482 545 483 545 453 575 443 524 484 514 504 554 454 543 455 543 475 553 445 583 445 521 477 582 446 15969 585 2409 474 1065 483 514 993 549 449 1033 515 543 475 553 475 522 476 552 965 1030 997 576 452 1029 998 1028 479 111668 587 2407 475 1063 485 543 964 517 481 1031 507 552 476 551 477 520 478 550 968 1028 999 574 454 1027 990 1036 481 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 174 7439 196 7441 174 7437 173 7439 171 7441 174 7438 177 7460 170 7442 178 7433 177 4915 173 4918 170 55186 174 7438 172 7440 170 7441 174 7438 177 7461 169 7416 173 7464 176 7435 175 7437 173 4918 175 4917 176 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 173 7438 172 7441 174 7438 177 7434 176 7462 168 7443 177 7435 175 4917 176 4915 173 7438 177 4941 173 55166 174 7438 197 7441 174 7437 173 7439 171 7441 174 7438 177 7460 170 4922 171 4893 195 7443 172 4920 173 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 175 7436 179 4913 175 4917 171 4920 173 4945 169 4896 177 7435 175 7436 174 7464 176 4916 177 4914 169 55180 170 7441 169 4924 169 4922 171 4920 173 4919 174 4917 176 7435 175 7463 177 7434 176 4916 177 4914 174 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 927 827 1709 936 796 932 789 938 793 1717 1736 936 795 932 789 1721 927 827 1709 909 822 90851 898 829 1738 934 798 930 791 936 796 1715 1738 934 797 930 791 1719 899 828 1739 934 797 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 176 5991 177 1372 176 1320 177 1344 173 1349 174 1374 169 1327 170 4449 176 1346 171 1351 177 1345 172 1349 168 1353 175 5963 175 65573 173 5995 178 1344 174 1348 175 1348 174 1347 170 1378 170 1325 172 4447 178 1344 174 1349 169 1353 175 1347 170 1352 176 5961 177 # -name: POWER +name: Power type: parsed protocol: NEC address: 71 00 00 00 command: 4a 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 60 00 00 00 command: 03 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 60 00 00 00 command: 00 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 42 00 00 00 command: 01 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 50 ad 00 00 command: 00 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 50 ad 00 00 command: 02 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 50 00 00 00 command: 3f 00 00 00 # -name: POWER +name: Power type: parsed protocol: Samsung32 address: 06 00 00 00 command: 0f 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 08 00 00 00 command: 12 00 00 00 # -name: POWER +name: Power type: parsed protocol: Samsung32 address: 08 00 00 00 command: 0b 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 83 55 00 00 command: c2 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 00 00 00 00 command: 51 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 00 bd 00 00 command: 01 00 00 00 # -name: POWER +name: Power type: parsed protocol: Samsung32 address: 00 00 00 00 command: 0f 00 00 00 # -name: POWER +name: Power type: parsed protocol: Samsung32 address: 16 00 00 00 command: 0f 00 00 00 # -name: POWER +name: Power type: parsed protocol: NEC address: 01 00 00 00 command: 01 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 80 68 00 00 command: 49 00 00 00 # -name: POWER +name: Power type: parsed protocol: NECext address: 86 02 00 00 command: 49 00 00 00 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 2383 609 1214 600 612 580 1213 608 614 583 1210 605 607 586 616 611 1192 599 613 608 614 579 613 615 607 26670 2387 601 1212 600 612 589 1214 603 609 586 1217 595 607 594 618 606 1187 602 610 609 613 588 614 610 612 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 178 7761 176 11308 546 957 540 1958 538 970 537 1955 541 962 545 1953 543 964 543 962 545 957 540 970 548 960 547 1945 541 1950 546 965 542 1953 543 962 545 1945 540 970 537 1958 538 7881 172 7744 172 11318 536 971 536 1956 540 963 534 1964 542 966 541 1951 535 968 539 971 536 971 536 969 538 964 533 1965 541 1954 542 963 534 1956 540 971 536 1959 537 968 539 1951 535 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3444 1767 413 486 420 1343 419 478 418 485 411 490 416 479 417 489 417 482 414 485 421 482 414 483 413 490 416 485 411 1343 419 487 419 480 416 483 413 490 416 482 414 488 418 483 413 483 413 492 414 1345 417 482 414 489 417 480 416 487 419 482 414 481 415 491 415 484 412 1347 415 488 418 1338 414 1348 414 1347 415 1340 412 494 412 487 419 1340 412 491 415 1341 421 1341 421 1340 412 1343 419 487 419 1339 413 74311 3445 1753 437 461 445 1316 446 456 440 455 441 465 441 458 438 461 445 458 438 460 436 466 440 461 445 451 445 460 446 1313 439 460 446 457 439 459 437 465 441 460 446 450 436 469 437 462 444 456 440 1322 440 457 439 464 442 459 437 459 437 468 438 461 445 455 441 462 444 1312 440 463 443 1317 445 1310 442 1323 439 1320 442 457 439 464 442 1315 437 466 440 1320 442 1313 439 1326 446 1313 439 460 446 1317 445 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 278 1845 274 808 271 806 273 812 278 805 275 805 274 1840 279 1844 275 809 281 1836 272 806 274 812 278 805 274 1842 277 802 277 44956 279 1842 277 804 275 802 277 808 271 811 279 1838 281 798 271 814 276 1844 275 806 273 1841 278 1845 274 1846 273 808 271 1843 276 44959 275 1845 274 807 272 805 275 811 279 804 275 805 274 1839 280 1844 275 808 271 1845 274 805 274 811 279 804 275 1841 278 801 278 44955 280 1841 278 802 277 801 278 807 272 810 280 1837 271 807 272 813 277 1843 276 805 274 1839 280 1843 276 1845 274 807 272 1842 277 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 881 909 1750 928 875 927 876 921 872 929 874 927 876 918 875 930 873 1815 874 924 1745 937 876 88694 880 922 1747 933 880 914 879 926 877 922 871 927 876 927 876 920 873 1818 871 929 1740 934 879 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 509 1717 504 629 512 631 510 627 504 633 508 633 508 1713 508 1719 512 1713 508 625 505 637 504 633 508 629 512 629 512 623 508 1719 512 626 505 628 513 631 510 627 514 623 507 632 509 1713 508 632 509 1716 505 1715 506 1724 507 1716 505 1719 512 1715 506 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 506 493 505 4059 505 5051 501 506 502 4065 499 511 497 4063 501 5058 504 504 504 4060 504 5052 500 507 501 4066 498 5056 506 5042 500 515 503 119614 504 505 503 4065 499 5051 501 511 497 4069 505 499 499 4072 502 5050 502 506 502 4066 498 5053 499 512 506 4060 504 5044 498 5061 501 508 500 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8887 4470 532 1738 513 1708 533 1708 533 577 533 576 534 569 531 582 538 1705 536 571 539 1707 534 571 539 571 539 570 540 1699 532 581 539 568 532 575 535 576 534 571 539 571 539 569 541 1698 533 1717 534 1708 533 1710 531 1716 535 1706 535 1711 540 1705 536 567 533 580 540 567 533 39042 8915 2231 530 94849 8917 2256 535 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8313 4161 515 1574 514 1571 507 569 510 562 507 563 506 562 507 568 511 562 507 1580 508 1577 511 1582 506 567 513 1575 513 554 505 571 509 564 505 22604 513 1573 505 1589 509 563 506 564 505 562 507 569 511 562 507 563 506 1579 509 1584 514 1576 512 558 511 1574 514 562 507 565 515 556 513 22593 514 1581 507 1583 505 564 505 563 506 570 510 563 506 564 505 562 507 1586 512 1578 510 1577 511 557 512 1581 507 566 514 556 513 555 514 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8735 4383 558 573 557 550 560 568 562 544 566 1722 560 540 560 1732 560 544 566 1719 563 1701 560 1723 559 1704 557 574 556 1699 562 574 556 1703 559 1727 565 1698 563 1721 561 546 564 1723 559 541 559 577 564 541 559 571 560 548 562 565 566 1697 565 567 564 1693 558 1733 559 1701 560 39926 8754 2247 565 92341 8758 2243 589 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3298 3336 821 2506 825 881 820 2505 826 2529 823 856 825 2524 817 866 825 2528 813 2513 818 888 813 862 819 887 814 865 826 2522 820 864 827 2526 815 861 820 887 814 2510 821 885 816 863 818 2531 821 2512 819 2559 793 32401 3298 3349 818 2507 824 882 819 2509 822 2527 814 868 823 2530 821 855 826 2531 821 2504 827 879 822 857 824 875 816 867 824 2529 823 854 827 2530 821 853 817 889 822 2506 825 873 818 866 825 2527 814 2513 818 2564 788 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8840 4439 532 566 534 566 534 1668 532 1674 536 1669 531 562 538 566 534 563 537 1667 533 567 533 563 537 563 537 562 538 1662 538 1671 529 568 532 565 535 566 534 1668 532 1674 536 1669 531 562 538 1672 528 1675 536 1668 532 1675 535 559 531 1676 534 565 535 558 532 1678 532 565 535 562 538 563 537 1665 535 565 535 1670 530 1669 531 572 538 1666 534 1669 531 1676 534 22777 8858 4447 535 92577 8858 4413 538 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 289 2112 261 2109 295 2101 262 918 294 912 259 915 297 2098 265 916 296 909 262 2107 297 905 266 913 289 918 263 910 292 909 262 918 294 24789 263 2106 298 2098 265 2110 294 913 258 916 296 905 266 2108 296 911 260 914 288 2107 266 914 298 908 263 911 291 910 261 919 293 913 258 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8898 4453 569 578 573 577 574 571 570 1745 567 1747 565 1743 569 583 568 579 572 1740 572 1744 568 1742 570 579 572 576 575 568 573 1746 566 1746 566 580 571 1745 567 577 574 576 575 1739 573 569 572 581 570 577 574 1738 574 576 575 1735 567 1748 574 574 567 1742 570 1748 574 1737 575 41005 8876 2261 571 93850 8898 2264 568 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3215 1637 410 430 405 439 406 1242 408 435 410 1242 408 428 407 440 405 435 410 1240 410 1243 407 431 404 440 405 437 408 1238 402 1254 406 434 401 439 406 438 407 431 404 440 405 437 408 428 407 439 406 434 401 439 406 438 407 1241 409 434 401 441 404 432 403 444 401 1249 401 439 406 438 407 1241 409 434 401 441 404 433 402 444 401 1249 401 439 406 1248 402 436 409 434 401 441 404 433 402 444 401 439 406 45471 3239 1614 403 435 400 444 401 1250 400 437 408 1248 402 438 407 433 402 442 403 1245 405 1248 423 420 405 431 404 443 402 1248 402 1248 422 421 404 435 400 443 402 440 405 431 404 443 402 438 407 433 402 442 403 435 400 443 402 1250 400 436 399 447 408 432 403 438 407 1246 404 434 401 443 402 1250 400 436 399 447 408 432 403 437 408 1246 404 434 401 1253 407 434 401 436 399 447 408 432 403 437 408 436 399 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 7928 3948 504 515 503 518 500 1583 505 516 502 1584 504 510 508 516 502 516 502 3952 510 1579 509 507 501 1587 501 519 510 1571 507 518 500 517 501 517 501 23073 7931 3943 509 514 504 515 503 1578 500 524 505 1580 508 510 508 514 504 511 507 3951 511 1576 502 513 505 1586 502 515 503 1582 506 515 503 513 505 516 502 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8837 4464 538 610 531 619 532 613 538 1748 534 1750 532 611 540 613 538 609 532 615 536 614 537 608 533 1753 539 1745 537 606 535 618 533 614 537 609 532 619 532 613 538 611 540 609 532 611 540 1748 534 1749 533 1750 532 1754 538 1743 539 1747 535 1750 532 1747 535 618 533 613 538 44105 8864 2224 537 93399 8912 2202 538 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 492 4975 496 4993 498 498 500 4056 498 504 494 4055 499 4963 497 532 496 4031 492 4996 495 502 496 4060 494 4972 499 4990 491 506 492 4063 491 511 497 4052 492 4970 490 539 500 4027 496 103358 500 4961 500 4995 496 505 493 4056 498 499 499 4057 497 4969 491 533 496 4026 497 4997 494 508 490 4059 495 4967 494 5001 490 511 497 4053 491 505 493 4063 491 4975 496 528 490 4032 491 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 2357 610 1183 592 1191 614 1179 595 1188 618 584 613 1180 593 588 613 1190 612 590 605 587 613 589 605 587 25398 2355 623 1180 591 1181 627 1186 589 1183 618 584 616 1187 587 584 614 1189 615 587 605 587 615 587 609 593 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3425 3482 848 2607 846 2639 845 2608 846 2639 845 2612 852 2624 850 2613 851 911 851 885 847 919 843 891 851 915 847 890 852 907 845 897 845 917 845 891 851 915 847 887 845 2639 845 2612 852 2625 849 2613 851 2630 844 34282 3455 3478 852 2601 852 2633 851 2605 848 2629 845 2617 847 2633 851 2604 849 917 845 890 852 913 849 889 843 915 847 896 846 916 846 890 852 914 848 885 847 919 843 895 847 2630 844 2617 847 2634 850 2605 848 2636 848 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 500 500 498 4066 498 5058 504 502 506 4061 503 508 500 4060 504 5055 497 5055 497 511 497 4071 503 5047 505 507 501 4065 499 5049 503 512 496 124314 501 508 500 4067 507 5043 499 513 505 4060 504 501 497 4073 501 5052 500 5052 500 512 496 4066 498 5058 504 506 502 4058 506 5053 499 509 499 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 176 7763 174 8817 169 10842 544 1955 541 967 540 1952 544 959 538 972 546 962 545 1947 538 1952 544 967 540 1955 541 964 543 1947 539 972 546 1949 547 7873 170 7746 170 10350 175 11811 543 960 537 1961 535 972 535 970 537 965 542 1956 540 1956 540 965 542 1947 539 973 534 1960 536 969 538 1952 534 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 2570 2682 1189 1208 1186 2665 1186 1217 1187 2668 1183 1215 1179 2671 1190 2695 1186 1187 1187 2692 1179 2671 1190 1213 1181 1193 1191 1207 1187 2663 1188 1215 1179 2676 1185 46941 2563 2685 1186 1191 1183 2698 1184 1188 1186 2691 1180 1197 1187 2694 1187 2666 1185 1210 1184 2674 1187 2695 1186 1185 1189 1206 1188 1189 1185 2696 1186 1186 1187 2689 1182 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 873 916 1773 906 877 925 878 919 874 928 875 925 878 1806 1770 914 879 1809 870 928 1772 88684 870 926 1774 908 875 926 877 917 876 929 874 925 878 1809 1777 905 877 1808 871 930 1770 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 874 906 877 912 1767 904 879 908 874 918 875 915 878 907 875 920 873 1795 874 914 1775 897 875 88704 879 914 868 922 1767 897 875 919 874 915 878 911 872 920 873 914 879 1792 877 914 1775 889 873 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3325 1560 406 444 401 453 402 1226 404 449 406 1226 404 443 402 454 401 449 406 1224 406 1228 402 446 409 444 401 451 404 1223 407 1230 410 440 405 445 410 443 402 446 409 444 401 451 404 442 403 453 402 448 407 443 402 451 404 1224 406 448 407 444 401 445 410 446 409 1221 409 442 403 1231 409 439 406 1227 403 450 405 441 404 452 403 1227 403 448 407 446 409 439 406 447 408 444 401 445 400 456 410 440 405 52348 3320 1553 403 445 400 454 401 1230 400 447 408 1228 402 449 406 444 401 452 403 1225 405 1229 431 421 404 442 403 454 401 1229 401 1229 431 423 402 446 399 455 400 451 404 442 403 453 402 448 407 443 402 451 404 444 401 452 403 1229 401 445 400 457 398 451 404 446 399 1235 405 443 402 1232 408 444 401 1225 405 452 403 447 398 452 403 1231 399 449 406 447 408 443 402 444 401 456 399 450 405 445 400 453 402 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 990 915 980 909 986 924 981 2829 981 934 981 2823 987 923 982 2828 982 933 982 906 989 33895 989 907 988 927 988 900 985 2841 979 915 980 2851 980 909 986 2840 980 914 981 934 981 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 821 5754 848 2490 841 2492 819 2524 817 5726 845 2492 839 5727 844 5757 845 5727 854 2483 848 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 5092 1621 406 2599 406 2598 407 2605 1653 1616 411 2619 1609 1606 1654 1618 409 2623 382 2600 405 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8879 4446 566 1747 565 584 567 1744 568 581 570 1744 568 574 567 586 565 582 569 578 563 1753 570 575 566 1749 563 585 566 1743 569 1749 563 1748 564 582 569 1747 565 580 571 578 573 1741 571 572 569 584 567 580 571 1741 571 578 573 1738 564 1751 572 577 564 1744 568 1750 572 1740 572 41007 8906 2257 565 93855 8872 2265 567 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 1144 1010 6795 26754 1151 997 6798 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 1144 1009 1120 1006 1143 1991 1116 26758 1146 1006 1123 1003 1146 1988 1119 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 170 46238 169 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8906 4165 572 1672 569 1678 563 640 572 637 565 643 569 633 569 643 569 1674 567 639 563 1684 567 637 565 1681 570 1675 566 1673 568 1681 570 636 566 640 572 637 565 639 563 1684 567 640 572 630 572 640 572 634 568 1675 566 1681 570 1670 571 638 564 1681 570 1669 572 1678 563 1680 571 40485 8898 2252 570 85621 8955 2194 567 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 446 1191 449 1194 446 1195 445 1204 446 1200 1316 459 447 1193 447 1202 448 1197 443 1201 449 1191 449 34491 443 1204 446 1197 443 1198 442 1207 443 1202 1314 436 440 1201 449 1199 441 1205 445 1198 442 1199 441 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 4268 4327 522 1593 516 1603 516 1597 522 519 520 520 519 515 514 531 518 520 519 519 520 521 518 519 520 1597 522 1595 514 1597 522 1599 520 1595 514 524 515 1604 515 521 518 523 516 524 515 519 520 525 514 524 515 1599 520 522 517 1596 513 1605 514 1602 517 1595 514 1607 522 1592 516 40481 8748 2187 523 93986 8721 2189 521 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 247 3006 244 153 178 1007 251 117 637 597 381 678 218 156 175 529 382 679 217 157 174 24963 247 3038 252 117 219 994 249 119 217 483 250 117 173 531 278 779 173 167 169 569 383 679 217 156 175 126538 246 3039 251 118 218 995 247 120 195 504 249 118 172 532 277 780 172 168 178 560 382 680 216 157 174 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8872 4454 568 579 572 578 573 572 569 581 570 1744 568 575 566 587 564 582 569 1743 569 1747 565 1745 567 1748 564 584 567 1741 571 1747 565 1747 565 1747 565 1751 571 1739 563 1752 570 578 563 1745 567 1751 572 1741 571 575 566 585 566 578 573 577 564 1750 573 571 570 583 568 579 572 41007 8905 2258 574 93846 8871 2266 566 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 4278 4318 521 517 522 520 519 517 522 520 519 521 518 516 513 531 518 520 519 1595 514 1605 514 1599 520 1598 521 1595 513 1598 521 1600 519 1596 513 1602 517 1601 518 1595 514 1604 515 525 514 520 519 526 513 525 514 524 515 527 522 513 516 526 513 1603 516 1595 514 1607 522 1593 516 40481 8749 2186 524 93985 8722 2189 521 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 2746 8423 2744 19607 2746 19601 2742 8431 2745 8424 2742 19608 2745 8419 2747 19608 2745 19607 2746 8422 2744 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 170 6683 174 4889 175 10382 372 849 821 2498 176 10378 264 2479 842 2492 839 2475 846 2483 838 2481 840 2495 836 2477 844 845 846 37009 172 6681 176 4888 175 10381 373 848 924 2395 844 2490 174 10383 248 2492 172 10386 245 2495 169 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8898 4453 569 578 573 577 574 1737 565 584 567 582 569 574 567 1751 572 1741 571 1741 571 1744 568 576 575 1741 571 1743 569 1739 573 579 572 575 566 581 570 580 571 574 567 1748 575 1739 573 570 571 582 569 578 573 1739 573 1742 570 1740 572 577 574 575 566 1742 570 1749 574 1738 574 41006 8875 2262 570 93850 8907 2256 566 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8872 4454 568 1745 567 1749 563 1746 566 584 567 1747 565 1743 569 583 568 579 572 575 566 584 567 578 563 1752 571 578 563 580 571 1747 565 1747 565 581 570 1746 566 578 573 577 564 1751 572 571 570 583 568 579 572 1740 572 578 563 1747 565 1750 573 576 565 1743 569 1749 563 1749 563 41017 8906 2257 565 93855 8872 2265 567 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 179 2644 178 8174 170 2646 176 8181 173 2648 174 8177 177 2640 172 5419 174 5413 170 2649 173 2644 178 2646 176 2646 176 5409 174 28831 174 2651 171 8183 171 2648 174 8174 170 2655 177 8177 177 2642 170 5412 171 5420 173 2649 173 2646 176 2640 172 2653 169 5419 174 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 174 2648 174 8178 176 2640 171 8185 169 2653 169 8182 172 2645 177 2647 557 2264 558 5027 174 5410 173 5418 175 5413 170 28837 168 2649 173 8184 170 2652 170 8181 173 2643 169 8188 176 2646 176 2643 168 2648 174 5417 176 5411 172 5413 170 5413 170 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 178 706 266 139 268 137 173 173 198 234 178 126768 175 299 169 86 275 130 267 138 172 230 177 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 554 1922 584 3809 582 3782 578 3821 580 1920 555 1941 544 1922 574 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 476 1470 465 3479 474 3469 474 3477 475 1467 468 1471 474 27473 472 1474 472 3475 467 3478 475 3468 474 1471 475 1468 467 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 301 2188 236 2169 235 2205 230 1009 234 1070 183 1064 179 2198 206 1046 207 1069 173 2207 207 1042 211 1062 201 1043 210 1032 231 1005 237 1039 234 1006 236 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8872 4453 570 1744 568 582 569 1741 571 1744 568 580 571 1738 564 1754 569 578 563 584 567 1749 563 581 570 580 571 1743 569 573 568 585 566 1746 566 580 571 580 571 1739 563 586 565 1749 563 579 572 582 569 577 564 1748 564 1752 571 574 567 1748 564 584 567 1742 570 1748 564 1747 565 41015 8898 2265 567 93853 8875 2262 570 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8904 4446 576 572 569 581 570 575 566 584 567 582 569 1739 573 579 572 575 566 1746 566 1750 572 1738 574 1741 571 1742 570 573 568 1750 572 1740 572 1740 572 578 573 572 569 581 570 1744 568 574 567 586 575 572 569 578 573 1742 570 1740 572 1743 569 579 572 1737 565 1753 570 1743 569 41010 8881 2257 565 93855 8903 2259 573 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8878 4448 564 584 567 583 568 577 564 586 565 584 567 1741 571 582 569 1743 569 1743 569 1746 566 1744 568 1747 565 1749 563 579 572 1747 565 581 570 1743 569 1746 566 578 573 1743 569 579 572 571 570 583 568 579 572 575 566 584 567 1743 569 581 570 1744 568 1740 572 1746 566 1746 566 41013 8898 2264 568 93853 8872 2265 567 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8900 4450 572 575 566 585 566 578 573 1743 569 579 572 1736 566 587 574 572 569 1743 569 1747 565 1745 567 582 569 1745 567 575 566 1753 570 1742 570 1742 570 1746 566 578 573 1742 570 578 573 570 571 582 569 578 573 574 567 583 568 1742 570 579 572 1742 570 1738 574 1744 568 1744 568 41012 8871 2266 566 93855 8903 2258 574 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8872 4454 568 1745 567 1749 563 1747 565 1750 562 1752 571 1737 565 1754 568 1743 569 578 563 587 564 581 570 580 571 577 564 579 572 581 570 576 565 1748 564 1751 572 1739 563 1752 571 1743 569 1739 563 590 571 575 566 581 570 580 571 574 567 583 568 580 571 572 569 1750 562 1749 563 41017 8905 2257 575 93846 8871 2266 566 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 176 7764 173 11361 544 1951 545 1948 176 8815 171 2319 177 2322 174 2321 175 2318 178 2313 173 18281 170 7746 170 11369 536 1954 542 1957 539 969 538 1954 542 1949 536 1962 534 1960 174 2320 176 2315 170 2328 178 2317 168 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 172 23035 176 2267 178 2285 170 2270 175 2288 177 11602 222 2218 216 2246 173 183 173 935 221 218 174 864 221 1250 171 1310 223 2219 216 2247 218 1244 223 216 176 869 170 1296 217 2239 216 1254 223 216 176 866 219 9127 169 7642 173 2284 171 2273 172 2290 175 2263 171 10143 178 10623 222 1245 222 217 175 1843 220 2226 219 1264 223 1237 174 183 178 952 219 2222 223 216 176 866 219 1249 172 9190 173 7637 178 2266 169 2286 169 2280 175 2284 171 10125 175 10622 224 215 177 868 216 2228 217 2238 171 1299 224 215 177 865 174 183 173 934 222 216 176 1848 215 1247 220 1265 222 762 170 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 7410 1482 382 2742 375 2747 380 2751 1630 1535 400 2724 1657 1529 1629 1538 407 2720 407 2716 401 2722 4125 1549 376 2751 376 2748 379 2744 1626 1541 405 2723 1658 1530 1628 1531 404 2726 401 2726 401 2723 4124 1542 383 2747 380 2747 380 2745 1626 1534 401 2729 1652 1539 1629 1532 403 2719 408 2722 405 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 4277 4319 520 518 521 521 518 518 521 1597 522 1594 515 1597 522 1599 520 1594 515 1600 519 1600 519 1594 515 526 513 527 522 512 517 528 521 517 522 516 513 1605 514 522 517 525 514 525 514 521 518 526 513 525 514 1600 519 523 516 1597 522 1596 513 1603 516 1595 514 1607 522 1593 516 40481 8749 2186 524 93986 8721 2188 522 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 1325 431 445 1199 1317 455 441 1207 443 1202 1294 457 449 1190 440 1209 441 1205 445 1198 1318 454 442 93237 1320 434 442 1201 1325 448 448 1200 440 1205 1291 460 446 1193 447 1202 448 1198 442 1201 1325 447 449 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8876 4450 572 575 566 585 566 578 573 577 564 585 566 577 564 589 572 574 567 1745 567 1749 563 1747 565 1750 562 1751 571 1737 565 1754 569 1743 569 1743 569 1747 565 579 572 1743 569 579 572 571 570 583 568 579 572 575 566 584 567 1743 569 581 570 1744 568 1740 572 1746 566 1746 566 41013 8898 2265 567 93853 8873 2264 568 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 300 1791 297 744 295 743 296 751 298 745 294 747 302 736 293 1837 271 745 294 747 302 1793 295 752 297 1802 296 1801 297 742 297 1806 302 31592 296 1801 297 742 297 749 300 744 295 745 294 745 294 752 297 1829 269 746 293 745 294 1809 300 744 295 1802 296 1799 299 748 301 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 178 2003 177 2899 177 1996 174 2908 168 2910 177 2000 170 2004 176 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8898 4173 564 642 570 1677 564 1677 564 645 567 640 572 631 571 641 571 635 567 639 563 1683 568 1673 568 641 571 636 566 637 565 647 565 641 571 634 568 642 570 1671 570 639 563 1682 569 632 570 643 569 636 566 1677 564 1683 568 636 566 1680 571 636 566 1674 567 1682 569 1674 567 40489 8904 2246 566 85626 8902 2248 564 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8897 4454 569 578 573 1743 569 576 565 1750 572 576 575 1733 569 584 567 1745 567 1745 567 583 568 1742 570 579 572 1742 570 573 568 1750 573 574 567 580 571 580 571 573 568 582 569 580 571 572 569 584 567 1744 568 1744 568 1748 575 1735 567 1749 574 1740 572 1736 566 1752 571 576 575 41005 8878 2259 563 93858 8901 2260 572 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8880 4446 566 1747 565 585 566 578 573 577 564 1750 573 571 570 1748 564 582 569 578 573 1743 569 1741 571 1744 568 580 571 1737 565 588 563 1749 563 583 568 582 569 576 565 1750 573 576 565 578 573 580 571 576 565 1747 565 1751 571 1738 564 586 565 1749 563 1745 567 1751 572 1741 571 41008 8905 2258 574 93846 8871 2266 566 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3892 3856 525 978 529 977 530 969 528 977 530 974 523 975 532 1924 531 971 526 1924 531 975 532 1916 529 976 531 1921 524 1947 508 1925 530 1920 525 1926 529 1925 530 970 527 1927 528 976 531 1915 530 978 529 1921 1033 9201 3871 3867 524 977 530 975 522 981 526 972 525 983 524 978 529 1921 524 982 525 1923 532 973 524 1928 527 971 526 1931 524 1950 505 1922 523 1931 524 1924 531 1923 532 972 525 1922 523 985 533 1918 527 975 532 1922 1032 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8900 4451 571 576 575 1742 570 1739 573 1742 570 578 573 1736 566 1752 570 576 575 1737 575 575 566 579 572 578 573 1741 571 571 570 583 568 1745 567 579 572 578 573 1738 574 575 566 1748 575 568 573 581 570 576 575 1737 565 1751 572 573 568 1747 576 573 568 1741 571 1747 565 1747 565 41014 8878 2260 562 93858 8901 2261 571 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 875 904 1745 924 879 913 880 1786 873 919 874 917 1742 922 871 1802 877 912 1747 921 872 88714 880 907 1742 930 873 917 876 1788 871 924 879 910 1749 919 874 1798 871 916 1743 928 875 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 4758 1543 403 2731 407 2726 402 2739 1601 1514 401 2760 1580 1530 1577 1540 406 2758 400 2708 1602 1535 400 2740 408 2729 409 2726 401 2731 1599 1520 405 2758 1572 1540 1578 1532 403 2737 431 2706 1604 1535 411 2722 405 2734 403 2734 404 2731 1599 1512 403 2764 1576 1539 1578 1533 402 2730 428 2712 1608 1534 402 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8873 4453 570 578 563 1753 570 575 566 1749 563 585 566 1742 570 583 568 1744 568 1744 568 582 569 1741 571 578 573 1741 571 572 569 1749 563 583 568 1745 567 1748 564 1746 566 583 568 581 570 1738 564 589 572 1740 572 574 567 584 567 577 564 1752 571 1743 569 573 568 1751 572 575 566 41014 8900 2263 569 93851 8878 2259 563 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 171 313 176 798 337 133 600 232 170 126777 176 65 169 496 176 200 609 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 500 527 491 4033 500 4986 495 510 498 4054 500 499 499 4048 496 4974 497 530 499 4026 497 4989 492 513 495 4057 497 4967 493 4992 499 506 492 4060 494 505 493 4054 500 98563 497 529 500 4025 498 4988 493 512 496 4056 498 501 497 4050 493 4976 495 532 497 4028 495 4991 500 505 493 4059 495 4969 491 4994 497 508 490 4062 492 507 491 4056 498 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 879 901 871 1796 1770 903 869 917 876 916 877 913 880 906 877 918 875 914 879 1788 1768 1784 875 87826 871 921 872 1797 1769 897 875 919 874 915 878 910 873 920 873 914 879 913 870 1800 1776 1767 871 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 874 905 1774 894 878 914 879 908 875 917 876 915 878 907 876 919 874 1793 876 913 1777 895 878 88703 872 920 1769 901 872 913 870 925 878 910 873 916 877 916 877 910 873 1798 871 919 1770 894 878 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3339 1714 415 445 420 1283 418 440 415 448 418 444 422 435 420 446 420 440 415 445 421 443 412 445 421 443 412 449 416 1279 412 455 421 439 416 443 412 452 414 444 411 452 414 1287 414 442 413 453 413 1287 414 446 419 444 422 437 418 445 421 441 414 442 413 452 414 447 419 1281 420 443 412 1285 416 1287 414 1287 414 1282 419 447 419 441 414 1286 415 448 418 1280 421 1282 419 443 412 1283 418 448 418 1283 418 73871 3336 1694 414 444 411 1291 410 452 414 442 413 453 413 448 418 442 413 450 416 443 412 450 416 447 419 437 418 448 418 1282 419 441 414 449 417 442 413 449 417 446 420 436 419 1287 414 446 420 440 415 1288 413 445 410 453 413 449 417 439 416 450 416 445 421 439 416 447 419 1279 412 451 415 1287 414 1282 419 1287 414 1285 416 444 411 453 413 1285 416 447 419 1283 418 1278 413 453 413 1287 414 446 419 1284 417 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 4411 4332 558 1660 561 596 565 612 559 597 564 617 565 585 566 620 561 591 560 620 561 595 566 611 560 597 564 1653 558 592 559 627 565 589 562 617 564 1630 560 617 564 592 559 22591 4439 4322 558 1639 561 618 563 590 561 622 560 592 559 624 557 597 564 612 559 600 561 618 563 590 561 622 559 1628 562 621 561 594 567 609 562 597 564 1652 559 595 566 617 564 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3444 1767 413 487 419 1274 417 481 415 1277 414 488 418 1267 414 492 414 1276 415 484 412 1282 419 478 418 1275 416 1276 415 480 416 1280 421 479 417 1272 419 1275 416 1272 419 1274 417 484 412 484 412 493 413 1277 414 485 421 1273 418 479 417 486 420 1271 420 476 420 486 420 479 417 482 414 1280 411 1276 415 488 418 1273 418 478 418 488 418 481 415 1275 416 487 419 478 418 485 411 1280 421 475 421 1275 416 1273 418 69071 3439 1759 441 456 440 1253 438 463 443 1243 438 468 438 1251 440 460 446 1247 444 454 442 1251 440 461 445 1241 440 1256 445 455 441 1248 443 461 445 1242 439 1254 447 1245 446 1240 441 465 441 458 438 462 444 1249 442 456 440 1252 439 463 443 452 444 1252 439 461 445 454 442 461 445 452 444 1249 442 1250 441 454 442 1254 437 463 443 456 440 463 443 1245 446 456 440 462 444 451 445 1251 440 460 436 1253 438 1256 445 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8705 4317 583 682 581 688 585 678 585 1721 581 1722 580 681 582 690 583 682 581 1721 581 1725 587 1713 579 689 584 683 580 1718 584 1724 588 1714 588 677 586 683 580 683 580 1726 586 680 583 678 585 687 586 679 584 1718 584 1721 581 1719 583 685 588 1716 586 1713 579 1729 583 1719 583 41145 8706 2217 585 94686 8705 2217 584 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8888 4470 532 576 534 576 534 571 539 572 538 1706 535 568 532 581 529 578 532 1711 530 581 529 1711 530 1717 534 574 536 1703 538 575 535 572 538 1705 536 1711 530 1711 530 1716 535 1709 532 571 529 585 535 571 529 579 531 579 531 574 536 574 536 573 537 1701 530 1720 531 1712 529 39045 8912 2261 540 94838 8911 2235 536 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 874 915 1774 904 879 923 870 1816 1770 1800 1776 904 879 1805 874 931 1769 909 874 88698 876 927 1773 904 879 922 871 1819 1767 1796 1770 914 879 1809 870 928 1772 910 873 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 178 7753 174 2308 178 2284 171 2316 170 2298 177 10228 174 10724 223 1256 221 217 175 1868 169 2293 172 1327 216 1263 178 1316 217 2245 220 218 174 887 218 1261 170 10707 174 7752 175 2296 169 2314 171 2293 172 2307 179 10216 176 6265 174 10729 219 1258 219 1272 215 1268 173 2310 221 1255 222 217 175 877 172 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 532 1692 559 561 529 574 567 559 531 566 564 555 535 1694 557 568 532 1691 560 560 530 573 557 568 532 565 565 555 535 567 563 1688 533 564 567 554 536 567 563 562 538 558 562 558 532 1697 565 561 539 1683 558 1689 532 1697 564 1687 534 1689 562 1684 537 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 874 915 1774 904 879 923 870 927 876 1814 876 925 1775 900 872 1821 879 920 1769 909 874 88702 871 926 1774 907 876 925 878 917 876 1817 873 927 1773 905 878 1813 876 921 1769 912 871 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8884 4474 538 569 531 1716 535 570 530 580 530 1715 536 567 533 580 530 577 533 1710 531 1715 536 1705 536 1710 531 1714 537 1702 529 1720 531 1712 529 578 532 1715 536 1705 536 1710 531 577 533 570 530 584 536 571 529 1714 537 573 537 568 532 578 532 1713 538 1701 530 1719 532 1711 530 39044 8913 2260 531 94848 8907 2239 532 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 446 1694 455 1706 453 1705 424 628 452 597 452 622 458 1673 456 625 455 594 455 1706 454 590 459 621 459 591 458 616 453 591 458 622 539 22750 459 1675 454 1733 427 1712 427 622 458 588 451 622 458 1681 458 618 451 595 454 1705 455 598 451 625 454 592 457 616 453 598 451 626 535 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 3429 3445 875 2555 868 875 867 871 871 2560 873 868 874 2551 872 874 868 871 871 869 873 870 872 865 867 2565 868 873 869 2556 867 2567 866 874 868 2560 873 870 872 2555 868 2564 869 2586 847 2577 846 2589 844 870 872 32983 3470 3430 869 2559 874 868 874 867 875 2550 873 873 869 2559 874 865 867 877 875 862 870 873 869 872 870 2555 868 877 875 2554 869 2559 874 869 873 2554 869 873 869 2562 871 2553 870 2590 843 2586 847 2581 842 876 866 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8873 4452 571 1743 569 580 571 574 567 583 568 1746 566 1742 570 1748 564 582 569 578 563 1753 570 1740 572 1743 569 579 572 571 570 583 568 1744 568 579 572 578 573 572 569 1746 566 582 569 574 567 586 565 582 569 1743 569 1746 566 1744 568 581 570 1745 567 1741 571 1747 565 1746 566 41014 8898 2264 568 93853 8874 2263 569 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8902 4448 574 1739 573 1742 570 575 566 584 567 581 570 573 568 585 566 1746 566 580 571 580 571 1739 573 1742 570 1743 569 1739 573 1745 567 579 572 1741 571 1744 568 1742 570 1745 567 1747 565 1743 569 1749 573 1739 573 573 568 583 568 576 575 575 566 583 568 575 566 587 574 572 569 41011 8871 2266 566 93854 8902 2260 572 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 4275 4320 519 520 519 523 516 520 519 1599 520 520 519 515 524 521 518 520 519 1595 524 1595 524 1588 520 521 518 1599 520 1591 517 1603 516 1599 520 1595 524 1594 525 1588 521 1597 522 518 521 513 526 519 520 518 521 517 522 520 519 517 522 519 520 1597 522 1589 519 1601 518 1597 522 40475 8724 2186 514 93995 8752 2183 516 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 10380 4892 599 620 592 605 597 620 592 604 598 623 589 2081 598 627 595 2080 599 2101 598 2105 574 2099 590 2088 591 2111 599 591 590 2116 594 599 593 626 596 2082 597 619 593 2086 593 626 596 594 598 627 595 598 594 2106 593 604 598 2100 589 607 595 2107 593 2079 590 2116 594 2082 750 41149 8722 2109 590 94682 8727 2104 596 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 8879 4446 566 1747 565 1751 571 573 568 582 569 580 571 571 570 584 567 1744 568 579 572 578 573 1737 565 1751 572 1742 570 1738 564 1754 568 578 573 574 567 584 567 577 564 1752 571 577 564 580 571 582 569 577 564 1748 564 1752 571 1739 563 587 564 1750 572 1736 566 1752 571 1742 570 41009 8903 2260 572 93848 8880 2257 565 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 4271 4324 515 1600 519 1600 519 1594 515 1603 516 1601 518 1593 516 1605 514 1601 518 520 519 522 517 520 519 522 517 523 516 518 521 523 516 522 517 1598 521 1597 522 1591 518 1600 519 521 518 516 523 522 517 521 518 520 519 523 516 520 519 522 517 1599 520 1591 518 1603 516 1599 520 40477 8753 2183 516 93993 8724 2185 515 # -name: POWER +name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 7847 3931 470 1448 467 495 472 1443 472 490 467 1451 464 491 466 499 468 491 466 4413 467 1455 470 486 471 1449 466 495 472 1441 464 501 466 492 465 494 463 22093 7851 3934 467 1454 471 490 467 1446 469 496 471 1446 469 489 468 494 473 484 473 4410 470 1449 466 489 468 1455 470 489 468 1449 466 496 471 486 471 490 467 +# +name: Power +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 0C 00 00 00 +# +name: Mute +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 0D 00 00 00 +# +name: Vol_up +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 10 00 00 00 +# +name: Vol_dn +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 11 00 00 00 +# +name: Ch_next +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 20 00 00 00 +# +name: Ch_prev +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 21 00 00 00 diff --git a/assets/unit_tests/infrared/test_kaseikyo.irtest b/assets/unit_tests/infrared/test_kaseikyo.irtest new file mode 100644 index 000000000..d0142fecd --- /dev/null +++ b/assets/unit_tests/infrared/test_kaseikyo.irtest @@ -0,0 +1,105 @@ +Filetype: IR tests file +Version: 1 +# +name: decoder_input1 +type: raw +data: 1000000 3363 1685 407 436 411 432 415 1240 434 410 437 1245 439 404 433 1249 435 408 439 431 406 1249 435 435 412 405 442 1241 433 1249 435 408 439 405 442 428 409 434 413 430 407 411 436 433 414 429 408 1248 436 407 440 1243 441 428 409 434 413 431 406 1249 435 1248 436 406 441 1242 442 1240 434 409 438 431 416 428 409 408 439 430 407 411 436 407 440 429 408 436 411 432 415 402 435 1247 437 1245 439 1243 441 1238 436 +# +name: decoder_expected1 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 1B 00 00 00 +repeat: false +# +name: decoder_input2 +type: raw +data: 1000000 3365 1683 409 434 413 431 406 1276 408 435 412 1270 414 429 408 1248 436 434 413 430 407 1275 409 434 413 431 406 1276 408 1248 436 433 414 430 407 437 410 433 414 429 408 436 411 432 415 428 409 1246 438 432 415 1267 407 437 410 433 414 429 408 436 411 432 415 1266 408 1250 434 1248 436 432 415 429 408 435 412 432 415 428 409 434 413 430 407 437 410 433 414 429 408 436 411 432 415 428 409 435 412 1240 434 +# +name: decoder_expected2 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 1C 00 00 00 +repeat: false +# +name: decoder_input3 +type: raw +data: 1000000 3361 1661 442 427 410 434 413 1243 441 428 409 1247 437 432 415 1241 433 410 437 407 440 1242 432 437 410 407 440 1242 442 1241 433 436 411 407 440 430 407 436 411 406 441 402 435 435 412 431 416 1240 434 410 437 1245 439 404 433 411 436 407 440 403 434 436 411 432 415 429 408 1249 435 1247 437 1245 439 430 407 1250 434 434 413 404 433 438 409 434 413 1243 441 1241 433 410 437 1245 439 430 407 1250 434 432 415 +# +name: decoder_expected3 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 70 01 00 00 +repeat: false +# +name: decoder_input4 +type: raw +data: 1000000 3365 1656 436 406 441 402 435 1248 436 406 441 1242 432 410 437 1246 438 404 433 410 437 1246 438 404 433 437 410 1245 491 1190 442 401 436 435 412 431 416 427 410 433 414 429 408 435 412 431 416 1240 434 435 412 1244 440 1241 433 436 411 433 414 402 435 409 438 405 442 402 435 1247 437 1244 440 1241 433 437 410 1245 439 430 407 410 437 406 441 402 435 409 438 1243 441 402 435 1247 437 406 441 1240 434 433 414 +# +name: decoder_expected4 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 70 01 00 00 +repeat: false +# +name: decoder_input5 +type: raw +data: 1000000 3357 1665 438 431 416 428 409 1247 437 432 415 1241 433 436 411 1245 439 430 407 436 411 1245 439 430 407 437 410 1246 438 1243 441 428 409 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 427 410 1247 437 1245 439 430 407 437 410 1246 438 1244 440 429 408 1250 434 1248 488 355 440 429 408 436 411 432 415 428 408 435 412 431 416 428 409 1247 437 432 415 428 409 1248 436 1246 490 1191 441 1240 434 +# +name: decoder_expected5 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 1B 00 00 00 +repeat: false +# +name: decoder_input6 +type: raw +data: 1000000 3358 1664 439 430 407 437 410 1245 439 430 407 1250 434 434 413 1243 441 428 409 435 412 1244 440 428 409 435 412 1244 440 1242 432 437 410 434 413 430 407 436 411 432 415 428 409 435 412 431 416 1240 434 435 412 1244 440 1242 442 427 410 434 413 1243 441 427 409 1247 437 433 414 429 408 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 1240 486 357 438 432 415 1240 434 436 411 432 415 425 412 +# +name: decoder_expected6 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 05 00 00 00 +repeat: false +# +name: encoder_decoder_input1 +type: parsed_array +count: 4 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 1B 00 00 00 +repeat: false +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 70 01 00 00 +repeat: false +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 05 00 00 00 +repeat: false +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 1B 00 00 00 +repeat: false +# diff --git a/assets/unit_tests/subghz/ansonic.sub b/assets/unit_tests/subghz/ansonic.sub new file mode 100644 index 000000000..a7219b12c --- /dev/null +++ b/assets/unit_tests/subghz/ansonic.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Ansonic +Bit: 12 +Key: 00 00 00 00 00 00 05 5A diff --git a/assets/unit_tests/subghz/ansonic_raw.sub b/assets/unit_tests/subghz/ansonic_raw.sub new file mode 100644 index 000000000..6d4c78ebe --- /dev/null +++ b/assets/unit_tests/subghz/ansonic_raw.sub @@ -0,0 +1,11 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 57 -144 401 -86 173 -202 143 -258 133 -88 257 -144 287 -58 402 -56 259 -230 259 -86 85 -96 95 -174 286 -162 57 -230 253 -400 229 -88 536 -58 85 -72 167 -110 263 -72 229 -58 85 -86 87 -262 119 -288 163 -210 321 -320 186 -140 261 -96 143 -456 117 -216 143 -246 239 -102 121 -72 71 -191 167 -263 191 -96 239 -80 57 -116 143 -118 167 -168 215 -288 191 -106 287 -114 517 -88 113 -394 173 -70 215 -100 661 -86 201 -114 259 -58 287 -86 57 -202 399 -200 57 -288 229 -144 115 -1425 83 -142 173 -86 459 -112 223 -144 201 -116 143 -114 196 -17422 287 -614 1075 -1132 533 -560 1125 -1112 561 -530 1133 -1088 585 -512 1129 -1104 563 -1102 571 -1072 591 -524 1137 -538 1115 -19392 589 -522 1139 -1090 569 -520 1137 -1100 587 -520 1135 -1092 569 -514 1155 -1076 599 -1062 585 -1086 585 -532 1125 -528 1145 -19396 581 -504 1155 -1064 619 -506 1145 -1080 595 -508 1149 -1062 587 -536 1129 -1088 585 -1090 589 -1062 587 -510 1177 -506 1149 -19394 587 -524 1147 -1076 597 -512 1129 -1072 617 -498 1159 -1066 599 -514 1147 -1076 595 -1060 613 -1060 589 -506 1179 -510 1155 -19378 605 -514 1129 -1098 601 -488 1177 -1062 583 -510 1161 -1072 587 -516 1169 -1058 587 -1082 589 -1076 593 -532 1121 -536 1127 -19444 549 -542 1129 -1092 601 -510 1147 -1098 581 -506 1123 -1100 585 -510 1147 -1106 591 -1060 597 -1060 595 -510 1151 -534 1157 -19390 593 -512 1163 -1064 597 -510 1173 -1042 617 -510 1159 -1056 579 -556 1133 -1054 609 -1060 607 -1078 585 -528 1147 -516 1135 -19444 559 -534 1165 -1052 605 -510 1149 -1062 599 -520 1173 -1054 591 -540 1111 -1116 573 -1088 593 -1042 615 -534 1117 -536 1129 -19436 569 -530 1157 -1052 605 -504 1177 -1062 591 -536 1113 -1114 589 -514 1125 -1112 563 -1084 587 -1080 589 -536 1123 -532 1165 -19392 599 -524 1143 -1080 595 -540 1125 -1090 563 -534 1149 -1084 567 -534 1147 -1092 587 -1086 585 -1064 565 -558 1123 -532 1143 -19450 549 -556 1121 -1086 585 -534 1137 -1094 583 -516 1133 -1114 563 -536 1123 -1108 573 -1082 597 -1078 565 -532 1143 -524 1135 -19464 561 -528 1149 -1066 613 -508 1151 -1076 587 -532 1125 -1076 609 -506 1175 -1076 563 -1106 563 -1084 591 -534 1157 -484 1179 -19414 589 -528 1131 -1096 587 -520 1163 -1080 563 -550 1121 -1098 555 -562 1117 -1082 575 -1112 563 -1104 559 -558 1121 -530 1149 -19442 591 -498 1175 -1066 585 -534 1121 -1114 565 -540 1115 -1096 585 -538 1123 -1110 559 -1086 585 -1084 589 -530 1125 -558 1121 -19454 563 -542 1163 -1044 585 -558 1131 -1092 571 -536 1133 -1088 587 -518 1149 -1086 601 -1058 587 -1080 611 -510 +RAW_Data: 1125 -536 1145 -19444 567 -542 1151 -1086 581 -534 1133 -1084 583 -530 1141 -1090 587 -516 1121 -1114 591 -1062 595 -1092 567 -538 1131 -536 1145 -19448 589 -516 1143 -1076 591 -540 1115 -1090 591 -538 1115 -1096 597 -512 1155 -1078 581 -1082 585 -1104 559 -530 1153 -516 1147 -19438 589 -526 1157 -1056 609 -534 1137 -1078 585 -532 1149 -1076 585 -536 1123 -1104 587 -1062 597 -1082 587 -536 1121 -554 1121 -19464 555 -560 1123 -1104 563 -542 1131 -1096 565 -536 1143 -1060 621 -492 1175 -1078 571 -1100 577 -1092 591 -494 1167 -538 1117 -19452 593 -538 1133 -1074 603 -510 1157 -1062 595 -548 1115 -1102 573 -538 1139 -1094 567 -1094 583 -1092 567 -536 1127 -560 1113 -19478 563 -534 1151 -1084 569 -534 1135 -1116 565 -534 1135 -1084 583 -532 1155 -1060 587 -1084 589 -1084 581 -536 1125 -546 1141 -19460 577 -536 1123 -1102 565 -554 1119 -1096 593 -520 1133 -1108 563 -546 1131 -1088 569 -1094 573 -1100 583 -526 1147 -540 1123 -19466 587 -530 1129 -1100 587 -510 1143 -1084 609 -510 1131 -1110 567 -542 1121 -1102 567 -1110 563 -1106 567 -532 1149 -518 1145 -19458 573 -534 1147 -1082 575 -556 1149 -1048 589 -532 1157 -1088 565 -536 1129 -1088 589 -1106 563 -1086 587 -536 1129 -548 1147 -4298 103 -56 87 -344 87 -258 85 -278 215 -70 77 -344 279 -56 87 -56 197 -198 143 -288 362 -86 605 -144 57 -268 95 -114 143 -144 173 -144 143 -402 57 -202 259 -391 652 -340 427 -230 173 -356 97 -144 111 -246 219 -96 191 -114 173 -288 115 -56 109 -106 199 -106 73 -130 57 -172 85 -260 373 -56 629 -200 690 -230 273 -120 85 -460 85 -314 77 -78 111 -88 401 -116 171 -312 71 -500 81 -224 229 -88 257 -370 181 -172 200 -116 535 -174 113 -294 213 -359 445 -144 258 -114 115 -202 675 -509 239 -432 373 -538 85 -58 113 -86 761 -104 113 -318 443 -70 143 -144 647 -204 111 -334 87 -114 115 -144 113 -188 177 -144 199 -260 143 -86 87 -622 57 -116 171 -58 139 -222 55 -346 315 -76 345 -114 139 -171 195 -52 53 -98 119 -144 143 -244 95 -72 95 -96 167 -302 253 -186 307 -444 287 -449 115 -172 57 -172 316 -202 85 -370 697 -116 57 -144 171 -202 259 -114 85 -144 87 -315 85 -58 201 -116 171 -272 121 -358 171 -403 113 -86 115 -202 489 -229 115 -392 95 -116 171 -140 93 -102 143 -543 245 -358 215 -120 387 -288 171 -202 221 -202 115 -748 57 -316 143 -260 143 -288 115 -316 115 -58 85 -288 143 -460 485 -96 71 -104 199 -96 199 -202 143 -86 201 -116 85 -230 211 -288 115 -605 365 -126 53 -172 +RAW_Data: 317 -144 57 -486 53 -282 115 -585 97 -72 229 -174 257 -440 225 -86 173 -518 243 -167 95 -259 137 -96 694 -58 227 -80 279 -287 71 -72 301 -72 121 -106 51 -84 57 -58 199 -260 143 -288 219 -174 113 -681 115 -172 403 -58 113 -116 113 -432 171 -202 55 -108 95 -212 113 -72 527 -166 95 -212 195 -108 603 -142 239 -296 173 -346 373 -287 53 -80 79 -72 95 -238 95 -312 167 -618 143 -288 95 -72 95 -72 141 -210 55 -258 143 -328 305 -58 87 -86 315 -116 195 -218 85 -290 285 -220 215 -189 201 -58 57 -645 119 -96 71 -144 119 -406 143 -72 191 -72 631 -268 344 -56 115 -260 315 -140 455 -518 57 -58 171 -144 488 -86 219 -232 257 -144 85 -174 171 -260 115 -56 87 -166 197 -58 83 -56 85 -288 113 -410 115 -172 163 -202 113 -58 201 -144 201 -86 143 -264 167 -212 113 -116 139 -72 181 -287 343 -430 201 -260 201 -462 143 -192 301 -230 191 -454 187 -144 315 -164 143 -477 165 -58 201 -114 143 -490 115 -86 201 -58 113 -88 85 -58 203 -198 375 -86 171 -346 95 -88 257 -170 81 -56 143 -172 335 -230 173 -202 133 -471 187 -264 215 -86 115 -198 159 -72 179 -112 195 -116 449 -216 93 -96 167 -216 71 -216 71 -166 235 -86 447 -102 101 -226 195 -213 71 -144 215 -144 215 -261 241 -136 269 -142 263 -311 215 -172 201 -144 265 -168 71 -404 259 -86 85 -230 115 -650 143 -202 749 -512 248 -316 201 -154 71 -96 95 -360 105 -56 57 -432 95 -288 95 -286 95 -96 166 -144 93 -144 167 -150 904 -162 95 -526 287 -244 95 -240 383 -120 167 -394 430 -854 95 -72 143 -194 227 -120 167 -264 405 -144 143 -72 143 -72 141 -120 187 -86 143 -164 170 -96 143 -58 143 -86 402 -166 153 -120 95 -96 69 -96 71 -359 404 -338 71 -225 93 -74 97 -54 161 -114 319 -288 113 -116 459 -202 115 -114 115 -116 143 -86 57 -56 87 -114 85 -375 113 -58 311 -240 203 -288 95 -72 119 -383 213 -384 115 -86 171 -58 53 -104 401 -58 115 -86 373 -116 143 -144 161 -216 406 -72 263 -96 215 -72 95 -94 167 -96 191 -240 95 -94 214 -120 403 -116 200 -114 57 -172 220 -120 137 -364 334 -392 115 -260 199 -116 373 -188 95 -110 143 -172 87 -114 172 -230 57 -316 201 -56 249 -485 171 -202 87 -86 85 -144 345 -86 171 -58 259 -58 295 -120 95 -120 71 -192 635 -118 167 -96 375 -72 119 -120 261 -144 167 -96 95 -96 923 -215 71 -433 71 -477 +RAW_Data: 191 -240 85 -72 637 -408 213 -510 261 -168 143 -126 79 -106 167 -72 117 -218 251 -168 119 -96 215 -182 191 -238 517 -116 201 -144 255 -154 97 -94 215 -72 95 -120 71 -288 261 -106 434 -96 606 -232 229 -432 85 -174 343 -58 329 -156 55 -116 259 -144 488 -56 307 -339 115 -202 334 -88 113 -86 57 -174 143 -144 401 -376 85 -240 267 -82 95 -216 137 -158 85 -144 143 -58 221 -308 295 -114 87 -114 301 -120 358 -517 71 -262 191 -144 57 -140 165 -407 53 -262 217 -120 238 -358 119 -357 71 -72 119 -96 428 -72 95 -72 167 -72 93 -240 335 -96 357 -240 173 -230 143 -114 87 -200 143 -232 287 -150 97 -288 71 -72 93 -288 115 -58 143 -230 109 -264 71 -72 119 -72 238 -242 97 -78 163 -86 115 -518 79 -560 205 -449 969 -144 507 -86 231 -114 345 -58 979 -110 85 -288 287 -404 229 -202 57 -274 233 -86 115 -202 632 -230 85 -312 369 -392 460 -450 75 -280 85 -202 201 -86 229 -174 143 -144 233 -528 115 -212 127 -202 287 -172 403 -172 139 -128 165 -138 261 -392 143 -480 142 -189 291 -80 53 -283 167 -140 113 -1008 191 -144 119 -120 71 -193 241 -462 201 -58 143 -344 539 -316 113 -174 85 -116 113 -250 239 -168 405 -168 239 -158 85 -144 115 -86 57 -86 341 -144 171 -202 85 -202 115 -114 719 -88 55 -318 257 -56 254 -86 171 -116 459 -174 171 -329 95 -134 85 -314 431 -306 77 -316 401 -86 173 -404 281 -1073 488 -94 217 -78 101 -98 214 -120 215 -340 403 -535 143 -564 115 -116 199 -58 85 -174 315 -58 335 -136 55 -260 143 -144 229 -460 143 -58 143 -144 171 -202 115 -374 291 -130 339 -82 143 -58 171 -58 201 -86 85 -174 1022 -56 85 -82 255 -240 103 -202 431 -278 95 -216 119 -72 71 -96 71 -559 57 -144 171 -88 113 -86 231 -414 131 -192 237 -360 95 -168 145 -168 213 -120 167 -96 143 -110 57 -86 259 -56 87 -777 295 -96 57 -86 173 -86 171 -404 143 -172 231 -200 57 -441 55 -58 173 -56 87 -86 171 -72 287 -72 119 -262 119 -144 71 -72 121 -310 71 -302 113 -54 193 -80 307 -58 257 -232 143 -56 143 -116 219 -72 695 -70 71 -460 85 -232 719 -363 57 -402 604 -230 287 -138 83 -172 259 -58 171 -174 55 -88 489 -114 143 -116 171 -116 143 -58 199 -144 145 -343 374 -186 235 -140 77 -86 143 -202 143 -144 113 -144 143 -58 732 -96 263 -264 71 -206 95 -168 215 -144 271 -80 139 -88 85 -414 75 -100 +RAW_Data: 285 -96 627 -362 53 -84 201 -374 113 -202 115 -202 421 -316 85 -58 139 -224 87 -86 229 -58 243 -178 267 -288 95 -336 171 -96 213 -288 71 -405 95 -96 95 -384 95 -72 213 -72 95 -96 95 -272 87 -1083 85 -58 113 -88 257 -116 143 -292 175 -318 95 -120 95 -144 95 -72 71 -216 368 -116 373 -172 115 -58 85 -116 143 -86 85 -144 201 -86 201 -202 257 -144 201 -174 113 -144 115 -144 257 -202 585 -364 173 -138 287 -422 431 -86 85 -96 869 -186 95 -52 115 -86 115 -58 55 -276 365 -86 85 -489 171 -140 577 -106 718 -144 391 -232 195 -82 143 -172 109 -120 167 -96 280 -216 145 -240 215 -186 163 -96 141 -172 159 -603 257 -108 629 -192 119 -80 87 -172 57 -144 286 -86 57 -230 344 -58 113 -537 75 -96 537 -86 403 -196 167 -264 119 -238 119 -120 167 -96 95 -478 95 -120 167 -216 1085 -96 358 -72 263 -72 69 -120 143 -96 71 -96 191 -362 55 -144 57 -260 113 -58 85 -174 55 -88 257 -86 231 -194 55 -58 115 -56 55 -339 55 -58 374 -172 139 -82 419 -98 119 -261 71 -72 71 -240 713 -86 143 -218 295 -72 53 -56 431 -58 317 -144 161 -144 373 -144 173 -144 57 -114 85 -116 195 -72 708 -172 115 -86 191 -96 506 -120 71 -174 85 -58 363 -114 317 -230 316 -200 87 -114 57 -230 115 -315 173 -280 694 -212 453 -256 143 -202 113 -540 352 -116 257 -116 457 -56 109 -58 143 -230 259 -144 259 -525 119 -408 247 -112 389 -72 431 -96 137 -236 97 -474 201 -298 71 -82 55 -116 55 -112 199 -174 191 -86 143 -144 115 -114 317 -86 85 -230 87 -114 259 -84 107 -130 143 -94 153 -86 135 -94 215 -72 239 -94 435 -96 263 -142 166 -334 87 -194 179 -96 115 -284 135 -56 57 -144 463 -204 143 -316 201 -58 403 -86 141 -288 85 -202 139 -397 171 -174 305 -202 85 -144 373 -253 161 -492 181 -191 95 -216 315 -191 71 -166 97 -126 337 -96 71 -96 189 -168 295 -84 197 -86 259 -345 137 -144 167 -796 115 -344 455 -72 119 -96 119 -550 209 -88 85 -86 143 -340 167 -260 143 -537 85 -226 51 -537 57 -260 315 -461 51 -84 199 -358 383 -96 143 -257 115 -86 173 -86 201 -144 143 -316 85 -86 479 -88 85 -72 71 -104 115 -116 267 -72 137 -144 143 -116 85 -86 373 -288 115 -200 87 -114 259 -114 259 -462 143 -144 171 -86 57 -58 137 -144 57 -634 343 -72 205 -86 143 -258 57 -232 113 -230 461 -58 185 -74 537 -86 +RAW_Data: 535 -142 57 -58 55 -116 115 -432 85 -172 259 -192 167 -120 117 -72 119 -240 334 -72 71 -267 285 -144 119 -374 85 -88 85 -114 143 -202 229 -58 143 -202 115 -202 171 -86 71 -144 87 -56 173 -373 143 -116 113 -462 169 -80 215 -148 115 -336 85 -230 163 -432 85 -374 639 -174 85 -58 57 -82 295 -352 269 -532 414 -322 95 -287 263 -268 115 -56 259 -76 85 -282 401 -305 516 -114 115 -202 171 -86 451 -110 85 -346 201 -274 149 -202 85 -364 366 -258 57 -114 259 -172 142 -144 85 -116 85 -480 171 -144 57 -352 115 -116 535 -404 315 -202 163 -158 517 -316 215 -98 85 -346 85 -144 87 -86 257 -82 167 -58 85 -116 113 -894 233 -186 77 -266 147 -72 71 -82 57 -86 171 -58 57 -86 201 -364 143 -202 115 -114 85 -88 113 -86 87 -230 57 -76 613 -72 85 -96 209 -346 458 -58 547 -490 201 -315 315 -116 75 -168 359 -335 95 -384 93 -120 71 -312 251 -366 233 -96 189 -240 263 -192 271 -58 115 -58 229 -346 459 -174 113 -144 173 -144 218 -224 57 -116 215 -72 103 -202 513 -210 433 -116 113 -174 650 -273 147 -450 375 -86 115 -172 536 -84 85 -230 85 -58 195 -468 287 -110 551 -214 167 -311 213 -250 85 -58 85 -355 113 -230 115 -144 117 -288 195 -202 57 -376 123 -144 236 -168 553 -284 119 -72 143 -188 161 -120 93 -312 335 -58 55 -260 105 -244 143 -120 381 -268 173 -268 635 -168 453 -318 71 -167 71 -406 191 -172 215 -408 119 -144 93 -120 97 -130 143 -192 308 -122 147 -550 313 -96 139 -162 167 -96 431 -80 83 -112 201 -86 287 -86 229 -116 57 -288 113 -174 143 -116 113 -144 115 -518 57 -230 57 -172 231 -86 113 -314 183 -144 119 -72 165 -446 81 -86 135 -190 143 -96 71 -72 411 -96 143 -120 69 -216 349 -72 95 -96 517 -646 163 -86 113 -116 171 -116 143 -116 113 -287 259 -114 517 -168 141 -116 105 -72 95 -96 311 -118 159 -310 191 -54 143 -258 115 -450 219 -54 339 -372 239 -72 167 -174 113 -58 57 -144 259 -172 143 -336 113 -174 85 -230 83 -668 85 -202 113 -144 57 -116 373 -316 719 -288 115 -58 75 -120 139 -144 229 -144 57 -144 171 -192 391 -202 403 -58 315 -188 259 -56 115 -144 85 -404 57 -58 105 -102 429 -406 81 -172 57 -144 287 -230 287 -220 317 -458 283 -58 113 -86 269 -72 281 -58 85 -202 113 -52 421 -58 229 -480 259 -58 143 -660 155 -638 123 -86 57 -86 143 -346 143 -144 57 -144 \ No newline at end of file diff --git a/assets/unit_tests/subghz/holtek.sub b/assets/unit_tests/subghz/holtek.sub index b77a759d2..8dbd3ea20 100644 --- a/assets/unit_tests/subghz/holtek.sub +++ b/assets/unit_tests/subghz/holtek.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 418000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: Holtek Bit: 40 diff --git a/assets/unit_tests/subghz/holtek_raw.sub b/assets/unit_tests/subghz/holtek_raw.sub index 8eac0a398..d63c4a00a 100644 --- a/assets/unit_tests/subghz/holtek_raw.sub +++ b/assets/unit_tests/subghz/holtek_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 418000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 2243 -98 331 -100 1129 -66 761 -100 1393 -100 165 -66 2883 -64 357 -66 4703 -68 927 -98 233 -134 461 -66 3855 -134 165 -98 1281 -100 2053 -66 3061 -98 331 -98 8981 -66 365 -66 631 -100 1027 -100 4521 -134 597 -66 3187 -66 2619 -100 3011 -98 1151 -66 953 -100 1423 -66 1755 -166 333 -98 1557 -66 761 -66 865 -66 4837 -132 357 -132 2419 -100 1023 -66 65 -66 2507 -66 131 -66 761 -66 997 -66 333 -100 2259 -68 431 -100 2523 -66 987 -100 363 -66 363 -66 1197 -68 1589 -164 951 -96 5351 -66 697 -100 163 -100 4683 -66 2265 -68 2051 -64 457 -64 3005 -132 1057 -66 2221 -100 1661 -98 695 -100 99 -66 861 -66 1957 -100 731 -132 1857 -100 3177 -98 1807 -98 463 -66 499 -134 1129 -100 3737 -100 1889 -66 263 -98 623 -66 2103 -98 3165 -66 131 -100 195 -66 691 -66 67 -132 531 -66 1857 -100 199 -68 97 -68 197 -68 697 -68 233 -100 3749 -134 1691 -68 3289 -66 3751 -68 65 -100 853 -66 531 -132 1299 -66 1585 -98 65 -98 1577 -66 785 -98 1151 -66 165 -68 397 -100 4255 -100 857 -100 1017 -66 1575 -130 1255 -234 1923 -66 199 -102 301 -66 231 -66 691 -64 227 -64 195 -66 1257 -100 2353 -100 235 -100 1163 -66 5423 -66 2049 -66 1807 -66 523 -198 693 -100 367 -100 597 -100 4013 -100 233 -166 365 -66 1827 -100 1491 -100 785 -64 885 -66 599 -134 2847 -100 667 -100 4943 -98 3319 -98 6729 -98 361 -96 391 -66 723 -132 503 -66 1583 -166 297 -234 2045 -66 1185 -134 661 -66 195 -66 291 -164 523 -98 1679 -134 233 -132 761 -394 855 -100 2003 -164 261 -66 229 -96 953 -66 3889 -66 929 -66 993 -68 3099 -132 1673 -66 1833 -100 563 -100 1131 -100 3219 -232 4411 -100 1095 -100 5315 -100 631 -198 461 -198 1907 -100 1743 -68 863 -132 4013 -64 295 -66 3883 -100 2707 -198 923 -100 2539 -166 629 -100 563 -100 3783 -68 893 -66 2987 -98 2357 -98 1665 -66 599 -66 1259 -232 165 -66 1361 -66 1645 -166 1543 -66 565 -66 401 -134 465 -100 831 -98 2405 -100 1055 -66 2109 -100 1161 -68 431 -100 265 -68 235 -66 463 -66 3453 -100 433 -66 2693 -132 263 -166 729 -134 763 -134 1327 -100 397 -234 795 -68 563 -66 1625 -98 267 -66 4835 -66 197 -66 589 -66 7575 -100 1959 -100 131 -68 297 -134 261 -98 433 -66 1427 -66 2421 -100 2925 -166 1921 -134 1645 -66 97 -132 5423 -100 2423 -98 1065 -66 1715 -132 963 -66 2403 -66 1117 -328 1981 -66 527 -100 427 -164 865 -66 2129 -232 165 -68 165 -66 131 -366 131 -100 2613 -450 diff --git a/assets/unit_tests/subghz/honeywell_wdb.sub b/assets/unit_tests/subghz/honeywell_wdb.sub index bce11cd0d..0f2316d5e 100644 --- a/assets/unit_tests/subghz/honeywell_wdb.sub +++ b/assets/unit_tests/subghz/honeywell_wdb.sub @@ -1,7 +1,7 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 868350000 -Preset: FuriHalSubGhzPreset2FSKDev476Async +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async Protocol: Honeywell Bit: 48 Key: 00 00 0E DB 70 20 00 01 diff --git a/assets/unit_tests/subghz/honeywell_wdb_raw.sub b/assets/unit_tests/subghz/honeywell_wdb_raw.sub index 0fbd6d23e..a8bbaa8fb 100644 --- a/assets/unit_tests/subghz/honeywell_wdb_raw.sub +++ b/assets/unit_tests/subghz/honeywell_wdb_raw.sub @@ -1,7 +1,7 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 868250000 -Preset: FuriHalSubGhzPreset2FSKDev476Async +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 414 -1374 51 -108 79 -216 189 -584 53 -214 53 -400 215 -264 129 -322 77 -288 79 -54 107 -184 51 -446 53 -160 161 -305 263 -104 264 -134 53 -806 267 -80 187 -314 53 -158 83 -1172 243 -833 79 -188 345 -208 77 -182 225 -100 107 -106 187 -190 105 -432 176 -174 119 -328 79 -432 79 -102 99 -514 127 -218 79 -184 77 -52 101 -356 53 -615 243 -266 181 -102 212 -138 81 -486 205 -574 77 -256 155 -152 217 -78 75 -1010 393 -346 79 -488 107 -284 121 -72 215 -174 183 -54 257 -332 179 -52 79 -238 53 -636 161 -80 227 -950 346 -160 109 -564 295 -160 299 -160 435 -487 312 -52 105 -292 77 -246 137 -498 366 -104 255 -362 97 -210 53 -555 374 -206 129 -96 303 -280 179 -172 75 -102 99 -76 353 -228 131 -252 147 -130 79 -304 103 -82 139 -52 103 -183 77 -284 75 -454 55 -188 77 -222 161 -128 107 -136 187 -270 53 -371 184 -364 103 -284 129 -52 103 -321 81 -554 261 -1048 107 -106 243 -280 103 -478 215 -530 53 -108 53 -236 203 -180 51 -78 77 -338 81 -82 53 -231 75 -124 256 -232 227 -448 131 -340 131 -266 107 -346 51 -254 75 -134 210 -182 103 -280 127 -122 305 -310 255 -528 77 -513 79 -214 209 -102 53 -80 133 -1727 237 -78 79 -242 53 -296 133 -532 53 -513 53 -54 131 -190 53 -661 129 -218 107 -394 103 -554 157 -112 343 -314 103 -283 79 -304 135 -56 111 -272 189 -370 271 -270 105 -956 79 -184 77 -868 307 -156 129 -731 51 -200 161 -84 81 -326 51 -54 157 -168 369 -152 101 -188 81 -398 239 -132 215 -54 105 -182 317 -206 99 -198 97 -274 157 -271 121 -268 200 -330 499 -184 55 -434 133 -646 283 -152 255 -428 101 -350 199 -124 145 -304 308 -102 75 -386 107 -186 129 -534 101 -180 175 -100 151 -98 53 -78 133 -794 183 -150 83 -220 185 -280 83 -537 107 -308 55 -138 79 -714 79 -538 77 -106 133 -242 263 -236 75 -884 215 -136 81 -270 133 -624 105 -132 79 -848 291 -373 79 -244 55 -619 203 -202 77 -156 103 -176 99 -408 107 -318 77 -316 79 -500 55 -691 51 -340 129 -266 53 -486 103 -376 103 -185 51 -156 81 -106 77 -104 79 -502 208 -292 133 -432 105 -52 105 -80 51 -158 317 -372 181 -844 51 -270 107 -312 79 -302 83 -444 53 -640 77 -268 183 -138 85 -192 79 -158 131 -132 155 -220 81 -245 127 -386 185 -296 53 -608 77 -308 51 -822 105 -832 51 -850 155 -242 105 -422 79 -270 155 -76 77 -162 55 -414 RAW_Data: 109 -630 77 -190 53 -52 131 -860 53 -1300 83 -1122 99 -432 53 -724 81 -390 159 -106 81 -2607 79 -474 75 -694 151 -146 73 -104 53 -380 77 -278 77 -128 73 -148 73 -248 99 -314 75 -154 97 -2225 51 -952 183 -970 77 -1210 51 -326 103 -368 151 -252 121 -784 51 -108 187 -256 227 -242 105 -104 150 -128 77 -1287 173 -2513 77 -1144 105 -1268 111 -3964 101 -1348 81 -2718 81 -302 79 -1999 55 -1268 57 -7269 53 -424 83 -3736 105 -1552 51 -8460 57 -5224 101 -244 121 -4124 103 -10775 133 -1858 73 -20679 51 -5532 97 -1718 103 -3764 51 -6496 79 -792 53 -13269 81 -18787 57 -9140 83 -3302 53 -11039 55 -26162 79 -1713 103 -5860 53 -9589 55 -8389 81 -254 85 -16287 73 -8336 53 -6237 75 -828 55 -19389 73 -8028 79 -6284 83 -3460 77 -13504 75 -3198 75 -1280 51 -1948 51 -758 79 -22076 75 -1681 73 -21693 57 -8106 53 -838 51 -10074 51 -4760 55 -492 79 -6558 79 -22996 75 -13904 53 -5564 55 -16578 83 -25603 77 -410 75 -16694 81 -606 53 -2987 53 -3898 107 -3248 79 -5168 55 -754 81 -20662 53 -11066 51 -8624 79 -26384 79 -2214 81 -7442 79 -12488 53 -1656 71 -4508 55 -15680 57 -6669 51 -5410 71 -6411 53 -6082 79 -13772 53 -5945 55 -574 73 -11921 51 -3472 55 -2323 55 -10414 51 -16069 81 -2678 77 -10775 53 -2106 110 -11794 55 -17082 51 -6184 71 -8376 79 -7152 77 -2691 53 -6332 53 -2074 57 -6804 51 -166 53 -2254 53 -452 51 -6771 53 -42749 53 -6658 73 -2034 111 -3440 109 -626 53 -7291 85 -2546 53 -5287 51 -9902 53 -10040 53 -366 79 -13848 51 -3739 77 -47536 51 -354 77 -228 77 -712 53 -530 53 -162 55 -12640 71 -1708 51 -1034 77 -12891 79 -3334 51 -7644 79 -12676 81 -3036 53 -2038 53 -180 57 -818 55 -21459 71 -3009 51 -6572 53 -4015 105 -642 77 -23618 53 -4921 83 -3026 73 -3672 53 -12654 81 -8632 51 -9419 171 -19195 105 -13041 55 -21910 51 -1051 77 -10292 51 -12884 51 -6589 53 -8718 51 -2510 103 -15406 55 -6014 99 -966 73 -2725 53 -12715 51 -4228 55 -3192 57 -8672 51 -14740 51 -17032 75 -11111 1761 -182 399 -106 215 -270 355 -132 329 -150 177 -312 315 -170 169 -290 331 -162 173 -286 187 -312 163 -300 329 -164 311 -162 171 -316 289 -176 297 -194 311 -164 143 -324 167 -314 163 -310 161 -312 163 -300 175 -300 331 -162 173 -312 161 -312 163 -314 161 -298 189 -300 165 -312 161 -312 189 -276 173 -316 193 -310 163 -312 161 -310 163 -300 175 -296 193 -284 191 -284 187 -310 163 -300 175 -300 169 -320 177 -314 485 -464 177 -292 353 -134 diff --git a/assets/unit_tests/subghz/hormann_hsm_raw.sub b/assets/unit_tests/subghz/hormann_hsm_raw.sub index bf82d46cf..ac14f5819 100644 --- a/assets/unit_tests/subghz/hormann_hsm_raw.sub +++ b/assets/unit_tests/subghz/hormann_hsm_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 868350000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 1718, -32700, 32700, -32700, 11939, -494, 1047, -496, 1015, -516, 1041, -476, 1037, -510, 1035, -512, 1035, -484, 1047, -492, 1031, -504, 513, -1042, 499, -1020, 1041, -512, 511, -1004, 1035, -512, 507, -1042, 481, -1050, 1017, -522, 489, -1040, 1013, -514, 513, -1010, 543, -1008, 513, -1012, 1045, -522, 1003, -512, 1029, -518, 1009, -514, 1039, -476, 541, -1004, 509, -1038, 1035, -482, 539, -1008, 1033, -490, 539, -1018, 1009, -508, 1059, -482, 1041, -512, 1005, -508, 541, -1006, 513, -1008, 535, -1000, 1035, -522, 525, -1016, 515, -1006, 1021, -514, 1045, -478, 12441, -482, 1045, -512, 1005, -512, 1035, -514, 1037, -482, 1035, -520, 1031, -504, 1035, -486, 1041, -510, 501, -1018, 513, -1042, 1007, -512, 509, -1042, 1007, -506, 541, -1010, 507, -1016, 1031, -520, 513, -1010, 1021, -516, 515, -1046, 477, -1042, 513, -1012, 1051, -484, 1043, -512, 1027, -484, 1043, -514, 1007, -512, 509, -1040, 507, -1036, 1011, -526, 493, -1032, 1033, -486, 509, -1056, 1011, -514, 1007, -512, 1037, -514, 1001, -516, 507, -1034, 517, -1036, 493, -1018, 1041, -502, 525, -1016, 513, -1040, 1005, -512, 1033, -506, 12423, -478, 1039, -514, 1009, -516, 1033, -522, 1001, -522, 1039, -510, 1027, -484, 1041, -512, 1007, -510, 509, -1038, 507, -1034, 1037, -482, 507, -1042, 1037, -520, 487, -1014, 527, -1022, 1007, -512, 509, -1038, 1037, -512, 509, -1008, 507, -1044, 507, -1018, 1027, -514, 1005, -528, 1017, -514, 1041, -512, 1003, -512, 509, -1042, 507, -1010, 1035, -520, 507, -1018, 1033, -508, 511, -1038, 991, -516, 1041, -512, 1005, -512, 1035, -514, 507, -1040, 507, -1014, 519, -1014, 1041, -510, 499, -1018, 515, -1046, 1005, -514, 1011, -520, 12425, -480, 1035, -516, 1035, -492, 1031, -522, 1009, -508, 1021, -518, 1041, -512, 1007, -512, 1037, -512, 509, -1008, 519, -1016, 1029, -522, 513, -1008, 1025, -516, 515, -1044, 479, -1038, 1037, -482, 541, -1008, 1035, -520, 507, -1010, 503, -1026, 515, -1042, 1005, -512, 1037, -512, 1005, -520, 1015, -520, 1039, -510, 499, -1018, 515, -1042, 1007, -512, 507, -1040, 1011, -520, 507, -1016, 1031, -520, 1009, -540, 989, -518, 1043, -512, 511, -1006, 507, -1038, 505, -1036, 1001, -516, 509, -1050, 491, -1052, 1009, -500, 1021, -516, 12409, -524, 1015, -516, 1009, -510, 1037, -512, 1033, -506, 1039, -480, 1035, -524, 1019, -522, 1015, -500, 525, -1014, 515, -1040, 1005, -512, 509, -1042, 1011, -524, 493, -1018, 517, -1036, 1017, -514, 515, -1008, 1035, -514, 507, -1040, 505, -1034, 507, -1010, 1035, -492, 1031, -520, 1009, -540, 991, -516, 1043, -514, 511, -1010, 513, -1044, 1015, -492, 531, -1020, 1009, -528, 521, -1012, 1009, -510, 1037, -512, 1037, -482, 1033, -518, 505, -1032, 507, -1016, 537, -1000, 1025, -520, 513, -1034, 519, -1010, 1007, -512, 1039, -514, 12395, -510, 1037, -512, 1037, -482, 1045, -490, 1057, -488, 1045, -496, 1017, -516, 1041, -514, 1005, -512, 507, -1046, 481, -1044, 1035, -494, 505, -1048, 1011, -502, 527, -1018, 513, -1042, 1005, -516, 513, -1010, 1035, -520, 505, -1032, 507, -1022, 503, -1034, 1013, -504, 1023, -516, 1041, -512, 1005, -512, 1037, -514, 507, -1010, 519, -1050 diff --git a/assets/unit_tests/subghz/kia_seed_raw.sub b/assets/unit_tests/subghz/kia_seed_raw.sub index b3453536b..c78e6d34a 100644 --- a/assets/unit_tests/subghz/kia_seed_raw.sub +++ b/assets/unit_tests/subghz/kia_seed_raw.sub @@ -1,7 +1,7 @@ Filetype: Flipper SubGhz RAW File Version: 1 Frequency: 433920000 -Preset: FuriHalSubGhzPreset2FSKDev476Async +Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 51 -5136 1113 -104 127 -606 179 -126 325 -102 147 -200 205 -80 107 -190 81 -54 135 -378 181 -204 199 -104 121 -146 51 -230 75 -76 227 -102 415 -236 159 -54 159 -212 77 -312 73 -74 75 -128 155 -102 125 -358 125 -198 173 -608 75 -248 203 -558 53 -186 367 -180 151 -132 133 -138 135 -424 51 -240 53 -132 703 -80 79 -80 129 -294 77 -102 193 -270 77 -52 131 -180 51 -76 77 -76 51 -666 77 -104 101 -54 77 -164 107 -622 615 -294 133 -138 217 -180 75 -104 185 -80 107 -134 159 -398 181 -74 97 -198 73 -76 99 -102 127 -248 71 -128 211 -106 109 -104 101 -384 311 -398 51 -240 111 -84 81 -110 81 -668 105 -54 77 -158 79 -342 493 -78 153 -152 179 -122 223 -696 139 -590 81 -212 83 -322 129 -132 307 -346 129 -78 77 -232 77 -104 503 -52 187 -290 159 -108 159 -108 51 -136 53 -132 103 -566 177 -326 129 -176 177 -52 355 -308 129 -176 73 -104 151 -262 193 -168 111 -162 247 -52 131 -54 107 -84 83 -218 135 -402 107 -300 105 -220 81 -82 137 -82 163 -238 51 -192 293 -184 319 -454 81 -132 131 -132 79 -866 131 -212 163 -112 133 -106 211 -110 189 -214 51 -410 107 -454 131 -562 157 -52 367 -378 165 -110 81 -134 81 -82 75 -104 217 -638 81 -84 109 -160 81 -160 135 -54 53 -478 239 -78 209 -54 107 -52 155 -238 53 -184 105 -318 105 -370 233 -54 109 -246 217 -110 245 -110 209 -80 129 -514 79 -54 79 -314 155 -156 109 -292 79 -504 51 -644 157 -132 53 -208 53 -76 103 -76 77 -260 105 -82 133 -212 163 -136 53 -380 313 -178 193 -172 207 -82 79 -186 107 -136 53 -136 159 -156 97 -98 99 -254 179 -106 135 -214 307 -310 77 -76 123 -242 129 -52 103 -584 103 -178 203 -324 101 -572 253 -78 125 -126 129 -408 131 -52 129 -76 51 -132 79 -248 79 -414 51 -338 717 -322 79 -180 105 -294 109 -254 241 -76 251 -52 101 -152 125 -260 155 -80 77 -292 51 -226 209 -338 147 -100 51 -366 53 -348 133 -268 79 -366 55 -224 111 -248 81 -218 137 -168 79 -288 291 -132 51 -238 79 -78 103 -102 329 -102 123 -178 53 -202 51 -130 79 -138 51 -126 151 -180 99 -388 75 -126 51 -636 329 -150 99 -152 73 -122 177 -124 151 -386 297 -72 103 -130 51 -256 207 -354 75 -124 71 -148 351 -262 51 -540 77 -200 101 -132 77 -362 77 -254 103 -106 79 -296 51 -130 361 -240 51 -358 51 -284 201 -158 185 -288 183 -424 77 -130 RAW_Data: 121 -146 99 -124 327 -74 99 -148 99 -252 183 -112 161 -216 263 -232 77 -102 103 -416 77 -382 345 -160 131 -110 167 -138 79 -158 133 -54 163 -84 107 -80 107 -108 107 -136 225 -82 321 -108 79 -106 107 -106 55 -162 103 -160 133 -192 53 -84 269 -106 77 -162 53 -136 161 -82 135 -346 77 -864 107 -140 55 -250 189 -128 201 -502 157 -564 105 -52 107 -1150 53 -218 237 -76 159 -1010 129 -196 139 -244 101 -270 77 -106 133 -110 109 -254 501 -496 237 -692 107 -54 159 -134 347 -210 51 -190 55 -276 103 -184 229 -98 149 -252 123 -156 75 -150 537 -236 103 -204 101 -100 73 -368 283 -102 77 -652 229 -206 99 -150 197 -206 53 -80 131 -274 101 -186 159 -396 51 -188 81 -188 83 -350 105 -428 129 -128 79 -78 53 -104 351 -284 235 -130 79 -242 51 -186 77 -134 135 -434 131 -552 53 -136 107 -140 141 -252 53 -54 53 -82 51 -770 79 -130 103 -432 127 -208 153 -226 131 -248 101 -902 125 -206 129 -102 77 -154 77 -188 105 -322 105 -166 133 -474 77 -728 161 -190 107 -54 133 -80 79 -190 53 -162 53 -404 77 -334 105 -52 153 -728 423 -700 133 -476 53 -162 53 -542 159 -168 189 -134 163 -134 81 -106 133 -54 267 -108 163 -242 165 -56 167 -294 81 -136 185 -660 51 -304 157 -54 109 -378 133 -272 159 -304 135 -110 53 -402 105 -630 185 -78 257 -422 173 -252 247 -100 123 -330 107 -166 109 -502 127 -156 219 -76 101 -348 71 -586 149 -154 73 -74 77 -358 131 -270 81 -108 159 -516 295 -346 53 -240 157 -240 55 -562 133 -110 213 -320 103 -560 79 -286 51 -134 55 -452 79 -136 79 -80 77 -52 185 -504 79 -216 79 -242 135 -262 75 -206 125 -472 111 -564 79 -322 79 -432 217 -456 167 -272 103 -664 53 -302 127 -138 241 -184 53 -80 81 -192 395 -604 133 -136 53 -1044 105 -166 215 -426 133 -1222 101 -348 127 -124 123 -248 153 -492 177 -102 103 -180 77 -204 151 -282 101 -74 103 -76 101 -170 73 -506 101 -368 51 -124 193 -236 53 -310 77 -130 155 -366 229 -526 129 -428 83 -922 51 -106 131 -82 219 -706 79 -80 81 -162 133 -276 195 -220 51 -266 81 -54 51 -238 53 -686 77 -354 229 -164 195 -164 81 -294 81 -132 197 -238 73 -202 181 -180 149 -128 207 -192 111 -56 187 -808 77 -608 79 -898 77 -208 97 -200 73 -184 75 -78 203 -664 105 -384 103 -102 129 -238 131 -1346 137 -526 105 -390 129 -138 81 -484 103 -222 109 -268 51 -240 diff --git a/assets/unit_tests/subghz/linear.sub b/assets/unit_tests/subghz/linear.sub index 19ab5d722..d5ca8ff9b 100644 --- a/assets/unit_tests/subghz/linear.sub +++ b/assets/unit_tests/subghz/linear.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 300000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: Linear Bit: 10 diff --git a/assets/unit_tests/subghz/linear_raw.sub b/assets/unit_tests/subghz/linear_raw.sub index c143fa028..5bc6782f5 100644 --- a/assets/unit_tests/subghz/linear_raw.sub +++ b/assets/unit_tests/subghz/linear_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 300000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 361 -4326 2555 -28090 65 -302 65 -1890 131 -2062 2547 -95068 2565 -48020 67 -47036 2551 -30016 2561 -47778 67 -14884 2481 -95118 2537 -95132 2549 -48496 65 -21526 2571 -16342 97 -4142 65 -1938 2543 -95008 2551 -44030 1579 -548 1469 -588 433 -1568 469 -1540 479 -1550 1489 -538 473 -1574 1467 -538 1493 -566 443 -22130 693 -3742 321 -1702 341 -1690 1353 -628 391 -1666 1401 -584 1445 -596 391 -22192 1487 -554 1471 -550 447 -1584 453 -1568 445 -1578 1469 -550 459 -1598 1465 -540 1461 -576 445 -22164 1479 -546 1501 -544 453 -1568 447 -1580 445 -1584 1451 -560 443 -1594 1473 -548 1479 -556 441 -22160 1475 -548 1487 -554 449 -1578 451 -1586 451 -1572 1469 -552 449 -1578 1473 -558 1469 -552 449 -22142 1507 -546 1479 -548 457 -1574 447 -1580 447 -1584 1475 -554 441 -1592 1465 -546 1473 -546 483 -22132 1157 -1082 67 -166 65 -198 65 -1150 257 -1706 339 -1666 365 -1642 1409 -610 407 -1626 1417 -620 1435 -580 409 -22184 1495 -572 1463 -552 447 -1584 453 -1574 447 -1580 1473 -548 459 -1568 1501 -520 1501 -552 463 -22124 535 -7674 339 -1680 1345 -678 361 -1664 1395 -618 1411 -646 373 -22194 1511 -526 1477 -550 483 -1542 479 -1562 471 -1566 1483 -546 445 -1574 1471 -534 1497 -548 483 -169938 2539 -89172 167 -5768 2551 -93014 967 -252 1571 -5844 2503 -87590 65 -5948 2523 -64008 1605 -520 1473 -554 449 -1584 449 -1576 447 -1546 1505 -546 451 -1574 1469 -546 1475 -548 459 -22160 1525 -506 1501 -516 479 -1550 469 -1570 469 -1570 1471 -538 471 -1568 1489 -516 1501 -554 443 -22152 1507 -522 1511 -518 481 -1580 451 -1542 483 -1548 1505 -546 451 -1570 1471 -552 1473 -548 455 -22180 1505 -522 1501 -544 451 -1560 475 -1548 481 -1556 1477 -552 475 -1546 1505 -516 1517 -516 473 -22148 1515 -518 1499 -546 485 -1570 443 -1580 447 -1582 1475 -526 483 -1548 1507 -546 1479 -558 449 -22138 1495 -586 1437 -616 403 -1594 429 -1608 417 -1606 1465 -556 433 -1606 1465 -536 1499 -544 445 -22170 1401 -792 1245 -770 271 -1716 325 -1680 359 -1664 1397 -620 393 -1644 1407 -614 1433 -586 429 -22202 1477 -572 1469 -548 449 -1576 457 -1578 479 -1544 1473 -590 429 -1570 1491 -540 1509 -554 429 -22196 1481 -556 1467 -580 439 -1592 431 -1608 415 -1608 1441 -586 463 -1568 1467 -552 1489 -534 473 -22166 1485 -574 1469 -580 453 -1572 443 -1578 447 -1578 1477 -554 447 -1580 1479 -550 1481 -550 475 -22160 1525 -542 1467 -540 483 -1574 447 -1582 451 -1578 1481 -558 447 -1560 1493 -550 1505 -544 449 -22182 1435 -646 1391 -664 369 -1642 385 -1652 395 -1600 1463 -584 417 -1606 1467 -576 1443 -586 429 -148588 275 -106 847 -128 877 -100 281 -97826 2541 -92318 2505 -12474 2563 -73674 2555 -41600 2543 -76554 1601 -516 1471 -554 diff --git a/assets/unit_tests/subghz/megacode.sub b/assets/unit_tests/subghz/megacode.sub index e968fd9bd..787e84b0d 100644 --- a/assets/unit_tests/subghz/megacode.sub +++ b/assets/unit_tests/subghz/megacode.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 318000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: MegaCode Bit: 24 diff --git a/assets/unit_tests/subghz/megacode_raw.sub b/assets/unit_tests/subghz/megacode_raw.sub index c2235d8c5..8a061cdfd 100644 --- a/assets/unit_tests/subghz/megacode_raw.sub +++ b/assets/unit_tests/subghz/megacode_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 318000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: -754 361 -17246 131 -8734 65 -71908 65 -27774 65 -1230 65 -13826 99 -800 65 -634 67 -796 99 -47716 65 -18338 67 -18176 131 -7986 65 -1084 131 -2090 65 -48694 163 -40926 65 -4538 65 -10224 65 -9874 20955 -1970 1023 -4928 1007 -4946 1011 -7910 981 -1992 1021 -7898 1013 -1962 1007 -7896 1019 -4930 995 -4962 987 -1974 1029 -4924 1019 -4920 1025 -7896 1005 -1964 1027 -7894 1007 -4932 1033 -1948 1009 -7916 1009 -1982 1001 -4942 1011 -7880 1011 -2000 1003 -13820 1011 -1990 985 -4944 1021 -4932 995 -7916 1025 -1944 1025 -7892 1015 -1970 1025 -7892 1001 -4934 1031 -4926 1015 -1970 989 -4938 1007 -4954 1019 -7874 1013 -2000 985 -7896 1011 -4950 1001 -1968 1023 -7900 999 -1968 1007 -4968 973 -7936 985 -1970 1009 -13856 1013 -1968 981 -4962 1009 -4926 1005 -7924 1003 -1968 1013 -7892 1003 -1992 985 -7904 1011 -4936 1023 -4948 997 -1960 1015 -4950 987 -4970 993 -7896 1005 -1970 1009 -7928 1005 -4914 1023 -1984 1001 -7894 1011 -1970 999 -4962 1003 -7890 1007 -1992 1013 -13810 1011 -1994 987 -4938 1005 -4964 999 -7904 1013 -1974 987 -7910 1021 -1964 1015 -7894 1021 -4912 1019 -4948 1007 -1970 1017 -4916 1021 -4946 985 -7914 1003 -1972 1009 -7898 1041 -4912 1013 -1994 985 -7892 1027 -1964 1013 -4938 1003 -7904 1023 -1966 1017 -13824 1025 -1968 983 -4950 1007 -4944 999 -7900 1029 -1964 1015 -7914 981 -2000 981 -7932 979 -4936 1021 -4926 1013 -1998 979 -4946 1035 -4908 1031 -7896 999 -1964 1025 -7902 1021 -4914 1023 -1966 1015 -7908 1007 -1970 1003 -4952 981 -7910 1001 -1974 1003 -13832 1015 -1976 999 -4958 1019 -4938 985 -7900 1025 -1966 1011 -7914 1015 -1964 1009 -7906 1017 -4908 1027 -4916 1011 -2000 981 -4954 1007 -4926 1017 -7920 1011 -1958 1009 -7906 1019 -4912 1025 -1980 1009 -7900 1013 -1948 1027 -4916 1011 -7918 1005 -1976 1003 -13846 1013 -1970 1005 -4914 1041 -4916 1007 -7918 987 -2000 987 -7902 1013 -1970 1015 -7894 1007 -4936 1019 -4944 1009 -1970 1013 -4918 1019 -4954 977 -7914 1019 -1964 1007 -7906 1003 -4956 983 -1982 1027 -7896 1005 -1962 1027 -4930 1007 -7906 999 -1972 1009 -13850 1009 -1952 1027 -4944 983 -4954 1023 -7894 1007 -1952 1031 -7882 1035 -1970 1011 -7894 1007 -4936 1025 -4920 1023 -1968 1011 -4928 1009 -4920 1015 -7902 1015 -1966 1021 -7894 1011 -4948 997 -1970 1009 -7902 1027 -1978 1011 -4930 999 -7918 1007 -1966 1013 -13824 1015 -1966 1027 -4918 1027 -4912 1021 -7888 1009 -1998 989 -7918 1019 -1960 989 -7930 983 -4946 1025 -4920 1013 -1970 1015 -4950 1009 -4926 1005 -7892 1037 -1942 1011 -7926 1005 -4918 1007 -1996 985 -7928 1011 -1964 993 -4946 1025 -7880 1003 -1972 1009 -13854 1011 -1948 1019 -4956 983 -4946 1025 -7898 1003 -1966 1025 -7900 1019 -1964 1011 -7890 1021 -4912 1017 -4930 1013 -1968 1015 -4952 1009 -4932 1007 -7920 1009 -1972 979 -7924 1009 -4944 1003 -1970 1015 -7894 1017 diff --git a/assets/unit_tests/subghz/nero_radio_raw.sub b/assets/unit_tests/subghz/nero_radio_raw.sub index 265ed20ad..b1da069d1 100644 --- a/assets/unit_tests/subghz/nero_radio_raw.sub +++ b/assets/unit_tests/subghz/nero_radio_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 434420000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 1487 -32700 235 -202 229 -218 217 -176 197 -220 217 -210 191 -218 217 -210 189 -218 217 -210 187 -212 181 -230 203 -194 211 -212 213 -226 197 -188 211 -212 213 -226 197 -186 211 -212 213 -228 195 -186 211 -212 239 -172 235 -218 181 -212 193 -218 219 -208 191 -212 211 -200 205 -224 181 -212 213 -228 197 -188 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 237 -174 235 -218 789 -244 205 -384 415 -230 185 -418 419 -226 179 -436 419 -190 435 -198 415 -210 215 -406 187 -436 421 -190 213 -438 193 -404 413 -224 185 -430 411 -238 181 -432 201 -428 199 -404 207 -420 205 -426 385 -244 213 -400 411 -192 221 -400 239 -412 205 -426 205 -394 203 -412 243 -390 203 -426 417 -214 215 -402 211 -428 203 -392 205 -406 231 -396 411 -232 203 -408 207 -424 393 -232 203 -408 209 -422 395 -232 397 -244 391 -202 219 -430 389 -238 405 -208 425 -206 217 -394 389 -238 207 -388 243 -382 411 -244 383 -1234 219 -406 225 -198 255 -182 175 -204 211 -212 201 -208 227 -182 211 -214 227 -198 187 -212 211 -214 225 -198 185 -212 211 -240 171 -230 181 -212 213 -226 199 -188 211 -210 213 -228 195 -188 211 -212 213 -226 197 -186 211 -212 213 -228 195 -186 211 -212 213 -228 195 -188 209 -212 239 -172 227 -182 211 -212 227 -198 189 -212 211 -214 225 -198 187 -212 241 -182 227 -198 185 -212 211 -214 227 -196 185 -212 211 -240 173 -234 219 -182 209 -194 833 -210 213 -410 409 -232 199 -406 415 -204 215 -428 387 -244 391 -204 425 -206 215 -428 205 -382 419 -226 181 -434 195 -440 413 -192 219 -400 447 -202 217 -396 203 -418 189 -438 183 -436 195 -408 443 -194 217 -400 445 -204 217 -396 201 -430 199 -404 209 -422 205 -426 205 -412 193 -420 417 -202 223 -402 239 -408 207 -392 237 -382 223 -412 409 -226 191 -430 209 -416 395 -232 197 -404 209 -422 395 -232 397 -242 393 -202 217 -432 389 -238 407 -206 393 -238 217 -396 387 -238 209 -386 243 -388 411 -240 407 -4594 195 -408 245 -214 173 -218 211 -182 231 -204 223 -180 211 -214 227 -198 187 -212 211 -214 225 -198 185 -212 211 -214 227 -196 185 -212 211 -214 227 -196 185 -212 211 -240 173 -234 217 -182 211 -194 219 -218 207 -192 211 -212 199 -206 223 -182 211 -214 227 -198 187 -212 211 -240 171 -236 217 -182 211 -194 217 -220 207 -192 211 -212 201 -206 193 -212 241 -182 227 -200 187 -212 209 -240 173 -228 diff --git a/assets/unit_tests/subghz/nero_sketch_raw.sub b/assets/unit_tests/subghz/nero_sketch_raw.sub index 6660b5c42..4ac213366 100644 --- a/assets/unit_tests/subghz/nero_sketch_raw.sub +++ b/assets/unit_tests/subghz/nero_sketch_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 315000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: -2704 519 -368 279 -698 297 -712 259 -716 309 -688 647 -356 289 -710 615 -384 275 -692 345 -654 321 -696 619 -376 317 -658 335 -652 325 -684 655 -336 317 -698 295 -682 325 -678 639 -376 319 -662 331 -652 677 -352 631 -326 677 -326 315 -682 323 -680 317 -670 315 -702 637 -362 315 -654 679 -352 315 -658 315 -690 321 -676 647 -346 317 -660 337 -688 289 -686 655 -338 1007 -334 317 -350 301 -344 343 -350 315 -334 325 -352 313 -334 313 -346 349 -316 333 -326 351 -314 333 -348 315 -350 301 -344 343 -352 315 -302 353 -352 313 -334 313 -346 315 -350 301 -358 351 -314 333 -316 345 -350 303 -344 343 -352 313 -334 323 -350 315 -334 313 -344 349 -304 343 -340 317 -350 301 -356 315 -350 301 -346 345 -350 301 -344 343 -352 315 -302 353 -352 313 -334 313 -346 315 -350 333 -326 353 -314 333 -314 347 -350 1315 -320 681 -346 297 -680 317 -672 315 -700 311 -672 649 -354 323 -684 643 -352 313 -658 347 -654 323 -664 651 -376 317 -660 333 -652 323 -684 655 -338 317 -688 321 -676 293 -704 639 -338 353 -662 299 -684 647 -350 667 -326 677 -326 313 -676 323 -680 317 -670 315 -700 639 -362 315 -688 647 -352 313 -658 351 -662 297 -682 647 -350 349 -656 311 -688 321 -660 653 -342 1007 -334 317 -350 303 -344 343 -350 317 -332 325 -352 315 -336 311 -346 349 -316 333 -328 351 -316 303 -344 345 -350 303 -342 343 -316 351 -300 355 -350 315 -334 313 -344 315 -352 331 -328 351 -314 333 -316 345 -350 301 -344 341 -352 315 -332 323 -352 315 -334 313 -344 349 -316 333 -328 351 -316 333 -314 347 -350 301 -344 341 -352 315 -334 323 -352 315 -336 311 -346 313 -352 331 -328 351 -316 303 -344 345 -350 303 -342 343 -352 1281 -352 645 -346 333 -678 315 -672 313 -702 311 -672 643 -354 325 -676 645 -352 313 -690 315 -654 323 -704 649 -344 317 -660 335 -652 323 -684 671 -346 317 -660 335 -684 291 -682 671 -346 317 -660 335 -652 677 -352 627 -364 645 -324 351 -648 323 -682 317 -674 347 -656 643 -360 313 -676 649 -350 315 -692 307 -688 287 -690 653 -338 353 -664 297 -678 319 -684 655 -338 983 -362 315 -350 303 -342 341 -352 315 -334 323 -352 313 -334 313 -346 313 -352 333 -326 351 -316 303 -344 345 -350 303 -342 343 -316 351 -300 355 -352 313 -334 313 -346 313 -352 301 -358 351 -314 305 -344 345 -350 301 -342 343 -352 315 -302 353 -352 315 -334 313 -344 349 -304 341 -342 317 -350 301 -354 317 -350 301 -344 345 -350 315 diff --git a/assets/unit_tests/subghz/power_smart.sub b/assets/unit_tests/subghz/power_smart.sub index 445458b8c..5dd922bda 100644 --- a/assets/unit_tests/subghz/power_smart.sub +++ b/assets/unit_tests/subghz/power_smart.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 433420000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: Power Smart Bit: 64 diff --git a/assets/unit_tests/subghz/power_smart_raw.sub b/assets/unit_tests/subghz/power_smart_raw.sub index 0aafc87ca..c52d30d61 100644 --- a/assets/unit_tests/subghz/power_smart_raw.sub +++ b/assets/unit_tests/subghz/power_smart_raw.sub @@ -1,7 +1,7 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 433420000 -Preset: FuriHalSubGhzPresetOok270Async +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: -68 521674 -200 213 -258 217 -218 217 -246 193 -242 211 -466 451 -450 225 -212 211 -242 211 -238 243 -200 223 -254 419 -226 221 -432 251 -212 467 -240 203 -450 449 -246 205 -238 217 -218 217 -444 439 -478 431 -230 211 -462 247 -214 435 -454 449 -456 441 -442 471 -448 449 -246 205 -238 215 -220 217 -244 201 -254 217 -430 235 -220 461 -198 223 -442 275 -212 429 -458 245 -212 203 -228 253 -218 431 -448 451 -442 235 -218 463 -426 485 -214 209 -234 221 -254 219 -214 207 -228 253 -428 459 -418 245 -216 243 -194 241 -212 241 -212 211 -244 441 -228 215 -460 217 -244 435 -236 235 -440 439 -246 213 -208 229 -256 217 -430 445 -450 445 -236 253 -420 225 -232 429 -450 465 -448 439 -448 449 -454 453 -230 225 -214 241 -212 211 -242 211 -244 237 -442 237 -218 429 -230 221 -442 241 -246 425 -458 245 -212 203 -228 255 -218 427 -448 451 -446 235 -256 425 -462 417 -244 251 -208 201 -256 217 -218 209 -232 255 -420 451 -448 217 -242 211 -238 209 -234 221 -256 217 -218 437 -238 217 -464 203 -256 419 -226 223 -464 441 -244 213 -240 197 -254 219 -430 445 -452 479 -204 253 -420 225 -222 463 -452 449 -450 449 -452 447 -450 451 -238 215 -220 217 -244 199 -256 217 -218 207 -440 241 -254 423 -238 217 -462 197 -224 471 -448 225 -220 213 -242 211 -212 465 -446 449 -456 245 -214 437 -456 449 -232 233 -216 219 -254 207 -204 253 -218 219 -440 451 -456 237 -216 217 -254 209 -202 253 -218 219 -210 437 -244 253 -422 237 -218 423 -226 247 -430 451 -242 203 -228 253 -220 217 -442 449 -454 449 -240 217 -430 237 -218 461 -426 453 -450 465 -448 451 -440 443 -246 215 -250 201 -224 255 -218 215 -208 227 -442 239 -208 457 -238 219 -464 201 -256 425 -464 201 -256 217 -210 241 -216 451 -426 445 -486 207 -236 441 -430 479 -194 223 -256 217 -218 209 -234 253 -218 217 -438 449 -454 239 -216 217 -256 207 -202 255 -218 217 -210 439 -244 253 -424 235 -218 457 -224 229 -436 449 -230 221 -214 211 -242 211 -468 447 -450 449 -250 207 -454 239 -218 431 -446 451 -448 443 -486 447 -448 431 -240 207 -232 223 -254 219 -216 209 -230 253 -426 233 -234 433 -244 213 -432 237 -218 463 -446 221 -212 211 -242 239 -208 457 -456 451 -456 223 -212 467 -450 453 -204 253 -218 211 -240 215 -218 255 -208 201 -442 479 -452 203 -254 217 -212 241 -216 217 -256 207 -202 439 -240 245 -430 237 -218 463 -240 217 -428 465 -202 253 -218 213 -234 219 RAW_Data: -444 443 -452 455 -244 213 -440 237 -254 427 -450 429 -480 417 -454 477 -430 445 -244 217 -250 203 -224 253 -220 215 -206 221 -462 217 -212 467 -234 231 -440 239 -220 427 -446 219 -242 211 -244 237 -206 451 -458 451 -458 203 -254 427 -450 441 -246 213 -242 193 -244 211 -242 211 -212 241 -442 451 -454 245 -214 241 -198 255 -218 217 -210 225 -212 461 -246 213 -440 225 -242 435 -240 209 -456 457 -246 211 -204 229 -254 219 -428 445 -452 445 -238 253 -426 231 -232 435 -448 455 -448 439 -446 449 -456 451 -230 223 -214 241 -212 211 -242 211 -244 237 -442 237 -218 427 -232 221 -442 239 -246 427 -458 245 -212 205 -228 253 -220 427 -446 471 -442 237 -216 451 -462 411 -244 251 -216 205 -222 211 -242 211 -242 211 -480 453 -418 245 -252 207 -202 253 -218 219 -210 229 -256 425 -232 221 -430 251 -212 469 -204 235 -446 441 -246 213 -242 193 -244 211 -464 451 -456 441 -248 215 -450 201 -256 427 -452 457 -450 455 -450 439 -446 451 -238 217 -256 207 -202 255 -218 217 -210 231 -442 237 -244 423 -240 217 -464 203 -254 419 -454 227 -210 243 -212 241 -212 477 -418 453 -484 199 -226 431 -267004 97 -422 97 -164 65 -490 97 -2222 559 -66 163 -64 295 -100 497 -100 263 -98 361 -460 793 -66 1803 -100 339 -68 1733 diff --git a/assets/unit_tests/subghz/scher_khan_magic_code.sub b/assets/unit_tests/subghz/scher_khan_magic_code.sub index 81467b0a7..19d6c8160 100644 --- a/assets/unit_tests/subghz/scher_khan_magic_code.sub +++ b/assets/unit_tests/subghz/scher_khan_magic_code.sub @@ -1,7 +1,7 @@ Filetype: Flipper SubGhz RAW File Version: 1 Frequency: 433920000 -Preset: FuriHalSubGhzPreset2FSKDev238Async +Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 95 -240 191 -412 187 -152 95 -120 263 -220 373 -116 199 -88 239 -334 503 -142 167 -174 85 -172 143 -86 85 -96 119 -196 259 -96 167 -148 201 -96 481 -120 237 -264 143 -144 105 -790 257 -88 243 -144 143 -86 553 -216 103 -174 143 -142 119 -144 119 -224 307 -144 239 -120 127 -390 189 -168 119 -312 191 -142 215 -96 103 -86 307 -274 191 -104 143 -96 167 -120 357 -390 651 -218 197 -170 143 -164 85 -210 143 -116 143 -86 255 -160 95 -114 199 -88 545 -288 173 -114 115 -202 85 -316 145 -144 113 -562 105 -254 85 -144 287 -116 115 -202 87 -346 287 -244 103 -370 143 -230 143 -144 85 -88 113 -230 231 -984 119 -144 171 -144 201 -174 115 -288 113 -192 277 -114 345 -202 249 -316 83 -228 119 -146 151 -116 85 -202 85 -144 437 -272 115 -144 85 -86 173 -142 163 -260 205 -280 339 -88 229 -194 363 -144 585 -394 599 -144 85 -100 151 -574 171 -174 85 -114 239 -288 211 -410 115 -144 489 -116 115 -116 201 -336 95 -96 93 -120 95 -128 115 -116 257 -174 141 -292 113 -162 165 -192 287 -192 161 -192 1125 -202 85 -222 461 -144 113 -116 115 -114 201 -288 259 -288 401 -174 85 -106 119 -96 431 -130 85 -114 375 -494 95 -96 167 -288 281 -104 411 -214 335 -144 119 -534 85 -144 143 -144 229 -246 97 -82 85 -260 201 -304 403 -116 391 -172 229 -448 81 -82 95 -116 201 -86 521 -144 119 -336 119 -142 95 -144 119 -96 267 -170 283 -288 95 -190 167 -288 199 -116 247 -144 87 -144 85 -114 215 -260 115 -116 113 -338 103 -240 167 -96 119 -120 95 -96 405 -188 85 -116 191 -334 143 -164 85 -230 115 -154 119 -166 143 -288 195 -144 87 -86 343 -232 113 -202 115 -86 201 -278 201 -86 85 -86 303 -286 95 -120 95 -168 167 -118 129 -94 95 -432 151 -218 119 -186 235 -230 167 -632 143 -144 231 -156 153 -282 147 -96 143 -592 261 -120 191 -96 167 -576 333 -342 171 -462 173 -514 157 -432 87 -312 397 -378 287 -374 403 -230 117 -264 257 -170 189 -346 137 -528 117 -240 805 -454 539 -288 171 -86 173 -286 153 -288 115 -172 173 -158 399 -114 85 -116 201 -116 85 -216 215 -432 113 -100 113 -338 123 -146 289 -86 143 -114 343 -94 95 -96 311 -110 171 -86 143 -88 969 -312 119 -316 401 -134 255 -202 323 -86 87 -114 253 -228 95 -172 85 -170 165 -86 343 -192 119 -144 213 -112 191 -334 287 -96 359 -500 137 -120 261 -242 119 -118 283 -210 167 -178 287 -174 343 -268 RAW_Data: 185 -96 119 -978 215 -120 213 -96 265 -172 261 -168 225 -462 307 -312 87 -172 115 -288 423 -84 87 -230 143 -404 109 -114 233 -216 165 -240 95 -288 95 -192 215 -238 335 -116 199 -170 135 -144 109 -408 285 -130 97 -98 115 -86 85 -86 87 -120 167 -144 119 -264 171 -316 231 -202 257 -274 115 -114 421 -86 85 -202 143 -144 201 -86 229 -86 143 -116 115 -86 115 -114 85 -202 143 -288 163 -186 483 -202 167 -212 373 -288 115 -86 171 -116 113 -230 231 -230 201 -172 171 -318 113 -390 101 -200 317 -404 287 -220 171 -232 199 -490 171 -144 173 -168 223 -202 173 -200 115 -172 119 -288 209 -220 85 -86 85 -480 423 -450 201 -364 411 -216 143 -120 219 -538 513 -330 199 -120 119 -96 127 -138 141 -140 117 -312 429 -182 259 -422 1027 -280 303 -210 131 -168 85 -86 201 -230 547 -174 113 -88 259 -202 109 -96 215 -144 291 -168 151 -316 85 -258 287 -260 279 -302 171 -284 189 -168 95 -96 309 -240 167 -384 165 -96 169 -226 95 -216 271 -122 195 -102 113 -116 339 -168 85 -192 583 -114 297 -406 167 -326 209 -212 119 -262 143 -86 87 -114 125 -172 115 -172 173 -576 219 -192 335 -120 85 -116 171 -202 711 -116 85 -172 373 -232 381 -144 95 -244 173 -210 119 -198 85 -86 143 -312 211 -96 115 -86 113 -528 103 -150 191 -144 119 -98 333 -172 215 -114 87 -86 529 -490 85 -356 239 -374 143 -260 457 -312 157 -394 131 -116 353 -172 229 -490 171 -144 189 -166 305 -144 85 -288 201 -356 147 -106 167 -740 143 -312 95 -242 137 -88 465 -126 239 -358 167 -120 1385 -130 385 -192 95 -120 817 -218 805 -140 171 -394 143 -86 371 -430 143 -216 187 -84 143 -316 173 -86 115 -202 85 -114 215 -96 167 -316 107 -368 313 -120 95 -382 197 -216 289 -388 119 -264 109 -86 113 -202 963 -172 111 -104 113 -346 115 -266 255 -88 229 -202 109 -288 357 -348 365 -226 381 -144 455 -144 901 -532 531 -108 103 -142 225 -144 143 -230 85 -144 141 -142 645 -82 167 -116 143 -172 403 -230 85 -144 87 -288 229 -144 115 -144 371 -82 107 -306 333 -116 113 -88 143 -86 229 -174 617 -96 677 -286 95 -240 95 -168 141 -216 95 -212 209 -120 95 -144 95 -240 165 -116 765 -230 229 -88 141 -192 97 -86 111 -114 231 -172 143 -144 365 -96 291 -144 129 -144 87 -168 141 -120 215 -144 143 -238 215 -192 95 -96 95 -144 403 -216 163 -102 155 -120 95 -120 263 -120 403 -374 431 -230 303 -134 791 -288 diff --git a/assets/unit_tests/subghz/security_pls_1_0.sub b/assets/unit_tests/subghz/security_pls_1_0.sub index f0909a776..42fe7900b 100644 --- a/assets/unit_tests/subghz/security_pls_1_0.sub +++ b/assets/unit_tests/subghz/security_pls_1_0.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 310000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: Security+ 1.0 Bit: 42 diff --git a/assets/unit_tests/subghz/security_pls_1_0_raw.sub b/assets/unit_tests/subghz/security_pls_1_0_raw.sub index 5542663d6..ceecad35b 100644 --- a/assets/unit_tests/subghz/security_pls_1_0_raw.sub +++ b/assets/unit_tests/subghz/security_pls_1_0_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 390000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 297 -1592 167 -1594 231 -366 65 -598 65 -600 197 -98 199 -1098 99 -98 65 -862 133 -628 165 -597 361 -6828 65 -1668 231 -632 65 -792 163 -1284 163 -5554 199 -1588 165 -5300 97 -992 65 -432 197 -2566 99 -2340 63 -5932 65 -1462 65 -1062 131 -2454 65 -1446 133 -1682 65 -1260 65 -368 131 -1482 65 -134 131 -1512 197 -1710 131 -824 99 -66 133 -464 131 -866 65 -698 65 -200 65 -894 99 -1692 233 -1130 97 -2160 265 -1392 99 -132 131 -166 65 -1318 327 -1548 163 -6980 165 -994 65 -698 131 -1580 129 -132 63 -758 97 -466 99 -1590 395 -1024 97 -600 99 -732 63 -228 129 -98 165 -292 99 -696 231 -232 197 -166 133 -132 131 -430 165 -664 199 -1596 197 -530 65 -1054 63 -3474 165 -4504 65 -2980 99 -268 133 -2356 131 -798 99 -132 99 -796 97 -1692 97 -3804 199 -166 67 -66 97 -132 99 -5058 129 -1512 163 -1290 65 -298 199 -398 65 -1034 65 -1332 167 -3448 65 -1750 65 -1186 131 -462 65 -920 65 -166 97 -362 131 -1704 131 -1748 65 -2124 65 -1064 133 -1694 197 -5148 99 -566 131 -1696 99 -598 65 -698 231 -692 267 -2490 99 -1618 165 -1760 197 -2152 165 -1226 195 -100 99 -66 131 -100 99 -400 65 -456 131 -198 165 -368 261 -826 365 -858 99 -132 163 -460 229 -264 65 -664 165 -234 231 -98 165 -100 99 -1986 99 -2526 65 -832 167 -1762 231 -728 67 -530 133 -2082 97 -960 67 -498 199 -1658 133 -1488 99 -2652 99 -1258 165 -5284 99 -1660 65 -1790 65 -432 65 -3804 197 -2170 131 -196 129 -66 97 -832 99 -2466 97 -1622 99 -1624 163 -394 163 -1018 133 -1856 99 -430 67 -826 197 -3156 65 -166 97 -558 65 -1690 131 -1498 99 -66 65 -2716 161 -658 99 -1054 65 -230 65 -2074 97 -2042 65 -3074 263 -1698 165 -492 65 -400 131 -196 131 -368 165 -134 203 -168 201 -134 165 -398 301 -628 99 -1032 99 -830 65 -66 65 -432 229 -230 161 -394 197 -228 197 -330 327 -66 197 -822 231 -392 131 -892 393 -1456 131 -64 163 -1418 65 -166 165 -1428 133 -1526 163 -954 65 -888 99 -1956 133 -1194 131 -3514 65 -1728 97 -662 67 -1590 167 -2714 65 -930 65 -166 65 -728 131 -2752 65 -434 133 -1828 99 -3704 129 -1912 133 -1544 227 -494 133 -202 97 -466 65 -200 131 -1580 263 -1708 65 -626 65 -626 199 -166 133 -3446 99 -3420 99 -398 131 -1164 131 -7018 131 -3468 63 -1658 199 -956 101 -598 99 -1790 97 -1192 65 -362 133 -630 65 -100 429 -794 65 -696 231 -1130 65 -268 395 -730 131 -466 431 -66 233 -168 197 -998 97 -264 65 -796 163 -236 diff --git a/assets/unit_tests/subghz/security_pls_2_0.sub b/assets/unit_tests/subghz/security_pls_2_0.sub index a13ab0fc8..85abdb3e1 100644 --- a/assets/unit_tests/subghz/security_pls_2_0.sub +++ b/assets/unit_tests/subghz/security_pls_2_0.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 315000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: Security+ 2.0 Bit: 62 diff --git a/assets/unit_tests/subghz/security_pls_2_0_raw.sub b/assets/unit_tests/subghz/security_pls_2_0_raw.sub index 4cd9822c4..f3c0b4992 100644 --- a/assets/unit_tests/subghz/security_pls_2_0_raw.sub +++ b/assets/unit_tests/subghz/security_pls_2_0_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 390000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: -130 1115 -68 55471 -1962 167 -1664 99 -364 427 -232 759 -132 199 -100 267 -134 527 -132 99 -1292 99 -266 65 -68 131 -762 327 -98 393 -298 37611 -730 131 -428 1487 -3910 327 -100 295 -98 491 -492 65 -98 197 -860 97 -98 131 -856 131 -134 231 -792 229 -66 919 -198 7527 -7444 131 -722 97 -230 65 -98 527 -100 267 -68 229 -262 361 -528 195 -624 131 -164 495 -66 1697 -66 1791 -132 3715 -8320 65 -394 165 -166 461 -1094 297 -532 131 -764 197 -98 261 -230 165 -100 6235 -12144 265 -600 131 -134 65 -298 233 -958 163 -196 97 -756 263 -98 465 -134 97 -164 3051 -16552 431 -68 131 -166 131 -264 331 -298 561 -166 689 -66 557 -264 3173 -8316 65 -166 531 -298 197 -234 233 -134 233 -892 163 -296 65 -890 161 -232 331 -100 397 -15072 199 -100 99 -394 165 -164 129 -132 197 -132 199 -798 99 -1032 67 -398 427 -100 391 -98 363 -166 297 -66 2869 -66 2299 -12300 97 -660 99 -662 263 -594 131 -662 133 -66 199 -332 97 -134 99 -132 65 -134 131 -100 431 -100 2143 -66 429 -8840 97 -6976 97 -66 397 -66 65 -166 131 -230 131 -294 99 -228 97 -230 129 -756 133 -1298 1293 -132 529 -100 2567 -15654 229 -328 97 -198 131 -66 195 -1492 131 -398 99 -134 99 -366 265 -168 99 -100 235 -100 131 -396 299 -200 1029 -66 6461 -6804 99 -1062 65 -960 65 -232 131 -98 195 -1708 63 -196 261 -164 331 -66 261 -12094 65 -298 265 -198 163 -592 131 -2402 63 -66 297 -198 97 -66 393 -264 4003 -15878 231 -98 261 -496 229 -264 65 -858 131 -994 133 -66 331 -66 429 -100 165 -132 297 -15004 99 -1466 97 -266 165 -198 463 -796 231 -66 131 -298 99 -100 133 -134 167 -430 99 -66 365 -100 297 -134 265 -132 563 -98 1217 -66 6399 -8742 99 -592 99 -426 397 -2338 199 -66 995 -134 229 -132 65 -164 3989 -66 3675 -6962 165 -466 65 -564 399 -66 199 -134 263 -396 97 -132 97 -100 97 -428 393 -624 131 -988 229 -66 363 -230 791 -164 7883 -8286 165 -134 99 -66 197 -100 99 -68 131 -164 163 -398 197 -2162 2005 -66 97 -100 4365 -98 1255 -12012 99 -132 165 -462 65 -166 97 -564 65 -100 331 -794 199 -364 261 -496 331 -132 823 -66 6233 -10976 165 -764 165 -200 195 -296 97 -19176 195 -230 129 -658 131 -132 293 -66 133 -860 65 -858 131 -64 229 -66 227 -66 161 -66 9051 -7316 65 -1494 131 -98 165 -198 65 -134 365 -398 297 -100 3969 -14874 99 -998 65 -564 67 -364 263 -132 163 -528 197 -132 65 -264 65 -264 431 -100 301 -66 297 diff --git a/assets/unit_tests/subghz/somfy_keytis_raw.sub b/assets/unit_tests/subghz/somfy_keytis_raw.sub index 21145c064..2bed74fe1 100644 --- a/assets/unit_tests/subghz/somfy_keytis_raw.sub +++ b/assets/unit_tests/subghz/somfy_keytis_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 433420000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 1753 -32700 2581 -2598 2513 -2604 2535 -2568 2529 -2602 2519 -2608 2523 -2574 2529 -2598 2517 -2604 2537 -2596 2521 -2574 2523 -2600 2515 -2530 4829 -1304 1305 -1308 1309 -678 669 -1280 1305 -680 639 -678 653 -1308 1273 -1334 645 -686 645 -684 611 -716 645 -684 1273 -1340 1269 -1338 641 -684 643 -684 643 -684 609 -714 1273 -682 643 -680 643 -1340 643 -684 1271 -1310 645 -684 1303 -682 643 -1308 1301 -1312 645 -682 1275 -1350 649 -644 649 -702 647 -678 679 -654 645 -646 681 -644 683 -642 1309 -672 647 -690 645 -1314 635 -680 1297 -688 651 -1316 647 -682 617 -686 649 -682 1307 -660 639 -704 639 -1304 1293 -688 653 -672 621 -696 653 -664 653 -668 651 -670 647 -704 613 -704 617 -704 613 -708 613 -708 613 -710 613 -1336 665 -652 1285 -680 655 -1340 583 -1734 2523 -2570 2543 -2572 2529 -2564 2533 -2570 2531 -2596 2519 -2478 4805 -1310 1299 -1314 1313 -680 637 -1328 1289 -684 623 -690 653 -1322 1289 -1308 651 -668 651 -688 615 -688 679 -654 1305 -1304 1305 -1304 645 -686 645 -688 643 -652 643 -712 1273 -680 653 -680 651 -1312 647 -682 1273 -1342 639 -680 1271 -718 613 -1348 1281 -1310 639 -680 1295 -1314 653 -678 645 -682 645 -678 675 -648 651 -678 651 -678 647 -678 1281 -698 613 -706 613 -1336 669 -658 1293 -684 639 -1330 629 -686 653 -686 653 -654 1309 -678 621 -1340 1273 -712 603 -712 639 -672 653 -664 651 -666 651 -670 651 -702 617 -672 649 -704 613 -706 615 -706 615 -708 613 -1334 1293 -688 621 -1316 659 -686 1281 -2338 2501 -2600 2511 -2606 2491 -2624 2481 -2610 2521 -2560 2527 -2506 4797 -1308 1299 -1312 1317 -678 635 -1294 1323 -682 619 -692 635 -1334 1291 -1312 653 -658 679 -656 647 -688 643 -654 1303 -1336 1301 -1304 643 -690 641 -654 675 -654 643 -682 1303 -678 655 -680 645 -1308 677 -654 1309 -1310 645 -682 1275 -686 647 -1320 1281 -1318 679 -642 1317 -1300 667 -636 677 -660 673 -656 705 -612 671 -670 643 -670 681 -646 1309 -660 669 -674 637 -1310 673 -658 1293 -690 653 -1290 657 -688 649 -656 679 -658 1303 -676 635 -1314 657 -658 1307 -678 669 -670 621 -690 635 -672 649 -674 647 -674 647 -674 647 -708 613 -710 645 -676 647 -676 613 -1336 631 -688 1285 -680 655 -1306 647 -688 611 -1706 2535 -2592 2529 -2570 2533 -2566 2543 -2570 2529 -2572 2533 -2474 4807 -1294 1325 -1314 1279 -680 665 -1290 1323 -648 659 -688 653 -1290 1317 -1310 657 -656 679 -658 641 -688 643 -656 1303 -1334 1299 -1302 675 -646 675 -644 677 -658 643 -688 1303 -676 641 -676 633 -1314 691 -654 1277 -1336 649 -656 1305 -674 667 -1284 1315 -1308 653 -656 1305 -1306 679 -658 645 -690 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index 928838d3c..a6a3c9866 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -145,3 +145,24 @@ 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 +RAW_Data: 57 -144 401 -86 173 -202 143 -258 133 -88 257 -144 287 -58 402 -56 259 -230 259 -86 85 -96 95 -174 286 -162 57 -230 253 -400 229 -88 536 -58 85 -72 167 -110 263 -72 229 -58 85 -86 87 -262 119 -288 163 -210 321 -320 186 -140 261 -96 143 -456 117 -216 143 -246 239 -102 121 -72 71 -191 167 -263 191 -96 239 -80 57 -116 143 -118 167 -168 215 -288 191 -106 287 -114 517 -88 113 -394 173 -70 215 -100 661 -86 201 -114 259 -58 287 -86 57 -202 399 -200 57 -288 229 -144 115 -1425 83 -142 173 -86 459 -112 223 -144 201 -116 143 -114 196 -17422 287 -614 1075 -1132 533 -560 1125 -1112 561 -530 1133 -1088 585 -512 1129 -1104 563 -1102 571 -1072 591 -524 1137 -538 1115 -19392 589 -522 1139 -1090 569 -520 1137 -1100 587 -520 1135 -1092 569 -514 1155 -1076 599 -1062 585 -1086 585 -532 1125 -528 1145 -19396 581 -504 1155 -1064 619 -506 1145 -1080 595 -508 1149 -1062 587 -536 1129 -1088 585 -1090 589 -1062 587 -510 1177 -506 1149 -19394 587 -524 1147 -1076 597 -512 1129 -1072 617 -498 1159 -1066 599 -514 1147 -1076 595 -1060 613 -1060 589 -506 1179 -510 1155 -19378 605 -514 1129 -1098 601 -488 1177 -1062 583 -510 1161 -1072 587 -516 1169 -1058 587 -1082 589 -1076 593 -532 1121 -536 1127 -19444 549 -542 1129 -1092 601 -510 1147 -1098 581 -506 1123 -1100 585 -510 1147 -1106 591 -1060 597 -1060 595 -510 1151 -534 1157 -19390 593 -512 1163 -1064 597 -510 1173 -1042 617 -510 1159 -1056 579 -556 1133 -1054 609 -1060 607 -1078 585 -528 1147 -516 1135 -19444 559 -534 1165 -1052 605 -510 1149 -1062 599 -520 1173 -1054 591 -540 1111 -1116 573 -1088 593 -1042 615 -534 1117 -536 1129 -19436 569 -530 1157 -1052 605 -504 1177 -1062 591 -536 1113 -1114 589 -514 1125 -1112 563 -1084 587 -1080 589 -536 1123 -532 1165 -19392 599 -524 1143 -1080 595 -540 1125 -1090 563 -534 1149 -1084 567 -534 1147 -1092 587 -1086 585 -1064 565 -558 1123 -532 1143 -19450 549 -556 1121 -1086 585 -534 1137 -1094 583 -516 1133 -1114 563 -536 1123 -1108 573 -1082 597 -1078 565 -532 1143 -524 1135 -19464 561 -528 1149 -1066 613 -508 1151 -1076 587 -532 1125 -1076 609 -506 1175 -1076 563 -1106 563 -1084 591 -534 1157 -484 1179 -19414 589 -528 1131 -1096 587 -520 1163 -1080 563 -550 1121 -1098 555 -562 1117 -1082 575 -1112 563 -1104 559 -558 1121 -530 1149 -19442 591 -498 1175 -1066 585 -534 1121 -1114 565 -540 1115 -1096 585 -538 1123 -1110 559 -1086 585 -1084 589 -530 1125 -558 1121 -19454 563 -542 1163 -1044 585 -558 1131 -1092 571 -536 1133 -1088 587 -518 1149 -1086 601 -1058 587 -1080 611 -510 +RAW_Data: 1125 -536 1145 -19444 567 -542 1151 -1086 581 -534 1133 -1084 583 -530 1141 -1090 587 -516 1121 -1114 591 -1062 595 -1092 567 -538 1131 -536 1145 -19448 589 -516 1143 -1076 591 -540 1115 -1090 591 -538 1115 -1096 597 -512 1155 -1078 581 -1082 585 -1104 559 -530 1153 -516 1147 -19438 589 -526 1157 -1056 609 -534 1137 -1078 585 -532 1149 -1076 585 -536 1123 -1104 587 -1062 597 -1082 587 -536 1121 -554 1121 -19464 555 -560 1123 -1104 563 -542 1131 -1096 565 -536 1143 -1060 621 -492 1175 -1078 571 -1100 577 -1092 591 -494 1167 -538 1117 -19452 593 -538 1133 -1074 603 -510 1157 -1062 595 -548 1115 -1102 573 -538 1139 -1094 567 -1094 583 -1092 567 -536 1127 -560 1113 -19478 563 -534 1151 -1084 569 -534 1135 -1116 565 -534 1135 -1084 583 -532 1155 -1060 587 -1084 589 -1084 581 -536 1125 -546 1141 -19460 577 -536 1123 -1102 565 -554 1119 -1096 593 -520 1133 -1108 563 -546 1131 -1088 569 -1094 573 -1100 583 -526 1147 -540 1123 -19466 587 -530 1129 -1100 587 -510 1143 -1084 609 -510 1131 -1110 567 -542 1121 -1102 567 -1110 563 -1106 567 -532 1149 -518 1145 -19458 573 -534 1147 -1082 575 -556 1149 -1048 589 -532 1157 -1088 565 -536 1129 -1088 589 -1106 563 -1086 587 -536 1129 -548 1147 -4298 103 -56 87 -344 87 -258 85 -278 215 -70 77 -344 279 -56 87 -56 197 -198 143 -288 362 -86 605 -144 57 -268 95 -114 143 -144 173 -144 143 -402 57 -202 259 -391 652 -340 427 -230 173 -356 97 -144 111 -246 219 -96 191 -114 173 -288 115 -56 109 -106 199 -106 73 -130 57 -172 85 -260 373 -56 629 -200 690 -230 273 -120 85 -460 85 -314 77 -78 111 -88 401 -116 171 -312 71 -500 81 -224 229 -88 257 -370 181 -172 200 -116 535 -174 113 -294 213 -359 445 -144 258 -114 115 -202 675 -509 239 -432 373 -538 85 -58 113 -86 761 -104 113 -318 443 -70 143 -144 647 -204 111 -334 87 -114 115 -144 113 -188 177 -144 199 -260 143 -86 87 -622 57 -116 171 -58 139 -222 55 -346 315 -76 345 -114 139 -171 195 -52 53 -98 119 -144 143 -244 95 -72 95 -96 167 -302 253 -186 307 -444 287 -449 115 -172 57 -172 316 -202 85 -370 697 -116 57 -144 171 -202 259 -114 85 -144 87 -315 85 -58 201 -116 171 -272 121 -358 171 -403 113 -86 115 -202 489 -229 115 -392 95 -116 171 -140 93 -102 143 -543 245 -358 215 -120 387 -288 171 -202 221 -202 115 -748 57 -316 143 -260 143 -288 115 -316 115 -58 85 -288 143 -460 485 -96 71 -104 199 -96 199 -202 143 -86 201 -116 85 -230 211 -288 115 -605 365 -126 53 -172 +RAW_Data: 317 -144 57 -486 53 -282 115 -585 97 -72 229 -174 257 -440 225 -86 173 -518 243 -167 95 -259 137 -96 694 -58 227 -80 279 -287 71 -72 301 -72 121 -106 51 -84 57 -58 199 -260 143 -288 219 -174 113 -681 115 -172 403 -58 113 -116 113 -432 171 -202 55 -108 95 -212 113 -72 527 -166 95 -212 195 -108 603 -142 239 -296 173 -346 373 -287 53 -80 79 -72 95 -238 95 -312 167 -618 143 -288 95 -72 95 -72 141 -210 55 -258 143 -328 305 -58 87 -86 315 -116 195 -218 85 -290 285 -220 215 -189 201 -58 57 -645 119 -96 71 -144 119 -406 143 -72 191 -72 631 -268 344 -56 115 -260 315 -140 455 -518 57 -58 171 -144 488 -86 219 -232 257 -144 85 -174 171 -260 115 -56 87 -166 197 -58 83 -56 85 -288 113 -410 115 -172 163 -202 113 -58 201 -144 201 -86 143 -264 167 -212 113 -116 139 -72 181 -287 343 -430 201 -260 201 -462 143 -192 301 -230 191 -454 187 -144 315 -164 143 -477 165 -58 201 -114 143 -490 115 -86 201 -58 113 -88 85 -58 203 -198 375 -86 171 -346 95 -88 257 -170 81 -56 143 -172 335 -230 173 -202 133 -471 187 -264 215 -86 115 -198 159 -72 179 -112 195 -116 449 -216 93 -96 167 -216 71 -216 71 -166 235 -86 447 -102 101 -226 195 -213 71 -144 215 -144 215 -261 241 -136 269 -142 263 -311 215 -172 201 -144 265 -168 71 -404 259 -86 85 -230 115 -650 143 -202 749 -512 248 -316 201 -154 71 -96 95 -360 105 -56 57 -432 95 -288 95 -286 95 -96 166 -144 93 -144 167 -150 904 -162 95 -526 287 -244 95 -240 383 -120 167 -394 430 -854 95 -72 143 -194 227 -120 167 -264 405 -144 143 -72 143 -72 141 -120 187 -86 143 -164 170 -96 143 -58 143 -86 402 -166 153 -120 95 -96 69 -96 71 -359 404 -338 71 -225 93 -74 97 -54 161 -114 319 -288 113 -116 459 -202 115 -114 115 -116 143 -86 57 -56 87 -114 85 -375 113 -58 311 -240 203 -288 95 -72 119 -383 213 -384 115 -86 171 -58 53 -104 401 -58 115 -86 373 -116 143 -144 161 -216 406 -72 263 -96 215 -72 95 -94 167 -96 191 -240 95 -94 214 -120 403 -116 200 -114 57 -172 220 -120 137 -364 334 -392 115 -260 199 -116 373 -188 95 -110 143 -172 87 -114 172 -230 57 -316 201 -56 249 -485 171 -202 87 -86 85 -144 345 -86 171 -58 259 -58 295 -120 95 -120 71 -192 635 -118 167 -96 375 -72 119 -120 261 -144 167 -96 95 -96 923 -215 71 -433 71 -477 +RAW_Data: 191 -240 85 -72 637 -408 213 -510 261 -168 143 -126 79 -106 167 -72 117 -218 251 -168 119 -96 215 -182 191 -238 517 -116 201 -144 255 -154 97 -94 215 -72 95 -120 71 -288 261 -106 434 -96 606 -232 229 -432 85 -174 343 -58 329 -156 55 -116 259 -144 488 -56 307 -339 115 -202 334 -88 113 -86 57 -174 143 -144 401 -376 85 -240 267 -82 95 -216 137 -158 85 -144 143 -58 221 -308 295 -114 87 -114 301 -120 358 -517 71 -262 191 -144 57 -140 165 -407 53 -262 217 -120 238 -358 119 -357 71 -72 119 -96 428 -72 95 -72 167 -72 93 -240 335 -96 357 -240 173 -230 143 -114 87 -200 143 -232 287 -150 97 -288 71 -72 93 -288 115 -58 143 -230 109 -264 71 -72 119 -72 238 -242 97 -78 163 -86 115 -518 79 -560 205 -449 969 -144 507 -86 231 -114 345 -58 979 -110 85 -288 287 -404 229 -202 57 -274 233 -86 115 -202 632 -230 85 -312 369 -392 460 -450 75 -280 85 -202 201 -86 229 -174 143 -144 233 -528 115 -212 127 -202 287 -172 403 -172 139 -128 165 -138 261 -392 143 -480 142 -189 291 -80 53 -283 167 -140 113 -1008 191 -144 119 -120 71 -193 241 -462 201 -58 143 -344 539 -316 113 -174 85 -116 113 -250 239 -168 405 -168 239 -158 85 -144 115 -86 57 -86 341 -144 171 -202 85 -202 115 -114 719 -88 55 -318 257 -56 254 -86 171 -116 459 -174 171 -329 95 -134 85 -314 431 -306 77 -316 401 -86 173 -404 281 -1073 488 -94 217 -78 101 -98 214 -120 215 -340 403 -535 143 -564 115 -116 199 -58 85 -174 315 -58 335 -136 55 -260 143 -144 229 -460 143 -58 143 -144 171 -202 115 -374 291 -130 339 -82 143 -58 171 -58 201 -86 85 -174 1022 -56 85 -82 255 -240 103 -202 431 -278 95 -216 119 -72 71 -96 71 -559 57 -144 171 -88 113 -86 231 -414 131 -192 237 -360 95 -168 145 -168 213 -120 167 -96 143 -110 57 -86 259 -56 87 -777 295 -96 57 -86 173 -86 171 -404 143 -172 231 -200 57 -441 55 -58 173 -56 87 -86 171 -72 287 -72 119 -262 119 -144 71 -72 121 -310 71 -302 113 -54 193 -80 307 -58 257 -232 143 -56 143 -116 219 -72 695 -70 71 -460 85 -232 719 -363 57 -402 604 -230 287 -138 83 -172 259 -58 171 -174 55 -88 489 -114 143 -116 171 -116 143 -58 199 -144 145 -343 374 -186 235 -140 77 -86 143 -202 143 -144 113 -144 143 -58 732 -96 263 -264 71 -206 95 -168 215 -144 271 -80 139 -88 85 -414 75 -100 +RAW_Data: 285 -96 627 -362 53 -84 201 -374 113 -202 115 -202 421 -316 85 -58 139 -224 87 -86 229 -58 243 -178 267 -288 95 -336 171 -96 213 -288 71 -405 95 -96 95 -384 95 -72 213 -72 95 -96 95 -272 87 -1083 85 -58 113 -88 257 -116 143 -292 175 -318 95 -120 95 -144 95 -72 71 -216 368 -116 373 -172 115 -58 85 -116 143 -86 85 -144 201 -86 201 -202 257 -144 201 -174 113 -144 115 -144 257 -202 585 -364 173 -138 287 -422 431 -86 85 -96 869 -186 95 -52 115 -86 115 -58 55 -276 365 -86 85 -489 171 -140 577 -106 718 -144 391 -232 195 -82 143 -172 109 -120 167 -96 280 -216 145 -240 215 -186 163 -96 141 -172 159 -603 257 -108 629 -192 119 -80 87 -172 57 -144 286 -86 57 -230 344 -58 113 -537 75 -96 537 -86 403 -196 167 -264 119 -238 119 -120 167 -96 95 -478 95 -120 167 -216 1085 -96 358 -72 263 -72 69 -120 143 -96 71 -96 191 -362 55 -144 57 -260 113 -58 85 -174 55 -88 257 -86 231 -194 55 -58 115 -56 55 -339 55 -58 374 -172 139 -82 419 -98 119 -261 71 -72 71 -240 713 -86 143 -218 295 -72 53 -56 431 -58 317 -144 161 -144 373 -144 173 -144 57 -114 85 -116 195 -72 708 -172 115 -86 191 -96 506 -120 71 -174 85 -58 363 -114 317 -230 316 -200 87 -114 57 -230 115 -315 173 -280 694 -212 453 -256 143 -202 113 -540 352 -116 257 -116 457 -56 109 -58 143 -230 259 -144 259 -525 119 -408 247 -112 389 -72 431 -96 137 -236 97 -474 201 -298 71 -82 55 -116 55 -112 199 -174 191 -86 143 -144 115 -114 317 -86 85 -230 87 -114 259 -84 107 -130 143 -94 153 -86 135 -94 215 -72 239 -94 435 -96 263 -142 166 -334 87 -194 179 -96 115 -284 135 -56 57 -144 463 -204 143 -316 201 -58 403 -86 141 -288 85 -202 139 -397 171 -174 305 -202 85 -144 373 -253 161 -492 181 -191 95 -216 315 -191 71 -166 97 -126 337 -96 71 -96 189 -168 295 -84 197 -86 259 -345 137 -144 167 -796 115 -344 455 -72 119 -96 119 -550 209 -88 85 -86 143 -340 167 -260 143 -537 85 -226 51 -537 57 -260 315 -461 51 -84 199 -358 383 -96 143 -257 115 -86 173 -86 201 -144 143 -316 85 -86 479 -88 85 -72 71 -104 115 -116 267 -72 137 -144 143 -116 85 -86 373 -288 115 -200 87 -114 259 -114 259 -462 143 -144 171 -86 57 -58 137 -144 57 -634 343 -72 205 -86 143 -258 57 -232 113 -230 461 -58 185 -74 537 -86 +RAW_Data: 535 -142 57 -58 55 -116 115 -432 85 -172 259 -192 167 -120 117 -72 119 -240 334 -72 71 -267 285 -144 119 -374 85 -88 85 -114 143 -202 229 -58 143 -202 115 -202 171 -86 71 -144 87 -56 173 -373 143 -116 113 -462 169 -80 215 -148 115 -336 85 -230 163 -432 85 -374 639 -174 85 -58 57 -82 295 -352 269 -532 414 -322 95 -287 263 -268 115 -56 259 -76 85 -282 401 -305 516 -114 115 -202 171 -86 451 -110 85 -346 201 -274 149 -202 85 -364 366 -258 57 -114 259 -172 142 -144 85 -116 85 -480 171 -144 57 -352 115 -116 535 -404 315 -202 163 -158 517 -316 215 -98 85 -346 85 -144 87 -86 257 -82 167 -58 85 -116 113 -894 233 -186 77 -266 147 -72 71 -82 57 -86 171 -58 57 -86 201 -364 143 -202 115 -114 85 -88 113 -86 87 -230 57 -76 613 -72 85 -96 209 -346 458 -58 547 -490 201 -315 315 -116 75 -168 359 -335 95 -384 93 -120 71 -312 251 -366 233 -96 189 -240 263 -192 271 -58 115 -58 229 -346 459 -174 113 -144 173 -144 218 -224 57 -116 215 -72 103 -202 513 -210 433 -116 113 -174 650 -273 147 -450 375 -86 115 -172 536 -84 85 -230 85 -58 195 -468 287 -110 551 -214 167 -311 213 -250 85 -58 85 -355 113 -230 115 -144 117 -288 195 -202 57 -376 123 -144 236 -168 553 -284 119 -72 143 -188 161 -120 93 -312 335 -58 55 -260 105 -244 143 -120 381 -268 173 -268 635 -168 453 -318 71 -167 71 -406 191 -172 215 -408 119 -144 93 -120 97 -130 143 -192 308 -122 147 -550 313 -96 139 -162 167 -96 431 -80 83 -112 201 -86 287 -86 229 -116 57 -288 113 -174 143 -116 113 -144 115 -518 57 -230 57 -172 231 -86 113 -314 183 -144 119 -72 165 -446 81 -86 135 -190 143 -96 71 -72 411 -96 143 -120 69 -216 349 -72 95 -96 517 -646 163 -86 113 -116 171 -116 143 -116 113 -287 259 -114 517 -168 141 -116 105 -72 95 -96 311 -118 159 -310 191 -54 143 -258 115 -450 219 -54 339 -372 239 -72 167 -174 113 -58 57 -144 259 -172 143 -336 113 -174 85 -230 83 -668 85 -202 113 -144 57 -116 373 -316 719 -288 115 -58 75 -120 139 -144 229 -144 57 -144 171 -192 391 -202 403 -58 315 -188 259 -56 115 -144 85 -404 57 -58 105 -102 429 -406 81 -172 57 -144 287 -230 287 -220 317 -458 283 -58 113 -86 269 -72 281 -58 85 -202 113 -52 421 -58 229 -480 259 -58 143 -660 155 -638 123 -86 57 -86 143 -346 143 -144 57 -144 \ No newline at end of file diff --git a/debug/flipperapps.py b/debug/flipperapps.py index 8e1aa2daf..e815e40b1 100644 --- a/debug/flipperapps.py +++ b/debug/flipperapps.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Tuple, Dict +from typing import Optional, Tuple, Dict, ClassVar import struct import posixpath import os @@ -22,14 +22,18 @@ class AppState: debug_link_elf: str = "" debug_link_crc: int = 0 + DEBUG_ELF_ROOT: ClassVar[Optional[str]] = None + def __post_init__(self): if self.other_sections is None: self.other_sections = {} - def get_original_elf_path(self, elf_path="build/latest/.extapps") -> str: + def get_original_elf_path(self) -> str: + if self.DEBUG_ELF_ROOT is None: + raise ValueError("DEBUG_ELF_ROOT not set; call fap-set-debug-elf-root") return ( - posixpath.join(elf_path, self.debug_link_elf) - if elf_path + posixpath.join(self.DEBUG_ELF_ROOT, self.debug_link_elf) + if self.DEBUG_ELF_ROOT else self.debug_link_elf ) @@ -84,7 +88,9 @@ class AppState: if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]): debug_link_data = ( gdb.selected_inferior() - .read_memory(int(app_state["debug_link_info"]["debug_link"]), debug_link_size) + .read_memory( + int(app_state["debug_link_info"]["debug_link"]), debug_link_size + ) .tobytes() ) state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data( @@ -103,6 +109,29 @@ class AppState: return state +class SetFapDebugElfRoot(gdb.Command): + """Set path to original ELF files for debug info""" + + def __init__(self): + super().__init__( + "fap-set-debug-elf-root", gdb.COMMAND_FILES, gdb.COMPLETE_FILENAME + ) + self.dont_repeat() + + def invoke(self, arg, from_tty): + AppState.DEBUG_ELF_ROOT = arg + try: + global helper + print(f"Set '{arg}' as debug info lookup path for Flipper external apps") + helper.attach_fw() + gdb.events.stop.connect(helper.handle_stop) + except gdb.error as e: + print(f"Support for Flipper external apps debug is not available: {e}") + + +SetFapDebugElfRoot() + + class FlipperAppDebugHelper: def __init__(self): self.app_ptr = None @@ -149,9 +178,4 @@ class FlipperAppDebugHelper: helper = FlipperAppDebugHelper() -try: - helper.attach_fw() - print("Support for Flipper external apps debug is enabled") - gdb.events.stop.connect(helper.handle_stop) -except gdb.error as e: - print(f"Support for Flipper external apps debug is not available: {e}") +print("Support for Flipper external apps debug is loaded") diff --git a/documentation/.gitignore b/documentation/.gitignore new file mode 100644 index 000000000..c18ff03bb --- /dev/null +++ b/documentation/.gitignore @@ -0,0 +1,2 @@ +/html +/latex \ No newline at end of file diff --git a/documentation/AppManifests.md b/documentation/AppManifests.md index f4814ee5d..d70a12f9c 100644 --- a/documentation/AppManifests.md +++ b/documentation/AppManifests.md @@ -40,6 +40,7 @@ Only 2 parameters are mandatory: ***appid*** and ***apptype***, others are optio * **icon**: Animated icon name from built-in assets to be used when building app as a part of firmware. * **order**: Order of an application within its group when sorting entries in it. The lower the order is, the closer to the start of the list the item is placed. *Used for ordering startup hooks and menu entries.* * **sdk_headers**: List of C header files from this app's code to include in API definitions for external applications. +* **targets**: list of strings, target names, which this application is compatible with. If not specified, application is built for all targets. Default value is `["all"]`. #### Parameters for external applications diff --git a/documentation/Doxyfile b/documentation/Doxyfile index 6d6bb8aa8..1824e5a52 100644 --- a/documentation/Doxyfile +++ b/documentation/Doxyfile @@ -872,12 +872,9 @@ WARN_LOGFILE = # Note: If this tag is empty the current directory is searched. INPUT = applications \ - core \ - lib/infrared \ - lib/subghz \ - lib/toolbox \ - lib/onewire \ - firmware/targets/furi_hal_include + lib \ + firmware \ + furi # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -930,7 +927,18 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = \ + lib/mlib \ + lib/STM32CubeWB \ + lib/littlefs \ + lib/nanopb \ + assets/protobuf \ + lib/libusb_stm32 \ + lib/FreeRTOS-Kernel \ + lib/microtar \ + lib/mbedtls \ + lib/cxxheaderparser \ + applications/plugins/dap_link/lib/free-dap # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/documentation/KeyCombo.md b/documentation/KeyCombo.md index 359fd5b9b..e6f55dc52 100644 --- a/documentation/KeyCombo.md +++ b/documentation/KeyCombo.md @@ -1,7 +1,7 @@ # Key Combos -There are times when your flipper feels blue and don't respond to your commands. -In that case you may find this guide useful. +There are times when your flipper feels blue and doesn't respond to your commands. +In that case, you may find this guide useful. ## Basic Combos @@ -9,7 +9,7 @@ In that case you may find this guide useful. ### Hardware Reset -- Press `LEFT` and `BACK` and hold for couple seconds +- Press `LEFT` and `BACK` and hold for a couple of seconds - Release `LEFT` and `BACK` This combo performs hardware reset by pulling MCU reset line down. @@ -29,7 +29,7 @@ There is 1 case when it's not working: - If you have not disconnected USB, then disconnect USB and repeat previous step - Release `BACK` key -This combo performs reset by switching SYS power line off and then on. +This combo performs a reset by switching SYS power line off and then on. Main components involved: Keys -> DD6(bq25896, charger) There is 1 case when it's not working: @@ -60,13 +60,13 @@ There is 1 case when it's not working: ### Hardware Reset + Software DFU -- Press `LEFT` and `BACK` and hold for couple seconds +- Press `LEFT` and `BACK` and hold for a couple of seconds - Release `BACK` - Device will enter DFU with indication (Blue LED + DFU Screen) - Release `LEFT` This combo performs hardware reset by pulling MCU reset line down. -Then `LEFT` key indicates to boot-loader that DFU mode requested. +Then `LEFT` key indicates to boot-loader that DFU mode is requested. There are 2 cases when it's not working: @@ -76,7 +76,7 @@ There are 2 cases when it's not working: ### Hardware Reset + Hardware DFU -- Press `LEFT` and `BACK` and `OK` and hold for couple seconds +- Press `LEFT` and `BACK` and `OK` and hold for a couple of seconds - Release `BACK` and `LEFT` - Device will enter DFU without indication @@ -127,8 +127,8 @@ There are 2 cases when it's not working: If none of the described methods were useful: -- Ensure battery charged -- Disconnect battery and connect again (Requires disassembly) -- Try to Flash device with ST-Link or other programmer that support SWD +- Ensure the battery charged +- Disconnect the battery and connect again (Requires disassembly) +- Try to Flash device with ST-Link or other programmer that supports SWD -If you still here and your device is not working: it's not software issue. +If you still here and your device is not working: it's not a software issue. diff --git a/documentation/UnitTests.md b/documentation/UnitTests.md new file mode 100644 index 000000000..896426567 --- /dev/null +++ b/documentation/UnitTests.md @@ -0,0 +1,49 @@ +# Unit tests +## Intro +Unit tests are special pieces of code that apply known inputs to the feature code and check the results to see if they were correct. +They are crucial for writing robust, bug-free code. + +Flipper Zero firmware includes a separate application called [unit_tests](/applications/debug/unit_tests). +It is run directly on the Flipper Zero in order to employ its hardware features and to rule out any platform-related differences. + +When contributing code to the Flipper Zero firmware, it is highly desirable to supply unit tests along with the proposed features. +Running existing unit tests is useful to ensure that the new code doesn't introduce any regressions. + +## Running unit tests +In order to run the unit tests, follow these steps: +1. Compile the firmware with the tests enabled: `./fbt FIRMWARE_APP_SET=unit_tests`. +2. Flash the firmware using your preferred method. +3. Copy the [assets/unit_tests](assets/unit_tests) folder to the root of your Flipper Zero's SD card. +4. Launch the CLI session and run the `unit_tests` command. + +**NOTE:** To run a particular test (and skip all others), specify its name as the command argument. +See [test_index.c](applications/debug/unit_tests/test_index.c) for the complete list of test names. + +## Adding unit tests +### General +#### Entry point +The common entry point for all tests is the [unit_tests](applications/debug/unit_tests) application. Test-specific code is placed into an arbitrarily named subdirectory and is then called from the [test_index.c](applications/debug/unit_tests/test_index.c) source file. +#### Test assets +Some unit tests require external data in order to function. These files (commonly called assets) reside in the [assets/unit_tests](/assets/unit_tests) directory in their respective subdirectories. Asset files can be of any type (plain text, FlipperFormat(FFF), binary, etc). +### Application-specific +#### Infrared +Each infrared protocol has a corresponding set of unit tests, so it makes sense to implement one when adding support for a new protocol. +In order to add unit tests for your protocol, follow these steps: +1. Create a file named `test_.irtest` in the [assets](assets/unit_tests/infrared) directory. +2. Fill it with the test data (more on it below). +3. Add the test code to [infrared_test.c](applications/debug/unit_tests/infrared/infrared_test.c). +4. Update the [assets](assets/unit_tests/infrared) on your Flipper Zero and run the tests to see if they pass. + +##### Test data format +Each unit test has 3 sections: +1. `decoder` - takes in raw signal and outputs decoded messages. +2. `encoder` - takes in decoded messages and outputs a raw signal. +3. `encoder_decoder` - takes in decoded messages, turns them into a raw signal, and then decodes again. + +Infrared test asset files have an `.irtest` extension and are regular `.ir` files with a few additions. +Decoder input data has signal names `decoder_input_N`, where N is a test sequence number. Expected data goes under the name `decoder_expected_N`. When testing the encoder these two are switched. + +Decoded data is represented in arrays (since a single raw signal may decode to several messages). If there is only one signal, then it has to be an array of size 1. Use the existing files as syntax examples. + +##### Getting raw signals +Recording raw IR signals are possible using the Flipper Zero. Launch the CLI session, run `ir rx raw`, then point the remote towards Flipper's receiver and send the signals. The raw signal data will be printed to the console in a convenient format. diff --git a/documentation/UniversalRemotes.md b/documentation/UniversalRemotes.md index 0a833727e..186a0e65a 100644 --- a/documentation/UniversalRemotes.md +++ b/documentation/UniversalRemotes.md @@ -1,7 +1,19 @@ # Universal Remotes +## Televisions +Adding your TV set to the universal remote is quite straightforward. Up to 6 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`, `Ch_next`, `Ch_prev`. Any of them can be omitted if not supported by the TV. + +Each signal is recorded using the following algorithm: +1. Get the remote and point it to Flipper's IR receiver. +2. Start learning a new remote if it's the first button or press `+` to add a new button otherwise. +3. Press a remote button and save it under a corresponding name. +4. Repeat steps 2-3 until all required signals are saved. + +The signal names are self-explanatory. Don't forget to make sure that every recorded signal does what it's supposed to. + +If everything checks out, append these signals **to the end** of the [TV universal remote file](/assets/resources/infrared/assets/tv.ir). + ## Audio Players -### Recording signals -Adding your audio player to the universal remote is quite straightforward. 8 signals can be recorded: `Power`, `Play`, `Pause`, `Vol_up`, `Vol_dn`, `Next`, `Prev`, `Mute`. Any of them can be omitted if it is not supported by the device. +Adding your audio player to the universal remote is done in the same manner as described above. Up to 8 signals can be recorded: `Power`, `Play`, `Pause`, `Vol_up`, `Vol_dn`, `Next`, `Prev`, `Mute`. Any of them can be omitted if not supported by the player. The signal names are self-explanatory. On many remotes, the `Play` button doubles as `Pause`. In this case record it as `Play` omitting the `Pause`. @@ -9,12 +21,7 @@ Make sure that every signal does what it's supposed to. If everything checks out, append these signals **to the end** of the [Audio players universal remote file](/assets/resources/infrared/assets/audio.ir). -The order of signals is not important, but they must be preceded by a following comment: `# Model: ` in order to keep the library organised. - -When done, open a pull request containing the changed file. - ## Air Conditioners -### Recording signals Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote. The majority of A/C remotes have a small display which shows current mode, temperature and other settings. When the user presses a button, a whole set of parameters is transmitted to the device, which must be recorded and used as a whole. @@ -49,6 +56,7 @@ Test the file against the actual device. Make sure that every signal does what i If everything checks out, append these signals **to the end** of the [A/C universal remote file](/assets/resources/infrared/assets/ac.ir). +## Final steps The order of signals is not important, but they must be preceded by a following comment: `# Model: ` in order to keep the library organised. When done, open a pull request containing the changed file. diff --git a/documentation/fbt.md b/documentation/fbt.md index 2bf9ea28e..4726268d0 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -44,7 +44,7 @@ To run cleanup (think of `make clean`) for specified targets, add `-c` option. - `fw_dist` - build & publish firmware to `dist` folder. This is a default target, when no other are specified - `fap_dist` - build external plugins & publish to `dist` folder -- `updater_package`, `updater_minpackage` - build self-update package. Minimal version only inclues firmware's DFU file; full version also includes radio stack & resources for SD card +- `updater_package`, `updater_minpackage` - build self-update package. Minimal version only includes firmware's DFU file; full version also includes radio stack & resources for SD card - `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper - `flash` - flash attached device with OpenOCD over ST-Link - `flash_usb`, `flash_usb_full` - build, upload and install update package to device over USB. See details on `updater_package`, `updater_minpackage` diff --git a/fbt_options.py b/fbt_options.py index 6ef9759e3..7a805c996 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -32,7 +32,7 @@ COPRO_STACK_TYPE = "ble_light" # Leave 0 to let scripts automatically calculate it COPRO_STACK_ADDR = "0x0" -# If you override COPRO_CUBE_DIR on commandline, override this aswell +# If you override COPRO_CUBE_DIR on commandline, override this as well COPRO_STACK_BIN_DIR = posixpath.join( COPRO_CUBE_DIR, "Projects", @@ -49,12 +49,12 @@ OPENOCD_OPTS = [ "-c", "transport select hla_swd", "-f", - "debug/stm32wbx.cfg", + "${FBT_DEBUG_DIR}/stm32wbx.cfg", "-c", "stm32wbx.cpu configure -rtos auto", ] -SVD_FILE = "debug/STM32WB55_CM4.svd" +SVD_FILE = "${FBT_DEBUG_DIR}/STM32WB55_CM4.svd" # Look for blackmagic probe on serial ports and local network BLACKMAGIC = "auto" @@ -81,6 +81,7 @@ FIRMWARE_APPS = { "basic_services", "updater_app", "unit_tests", + "nfc", ], } diff --git a/firmware.scons b/firmware.scons index a0c1ab339..d674bf160 100644 --- a/firmware.scons +++ b/firmware.scons @@ -20,10 +20,9 @@ env = ENV.Clone( BUILD_DIR=fw_build_meta["build_dir"], IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware", FW_FLAVOR=fw_build_meta["flavor"], - PLUGIN_ELF_DIR="${BUILD_DIR}", - LIB_DIST_DIR="${BUILD_DIR}/lib", + LIB_DIST_DIR=fw_build_meta["build_dir"].Dir("lib"), LINT_SOURCES=[ - "applications", + Dir("applications"), ], LIBPATH=[ "${LIB_DIST_DIR}", @@ -41,11 +40,11 @@ env = ENV.Clone( FW_LIB_OPTS={ "Default": { "CCFLAGS": [ - "-Os", + "-Og" if ENV["LIB_DEBUG"] else "-Os", ], "CPPDEFINES": [ "NDEBUG", - "FURI_NDEBUG", + "FURI_DEBUG" if ENV["LIB_DEBUG"] else "FURI_NDEBUG", ], # You can add other entries named after libraries # If they are present, they have precedence over Default @@ -142,12 +141,14 @@ for app_dir, _ in env["APPDIRS"]: fwenv.PrepareApplicationsBuild() -# Build external apps +# Build external apps + configure SDK if env["IS_BASE_FIRMWARE"]: - extapps = fwenv["FW_EXTAPPS"] = SConscript( - "site_scons/extapps.scons", exports={"ENV": fwenv} + fwenv.SetDefault(FBT_FAP_DEBUG_ELF_ROOT="${BUILD_DIR}/.extapps") + fwenv["FW_EXTAPPS"] = SConscript( + "site_scons/extapps.scons", + exports={"ENV": fwenv}, ) - fw_artifacts.append(extapps["sdk_tree"]) + fw_artifacts.append(fwenv["FW_EXTAPPS"].sdk_tree) # Add preprocessor definitions for current set of apps @@ -263,7 +264,10 @@ fw_artifacts.extend( fwcdb = fwenv.CompilationDatabase() # without filtering, both updater & firmware commands would be generated in same file -fwenv.Replace(COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*")) +fwenv.Replace( + COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*"), + COMPILATIONDB_SRCPATH_FILTER="*.c*", +) AlwaysBuild(fwcdb) Precious(fwcdb) NoClean(fwcdb) diff --git a/firmware/SConscript b/firmware/SConscript index 19dde2e4c..a16f14e65 100644 --- a/firmware/SConscript +++ b/firmware/SConscript @@ -1,7 +1,7 @@ Import("env") env.Append( - LINT_SOURCES=["firmware"], + LINT_SOURCES=[Dir(".")], SDK_HEADERS=[ *env.GlobRecursive("*.h", "targets/furi_hal_include", "*_i.h"), *env.GlobRecursive("*.h", "targets/f${TARGET_HW}/furi_hal", "*_i.h"), diff --git a/firmware/targets/f7/Src/main.c b/firmware/targets/f7/Src/main.c index 5f33569ae..d9a2221a2 100644 --- a/firmware/targets/f7/Src/main.c +++ b/firmware/targets/f7/Src/main.c @@ -26,10 +26,7 @@ int main() { // Flipper critical FURI HAL furi_hal_init_early(); - FuriThread* main_thread = furi_thread_alloc(); - furi_thread_set_name(main_thread, "Init"); - furi_thread_set_stack_size(main_thread, 4096); - furi_thread_set_callback(main_thread, init_task); + FuriThread* main_thread = furi_thread_alloc_ex("Init", 4096, init_task, NULL); #ifdef FURI_RAM_EXEC furi_thread_start(main_thread); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3d9d19122..a0ea8e7b2 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,+,7.3,, +Version,+,10.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -133,6 +133,7 @@ Header,+,lib/libusb_stm32/inc/hid_usage_simulation.h,, Header,+,lib/libusb_stm32/inc/hid_usage_sport.h,, Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,, Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,, +Header,-,lib/libusb_stm32/inc/stm32_compat.h,, Header,+,lib/libusb_stm32/inc/usb.h,, Header,+,lib/libusb_stm32/inc/usb_cdc.h,, Header,+,lib/libusb_stm32/inc/usb_cdca.h,, @@ -805,21 +806,22 @@ Function,-,fgetpos,int,"FILE*, fpos_t*" Function,-,fgets,char*,"char*, int, FILE*" Function,-,fgets_unlocked,char*,"char*, int, FILE*" Function,+,file_browser_alloc,FileBrowser*,FuriString* -Function,+,file_browser_configure,void,"FileBrowser*, const char*, _Bool, const Icon*, _Bool" +Function,+,file_browser_configure,void,"FileBrowser*, const char*, const char*, _Bool, _Bool, const Icon*, _Bool" Function,+,file_browser_free,void,FileBrowser* Function,+,file_browser_get_view,View*,FileBrowser* Function,+,file_browser_set_callback,void,"FileBrowser*, FileBrowserCallback, void*" Function,+,file_browser_set_item_callback,void,"FileBrowser*, FileBrowserLoadItemCallback, void*" Function,+,file_browser_start,void,"FileBrowser*, FuriString*" Function,+,file_browser_stop,void,FileBrowser* -Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, _Bool" +Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, const char*, _Bool, _Bool" Function,+,file_browser_worker_folder_enter,void,"BrowserWorker*, FuriString*, int32_t" Function,+,file_browser_worker_folder_exit,void,BrowserWorker* Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, int32_t" Function,+,file_browser_worker_free,void,BrowserWorker* +Function,+,file_browser_worker_is_in_start_folder,_Bool,BrowserWorker* Function,+,file_browser_worker_load,void,"BrowserWorker*, uint32_t, uint32_t" Function,+,file_browser_worker_set_callback_context,void,"BrowserWorker*, void*" -Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool" +Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool, _Bool" Function,+,file_browser_worker_set_folder_callback,void,"BrowserWorker*, BrowserWorkerFolderOpenCallback" Function,+,file_browser_worker_set_item_callback,void,"BrowserWorker*, BrowserWorkerListItemCallback" Function,+,file_browser_worker_set_list_callback,void,"BrowserWorker*, BrowserWorkerListLoadCallback" @@ -1119,7 +1121,7 @@ Function,+,furi_hal_ibutton_start_drive_in_isr,void, Function,+,furi_hal_ibutton_start_interrupt,void, Function,+,furi_hal_ibutton_start_interrupt_in_isr,void, Function,+,furi_hal_ibutton_stop,void, -Function,+,furi_hal_info_get,void,"FuriHalInfoValueCallback, void*" +Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*" Function,+,furi_hal_infrared_async_rx_set_capture_isr_callback,void,"FuriHalInfraredRxCaptureCallback, void*" Function,+,furi_hal_infrared_async_rx_set_timeout,void,uint32_t Function,+,furi_hal_infrared_async_rx_set_timeout_isr_callback,void,"FuriHalInfraredRxTimeoutCallback, void*" @@ -1184,14 +1186,15 @@ Function,+,furi_hal_nfc_tx_rx_full,_Bool,FuriHalNfcTxRxContext* Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, Function,+,furi_hal_power_check_otg_status,void, +Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" Function,+,furi_hal_power_deep_sleep_available,_Bool, Function,+,furi_hal_power_disable_external_3_3v,void, Function,+,furi_hal_power_disable_otg,void, -Function,+,furi_hal_power_dump_state,void, Function,+,furi_hal_power_enable_external_3_3v,void, Function,+,furi_hal_power_enable_otg,void, Function,+,furi_hal_power_gauge_is_ok,_Bool, Function,+,furi_hal_power_get_bat_health_pct,uint8_t, +Function,+,furi_hal_power_get_battery_charging_voltage,float, Function,+,furi_hal_power_get_battery_current,float,FuriHalPowerIC Function,+,furi_hal_power_get_battery_design_capacity,uint32_t, Function,+,furi_hal_power_get_battery_full_capacity,uint32_t, @@ -1200,7 +1203,7 @@ Function,+,furi_hal_power_get_battery_temperature,float,FuriHalPowerIC Function,+,furi_hal_power_get_battery_voltage,float,FuriHalPowerIC Function,+,furi_hal_power_get_pct,uint8_t, Function,+,furi_hal_power_get_usb_voltage,float, -Function,+,furi_hal_power_info_get,void,"FuriHalPowerInfoCallback, void*" +Function,+,furi_hal_power_info_get,void,"PropertyValueCallback, char, void*" Function,-,furi_hal_power_init,void, Function,+,furi_hal_power_insomnia_enter,void, Function,+,furi_hal_power_insomnia_exit,void, @@ -1210,6 +1213,7 @@ Function,+,furi_hal_power_is_charging_done,_Bool, Function,+,furi_hal_power_is_otg_enabled,_Bool, Function,+,furi_hal_power_off,void, Function,+,furi_hal_power_reset,void, +Function,+,furi_hal_power_set_battery_charging_voltage,void,float Function,+,furi_hal_power_shutdown,void, Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, @@ -1260,6 +1264,7 @@ Function,+,furi_hal_rtc_deinit_early,void, Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode, Function,+,furi_hal_rtc_get_datetime,void,FuriHalRtcDateTime* Function,+,furi_hal_rtc_get_fault_data,uint32_t, +Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, Function,+,furi_hal_rtc_get_log_level,uint8_t, Function,+,furi_hal_rtc_get_pin_fails,uint32_t, Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister @@ -1272,6 +1277,7 @@ Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* Function,+,furi_hal_rtc_set_fault_data,void,uint32_t Function,+,furi_hal_rtc_set_flag,void,FuriHalRtcFlag +Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" @@ -1469,6 +1475,7 @@ Function,+,furi_string_utf8_length,size_t,FuriString* Function,+,furi_string_utf8_push,void,"FuriString*, FuriStringUnicodeValue" Function,+,furi_string_vprintf,int,"FuriString*, const char[], va_list" Function,+,furi_thread_alloc,FuriThread*, +Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*" Function,+,furi_thread_catch,void, Function,-,furi_thread_disable_heap_trace,void,FuriThread* Function,+,furi_thread_enable_heap_trace,void,FuriThread* @@ -1844,6 +1851,7 @@ Function,-,mf_classic_get_read_sectors_and_keys,void,"MfClassicData*, uint8_t*, Function,-,mf_classic_get_sector_by_block,uint8_t,uint8_t Function,-,mf_classic_get_sector_trailer_block_num_by_sector,uint8_t,uint8_t Function,-,mf_classic_get_sector_trailer_by_sector,MfClassicSectorTrailer*,"MfClassicData*, uint8_t" +Function,-,mf_classic_get_total_block_num,uint16_t,MfClassicType Function,-,mf_classic_get_total_sectors_num,uint8_t,MfClassicType Function,-,mf_classic_get_type_str,const char*,MfClassicType Function,-,mf_classic_is_allowed_access_data_block,_Bool,"MfClassicData*, uint8_t, MfClassicKey, MfClassicAction" @@ -1896,6 +1904,7 @@ Function,-,mf_df_prepare_read_records,uint16_t,"uint8_t*, uint8_t, uint32_t, uin Function,-,mf_df_prepare_select_application,uint16_t,"uint8_t*, uint8_t[3]" Function,-,mf_df_read_card,_Bool,"FuriHalNfcTxRxContext*, MifareDesfireData*" Function,-,mf_ul_check_card_type,_Bool,"uint8_t, uint8_t, uint8_t" +Function,-,mf_ul_is_full_capture,_Bool,MfUltralightData* Function,-,mf_ul_prepare_emulation,void,"MfUltralightEmulator*, MfUltralightData*" Function,-,mf_ul_prepare_emulation_response,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*, uint32_t*, void*" Function,-,mf_ul_pwdgen_amiibo,uint32_t,FuriHalNfcDevData* @@ -2034,6 +2043,7 @@ Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" Function,-,printf,int,"const char*, ..." Function,-,prng_successor,uint32_t,"uint32_t, uint32_t" +Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t" Function,+,protocol_dict_decoders_feed,ProtocolId,"ProtocolDict*, _Bool, uint32_t" Function,+,protocol_dict_decoders_feed_by_feature,ProtocolId,"ProtocolDict*, uint32_t, _Bool, uint32_t" @@ -2279,10 +2289,15 @@ Function,+,rpc_session_set_context,void,"RpcSession*, void*" Function,+,rpc_session_set_send_bytes_callback,void,"RpcSession*, RpcSendBytesCallback" Function,+,rpc_session_set_terminated_callback,void,"RpcSession*, RpcSessionTerminatedCallback" Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, RpcAppSystemEvent, _Bool" +Function,+,rpc_system_app_error_reset,void,RpcAppSystem* +Function,+,rpc_system_app_exchange_data,void,"RpcAppSystem*, const uint8_t*, size_t" Function,+,rpc_system_app_get_data,const char*,RpcAppSystem* Function,+,rpc_system_app_send_exited,void,RpcAppSystem* Function,+,rpc_system_app_send_started,void,RpcAppSystem* Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback, void*" +Function,+,rpc_system_app_set_data_exchange_callback,void,"RpcAppSystem*, RpcAppSystemDataExchangeCallback, void*" +Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t" +Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*" Function,-,rpmatch,int,const char* Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 3cf02009f..30be3c7ce 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -40,11 +40,7 @@ bool ble_app_init() { ble_app->hci_mtx = furi_mutex_alloc(FuriMutexTypeNormal); ble_app->hci_sem = furi_semaphore_alloc(1, 0); // HCI transport layer thread to handle user asynch events - ble_app->thread = furi_thread_alloc(); - furi_thread_set_name(ble_app->thread, "BleHciDriver"); - furi_thread_set_stack_size(ble_app->thread, 1024); - furi_thread_set_context(ble_app->thread, ble_app); - furi_thread_set_callback(ble_app->thread, ble_app_hci_thread); + ble_app->thread = furi_thread_alloc_ex("BleHciDriver", 1024, ble_app_hci_thread, ble_app); furi_thread_start(ble_app->thread); // Initialize Ble Transport Layer diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index 87af5f2a8..b3752f17f 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -78,11 +78,7 @@ void ble_glue_init() { ble_glue->shci_sem = furi_semaphore_alloc(1, 0); // FreeRTOS system task creation - ble_glue->thread = furi_thread_alloc(); - furi_thread_set_name(ble_glue->thread, "BleShciDriver"); - furi_thread_set_stack_size(ble_glue->thread, 1024); - furi_thread_set_context(ble_glue->thread, ble_glue); - furi_thread_set_callback(ble_glue->thread, ble_glue_shci_thread); + ble_glue->thread = furi_thread_alloc_ex("BleShciDriver", 1024, ble_glue_shci_thread, ble_glue); furi_thread_start(ble_glue->thread); // System channel initialization diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index aa8cd2c90..3e29527ec 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -179,7 +179,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { case EVT_BLUE_GAP_PASS_KEY_REQUEST: { // Generate random PIN code - uint32_t pin = rand() % 999999; + uint32_t pin = rand() % 999999; //-V1064 aci_gap_pass_key_resp(gap->service.connection_handle, pin); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { FURI_LOG_I(TAG, "Pass key request event. Pin: ******"); @@ -478,7 +478,6 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { gap = malloc(sizeof(Gap)); gap->config = config; - srand(DWT->CYCCNT); // Create advertising timer gap->advertise_timer = furi_timer_alloc(gap_advetise_timer_callback, FuriTimerTypeOnce, NULL); // Initialization of GATT & GAP layer @@ -493,11 +492,7 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { gap->enable_adv = true; // Thread configuration - gap->thread = furi_thread_alloc(); - furi_thread_set_name(gap->thread, "BleGapDriver"); - furi_thread_set_stack_size(gap->thread, 1024); - furi_thread_set_context(gap->thread, gap); - furi_thread_set_callback(gap->thread, gap_app); + gap->thread = furi_thread_alloc_ex("BleGapDriver", 1024, gap_app, gap); furi_thread_start(gap->thread); // Command queue allocation diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index 141efdb60..ec82c377a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -61,26 +61,25 @@ void furi_hal_init() { furi_hal_crypto_init(); - // USB -#ifndef FURI_RAM_EXEC - furi_hal_usb_init(); - FURI_LOG_I(TAG, "USB OK"); -#endif - furi_hal_i2c_init(); // High Level furi_hal_power_init(); furi_hal_light_init(); + + furi_hal_bt_init(); + furi_hal_memory_init(); + furi_hal_compress_icon_init(); + #ifndef FURI_RAM_EXEC + // USB + furi_hal_usb_init(); + FURI_LOG_I(TAG, "USB OK"); furi_hal_vibro_init(); furi_hal_subghz_init(); furi_hal_nfc_init(); furi_hal_rfid_init(); #endif - furi_hal_bt_init(); - furi_hal_memory_init(); - furi_hal_compress_icon_init(); // FatFS driver initialization MX_FATFS_Init(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_crypto.c b/firmware/targets/f7/furi_hal/furi_hal_crypto.c index dbd8c58c2..e0ed3ab9b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_crypto.c +++ b/firmware/targets/f7/furi_hal/furi_hal_crypto.c @@ -91,7 +91,7 @@ bool furi_hal_crypto_verify_key(uint8_t key_slot) { uint8_t keys_nb = 0; uint8_t valid_keys_nb = 0; uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS; - uint8_t empty_iv[16]; + uint8_t empty_iv[16] = {0}; furi_hal_crypto_verify_enclave(&keys_nb, &valid_keys_nb); if(key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key if(key_slot > keys_nb) return false; diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index 15ce41a20..c984ef4d5 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -8,16 +8,25 @@ #include #include -void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) { - FuriString* value; - value = furi_string_alloc(); +void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { + FuriString* key = furi_string_alloc(); + FuriString* value = furi_string_alloc(); + + PropertyValueContext property_context = { + .key = key, .value = value, .out = out, .sep = sep, .last = false, .context = context}; // Device Info version - out("device_info_major", "2", false, context); - out("device_info_minor", "0", false, context); + if(sep == '.') { + property_value_out(&property_context, NULL, 2, "format", "major", "3"); + property_value_out(&property_context, NULL, 2, "format", "minor", "0"); + } else { + property_value_out(&property_context, NULL, 3, "device", "info", "major", "2"); + property_value_out(&property_context, NULL, 3, "device", "info", "minor", "0"); + } // Model name - out("hardware_model", furi_hal_version_get_model_name(), false, context); + property_value_out( + &property_context, NULL, 2, "hardware", "model", furi_hal_version_get_model_name()); // Unique ID furi_string_reset(value); @@ -25,93 +34,211 @@ void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) { for(size_t i = 0; i < furi_hal_version_uid_size(); i++) { furi_string_cat_printf(value, "%02X", uid[i]); } - out("hardware_uid", furi_string_get_cstr(value), false, context); + property_value_out(&property_context, NULL, 2, "hardware", "uid", furi_string_get_cstr(value)); // OTP Revision - furi_string_printf(value, "%d", furi_hal_version_get_otp_version()); - out("hardware_otp_ver", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%lu", furi_hal_version_get_hw_timestamp()); - out("hardware_timestamp", furi_string_get_cstr(value), false, context); + property_value_out( + &property_context, "%d", 3, "hardware", "otp", "ver", furi_hal_version_get_otp_version()); + property_value_out( + &property_context, "%lu", 2, "hardware", "timestamp", furi_hal_version_get_hw_timestamp()); // Board Revision - furi_string_printf(value, "%d", furi_hal_version_get_hw_version()); - out("hardware_ver", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%d", furi_hal_version_get_hw_target()); - out("hardware_target", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%d", furi_hal_version_get_hw_body()); - out("hardware_body", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%d", furi_hal_version_get_hw_connect()); - out("hardware_connect", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%d", furi_hal_version_get_hw_display()); - out("hardware_display", furi_string_get_cstr(value), false, context); + property_value_out( + &property_context, "%d", 2, "hardware", "ver", furi_hal_version_get_hw_version()); + property_value_out( + &property_context, "%d", 2, "hardware", "target", furi_hal_version_get_hw_target()); + property_value_out( + &property_context, "%d", 2, "hardware", "body", furi_hal_version_get_hw_body()); + property_value_out( + &property_context, "%d", 2, "hardware", "connect", furi_hal_version_get_hw_connect()); + property_value_out( + &property_context, "%d", 2, "hardware", "display", furi_hal_version_get_hw_display()); // Board Personification - furi_string_printf(value, "%d", furi_hal_version_get_hw_color()); - out("hardware_color", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%d", furi_hal_version_get_hw_region()); - out("hardware_region", furi_string_get_cstr(value), false, context); - out("hardware_region_provisioned", furi_hal_region_get_name(), false, context); + property_value_out( + &property_context, "%d", 2, "hardware", "color", furi_hal_version_get_hw_color()); + + if(sep == '.') { + property_value_out( + &property_context, + "%d", + 3, + "hardware", + "region", + "builtin", + furi_hal_version_get_hw_region()); + } else { + property_value_out( + &property_context, "%d", 2, "hardware", "region", furi_hal_version_get_hw_region()); + } + + property_value_out( + &property_context, + NULL, + 3, + "hardware", + "region", + "provisioned", + furi_hal_region_get_name()); + const char* name = furi_hal_version_get_name_ptr(); if(name) { - out("hardware_name", name, false, context); + property_value_out(&property_context, NULL, 2, "hardware", "name", name); } // Firmware version const Version* firmware_version = furi_hal_version_get_firmware_version(); if(firmware_version) { - out("firmware_commit", version_get_githash(firmware_version), false, context); - out("firmware_commit_dirty", - version_get_dirty_flag(firmware_version) ? "true" : "false", - false, - context); - out("firmware_branch", version_get_gitbranch(firmware_version), false, context); - out("firmware_branch_num", version_get_gitbranchnum(firmware_version), false, context); - out("firmware_version", version_get_version(firmware_version), false, context); - out("firmware_build_date", version_get_builddate(firmware_version), false, context); - furi_string_printf(value, "%d", version_get_target(firmware_version)); - out("firmware_target", furi_string_get_cstr(value), false, context); + if(sep == '.') { + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "commit", + "hash", + version_get_githash(firmware_version)); + } else { + property_value_out( + &property_context, + NULL, + 2, + "firmware", + "commit", + version_get_githash(firmware_version)); + } + + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "commit", + "dirty", + version_get_dirty_flag(firmware_version) ? "true" : "false"); + + if(sep == '.') { + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "branch", + "name", + version_get_gitbranch(firmware_version)); + } else { + property_value_out( + &property_context, + NULL, + 2, + "firmware", + "branch", + version_get_gitbranch(firmware_version)); + } + + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "branch", + "num", + version_get_gitbranchnum(firmware_version)); + property_value_out( + &property_context, + NULL, + 2, + "firmware", + "version", + version_get_version(firmware_version)); + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "build", + "date", + version_get_builddate(firmware_version)); + property_value_out( + &property_context, "%d", 2, "firmware", "target", version_get_target(firmware_version)); } if(furi_hal_bt_is_alive()) { const BleGlueC2Info* ble_c2_info = ble_glue_get_c2_info(); - out("radio_alive", "true", false, context); - out("radio_mode", ble_c2_info->mode == BleGlueC2ModeFUS ? "FUS" : "Stack", false, context); + property_value_out(&property_context, NULL, 2, "radio", "alive", "true"); + property_value_out( + &property_context, + NULL, + 2, + "radio", + "mode", + ble_c2_info->mode == BleGlueC2ModeFUS ? "FUS" : "Stack"); // FUS Info - furi_string_printf(value, "%d", ble_c2_info->FusVersionMajor); - out("radio_fus_major", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%d", ble_c2_info->FusVersionMinor); - out("radio_fus_minor", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%d", ble_c2_info->FusVersionSub); - out("radio_fus_sub", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%dK", ble_c2_info->FusMemorySizeSram2B); - out("radio_fus_sram2b", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%dK", ble_c2_info->FusMemorySizeSram2A); - out("radio_fus_sram2a", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%dK", ble_c2_info->FusMemorySizeFlash * 4); - out("radio_fus_flash", furi_string_get_cstr(value), false, context); + property_value_out( + &property_context, "%d", 3, "radio", "fus", "major", ble_c2_info->FusVersionMajor); + property_value_out( + &property_context, "%d", 3, "radio", "fus", "minor", ble_c2_info->FusVersionMinor); + property_value_out( + &property_context, "%d", 3, "radio", "fus", "sub", ble_c2_info->FusVersionSub); + property_value_out( + &property_context, + "%dK", + 3, + "radio", + "fus", + "sram2b", + ble_c2_info->FusMemorySizeSram2B); + property_value_out( + &property_context, + "%dK", + 3, + "radio", + "fus", + "sram2a", + ble_c2_info->FusMemorySizeSram2A); + property_value_out( + &property_context, + "%dK", + 3, + "radio", + "fus", + "flash", + ble_c2_info->FusMemorySizeFlash * 4); // Stack Info - furi_string_printf(value, "%d", ble_c2_info->StackType); - out("radio_stack_type", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%d", ble_c2_info->VersionMajor); - out("radio_stack_major", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%d", ble_c2_info->VersionMinor); - out("radio_stack_minor", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%d", ble_c2_info->VersionSub); - out("radio_stack_sub", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%d", ble_c2_info->VersionBranch); - out("radio_stack_branch", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%d", ble_c2_info->VersionReleaseType); - out("radio_stack_release", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%dK", ble_c2_info->MemorySizeSram2B); - out("radio_stack_sram2b", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%dK", ble_c2_info->MemorySizeSram2A); - out("radio_stack_sram2a", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%dK", ble_c2_info->MemorySizeSram1); - out("radio_stack_sram1", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%dK", ble_c2_info->MemorySizeFlash * 4); - out("radio_stack_flash", furi_string_get_cstr(value), false, context); + property_value_out( + &property_context, "%d", 3, "radio", "stack", "type", ble_c2_info->StackType); + property_value_out( + &property_context, "%d", 3, "radio", "stack", "major", ble_c2_info->VersionMajor); + property_value_out( + &property_context, "%d", 3, "radio", "stack", "minor", ble_c2_info->VersionMinor); + property_value_out( + &property_context, "%d", 3, "radio", "stack", "sub", ble_c2_info->VersionSub); + property_value_out( + &property_context, "%d", 3, "radio", "stack", "branch", ble_c2_info->VersionBranch); + property_value_out( + &property_context, + "%d", + 3, + "radio", + "stack", + "release", + ble_c2_info->VersionReleaseType); + property_value_out( + &property_context, "%dK", 3, "radio", "stack", "sram2b", ble_c2_info->MemorySizeSram2B); + property_value_out( + &property_context, "%dK", 3, "radio", "stack", "sram2a", ble_c2_info->MemorySizeSram2A); + property_value_out( + &property_context, "%dK", 3, "radio", "stack", "sram1", ble_c2_info->MemorySizeSram1); + property_value_out( + &property_context, + "%dK", + 3, + "radio", + "stack", + "flash", + ble_c2_info->MemorySizeFlash * 4); // Mac address furi_string_reset(value); @@ -119,23 +246,33 @@ void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) { for(size_t i = 0; i < 6; i++) { furi_string_cat_printf(value, "%02X", ble_mac[i]); } - out("radio_ble_mac", furi_string_get_cstr(value), false, context); + property_value_out( + &property_context, NULL, 3, "radio", "ble", "mac", furi_string_get_cstr(value)); // Signature verification uint8_t enclave_keys = 0; uint8_t enclave_valid_keys = 0; bool enclave_valid = furi_hal_crypto_verify_enclave(&enclave_keys, &enclave_valid_keys); - furi_string_printf(value, "%d", enclave_valid_keys); - out("enclave_valid_keys", furi_string_get_cstr(value), false, context); - out("enclave_valid", enclave_valid ? "true" : "false", false, context); + if(sep == '.') { + property_value_out( + &property_context, "%d", 3, "enclave", "keys", "valid", enclave_valid_keys); + } else { + property_value_out( + &property_context, "%d", 3, "enclave", "valid", "keys", enclave_valid_keys); + } + + property_value_out( + &property_context, NULL, 2, "enclave", "valid", enclave_valid ? "true" : "false"); } else { - out("radio_alive", "false", false, context); + property_value_out(&property_context, NULL, 2, "radio", "alive", "false"); } - furi_string_printf(value, "%u", PROTOBUF_MAJOR_VERSION); - out("protobuf_version_major", furi_string_get_cstr(value), false, context); - furi_string_printf(value, "%u", PROTOBUF_MINOR_VERSION); - out("protobuf_version_minor", furi_string_get_cstr(value), true, context); + property_value_out( + &property_context, "%u", 3, "protobuf", "version", "major", PROTOBUF_MAJOR_VERSION); + property_context.last = true; + property_value_out( + &property_context, "%u", 3, "protobuf", "version", "minor", PROTOBUF_MINOR_VERSION); + furi_string_free(key); furi_string_free(value); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_memory.c b/firmware/targets/f7/furi_hal/furi_hal_memory.c index 9cf2c3120..ec71e6660 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_memory.c +++ b/firmware/targets/f7/furi_hal/furi_hal_memory.c @@ -81,6 +81,10 @@ void furi_hal_memory_init() { } void* furi_hal_memory_alloc(size_t size) { + if(FURI_IS_IRQ_MODE()) { + furi_crash("memmgt in ISR"); + } + if(furi_hal_memory == NULL) { return NULL; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 2d27313ae..75c695afb 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -244,6 +244,9 @@ bool furi_hal_nfc_listen( params.lmConfigPA.SEL_RES = sak; rfalNfcDiscover(¶ms); + // Disable EMD suppression. + st25r3916ModifyRegister(ST25R3916_REG_EMD_SUP_CONF, ST25R3916_REG_EMD_SUP_CONF_emd_emv, 0); + uint32_t start = DWT->CYCCNT; while(state != RFAL_NFC_STATE_ACTIVATED) { rfalNfcWorker(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 86505c573..2d709620d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -308,11 +309,13 @@ void furi_hal_power_shutdown() { void furi_hal_power_off() { // Crutch: shutting down with ext 3V3 off is causing LSE to stop furi_hal_power_enable_external_3_3v(); - furi_delay_us(1000); + furi_hal_vibro_on(true); + furi_delay_us(50000); // Send poweroff to charger furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); bq25896_poweroff(&furi_hal_i2c_handle_power); furi_hal_i2c_release(&furi_hal_i2c_handle_power); + furi_hal_vibro_on(false); } void furi_hal_power_reset() { @@ -338,6 +341,20 @@ bool furi_hal_power_is_otg_enabled() { return ret; } +float furi_hal_power_get_battery_charging_voltage() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + float ret = (float)bq25896_get_vreg_voltage(&furi_hal_i2c_handle_power) / 1000.0f; + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + +void furi_hal_power_set_battery_charging_voltage(float voltage) { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + // Adding 0.0005 is necessary because 4.016f is 4.015999794000, which gets truncated + bq25896_set_vreg_voltage(&furi_hal_i2c_handle_power, (uint16_t)(voltage * 1000.0f + 0.0005f)); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} + void furi_hal_power_check_otg_status() { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); if(bq25896_check_otg_fault(&furi_hal_i2c_handle_power)) @@ -422,76 +439,6 @@ float furi_hal_power_get_usb_voltage() { return ret; } -void furi_hal_power_dump_state() { - BatteryStatus battery_status; - OperationStatus operation_status; - - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - - if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) == BQ27220_ERROR || - bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) == - BQ27220_ERROR) { - printf("Failed to get bq27220 status. Communication error.\r\n"); - } else { - // Operation status register - printf( - "bq27220: CALMD: %d, SEC: %d, EDV2: %d, VDQ: %d, INITCOMP: %d, SMTH: %d, BTPINT: %d, CFGUPDATE: %d\r\n", - operation_status.CALMD, - operation_status.SEC, - operation_status.EDV2, - operation_status.VDQ, - operation_status.INITCOMP, - operation_status.SMTH, - operation_status.BTPINT, - operation_status.CFGUPDATE); - // Battery status register, part 1 - printf( - "bq27220: CHGINH: %d, FC: %d, OTD: %d, OTC: %d, SLEEP: %d, OCVFAIL: %d, OCVCOMP: %d, FD: %d\r\n", - battery_status.CHGINH, - battery_status.FC, - battery_status.OTD, - battery_status.OTC, - battery_status.SLEEP, - battery_status.OCVFAIL, - battery_status.OCVCOMP, - battery_status.FD); - // Battery status register, part 2 - printf( - "bq27220: DSG: %d, SYSDWN: %d, TDA: %d, BATTPRES: %d, AUTH_GD: %d, OCVGD: %d, TCA: %d, RSVD: %d\r\n", - battery_status.DSG, - battery_status.SYSDWN, - battery_status.TDA, - battery_status.BATTPRES, - battery_status.AUTH_GD, - battery_status.OCVGD, - battery_status.TCA, - battery_status.RSVD); - // Voltage and current info - printf( - "bq27220: Full capacity: %dmAh, Design capacity: %dmAh, Remaining capacity: %dmAh, State of Charge: %d%%, State of health: %d%%\r\n", - bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power), - bq27220_get_design_capacity(&furi_hal_i2c_handle_power), - bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power), - bq27220_get_state_of_charge(&furi_hal_i2c_handle_power), - bq27220_get_state_of_health(&furi_hal_i2c_handle_power)); - printf( - "bq27220: Voltage: %dmV, Current: %dmA, Temperature: %dC\r\n", - bq27220_get_voltage(&furi_hal_i2c_handle_power), - bq27220_get_current(&furi_hal_i2c_handle_power), - (int)furi_hal_power_get_battery_temperature_internal(FuriHalPowerICFuelGauge)); - } - - printf( - "bq25896: VBUS: %d, VSYS: %d, VBAT: %d, Current: %d, NTC: %ldm%%\r\n", - bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power), - bq25896_get_vsys_voltage(&furi_hal_i2c_handle_power), - bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power), - bq25896_get_vbat_current(&furi_hal_i2c_handle_power), - bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power)); - - furi_hal_i2c_release(&furi_hal_i2c_handle_power); -} - void furi_hal_power_enable_external_3_3v() { furi_hal_gpio_write(&periph_power, 1); } @@ -526,57 +473,236 @@ void furi_hal_power_suppress_charge_exit() { } } -void furi_hal_power_info_get(FuriHalPowerInfoCallback out, void* context) { +void furi_hal_power_info_get(PropertyValueCallback out, char sep, void* context) { furi_assert(out); - FuriString* value; - value = furi_string_alloc(); + FuriString* value = furi_string_alloc(); + FuriString* key = furi_string_alloc(); - // Power Info version - out("power_info_major", "1", false, context); - out("power_info_minor", "0", false, context); + PropertyValueContext property_context = { + .key = key, .value = value, .out = out, .sep = sep, .last = false, .context = context}; + + if(sep == '.') { + property_value_out(&property_context, NULL, 2, "format", "major", "2"); + property_value_out(&property_context, NULL, 2, "format", "minor", "1"); + } else { + property_value_out(&property_context, NULL, 3, "power", "info", "major", "1"); + property_value_out(&property_context, NULL, 3, "power", "info", "minor", "1"); + } uint8_t charge = furi_hal_power_get_pct(); + property_value_out(&property_context, "%u", 2, "charge", "level", charge); - furi_string_printf(value, "%u", charge); - out("charge_level", furi_string_get_cstr(value), false, context); - + const char* charge_state; if(furi_hal_power_is_charging()) { - if(charge < 100) { - furi_string_printf(value, "charging"); + if((charge < 100) && (!furi_hal_power_is_charging_done())) { + charge_state = "charging"; } else { - furi_string_printf(value, "charged"); + charge_state = "charged"; } } else { - furi_string_printf(value, "discharging"); + charge_state = "discharging"; } - out("charge_state", furi_string_get_cstr(value), false, context); + property_value_out(&property_context, NULL, 2, "charge", "state", charge_state); + uint16_t charge_voltage = (uint16_t)(furi_hal_power_get_battery_charging_voltage() * 1000.f); + property_value_out(&property_context, "%u", 2, "charge", "voltage", charge_voltage); uint16_t voltage = (uint16_t)(furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge) * 1000.f); - furi_string_printf(value, "%u", voltage); - out("battery_voltage", furi_string_get_cstr(value), false, context); - + property_value_out(&property_context, "%u", 2, "battery", "voltage", voltage); int16_t current = (int16_t)(furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge) * 1000.f); - furi_string_printf(value, "%d", current); - out("battery_current", furi_string_get_cstr(value), false, context); - + property_value_out(&property_context, "%d", 2, "battery", "current", current); int16_t temperature = (int16_t)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge); - furi_string_printf(value, "%d", temperature); - out("gauge_temp", furi_string_get_cstr(value), false, context); - - furi_string_printf(value, "%u", furi_hal_power_get_bat_health_pct()); - out("battery_health", furi_string_get_cstr(value), false, context); - - furi_string_printf(value, "%lu", furi_hal_power_get_battery_remaining_capacity()); - out("capacity_remain", furi_string_get_cstr(value), false, context); - - furi_string_printf(value, "%lu", furi_hal_power_get_battery_full_capacity()); - out("capacity_full", furi_string_get_cstr(value), false, context); - - furi_string_printf(value, "%lu", furi_hal_power_get_battery_design_capacity()); - out("capacity_design", furi_string_get_cstr(value), true, context); + property_value_out(&property_context, "%d", 2, "battery", "temp", temperature); + property_value_out( + &property_context, "%u", 2, "battery", "health", furi_hal_power_get_bat_health_pct()); + property_value_out( + &property_context, + "%lu", + 2, + "capacity", + "remain", + furi_hal_power_get_battery_remaining_capacity()); + property_value_out( + &property_context, + "%lu", + 2, + "capacity", + "full", + furi_hal_power_get_battery_full_capacity()); + property_context.last = true; + property_value_out( + &property_context, + "%lu", + 2, + "capacity", + "design", + furi_hal_power_get_battery_design_capacity()); + furi_string_free(key); furi_string_free(value); } + +void furi_hal_power_debug_get(PropertyValueCallback out, void* context) { + furi_assert(out); + + FuriString* value = furi_string_alloc(); + FuriString* key = furi_string_alloc(); + + PropertyValueContext property_context = { + .key = key, .value = value, .out = out, .sep = '.', .last = false, .context = context}; + + BatteryStatus battery_status; + OperationStatus operation_status; + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + + // Power Debug version + property_value_out(&property_context, NULL, 2, "format", "major", "1"); + property_value_out(&property_context, NULL, 2, "format", "minor", "0"); + + property_value_out( + &property_context, + "%d", + 2, + "charger", + "vbus", + bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 2, + "charger", + "vsys", + bq25896_get_vsys_voltage(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 2, + "charger", + "vbat", + bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 2, + "charger", + "vreg", + bq25896_get_vreg_voltage(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 2, + "charger", + "current", + bq25896_get_vbat_current(&furi_hal_i2c_handle_power)); + + const uint32_t ntc_mpct = bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power); + + if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) != BQ27220_ERROR && + bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) != + BQ27220_ERROR) { + property_value_out(&property_context, "%lu", 2, "charger", "ntc", ntc_mpct); + property_value_out(&property_context, "%d", 2, "gauge", "calmd", operation_status.CALMD); + property_value_out(&property_context, "%d", 2, "gauge", "sec", operation_status.SEC); + property_value_out(&property_context, "%d", 2, "gauge", "edv2", operation_status.EDV2); + property_value_out(&property_context, "%d", 2, "gauge", "vdq", operation_status.VDQ); + property_value_out( + &property_context, "%d", 2, "gauge", "initcomp", operation_status.INITCOMP); + property_value_out(&property_context, "%d", 2, "gauge", "smth", operation_status.SMTH); + property_value_out(&property_context, "%d", 2, "gauge", "btpint", operation_status.BTPINT); + property_value_out( + &property_context, "%d", 2, "gauge", "cfgupdate", operation_status.CFGUPDATE); + + // Battery status register, part 1 + property_value_out(&property_context, "%d", 2, "gauge", "chginh", battery_status.CHGINH); + property_value_out(&property_context, "%d", 2, "gauge", "fc", battery_status.FC); + property_value_out(&property_context, "%d", 2, "gauge", "otd", battery_status.OTD); + property_value_out(&property_context, "%d", 2, "gauge", "otc", battery_status.OTC); + property_value_out(&property_context, "%d", 2, "gauge", "sleep", battery_status.SLEEP); + property_value_out(&property_context, "%d", 2, "gauge", "ocvfail", battery_status.OCVFAIL); + property_value_out(&property_context, "%d", 2, "gauge", "ocvcomp", battery_status.OCVCOMP); + property_value_out(&property_context, "%d", 2, "gauge", "fd", battery_status.FD); + + // Battery status register, part 2 + property_value_out(&property_context, "%d", 2, "gauge", "dsg", battery_status.DSG); + property_value_out(&property_context, "%d", 2, "gauge", "sysdwn", battery_status.SYSDWN); + property_value_out(&property_context, "%d", 2, "gauge", "tda", battery_status.TDA); + property_value_out( + &property_context, "%d", 2, "gauge", "battpres", battery_status.BATTPRES); + property_value_out(&property_context, "%d", 2, "gauge", "authgd", battery_status.AUTH_GD); + property_value_out(&property_context, "%d", 2, "gauge", "ocvgd", battery_status.OCVGD); + property_value_out(&property_context, "%d", 2, "gauge", "tca", battery_status.TCA); + property_value_out(&property_context, "%d", 2, "gauge", "rsvd", battery_status.RSVD); + + // Voltage and current info + property_value_out( + &property_context, + "%d", + 3, + "gauge", + "capacity", + "full", + bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 3, + "gauge", + "capacity", + "design", + bq27220_get_design_capacity(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 3, + "gauge", + "capacity", + "remain", + bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 3, + "gauge", + "state", + "charge", + bq27220_get_state_of_charge(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 3, + "gauge", + "state", + "health", + bq27220_get_state_of_health(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 2, + "gauge", + "voltage", + bq27220_get_voltage(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 2, + "gauge", + "current", + bq27220_get_current(&furi_hal_i2c_handle_power)); + + property_context.last = true; + const int battery_temp = + (int)furi_hal_power_get_battery_temperature_internal(FuriHalPowerICFuelGauge); + property_value_out(&property_context, "%d", 2, "gauge", "temperature", battery_temp); + } else { + property_context.last = true; + property_value_out(&property_context, "%lu", 2, "charger", "ntc", ntc_mpct); + } + + furi_string_free(key); + furi_string_free(value); + + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index 8d53ec48a..64e5e19f9 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -10,7 +10,7 @@ extern "C" { #endif /* Input Related Constants */ -#define INPUT_DEBOUNCE_TICKS 30 +#define INPUT_DEBOUNCE_TICKS 4 /* Input Keys */ typedef enum { diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index 14ece9466..c38cbfec5 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -30,7 +30,8 @@ typedef struct { uint8_t log_reserved : 4; uint8_t flags; uint8_t boot_mode : 4; - uint16_t reserved : 12; + uint8_t heap_track_mode : 2; + uint16_t reserved : 10; } DeveloperReg; _Static_assert(sizeof(DeveloperReg) == 4, "DeveloperReg size mismatch"); @@ -224,6 +225,19 @@ FuriHalRtcBootMode furi_hal_rtc_get_boot_mode() { return (FuriHalRtcBootMode)data->boot_mode; } +void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + DeveloperReg* data = (DeveloperReg*)&data_reg; + data->heap_track_mode = mode; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + DeveloperReg* data = (DeveloperReg*)&data_reg; + return (FuriHalRtcHeapTrackMode)data->heap_track_mode; +} + void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime) { furi_assert(datetime); diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 5eeb4e9a5..726b2d7fa 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -488,13 +488,9 @@ void furi_hal_subghz_stop_async_rx() { furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } -#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256) -#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2) -#define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 333 - typedef struct { uint32_t* buffer; - bool flip_flop; + LevelDuration carry_ld; FuriHalSubGhzAsyncTxCallback callback; void* callback_context; uint64_t duty_high; @@ -504,37 +500,48 @@ typedef struct { static FuriHalSubGhzAsyncTx furi_hal_subghz_async_tx = {0}; static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); while(samples > 0) { bool is_odd = samples % 2; - LevelDuration ld = - furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context); + LevelDuration ld; + if(level_duration_is_reset(furi_hal_subghz_async_tx.carry_ld)) { + ld = furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context); + } else { + ld = furi_hal_subghz_async_tx.carry_ld; + furi_hal_subghz_async_tx.carry_ld = level_duration_reset(); + } if(level_duration_is_wait(ld)) { - return; + *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; + buffer++; + samples--; } else if(level_duration_is_reset(ld)) { - // One more even sample required to end at low level - if(is_odd) { - *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - buffer++; - samples--; - furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - } + *buffer = 0; + buffer++; + samples--; + LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_TIM_EnableIT_UPDATE(TIM2); break; } else { - // Inject guard time if level is incorrect bool level = level_duration_get_level(ld); - if(is_odd == level) { + + // Inject guard time if level is incorrect + if(is_odd != level) { *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; buffer++; samples--; - if(!level) { + if(is_odd) { furi_hal_subghz_async_tx.duty_high += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; } else { furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; } - // This code must be invoked only once: when encoder starts with low level. - // Otherwise whole thing will crash. - furi_check(samples > 0); + + // Special case: prevent buffer overflow if sample is last + if(samples == 0) { + furi_hal_subghz_async_tx.carry_ld = ld; + break; + } } uint32_t duration = level_duration_get_duration(ld); @@ -543,22 +550,17 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { buffer++; samples--; - if(level) { + if(is_odd) { furi_hal_subghz_async_tx.duty_high += duration; } else { furi_hal_subghz_async_tx.duty_low += duration; } } } - - memset(buffer, 0, samples * sizeof(uint32_t)); } static void furi_hal_subghz_async_tx_dma_isr() { - furi_assert( - furi_hal_subghz.state == SubGhzStateAsyncTx || - furi_hal_subghz.state == SubGhzStateAsyncTxEnd || - furi_hal_subghz.state == SubGhzStateAsyncTxLast); + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); if(LL_DMA_IsActiveFlag_HT1(DMA1)) { LL_DMA_ClearFlag_HT1(DMA1); furi_hal_subghz_async_tx_refill( @@ -578,11 +580,14 @@ static void furi_hal_subghz_async_tx_timer_isr() { if(LL_TIM_GetAutoReload(TIM2) == 0) { if(furi_hal_subghz.state == SubGhzStateAsyncTx) { furi_hal_subghz.state = SubGhzStateAsyncTxLast; + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) { + furi_hal_subghz.state = SubGhzStateAsyncTxEnd; //forcibly pulls the pin to the ground so that there is no carrier furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow); - } else { - furi_hal_subghz.state = SubGhzStateAsyncTxEnd; LL_TIM_DisableCounter(TIM2); + } else { + furi_crash(NULL); } } } @@ -605,8 +610,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* furi_hal_subghz_async_tx.buffer = malloc(API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); - furi_hal_subghz_async_tx_refill( - furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL); // Connect CC1101_GD0 to TIM2 as output furi_hal_gpio_init_ex( @@ -647,14 +650,16 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE; TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE; TIM_OC_InitStruct.CompareValue = 0; - TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH; + TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_LOW; LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct); LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2); LL_TIM_DisableMasterSlaveMode(TIM2); furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_async_tx_timer_isr, NULL); - LL_TIM_EnableIT_UPDATE(TIM2); + furi_hal_subghz_async_tx_refill( + furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL); + LL_TIM_EnableDMAReq_UPDATE(TIM2); LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); @@ -673,8 +678,8 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* &SUBGHZ_DEBUG_CC1101_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); const GpioPin* gpio = &SUBGHZ_DEBUG_CC1101_PIN; - subghz_debug_gpio_buff[0] = gpio->pin; - subghz_debug_gpio_buff[1] = (uint32_t)gpio->pin << GPIO_NUMBER; + subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; + subghz_debug_gpio_buff[1] = gpio->pin; dma_config.MemoryOrM2MDstAddress = (uint32_t)subghz_debug_gpio_buff; dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->BSRR); diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb.c b/firmware/targets/f7/furi_hal/furi_hal_usb.c index 86b10c79c..e740155f5 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "usb.h" @@ -11,35 +12,67 @@ #define USB_RECONNECT_DELAY 500 +typedef enum { + UsbApiEventTypeSetConfig, + UsbApiEventTypeGetConfig, + UsbApiEventTypeLock, + UsbApiEventTypeUnlock, + UsbApiEventTypeIsLocked, + UsbApiEventTypeEnable, + UsbApiEventTypeDisable, + UsbApiEventTypeReinit, + UsbApiEventTypeSetStateCallback, +} UsbApiEventType; + +typedef struct { + FuriHalUsbStateCallback callback; + void* context; +} UsbApiEventDataStateCallback; + +typedef struct { + FuriHalUsbInterface* interface; + void* context; +} UsbApiEventDataInterface; + +typedef union { + UsbApiEventDataStateCallback state_callback; + UsbApiEventDataInterface interface; +} UsbApiEventData; + +typedef union { + bool bool_value; + void* void_value; +} UsbApiEventReturnData; + +typedef struct { + FuriApiLock lock; + UsbApiEventType type; + UsbApiEventData data; + UsbApiEventReturnData* return_data; +} UsbApiEventMessage; + typedef struct { FuriThread* thread; + FuriMessageQueue* queue; bool enabled; bool connected; bool mode_lock; - FuriHalUsbInterface* if_cur; - FuriHalUsbInterface* if_next; - void* if_ctx; + bool request_pending; + FuriHalUsbInterface* interface; + void* interface_context; FuriHalUsbStateCallback callback; - void* cb_ctx; + void* callback_context; } UsbSrv; typedef enum { - EventModeChange = (1 << 0), - EventEnable = (1 << 1), - EventDisable = (1 << 2), - EventReinit = (1 << 3), - - EventReset = (1 << 4), - EventRequest = (1 << 5), - - EventModeChangeStart = (1 << 6), + UsbEventReset = (1 << 0), + UsbEventRequest = (1 << 1), + UsbEventMessage = (1 << 2), } UsbEvent; -#define USB_SRV_ALL_EVENTS \ - (EventModeChange | EventEnable | EventDisable | EventReinit | EventReset | EventRequest | \ - EventModeChangeStart) +#define USB_SRV_ALL_EVENTS (UsbEventReset | UsbEventRequest | UsbEventMessage) -static UsbSrv usb; +PLACE_IN_SECTION("MB_MEM2") static UsbSrv usb = {0}; static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); @@ -74,68 +107,133 @@ void furi_hal_usb_init(void) { // Reset callback will be enabled after first mode change to avoid getting false reset events usb.enabled = false; - usb.if_cur = NULL; + usb.interface = NULL; NVIC_SetPriority(USB_LP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_SetPriority(USB_HP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0)); NVIC_EnableIRQ(USB_LP_IRQn); NVIC_EnableIRQ(USB_HP_IRQn); - usb.thread = furi_thread_alloc(); - furi_thread_set_name(usb.thread, "UsbDriver"); - furi_thread_set_stack_size(usb.thread, 1024); - furi_thread_set_callback(usb.thread, furi_hal_usb_thread); + usb.queue = furi_message_queue_alloc(1, sizeof(UsbApiEventMessage)); + usb.thread = furi_thread_alloc_ex("UsbDriver", 1024, furi_hal_usb_thread, NULL); + furi_thread_mark_as_service(usb.thread); furi_thread_start(usb.thread); FURI_LOG_I(TAG, "Init OK"); } -bool furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx) { - if(usb.mode_lock) { - return false; - } +static void furi_hal_usb_send_message(UsbApiEventMessage* message) { + furi_message_queue_put(usb.queue, message, FuriWaitForever); + furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventMessage); + api_lock_wait_unlock_and_free(message->lock); +} - usb.if_next = new_if; - usb.if_ctx = ctx; - if(usb.thread == NULL) { - // Service thread hasn't started yet, so just save interface mode - return true; - } - furi_assert(usb.thread); - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventModeChange); - return true; +bool furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx) { + UsbApiEventReturnData return_data = { + .bool_value = false, + }; + + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeSetConfig, + .data.interface = + { + .interface = new_if, + .context = ctx, + }, + .return_data = &return_data, + }; + + furi_hal_usb_send_message(&msg); + return return_data.bool_value; } FuriHalUsbInterface* furi_hal_usb_get_config() { - return usb.if_cur; + UsbApiEventReturnData return_data = { + .void_value = NULL, + }; + + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeGetConfig, + .return_data = &return_data, + }; + + furi_hal_usb_send_message(&msg); + return return_data.void_value; } void furi_hal_usb_lock() { - FURI_LOG_I(TAG, "Mode lock"); - usb.mode_lock = true; + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeLock, + }; + + furi_hal_usb_send_message(&msg); } void furi_hal_usb_unlock() { - FURI_LOG_I(TAG, "Mode unlock"); - usb.mode_lock = false; + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeUnlock, + }; + + furi_hal_usb_send_message(&msg); } bool furi_hal_usb_is_locked() { - return usb.mode_lock; + UsbApiEventReturnData return_data = { + .bool_value = false, + }; + + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeIsLocked, + .return_data = &return_data, + }; + + furi_hal_usb_send_message(&msg); + return return_data.bool_value; } void furi_hal_usb_disable() { - furi_assert(usb.thread); - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventDisable); + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeDisable, + }; + + furi_hal_usb_send_message(&msg); } void furi_hal_usb_enable() { - furi_assert(usb.thread); - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventEnable); + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeEnable, + }; + + furi_hal_usb_send_message(&msg); } void furi_hal_usb_reinit() { - furi_assert(usb.thread); - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventReinit); + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeReinit, + }; + + furi_hal_usb_send_message(&msg); +} + +void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) { + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeSetStateCallback, + .data.state_callback = + { + .callback = cb, + .context = ctx, + }, + }; + + furi_hal_usb_send_message(&msg); } /* Get device / configuration descriptors */ @@ -144,29 +242,29 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_ const uint8_t dnumber = req->wValue & 0xFF; const void* desc; uint16_t len = 0; - if(usb.if_cur == NULL) return usbd_fail; + if(usb.interface == NULL) return usbd_fail; switch(dtype) { case USB_DTYPE_DEVICE: - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventRequest); + furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventRequest); if(usb.callback != NULL) { - usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.cb_ctx); + usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.callback_context); } - desc = usb.if_cur->dev_descr; + desc = usb.interface->dev_descr; break; case USB_DTYPE_CONFIGURATION: - desc = usb.if_cur->cfg_descr; - len = ((struct usb_string_descriptor*)(usb.if_cur->cfg_descr))->wString[0]; + desc = usb.interface->cfg_descr; + len = ((struct usb_string_descriptor*)(usb.interface->cfg_descr))->wString[0]; break; case USB_DTYPE_STRING: if(dnumber == UsbDevLang) { desc = &dev_lang_desc; - } else if((dnumber == UsbDevManuf) && (usb.if_cur->str_manuf_descr != NULL)) { - desc = usb.if_cur->str_manuf_descr; - } else if((dnumber == UsbDevProduct) && (usb.if_cur->str_prod_descr != NULL)) { - desc = usb.if_cur->str_prod_descr; - } else if((dnumber == UsbDevSerial) && (usb.if_cur->str_serial_descr != NULL)) { - desc = usb.if_cur->str_serial_descr; + } else if((dnumber == UsbDevManuf) && (usb.interface->str_manuf_descr != NULL)) { + desc = usb.interface->str_manuf_descr; + } else if((dnumber == UsbDevProduct) && (usb.interface->str_prod_descr != NULL)) { + desc = usb.interface->str_prod_descr; + } else if((dnumber == UsbDevSerial) && (usb.interface->str_serial_descr != NULL)) { + desc = usb.interface->str_serial_descr; } else return usbd_fail; break; @@ -183,18 +281,13 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_ return usbd_ack; } -void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) { - usb.callback = cb; - usb.cb_ctx = ctx; -} - static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(dev); UNUSED(event); UNUSED(ep); - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventReset); + furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventReset); if(usb.callback != NULL) { - usb.callback(FuriHalUsbStateEventReset, usb.cb_ctx); + usb.callback(FuriHalUsbStateEventReset, usb.callback_context); } } @@ -202,14 +295,14 @@ static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(dev); UNUSED(event); UNUSED(ep); - if((usb.if_cur != NULL) && (usb.connected == true)) { + if((usb.interface != NULL) && (usb.connected == true)) { usb.connected = false; - usb.if_cur->suspend(&udev); + usb.interface->suspend(&udev); furi_hal_power_insomnia_exit(); } if(usb.callback != NULL) { - usb.callback(FuriHalUsbStateEventSuspend, usb.cb_ctx); + usb.callback(FuriHalUsbStateEventSuspend, usb.callback_context); } } @@ -217,101 +310,161 @@ static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(dev); UNUSED(event); UNUSED(ep); - if((usb.if_cur != NULL) && (usb.connected == false)) { + if((usb.interface != NULL) && (usb.connected == false)) { usb.connected = true; - usb.if_cur->wakeup(&udev); + usb.interface->wakeup(&udev); furi_hal_power_insomnia_enter(); } if(usb.callback != NULL) { - usb.callback(FuriHalUsbStateEventWakeup, usb.cb_ctx); + usb.callback(FuriHalUsbStateEventWakeup, usb.callback_context); } } +static void usb_process_mode_start(FuriHalUsbInterface* interface, void* context) { + if(usb.interface != NULL) { + usb.interface->deinit(&udev); + } + + __disable_irq(); + usb.interface = interface; + usb.interface_context = context; + __enable_irq(); + + if(interface != NULL) { + interface->init(&udev, interface, context); + usbd_reg_event(&udev, usbd_evt_reset, reset_evt); + FURI_LOG_I(TAG, "USB Mode change done"); + usb.enabled = true; + } +} + +static void usb_process_mode_change(FuriHalUsbInterface* interface, void* context) { + if(interface != usb.interface) { + if(usb.enabled) { + // Disable current interface + susp_evt(&udev, 0, 0); + usbd_connect(&udev, false); + usb.enabled = false; + furi_delay_ms(USB_RECONNECT_DELAY); + } + usb_process_mode_start(interface, context); + } +} + +static void usb_process_mode_reinit() { + // Temporary disable callback to avoid getting false reset events + usbd_reg_event(&udev, usbd_evt_reset, NULL); + FURI_LOG_I(TAG, "USB Reinit"); + susp_evt(&udev, 0, 0); + usbd_connect(&udev, false); + usb.enabled = false; + + usbd_enable(&udev, false); + usbd_enable(&udev, true); + + furi_delay_ms(USB_RECONNECT_DELAY); + usb_process_mode_start(usb.interface, usb.interface_context); +} + +static bool usb_process_set_config(FuriHalUsbInterface* interface, void* context) { + if(usb.mode_lock) { + return false; + } else { + usb_process_mode_change(interface, context); + return true; + } +} + +static void usb_process_enable(bool enable) { + if(enable) { + if((!usb.enabled) && (usb.interface != NULL)) { + usbd_connect(&udev, true); + usb.enabled = true; + FURI_LOG_I(TAG, "USB Enable"); + } + } else { + if(usb.enabled) { + susp_evt(&udev, 0, 0); + usbd_connect(&udev, false); + usb.enabled = false; + usb.request_pending = false; + FURI_LOG_I(TAG, "USB Disable"); + } + } +} + +static void usb_process_message(UsbApiEventMessage* message) { + switch(message->type) { + case UsbApiEventTypeSetConfig: + message->return_data->bool_value = usb_process_set_config( + message->data.interface.interface, message->data.interface.context); + break; + case UsbApiEventTypeGetConfig: + message->return_data->void_value = usb.interface; + break; + case UsbApiEventTypeLock: + FURI_LOG_I(TAG, "Mode lock"); + usb.mode_lock = true; + break; + case UsbApiEventTypeUnlock: + FURI_LOG_I(TAG, "Mode unlock"); + usb.mode_lock = false; + break; + case UsbApiEventTypeIsLocked: + message->return_data->bool_value = usb.mode_lock; + break; + case UsbApiEventTypeDisable: + usb_process_enable(false); + break; + case UsbApiEventTypeEnable: + usb_process_enable(true); + break; + case UsbApiEventTypeReinit: + usb_process_mode_reinit(); + break; + case UsbApiEventTypeSetStateCallback: + usb.callback = message->data.state_callback.callback; + usb.callback_context = message->data.state_callback.context; + break; + } + + api_lock_unlock(message->lock); +} + static int32_t furi_hal_usb_thread(void* context) { UNUSED(context); - bool usb_request_pending = false; uint8_t usb_wait_time = 0; - FuriHalUsbInterface* if_new = NULL; - FuriHalUsbInterface* if_ctx_new = NULL; - if(usb.if_next != NULL) { - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventModeChange); + if(furi_message_queue_get_count(usb.queue) > 0) { + furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventMessage); } while(true) { uint32_t flags = furi_thread_flags_wait(USB_SRV_ALL_EVENTS, FuriFlagWaitAny, 500); + + { + UsbApiEventMessage message; + if(furi_message_queue_get(usb.queue, &message, 0) == FuriStatusOk) { + usb_process_message(&message); + } + } + if((flags & FuriFlagError) == 0) { - if(flags & EventModeChange) { - if(usb.if_next != usb.if_cur) { - if_new = usb.if_next; - if_ctx_new = usb.if_ctx; - if(usb.enabled) { // Disable current interface - susp_evt(&udev, 0, 0); - usbd_connect(&udev, false); - usb.enabled = false; - furi_delay_ms(USB_RECONNECT_DELAY); - } - flags |= EventModeChangeStart; - } - } - if(flags & EventReinit) { - // Temporary disable callback to avoid getting false reset events - usbd_reg_event(&udev, usbd_evt_reset, NULL); - FURI_LOG_I(TAG, "USB Reinit"); - susp_evt(&udev, 0, 0); - usbd_connect(&udev, false); - usb.enabled = false; - - usbd_enable(&udev, false); - usbd_enable(&udev, true); - - if_new = usb.if_cur; - furi_delay_ms(USB_RECONNECT_DELAY); - flags |= EventModeChangeStart; - } - if(flags & EventModeChangeStart) { // Second stage of mode change process - if(usb.if_cur != NULL) { - usb.if_cur->deinit(&udev); - } - if(if_new != NULL) { - if_new->init(&udev, if_new, if_ctx_new); - usbd_reg_event(&udev, usbd_evt_reset, reset_evt); - FURI_LOG_I(TAG, "USB Mode change done"); - usb.enabled = true; - } - usb.if_cur = if_new; - } - if(flags & EventEnable) { - if((!usb.enabled) && (usb.if_cur != NULL)) { - usbd_connect(&udev, true); - usb.enabled = true; - FURI_LOG_I(TAG, "USB Enable"); - } - } - if(flags & EventDisable) { + if(flags & UsbEventReset) { if(usb.enabled) { - susp_evt(&udev, 0, 0); - usbd_connect(&udev, false); - usb.enabled = false; - usb_request_pending = false; - FURI_LOG_I(TAG, "USB Disable"); - } - } - if(flags & EventReset) { - if(usb.enabled) { - usb_request_pending = true; + usb.request_pending = true; usb_wait_time = 0; } } - if(flags & EventRequest) { - usb_request_pending = false; + if(flags & UsbEventRequest) { + usb.request_pending = false; } - } else if(usb_request_pending) { + } else if(usb.request_pending) { usb_wait_time++; if(usb_wait_time > 4) { - furi_hal_usb_reinit(); - usb_request_pending = false; + usb_process_mode_reinit(); + usb.request_pending = false; } } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c b/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c index 60068f3b4..c099aec8a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c @@ -34,13 +34,13 @@ static const uint8_t hid_u2f_report_desc[] = { HID_COLLECTION(HID_APPLICATION_COLLECTION), HID_USAGE(HID_FIDO_INPUT), HID_LOGICAL_MINIMUM(0x00), - HID_LOGICAL_MAXIMUM(0xFF), + HID_RI_LOGICAL_MAXIMUM(16, 0xFF), HID_REPORT_SIZE(8), HID_REPORT_COUNT(HID_U2F_PACKET_LEN), HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), HID_USAGE(HID_FIDO_OUTPUT), HID_LOGICAL_MINIMUM(0x00), - HID_LOGICAL_MAXIMUM(0xFF), + HID_RI_LOGICAL_MAXIMUM(16, 0xFF), HID_REPORT_SIZE(8), HID_REPORT_COUNT(HID_U2F_PACKET_LEN), HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), diff --git a/firmware/targets/furi_hal_include/furi_hal_info.h b/firmware/targets/furi_hal_include/furi_hal_info.h index 4a335f2af..fa3267f5d 100644 --- a/firmware/targets/furi_hal_include/furi_hal_info.h +++ b/firmware/targets/furi_hal_include/furi_hal_info.h @@ -7,27 +7,20 @@ #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif -/** Callback type called every time another key-value pair of device information is ready - * - * @param key[in] device information type identifier - * @param value[in] device information value - * @param last[in] whether the passed key-value pair is the last one - * @param context[in] to pass to callback - */ -typedef void ( - *FuriHalInfoValueCallback)(const char* key, const char* value, bool last, void* context); - /** Get device information * * @param[in] callback callback to provide with new data + * @param[in] sep category separator character * @param[in] context context to pass to callback */ -void furi_hal_info_get(FuriHalInfoValueCallback callback, void* context); +void furi_hal_info_get(PropertyValueCallback callback, char sep, void* context); #ifdef __cplusplus } diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/firmware/targets/furi_hal_include/furi_hal_power.h index e94877afc..39a11e99f 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/firmware/targets/furi_hal_include/furi_hal_power.h @@ -7,6 +7,8 @@ #include #include +#include +#include #ifdef __cplusplus extern "C" { @@ -119,6 +121,22 @@ void furi_hal_power_check_otg_status(); */ bool furi_hal_power_is_otg_enabled(); +/** Get battery charging voltage in V + * + * @return voltage in V + */ +float furi_hal_power_get_battery_charging_voltage(); + +/** Set battery charging voltage in V + * + * Invalid values will be clamped to the nearest valid value. + * + * @param voltage[in] voltage in V + * + * @return voltage in V + */ +void furi_hal_power_set_battery_charging_voltage(float voltage); + /** Get remaining battery battery capacity in mAh * * @return capacity in mAh @@ -167,10 +185,6 @@ float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic); */ float furi_hal_power_get_usb_voltage(); -/** Get power system component state - */ -void furi_hal_power_dump_state(); - /** Enable 3.3v on external gpio and sd card */ void furi_hal_power_enable_external_3_3v(); @@ -189,22 +203,20 @@ void furi_hal_power_suppress_charge_enter(); */ void furi_hal_power_suppress_charge_exit(); -/** Callback type called by furi_hal_power_info_get every time another key-value pair of information is ready - * - * @param key[in] power information type identifier - * @param value[in] power information value - * @param last[in] whether the passed key-value pair is the last one - * @param context[in] to pass to callback - */ -typedef void ( - *FuriHalPowerInfoCallback)(const char* key, const char* value, bool last, void* context); - /** Get power information + * + * @param[in] callback callback to provide with new data + * @param[in] sep category separator character + * @param[in] context context to pass to callback + */ +void furi_hal_power_info_get(PropertyValueCallback callback, char sep, void* context); + +/** Get power debug information * * @param[in] callback callback to provide with new data * @param[in] context context to pass to callback */ -void furi_hal_power_info_get(FuriHalPowerInfoCallback callback, void* context); +void furi_hal_power_debug_get(PropertyValueCallback callback, void* context); #ifdef __cplusplus } diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index 361225fb2..5ce122271 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -39,6 +39,13 @@ typedef enum { FuriHalRtcBootModePostUpdate, /**< Boot to Update, post update */ } FuriHalRtcBootMode; +typedef enum { + FuriHalRtcHeapTrackModeNone = 0, /**< Disable allocation tracking */ + FuriHalRtcHeapTrackModeMain, /**< Enable allocation tracking for main application thread */ + FuriHalRtcHeapTrackModeTree, /**< Enable allocation tracking for main and children application threads */ + FuriHalRtcHeapTrackModeAll, /**< Enable allocation tracking for all threads */ +} FuriHalRtcHeapTrackMode; + typedef enum { FuriHalRtcRegisterHeader, /**< RTC structure header */ FuriHalRtcRegisterSystem, /**< Various system bits */ @@ -79,6 +86,10 @@ void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode); FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(); +void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode); + +FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(); + void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime); void furi_hal_rtc_get_datetime(FuriHalRtcDateTime* datetime); diff --git a/firmware/targets/furi_hal_include/furi_hal_subghz.h b/firmware/targets/furi_hal_include/furi_hal_subghz.h index d610b01b7..1f99386c0 100644 --- a/firmware/targets/furi_hal_include/furi_hal_subghz.h +++ b/firmware/targets/furi_hal_include/furi_hal_subghz.h @@ -14,6 +14,11 @@ extern "C" { #endif +/** Low level buffer dimensions and guard times */ +#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256) +#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2) +#define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 999 + /** Radio Presets */ typedef enum { FuriHalSubGhzPresetIDLE, /**< default configuration */ diff --git a/firmware/targets/furi_hal_include/furi_hal_usb_hid.h b/firmware/targets/furi_hal_include/furi_hal_usb_hid.h index f4b40c89d..a9f094814 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb_hid.h +++ b/firmware/targets/furi_hal_include/furi_hal_usb_hid.h @@ -10,8 +10,6 @@ extern "C" { #endif #define HID_KEYBOARD_NONE 0x00 -// Remapping the colon key which is shift + ; to comma -#define HID_KEYBOARD_COMMA HID_KEYBOARD_COLON /** HID keyboard modifier keys */ enum HidKeyboardMods { diff --git a/furi/SConscript b/furi/SConscript index f95ef13f9..8f8caeb87 100644 --- a/furi/SConscript +++ b/furi/SConscript @@ -2,8 +2,7 @@ Import("env") env.Append( LINT_SOURCES=[ - "furi", - "furi/core", + Dir("."), ] ) diff --git a/furi/core/check.h b/furi/core/check.h index 78efc1451..192c5260e 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -46,7 +46,7 @@ FURI_NORETURN void __furi_halt(); /** Check condition and crash if check failed */ #define furi_check(__e) \ do { \ - if((__e) == 0) { \ + if(!(__e)) { \ furi_crash("furi_check failed\r\n"); \ } \ } while(0) @@ -55,7 +55,7 @@ FURI_NORETURN void __furi_halt(); #ifdef FURI_DEBUG #define furi_assert(__e) \ do { \ - if((__e) == 0) { \ + if(!(__e)) { \ furi_crash("furi_assert failed\r\n"); \ } \ } while(0) diff --git a/furi/core/event_flag.c b/furi/core/event_flag.c index 5d2a49910..07dd30a16 100644 --- a/furi/core/event_flag.c +++ b/furi/core/event_flag.c @@ -25,7 +25,7 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { uint32_t rflags; BaseType_t yield; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { yield = pdFALSE; if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) { rflags = (uint32_t)FuriStatusErrorResource; @@ -48,7 +48,7 @@ uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) { EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; uint32_t rflags; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { rflags = xEventGroupGetBitsFromISR(hEventGroup); if(xEventGroupClearBitsFromISR(hEventGroup, (EventBits_t)flags) == pdFAIL) { @@ -73,7 +73,7 @@ uint32_t furi_event_flag_get(FuriEventFlag* instance) { EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; uint32_t rflags; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { rflags = xEventGroupGetBitsFromISR(hEventGroup); } else { rflags = xEventGroupGetBits(hEventGroup); diff --git a/furi/core/log.c b/furi/core/log.c index bb163c956..a3967ed92 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -8,7 +8,7 @@ typedef struct { FuriLogLevel log_level; FuriLogPuts puts; - FuriLogTimestamp timetamp; + FuriLogTimestamp timestamp; FuriMutex* mutex; } FuriLogParams; @@ -18,7 +18,7 @@ void furi_log_init() { // Set default logging parameters furi_log.log_level = FURI_LOG_LEVEL_DEFAULT; furi_log.puts = furi_hal_console_puts; - furi_log.timetamp = furi_get_tick; + furi_log.timestamp = furi_get_tick; furi_log.mutex = furi_mutex_alloc(FuriMutexTypeNormal); } @@ -59,7 +59,7 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form furi_string_printf( string, "%lu %s[%s][%s] " FURI_LOG_CLR_RESET, - furi_log.timetamp(), + furi_log.timestamp(), color, log_letter, tag); @@ -98,5 +98,5 @@ void furi_log_set_puts(FuriLogPuts puts) { void furi_log_set_timestamp(FuriLogTimestamp timestamp) { furi_assert(timestamp); - furi_log.timetamp = timestamp; + furi_log.timestamp = timestamp; } diff --git a/furi/core/memmgr.h b/furi/core/memmgr.h index d3ad0c87c..bc0c35faa 100644 --- a/furi/core/memmgr.h +++ b/furi/core/memmgr.h @@ -1,6 +1,6 @@ /** * @file memmgr.h - * Furi: memory managment API and glue + * Furi: memory management API and glue */ #pragma once diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 32b2875cc..01153fe57 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -150,8 +150,7 @@ void memmgr_heap_disable_thread_trace(FuriThreadId thread_id) { vTaskSuspendAll(); { memmgr_heap_thread_trace_depth++; - furi_check(MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id) != NULL); - MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id); + furi_check(MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id)); memmgr_heap_thread_trace_depth--; } (void)xTaskResumeAll(); @@ -212,7 +211,8 @@ static inline void traceFREE(void* pointer, size_t size) { MemmgrHeapAllocDict_t* alloc_dict = MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id); if(alloc_dict) { - MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer); + // In some cases thread may want to release memory that was not allocated by it + (void)MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer); } memmgr_heap_thread_trace_depth--; } @@ -340,6 +340,10 @@ void* pvPortMalloc(size_t xWantedSize) { void* pvReturn = NULL; size_t to_wipe = xWantedSize; + if(FURI_IS_IRQ_MODE()) { + furi_crash("memmgt in ISR"); + } + #ifdef HEAP_PRINT_DEBUG BlockLink_t* print_heap_block = NULL; #endif @@ -486,6 +490,10 @@ void vPortFree(void* pv) { uint8_t* puc = (uint8_t*)pv; BlockLink_t* pxLink; + if(FURI_IS_IRQ_MODE()) { + furi_crash("memmgt in ISR"); + } + if(pv != NULL) { /* The memory being freed will have an BlockLink_t structure immediately before it. */ diff --git a/furi/core/memmgr_heap.h b/furi/core/memmgr_heap.h index 1521fce42..9aacba1ca 100644 --- a/furi/core/memmgr_heap.h +++ b/furi/core/memmgr_heap.h @@ -1,6 +1,6 @@ /** * @file memmgr_heap.h - * Furi: heap memory managment API and allocator + * Furi: heap memory management API and allocator */ #pragma once diff --git a/furi/core/mutex.c b/furi/core/mutex.c index 78ea05196..ab66b0f18 100644 --- a/furi/core/mutex.c +++ b/furi/core/mutex.c @@ -45,7 +45,7 @@ FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { stat = FuriStatusErrorISR; } else if(hMutex == NULL) { stat = FuriStatusErrorParameter; @@ -85,7 +85,7 @@ FuriStatus furi_mutex_release(FuriMutex* instance) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { stat = FuriStatusErrorISR; } else if(hMutex == NULL) { stat = FuriStatusErrorParameter; @@ -111,7 +111,7 @@ FuriThreadId furi_mutex_get_owner(FuriMutex* instance) { hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); - if((FURI_IS_IRQ_MODE() != 0U) || (hMutex == NULL)) { + if((FURI_IS_IRQ_MODE()) || (hMutex == NULL)) { owner = 0; } else { owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex); diff --git a/furi/core/semaphore.c b/furi/core/semaphore.c index a204cbe6e..8c99bfc54 100644 --- a/furi/core/semaphore.c +++ b/furi/core/semaphore.c @@ -45,7 +45,7 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { if(timeout != 0U) { stat = FuriStatusErrorParameter; } else { @@ -80,7 +80,7 @@ FuriStatus furi_semaphore_release(FuriSemaphore* instance) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { yield = pdFALSE; if(xSemaphoreGiveFromISR(hSemaphore, &yield) != pdTRUE) { @@ -104,7 +104,7 @@ uint32_t furi_semaphore_get_count(FuriSemaphore* instance) { SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; uint32_t count; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore); } else { count = (uint32_t)uxSemaphoreGetCount(hSemaphore); diff --git a/furi/core/stream_buffer.c b/furi/core/stream_buffer.c index b9d0629fe..2df84fa5b 100644 --- a/furi/core/stream_buffer.c +++ b/furi/core/stream_buffer.c @@ -23,7 +23,7 @@ size_t furi_stream_buffer_send( uint32_t timeout) { size_t ret; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { BaseType_t yield; ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield); portYIELD_FROM_ISR(yield); @@ -41,7 +41,7 @@ size_t furi_stream_buffer_receive( uint32_t timeout) { size_t ret; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { BaseType_t yield; ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield); portYIELD_FROM_ISR(yield); diff --git a/furi/core/string.c b/furi/core/string.c index 099f70c11..901b1f625 100644 --- a/furi/core/string.c +++ b/furi/core/string.c @@ -29,13 +29,13 @@ FuriString* furi_string_alloc() { } FuriString* furi_string_alloc_set(const FuriString* s) { - FuriString* string = malloc(sizeof(FuriString)); + FuriString* string = malloc(sizeof(FuriString)); //-V773 string_init_set(string->string, s->string); return string; } FuriString* furi_string_alloc_set_str(const char cstr[]) { - FuriString* string = malloc(sizeof(FuriString)); + FuriString* string = malloc(sizeof(FuriString)); //-V773 string_init_set(string->string, cstr); return string; } diff --git a/furi/core/thread.c b/furi/core/thread.c index 508146f63..34cc7d987 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -12,6 +12,8 @@ #include #include +#define TAG "FuriThread" + #define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers typedef struct FuriThreadStdout FuriThreadStdout; @@ -50,6 +52,7 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread); __attribute__((__noreturn__)) void furi_thread_catch() { asm volatile("nop"); // extra magic furi_crash("You are doing it wrong"); + __builtin_unreachable(); } static void furi_thread_set_state(FuriThread* thread, FuriThreadState state) { @@ -81,6 +84,12 @@ static void furi_thread_body(void* context) { if(thread->heap_trace_enabled == true) { furi_delay_ms(33); thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle); + furi_log_print_format( + thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo, + TAG, + "%s allocation balance: %d", + thread->name ? thread->name : "Thread", + thread->heap_size); memmgr_heap_disable_thread_trace((FuriThreadId)task_handle); } @@ -88,8 +97,8 @@ static void furi_thread_body(void* context) { if(thread->is_service) { FURI_LOG_E( - "Service", - "%s thread exited. Thread memory cannot be reclaimed.", + TAG, + "%s service thread exited. Thread memory cannot be reclaimed.", thread->name ? thread->name : ""); } @@ -112,6 +121,30 @@ FuriThread* furi_thread_alloc() { FuriThread* thread = malloc(sizeof(FuriThread)); thread->output.buffer = furi_string_alloc(); thread->is_service = false; + + FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); + if(mode == FuriHalRtcHeapTrackModeAll) { + thread->heap_trace_enabled = true; + } else if(mode == FuriHalRtcHeapTrackModeTree && furi_thread_get_current_id()) { + FuriThread* parent = pvTaskGetThreadLocalStoragePointer(NULL, 0); + if(parent) thread->heap_trace_enabled = parent->heap_trace_enabled; + } else { + thread->heap_trace_enabled = false; + } + + return thread; +} + +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; } @@ -228,14 +261,12 @@ FuriThreadId furi_thread_get_id(FuriThread* thread) { void furi_thread_enable_heap_trace(FuriThread* thread) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); - furi_assert(thread->heap_trace_enabled == false); thread->heap_trace_enabled = true; } void furi_thread_disable_heap_trace(FuriThread* thread) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); - furi_assert(thread->heap_trace_enabled == true); thread->heap_trace_enabled = false; } diff --git a/furi/core/thread.h b/furi/core/thread.h index fda81bb3a..c2f5a9130 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -48,7 +48,7 @@ typedef int32_t (*FuriThreadCallback)(void* context); */ typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size); -/** FuriThread state change calback called upon thread state change +/** FuriThread state change callback called upon thread state change * @param state new thread state * @param context callback context */ @@ -60,6 +60,20 @@ typedef void (*FuriThreadStateCallback)(FuriThreadState state, void* context); */ FuriThread* furi_thread_alloc(); +/** Allocate FuriThread, shortcut version + * + * @param name + * @param stack_size + * @param callback + * @param context + * @return FuriThread* + */ +FuriThread* furi_thread_alloc_ex( + const char* name, + uint32_t stack_size, + FuriThreadCallback callback, + void* context); + /** Release FuriThread * * @param thread FuriThread instance @@ -180,7 +194,7 @@ size_t furi_thread_get_heap_size(FuriThread* thread); */ int32_t furi_thread_get_return_code(FuriThread* thread); -/** Thread releated methods that doesn't involve FuriThread directly */ +/** Thread related methods that doesn't involve FuriThread directly */ /** Get FreeRTOS FuriThreadId for current thread * diff --git a/furi/core/valuemutex.c b/furi/core/valuemutex.c index af2a0755c..bf4e6130b 100644 --- a/furi/core/valuemutex.c +++ b/furi/core/valuemutex.c @@ -4,7 +4,7 @@ bool init_mutex(ValueMutex* valuemutex, void* value, size_t size) { // mutex without name, - // no attributes (unfortunatly robust mutex is not supported by FreeRTOS), + // no attributes (unfortunately robust mutex is not supported by FreeRTOS), // with dynamic memory allocation valuemutex->mutex = furi_mutex_alloc(FuriMutexTypeNormal); if(valuemutex->mutex == NULL) return false; diff --git a/furi/core/valuemutex.h b/furi/core/valuemutex.h index 41762fdd3..0d867a1df 100644 --- a/furi/core/valuemutex.h +++ b/furi/core/valuemutex.h @@ -39,7 +39,7 @@ bool delete_mutex(ValueMutex* valuemutex); void* acquire_mutex(ValueMutex* valuemutex, uint32_t timeout); /** - * Helper: infinitly wait for mutex + * Helper: infinitely wait for mutex */ static inline void* acquire_mutex_block(ValueMutex* valuemutex) { return acquire_mutex(valuemutex, FuriWaitForever); @@ -135,7 +135,7 @@ void consumer_app(void* _p) { flapp_exit(NULL); } - // continously read value every 1s + // continuously read value every 1s uint32_t counter; while(1) { if(read_mutex(counter_mutex, &counter, sizeof(counter), OsWaitForever)) { diff --git a/furi/flipper.c b/furi/flipper.c index 2acfea015..73899e58b 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -34,11 +34,11 @@ void flipper_init() { for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) { FURI_LOG_I(TAG, "starting service %s", FLIPPER_SERVICES[i].name); - FuriThread* thread = furi_thread_alloc(); - - furi_thread_set_name(thread, FLIPPER_SERVICES[i].name); - furi_thread_set_stack_size(thread, FLIPPER_SERVICES[i].stack_size); - furi_thread_set_callback(thread, FLIPPER_SERVICES[i].app); + FuriThread* thread = furi_thread_alloc_ex( + FLIPPER_SERVICES[i].name, + FLIPPER_SERVICES[i].stack_size, + FLIPPER_SERVICES[i].app, + NULL); furi_thread_mark_as_service(thread); furi_thread_start(thread); diff --git a/furi/furi.c b/furi/furi.c index 76aed024f..a616bce63 100644 --- a/furi/furi.c +++ b/furi/furi.c @@ -15,9 +15,9 @@ void furi_run() { furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); #if(__ARM_ARCH_7A__ == 0U) - /* Service Call interrupt might be configured before kernel start */ - /* and when its priority is lower or equal to BASEPRI, svc intruction */ - /* causes a Hard Fault. */ + /* Service Call interrupt might be configured before kernel start */ + /* and when its priority is lower or equal to BASEPRI, svc instruction */ + /* causes a Hard Fault. */ NVIC_SetPriority(SVCall_IRQn, 0U); #endif diff --git a/lib/SConscript b/lib/SConscript index 60ffabfa9..abede5f33 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -2,19 +2,19 @@ Import("env") env.Append( LINT_SOURCES=[ - "lib/app-scened-template", - "lib/digital_signal", - "lib/drivers", - "lib/flipper_format", - "lib/infrared", - "lib/nfc", - "lib/one_wire", - "lib/ST25RFAL002", - "lib/subghz", - "lib/toolbox", - "lib/u8g2", - "lib/update_util", - "lib/print", + Dir("app-scened-template"), + Dir("digital_signal"), + Dir("drivers"), + Dir("flipper_format"), + Dir("infrared"), + Dir("nfc"), + Dir("one_wire"), + Dir("ST25RFAL002"), + Dir("subghz"), + Dir("toolbox"), + Dir("u8g2"), + Dir("update_util"), + Dir("print"), ], SDK_HEADERS=[ File("one_wire/one_wire_host_timing.h"), diff --git a/lib/ST25RFAL002/platform.c b/lib/ST25RFAL002/platform.c index af9dc6ff7..754e25650 100644 --- a/lib/ST25RFAL002/platform.c +++ b/lib/ST25RFAL002/platform.c @@ -45,11 +45,9 @@ void platformDisableIrqCallback() { void platformSetIrqCallback(PlatformIrqCallback callback) { rfal_platform.callback = callback; - rfal_platform.thread = furi_thread_alloc(); - - furi_thread_set_name(rfal_platform.thread, "RfalIrqDriver"); - furi_thread_set_callback(rfal_platform.thread, rfal_platform_irq_thread); - furi_thread_set_stack_size(rfal_platform.thread, 1024); + rfal_platform.thread = + furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); + furi_thread_mark_as_service(rfal_platform.thread); furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); furi_thread_start(rfal_platform.thread); diff --git a/lib/drivers/bq25896.c b/lib/drivers/bq25896.c index 1fb9d53e7..7e3008d62 100644 --- a/lib/drivers/bq25896.c +++ b/lib/drivers/bq25896.c @@ -132,6 +132,33 @@ bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle) { return bq25896_regs.r03.OTG_CONFIG; } +uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle) { + furi_hal_i2c_read_reg_8( + handle, BQ25896_ADDRESS, 0x06, (uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT); + return (uint16_t)bq25896_regs.r06.VREG * 16 + 3840; +} + +void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage) { + if(vreg_voltage < 3840) { + // Minimum value is 3840 mV + bq25896_regs.r06.VREG = 0; + } else { + // Find the nearest voltage value (subtract offset, divide into sections) + // Values are truncated downward as needed (e.g. 4200mV -> 4192 mV) + bq25896_regs.r06.VREG = (uint8_t)((vreg_voltage - 3840) / 16); + } + + // Do not allow values above 23 (0x17, 4208mV) + // Exceeding 4.2v will overcharge the battery! + if(bq25896_regs.r06.VREG > 23) { + bq25896_regs.r06.VREG = 23; + } + + // Apply changes + furi_hal_i2c_write_reg_8( + handle, BQ25896_ADDRESS, 0x06, *(uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT); +} + bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle) { furi_hal_i2c_read_reg_8( handle, BQ25896_ADDRESS, 0x0C, (uint8_t*)&bq25896_regs.r0C, BQ25896_I2C_TIMEOUT); diff --git a/lib/drivers/bq25896.h b/lib/drivers/bq25896.h index c8da0a064..c8a8526a1 100644 --- a/lib/drivers/bq25896.h +++ b/lib/drivers/bq25896.h @@ -36,6 +36,15 @@ void bq25896_disable_otg(FuriHalI2cBusHandle* handle); /** Is otg enabled */ bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle); +/** Get VREG (charging) voltage in mV */ +uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle); + +/** Set VREG (charging) voltage in mV + * + * Valid range: 3840mV - 4208mV, in steps of 16mV + */ +void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage); + /** Check OTG BOOST Fault status */ bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle); diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index cf44eebb2..618a36231 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -104,11 +104,8 @@ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); furi_check(manifest->stack_size > 0); - app->thread = furi_thread_alloc(); - furi_thread_set_stack_size(app->thread, manifest->stack_size); - furi_thread_set_name(app->thread, manifest->name); - furi_thread_set_callback(app->thread, flipper_application_thread); - furi_thread_set_context(app->thread, args); + app->thread = furi_thread_alloc_ex( + manifest->name, manifest->stack_size, flipper_application_thread, args); return app->thread; } diff --git a/lib/flipper_format/flipper_format_stream.c b/lib/flipper_format/flipper_format_stream.c index 41934a3b1..9cce95d47 100644 --- a/lib/flipper_format/flipper_format_stream.c +++ b/lib/flipper_format/flipper_format_stream.c @@ -313,7 +313,7 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa furi_crash("Unknown FF type"); } - if((size_t)(i + 1) < write_data->data_size) { + if(((size_t)i + 1) < write_data->data_size) { furi_string_cat(value, " "); } diff --git a/lib/infrared/encoder_decoder/common/infrared_common_decoder.c b/lib/infrared/encoder_decoder/common/infrared_common_decoder.c index bff4c73db..7f1c3a4fd 100644 --- a/lib/infrared/encoder_decoder/common/infrared_common_decoder.c +++ b/lib/infrared/encoder_decoder/common/infrared_common_decoder.c @@ -85,8 +85,8 @@ static InfraredStatus infrared_common_decode_bits(InfraredCommonDecoder* decoder if(timings->min_split_time && !level) { if(timing > timings->min_split_time) { /* long low timing - check if we're ready for any of protocol modification */ - for(size_t i = 0; decoder->protocol->databit_len[i] && - (i < COUNT_OF(decoder->protocol->databit_len)); + for(size_t i = 0; i < COUNT_OF(decoder->protocol->databit_len) && + decoder->protocol->databit_len[i]; ++i) { if(decoder->protocol->databit_len[i] == decoder->databit_cnt) { return InfraredStatusReady; @@ -199,7 +199,7 @@ InfraredMessage* infrared_common_decoder_check_ready(InfraredCommonDecoder* deco bool found_length = false; for(size_t i = 0; - decoder->protocol->databit_len[i] && (i < COUNT_OF(decoder->protocol->databit_len)); + i < COUNT_OF(decoder->protocol->databit_len) && decoder->protocol->databit_len[i]; ++i) { if(decoder->protocol->databit_len[i] == decoder->databit_cnt) { found_length = true; diff --git a/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c b/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c index e8f7664a7..3dd26e9d8 100644 --- a/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c +++ b/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c @@ -115,3 +115,26 @@ const InfraredCommonProtocolSpec protocol_sirc = { .decode_repeat = NULL, .encode_repeat = infrared_encoder_sirc_encode_repeat, }; + +const InfraredCommonProtocolSpec protocol_kaseikyo = { + .timings = + { + .preamble_mark = INFRARED_KASEIKYO_PREAMBLE_MARK, + .preamble_space = INFRARED_KASEIKYO_PREAMBLE_SPACE, + .bit1_mark = INFRARED_KASEIKYO_BIT1_MARK, + .bit1_space = INFRARED_KASEIKYO_BIT1_SPACE, + .bit0_mark = INFRARED_KASEIKYO_BIT0_MARK, + .bit0_space = INFRARED_KASEIKYO_BIT0_SPACE, + .preamble_tolerance = INFRARED_KASEIKYO_PREAMBLE_TOLERANCE, + .bit_tolerance = INFRARED_KASEIKYO_BIT_TOLERANCE, + .silence_time = INFRARED_KASEIKYO_SILENCE, + .min_split_time = INFRARED_KASEIKYO_MIN_SPLIT_TIME, + }, + .databit_len[0] = 48, + .no_stop_bit = false, + .decode = infrared_common_decode_pdwm, + .encode = infrared_common_encode_pdwm, + .interpret = infrared_decoder_kaseikyo_interpret, + .decode_repeat = NULL, + .encode_repeat = NULL, +}; diff --git a/lib/infrared/encoder_decoder/infrared.c b/lib/infrared/encoder_decoder/infrared.c index 71bd6bb6d..2c5ef0fff 100644 --- a/lib/infrared/encoder_decoder/infrared.c +++ b/lib/infrared/encoder_decoder/infrared.c @@ -110,6 +110,20 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { .free = infrared_encoder_sirc_free}, .get_protocol_spec = infrared_sirc_get_spec, }, + { + .decoder = + {.alloc = infrared_decoder_kaseikyo_alloc, + .decode = infrared_decoder_kaseikyo_decode, + .reset = infrared_decoder_kaseikyo_reset, + .check_ready = infrared_decoder_kaseikyo_check_ready, + .free = infrared_decoder_kaseikyo_free}, + .encoder = + {.alloc = infrared_encoder_kaseikyo_alloc, + .encode = infrared_encoder_kaseikyo_encode, + .reset = infrared_encoder_kaseikyo_reset, + .free = infrared_encoder_kaseikyo_free}, + .get_protocol_spec = infrared_kaseikyo_get_spec, + }, }; static int infrared_find_index_by_protocol(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/infrared.h b/lib/infrared/encoder_decoder/infrared.h index 945913000..086950f1e 100644 --- a/lib/infrared/encoder_decoder/infrared.h +++ b/lib/infrared/encoder_decoder/infrared.h @@ -31,6 +31,7 @@ typedef enum { InfraredProtocolSIRC, InfraredProtocolSIRC15, InfraredProtocolSIRC20, + InfraredProtocolKaseikyo, InfraredProtocolMAX, } InfraredProtocol; diff --git a/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h b/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h index 575961f13..6146f7b4e 100644 --- a/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h +++ b/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h @@ -267,3 +267,54 @@ InfraredStatus infrared_encoder_sirc_encode_repeat( bool* level); extern const InfraredCommonProtocolSpec protocol_sirc; + +/*************************************************************************************************** +* Kaseikyo protocol description +* https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp +**************************************************************************************************** +* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble +* mark space Modulation up to period repeat repeat +* mark space +* +* 3360 1665 48 bit ...130000 3456 1728 +* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________ +* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ___________ +* +***************************************************************************************************/ + +#define INFRARED_KASEIKYO_UNIT 432 +#define INFRARED_KASEIKYO_PREAMBLE_MARK (8 * INFRARED_KASEIKYO_UNIT) +#define INFRARED_KASEIKYO_PREAMBLE_SPACE (4 * INFRARED_KASEIKYO_UNIT) +#define INFRARED_KASEIKYO_BIT1_MARK INFRARED_KASEIKYO_UNIT +#define INFRARED_KASEIKYO_BIT1_SPACE (3 * INFRARED_KASEIKYO_UNIT) +#define INFRARED_KASEIKYO_BIT0_MARK INFRARED_KASEIKYO_UNIT +#define INFRARED_KASEIKYO_BIT0_SPACE INFRARED_KASEIKYO_UNIT +#define INFRARED_KASEIKYO_REPEAT_PERIOD 130000 +#define INFRARED_KASEIKYO_SILENCE INFRARED_KASEIKYO_REPEAT_PERIOD +#define INFRARED_KASEIKYO_MIN_SPLIT_TIME INFRARED_KASEIKYO_REPEAT_PAUSE_MIN +#define INFRARED_KASEIKYO_REPEAT_PAUSE_MIN 4000 +#define INFRARED_KASEIKYO_REPEAT_PAUSE_MAX 150000 +#define INFRARED_KASEIKYO_REPEAT_MARK INFRARED_KASEIKYO_PREAMBLE_MARK +#define INFRARED_KASEIKYO_REPEAT_SPACE (INFRARED_KASEIKYO_REPEAT_PERIOD - 56000) +#define INFRARED_KASEIKYO_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_KASEIKYO_BIT_TOLERANCE 120 // us + +void* infrared_decoder_kaseikyo_alloc(void); +void infrared_decoder_kaseikyo_reset(void* decoder); +void infrared_decoder_kaseikyo_free(void* decoder); +InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* decoder); +InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration); +void* infrared_encoder_kaseikyo_alloc(void); +InfraredStatus + infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level); +void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message); +void infrared_encoder_kaseikyo_free(void* encoder_ptr); +bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder); +InfraredStatus infrared_decoder_kaseikyo_decode_repeat(InfraredCommonDecoder* decoder); +InfraredStatus infrared_encoder_kaseikyo_encode_repeat( + InfraredCommonEncoder* encoder, + uint32_t* duration, + bool* level); +const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol); + +extern const InfraredCommonProtocolSpec protocol_kaseikyo; diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c b/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c new file mode 100644 index 000000000..b8db81d7e --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c @@ -0,0 +1,54 @@ +#include "infrared.h" +#include "infrared_protocol_defs_i.h" +#include +#include +#include +#include "../infrared_i.h" + +InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* ctx) { + return infrared_common_decoder_check_ready(ctx); +} + +bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder) { + furi_assert(decoder); + + bool result = false; + uint16_t vendor_id = ((uint16_t)(decoder->data[1]) << 8) | (uint16_t)decoder->data[0]; + uint8_t vendor_parity = decoder->data[2] & 0x0f; + uint8_t genre1 = decoder->data[2] >> 4; + uint8_t genre2 = decoder->data[3] & 0x0f; + uint16_t data = (uint16_t)(decoder->data[3] >> 4) | ((uint16_t)(decoder->data[4] & 0x3f) << 4); + uint8_t id = decoder->data[4] >> 6; + uint8_t parity = decoder->data[5]; + + uint8_t vendor_parity_check = decoder->data[0] ^ decoder->data[1]; + vendor_parity_check = (vendor_parity_check & 0xf) ^ (vendor_parity_check >> 4); + uint8_t parity_check = decoder->data[2] ^ decoder->data[3] ^ decoder->data[4]; + + if(vendor_parity == vendor_parity_check && parity == parity_check) { + decoder->message.command = (uint32_t)data; + decoder->message.address = ((uint32_t)id << 24) | ((uint32_t)vendor_id << 8) | + ((uint32_t)genre1 << 4) | (uint32_t)genre2; + decoder->message.protocol = InfraredProtocolKaseikyo; + decoder->message.repeat = false; + result = true; + } + + return result; +} + +void* infrared_decoder_kaseikyo_alloc(void) { + return infrared_common_decoder_alloc(&protocol_kaseikyo); +} + +InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration) { + return infrared_common_decode(decoder, level, duration); +} + +void infrared_decoder_kaseikyo_free(void* decoder) { + infrared_common_decoder_free(decoder); +} + +void infrared_decoder_kaseikyo_reset(void* decoder) { + infrared_common_decoder_reset(decoder); +} diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c b/lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c new file mode 100644 index 000000000..5814c7255 --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c @@ -0,0 +1,45 @@ +#include +#include "common/infrared_common_i.h" +#include +#include "../infrared_i.h" +#include "infrared_protocol_defs_i.h" +#include + +void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message) { + furi_assert(encoder_ptr); + + InfraredCommonEncoder* encoder = encoder_ptr; + infrared_common_encoder_reset(encoder); + + uint32_t address = message->address; + uint16_t command = message->command; + + uint8_t id = (address >> 24) & 3; + uint16_t vendor_id = (address >> 8) & 0xffff; + uint8_t genre1 = (address >> 4) & 0xf; + uint8_t genre2 = address & 0xf; + + encoder->data[0] = (uint8_t)(vendor_id & 0xff); + encoder->data[1] = (uint8_t)(vendor_id >> 8); + uint8_t vendor_parity = encoder->data[0] ^ encoder->data[1]; + vendor_parity = (vendor_parity & 0xf) ^ (vendor_parity >> 4); + encoder->data[2] = (vendor_parity & 0xf) | (genre1 << 4); + encoder->data[3] = (genre2 & 0xf) | ((uint8_t)(command & 0xf) << 4); + encoder->data[4] = (id << 6) | (uint8_t)(command >> 4); + encoder->data[5] = encoder->data[2] ^ encoder->data[3] ^ encoder->data[4]; + + encoder->bits_to_encode = encoder->protocol->databit_len[0]; +} + +void* infrared_encoder_kaseikyo_alloc(void) { + return infrared_common_encoder_alloc(&protocol_kaseikyo); +} + +void infrared_encoder_kaseikyo_free(void* encoder_ptr) { + infrared_common_encoder_free(encoder_ptr); +} + +InfraredStatus + infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level) { + return infrared_common_encode(encoder_ptr, duration, level); +} diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_kaseikyo_spec.c b/lib/infrared/encoder_decoder/kaseikyo/infrared_kaseikyo_spec.c new file mode 100644 index 000000000..87c86c7b3 --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_kaseikyo_spec.c @@ -0,0 +1,17 @@ +#include "../infrared_i.h" +#include "infrared_protocol_defs_i.h" + +static const InfraredProtocolSpecification infrared_kaseikyo_protocol_specification = { + .name = "Kaseikyo", + .address_length = 26, + .command_length = 10, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, +}; + +const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol) { + if(protocol == InfraredProtocolKaseikyo) + return &infrared_kaseikyo_protocol_specification; + else + return NULL; +} diff --git a/lib/infrared/worker/infrared_worker.c b/lib/infrared/worker/infrared_worker.c index c03f180f6..57288f3c3 100644 --- a/lib/infrared/worker/infrared_worker.c +++ b/lib/infrared/worker/infrared_worker.c @@ -223,10 +223,7 @@ void infrared_worker_rx_set_received_signal_callback( InfraredWorker* infrared_worker_alloc() { InfraredWorker* instance = malloc(sizeof(InfraredWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "InfraredWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); + instance->thread = furi_thread_alloc_ex("InfraredWorker", 2048, NULL, instance); size_t buffer_size = MAX(sizeof(InfraredWorkerTiming) * (MAX_TIMINGS_AMOUNT + 1), diff --git a/lib/lfrfid/SConscript b/lib/lfrfid/SConscript index 69ea9d3c1..f9431ca75 100644 --- a/lib/lfrfid/SConscript +++ b/lib/lfrfid/SConscript @@ -2,7 +2,7 @@ Import("env") env.Append( LINT_SOURCES=[ - "lib/lfrfid", + Dir("."), ], CPPPATH=[ "#/lib/lfrfid", diff --git a/lib/lfrfid/lfrfid_raw_worker.c b/lib/lfrfid/lfrfid_raw_worker.c index 1547d20f9..8c69acedb 100644 --- a/lib/lfrfid/lfrfid_raw_worker.c +++ b/lib/lfrfid/lfrfid_raw_worker.c @@ -61,10 +61,7 @@ static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context); LFRFIDRawWorker* lfrfid_raw_worker_alloc() { LFRFIDRawWorker* worker = malloc(sizeof(LFRFIDRawWorker)); - worker->thread = furi_thread_alloc(); - furi_thread_set_name(worker->thread, "lfrfid_raw_worker"); - furi_thread_set_context(worker->thread, worker); - furi_thread_set_stack_size(worker->thread, 2048); + worker->thread = furi_thread_alloc_ex("LfrfidRawWorker", 2048, NULL, worker); worker->events = furi_event_flag_alloc(NULL); diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c index 8b4f8b6a9..f33c1aed6 100644 --- a/lib/lfrfid/lfrfid_worker.c +++ b/lib/lfrfid/lfrfid_worker.c @@ -29,11 +29,7 @@ LFRFIDWorker* lfrfid_worker_alloc(ProtocolDict* dict) { worker->raw_filename = NULL; worker->mode_storage = NULL; - worker->thread = furi_thread_alloc(); - furi_thread_set_name(worker->thread, "lfrfid_worker"); - furi_thread_set_callback(worker->thread, lfrfid_worker_thread); - furi_thread_set_context(worker->thread, worker); - furi_thread_set_stack_size(worker->thread, 2048); + worker->thread = furi_thread_alloc_ex("LfrfidWorker", 2048, lfrfid_worker_thread, worker); worker->protocols = dict; @@ -140,9 +136,8 @@ size_t lfrfid_worker_dict_get_data_size(LFRFIDWorker* worker, LFRFIDProtocol pro static int32_t lfrfid_worker_thread(void* thread_context) { LFRFIDWorker* worker = thread_context; - bool running = true; - while(running) { + while(true) { uint32_t flags = furi_thread_flags_wait(LFRFIDEventAll, FuriFlagWaitAny, FuriWaitForever); if(flags != FuriFlagErrorTimeout) { // stop thread diff --git a/lib/libusb_stm32 b/lib/libusb_stm32 index 6a88ec4d7..9168e2a31 160000 --- a/lib/libusb_stm32 +++ b/lib/libusb_stm32 @@ -1 +1 @@ -Subproject commit 6a88ec4d7709ca8605b5ec3e609057c330ca2a70 +Subproject commit 9168e2a31db946326fb84016a74ea2ab5bf87f54 diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index c6b70a677..b086298de 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -5,7 +5,7 @@ env.Append( "#/lib/nfc", ], SDK_HEADERS=[ - File("#/lib/nfc/nfc_device.h"), + File("nfc_device.h"), ], ) diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/helpers/reader_analyzer.c index 7fed9c6f6..73b4b125e 100644 --- a/lib/nfc/helpers/reader_analyzer.c +++ b/lib/nfc/helpers/reader_analyzer.c @@ -104,11 +104,8 @@ ReaderAnalyzer* reader_analyzer_alloc() { instance->stream = furi_stream_buffer_alloc(READER_ANALYZER_MAX_BUFF_SIZE, sizeof(ReaderAnalyzerHeader)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "ReaderAnalyzerWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_callback(instance->thread, reader_analyzer_thread); - furi_thread_set_context(instance->thread, instance); + instance->thread = + furi_thread_alloc_ex("ReaderAnalyzerWorker", 2048, reader_analyzer_thread, instance); furi_thread_set_priority(instance->thread, FuriThreadPriorityLow); return instance; diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index a5e3fc14f..3ab10a4f6 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -7,11 +7,12 @@ #include #include -#define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/cache") +#define TAG "NfcDevice" +#define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/.cache") #define NFC_DEVICE_KEYS_EXTENSION ".keys" static const char* nfc_file_header = "Flipper NFC device"; -static const uint32_t nfc_file_version = 2; +static const uint32_t nfc_file_version = 3; static const char* nfc_keys_file_header = "Flipper NFC keys"; static const uint32_t nfc_keys_file_version = 1; @@ -26,6 +27,11 @@ NfcDevice* nfc_device_alloc() { nfc_dev->dialogs = furi_record_open(RECORD_DIALOGS); nfc_dev->load_path = furi_string_alloc(); nfc_dev->dev_data.parsed_data = furi_string_alloc(); + + // Rename cache folder name for backward compatibility + if(storage_common_stat(nfc_dev->storage, "/ext/nfc/cache", NULL) == FSE_OK) { + storage_common_rename(nfc_dev->storage, "/ext/nfc/cache", NFC_DEVICE_KEYS_FOLDER); + } return nfc_dev; } @@ -213,6 +219,9 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { uint32_t auth_counter; if(!flipper_format_read_uint32(file, "Failed authentication attempts", &auth_counter, 1)) auth_counter = 0; + data->curr_authlim = auth_counter; + + data->auth_success = mf_ul_is_full_capture(data); parsed = true; } while(false); @@ -627,7 +636,10 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { *app_head = app; app_head = &app->next; } - if(!parsed_apps) break; + if(!parsed_apps) { + // accept non-parsed apps, just log a warning: + FURI_LOG_W(TAG, "Non-parsed apps found!"); + } } parsed = true; } while(false); @@ -1006,12 +1018,7 @@ static void nfc_device_get_shadow_path(FuriString* orig_path, FuriString* shadow furi_string_cat_printf(shadow_path, "%s", NFC_APP_SHADOW_EXTENSION); } -static bool nfc_device_save_file( - NfcDevice* dev, - const char* dev_name, - const char* folder, - const char* extension, - bool use_load_path) { +bool nfc_device_save(NfcDevice* dev, const char* dev_name) { furi_assert(dev); bool saved = false; @@ -1021,19 +1028,10 @@ static bool nfc_device_save_file( temp_str = furi_string_alloc(); do { - if(use_load_path && !furi_string_empty(dev->load_path)) { - // Get directory name - path_extract_dirname(furi_string_get_cstr(dev->load_path), temp_str); - // Create nfc directory if necessary - if(!storage_simply_mkdir(dev->storage, furi_string_get_cstr(temp_str))) break; - // Make path to file to save - furi_string_cat_printf(temp_str, "/%s%s", dev_name, extension); - } else { - // Create nfc directory if necessary - if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break; - // First remove nfc device file if it was saved - furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension); - } + // Create nfc directory if necessary + if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break; + // First remove nfc device file if it was saved + furi_string_printf(temp_str, "%s", dev_name); // Open file if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; // Write header @@ -1048,7 +1046,9 @@ static bool nfc_device_save_file( if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) break; if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; - if(!flipper_format_write_hex(file, "ATQA", data->atqa, 2)) break; + // Save ATQA in MSB order for correct companion apps display + uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; + if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; // Save more data if necessary if(dev->format == NfcDeviceSaveFormatMifareUl) { @@ -1072,13 +1072,19 @@ static bool nfc_device_save_file( return saved; } -bool nfc_device_save(NfcDevice* dev, const char* dev_name) { - return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_EXTENSION, true); -} - -bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) { +bool nfc_device_save_shadow(NfcDevice* dev, const char* path) { dev->shadow_file_exist = true; - return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION, true); + // Replace extension from .nfc to .shd if necessary + FuriString* orig_path = furi_string_alloc(); + furi_string_set_str(orig_path, path); + FuriString* shadow_path = furi_string_alloc(); + nfc_device_get_shadow_path(orig_path, shadow_path); + + bool file_saved = nfc_device_save(dev, furi_string_get_cstr(shadow_path)); + furi_string_free(orig_path); + furi_string_free(shadow_path); + + return file_saved; } static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dialog) { @@ -1090,6 +1096,9 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia temp_str = furi_string_alloc(); bool deprecated_version = false; + // Version 2 of file format had ATQA bytes swapped + uint32_t version_with_lsb_atqa = 2; + if(dev->loading_cb) { dev->loading_cb(dev->loading_cb_ctx, true); } @@ -1108,9 +1117,12 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia // Read and verify file header uint32_t version = 0; if(!flipper_format_read_header(file, temp_str, &version)) break; - if(furi_string_cmp_str(temp_str, nfc_file_header) || (version != nfc_file_version)) { - deprecated_version = true; - break; + if(furi_string_cmp_str(temp_str, nfc_file_header)) break; + if(version != nfc_file_version) { + if(version < version_with_lsb_atqa) { + deprecated_version = true; + break; + } } // Read Nfc device type if(!flipper_format_read_string(file, "Device type", temp_str)) break; @@ -1120,7 +1132,14 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!(data_cnt == 4 || data_cnt == 7)) break; data->uid_len = data_cnt; if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; - if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; + if(version == version_with_lsb_atqa) { + if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; + } else { + uint8_t atqa[2] = {}; + if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; + data->atqa[0] = atqa[1]; + data->atqa[1] = atqa[0]; + } if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; // Load CUID uint8_t* cuid_start = data->uid; @@ -1188,14 +1207,16 @@ bool nfc_file_select(NfcDevice* dev) { const DialogsFileBrowserOptions browser_options = { .extension = NFC_APP_EXTENSION, .skip_assets = true, + .hide_dot_files = true, .icon = &I_Nfc_10px, .hide_ext = true, .item_loader_callback = NULL, .item_loader_context = NULL, + .base_path = NFC_APP_FOLDER, }; bool res = - dialog_file_browser_show(dev->dialogs, dev->load_path, nfc_app_folder, &browser_options); + dialog_file_browser_show(dev->dialogs, dev->load_path, dev->load_path, &browser_options); furi_string_free(nfc_app_folder); if(res) { diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index c8e8517ae..4be07f016 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -51,12 +51,23 @@ typedef struct { MfClassicDict* dict; } NfcMfClassicDictAttackData; +typedef enum { + NfcReadModeAuto, + NfcReadModeMfClassic, + NfcReadModeMfUltralight, + NfcReadModeMfDesfire, + NfcReadModeEMV, + NfcReadModeNFCA, +} NfcReadMode; + typedef struct { FuriHalNfcDevData nfc_data; NfcProtocol protocol; + NfcReadMode read_mode; union { NfcReaderRequestData reader_data; NfcMfClassicDictAttackData mf_classic_dict_attack_data; + MfUltralightAuth mf_ul_auth; }; union { EmvData emv_data; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index e1e379a06..450428a18 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -12,11 +12,7 @@ NfcWorker* nfc_worker_alloc() { NfcWorker* nfc_worker = malloc(sizeof(NfcWorker)); // Worker thread attributes - nfc_worker->thread = furi_thread_alloc(); - furi_thread_set_name(nfc_worker->thread, "NfcWorker"); - furi_thread_set_stack_size(nfc_worker->thread, 8192); - furi_thread_set_callback(nfc_worker->thread, nfc_worker_task); - furi_thread_set_context(nfc_worker->thread, nfc_worker); + nfc_worker->thread = furi_thread_alloc_ex("NfcWorker", 8192, nfc_worker_task, nfc_worker); nfc_worker->callback = NULL; nfc_worker->context = NULL; @@ -70,12 +66,12 @@ void nfc_worker_start( void nfc_worker_stop(NfcWorker* nfc_worker) { furi_assert(nfc_worker); - if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) { - return; + furi_assert(nfc_worker->thread); + if(furi_thread_get_state(nfc_worker->thread) != FuriThreadStateStopped) { + furi_hal_nfc_stop(); + nfc_worker_change_state(nfc_worker, NfcWorkerStateStop); + furi_thread_join(nfc_worker->thread); } - furi_hal_nfc_stop(); - nfc_worker_change_state(nfc_worker, NfcWorkerStateStop); - furi_thread_join(nfc_worker->thread); } void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) { @@ -90,7 +86,11 @@ int32_t nfc_worker_task(void* context) { furi_hal_nfc_exit_sleep(); if(nfc_worker->state == NfcWorkerStateRead) { - nfc_worker_read(nfc_worker); + if(nfc_worker->dev_data->read_mode == NfcReadModeAuto) { + nfc_worker_read(nfc_worker); + } else { + nfc_worker_read_type(nfc_worker); + } } else if(nfc_worker->state == NfcWorkerStateUidEmulate) { nfc_worker_emulate_uid(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { @@ -394,6 +394,81 @@ void nfc_worker_read(NfcWorker* nfc_worker) { } } +void nfc_worker_read_type(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + NfcReadMode read_mode = nfc_worker->dev_data->read_mode; + nfc_device_data_clear(nfc_worker->dev_data); + NfcDeviceData* dev_data = nfc_worker->dev_data; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + FuriHalNfcTxRxContext tx_rx = {}; + NfcWorkerEvent event = 0; + bool card_not_detected_notified = false; + + while(nfc_worker->state == NfcWorkerStateRead) { + if(furi_hal_nfc_detect(nfc_data, 300)) { + FURI_LOG_D(TAG, "Card detected"); + furi_hal_nfc_sleep(); + // Process first found device + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + card_not_detected_notified = false; + if(nfc_data->type == FuriHalNfcTypeA) { + if(read_mode == NfcReadModeMfClassic) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic; + nfc_worker->dev_data->mf_classic_data.type = mf_classic_get_classic_type( + nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + if(nfc_worker_read_mf_classic(nfc_worker, &tx_rx)) { + FURI_LOG_D(TAG, "Card read"); + dev_data->protocol = NfcDeviceProtocolMifareClassic; + event = NfcWorkerEventReadMfClassicDone; + break; + } else { + FURI_LOG_D(TAG, "Card read failed"); + dev_data->protocol = NfcDeviceProtocolMifareClassic; + event = NfcWorkerEventReadMfClassicDictAttackRequired; + break; + } + } else if(read_mode == NfcReadModeMfUltralight) { + FURI_LOG_I(TAG, "Mifare Ultralight / NTAG"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareUl; + if(nfc_worker_read_mf_ultralight(nfc_worker, &tx_rx)) { + event = NfcWorkerEventReadMfUltralight; + break; + } + } else if(read_mode == NfcReadModeMfDesfire) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareDesfire; + if(nfc_worker_read_mf_desfire(nfc_worker, &tx_rx)) { + event = NfcWorkerEventReadMfDesfire; + break; + } + } else if(read_mode == NfcReadModeEMV) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV; + if(nfc_worker_read_bank_card(nfc_worker, &tx_rx)) { + event = NfcWorkerEventReadBankCard; + break; + } + } else if(read_mode == NfcReadModeNFCA) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + event = NfcWorkerEventReadUidNfcA; + break; + } + } else { + if(!card_not_detected_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_not_detected_notified = true; + } + } + } + furi_hal_nfc_sleep(); + furi_delay_ms(100); + } + // Notify caller and exit + if(event > NfcWorkerEventReserved) { + nfc_worker->callback(event, nfc_worker->context); + } +} + void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data; @@ -452,10 +527,25 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { } } +void nfc_worker_mf_ultralight_auth_received_callback(MfUltralightAuth auth, void* context) { + furi_assert(context); + + NfcWorker* nfc_worker = context; + nfc_worker->dev_data->mf_ul_auth = auth; + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventMfUltralightPwdAuth, nfc_worker->context); + } +} + void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; MfUltralightEmulator emulator = {}; mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data); + + // TODO rework with reader analyzer + emulator.auth_received_callback = nfc_worker_mf_ultralight_auth_received_callback; + emulator.context = nfc_worker; + while(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { mf_ul_reset_emulation(&emulator, true); furi_hal_nfc_emulate_nfca( @@ -830,7 +920,8 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { if(furi_hal_nfc_detect(nfc_data, 300) && nfc_data->type == FuriHalNfcTypeA) { if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); - if(data->auth_method == MfUltralightAuthMethodManual) { + if(data->auth_method == MfUltralightAuthMethodManual || + data->auth_method == MfUltralightAuthMethodAuto) { nfc_worker->callback(NfcWorkerEventMfUltralightPassKey, nfc_worker->context); key = nfc_util_bytes2num(data->auth_key, 4); } else if(data->auth_method == MfUltralightAuthMethodAmeebo) { diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index ce3a18241..fdcaa72fd 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -7,7 +7,6 @@ typedef struct NfcWorker NfcWorker; typedef enum { // Init states NfcWorkerStateNone, - NfcWorkerStateBroken, NfcWorkerStateReady, // Main worker states NfcWorkerStateRead, @@ -66,8 +65,8 @@ typedef enum { NfcWorkerEventDetectReaderMfkeyCollected, // Mifare Ultralight events - NfcWorkerEventMfUltralightPassKey, - + NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key + NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index b9f69e620..9733426ab 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -35,6 +35,8 @@ int32_t nfc_worker_task(void* context); void nfc_worker_read(NfcWorker* nfc_worker); +void nfc_worker_read_type(NfcWorker* nfc_worker); + void nfc_worker_emulate_uid(NfcWorker* nfc_worker); void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 7b0e17975..f4c7353a8 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -82,7 +82,7 @@ uint8_t mf_classic_get_total_sectors_num(MfClassicType type) { } } -static uint16_t mf_classic_get_total_block_num(MfClassicType type) { +uint16_t mf_classic_get_total_block_num(MfClassicType type) { if(type == MfClassicType1k) { return 64; } else if(type == MfClassicType4k) { @@ -712,46 +712,16 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data furi_assert(tx_rx); furi_assert(data); - uint8_t sectors_read = 0; - Crypto1 crypto = {}; uint8_t total_sectors = mf_classic_get_total_sectors_num(data->type); - uint64_t key_a = 0; - uint64_t key_b = 0; - MfClassicSectorReader sec_reader = {}; - MfClassicSector temp_sector = {}; for(size_t i = 0; i < total_sectors; i++) { - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); - // Load key A - if(mf_classic_is_key_found(data, i, MfClassicKeyA)) { - sec_reader.key_a = nfc_util_bytes2num(sec_tr->key_a, 6); - } else { - sec_reader.key_a = MF_CLASSIC_NO_KEY; - } - // Load key B - if(mf_classic_is_key_found(data, i, MfClassicKeyB)) { - sec_reader.key_b = nfc_util_bytes2num(sec_tr->key_b, 6); - } else { - sec_reader.key_b = MF_CLASSIC_NO_KEY; - } - if((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY)) { - sec_reader.sector_num = i; - if(mf_classic_read_sector_with_reader(tx_rx, &crypto, &sec_reader, &temp_sector)) { - uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); - for(uint8_t j = 0; j < temp_sector.total_blocks; j++) { - mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]); - } - sectors_read++; - } else { - // Invalid key, set it to not found - if(key_a != MF_CLASSIC_NO_KEY) { - mf_classic_set_key_not_found(data, i, MfClassicKeyA); - } else { - mf_classic_set_key_not_found(data, i, MfClassicKeyB); - } - } - } + mf_classic_read_sector(tx_rx, data, i); } + uint8_t sectors_read = 0; + uint8_t keys_found = 0; + mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); + FURI_LOG_D(TAG, "Read %d sectors and %d keys", sectors_read, keys_found); + return sectors_read; } diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index d5467b100..9a0bb5790 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -98,6 +98,8 @@ MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t S uint8_t mf_classic_get_total_sectors_num(MfClassicType type); +uint16_t mf_classic_get_total_block_num(MfClassicType type); + uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector); bool mf_classic_is_sector_trailer(uint8_t block); diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c index a8d1f5548..85e234bd9 100644 --- a/lib/nfc/protocols/mifare_ultralight.c +++ b/lib/nfc/protocols/mifare_ultralight.c @@ -51,7 +51,7 @@ void mf_ul_reset(MfUltralightData* data) { data->data_size = 0; data->data_read = 0; data->curr_authlim = 0; - data->has_auth = false; + data->auth_success = false; } static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) { @@ -756,6 +756,34 @@ bool mf_ul_read_card( mf_ultralight_read_tearing_flags(tx_rx, data); } data->curr_authlim = 0; + + if(reader->pages_read == reader->pages_to_read && + reader->supported_features & MfUltralightSupportAuth && !data->auth_success) { + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + if(config->access.authlim == 0) { + // Attempt to auth with default PWD + uint16_t pack; + data->auth_success = mf_ultralight_authenticate(tx_rx, MF_UL_DEFAULT_PWD, &pack); + if(data->auth_success) { + config->auth_data.pwd.value = MF_UL_DEFAULT_PWD; + config->auth_data.pack.value = pack; + } else { + furi_hal_nfc_sleep(); + furi_hal_nfc_activate_nfca(300, NULL); + } + } + } + } + + if(reader->pages_read != reader->pages_to_read) { + if(reader->supported_features & MfUltralightSupportAuth) { + // Probably password protected, fix AUTH0 and PROT so before AUTH0 + // can be written and since AUTH0 won't be readable, like on the + // original card + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + config->auth0 = reader->pages_read; + config->access.prot = true; + } } return card_read; @@ -1201,6 +1229,8 @@ static void mf_ul_emulate_write( } void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle) { + emulator->comp_write_cmd_started = false; + emulator->sector_select_cmd_started = false; emulator->curr_sector = 0; emulator->ntag_i2c_plus_sector3_lockout = false; emulator->auth_success = false; @@ -1244,8 +1274,7 @@ void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* d emulator->config = mf_ultralight_get_config_pages(&emulator->data); emulator->page_num = emulator->data.data_size / 4; emulator->data_changed = false; - emulator->comp_write_cmd_started = false; - emulator->sector_select_cmd_started = false; + memset(&emulator->auth_attempt, 0, sizeof(MfUltralightAuth)); mf_ul_reset_emulation(emulator, true); } @@ -1706,6 +1735,17 @@ bool mf_ul_prepare_emulation_response( } else if(cmd == MF_UL_AUTH) { if(emulator->supported_features & MfUltralightSupportAuth) { if(buff_rx_len == (1 + 4) * 8) { + // Record password sent by PCD + memcpy( + emulator->auth_attempt.pwd.raw, + &buff_rx[1], + sizeof(emulator->auth_attempt.pwd.raw)); + emulator->auth_attempted = true; + if(emulator->auth_received_callback) { + emulator->auth_received_callback( + emulator->auth_attempt, emulator->context); + } + uint16_t scaled_authlim = mf_ultralight_calc_auth_count(&emulator->data); if(scaled_authlim != 0 && emulator->data.curr_authlim >= scaled_authlim) { if(emulator->data.curr_authlim != UINT16_MAX) { @@ -1863,3 +1903,14 @@ bool mf_ul_prepare_emulation_response( return tx_bits > 0; } + +bool mf_ul_is_full_capture(MfUltralightData* data) { + if(data->data_read != data->data_size) return false; + + // Having read all the pages doesn't mean that we've got everything. + // By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000, + // so a default read on an auth-supported NTAG is never complete. + if(!(mf_ul_get_features(data->type) & MfUltralightSupportAuth)) return true; + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + return config->auth_data.pwd.value != 0 || config->auth_data.pack.value != 0; +} diff --git a/lib/nfc/protocols/mifare_ultralight.h b/lib/nfc/protocols/mifare_ultralight.h index 9642824f7..4ab22e89c 100644 --- a/lib/nfc/protocols/mifare_ultralight.h +++ b/lib/nfc/protocols/mifare_ultralight.h @@ -28,10 +28,13 @@ #define MF_UL_NTAG203_COUNTER_PAGE (41) +#define MF_UL_DEFAULT_PWD (0xFFFFFFFF) + typedef enum { MfUltralightAuthMethodManual, MfUltralightAuthMethodAmeebo, MfUltralightAuthMethodXiaomi, + MfUltralightAuthMethodAuto, } MfUltralightAuthMethod; // Important: order matters; some features are based on positioning in this enum @@ -110,7 +113,6 @@ typedef struct { uint8_t signature[32]; uint32_t counter[3]; uint8_t tearing[3]; - bool has_auth; MfUltralightAuthMethod auth_method; uint8_t auth_key[4]; bool auth_success; @@ -169,6 +171,9 @@ typedef struct { MfUltralightFeatures supported_features; } MfUltralightReader; +// TODO rework with reader analyzer +typedef void (*MfUltralightAuthReceivedCallback)(MfUltralightAuth auth, void* context); + typedef struct { MfUltralightData data; MfUltralightConfigPages* config; @@ -185,6 +190,12 @@ typedef struct { bool sector_select_cmd_started; bool ntag_i2c_plus_sector3_lockout; bool read_counter_incremented; + bool auth_attempted; + MfUltralightAuth auth_attempt; + + // TODO rework with reader analyzer + MfUltralightAuthReceivedCallback auth_received_callback; + void* context; } MfUltralightEmulator; void mf_ul_reset(MfUltralightData* data); @@ -241,3 +252,5 @@ bool mf_ul_prepare_emulation_response( uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data); uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data); + +bool mf_ul_is_full_capture(MfUltralightData* data); diff --git a/lib/one_wire/ibutton/ibutton_worker.c b/lib/one_wire/ibutton/ibutton_worker.c index 26982bcb6..9d5ea3897 100644 --- a/lib/one_wire/ibutton/ibutton_worker.c +++ b/lib/one_wire/ibutton/ibutton_worker.c @@ -37,11 +37,7 @@ iButtonWorker* ibutton_worker_alloc() { worker->emulate_cb = NULL; worker->cb_ctx = NULL; - worker->thread = furi_thread_alloc(); - furi_thread_set_name(worker->thread, "ibutton_worker"); - furi_thread_set_callback(worker->thread, ibutton_worker_thread); - furi_thread_set_context(worker->thread, worker); - furi_thread_set_stack_size(worker->thread, 2048); + worker->thread = furi_thread_alloc_ex("iButtonWorker", 2048, ibutton_worker_thread, worker); worker->protocols = protocol_dict_alloc(ibutton_protocols, iButtonProtocolMax); @@ -135,7 +131,9 @@ void ibutton_worker_switch_mode(iButtonWorker* worker, iButtonWorkerMode mode) { void ibutton_worker_notify_emulate(iButtonWorker* worker) { iButtonMessage message = {.type = iButtonMessageNotifyEmulate}; - furi_check(furi_message_queue_put(worker->messages, &message, 0) == FuriStatusOk); + // we're running in an interrupt context, so we can't wait + // and we can drop message if queue is full, that's ok for that message + furi_message_queue_put(worker->messages, &message, 0); } void ibutton_worker_set_key_p(iButtonWorker* worker, iButtonKey* key) { diff --git a/lib/one_wire/ibutton/ibutton_worker_modes.c b/lib/one_wire/ibutton/ibutton_worker_modes.c index 691aea9ee..b1e5904ca 100644 --- a/lib/one_wire/ibutton/ibutton_worker_modes.c +++ b/lib/one_wire/ibutton/ibutton_worker_modes.c @@ -222,8 +222,8 @@ void ibutton_worker_emulate_dallas_start(iButtonWorker* worker) { memcpy(device_id, key_id, key_size); onewire_slave_attach(worker->slave, worker->device); - onewire_slave_start(worker->slave); onewire_slave_set_result_callback(worker->slave, onewire_slave_callback, worker); + onewire_slave_start(worker->slave); } void ibutton_worker_emulate_dallas_stop(iButtonWorker* worker) { diff --git a/lib/print/printf_tiny.c b/lib/print/printf_tiny.c index 0db11922d..6e47f6528 100644 --- a/lib/print/printf_tiny.c +++ b/lib/print/printf_tiny.c @@ -541,7 +541,7 @@ static size_t _etoa( exp2 = (int)(expval * 3.321928094887362 + 0.5); const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; const double z2 = z * z; - conv.U = (uint64_t)(exp2 + 1023) << 52U; + conv.U = ((uint64_t)exp2 + 1023) << 52U; // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); // correct for rounding errors diff --git a/lib/subghz/protocols/ansonic.c b/lib/subghz/protocols/ansonic.c new file mode 100644 index 000000000..81b196e36 --- /dev/null +++ b/lib/subghz/protocols/ansonic.c @@ -0,0 +1,346 @@ +#include "ansonic.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolAnsonic" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \ + (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \ + (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_ansonic_const = { + .te_short = 555, + .te_long = 1111, + .te_delta = 120, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderAnsonic { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderAnsonic { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + AnsonicDecoderStepReset = 0, + AnsonicDecoderStepFoundStartBit, + AnsonicDecoderStepSaveDuration, + AnsonicDecoderStepCheckDuration, +} AnsonicDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder = { + .alloc = subghz_protocol_decoder_ansonic_alloc, + .free = subghz_protocol_decoder_ansonic_free, + + .feed = subghz_protocol_decoder_ansonic_feed, + .reset = subghz_protocol_decoder_ansonic_reset, + + .get_hash_data = subghz_protocol_decoder_ansonic_get_hash_data, + .serialize = subghz_protocol_decoder_ansonic_serialize, + .deserialize = subghz_protocol_decoder_ansonic_deserialize, + .get_string = subghz_protocol_decoder_ansonic_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder = { + .alloc = subghz_protocol_encoder_ansonic_alloc, + .free = subghz_protocol_encoder_ansonic_free, + + .deserialize = subghz_protocol_encoder_ansonic_deserialize, + .stop = subghz_protocol_encoder_ansonic_stop, + .yield = subghz_protocol_encoder_ansonic_yield, +}; + +const SubGhzProtocol subghz_protocol_ansonic = { + .name = SUBGHZ_PROTOCOL_ANSONIC_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_FM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_ansonic_decoder, + .encoder = &subghz_protocol_ansonic_encoder, +}; + +void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderAnsonic* instance = malloc(sizeof(SubGhzProtocolEncoderAnsonic)); + + instance->base.protocol = &subghz_protocol_ansonic; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_ansonic_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderAnsonic* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderAnsonic instance + * @return true On success + */ +static bool subghz_protocol_encoder_ansonic_get_upload(SubGhzProtocolEncoderAnsonic* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short * 35); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short); + //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(false, (uint32_t)subghz_protocol_ansonic_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short); + } + } + return true; +} + +bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderAnsonic* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_ansonic_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_ansonic_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_ansonic_stop(void* context) { + SubGhzProtocolEncoderAnsonic* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_ansonic_yield(void* context) { + SubGhzProtocolEncoderAnsonic* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderAnsonic* instance = malloc(sizeof(SubGhzProtocolDecoderAnsonic)); + instance->base.protocol = &subghz_protocol_ansonic; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_ansonic_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + free(instance); +} + +void subghz_protocol_decoder_ansonic_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + instance->decoder.parser_step = AnsonicDecoderStepReset; +} + +void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + + switch(instance->decoder.parser_step) { + case AnsonicDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short * 35) < + subghz_protocol_ansonic_const.te_delta * 35)) { + //Found header Ansonic + instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit; + } + break; + case AnsonicDecoderStepFoundStartBit: + if(!level) { + break; + } else if( + DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta) { + //Found start bit Ansonic + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + case AnsonicDecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_ansonic_const.te_short * 4)) { + instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_ansonic_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = AnsonicDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + case AnsonicDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_long) < + subghz_protocol_ansonic_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_long) < + subghz_protocol_ansonic_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + } else + instance->decoder.parser_step = AnsonicDecoderStepReset; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_ansonic_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * 12345678(10) k 9 + * AAA => 10101010 1 01 0 + * + * 1...10 - DIP + * k- KEY + */ + instance->cnt = instance->data & 0xFFF; + instance->btn = ((instance->data >> 1) & 0x3); +} + +uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_ansonic_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_ansonic_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + subghz_protocol_ansonic_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%03lX\r\n" + "Btn:%X\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.btn, + CNT_TO_DIP(instance->generic.cnt)); +} diff --git a/lib/subghz/protocols/ansonic.h b/lib/subghz/protocols/ansonic.h new file mode 100644 index 000000000..0170a6048 --- /dev/null +++ b/lib/subghz/protocols/ansonic.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_ANSONIC_NAME "Ansonic" + +typedef struct SubGhzProtocolDecoderAnsonic SubGhzProtocolDecoderAnsonic; +typedef struct SubGhzProtocolEncoderAnsonic SubGhzProtocolEncoderAnsonic; + +extern const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder; +extern const SubGhzProtocol subghz_protocol_ansonic; + +/** + * Allocate SubGhzProtocolEncoderAnsonic. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderAnsonic* pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderAnsonic. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void subghz_protocol_encoder_ansonic_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void subghz_protocol_encoder_ansonic_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_ansonic_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderAnsonic. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderAnsonic* pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void subghz_protocol_decoder_ansonic_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void subghz_protocol_decoder_ansonic_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic 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_ansonic_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param output Resulting text + */ +void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/nice_flo.c b/lib/subghz/protocols/nice_flo.c index ecd866a72..a57d5f4da 100644 --- a/lib/subghz/protocols/nice_flo.c +++ b/lib/subghz/protocols/nice_flo.c @@ -138,9 +138,9 @@ bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* FURI_LOG_E(TAG, "Deserialize error"); break; } - if((instance->generic.data_count_bit != - subghz_protocol_nice_flo_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != + if((instance->generic.data_count_bit < + subghz_protocol_nice_flo_const.min_count_bit_for_found) || + (instance->generic.data_count_bit > 2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; @@ -297,9 +297,9 @@ bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { break; } - if((instance->generic.data_count_bit != - subghz_protocol_nice_flo_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != + if((instance->generic.data_count_bit < + subghz_protocol_nice_flo_const.min_count_bit_for_found) || + (instance->generic.data_count_bit > 2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; diff --git a/lib/subghz/protocols/power_smart.c b/lib/subghz/protocols/power_smart.c index 63ca78711..1e8d10e95 100644 --- a/lib/subghz/protocols/power_smart.c +++ b/lib/subghz/protocols/power_smart.c @@ -312,7 +312,6 @@ void subghz_protocol_decoder_power_smart_feed( if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) == POWER_SMART_PACKET_HEADER) { if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) { - instance->decoder.decode_data = instance->decoder.decode_data; instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = subghz_protocol_power_smart_const.min_count_bit_for_found; diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index a4a0921ac..ab1c58765 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -15,8 +15,8 @@ #define TAG "SubGhzProtocolPrinceton" static const SubGhzBlockConst subghz_protocol_princeton_const = { - .te_short = 400, - .te_long = 1200, + .te_short = 390, + .te_long = 1170, .te_delta = 300, .min_count_bit_for_found = 24, }; @@ -245,8 +245,7 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t break; case PrincetonDecoderStepCheckDuration: if(!level) { - if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_short * 10 + - subghz_protocol_princeton_const.te_delta)) { + if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_long * 2)) { instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; if(instance->decoder.decode_count_bit == subghz_protocol_princeton_const.min_count_bit_for_found) { diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index ebf9b93e4..24aaae8df 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -12,7 +12,7 @@ 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_clemsa, &subghz_protocol_ansonic, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 23aa71be0..114dc5046 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -35,5 +35,6 @@ #include "magellan.h" #include "intertechno_v3.h" #include "clemsa.h" +#include "ansonic.h" extern const SubGhzProtocolRegistry subghz_protocol_registry; diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 29834b412..abc33188f 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -18,6 +18,7 @@ struct SubGhzFileEncoderWorker { volatile bool worker_running; volatile bool worker_stoping; bool level; + bool is_storage_slow; FuriString* str_data; FuriString* file_path; @@ -86,7 +87,7 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) { if(ret == sizeof(int32_t)) { LevelDuration level_duration = {.level = LEVEL_DURATION_RESET}; if(duration < 0) { - level_duration = level_duration_make(false, duration * -1); + level_duration = level_duration_make(false, -duration); } else if(duration > 0) { level_duration = level_duration_make(true, duration); } else if(duration == 0) { @@ -96,7 +97,7 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) { } return level_duration; } else { - FURI_LOG_E(TAG, "Slow flash read"); + instance->is_storage_slow = true; return level_duration_wait(); } } @@ -110,6 +111,7 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { SubGhzFileEncoderWorker* instance = context; FURI_LOG_I(TAG, "Worker start"); bool res = false; + instance->is_storage_slow = false; Stream* stream = flipper_format_get_raw_stream(instance->flipper_format); do { if(!flipper_format_file_open_existing( @@ -139,21 +141,21 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { furi_string_trim(instance->str_data); if(!subghz_file_encoder_worker_data_parse( instance, furi_string_get_cstr(instance->str_data))) { - //to stop DMA correctly subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); - subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); - break; } } else { - subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); break; } + } else { + furi_delay_ms(1); } - furi_delay_ms(5); } //waiting for the end of the transfer + if(instance->is_storage_slow) { + FURI_LOG_E(TAG, "Storage is slow"); + } FURI_LOG_I(TAG, "End read file"); while(!furi_hal_subghz_is_async_tx_complete() && instance->worker_running) { furi_delay_ms(5); @@ -174,11 +176,8 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() { SubGhzFileEncoderWorker* instance = malloc(sizeof(SubGhzFileEncoderWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzFEWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_file_encoder_worker_thread); + instance->thread = + furi_thread_alloc_ex("SubGhzFEWorker", 2048, subghz_file_encoder_worker_thread, instance); instance->stream = furi_stream_buffer_alloc(sizeof(int32_t) * 2048, sizeof(int32_t)); instance->storage = furi_record_open(RECORD_STORAGE); diff --git a/lib/subghz/subghz_keystore.c b/lib/subghz/subghz_keystore.c index 1b4b3b716..e06bd9796 100644 --- a/lib/subghz/subghz_keystore.c +++ b/lib/subghz/subghz_keystore.c @@ -464,7 +464,7 @@ bool subghz_keystore_raw_encrypted_save( } stream_write_cstring(output_stream, encrypted_line); - } while(ret > 0 && result); + } while(result); flipper_format_free(output_flipper_format); diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 37c0bfc5e..42124bebc 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -201,11 +201,8 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { SubGhzTxRxWorker* subghz_tx_rx_worker_alloc() { SubGhzTxRxWorker* instance = malloc(sizeof(SubGhzTxRxWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzTxRxWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_tx_rx_worker_thread); + instance->thread = + furi_thread_alloc_ex("SubGhzTxRxWorker", 2048, subghz_tx_rx_worker_thread, instance); instance->stream_tx = furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); instance->stream_rx = diff --git a/lib/subghz/subghz_worker.c b/lib/subghz/subghz_worker.c index 61146c168..35e399885 100644 --- a/lib/subghz/subghz_worker.c +++ b/lib/subghz/subghz_worker.c @@ -88,11 +88,8 @@ static int32_t subghz_worker_thread_callback(void* context) { SubGhzWorker* subghz_worker_alloc() { SubGhzWorker* instance = malloc(sizeof(SubGhzWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_worker_thread_callback); + instance->thread = + furi_thread_alloc_ex("SubGhzWorker", 2048, subghz_worker_thread_callback, instance); instance->stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 4096, sizeof(LevelDuration)); diff --git a/lib/toolbox/api_lock.h b/lib/toolbox/api_lock.h new file mode 100644 index 000000000..5902a4922 --- /dev/null +++ b/lib/toolbox/api_lock.h @@ -0,0 +1,43 @@ +#pragma once +#include + +/* +Testing 10000 api calls + +No lock + Time diff: 445269.218750 us + Time per call: 44.526920 us + +furi_thread_flags + Time diff: 430279.875000 us // lol wtf? smaller than no lock? + Time per call: 43.027988 us // I tested it many times, it's always smaller + +FuriEventFlag + Time diff: 831523.625000 us + Time per call: 83.152359 us + +FuriSemaphore + Time diff: 999807.125000 us + Time per call: 99.980713 us + +FuriMutex + Time diff: 1071417.500000 us + Time per call: 107.141747 us +*/ + +typedef FuriEventFlag* FuriApiLock; + +#define API_LOCK_EVENT (1U << 0) + +#define api_lock_alloc_locked() furi_event_flag_alloc() + +#define api_lock_wait_unlock(_lock) \ + furi_event_flag_wait(_lock, API_LOCK_EVENT, FuriFlagWaitAny, FuriWaitForever) + +#define api_lock_free(_lock) furi_event_flag_free(_lock) + +#define api_lock_unlock(_lock) furi_event_flag_set(_lock, API_LOCK_EVENT) + +#define api_lock_wait_unlock_and_free(_lock) \ + api_lock_wait_unlock(_lock); \ + api_lock_free(_lock); diff --git a/lib/toolbox/path.c b/lib/toolbox/path.c index 53e9fc092..3d161a196 100644 --- a/lib/toolbox/path.c +++ b/lib/toolbox/path.c @@ -38,7 +38,7 @@ void path_extract_extension(FuriString* path, char* ext, size_t ext_len_max) { size_t dot = furi_string_search_rchar(path, '.'); size_t filename_start = furi_string_search_rchar(path, '/'); - if((dot > 0) && (filename_start < dot)) { + if((dot != FURI_STRING_FAILURE) && (filename_start < dot)) { strlcpy(ext, &(furi_string_get_cstr(path))[dot], ext_len_max); } } @@ -95,22 +95,17 @@ bool path_contains_only_ascii(const char* path) { name_pos++; } - while(*name_pos != '\0') { - if((*name_pos >= '0') && (*name_pos <= '9')) { - name_pos++; - continue; - } else if((*name_pos >= 'A') && (*name_pos <= 'Z')) { - name_pos++; - continue; - } else if((*name_pos >= 'a') && (*name_pos <= 'z')) { - name_pos++; - continue; - } else if(strchr(" .!#\\$%&'()-@^_`{}~", *name_pos) != NULL) { - name_pos++; - continue; - } + for(; *name_pos; ++name_pos) { + const char c = *name_pos; - return false; + // Regular ASCII characters from 0x20 to 0x7e + const bool is_out_of_range = (c < ' ') || (c > '~'); + // Cross-platform forbidden character set + const bool is_forbidden = strchr("\\<>*|\":?", c); + + if(is_out_of_range || is_forbidden) { + return false; + } } return true; diff --git a/lib/toolbox/property.c b/lib/toolbox/property.c new file mode 100644 index 000000000..c258cdd6a --- /dev/null +++ b/lib/toolbox/property.c @@ -0,0 +1,33 @@ +#include "property.h" + +#include + +void property_value_out(PropertyValueContext* ctx, const char* fmt, unsigned int nparts, ...) { + furi_assert(ctx); + furi_string_reset(ctx->key); + + va_list args; + va_start(args, nparts); + + for(size_t i = 0; i < nparts; ++i) { + const char* keypart = va_arg(args, const char*); + furi_string_cat(ctx->key, keypart); + if(i < nparts - 1) { + furi_string_push_back(ctx->key, ctx->sep); + } + } + + const char* value_str; + + if(fmt) { + furi_string_vprintf(ctx->value, fmt, args); + value_str = furi_string_get_cstr(ctx->value); + } else { + // C string passthrough (no formatting) + value_str = va_arg(args, const char*); + } + + va_end(args); + + ctx->out(furi_string_get_cstr(ctx->key), value_str, ctx->last, ctx->context); +} diff --git a/lib/toolbox/property.h b/lib/toolbox/property.h new file mode 100644 index 000000000..524d8ff16 --- /dev/null +++ b/lib/toolbox/property.h @@ -0,0 +1,39 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** Callback type called every time another key-value pair of device information is ready + * + * @param key[in] device information type identifier + * @param value[in] device information value + * @param last[in] whether the passed key-value pair is the last one + * @param context[in] to pass to callback + */ +typedef void (*PropertyValueCallback)(const char* key, const char* value, bool last, void* context); + +typedef struct { + FuriString* key; /**< key string buffer, must be initialised before use */ + FuriString* value; /**< value string buffer, must be initialised before use */ + PropertyValueCallback out; /**< output callback function */ + char sep; /**< separator character between key parts */ + bool last; /**< flag to indicate last element */ + void* context; /**< user-defined context, passed through to out callback */ +} PropertyValueContext; + +/** Builds key and value strings and outputs them via a callback function + * + * @param ctx[in] local property context + * @param fmt[in] value format, set to NULL to bypass formatting + * @param nparts[in] number of key parts (separated by character) + * @param ...[in] list of key parts followed by value + */ +void property_value_out(PropertyValueContext* ctx, const char* fmt, unsigned int nparts, ...); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/random_name.c b/lib/toolbox/random_name.c index 5a5374398..64e712c7c 100644 --- a/lib/toolbox/random_name.c +++ b/lib/toolbox/random_name.c @@ -5,12 +5,6 @@ #include void set_random_name(char* name, uint8_t max_name_size) { - static bool rand_generator_inited = false; - - if(!rand_generator_inited) { - srand(DWT->CYCCNT); - rand_generator_inited = true; - } const char* prefix[] = { "ancient", "hollow", "strange", "disappeared", "unknown", "unthinkable", "unnamable", "nameless", "my", "concealed", diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index de7c6b682..a7460d889 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -52,6 +52,8 @@ class FlipperApplication: icon: Optional[str] = None order: int = 0 sdk_headers: List[str] = field(default_factory=list) + targets: List[str] = field(default_factory=lambda: ["all"]) + # .fap-specific sources: List[str] = field(default_factory=lambda: ["*.c*"]) fap_version: Tuple[int] = field(default_factory=lambda: (0, 1)) @@ -62,6 +64,7 @@ class FlipperApplication: fap_author: str = "" fap_weburl: str = "" fap_icon_assets: Optional[str] = None + fap_icon_assets_symbol: Optional[str] = None fap_extbuild: List[ExternallyBuiltFile] = field(default_factory=list) fap_private_libs: List[Library] = field(default_factory=list) # Internally used by fbt @@ -135,8 +138,8 @@ class AppManager: raise FlipperManifestException(f"Duplicate app declaration: {app.appid}") self.known_apps[app.appid] = app - def filter_apps(self, applist: List[str]): - return AppBuildset(self, applist) + def filter_apps(self, applist: List[str], hw_target: str): + return AppBuildset(self, applist, hw_target) class AppBuilderException(Exception): @@ -155,11 +158,13 @@ class AppBuildset: FlipperAppType.STARTUP, ) - def __init__(self, appmgr: AppManager, appnames: List[str]): + def __init__(self, appmgr: AppManager, appnames: List[str], hw_target: str): self.appmgr = appmgr self.appnames = set(appnames) + self.hw_target = hw_target self._orig_appnames = appnames self._process_deps() + self._filter_by_target() self._check_conflicts() self._check_unsatisfied() # unneeded? self.apps = sorted( @@ -170,6 +175,16 @@ class AppBuildset: def _is_missing_dep(self, dep_name: str): return dep_name not in self.appnames + def _filter_by_target(self): + for appname in self.appnames.copy(): + app = self.appmgr.get(appname) + # if app.apptype not in self.BUILTIN_APP_TYPES: + if not any(map(lambda t: t in app.targets, ["all", self.hw_target])): + print( + f"Removing {appname} due to target mismatch (building for {self.hw_target}, app supports {app.targets}" + ) + self.appnames.remove(appname) + def _process_deps(self): while True: provided = [] diff --git a/scripts/fbt/elfmanifest.py b/scripts/fbt/elfmanifest.py index 313a64c09..17bceddf4 100644 --- a/scripts/fbt/elfmanifest.py +++ b/scripts/fbt/elfmanifest.py @@ -5,6 +5,7 @@ import struct from dataclasses import dataclass, field from .appmanifest import FlipperApplication +from flipper.assets.icon import file2image _MANIFEST_MAGIC = 0x52474448 @@ -53,8 +54,6 @@ def assemble_manifest_data( ): image_data = b"" if app_manifest.fap_icon: - from flipper.assets.icon import file2image - image = file2image(os.path.join(app_manifest._apppath, app_manifest.fap_icon)) if (image.width, image.height) != (10, 10): raise ValueError( diff --git a/scripts/fbt/sdk/cache.py b/scripts/fbt/sdk/cache.py index 62d42798c..756c37827 100644 --- a/scripts/fbt/sdk/cache.py +++ b/scripts/fbt/sdk/cache.py @@ -89,6 +89,9 @@ class SdkCache: syms.update(map(lambda e: e.name, self.get_variables())) return syms + def get_disabled_names(self): + return set(map(lambda e: e.name, self.disabled_entries)) + def get_functions(self): return self._filter_enabled(self.sdk.functions) diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py index f5404458e..ee7562058 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -1,7 +1,6 @@ import SCons from SCons.Subst import quote_spaces from SCons.Errors import StopError -from SCons.Node.FS import _my_normcase import re import os @@ -43,12 +42,24 @@ def single_quote(arg_list): return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list) -def extract_abs_dir_path(node): +def extract_abs_dir(node): if isinstance(node, SCons.Node.FS.EntryProxy): node = node.get() for repo_dir in node.get_all_rdirs(): if os.path.exists(repo_dir.abspath): - return repo_dir.abspath + return repo_dir - raise StopError(f"Can't find absolute path for {node.name}") + +def extract_abs_dir_path(node): + abs_dir_node = extract_abs_dir(node) + if abs_dir_node is None: + raise StopError(f"Can't find absolute path for {node.name}") + + return abs_dir_node.abspath + + +def path_as_posix(path): + if SCons.Platform.platform_default() == "win32": + return path.replace(os.path.sep, os.path.altsep) + return path diff --git a/scripts/fbt_tools/compilation_db.py b/scripts/fbt_tools/compilation_db.py new file mode 100644 index 000000000..17ff6aaa3 --- /dev/null +++ b/scripts/fbt_tools/compilation_db.py @@ -0,0 +1,278 @@ +""" +Implements the ability for SCons to emit a compilation database for the MongoDB project. See +http://clang.llvm.org/docs/JSONCompilationDatabase.html for details on what a compilation +database is, and why you might want one. The only user visible entry point here is +'env.CompilationDatabase'. This method takes an optional 'target' to name the file that +should hold the compilation database, otherwise, the file defaults to compile_commands.json, +which is the name that most clang tools search for by default. +""" + +# Copyright 2020 MongoDB Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +import json +import itertools +import fnmatch +import SCons + +from SCons.Tool.cxx import CXXSuffixes +from SCons.Tool.cc import CSuffixes +from SCons.Tool.asm import ASSuffixes, ASPPSuffixes + +# TODO: Is there a better way to do this than this global? Right now this exists so that the +# emitter we add can record all of the things it emits, so that the scanner for the top level +# compilation database can access the complete list, and also so that the writer has easy +# access to write all of the files. But it seems clunky. How can the emitter and the scanner +# communicate more gracefully? +__COMPILATION_DB_ENTRIES = [] + + +# We make no effort to avoid rebuilding the entries. Someday, perhaps we could and even +# integrate with the cache, but there doesn't seem to be much call for it. +class __CompilationDbNode(SCons.Node.Python.Value): + def __init__(self, value): + SCons.Node.Python.Value.__init__(self, value) + self.Decider(changed_since_last_build_node) + + +def changed_since_last_build_node(child, target, prev_ni, node): + """Dummy decider to force always building""" + return True + + +def make_emit_compilation_DB_entry(comstr): + """ + Effectively this creates a lambda function to capture: + * command line + * source + * target + :param comstr: unevaluated command line + :return: an emitter which has captured the above + """ + user_action = SCons.Action.Action(comstr) + + def emit_compilation_db_entry(target, source, env): + """ + This emitter will be added to each c/c++ object build to capture the info needed + for clang tools + :param target: target node(s) + :param source: source node(s) + :param env: Environment for use building this node + :return: target(s), source(s) + """ + + dbtarget = __CompilationDbNode(source) + + entry = env.__COMPILATIONDB_Entry( + target=dbtarget, + source=[], + __COMPILATIONDB_UOUTPUT=target, + __COMPILATIONDB_USOURCE=source, + __COMPILATIONDB_UACTION=user_action, + __COMPILATIONDB_ENV=env, + ) + + # TODO: Technically, these next two lines should not be required: it should be fine to + # cache the entries. However, they don't seem to update properly. Since they are quick + # to re-generate disable caching and sidestep this problem. + env.AlwaysBuild(entry) + env.NoCache(entry) + + __COMPILATION_DB_ENTRIES.append(dbtarget) + + return target, source + + return emit_compilation_db_entry + + +def compilation_db_entry_action(target, source, env, **kw): + """ + Create a dictionary with evaluated command line, target, source + and store that info as an attribute on the target + (Which has been stored in __COMPILATION_DB_ENTRIES array + :param target: target node(s) + :param source: source node(s) + :param env: Environment for use building this node + :param kw: + :return: None + """ + + command = env["__COMPILATIONDB_UACTION"].strfunction( + target=env["__COMPILATIONDB_UOUTPUT"], + source=env["__COMPILATIONDB_USOURCE"], + env=env["__COMPILATIONDB_ENV"], + ) + + entry = { + "directory": env.Dir("#").abspath, + "command": command, + "file": env["__COMPILATIONDB_USOURCE"][0], + "output": env["__COMPILATIONDB_UOUTPUT"][0], + } + + target[0].write(entry) + + +def write_compilation_db(target, source, env): + entries = [] + + use_abspath = env["COMPILATIONDB_USE_ABSPATH"] in [True, 1, "True", "true"] + use_path_filter = env.subst("$COMPILATIONDB_PATH_FILTER") + use_srcpath_filter = env.subst("$COMPILATIONDB_SRCPATH_FILTER") + + for s in __COMPILATION_DB_ENTRIES: + entry = s.read() + source_file = entry["file"] + output_file = entry["output"] + + if source_file.rfile().srcnode().exists(): + source_file = source_file.rfile().srcnode() + + if use_abspath: + source_file = source_file.abspath + output_file = output_file.abspath + else: + source_file = source_file.path + output_file = output_file.path + + # print("output_file, path_filter", output_file, use_path_filter) + if use_path_filter and not fnmatch.fnmatch(output_file, use_path_filter): + continue + + if use_srcpath_filter and not fnmatch.fnmatch(source_file, use_srcpath_filter): + continue + + path_entry = { + "directory": entry["directory"], + "command": entry["command"], + "file": source_file, + "output": output_file, + } + + entries.append(path_entry) + + with open(target[0].path, "w") as output_file: + json.dump( + entries, output_file, sort_keys=True, indent=4, separators=(",", ": ") + ) + + +def scan_compilation_db(node, env, path): + return __COMPILATION_DB_ENTRIES + + +def compilation_db_emitter(target, source, env): + """fix up the source/targets""" + + # Someone called env.CompilationDatabase('my_targetname.json') + if not target and len(source) == 1: + target = source + + # Default target name is compilation_db.json + if not target: + target = [ + "compile_commands.json", + ] + + # No source should have been passed. Drop it. + if source: + source = [] + + return target, source + + +def generate(env, **kwargs): + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + env.SetDefault( + COMPILATIONDB_COMSTR=kwargs.get( + "COMPILATIONDB_COMSTR", "Building compilation database $TARGET" + ), + COMPILATIONDB_USE_ABSPATH=False, + COMPILATIONDB_PATH_FILTER="", + COMPILATIONDB_SRCPATH_FILTER="", + ) + + components_by_suffix = itertools.chain( + itertools.product( + CSuffixes, + [ + (static_obj, SCons.Defaults.StaticObjectEmitter, "$CCCOM"), + (shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCCCOM"), + ], + ), + itertools.product( + CXXSuffixes, + [ + (static_obj, SCons.Defaults.StaticObjectEmitter, "$CXXCOM"), + (shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCXXCOM"), + ], + ), + itertools.product( + ASSuffixes, + [(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASCOM")], + [(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASCOM")], + ), + itertools.product( + ASPPSuffixes, + [(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASPPCOM")], + [(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASPPCOM")], + ), + ) + + for entry in components_by_suffix: + suffix = entry[0] + builder, base_emitter, command = entry[1] + + # Assumes a dictionary emitter + emitter = builder.emitter.get(suffix, False) + if emitter: + # We may not have tools installed which initialize all or any of + # cxx, cc, or assembly. If not skip resetting the respective emitter. + builder.emitter[suffix] = SCons.Builder.ListEmitter( + [ + emitter, + make_emit_compilation_DB_entry(command), + ] + ) + + env.Append( + BUILDERS={ + "__COMPILATIONDB_Entry": SCons.Builder.Builder( + action=SCons.Action.Action(compilation_db_entry_action, None), + ), + "CompilationDatabase": SCons.Builder.Builder( + action=SCons.Action.Action( + write_compilation_db, "$COMPILATIONDB_COMSTR" + ), + target_scanner=SCons.Scanner.Scanner( + function=scan_compilation_db, node_class=None + ), + emitter=compilation_db_emitter, + suffix="json", + ), + } + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fbt_apps.py b/scripts/fbt_tools/fbt_apps.py index 55e282017..96528f4e5 100644 --- a/scripts/fbt_tools/fbt_apps.py +++ b/scripts/fbt_tools/fbt_apps.py @@ -1,7 +1,6 @@ from SCons.Builder import Builder from SCons.Action import Action from SCons.Warnings import warn, WarningOnByDefault -import SCons from ansi.color import fg from fbt.appmanifest import ( @@ -33,14 +32,12 @@ def LoadAppManifest(env, entry): def PrepareApplicationsBuild(env): - appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps(env["APPS"]) + appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps( + env["APPS"], env.subst("f${TARGET_HW}") + ) env.Append( SDK_HEADERS=appbuild.get_sdk_headers(), ) - env["APPBUILD_DUMP"] = env.Action( - DumpApplicationConfig, - "\tINFO\t", - ) def DumpApplicationConfig(target, source, env): @@ -68,6 +65,10 @@ def generate(env): env.AddMethod(PrepareApplicationsBuild) env.SetDefault( APPMGR=AppManager(), + APPBUILD_DUMP=env.Action( + DumpApplicationConfig, + "\tINFO\t", + ), ) env.Append( diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index 521c37e90..e17487358 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -139,7 +139,7 @@ def generate(env): BUILDERS={ "IconBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" icons ${ABSPATHGETTERFUNC(SOURCE)} ${TARGET.dir} --filename ${ICON_FILE_NAME}', + '${PYTHON3} "${ASSETS_COMPILER}" icons "${ABSPATHGETTERFUNC(SOURCE)}" "${TARGET.dir}" --filename ${ICON_FILE_NAME}', "${ICONSCOMSTR}", ), emitter=icons_emitter, diff --git a/scripts/fbt_tools/fbt_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py index 0c0ce3d32..9ff05cb73 100644 --- a/scripts/fbt_tools/fbt_debugopts.py +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -1,7 +1,6 @@ from re import search from SCons.Errors import UserError -from fbt_options import OPENOCD_OPTS def _get_device_serials(search_str="STLink"): @@ -20,6 +19,9 @@ def GetDevices(env): def generate(env, **kw): env.AddMethod(GetDevices) + env.SetDefault( + FBT_DEBUG_DIR="${ROOT_DIR}/debug", + ) if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto": env.Append( @@ -36,15 +38,15 @@ def generate(env, **kw): env.SetDefault( OPENOCD_GDB_PIPE=[ - "|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" + "|openocd -c 'gdb_port pipe; log_output ${FBT_DEBUG_DIR}/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" ], GDBOPTS_BASE=[ + "-ex", + "set pagination off", "-ex", "target extended-remote ${GDBREMOTE}", "-ex", "set confirm off", - "-ex", - "set pagination off", ], GDBOPTS_BLACKMAGIC=[ "-ex", @@ -58,17 +60,19 @@ def generate(env, **kw): ], GDBPYOPTS=[ "-ex", - "source debug/FreeRTOS/FreeRTOS.py", + "source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py", "-ex", - "source debug/flipperapps.py", + "source ${FBT_DEBUG_DIR}/flipperapps.py", "-ex", - "source debug/PyCortexMDebug/PyCortexMDebug.py", + "fap-set-debug-elf-root ${FBT_FAP_DEBUG_ELF_ROOT}", + "-ex", + "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py", "-ex", "svd_load ${SVD_FILE}", "-ex", "compare-sections", ], - JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash", + JFLASHPROJECT="${FBT_DEBUG_DIR}/fw.jflash", ) diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index fb59e5b95..f0b443486 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -22,7 +22,7 @@ def GetProjetDirName(env, project=None): def create_fw_build_targets(env, configuration_name): flavor = GetProjetDirName(env, configuration_name) - build_dir = env.Dir("build").Dir(flavor).abspath + build_dir = env.Dir("build").Dir(flavor) return env.SConscript( "firmware.scons", variant_dir=build_dir, @@ -131,7 +131,7 @@ def generate(env): "UsbInstall": Builder( action=[ Action( - '${PYTHON3} "${SELFUPDATE_SCRIPT}" dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}/update.fuf' + '${PYTHON3} "${SELFUPDATE_SCRIPT}" ${UPDATE_BUNDLE_DIR}/update.fuf' ), Touch("${TARGET}"), ] diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index fb4dc2f16..f0015cf25 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -1,6 +1,9 @@ +from dataclasses import dataclass, field +from typing import Optional from SCons.Builder import Builder from SCons.Action import Action from SCons.Errors import UserError +from SCons.Node import NodeList import SCons.Warnings from fbt.elfmanifest import assemble_manifest_data @@ -16,6 +19,15 @@ import shutil from ansi.color import fg +@dataclass +class FlipperExternalAppInfo: + app: FlipperApplication + compact: NodeList = field(default_factory=NodeList) + debug: NodeList = field(default_factory=NodeList) + validator: NodeList = field(default_factory=NodeList) + installer: NodeList = field(default_factory=NodeList) + + def BuildAppElf(env, app): ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR") app_work_dir = os.path.join(ext_apps_work_dir, app.appid) @@ -26,15 +38,7 @@ def BuildAppElf(env, app): app_alias = f"fap_{app.appid}" - # Deprecation stub - legacy_app_taget_name = f"{app_env['FIRMWARE_BUILD_CFG']}_{app.appid}" - - def legacy_app_build_stub(**kw): - raise UserError( - f"Target name '{legacy_app_taget_name}' is deprecated, use '{app_alias}' instead" - ) - - app_env.PhonyTarget(legacy_app_taget_name, Action(legacy_app_build_stub, None)) + app_artifacts = FlipperExternalAppInfo(app) externally_built_files = [] if app.fap_extbuild: @@ -53,11 +57,12 @@ def BuildAppElf(env, app): ) if app.fap_icon_assets: - app_env.CompileIcons( + fap_icons = app_env.CompileIcons( app_env.Dir(app_work_dir), app._appdir.Dir(app.fap_icon_assets), - icon_bundle_name=f"{app.appid}_icons", + icon_bundle_name=f"{app.fap_icon_assets_symbol if app.fap_icon_assets_symbol else app.appid }_icons", ) + app_env.Alias("_fap_icons", fap_icons) private_libs = [] @@ -115,20 +120,22 @@ def BuildAppElf(env, app): CPPPATH=env.Dir(app_work_dir), ) - app_elf_raw = app_env.Program( + app_artifacts.debug = app_env.Program( os.path.join(ext_apps_work_dir, f"{app.appid}_d"), app_sources, APP_ENTRY=app.entry_point, ) - app_env.Clean(app_elf_raw, [*externally_built_files, app_env.Dir(app_work_dir)]) + app_env.Clean( + app_artifacts.debug, [*externally_built_files, app_env.Dir(app_work_dir)] + ) - app_elf_dump = app_env.ObjDump(app_elf_raw) + app_elf_dump = app_env.ObjDump(app_artifacts.debug) app_env.Alias(f"{app_alias}_list", app_elf_dump) - app_elf_augmented = app_env.EmbedAppMetadata( + app_artifacts.compact = app_env.EmbedAppMetadata( os.path.join(ext_apps_work_dir, app.appid), - app_elf_raw, + app_artifacts.debug, APP=app, ) @@ -139,19 +146,21 @@ def BuildAppElf(env, app): } app_env.Depends( - app_elf_augmented, + app_artifacts.compact, [app_env["SDK_DEFINITION"], app_env.Value(manifest_vals)], ) if app.fap_icon: app_env.Depends( - app_elf_augmented, + app_artifacts.compact, app_env.File(f"{app._apppath}/{app.fap_icon}"), ) - app_elf_import_validator = app_env.ValidateAppImports(app_elf_augmented) - app_env.AlwaysBuild(app_elf_import_validator) - app_env.Alias(app_alias, app_elf_import_validator) - return (app_elf_augmented, app_elf_raw, app_elf_import_validator) + app_artifacts.validator = app_env.ValidateAppImports(app_artifacts.compact) + app_env.AlwaysBuild(app_artifacts.validator) + app_env.Alias(app_alias, app_artifacts.validator) + + env["EXT_APPS"][app.appid] = app_artifacts + return app_artifacts def prepare_app_metadata(target, source, env): @@ -182,11 +191,17 @@ def validate_app_imports(target, source, env): app_syms.add(line.split()[0]) unresolved_syms = app_syms - sdk_cache.get_valid_names() if unresolved_syms: - SCons.Warnings.warn( - SCons.Warnings.LinkWarning, - fg.brightyellow(f"{source[0].path}: app won't run. Unresolved symbols: ") - + fg.brightmagenta(f"{unresolved_syms}"), - ) + warning_msg = fg.brightyellow( + f"{source[0].path}: app won't run. Unresolved symbols: " + ) + fg.brightmagenta(f"{unresolved_syms}") + disabled_api_syms = unresolved_syms.intersection(sdk_cache.get_disabled_names()) + if disabled_api_syms: + warning_msg += ( + fg.brightyellow(" (in API, but disabled: ") + + fg.brightmagenta(f"{disabled_api_syms}") + + fg.brightyellow(")") + ) + SCons.Warnings.warn(SCons.Warnings.LinkWarning, warning_msg), def GetExtAppFromPath(env, app_dir): @@ -208,26 +223,26 @@ def GetExtAppFromPath(env, app_dir): if not app: raise UserError(f"Failed to resolve application for given APPSRC={app_dir}") - app_elf = env["_extapps"]["compact"].get(app.appid, None) - if not app_elf: + app_artifacts = env["EXT_APPS"].get(app.appid, None) + if not app_artifacts: raise UserError( f"Application {app.appid} is not configured for building as external" ) - app_validator = env["_extapps"]["validators"].get(app.appid, None) - - return (app, app_elf[0], app_validator[0]) + return app_artifacts def fap_dist_emitter(target, source, env): target_dir = target[0] target = [] - for dist_entry in env["_extapps"]["dist"].values(): - target.append(target_dir.Dir(dist_entry[0]).File(dist_entry[1][0].name)) - - for compact_entry in env["_extapps"]["compact"].values(): - source.extend(compact_entry) + for _, app_artifacts in env["EXT_APPS"].items(): + source.extend(app_artifacts.compact) + target.append( + target_dir.Dir(app_artifacts.app.fap_category).File( + app_artifacts.compact[0].name + ) + ) return (target, source) @@ -244,10 +259,9 @@ def fap_dist_action(target, source, env): def generate(env, **kw): env.SetDefault( - EXT_APPS_WORK_DIR=kw.get("EXT_APPS_WORK_DIR"), + EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}", APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py", ) - if not env["VERBOSE"]: env.SetDefault( FAPDISTCOMSTR="\tFAPDIST\t${TARGET}", @@ -256,6 +270,10 @@ def generate(env, **kw): APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}", ) + env.SetDefault( + EXT_APPS={}, # appid -> FlipperExternalAppInfo + ) + env.AddMethod(BuildAppElf) env.AddMethod(GetExtAppFromPath) env.Append( diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py index c46346b65..3a37eacc9 100644 --- a/scripts/fbt_tools/fbt_sdk.py +++ b/scripts/fbt_tools/fbt_sdk.py @@ -14,6 +14,7 @@ import json from fbt.sdk.collector import SdkCollector from fbt.sdk.cache import SdkCache +from fbt.util import path_as_posix def ProcessSdkDepends(env, filename): @@ -52,6 +53,8 @@ def prebuild_sdk_create_origin_file(target, source, env): class SdkMeta: + MAP_FILE_SUBST = "SDK_MAP_FILE_SUBST" + def __init__(self, env, tree_builder: "SdkTreeBuilder"): self.env = env self.treebuilder = tree_builder @@ -67,6 +70,7 @@ class SdkMeta: "linker_libs": self.env.subst("${LIBS}"), "app_ep_subst": self.env.subst("${APP_ENTRY}"), "sdk_path_subst": self.env.subst("${SDK_DIR_SUBST}"), + "map_file_subst": self.MAP_FILE_SUBST, "hardware": self.env.subst("${TARGET_HW}"), } with open(json_manifest_path, "wt") as f: @@ -75,9 +79,9 @@ class SdkMeta: def _wrap_scons_vars(self, vars: str): expanded_vars = self.env.subst( vars, - target=Entry("dummy"), + target=Entry(self.MAP_FILE_SUBST), ) - return expanded_vars.replace("\\", "/") + return path_as_posix(expanded_vars) class SdkTreeBuilder: @@ -142,13 +146,15 @@ class SdkTreeBuilder: meta.save_to(self.target[0].path) def build_sdk_file_path(self, orig_path: str) -> str: - return posixpath.normpath( - posixpath.join( - self.SDK_DIR_SUBST, - self.target_sdk_dir_name, - orig_path, + return path_as_posix( + posixpath.normpath( + posixpath.join( + self.SDK_DIR_SUBST, + self.target_sdk_dir_name, + orig_path, + ) ) - ).replace("\\", "/") + ) def emitter(self, target, source, env): target_folder = target[0] diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 6b53f7477..9c9f52958 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -238,9 +238,9 @@ class FlipperStorage: while read_size < size: self.read.until("Ready?" + self.CLI_EOL) self.send("y") - read_size = min(size - read_size, buffer_size) - filedata.extend(self.port.read(read_size)) - read_size = read_size + read_size + chunk_size = min(size - read_size, buffer_size) + filedata.extend(self.port.read(chunk_size)) + read_size = read_size + chunk_size percent = str(math.ceil(read_size / size * 100)) total_chunks = str(math.ceil(size / buffer_size)) diff --git a/scripts/lint.py b/scripts/lint.py index c178c8763..58f2d69f5 100755 --- a/scripts/lint.py +++ b/scripts/lint.py @@ -35,11 +35,23 @@ class Main(App): ) self.parser_format.set_defaults(func=self.format) + @staticmethod + def _filter_lint_directories(dirnames: list[str]): + # Skipping 3rd-party code - usually resides in subfolder "lib" + if "lib" in dirnames: + dirnames.remove("lib") + # Skipping hidden folders + for dirname in dirnames.copy(): + if dirname.startswith("."): + dirnames.remove(dirname) + def _check_folders(self, folders: list): show_message = False pattern = re.compile(SOURCE_CODE_DIR_PATTERN) for folder in folders: for dirpath, dirnames, filenames in os.walk(folder): + self._filter_lint_directories(dirnames) + for dirname in dirnames: if not pattern.match(dirname): to_fix = os.path.join(dirpath, dirname) @@ -54,9 +66,7 @@ class Main(App): output = [] for folder in folders: for dirpath, dirnames, filenames in os.walk(folder): - # Skipping 3rd-party code - usually resides in subfolder "lib" - if "lib" in dirnames: - dirnames.remove("lib") + self._filter_lint_directories(dirnames) for filename in filenames: ext = os.path.splitext(filename.lower())[1] diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 35cac7742..5b6fac5f7 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,7 +1,9 @@ -pyserial==3.5 +ansi==0.3.6 +black==22.6.0 +colorlog==6.7.0 heatshrink2==0.11.0 Pillow==9.1.1 -grpcio==1.47.0 -grpcio-tools==1.47.0 -protobuf==3.20.2 +protobuf==3.20.1 +pyserial==3.5 python3-protobuf==2.5.0 +SCons==4.4.0 diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 6e87bf95a..9fbd8fd9b 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -1,37 +1,39 @@ @echo off -if not [%FBT_ROOT%] == [] ( +if not ["%FBT_ROOT%"] == [""] ( goto already_set ) set "FBT_ROOT=%~dp0\..\..\" -pushd %FBT_ROOT% +pushd "%FBT_ROOT%" set "FBT_ROOT=%cd%" popd -if not [%FBT_NOENV%] == [] ( +if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=17" +set "FLIPPER_TOOLCHAIN_VERSION=19" -if [%FBT_TOOLCHAIN_ROOT%] == [] ( +if ["%FBT_TOOLCHAIN_ROOT%"] == [""] ( set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows" ) +set "FBT_TOOLCHAIN_VERSION_FILE=%FBT_TOOLCHAIN_ROOT%\VERSION" + if not exist "%FBT_TOOLCHAIN_ROOT%" ( - powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" "%FBT_TOOLCHAIN_ROOT%" -) -if not exist "%FBT_TOOLCHAIN_ROOT%\VERSION" ( - powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" "%FBT_TOOLCHAIN_ROOT%" + powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" %flipper_toolchain_version% "%FBT_TOOLCHAIN_ROOT%" ) -set /p REAL_TOOLCHAIN_VERSION=<"%FBT_TOOLCHAIN_ROOT%\VERSION" +if not exist "%FBT_TOOLCHAIN_VERSION_FILE%" ( + powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" %flipper_toolchain_version% "%FBT_TOOLCHAIN_ROOT%" +) + +set /p REAL_TOOLCHAIN_VERSION=<"%FBT_TOOLCHAIN_VERSION_FILE%" if not "%REAL_TOOLCHAIN_VERSION%" == "%FLIPPER_TOOLCHAIN_VERSION%" ( - powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" "%FBT_TOOLCHAIN_ROOT%" + powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" %flipper_toolchain_version% "%FBT_TOOLCHAIN_ROOT%" ) - set "HOME=%USERPROFILE%" set "PYTHONHOME=%FBT_TOOLCHAIN_ROOT%\python" set "PYTHONPATH=" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index d3fdb8cea..f3e4cb1fa 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:-"17"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"19"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; fbtenv_show_usage() @@ -13,7 +13,7 @@ fbtenv_show_usage() echo "Running this script manually is wrong, please source it"; echo "Example:"; printf "\tsource scripts/toolchain/fbtenv.sh\n"; - echo "To restore your enviroment source fbtenv.sh with '--restore'." + echo "To restore your environment source fbtenv.sh with '--restore'." echo "Example:"; printf "\tsource scripts/toolchain/fbtenv.sh --restore\n"; } @@ -227,7 +227,7 @@ fbtenv_curl_wget_check() echo; echo "$TOOLCHAIN_URL"; echo; - echo "And place in $FBT_TOOLCHAIN_PATH/toolchain/ dir mannualy"; + echo "And place in $FBT_TOOLCHAIN_PATH/toolchain/ dir manually"; return 1; fi echo "yes" diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 index c96bb119c..05ea4be2c 100644 --- a/scripts/toolchain/windows-toolchain-download.ps1 +++ b/scripts/toolchain/windows-toolchain-download.ps1 @@ -36,11 +36,11 @@ Add-Type -Assembly "System.IO.Compression.Filesystem" # Expand-Archive -LiteralPath "$toolchain_zip_temp_path" -DestinationPath "$download_dir" Write-Host -NoNewline "moving.." -Move-Item -LiteralPath "$toolchain_dist_temp_path" -Destination "$toolchain_target_path" +Move-Item -LiteralPath "$toolchain_dist_temp_path" -Destination "$toolchain_target_path" -Force Write-Host "done!" Write-Host -NoNewline "Cleaning up temporary files.." Remove-Item -LiteralPath "$toolchain_zip_temp_path" -Force Write-Host "done!" -# dasdasd \ No newline at end of file +# dasdasd diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index cadb417f8..fc2534ed5 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -15,7 +15,7 @@ AddOption( nargs=1, action="store", default="fbt_options.py", - help="Enviroment option file", + help="Environment option file", ) AddOption( @@ -23,7 +23,7 @@ AddOption( action="store", dest="extra_int_apps", default="", - help="List of applications to add to firmare's built-ins. Also see FIRMWARE_APP_SET and FIRMWARE_APPS", + help="List of applications to add to firmware's built-ins. Also see FIRMWARE_APP_SET and FIRMWARE_APPS", ) AddOption( @@ -63,6 +63,11 @@ vars.AddVariables( help="Enable debug build", default=True, ), + BoolVariable( + "LIB_DEBUG", + help="Enable debug build for libraries", + default=False, + ), BoolVariable( "COMPACT", help="Optimize for size", @@ -147,7 +152,7 @@ vars.AddVariables( PathVariable( "SVD_FILE", help="Path to SVD file", - validator=PathVariable.PathIsFile, + validator=PathVariable.PathAccept, default="", ), PathVariable( diff --git a/site_scons/environ.scons b/site_scons/environ.scons index 96424caad..acdc83e2a 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -61,8 +61,8 @@ coreenv = VAR_ENV.Clone( ABSPATHGETTERFUNC=extract_abs_dir_path, # Setting up temp file parameters - to overcome command line length limits TEMPFILEARGESCFUNC=tempfile_arg_esc_func, - FBT_SCRIPT_DIR=Dir("#/scripts"), ROOT_DIR=Dir("#"), + FBT_SCRIPT_DIR="${ROOT_DIR}/scripts", ) # If DIST_SUFFIX is set in environment, is has precedence (set by CI) diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index bb1d65ffc..b8f210563 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -1,4 +1,6 @@ -from SCons.Errors import UserError +from dataclasses import dataclass, field +from SCons.Node import NodeList +from SCons.Warnings import warn, WarningOnByDefault Import("ENV") @@ -7,14 +9,7 @@ from fbt.appmanifest import FlipperAppType appenv = ENV["APPENV"] = ENV.Clone( tools=[ - ( - "fbt_extapps", - { - "EXT_APPS_WORK_DIR": ENV.subst( - "${BUILD_DIR}/.extapps", - ) - }, - ), + "fbt_extapps", "fbt_assets", "fbt_sdk", ] @@ -60,22 +55,11 @@ appenv.AppendUnique( ) -extapps = appenv["_extapps"] = { - "compact": {}, - "debug": {}, - "validators": {}, - "dist": {}, - "resources_dist": None, - "sdk_tree": None, -} - - -def build_app_as_external(env, appdef): - compact_elf, debug_elf, validator = env.BuildAppElf(appdef) - extapps["compact"][appdef.appid] = compact_elf - extapps["debug"][appdef.appid] = debug_elf - extapps["validators"][appdef.appid] = validator - extapps["dist"][appdef.appid] = (appdef.fap_category, compact_elf) +@dataclass +class FlipperExtAppBuildArtifacts: + applications: dict = field(default_factory=dict) + resources_dist: NodeList = field(default_factory=NodeList) + sdk_tree: NodeList = field(default_factory=NodeList) apps_to_build_as_faps = [ @@ -85,42 +69,51 @@ apps_to_build_as_faps = [ if appenv["DEBUG_TOOLS"]: apps_to_build_as_faps.append(FlipperAppType.DEBUG) -for apptype in apps_to_build_as_faps: - for app in appenv["APPBUILD"].get_apps_of_type(apptype, True): - build_app_as_external(appenv, app) +known_extapps = [ + app + for apptype in apps_to_build_as_faps + for app in appenv["APPBUILD"].get_apps_of_type(apptype, True) +] # Ugly access to global option if extra_app_list := GetOption("extra_ext_apps"): - for extra_app in extra_app_list.split(","): - build_app_as_external(appenv, appenv["APPMGR"].get(extra_app)) + known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(","))) + +for app in known_extapps: + if not any(map(lambda t: t in app.targets, ["all", appenv.subst("f${TARGET_HW}")])): + warn( + WarningOnByDefault, + f"Can't build '{app.name}' (id '{app.appid}'): target mismatch" + f" (building for {appenv.subst('f${TARGET_HW}')}, app supports {app.targets}", + ) + continue + + appenv.BuildAppElf(app) if appenv["FORCE"]: - appenv.AlwaysBuild(extapps["compact"].values()) + appenv.AlwaysBuild( + list(app_artifact.compact for app_artifact in appenv["EXT_APPS"].values()) + ) -# Deprecation stub -def legacy_app_build_stub(**kw): - raise UserError(f"Target name 'firmware_extapps' is deprecated, use 'faps' instead") +Alias( + "faps", list(app_artifact.validator for app_artifact in appenv["EXT_APPS"].values()) +) - -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"), []) +extapps = FlipperExtAppBuildArtifacts() +extapps.applications = appenv["EXT_APPS"] +extapps.resources_dist = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) if appsrc := appenv.subst("$APPSRC"): - app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc) + app_artifacts = appenv.GetExtAppFromPath(appsrc) appenv.PhonyTarget( "launch_app", - '${PYTHON3} "${APP_RUN_SCRIPT}" ${SOURCE} --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', - source=fap_file, - FAP_CATEGORY=app_manifest.fap_category, + '${PYTHON3} "${APP_RUN_SCRIPT}" "${SOURCE}" --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', + source=app_artifacts.compact, + FAP_CATEGORY=app_artifacts.app.fap_category, ) - appenv.Alias("launch_app", app_validator) + appenv.Alias("launch_app", app_artifacts.validator) # SDK management @@ -131,12 +124,14 @@ sdk_source = appenv.SDKPrebuilder( (appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]), ) # Extra deps on headers included in deeper levels +# Available on second and subsequent builds Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d")) appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk") -sdk_tree = extapps["sdk_tree"] = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path) +sdk_tree = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path) # AlwaysBuild(sdk_tree) Alias("sdk_tree", sdk_tree) +extapps.sdk_tree = sdk_tree sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path) Precious(sdk_apicheck)