diff --git a/applications/about/about.c b/applications/about/about.c new file mode 100644 index 00000000..08734592 --- /dev/null +++ b/applications/about/about.c @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message); + +static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + const char* screen_header = "Product: Flipper Zero\n" + "Model: FZ.1\n"; + const char* screen_text = "FCC ID: 2A2V6-FZ\n" + "IC ID: 27624-FZ"; + + dialog_message_set_header(message, screen_header, 0, 0, AlignLeft, AlignTop); + dialog_message_set_text(message, screen_text, 0, 26, AlignLeft, AlignTop); + result = dialog_message_show(dialogs, message); + dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); + dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); + + return result; +} + +static DialogMessageButton address_screen(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + const char* screen_text = "Flipper Devices Inc\n" + "Suite B #551, 2803\n" + "Philadelphia Pike, Claymont\n" + "DE, USA 19703\n"; + + dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop); + result = dialog_message_show(dialogs, message); + dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); + + return result; +} + +static DialogMessageButton compliance_screen(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + const char* screen_text = "For all compliance\n" + "certificates please visit\n" + "www.flipp.dev/compliance"; + + dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop); + result = dialog_message_show(dialogs, message); + dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); + + return result; +} + +static DialogMessageButton icon1_screen(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + dialog_message_set_icon(message, &I_Certification1_103x23, 12, 12); + result = dialog_message_show(dialogs, message); + dialog_message_set_icon(message, NULL, 0, 0); + + return result; +} + +static DialogMessageButton icon2_screen(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + + dialog_message_set_icon(message, &I_Certification2_119x30, 4, 9); + result = dialog_message_show(dialogs, message); + dialog_message_set_icon(message, NULL, 0, 0); + + return result; +} + +static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + string_t buffer; + string_init(buffer); + const char* my_name = furi_hal_version_get_name_ptr(); + + string_cat_printf( + buffer, + "%d.F%dB%dC%d %s\n", + furi_hal_version_get_hw_version(), + furi_hal_version_get_hw_target(), + furi_hal_version_get_hw_body(), + furi_hal_version_get_hw_connect(), + my_name ? my_name : "Unknown"); + + dialog_message_set_header(message, "HW Version info:", 0, 0, AlignLeft, AlignTop); + dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); + result = dialog_message_show(dialogs, message); + dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); + dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); + string_clear(buffer); + + return result; +} + +static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + string_t buffer; + string_init(buffer); + const Version* ver = furi_hal_version_get_firmware_version(); + + if(!ver) { + string_cat_printf(buffer, "No info\n"); + } else { + string_cat_printf( + buffer, + "%s [%s]\n%s [%s]\n[%s] %s", + version_get_version(ver), + version_get_builddate(ver), + version_get_githash(ver), + version_get_gitbranchnum(ver), + version_get_target(ver), + version_get_gitbranch(ver)); + } + + dialog_message_set_header(message, "FW Version info:", 0, 0, AlignLeft, AlignTop); + dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); + result = dialog_message_show(dialogs, message); + dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); + dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); + string_clear(buffer); + + return result; +} + +static DialogMessageButton boot_version_screen(DialogsApp* dialogs, DialogMessage* message) { + DialogMessageButton result; + string_t buffer; + string_init(buffer); + const Version* ver = furi_hal_version_get_boot_version(); + + if(!ver) { + string_cat_printf(buffer, "No info\n"); + } else { + string_cat_printf( + buffer, + "%s [%s]\n%s [%s]\n[%s] %s", + version_get_version(ver), + version_get_builddate(ver), + version_get_githash(ver), + version_get_gitbranchnum(ver), + version_get_target(ver), + version_get_gitbranch(ver)); + } + + dialog_message_set_header(message, "Boot Version info:", 0, 0, AlignLeft, AlignTop); + dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); + result = dialog_message_show(dialogs, message); + dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); + dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); + string_clear(buffer); + + return result; +} + +const AboutDialogScreen about_screens[] = { + product_screen, + compliance_screen, + address_screen, + icon1_screen, + icon2_screen, + hw_version_screen, + fw_version_screen, + boot_version_screen}; + +const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen); + +int32_t about_settings_app(void* p) { + DialogsApp* dialogs = furi_record_open("dialogs"); + DialogMessage* message = dialog_message_alloc(); + + Gui* gui = furi_record_open("gui"); + ViewDispatcher* view_dispatcher = view_dispatcher_alloc(); + EmptyScreen* empty_screen = empty_screen_alloc(); + const uint32_t empty_screen_index = 0; + + size_t screen_index = 0; + DialogMessageButton screen_result; + + // draw empty screen to prevent menu flickering + view_dispatcher_add_view( + view_dispatcher, empty_screen_index, empty_screen_get_view(empty_screen)); + view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); + view_dispatcher_switch_to_view(view_dispatcher, empty_screen_index); + + while(1) { + if(screen_index >= about_screens_count - 1) { + dialog_message_set_buttons(message, "Back", NULL, NULL); + } else { + dialog_message_set_buttons(message, "Back", NULL, "Next"); + } + + screen_result = about_screens[screen_index](dialogs, message); + + if(screen_result == DialogMessageButtonLeft) { + if(screen_index <= 0) { + break; + } else { + screen_index--; + } + } else if(screen_result == DialogMessageButtonRight) { + if(screen_index < about_screens_count) { + screen_index++; + } + } else if(screen_result == DialogMessageButtonBack) { + break; + } + } + + dialog_message_free(message); + furi_record_close("dialogs"); + + view_dispatcher_remove_view(view_dispatcher, empty_screen_index); + view_dispatcher_free(view_dispatcher); + empty_screen_free(empty_screen); + furi_record_close("gui"); + + return 0; +} \ No newline at end of file diff --git a/applications/applications.c b/applications/applications.c old mode 100644 new mode 100755 index 9aa3795d..9c014450 --- a/applications/applications.c +++ b/applications/applications.c @@ -9,11 +9,11 @@ extern int32_t dolphin_srv(void* p); extern int32_t gui_srv(void* p); extern int32_t input_srv(void* p); extern int32_t loader_srv(void* p); -extern int32_t menu_srv(void* p); extern int32_t notification_srv(void* p); extern int32_t power_observer_srv(void* p); extern int32_t power_srv(void* p); extern int32_t storage_srv(void* p); +extern int32_t desktop_srv(void* p); // Apps extern int32_t accessor_app(void* p); @@ -46,11 +46,15 @@ extern void lfrfid_cli_init(); extern void nfc_cli_init(); extern void storage_cli_init(); extern void subghz_cli_init(); +extern void power_cli_init(); // Settings extern int32_t notification_settings_app(void* p); extern int32_t storage_settings_app(void* p); extern int32_t bt_settings_app(void* p); +extern int32_t desktop_settings_app(void* p); +extern int32_t about_settings_app(void* p); +extern int32_t power_settings_app(void* p); const FlipperApplication FLIPPER_SERVICES[] = { /* Services */ @@ -70,6 +74,10 @@ const FlipperApplication FLIPPER_SERVICES[] = { {.app = dolphin_srv, .name = "Dolphin", .stack_size = 1024, .icon = NULL}, #endif +#ifdef SRV_DESKTOP + {.app = desktop_srv, .name = "Desktop", .stack_size = 1024, .icon = NULL}, +#endif + #ifdef SRV_GUI {.app = gui_srv, .name = "Gui", .stack_size = 8192, .icon = NULL}, #endif @@ -78,8 +86,7 @@ const FlipperApplication FLIPPER_SERVICES[] = { {.app = input_srv, .name = "Input", .stack_size = 1024, .icon = NULL}, #endif -#ifdef SRV_MENU - {.app = menu_srv, .name = "Menu", .stack_size = 1024, .icon = NULL}, +#ifdef SRV_LOADER {.app = loader_srv, .name = "Loader", .stack_size = 1024, .icon = NULL}, #endif @@ -98,43 +105,6 @@ const FlipperApplication FLIPPER_SERVICES[] = { #ifdef SRV_STORAGE {.app = storage_srv, .name = "Storage", .stack_size = 4096, .icon = NULL}, #endif - -/* Fake services (autorun) */ -#ifdef SRV_BLINK - {.app = blink_test_app, .name = "Blink", .stack_size = 1024, .icon = &A_Plugins_14}, -#endif - -#ifdef SRV_LF_RFID - {.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_Plugins_14}, -#endif - -#ifdef SRV_IRDA - {.app = irda_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Plugins_14}, -#endif - -#ifdef SRV_MUSIC_PLAYER - {.app = music_player_app, .name = "Music Player", .stack_size = 1024, .icon = &A_Plugins_14}, -#endif - -#ifdef SRV_IBUTTON - {.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_Plugins_14}, -#endif - -#ifdef SRV_GPIO_TEST - {.app = gpio_test_app, .name = "GPIO Test", .stack_size = 1024, .icon = &A_Plugins_14}, -#endif - -#ifdef SRV_KEYPAD_TEST - {.app = keypad_test_app, .name = "Keypad Test", .stack_size = 1024, .icon = &A_Plugins_14}, -#endif - -#ifdef SRV_ACCESSOR - {.app = accessor_app, .name = "Accessor", .stack_size = 4096, .icon = &A_Plugins_14}, -#endif - -#ifdef SRV_STORAGE_TEST - {.app = storage_test_app, .name = "Storage Test", .stack_size = 1024, .icon = &A_Plugins_14}, -#endif }; const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication); @@ -142,18 +112,14 @@ const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperA // Main menu APP const FlipperApplication FLIPPER_APPS[] = { -#ifdef APP_IBUTTON - {.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14}, +#ifdef APP_SUBGHZ + {.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14}, #endif #ifdef APP_NFC {.app = nfc_app, .name = "NFC", .stack_size = 4096, .icon = &A_NFC_14}, #endif -#ifdef APP_SUBGHZ - {.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14}, -#endif - #ifdef APP_LF_RFID {.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_125khz_14}, #endif @@ -162,6 +128,10 @@ const FlipperApplication FLIPPER_APPS[] = { {.app = irda_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Infrared_14}, #endif +#ifdef APP_IBUTTON + {.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14}, +#endif + #ifdef APP_GPIO_TEST {.app = gpio_test_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14}, #endif @@ -175,22 +145,35 @@ const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = { #ifdef SRV_CLI crypto_cli_init, #endif + +#ifdef APP_IRDA irda_cli_init, +#endif + #ifdef APP_NFC nfc_cli_init, #endif + #ifdef APP_SUBGHZ subghz_cli_init, #endif + #ifdef APP_LF_RFID lfrfid_cli_init, #endif + #ifdef APP_IBUTTON ibutton_cli_init, #endif + #ifdef SRV_BT bt_cli_init, #endif + +#ifdef SRV_POWER + power_cli_init, +#endif + #ifdef SRV_STORAGE storage_cli_init, #endif @@ -257,16 +240,31 @@ const FlipperApplication FLIPPER_ARCHIVE = // Settings menu const FlipperApplication FLIPPER_SETTINGS_APPS[] = { +#ifdef SRV_BT + {.app = bt_settings_app, .name = "Bluetooth", .stack_size = 1024, .icon = NULL}, +#endif + #ifdef SRV_NOTIFICATION - {.app = notification_settings_app, .name = "Notification", .stack_size = 1024, .icon = NULL}, + {.app = notification_settings_app, + .name = "LCD and notifications", + .stack_size = 1024, + .icon = NULL}, #endif #ifdef SRV_STORAGE {.app = storage_settings_app, .name = "Storage", .stack_size = 2048, .icon = NULL}, #endif -#ifdef SRV_BT - {.app = bt_settings_app, .name = "Bluetooth", .stack_size = 1024, .icon = NULL}, +#ifdef SRV_POWER + {.app = power_settings_app, .name = "Power", .stack_size = 1024, .icon = NULL}, +#endif + +#ifdef SRV_DESKTOP + {.app = desktop_settings_app, .name = "Desktop", .stack_size = 1024, .icon = NULL}, +#endif + +#ifdef APP_ABOUT + {.app = about_settings_app, .name = "About", .stack_size = 1024, .icon = NULL}, #endif }; diff --git a/applications/applications.mk b/applications/applications.mk index 5e3263a6..7bd15518 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -1,264 +1,249 @@ APP_DIR = $(PROJECT_ROOT)/applications -LIB_DIR = $(PROJECT_ROOT)/lib +LIB_DIR = $(PROJECT_ROOT)/lib CFLAGS += -I$(APP_DIR) -C_SOURCES += $(shell find $(APP_DIR) -name *.c) -CPP_SOURCES += $(shell find $(APP_DIR) -name *.cpp) +C_SOURCES += $(shell find $(APP_DIR) -name *.c) +CPP_SOURCES += $(shell find $(APP_DIR) -name *.cpp) -# Use SRV_* for autostart app -# Use APP_* for add app to build - APP_RELEASE ?= 1 ifeq ($(APP_RELEASE), 1) # Services -SRV_BT = 1 -SRV_CLI = 1 -SRV_DIALOGS = 1 -SRV_DOLPHIN = 1 -SRV_GUI = 1 -SRV_INPUT = 1 -SRV_MENU = 1 +SRV_BT = 1 +SRV_CLI = 1 +SRV_DIALOGS = 1 +SRV_DOLPHIN = 1 +SRV_GUI = 1 +SRV_INPUT = 1 +SRV_LOADER = 1 SRV_NOTIFICATION = 1 -SRV_POWER = 1 +SRV_POWER = 1 SRV_POWER_OBSERVER = 1 -SRV_STORAGE = 1 +SRV_STORAGE = 1 # Apps -APP_ARCHIVE = 1 +SRV_DESKTOP = 1 +APP_ARCHIVE = 1 APP_GPIO_TEST = 1 -APP_IBUTTON = 1 -APP_IRDA = 1 -APP_LF_RFID = 1 -APP_NFC = 1 -APP_SUBGHZ = 1 +APP_IBUTTON = 1 +APP_IRDA = 1 +APP_LF_RFID = 1 +APP_NFC = 1 +APP_SUBGHZ = 1 +APP_ABOUT = 1 # Plugins APP_MUSIC_PLAYER = 1 # Debug APP_ACCESSOR = 1 -APP_BLINK = 1 +APP_BLINK = 1 APP_IRDA_MONITOR = 1 APP_KEYPAD_TEST = 1 -APP_SD_TEST = 1 +APP_SD_TEST = 1 APP_UNIT_TESTS = 0 APP_VIBRO_DEMO = 1 endif -SRV_BT ?= 0 -ifeq ($(SRV_BT), 1) -SRV_CLI = 1 -CFLAGS += -DSRV_BT -endif +# Applications +# that will be shown in menu +# Prefix with APP_* -SRV_DOLPHIN ?= 0 -ifeq ($(SRV_DOLPHIN), 1) -SRV_MENU = 1 -CFLAGS += -DSRV_DOLPHIN -endif - -SRV_POWER ?= 0 -ifeq ($(SRV_POWER), 1) -SRV_GUI = 1 -SRV_CLI = 1 -CFLAGS += -DSRV_POWER -endif - -SRV_POWER_OBSERVER ?= 0 -ifeq ($(SRV_POWER_OBSERVER), 1) -SRV_POWER = 1 -CFLAGS += -DSRV_POWER_OBSERVER -endif - -SRV_MENU ?= 0 -ifeq ($(SRV_MENU), 1) -CFLAGS += -DSRV_MENU -APP_MENU = 1 -endif -APP_MENU ?= 0 -ifeq ($(APP_MENU), 1) -SRV_INPUT = 1 -SRV_GUI = 1 -CFLAGS += -DAPP_MENU -endif APP_IRDA_MONITOR ?= 0 ifeq ($(APP_IRDA_MONITOR), 1) CFLAGS += -DAPP_IRDA_MONITOR +SRV_GUI = 1 endif + APP_UNIT_TESTS ?= 0 ifeq ($(APP_UNIT_TESTS), 1) CFLAGS += -DAPP_UNIT_TESTS endif + APP_ARCHIVE ?= 0 -ifeq ($(APP_NFC), 1) +ifeq ($(APP_ARCHIVE), 1) CFLAGS += -DAPP_ARCHIVE -APP_ARCHIVE = 1 +SRV_GUI = 1 endif -SRV_BLINK ?= 0 -ifeq ($(SRV_BLINK), 1) -CFLAGS += -DSRV_BLINK -APP_BLINK = 1 -endif + APP_BLINK ?= 0 ifeq ($(APP_BLINK), 1) CFLAGS += -DAPP_BLINK -SRV_INPUT = 1 +SRV_GUI = 1 endif -SRV_UART_WRITE ?= 0 -ifeq ($(SRV_UART_WRITE), 1) -CFLAGS += -DSRV_UART_WRITE -APP_UART_WRITE = 1 -endif -APP_UART_WRITE ?= 0 -ifeq ($(APP_UART_WRITE), 1) -CFLAGS += -DAPP_UART_WRITE -endif - -SRV_IPC ?= 0 -ifeq ($(SRV_IPC), 1) -CFLAGS += -DSRV_IPC -APP_IPC = 1 -endif -APP_IPC ?= 0 -ifeq ($(APP_IPC), 1) -CFLAGS += -DAPP_IPC -endif +APP_SUBGHZ ?= 0 ifeq ($(APP_SUBGHZ), 1) CFLAGS += -DAPP_SUBGHZ -SRV_INPUT = 1 -SRV_GUI = 1 -SRV_CLI = 1 +SRV_GUI = 1 +SRV_CLI = 1 endif -SRV_LF_RFID ?= 0 -ifeq ($(SRV_LF_RFID), 1) -CFLAGS += -DSRV_LF_RFID -APP_LF_RFID = 1 + +APP_ABOUT ?= 0 +ifeq ($(APP_ABOUT), 1) +CFLAGS += -DAPP_ABOUT +SRV_GUI = 1 endif + + APP_LF_RFID ?= 0 ifeq ($(APP_LF_RFID), 1) CFLAGS += -DAPP_LF_RFID -SRV_INPUT = 1 -SRV_GUI = 1 +SRV_GUI = 1 endif + APP_NFC ?= 0 ifeq ($(APP_NFC), 1) CFLAGS += -DAPP_NFC -SRV_MENU = 1 -SRV_INPUT = 1 -SRV_GUI = 1 +SRV_GUI = 1 endif -SRV_IRDA ?= 0 -ifeq ($(SRV_IRDA), 1) -CFLAGS += -DSRV_IRDA -APP_IRDA = 1 -endif + APP_IRDA ?= 0 ifeq ($(APP_IRDA), 1) CFLAGS += -DAPP_IRDA -SRV_INPUT = 1 -SRV_GUI = 1 +SRV_GUI = 1 endif + APP_VIBRO_DEMO ?= 0 ifeq ($(APP_VIBRO_DEMO), 1) CFLAGS += -DAPP_VIBRO_DEMO -SRV_INPUT = 1 +SRV_GUI = 1 endif -SRV_KEYPAD_TEST ?= 0 -ifeq ($(SRV_KEYPAD_TEST), 1) -CFLAGS += -DSRV_KEYPAD_TEST -APP_KEYPAD_TEST = 1 -endif + APP_KEYPAD_TEST ?= 0 ifeq ($(APP_KEYPAD_TEST), 1) CFLAGS += -DAPP_KEYPAD_TEST -APP_KEYPAD_TEST = 1 +SRV_GUI = 1 endif -SRV_ACCESSOR ?= 0 -ifeq ($(SRV_ACCESSOR), 1) -CFLAGS += -DSRV_ACCESSOR -APP_ACCESSOR = 1 -endif + APP_ACCESSOR ?= 0 ifeq ($(APP_ACCESSOR), 1) CFLAGS += -DAPP_ACCESSOR -APP_ACCESSOR = 1 +SRV_GUI = 1 endif -SRV_GPIO_TEST ?= 0 -ifeq ($(SRV_GPIO_TEST), 1) -CFLAGS += -DSRV_GPIO_TEST -APP_GPIO_TEST = 1 -endif + APP_GPIO_TEST ?= 0 ifeq ($(APP_GPIO_TEST), 1) CFLAGS += -DAPP_GPIO_TEST +SRV_GUI = 1 endif -SRV_MUSIC_PLAYER ?= 0 -ifeq ($(SRV_MUSIC_PLAYER), 1) -CFLAGS += -DSRV_MUSIC_PLAYER -APP_MUSIC_PLAYER = 1 -endif + APP_MUSIC_PLAYER ?= 0 ifeq ($(APP_MUSIC_PLAYER), 1) CFLAGS += -DAPP_MUSIC_PLAYER +SRV_GUI = 1 endif -SRV_IBUTTON ?= 0 -ifeq ($(SRV_IBUTTON), 1) -CFLAGS += -DSRV_IBUTTON -APP_IBUTTON = 1 -endif + APP_IBUTTON ?= 0 ifeq ($(APP_IBUTTON), 1) CFLAGS += -DAPP_IBUTTON +SRV_GUI = 1 +endif + + +# Services +# that will start with OS +# Prefix with SRV_* + + +SRV_BT ?= 0 +ifeq ($(SRV_BT), 1) +CFLAGS += -DSRV_BT +SRV_CLI = 1 +endif + + +SRV_DESKTOP ?= 0 +ifeq ($(SRV_DESKTOP), 1) +CFLAGS += -DSRV_DESKTOP +SRV_DOLPHIN = 1 +SRV_STORAGE = 1 +SRV_GUI = 1 +endif + + +SRV_DOLPHIN ?= 0 +ifeq ($(SRV_DOLPHIN), 1) +CFLAGS += -DSRV_DOLPHIN +endif + + +SRV_POWER_OBSERVER ?= 0 +ifeq ($(SRV_POWER_OBSERVER), 1) +CFLAGS += -DSRV_POWER_OBSERVER +SRV_POWER = 1 +endif + + +SRV_POWER ?= 0 +ifeq ($(SRV_POWER), 1) +CFLAGS += -DSRV_POWER +SRV_GUI = 1 +SRV_CLI = 1 +endif + + +SRV_LOADER ?= 0 +ifeq ($(SRV_LOADER), 1) +CFLAGS += -DSRV_LOADER +SRV_GUI = 1 +# Loader autostart hook +LOADER_AUTOSTART ?= "" +ifneq ($(strip $(LOADER_AUTOSTART)), "") +CFLAGS += -DLOADER_AUTOSTART="\"$(LOADER_AUTOSTART)\"" +endif +# Loader autostart hook END +endif + + +SRV_DIALOGS ?= 0 +ifeq ($(SRV_DIALOGS), 1) +CFLAGS += -DSRV_DIALOGS +SRV_GUI = 1 endif -# -# Essential services -# SRV_GUI ?= 0 ifeq ($(SRV_GUI), 1) CFLAGS += -DSRV_GUI +SRV_INPUT = 1 endif + SRV_INPUT ?= 0 ifeq ($(SRV_INPUT), 1) CFLAGS += -DSRV_INPUT endif + SRV_CLI ?= 0 ifeq ($(SRV_CLI), 1) -SRV_GUI = 1 CFLAGS += -DSRV_CLI endif + SRV_NOTIFICATION ?= 0 ifeq ($(SRV_NOTIFICATION), 1) CFLAGS += -DSRV_NOTIFICATION endif + SRV_STORAGE ?= 0 ifeq ($(SRV_STORAGE), 1) CFLAGS += -DSRV_STORAGE endif - -SRV_DIALOGS ?= 0 -ifeq ($(SRV_DIALOGS), 1) -CFLAGS += -DSRV_DIALOGS -endif diff --git a/applications/archive/archive.c b/applications/archive/archive.c index 68fc7287..8b4918a5 100644 --- a/applications/archive/archive.c +++ b/applications/archive/archive.c @@ -31,10 +31,10 @@ ArchiveApp* archive_alloc() { view_dispatcher_set_navigation_event_callback( archive->view_dispatcher, archive_back_event_callback); - archive->main_view = main_view_alloc(); + archive->browser = browser_alloc(); view_dispatcher_add_view( - archive->view_dispatcher, ArchiveViewBrowser, archive_main_get_view(archive->main_view)); + archive->view_dispatcher, ArchiveViewBrowser, archive_browser_get_view(archive->browser)); view_dispatcher_add_view( archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input)); @@ -49,7 +49,7 @@ void archive_free(ArchiveApp* archive) { view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput); view_dispatcher_free(archive->view_dispatcher); scene_manager_free(archive->scene_manager); - main_view_free(archive->main_view); + browser_free(archive->browser); text_input_free(archive->text_input); diff --git a/applications/archive/archive_i.h b/applications/archive/archive_i.h index 474b18e5..ba95ac4b 100644 --- a/applications/archive/archive_i.h +++ b/applications/archive/archive_i.h @@ -9,72 +9,20 @@ #include #include -#include -#include -#include -#include "applications.h" -#include "file-worker.h" - -#include "views/archive_main_view.h" +#include "views/archive_browser_view.h" #include "scenes/archive_scene.h" -#define MAX_FILE_SIZE 128 - typedef enum { ArchiveViewBrowser, ArchiveViewTextInput, ArchiveViewTotal, } ArchiveViewEnum; -static const char* tab_default_paths[] = { - [ArchiveTabFavorites] = "/any/favorites", - [ArchiveTabIButton] = "/any/ibutton", - [ArchiveTabNFC] = "/any/nfc", - [ArchiveTabSubGhz] = "/any/subghz/saved", - [ArchiveTabLFRFID] = "/any/lfrfid", - [ArchiveTabIrda] = "/any/irda", - [ArchiveTabBrowser] = "/any", -}; - -static inline const char* get_default_path(ArchiveFileTypeEnum type) { - switch(type) { - case ArchiveFileTypeIButton: - return tab_default_paths[ArchiveTabIButton]; - case ArchiveFileTypeNFC: - return tab_default_paths[ArchiveTabNFC]; - case ArchiveFileTypeSubGhz: - return tab_default_paths[ArchiveTabSubGhz]; - case ArchiveFileTypeLFRFID: - return tab_default_paths[ArchiveTabLFRFID]; - case ArchiveFileTypeIrda: - return tab_default_paths[ArchiveTabIrda]; - default: - return false; - } -} - -static inline const char* get_favorites_path() { - return tab_default_paths[ArchiveTabFavorites]; -} - -typedef enum { - EventTypeTick, - EventTypeKey, - EventTypeExit, -} EventType; - -typedef struct { - union { - InputEvent input; - } value; - EventType type; -} AppEvent; - struct ArchiveApp { Gui* gui; ViewDispatcher* view_dispatcher; SceneManager* scene_manager; - ArchiveMainView* main_view; + ArchiveBrowserView* browser; TextInput* text_input; char text_store[MAX_NAME_LEN]; }; diff --git a/applications/archive/helpers/archive_browser.c b/applications/archive/helpers/archive_browser.c new file mode 100644 index 00000000..19907984 --- /dev/null +++ b/applications/archive/helpers/archive_browser.c @@ -0,0 +1,260 @@ +#include "archive_browser.h" +#include "math.h" + +void archive_update_offset(ArchiveBrowserView* browser) { + furi_assert(browser); + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + size_t array_size = files_array_size(model->files); + uint16_t bounds = array_size > 3 ? 2 : array_size; + + if(array_size > 3 && model->idx >= array_size - 1) { + model->list_offset = model->idx - 3; + } else if(model->last_offset && model->last_offset != model->list_offset) { + model->list_offset = model->last_offset; + model->last_offset = !model->last_offset; + } else if(model->list_offset < model->idx - bounds) { + model->list_offset = CLAMP(model->idx - 2, array_size - bounds, 0); + } else if(model->list_offset > model->idx - bounds) { + model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0); + } + + return true; + }); +} + +void archive_update_focus(ArchiveBrowserView* browser, const char* target) { + furi_assert(browser); + furi_assert(target); + + archive_get_filenames(browser, string_get_cstr(browser->path)); + + if(!archive_file_array_size(browser) && !archive_get_depth(browser)) { + archive_switch_tab(browser, DEFAULT_TAB_DIR); + } else { + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + uint16_t idx = 0; + while(idx < files_array_size(model->files)) { + ArchiveFile_t* current = files_array_get(model->files, idx); + if(!string_search(current->name, target)) { + model->idx = idx; + break; + } + ++idx; + } + return false; + }); + + archive_update_offset(browser); + } +} + +size_t archive_file_array_size(ArchiveBrowserView* browser) { + uint16_t size = 0; + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + size = files_array_size(model->files); + return false; + }); + return size; +} + +void archive_file_array_rm_selected(ArchiveBrowserView* browser) { + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + files_array_remove_v(model->files, model->idx, model->idx + 1); + model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0); + return false; + }); + + if(!archive_file_array_size(browser) && !archive_get_depth(browser)) { + archive_switch_tab(browser, DEFAULT_TAB_DIR); + } + + archive_update_offset(browser); +} + +void archive_file_array_rm_all(ArchiveBrowserView* browser) { + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + files_array_clean(model->files); + return false; + }); +} + +ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) { + ArchiveFile_t* selected; + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + selected = files_array_size(model->files) ? files_array_get(model->files, model->idx) : + NULL; + return false; + }); + return selected; +} + +ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) { + ArchiveTabEnum tab_id; + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + tab_id = model->tab_idx; + return false; + }); + return tab_id; +} + +uint8_t archive_get_depth(ArchiveBrowserView* browser) { + uint8_t depth; + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + depth = model->depth; + return false; + }); + + return depth; +} + +const char* archive_get_path(ArchiveBrowserView* browser) { + return string_get_cstr(browser->path); +} + +const char* archive_get_name(ArchiveBrowserView* browser) { + ArchiveFile_t* selected = archive_get_current_file(browser); + return string_get_cstr(selected->name); +} + +void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + model->tab_idx = tab; + return false; + }); +} +void archive_set_last_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + model->last_tab = model->tab_idx; + return false; + }); +} + +void archive_add_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name) { + furi_assert(browser); + furi_assert(file_info); + furi_assert(name); + + ArchiveFile_t item; + + if(filter_by_extension(file_info, get_tab_ext(archive_get_tab(browser)), name)) { + ArchiveFile_t_init(&item); + string_init_set_str(item.name, name); + set_file_type(&item, file_info); + + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + files_array_push_back(model->files, item); + return false; + }); + + ArchiveFile_t_clear(&item); + } +} + +void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { + furi_assert(browser); + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + model->menu = show; + model->menu_idx = 0; + + if(show) { + ArchiveFile_t* selected = files_array_get(model->files, model->idx); + selected->fav = archive_is_favorite( + "%s/%s", string_get_cstr(browser->path), string_get_cstr(selected->name)); + } + + return true; + }); +} + +void archive_switch_dir(ArchiveBrowserView* browser, const char* path) { + furi_assert(browser); + furi_assert(path); + + string_set(browser->path, path); + archive_get_filenames(browser, string_get_cstr(browser->path)); + archive_update_offset(browser); +} + +void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { + furi_assert(browser); + ArchiveTabEnum tab = archive_get_tab(browser); + + if(key == InputKeyLeft) { + tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; + } else if(key == InputKeyRight) { + tab = (tab + 1) % ArchiveTabTotal; + } + + archive_set_tab(browser, tab); + + if((tab != ArchiveTabFavorites && + !archive_dir_empty(browser, archive_get_default_path(tab))) || + (tab == ArchiveTabFavorites && !archive_favorites_count(browser))) { + if(tab != ArchiveTabBrowser) { + archive_switch_tab(browser, key); + } + } else { + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + if(model->last_tab != model->tab_idx) { + model->idx = 0; + model->depth = 0; + } + return false; + }); + archive_switch_dir(browser, archive_get_default_path(tab)); + } + archive_set_last_tab(browser, tab); +} + +void archive_enter_dir(ArchiveBrowserView* browser, string_t name) { + furi_assert(browser); + furi_assert(name); + + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + model->last_idx = model->idx; + model->last_offset = model->list_offset; + model->idx = 0; + model->depth = CLAMP(model->depth + 1, MAX_DEPTH, 0); + return false; + }); + + string_cat(browser->path, "/"); + string_cat(browser->path, name); + + archive_switch_dir(browser, string_get_cstr(browser->path)); +} + +void archive_leave_dir(ArchiveBrowserView* browser) { + furi_assert(browser); + + const char* path = archive_get_path(browser); + char* last_char_ptr = strrchr(path, '/'); + + if(last_char_ptr) { + size_t pos = last_char_ptr - path; + string_left(browser->path, pos); + } + + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + model->depth = CLAMP(model->depth - 1, MAX_DEPTH, 0); + model->idx = model->last_idx; + return false; + }); + + archive_switch_dir(browser, path); +} diff --git a/applications/archive/helpers/archive_browser.h b/applications/archive/helpers/archive_browser.h new file mode 100644 index 00000000..23872e17 --- /dev/null +++ b/applications/archive/helpers/archive_browser.h @@ -0,0 +1,68 @@ +#pragma once + +#include "../archive_i.h" + +#define DEFAULT_TAB_DIR InputKeyRight //default tab swith direction + +static const char* tab_default_paths[] = { + [ArchiveTabFavorites] = "/any/favorites", + [ArchiveTabIButton] = "/any/ibutton", + [ArchiveTabNFC] = "/any/nfc", + [ArchiveTabSubGhz] = "/any/subghz/saved", + [ArchiveTabLFRFID] = "/any/lfrfid", + [ArchiveTabIrda] = "/any/irda", + [ArchiveTabBrowser] = "/any", +}; + +static const char* known_ext[] = { + [ArchiveFileTypeIButton] = ".ibtn", + [ArchiveFileTypeNFC] = ".nfc", + [ArchiveFileTypeSubGhz] = ".sub", + [ArchiveFileTypeLFRFID] = ".rfid", + [ArchiveFileTypeIrda] = ".ir", +}; + +static inline const char* get_tab_ext(ArchiveTabEnum tab) { + switch(tab) { + case ArchiveTabIButton: + return known_ext[ArchiveFileTypeIButton]; + case ArchiveTabNFC: + return known_ext[ArchiveFileTypeNFC]; + case ArchiveTabSubGhz: + return known_ext[ArchiveFileTypeSubGhz]; + case ArchiveTabLFRFID: + return known_ext[ArchiveFileTypeLFRFID]; + case ArchiveTabIrda: + return known_ext[ArchiveFileTypeIrda]; + default: + return "*"; + } +} + +static inline const char* archive_get_default_path(ArchiveTabEnum tab) { + return tab_default_paths[tab]; +} + +inline bool is_known_app(ArchiveFileTypeEnum type) { + return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown); +} + +void archive_update_offset(ArchiveBrowserView* browser); +void archive_update_focus(ArchiveBrowserView* browser, const char* target); + +size_t archive_file_array_size(ArchiveBrowserView* browser); +void archive_file_array_rm_selected(ArchiveBrowserView* browser); +void archive_file_array_rm_all(ArchiveBrowserView* browser); + +ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser); +ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser); +uint8_t archive_get_depth(ArchiveBrowserView* browser); +const char* archive_get_path(ArchiveBrowserView* browser); +const char* archive_get_name(ArchiveBrowserView* browser); + +void archive_add_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name); +void archive_show_file_menu(ArchiveBrowserView* browser, bool show); + +void archive_switch_tab(ArchiveBrowserView* browser, InputKey key); +void archive_enter_dir(ArchiveBrowserView* browser, string_t name); +void archive_leave_dir(ArchiveBrowserView* browser); diff --git a/applications/archive/helpers/archive_favorites.c b/applications/archive/helpers/archive_favorites.c index 6eeefdbb..7f419890 100644 --- a/applications/archive/helpers/archive_favorites.c +++ b/applications/archive/helpers/archive_favorites.c @@ -1,18 +1,47 @@ + #include "archive_favorites.h" -#include "archive_files.h" -#include "../views/archive_main_view.h" +#include "archive_browser.h" + +uint16_t archive_favorites_count(void* context) { + furi_assert(context); + + FileWorker* file_worker = file_worker_alloc(true); + + string_t buffer; + string_init(buffer); + + bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); + uint16_t lines = 0; + + if(result) { + while(1) { + if(!file_worker_read_until(file_worker, buffer, '\n')) { + break; + } + if(!string_size(buffer)) { + break; + } + ++lines; + } + } + + string_clear(buffer); + file_worker_close(file_worker); + file_worker_free(file_worker); + return lines; +} bool archive_favorites_read(void* context) { furi_assert(context); - ArchiveMainView* archive_view = context; + ArchiveBrowserView* archive_view = context; FileWorker* file_worker = file_worker_alloc(true); string_t buffer; FileInfo file_info; string_init(buffer); - bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS); + bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { while(1) { @@ -23,7 +52,7 @@ bool archive_favorites_read(void* context) { break; } - archive_view_add_item(archive_view, &file_info, string_get_cstr(buffer)); + archive_add_item(archive_view, &file_info, string_get_cstr(buffer)); string_clean(buffer); } } @@ -33,18 +62,19 @@ bool archive_favorites_read(void* context) { return result; } -bool archive_favorites_delete(const char* file_path, const char* name) { - furi_assert(file_path); - furi_assert(name); +bool archive_favorites_delete(const char* format, ...) { + va_list args; + va_start(args, format); + uint8_t len = vsnprintf(NULL, 0, format, args); + char filename[len + 1]; + vsnprintf(filename, len + 1, format, args); + va_end(args); FileWorker* file_worker = file_worker_alloc(true); - string_t path; string_t buffer; string_init(buffer); - string_init_printf(path, "%s/%s", file_path, name); - bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { while(1) { @@ -55,17 +85,13 @@ bool archive_favorites_delete(const char* file_path, const char* name) { break; } - if(string_search(buffer, path)) { - string_t temp; - string_init_printf(temp, "%s\r\n", string_get_cstr(buffer)); - archive_file_append(ARCHIVE_FAV_TEMP_PATH, temp); - string_clear(temp); + if(string_search_str(buffer, filename)) { + archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\r\n", string_get_cstr(buffer)); } } } string_clear(buffer); - string_clear(path); file_worker_close(file_worker); file_worker_remove(file_worker, ARCHIVE_FAV_PATH); @@ -76,19 +102,20 @@ bool archive_favorites_delete(const char* file_path, const char* name) { return result; } -bool archive_is_favorite(const char* file_path, const char* name) { - furi_assert(file_path); - furi_assert(name); +bool archive_is_favorite(const char* format, ...) { + va_list args; + va_start(args, format); + uint8_t len = vsnprintf(NULL, 0, format, args); + char filename[len + 1]; + vsnprintf(filename, len + 1, format, args); + va_end(args); FileWorker* file_worker = file_worker_alloc(true); - - string_t path; string_t buffer; string_init(buffer); - bool found = false; - string_init_printf(path, "%s/%s", file_path, name); - bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS); + bool found = false; + bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { while(1) { @@ -98,7 +125,7 @@ bool archive_is_favorite(const char* file_path, const char* name) { if(!string_size(buffer)) { break; } - if(!string_search(buffer, path)) { + if(!string_search_str(buffer, filename)) { found = true; break; } @@ -106,7 +133,6 @@ bool archive_is_favorite(const char* file_path, const char* name) { } string_clear(buffer); - string_clear(path); file_worker_close(file_worker); file_worker_free(file_worker); @@ -122,10 +148,8 @@ bool archive_favorites_rename(const char* file_path, const char* src, const char string_t path; string_t buffer; - string_t temp; string_init(buffer); - string_init(temp); string_init(path); string_printf(path, "%s/%s", file_path, src); @@ -139,14 +163,14 @@ bool archive_favorites_rename(const char* file_path, const char* src, const char if(!string_size(buffer)) { break; } - string_printf( - temp, "%s\r\n", string_search(buffer, path) ? string_get_cstr(buffer) : dst); - archive_file_append(ARCHIVE_FAV_TEMP_PATH, temp); - string_clean(temp); + + archive_file_append( + ARCHIVE_FAV_TEMP_PATH, + "%s\r\n", + string_search(buffer, path) ? string_get_cstr(buffer) : dst); } } - string_clear(temp); string_clear(buffer); string_clear(path); @@ -159,13 +183,8 @@ bool archive_favorites_rename(const char* file_path, const char* src, const char return result; } -void archive_add_to_favorites(const char* file_path, const char* name) { +void archive_add_to_favorites(const char* file_path) { furi_assert(file_path); - furi_assert(name); - string_t buffer_src; - - string_init_printf(buffer_src, "%s/%s\r\n", file_path, name); - archive_file_append(ARCHIVE_FAV_PATH, buffer_src); - string_clear(buffer_src); + archive_file_append(ARCHIVE_FAV_PATH, "%s\r\n", file_path); } diff --git a/applications/archive/helpers/archive_favorites.h b/applications/archive/helpers/archive_favorites.h index 7a8fce00..b07d7f87 100644 --- a/applications/archive/helpers/archive_favorites.h +++ b/applications/archive/helpers/archive_favorites.h @@ -4,8 +4,9 @@ #define ARCHIVE_FAV_PATH "/any/favorites.txt" #define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp" +uint16_t archive_favorites_count(void* context); bool archive_favorites_read(void* context); -bool archive_favorites_delete(const char* file_path, const char* name); -bool archive_is_favorite(const char* file_path, const char* name); +bool archive_favorites_delete(const char* format, ...); +bool archive_is_favorite(const char* format, ...); bool archive_favorites_rename(const char* file_path, const char* src, const char* dst); -void archive_add_to_favorites(const char* file_path, const char* name); +void archive_add_to_favorites(const char* file_path); \ No newline at end of file diff --git a/applications/archive/helpers/archive_files.c b/applications/archive/helpers/archive_files.c index 036df166..127b2fd3 100644 --- a/applications/archive/helpers/archive_files.c +++ b/applications/archive/helpers/archive_files.c @@ -1,6 +1,5 @@ #include "archive_files.h" -#include "archive_favorites.h" -#include "../archive_i.h" +#include "archive_browser.h" bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) { furi_assert(file_info); @@ -20,14 +19,12 @@ bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* n return result; } -void archive_trim_file_ext(char* name) { - size_t str_len = strlen(name); - char* end = name + str_len; - while(end > name && *end != '.' && *end != '\\' && *end != '/') { - --end; - } - if((end > name && *end == '.') && (*(end - 1) != '\\' && *(end - 1) != '/')) { - *end = '\0'; +void archive_trim_file_path(char* name, bool ext) { + char* slash = strrchr(name, '/') + 1; + if(strlen(slash)) strlcpy(name, slash, strlen(slash) + 1); + if(ext) { + char* dot = strrchr(name, '.'); + if(strlen(dot)) *dot = '\0'; } } @@ -49,24 +46,24 @@ void set_file_type(ArchiveFile_t* file, FileInfo* file_info) { } } -bool archive_get_filenames(void* context, uint8_t tab_id, const char* path) { +bool archive_get_filenames(void* context, const char* path) { furi_assert(context); - ArchiveMainView* main_view = context; - archive_file_array_clean(main_view); + bool res; + ArchiveBrowserView* browser = context; + archive_file_array_rm_all(browser); - if(tab_id != ArchiveTabFavorites) { - archive_read_dir(main_view, path); + if(archive_get_tab(browser) != ArchiveTabFavorites) { + res = archive_read_dir(browser, path); } else { - archive_favorites_read(main_view); + res = archive_favorites_read(browser); } - return true; + return res; } -bool archive_read_dir(void* context, const char* path) { +bool archive_dir_empty(void* context, const char* path) { // can be simpler? furi_assert(context); - ArchiveMainView* main_view = context; FileInfo file_info; Storage* fs_api = furi_record_open("storage"); File* directory = storage_file_alloc(fs_api); @@ -78,17 +75,52 @@ bool archive_read_dir(void* context, const char* path) { return false; } + bool files_found = false; while(1) { if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) { break; } + if(files_found) { + break; + } else if(storage_file_get_error(directory) == FSE_OK) { + files_found = name[0]; + } else { + return false; + } + } + storage_dir_close(directory); + storage_file_free(directory); - uint16_t files_cnt = archive_file_array_size(main_view); + furi_record_close("storage"); + return files_found; +} + +bool archive_read_dir(void* context, const char* path) { + furi_assert(context); + + ArchiveBrowserView* browser = context; + FileInfo file_info; + Storage* fs_api = furi_record_open("storage"); + File* directory = storage_file_alloc(fs_api); + char name[MAX_NAME_LEN]; + size_t files_cnt = 0; + + if(!storage_dir_open(directory, path)) { + storage_dir_close(directory); + storage_file_free(directory); + return false; + } + + while(1) { + if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) { + break; + } if(files_cnt > MAX_FILES) { break; } else if(storage_file_get_error(directory) == FSE_OK) { - archive_view_add_item(main_view, &file_info, name); + archive_add_item(browser, &file_info, name); + ++files_cnt; } else { storage_dir_close(directory); storage_file_free(directory); @@ -103,9 +135,15 @@ bool archive_read_dir(void* context, const char* path) { return true; } -void archive_file_append(const char* path, string_t string) { +void archive_file_append(const char* path, const char* format, ...) { furi_assert(path); - furi_assert(string); + + va_list args; + va_start(args, format); + uint8_t len = vsnprintf(NULL, 0, format, args); + char cstr_buff[len + 1]; + vsnprintf(cstr_buff, len + 1, format, args); + va_end(args); FileWorker* file_worker = file_worker_alloc(false); @@ -113,7 +151,7 @@ void archive_file_append(const char* path, string_t string) { FURI_LOG_E("Archive", "Append open error"); } - if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) { + if(!file_worker_write(file_worker, cstr_buff, strlen(cstr_buff))) { FURI_LOG_E("Archive", "Append write error"); } @@ -125,19 +163,22 @@ void archive_delete_file(void* context, string_t path, string_t name) { furi_assert(context); furi_assert(path); furi_assert(name); - ArchiveMainView* main_view = context; - FileWorker* file_worker = file_worker_alloc(false); + ArchiveBrowserView* browser = context; + FileWorker* file_worker = file_worker_alloc(true); string_t full_path; - string_init(full_path); - string_printf(full_path, "%s/%s", string_get_cstr(path), string_get_cstr(name)); - file_worker_remove(file_worker, string_get_cstr(full_path)); - file_worker_free(file_worker); - string_clear(full_path); + string_init_printf(full_path, "%s/%s", string_get_cstr(path), string_get_cstr(name)); - if(archive_is_favorite(string_get_cstr(path), string_get_cstr(name))) { - archive_favorites_delete(string_get_cstr(path), string_get_cstr(name)); + bool res = file_worker_remove(file_worker, string_get_cstr(full_path)); + file_worker_free(file_worker); + + if(archive_is_favorite(string_get_cstr(full_path))) { + archive_favorites_delete(string_get_cstr(full_path)); } - archive_file_array_remove_selected(main_view); + if(res) { + archive_file_array_rm_selected(browser); + } + + string_clear(full_path); } diff --git a/applications/archive/helpers/archive_files.h b/applications/archive/helpers/archive_files.h index 71864fe9..92fb7d20 100644 --- a/applications/archive/helpers/archive_files.h +++ b/applications/archive/helpers/archive_files.h @@ -49,8 +49,9 @@ ARRAY_DEF( bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name); void set_file_type(ArchiveFile_t* file, FileInfo* file_info); -void archive_trim_file_ext(char* name); -bool archive_get_filenames(void* context, uint8_t tab_id, const char* path); +void archive_trim_file_path(char* name, bool ext); +bool archive_get_filenames(void* context, const char* path); +bool archive_dir_empty(void* context, const char* path); bool archive_read_dir(void* context, const char* path); -void archive_file_append(const char* path, string_t string); +void archive_file_append(const char* path, const char* format, ...); void archive_delete_file(void* context, string_t path, string_t name); \ No newline at end of file diff --git a/applications/archive/scenes/archive_scene_browser.c b/applications/archive/scenes/archive_scene_browser.c index ed6204ff..03c78fae 100644 --- a/applications/archive/scenes/archive_scene_browser.c +++ b/applications/archive/scenes/archive_scene_browser.c @@ -1,32 +1,114 @@ #include "../archive_i.h" -#include "../views/archive_main_view.h" +#include "../helpers/archive_files.h" +#include "../helpers/archive_favorites.h" +#include "../helpers/archive_browser.h" +#include "../views/archive_browser_view.h" + +static const char* flipper_app_name[] = { + [ArchiveFileTypeIButton] = "iButton", + [ArchiveFileTypeNFC] = "NFC", + [ArchiveFileTypeSubGhz] = "Sub-GHz", + [ArchiveFileTypeLFRFID] = "125 kHz RFID", + [ArchiveFileTypeIrda] = "Infrared", +}; + +static void archive_run_in_app( + ArchiveBrowserView* browser, + ArchiveFile_t* selected, + bool full_path_provided) { + Loader* loader = furi_record_open("loader"); + + string_t full_path; + if(!full_path_provided) { + string_init_printf( + full_path, "%s/%s", string_get_cstr(browser->path), string_get_cstr(selected->name)); + } else { + string_init_set(full_path, selected->name); + } + loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path)); + + string_clear(full_path); + furi_record_close("loader"); +} void archive_scene_browser_callback(ArchiveBrowserEvent event, void* context) { ArchiveApp* archive = (ArchiveApp*)context; view_dispatcher_send_custom_event(archive->view_dispatcher, event); } -const void archive_scene_browser_on_enter(void* context) { +void archive_scene_browser_on_enter(void* context) { ArchiveApp* archive = (ArchiveApp*)context; - ArchiveMainView* main_view = archive->main_view; + ArchiveBrowserView* browser = archive->browser; - archive_browser_set_callback(main_view, archive_scene_browser_callback, archive); - archive_browser_update(main_view); + archive_browser_set_callback(browser, archive_scene_browser_callback, archive); + archive_update_focus(browser, archive->text_store); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser); } -const bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { +bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { ArchiveApp* archive = (ArchiveApp*)context; - bool consumed; + ArchiveBrowserView* browser = archive->browser; + ArchiveFile_t* selected = archive_get_current_file(browser); + + const char* path = archive_get_path(browser); + const char* name = archive_get_name(browser); + bool known_app = is_known_app(selected->type); + bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; + bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case ArchiveBrowserEventRename: - scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); + case ArchiveBrowserEventFileMenuOpen: + archive_show_file_menu(browser, true); consumed = true; break; + case ArchiveBrowserEventFileMenuClose: + archive_show_file_menu(browser, false); + consumed = true; + break; + case ArchiveBrowserEventFileMenuRun: + if(known_app) { + archive_run_in_app(browser, selected, favorites); + } + consumed = true; + break; + case ArchiveBrowserEventFileMenuPin: + if(favorites) { + archive_favorites_delete(name); + archive_file_array_rm_selected(browser); + } else if(known_app) { + if(archive_is_favorite("%s/%s", path, name)) { + archive_favorites_delete("%s/%s", path, name); + } else { + archive_file_append(ARCHIVE_FAV_PATH, "%s/%s\r\n", path, name); + } + } + archive_show_file_menu(browser, false); + consumed = true; + break; + + case ArchiveBrowserEventFileMenuRename: + if(known_app && !favorites) { + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); + } + consumed = true; + break; + case ArchiveBrowserEventFileMenuDelete: + archive_delete_file(browser, browser->path, selected->name); + archive_show_file_menu(browser, false); + consumed = true; + break; + case ArchiveBrowserEventEnterDir: + archive_enter_dir(browser, selected->name); + consumed = true; + break; + case ArchiveBrowserEventExit: - view_dispatcher_stop(archive->view_dispatcher); + if(archive_get_depth(browser)) { + archive_leave_dir(browser); + } else { + view_dispatcher_stop(archive->view_dispatcher); + } consumed = true; break; @@ -37,6 +119,6 @@ const bool archive_scene_browser_on_event(void* context, SceneManagerEvent event return consumed; } -const void archive_scene_browser_on_exit(void* context) { +void archive_scene_browser_on_exit(void* context) { // ArchiveApp* archive = (ArchiveApp*)context; } diff --git a/applications/archive/scenes/archive_scene_rename.c b/applications/archive/scenes/archive_scene_rename.c index 51918e5a..ad7669bc 100644 --- a/applications/archive/scenes/archive_scene_rename.c +++ b/applications/archive/scenes/archive_scene_rename.c @@ -1,22 +1,24 @@ #include "../archive_i.h" #include "../helpers/archive_favorites.h" #include "../helpers/archive_files.h" +#include "../helpers/archive_browser.h" #define SCENE_RENAME_CUSTOM_EVENT (0UL) +#define MAX_TEXT_INPUT_LEN 22 void archive_scene_rename_text_input_callback(void* context) { ArchiveApp* archive = (ArchiveApp*)context; view_dispatcher_send_custom_event(archive->view_dispatcher, SCENE_RENAME_CUSTOM_EVENT); } -const void archive_scene_rename_on_enter(void* context) { +void archive_scene_rename_on_enter(void* context) { ArchiveApp* archive = (ArchiveApp*)context; TextInput* text_input = archive->text_input; - ArchiveFile_t* current = archive_get_current_file(archive->main_view); + ArchiveFile_t* current = archive_get_current_file(archive->browser); strlcpy(archive->text_store, string_get_cstr(current->name), MAX_NAME_LEN); - archive_trim_file_ext(archive->text_store); + archive_trim_file_path(archive->text_store, true); text_input_set_header_text(text_input, "Rename:"); @@ -25,13 +27,13 @@ const void archive_scene_rename_on_enter(void* context) { archive_scene_rename_text_input_callback, archive, archive->text_store, - MAX_NAME_LEN, + MAX_TEXT_INPUT_LEN, false); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); } -const bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { +bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { ArchiveApp* archive = (ArchiveApp*)context; bool consumed = false; @@ -42,16 +44,14 @@ const bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) string_t buffer_src; string_t buffer_dst; - const char* path = archive_get_path(archive->main_view); - const char* name = archive_get_name(archive->main_view); + const char* path = archive_get_path(archive->browser); + const char* name = archive_get_name(archive->browser); string_init_printf(buffer_src, "%s/%s", path, name); string_init_printf(buffer_dst, "%s/%s", path, archive->text_store); - archive_set_name(archive->main_view, archive->text_store); - // append extension - ArchiveFile_t* file = archive_get_current_file(archive->main_view); + ArchiveFile_t* file = archive_get_current_file(archive->browser); string_cat(buffer_dst, known_ext[file->type]); storage_common_rename( @@ -72,9 +72,8 @@ const bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) return consumed; } -const void archive_scene_rename_on_exit(void* context) { +void archive_scene_rename_on_exit(void* context) { ArchiveApp* archive = (ArchiveApp*)context; // Clear view - text_input_set_header_text(archive->text_input, NULL); - text_input_set_result_callback(archive->text_input, NULL, NULL, NULL, 0, false); + text_input_clean(archive->text_input); } diff --git a/applications/archive/views/archive_browser_view.c b/applications/archive/views/archive_browser_view.c new file mode 100644 index 00000000..7b0a912f --- /dev/null +++ b/applications/archive/views/archive_browser_view.c @@ -0,0 +1,294 @@ +#include +#include "../archive_i.h" +#include "archive_browser_view.h" +#include "../helpers/archive_browser.h" + +static const char* ArchiveTabNames[] = { + [ArchiveTabFavorites] = "Favorites", + [ArchiveTabIButton] = "iButton", + [ArchiveTabNFC] = "NFC", + [ArchiveTabSubGhz] = "Sub-GHz", + [ArchiveTabLFRFID] = "RFID LF", + [ArchiveTabIrda] = "Infrared", + [ArchiveTabBrowser] = "Browser"}; + +static const Icon* ArchiveItemIcons[] = { + [ArchiveFileTypeIButton] = &I_ibutt_10px, + [ArchiveFileTypeNFC] = &I_Nfc_10px, + [ArchiveFileTypeSubGhz] = &I_sub1_10px, + [ArchiveFileTypeLFRFID] = &I_125_10px, + [ArchiveFileTypeIrda] = &I_ir_10px, + [ArchiveFileTypeFolder] = &I_dir_10px, + [ArchiveFileTypeUnknown] = &I_unknown_10px, +}; + +void archive_browser_set_callback( + ArchiveBrowserView* browser, + ArchiveBrowserViewCallback callback, + void* context) { + furi_assert(browser); + furi_assert(callback); + browser->callback = callback; + browser->context = context; +} + +static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 71, 17, 57, 46); + canvas_set_color(canvas, ColorBlack); + elements_slightly_rounded_frame(canvas, 70, 16, 58, 48); + + string_t menu[MENU_ITEMS]; + + string_init_set_str(menu[0], "Run in app"); + string_init_set_str(menu[1], "Pin"); + string_init_set_str(menu[2], "Rename"); + string_init_set_str(menu[3], "Delete"); + + ArchiveFile_t* selected = files_array_get(model->files, model->idx); + + if(!is_known_app(selected->type)) { + string_set_str(menu[0], "---"); + string_set_str(menu[1], "---"); + string_set_str(menu[2], "---"); + } else if(selected->fav) { + string_set_str(menu[1], "Unpin"); + } else if(model->tab_idx == ArchiveTabFavorites) { + string_set_str(menu[1], "Unpin"); + string_set_str(menu[2], "---"); + } + + for(size_t i = 0; i < MENU_ITEMS; i++) { + canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i])); + string_clear(menu[i]); + } + + canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7); +} + +static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1); + + canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11); +} + +static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { + furi_assert(model); + + size_t array_size = files_array_size(model->files); + bool scrollbar = array_size > 4; + + for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) { + string_t str_buff; + char cstr_buff[MAX_NAME_LEN]; + + size_t idx = CLAMP(i + model->list_offset, array_size, 0); + ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0)); + + strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name) + 1); + archive_trim_file_path(cstr_buff, is_known_app(file->type)); + string_init_set_str(str_buff, cstr_buff); + elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); + + if(model->idx == idx) { + archive_draw_frame(canvas, i, scrollbar); + } else { + canvas_set_color(canvas, ColorBlack); + } + + canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]); + canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); + string_clear(str_buff); + } + + if(scrollbar) { + elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size); + } + + if(model->menu) { + render_item_menu(canvas, model); + } +} + +static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* model) { + furi_assert(model); + + const char* tab_name = ArchiveTabNames[model->tab_idx]; + + canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, 0, 50, 13); + canvas_draw_box(canvas, 107, 0, 20, 13); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, 1, 0, 50, 12); + canvas_draw_line(canvas, 0, 1, 0, 11); + canvas_draw_line(canvas, 1, 12, 49, 12); + canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name); + + canvas_draw_frame(canvas, 108, 0, 20, 12); + canvas_draw_line(canvas, 107, 1, 107, 11); + canvas_draw_line(canvas, 108, 12, 126, 12); + + canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 50, 0); + canvas_draw_dot(canvas, 127, 0); + + canvas_set_color(canvas, ColorBlack); +} + +void archive_view_render(Canvas* canvas, void* model) { + ArchiveBrowserViewModel* m = model; + + archive_render_status_bar(canvas, model); + + if(files_array_size(m->files)) { + draw_list(canvas, m); + } else { + canvas_draw_str_aligned( + canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty"); + } +} + +View* archive_browser_get_view(ArchiveBrowserView* browser) { + furi_assert(browser); + return browser->view; +} + +bool archive_view_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + ArchiveBrowserView* browser = context; + + bool in_menu; + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + in_menu = model->menu; + return false; + }); + + if(in_menu) { + if(event->type == InputTypeShort) { + if(event->key == InputKeyUp || event->key == InputKeyDown) { + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + if(event->key == InputKeyUp) { + model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS; + } else if(event->key == InputKeyDown) { + model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS; + } + return true; + }); + } + + if(event->key == InputKeyOk) { + uint8_t idx; + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + idx = model->menu_idx; + return false; + }); + browser->callback(file_menu_actions[idx], browser->context); + } else if(event->key == InputKeyBack) { + browser->callback(ArchiveBrowserEventFileMenuClose, browser->context); + } + } + + } else { + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft || event->key == InputKeyRight) { + archive_switch_tab(browser, event->key); + } else if(event->key == InputKeyBack) { + browser->callback(ArchiveBrowserEventExit, browser->context); + } + } + if(event->key == InputKeyUp || event->key == InputKeyDown) { + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + uint16_t num_elements = (uint16_t)files_array_size(model->files); + if((event->type == InputTypeShort || event->type == InputTypeRepeat)) { + if(event->key == InputKeyUp) { + model->idx = ((model->idx - 1) + num_elements) % num_elements; + } else if(event->key == InputKeyDown) { + model->idx = (model->idx + 1) % num_elements; + } + } + + return true; + }); + archive_update_offset(browser); + } + + if(event->key == InputKeyOk) { + ArchiveFile_t* selected = archive_get_current_file(browser); + + if(selected) { + bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; + bool folder = selected->type == ArchiveFileTypeFolder; + + if(event->type == InputTypeShort) { + if(favorites) { + browser->callback(ArchiveBrowserEventFileMenuRun, browser->context); + } else if(folder) { + browser->callback(ArchiveBrowserEventEnterDir, browser->context); + } else { + browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); + } + } else if(event->type == InputTypeLong) { + if(folder || favorites) { + browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); + } + } + } + } + } + + return true; +} + +ArchiveBrowserView* browser_alloc() { + ArchiveBrowserView* browser = furi_alloc(sizeof(ArchiveBrowserView)); + browser->view = view_alloc(); + view_allocate_model(browser->view, ViewModelTypeLocking, sizeof(ArchiveBrowserViewModel)); + view_set_context(browser->view, browser); + view_set_draw_callback(browser->view, (ViewDrawCallback)archive_view_render); + view_set_input_callback(browser->view, archive_view_input); + + string_init(browser->path); + + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + files_array_init(model->files); + return true; + }); + + return browser; +} + +void browser_free(ArchiveBrowserView* browser) { + furi_assert(browser); + + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + files_array_clear(model->files); + return false; + }); + + string_clear(browser->path); + + view_free(browser->view); + free(browser); +} diff --git a/applications/archive/views/archive_browser_view.h b/applications/archive/views/archive_browser_view.h new file mode 100644 index 00000000..9f876b60 --- /dev/null +++ b/applications/archive/views/archive_browser_view.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "../helpers/archive_files.h" +#include "../helpers/archive_favorites.h" + +#define MAX_LEN_PX 110 +#define MAX_NAME_LEN 255 +#define FRAME_HEIGHT 12 +#define MENU_ITEMS 4 +#define MAX_DEPTH 32 + +typedef enum { + ArchiveTabFavorites, + ArchiveTabLFRFID, + ArchiveTabSubGhz, + ArchiveTabNFC, + ArchiveTabIButton, + ArchiveTabIrda, + ArchiveTabBrowser, + ArchiveTabTotal, +} ArchiveTabEnum; + +typedef enum { + ArchiveBrowserEventFileMenuOpen, + ArchiveBrowserEventFileMenuClose, + ArchiveBrowserEventFileMenuRun, + ArchiveBrowserEventFileMenuPin, + ArchiveBrowserEventFileMenuRename, + ArchiveBrowserEventFileMenuDelete, + ArchiveBrowserEventEnterDir, + ArchiveBrowserEventExit, +} ArchiveBrowserEvent; + +static const uint8_t file_menu_actions[MENU_ITEMS] = { + [0] = ArchiveBrowserEventFileMenuRun, + [1] = ArchiveBrowserEventFileMenuPin, + [2] = ArchiveBrowserEventFileMenuRename, + [3] = ArchiveBrowserEventFileMenuDelete, +}; + +typedef struct ArchiveBrowserView ArchiveBrowserView; + +typedef void (*ArchiveBrowserViewCallback)(ArchiveBrowserEvent event, void* context); + +typedef enum { + BrowserActionBrowse, + BrowserActionItemMenu, + BrowserActionTotal, +} BrowserActionEnum; + +struct ArchiveBrowserView { + View* view; + ArchiveBrowserViewCallback callback; + void* context; + + string_t path; +}; + +typedef struct { + ArchiveTabEnum tab_idx; + ArchiveTabEnum last_tab; + files_array_t files; + + uint8_t menu_idx; + bool menu; + + uint16_t idx; + uint16_t last_idx; + uint16_t list_offset; + uint16_t last_offset; + uint8_t depth; + +} ArchiveBrowserViewModel; + +void archive_browser_set_callback( + ArchiveBrowserView* browser, + ArchiveBrowserViewCallback callback, + void* context); + +View* archive_browser_get_view(ArchiveBrowserView* browser); + +ArchiveBrowserView* browser_alloc(); +void browser_free(ArchiveBrowserView* browser); diff --git a/applications/archive/views/archive_main_view.c b/applications/archive/views/archive_main_view.c deleted file mode 100644 index ab2e11d7..00000000 --- a/applications/archive/views/archive_main_view.c +++ /dev/null @@ -1,641 +0,0 @@ -#include -#include "../archive_i.h" -#include "archive_main_view.h" - -static const char* flipper_app_name[] = { - [ArchiveFileTypeIButton] = "iButton", - [ArchiveFileTypeNFC] = "NFC", - [ArchiveFileTypeSubGhz] = "Sub-GHz", - [ArchiveFileTypeLFRFID] = "125 kHz RFID", - [ArchiveFileTypeIrda] = "Infrared", -}; - -static const char* ArchiveTabNames[] = { - [ArchiveTabFavorites] = "Favorites", - [ArchiveTabIButton] = "iButton", - [ArchiveTabNFC] = "NFC", - [ArchiveTabSubGhz] = "Sub-GHz", - [ArchiveTabLFRFID] = "RFID LF", - [ArchiveTabIrda] = "Infrared", - [ArchiveTabBrowser] = "Browser"}; - -static const Icon* ArchiveItemIcons[] = { - [ArchiveFileTypeIButton] = &I_ibutt_10px, - [ArchiveFileTypeNFC] = &I_Nfc_10px, - [ArchiveFileTypeSubGhz] = &I_sub1_10px, - [ArchiveFileTypeLFRFID] = &I_125_10px, - [ArchiveFileTypeIrda] = &I_ir_10px, - [ArchiveFileTypeFolder] = &I_dir_10px, - [ArchiveFileTypeUnknown] = &I_unknown_10px, -}; - -void archive_browser_set_callback( - ArchiveMainView* main_view, - ArchiveMainViewCallback callback, - void* context) { - furi_assert(main_view); - furi_assert(callback); - main_view->callback = callback; - main_view->context = context; -} - -void update_offset(ArchiveMainView* main_view) { - furi_assert(main_view); - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - size_t array_size = files_array_size(model->files); - uint16_t bounds = array_size > 3 ? 2 : array_size; - - if(array_size > 3 && model->idx >= array_size - 1) { - model->list_offset = model->idx - 3; - } else if(model->list_offset < model->idx - bounds) { - model->list_offset = CLAMP(model->idx - 2, array_size - bounds, 0); - } else if(model->list_offset > model->idx - bounds) { - model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0); - } - return true; - }); -} - -size_t archive_file_array_size(ArchiveMainView* main_view) { - uint16_t size = 0; - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - size = files_array_size(model->files); - return true; - }); - return size; -} - -void archive_file_array_remove_selected(ArchiveMainView* main_view) { - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - files_array_remove_v(model->files, model->idx, model->idx + 1); - model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0); - return true; - }); - - update_offset(main_view); -} - -void archive_file_array_clean(ArchiveMainView* main_view) { - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - files_array_clean(model->files); - return true; - }); -} - -ArchiveFile_t* archive_get_current_file(ArchiveMainView* main_view) { - ArchiveFile_t* selected; - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - selected = files_array_size(model->files) > 0 ? - files_array_get(model->files, model->idx) : - NULL; - return true; - }); - return selected; -} - -ArchiveTabEnum archive_get_tab(ArchiveMainView* main_view) { - ArchiveTabEnum tab_id; - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - tab_id = model->tab_idx; - return true; - }); - return tab_id; -} - -void archive_set_tab(ArchiveMainView* main_view, ArchiveTabEnum tab) { - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - model->tab_idx = tab; - return true; - }); -} - -uint8_t archive_get_depth(ArchiveMainView* main_view) { - uint8_t depth; - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - depth = model->depth; - return true; - }); - - return depth; -} - -const char* archive_get_path(ArchiveMainView* main_view) { - return string_get_cstr(main_view->path); -} -const char* archive_get_name(ArchiveMainView* main_view) { - ArchiveFile_t* selected = archive_get_current_file(main_view); - return string_get_cstr(selected->name); -} - -void archive_set_name(ArchiveMainView* main_view, const char* name) { - furi_assert(main_view); - furi_assert(name); - - string_set(main_view->name, name); -} - -void archive_browser_update(ArchiveMainView* main_view) { - furi_assert(main_view); - - archive_get_filenames(main_view, archive_get_tab(main_view), string_get_cstr(main_view->path)); - - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - uint16_t idx = 0; - while(idx < files_array_size(model->files)) { - ArchiveFile_t* current = files_array_get(model->files, idx); - if(!string_search(current->name, string_get_cstr(main_view->name))) { - model->idx = idx; - break; - } - ++idx; - } - return true; - }); - - update_offset(main_view); -} - -void archive_view_add_item(ArchiveMainView* main_view, FileInfo* file_info, const char* name) { - furi_assert(main_view); - furi_assert(file_info); - furi_assert(name); - - ArchiveFile_t item; - - if(filter_by_extension(file_info, get_tab_ext(archive_get_tab(main_view)), name)) { - ArchiveFile_t_init(&item); - string_init_set_str(item.name, name); - set_file_type(&item, file_info); - - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - files_array_push_back(model->files, item); - return true; - }); - - ArchiveFile_t_clear(&item); - } -} - -static void render_item_menu(Canvas* canvas, ArchiveMainViewModel* model) { - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 71, 17, 57, 46); - canvas_set_color(canvas, ColorBlack); - elements_slightly_rounded_frame(canvas, 70, 16, 58, 48); - - string_t menu[MENU_ITEMS]; - - string_init_set_str(menu[0], "Run in app"); - string_init_set_str(menu[1], "Pin"); - string_init_set_str(menu[2], "Rename"); - string_init_set_str(menu[3], "Delete"); - - ArchiveFile_t* selected = files_array_get(model->files, model->idx); - - if(!is_known_app(selected->type)) { - string_set_str(menu[0], "---"); - string_set_str(menu[1], "---"); - string_set_str(menu[2], "---"); - } else if(selected->fav) { - string_set_str(menu[1], "Unpin"); - } - - for(size_t i = 0; i < MENU_ITEMS; i++) { - canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i])); - string_clear(menu[i]); - } - - canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7); -} - -static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); - - canvas_set_color(canvas, ColorWhite); - canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1); - - canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11); - canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11); -} - -static void draw_list(Canvas* canvas, ArchiveMainViewModel* model) { - furi_assert(model); - - size_t array_size = files_array_size(model->files); - bool scrollbar = array_size > 4; - - for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) { - string_t str_buff; - char cstr_buff[MAX_NAME_LEN]; - - size_t idx = CLAMP(i + model->list_offset, array_size, 0); - ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0)); - - string_init_set(str_buff, file->name); - string_right(str_buff, string_search_rchar(str_buff, '/') + 1); - strlcpy(cstr_buff, string_get_cstr(str_buff), string_size(str_buff) + 1); - - if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff); - - string_clean(str_buff); - string_set_str(str_buff, cstr_buff); - - elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); - - if(model->idx == idx) { - archive_draw_frame(canvas, i, scrollbar); - } else { - canvas_set_color(canvas, ColorBlack); - } - - canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]); - canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); - string_clear(str_buff); - } - - if(scrollbar) { - elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size); - } - - if(model->action == BrowserActionItemMenu) { - render_item_menu(canvas, model); - } -} - -static void archive_render_status_bar(Canvas* canvas, ArchiveMainViewModel* model) { - furi_assert(model); - - const char* tab_name = ArchiveTabNames[model->tab_idx]; - - canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); - - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 0, 0, 50, 13); - canvas_draw_box(canvas, 107, 0, 20, 13); - - canvas_set_color(canvas, ColorBlack); - canvas_draw_frame(canvas, 1, 0, 50, 12); - canvas_draw_line(canvas, 0, 1, 0, 11); - canvas_draw_line(canvas, 1, 12, 49, 12); - canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name); - - canvas_draw_frame(canvas, 108, 0, 20, 12); - canvas_draw_line(canvas, 107, 1, 107, 11); - canvas_draw_line(canvas, 108, 12, 126, 12); - - if(model->tab_idx > 0) { - canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7); - } - if(model->tab_idx < SIZEOF_ARRAY(ArchiveTabNames) - 1) { - canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7); - } - - canvas_set_color(canvas, ColorWhite); - canvas_draw_dot(canvas, 50, 0); - canvas_draw_dot(canvas, 127, 0); - - canvas_set_color(canvas, ColorBlack); -} - -void archive_view_render(Canvas* canvas, void* model) { - ArchiveMainViewModel* m = model; - - archive_render_status_bar(canvas, model); - - if(files_array_size(m->files) > 0) { - draw_list(canvas, m); - } else { - canvas_draw_str_aligned( - canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty"); - } -} - -View* archive_main_get_view(ArchiveMainView* main_view) { - furi_assert(main_view); - return main_view->view; -} - -static void archive_show_file_menu(ArchiveMainView* main_view) { - furi_assert(main_view); - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - ArchiveFile_t* selected; - selected = files_array_get(model->files, model->idx); - model->action = BrowserActionItemMenu; - model->menu_idx = 0; - selected->fav = is_known_app(selected->type) ? archive_is_favorite( - string_get_cstr(main_view->path), - string_get_cstr(selected->name)) : - false; - - return true; - }); -} - -static void archive_close_file_menu(ArchiveMainView* main_view) { - furi_assert(main_view); - - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - model->action = BrowserActionBrowse; - model->menu_idx = 0; - return true; - }); -} - -static void archive_run_in_app( - ArchiveMainView* main_view, - ArchiveFile_t* selected, - bool full_path_provided) { - Loader* loader = furi_record_open("loader"); - - string_t full_path; - - if(!full_path_provided) { - string_init_printf( - full_path, "%s/%s", string_get_cstr(main_view->path), string_get_cstr(selected->name)); - } else { - string_init_set(full_path, selected->name); - } - loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path)); - - string_clear(full_path); - furi_record_close("loader"); -} - -static void archive_file_menu_callback(ArchiveMainView* main_view) { - furi_assert(main_view); - - ArchiveFile_t* selected = archive_get_current_file(main_view); - const char* path = archive_get_path(main_view); - const char* name = archive_get_name(main_view); - - uint8_t idx; - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - idx = model->menu_idx; - return true; - }); - - switch(idx) { - case 0: - if(is_known_app(selected->type)) { - archive_run_in_app(main_view, selected, false); - } - break; - case 1: - if(is_known_app(selected->type)) { - if(!archive_is_favorite(path, name)) { - archive_set_name(main_view, string_get_cstr(selected->name)); - archive_add_to_favorites(path, name); - } else { - // delete from favorites - archive_favorites_delete(path, name); - } - archive_close_file_menu(main_view); - } - break; - case 2: - // open rename view - if(is_known_app(selected->type)) { - main_view->callback(ArchiveBrowserEventRename, main_view->context); - } - break; - case 3: - // confirmation? - archive_delete_file(main_view, main_view->path, selected->name); - archive_close_file_menu(main_view); - break; - - default: - archive_close_file_menu(main_view); - break; - } - selected = NULL; -} - -static void archive_switch_dir(ArchiveMainView* main_view, const char* path) { - furi_assert(main_view); - furi_assert(path); - - string_set(main_view->path, path); - archive_get_filenames(main_view, archive_get_tab(main_view), string_get_cstr(main_view->path)); - update_offset(main_view); -} - -void archive_switch_tab(ArchiveMainView* main_view) { - furi_assert(main_view); - - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - model->idx = 0; - model->depth = 0; - return true; - }); - - archive_switch_dir(main_view, tab_default_paths[archive_get_tab(main_view)]); -} - -static void archive_enter_dir(ArchiveMainView* main_view, string_t name) { - furi_assert(main_view); - furi_assert(name); - - // update last index - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - model->last_idx[model->depth] = - CLAMP(model->idx, files_array_size(model->files) - 1, 0); - model->idx = 0; - model->depth = CLAMP(model->depth + 1, MAX_DEPTH, 0); - return true; - }); - - string_cat(main_view->path, "/"); - string_cat(main_view->path, main_view->name); - - archive_switch_dir(main_view, string_get_cstr(main_view->path)); -} - -static void archive_leave_dir(ArchiveMainView* main_view) { - furi_assert(main_view); - - char* last_char_ptr = strrchr(string_get_cstr(main_view->path), '/'); - - if(last_char_ptr) { - size_t pos = last_char_ptr - string_get_cstr(main_view->path); - string_left(main_view->path, pos); - } - - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - model->depth = CLAMP(model->depth - 1, MAX_DEPTH, 0); - model->idx = model->last_idx[model->depth]; - return true; - }); - - archive_switch_dir(main_view, string_get_cstr(main_view->path)); -} - -bool archive_view_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - - ArchiveMainView* main_view = context; - - BrowserActionEnum action; - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - action = model->action; - return true; - }); - - switch(action) { - case BrowserActionItemMenu: - - if(event->type == InputTypeShort) { - if(event->key == InputKeyUp || event->key == InputKeyDown) { - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - if(event->key == InputKeyUp) { - model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS; - } else if(event->key == InputKeyDown) { - model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS; - } - return true; - }); - } - - if(event->key == InputKeyOk) { - archive_file_menu_callback(main_view); - } else if(event->key == InputKeyBack) { - archive_close_file_menu(main_view); - } - } - break; - - case BrowserActionBrowse: - - if(event->type == InputTypeShort) { - if(event->key == InputKeyLeft) { - ArchiveTabEnum tab = archive_get_tab(main_view); - if(tab) { - archive_set_tab(main_view, CLAMP(tab - 1, ArchiveTabTotal, 0)); - archive_switch_tab(main_view); - return true; - } - } else if(event->key == InputKeyRight) { - ArchiveTabEnum tab = archive_get_tab(main_view); - - if(tab < ArchiveTabTotal - 1) { - archive_set_tab(main_view, CLAMP(tab + 1, ArchiveTabTotal - 1, 0)); - archive_switch_tab(main_view); - return true; - } - - } else if(event->key == InputKeyBack) { - if(!archive_get_depth(main_view)) { - main_view->callback(ArchiveBrowserEventExit, main_view->context); - } else { - archive_leave_dir(main_view); - } - - return true; - } - } - if(event->key == InputKeyUp || event->key == InputKeyDown) { - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - uint16_t num_elements = (uint16_t)files_array_size(model->files); - if((event->type == InputTypeShort || event->type == InputTypeRepeat)) { - if(event->key == InputKeyUp) { - model->idx = ((model->idx - 1) + num_elements) % num_elements; - } else if(event->key == InputKeyDown) { - model->idx = (model->idx + 1) % num_elements; - } - } - - return true; - }); - update_offset(main_view); - } - - if(event->key == InputKeyOk) { - ArchiveFile_t* selected = archive_get_current_file(main_view); - - if(selected) { - archive_set_name(main_view, string_get_cstr(selected->name)); - if(selected->type == ArchiveFileTypeFolder) { - if(event->type == InputTypeShort) { - archive_enter_dir(main_view, main_view->name); - } else if(event->type == InputTypeLong) { - archive_show_file_menu(main_view); - } - } else { - if(event->type == InputTypeShort) { - if(archive_get_tab(main_view) == ArchiveTabFavorites) { - if(is_known_app(selected->type)) { - archive_run_in_app(main_view, selected, true); - } - } else { - archive_show_file_menu(main_view); - } - } - } - } - } - break; - default: - break; - } - - return true; -} - -ArchiveMainView* main_view_alloc() { - ArchiveMainView* main_view = furi_alloc(sizeof(ArchiveMainView)); - main_view->view = view_alloc(); - view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(ArchiveMainViewModel)); - view_set_context(main_view->view, main_view); - view_set_draw_callback(main_view->view, (ViewDrawCallback)archive_view_render); - view_set_input_callback(main_view->view, archive_view_input); - - string_init(main_view->name); - string_init(main_view->path); - - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - files_array_init(model->files); - return true; - }); - - return main_view; -} - -void main_view_free(ArchiveMainView* main_view) { - furi_assert(main_view); - - with_view_model( - main_view->view, (ArchiveMainViewModel * model) { - files_array_clear(model->files); - return false; - }); - - string_clear(main_view->name); - string_clear(main_view->path); - - view_free(main_view->view); - free(main_view); -} diff --git a/applications/archive/views/archive_main_view.h b/applications/archive/views/archive_main_view.h deleted file mode 100644 index b3d07879..00000000 --- a/applications/archive/views/archive_main_view.h +++ /dev/null @@ -1,117 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include "../helpers/archive_files.h" -#include "../helpers/archive_favorites.h" - -#define MAX_LEN_PX 110 -#define MAX_NAME_LEN 255 -#define FRAME_HEIGHT 12 -#define MENU_ITEMS 4 -#define MAX_DEPTH 32 - -typedef enum { - ArchiveTabFavorites, - ArchiveTabLFRFID, - ArchiveTabSubGhz, - ArchiveTabNFC, - ArchiveTabIButton, - ArchiveTabIrda, - ArchiveTabBrowser, - ArchiveTabTotal, -} ArchiveTabEnum; - -static const char* known_ext[] = { - [ArchiveFileTypeIButton] = ".ibtn", - [ArchiveFileTypeNFC] = ".nfc", - [ArchiveFileTypeSubGhz] = ".sub", - [ArchiveFileTypeLFRFID] = ".rfid", - [ArchiveFileTypeIrda] = ".ir", -}; - -static inline const char* get_tab_ext(ArchiveTabEnum tab) { - switch(tab) { - case ArchiveTabIButton: - return known_ext[ArchiveFileTypeIButton]; - case ArchiveTabNFC: - return known_ext[ArchiveFileTypeNFC]; - case ArchiveTabSubGhz: - return known_ext[ArchiveFileTypeSubGhz]; - case ArchiveTabLFRFID: - return known_ext[ArchiveFileTypeLFRFID]; - case ArchiveTabIrda: - return known_ext[ArchiveFileTypeIrda]; - default: - return "*"; - } -} - -typedef enum { - ArchiveBrowserEventRename, - ArchiveBrowserEventExit, - ArchiveBrowserEventLeaveDir, -} ArchiveBrowserEvent; - -typedef struct ArchiveMainView ArchiveMainView; - -typedef void (*ArchiveMainViewCallback)(ArchiveBrowserEvent event, void* context); - -typedef enum { - BrowserActionBrowse, - BrowserActionItemMenu, - BrowserActionTotal, -} BrowserActionEnum; - -struct ArchiveMainView { - View* view; - ArchiveMainViewCallback callback; - void* context; - - string_t name; - string_t path; -}; - -typedef struct { - ArchiveTabEnum tab_idx; - BrowserActionEnum action; - files_array_t files; - - uint8_t depth; - uint8_t menu_idx; - - uint16_t idx; - uint16_t last_idx[MAX_DEPTH]; - uint16_t list_offset; - -} ArchiveMainViewModel; - -void archive_browser_set_callback( - ArchiveMainView* main_view, - ArchiveMainViewCallback callback, - void* context); - -View* archive_main_get_view(ArchiveMainView* main_view); - -ArchiveMainView* main_view_alloc(); -void main_view_free(ArchiveMainView* main_view); - -void archive_file_array_remove_selected(ArchiveMainView* main_view); -void archive_file_array_clean(ArchiveMainView* main_view); - -void archive_view_add_item(ArchiveMainView* main_view, FileInfo* file_info, const char* name); -void archive_browser_update(ArchiveMainView* main_view); - -size_t archive_file_array_size(ArchiveMainView* main_view); -ArchiveFile_t* archive_get_current_file(ArchiveMainView* main_view); -const char* archive_get_path(ArchiveMainView* main_view); -const char* archive_get_name(ArchiveMainView* main_view); -void archive_set_name(ArchiveMainView* main_view, const char* name); - -static inline bool is_known_app(ArchiveFileTypeEnum type) { - return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown); -} diff --git a/applications/bt/bt_cli.c b/applications/bt/bt_cli.c index 47a05d06..3dcc9a8e 100644 --- a/applications/bt/bt_cli.c +++ b/applications/bt/bt_cli.c @@ -1,6 +1,7 @@ #include "bt_cli.h" #include #include +#include "bt_settings.h" void bt_cli_init() { Cli* cli = furi_record_open("cli"); @@ -18,13 +19,16 @@ void bt_cli_command_info(Cli* cli, string_t args, void* context) { string_t buffer; string_init(buffer); furi_hal_bt_dump_state(buffer); - printf(string_get_cstr(buffer)); + printf("%s", string_get_cstr(buffer)); string_clear(buffer); } void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) { uint16_t channel; uint16_t power; + BtSettings bt_settings; + bt_settings_load(&bt_settings); + int ret = sscanf(string_get_cstr(args), "%hu %hu", &channel, &power); if(ret != 2) { printf("sscanf returned %d, channel: %hu, power: %hu\r\n", ret, channel, power); @@ -39,6 +43,8 @@ void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) { printf("Power must be in 0...6 dB range, not %hu\r\n", power); return; } + + furi_hal_bt_stop_advertising(); printf("Transmitting carrier at %hu channel at %hu dB power\r\n", channel, power); printf("Press CTRL+C to stop\r\n"); furi_hal_bt_start_tone_tx(channel, 0x19 + power); @@ -47,10 +53,15 @@ void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) { osDelay(250); } furi_hal_bt_stop_tone_tx(); + if(bt_settings.enabled) { + furi_hal_bt_start_advertising(); + } } void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) { uint16_t channel; + BtSettings bt_settings; + bt_settings_load(&bt_settings); int ret = sscanf(string_get_cstr(args), "%hu", &channel); if(ret != 1) { printf("sscanf returned %d, channel: %hu\r\n", ret, channel); @@ -61,6 +72,8 @@ void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) { printf("Channel number must be in 0...39 range, not %hu\r\n", channel); return; } + + furi_hal_bt_stop_advertising(); printf("Receiving carrier at %hu channel\r\n", channel); printf("Press CTRL+C to stop\r\n"); @@ -73,12 +86,17 @@ void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) { } furi_hal_bt_stop_packet_test(); + if(bt_settings.enabled) { + furi_hal_bt_start_advertising(); + } } void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) { uint16_t channel; uint16_t pattern; uint16_t datarate; + BtSettings bt_settings; + bt_settings_load(&bt_settings); int ret = sscanf(string_get_cstr(args), "%hu %hu %hu", &channel, &pattern, &datarate); if(ret != 3) { printf("sscanf returned %d, channel: %hu %hu %hu\r\n", ret, channel, pattern, datarate); @@ -105,6 +123,7 @@ void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) { return; } + furi_hal_bt_stop_advertising(); printf( "Transmitting %hu pattern packet at %hu channel at %hu M datarate\r\n", pattern, @@ -118,11 +137,16 @@ void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) { } furi_hal_bt_stop_packet_test(); printf("Transmitted %lu packets", furi_hal_bt_get_transmitted_packets()); + if(bt_settings.enabled) { + furi_hal_bt_start_advertising(); + } } void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) { uint16_t channel; uint16_t datarate; + BtSettings bt_settings; + bt_settings_load(&bt_settings); int ret = sscanf(string_get_cstr(args), "%hu %hu", &channel, &datarate); if(ret != 2) { printf("sscanf returned %d, channel: %hu datarate: %hu\r\n", ret, channel, datarate); @@ -137,6 +161,8 @@ void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) { printf("Datarate must be in 1 or 2 Mb, not %hu\r\n", datarate); return; } + + furi_hal_bt_stop_advertising(); printf("Receiving packets at %hu channel at %hu M datarate\r\n", channel, datarate); printf("Press CTRL+C to stop\r\n"); furi_hal_bt_start_packet_rx(channel, datarate); @@ -150,4 +176,7 @@ void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) { } uint16_t packets_received = furi_hal_bt_stop_packet_test(); printf("Received %hu packets", packets_received); + if(bt_settings.enabled) { + furi_hal_bt_start_advertising(); + } } diff --git a/applications/bt/bt_service/bt.c b/applications/bt/bt_service/bt.c index d50cdf0e..91cfad81 100755 --- a/applications/bt/bt_service/bt.c +++ b/applications/bt/bt_service/bt.c @@ -26,6 +26,17 @@ static void bt_pin_code_show_event_handler(Bt* bt, uint32_t pin) { string_clear(pin_str); } +static void bt_battery_level_changed_callback(const void* _event, void* context) { + furi_assert(_event); + furi_assert(context); + + Bt* bt = context; + const PowerEvent* event = _event; + if(event->type == PowerEventTypeBatteryLevelChanged) { + bt_update_battery_level(bt, event->data.battery_level); + } +} + Bt* bt_alloc() { Bt* bt = furi_alloc(sizeof(Bt)); // Load settings @@ -45,6 +56,11 @@ Bt* bt_alloc() { bt->dialogs = furi_record_open("dialogs"); bt->dialog_message = dialog_message_alloc(); + // Power + bt->power = furi_record_open("power"); + PubSub* power_pubsub = power_get_pubsub(bt->power); + subscribe_pubsub(power_pubsub, bt_battery_level_changed_callback, bt); + return bt; } diff --git a/applications/bt/bt_service/bt_i.h b/applications/bt/bt_service/bt_i.h index fa78811a..624c6c64 100644 --- a/applications/bt/bt_service/bt_i.h +++ b/applications/bt/bt_service/bt_i.h @@ -9,7 +9,8 @@ #include #include -#include +#include +#include #include "../bt_settings.h" @@ -36,4 +37,5 @@ struct Bt { ViewPort* statusbar_view_port; DialogsApp* dialogs; DialogMessage* dialog_message; + Power* power; }; diff --git a/applications/cli/cli.c b/applications/cli/cli.c index 9a3ae259..d9fde866 100644 --- a/applications/cli/cli.c +++ b/applications/cli/cli.c @@ -276,7 +276,7 @@ static void cli_handle_escape(Cli* cli, char c) { string_set(cli->line, cli->last_line); cli->cursor_position = string_size(cli->line); // Show new line to user - printf(string_get_cstr(cli->line)); + printf("%s", string_get_cstr(cli->line)); } } else if(c == 'B') { } else if(c == 'C') { diff --git a/applications/cli/cli_commands.c b/applications/cli/cli_commands.c index f03c12e7..e43f81a0 100644 --- a/applications/cli/cli_commands.c +++ b/applications/cli/cli_commands.c @@ -138,7 +138,7 @@ void cli_command_help(Cli* cli, string_t args, void* context) { } // Right Column if(!CliCommandTree_end_p(it_right)) { - printf(string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr)); + printf("%s", string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr)); CliCommandTree_next(it_right); } }; @@ -146,7 +146,7 @@ void cli_command_help(Cli* cli, string_t args, void* context) { if(string_size(args) > 0) { cli_nl(); printf("Also I have no clue what '"); - printf(string_get_cstr(args)); + printf("%s", string_get_cstr(args)); printf("' is."); } } diff --git a/applications/desktop/desktop.c b/applications/desktop/desktop.c new file mode 100644 index 00000000..ef17fc87 --- /dev/null +++ b/applications/desktop/desktop.c @@ -0,0 +1,131 @@ +#include "desktop_i.h" + +static void desktop_lock_icon_callback(Canvas* canvas, void* context) { + furi_assert(canvas); + canvas_draw_icon(canvas, 0, 0, &I_Lock_8x8); +} + +bool desktop_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + Desktop* desktop = (Desktop*)context; + return scene_manager_handle_custom_event(desktop->scene_manager, event); +} + +bool desktop_back_event_callback(void* context) { + furi_assert(context); + Desktop* desktop = (Desktop*)context; + return scene_manager_handle_back_event(desktop->scene_manager); +} + +Desktop* desktop_alloc() { + Desktop* desktop = furi_alloc(sizeof(Desktop)); + + desktop->gui = furi_record_open("gui"); + desktop->scene_thread = furi_thread_alloc(); + desktop->view_dispatcher = view_dispatcher_alloc(); + desktop->scene_manager = scene_manager_alloc(&desktop_scene_handlers, desktop); + + view_dispatcher_enable_queue(desktop->view_dispatcher); + view_dispatcher_attach_to_gui( + desktop->view_dispatcher, desktop->gui, ViewDispatcherTypeWindow); + + view_dispatcher_set_event_callback_context(desktop->view_dispatcher, desktop); + view_dispatcher_set_custom_event_callback( + desktop->view_dispatcher, desktop_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + desktop->view_dispatcher, desktop_back_event_callback); + + desktop->main_view = desktop_main_alloc(); + desktop->lock_menu = desktop_lock_menu_alloc(); + desktop->locked_view = desktop_locked_alloc(); + desktop->debug_view = desktop_debug_alloc(); + desktop->first_start_view = desktop_first_start_alloc(); + desktop->hw_mismatch_view = desktop_hw_mismatch_alloc(); + + view_dispatcher_add_view( + desktop->view_dispatcher, DesktopViewMain, desktop_main_get_view(desktop->main_view)); + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewLockMenu, + desktop_lock_menu_get_view(desktop->lock_menu)); + view_dispatcher_add_view( + desktop->view_dispatcher, DesktopViewDebug, desktop_debug_get_view(desktop->debug_view)); + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewLocked, + desktop_locked_get_view(desktop->locked_view)); + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewFirstStart, + desktop_first_start_get_view(desktop->first_start_view)); + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewHwMismatch, + desktop_hw_mismatch_get_view(desktop->hw_mismatch_view)); + + // Lock icon + desktop->lock_viewport = view_port_alloc(); + view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8)); + view_port_draw_callback_set(desktop->lock_viewport, desktop_lock_icon_callback, desktop); + view_port_enabled_set(desktop->lock_viewport, false); + gui_add_view_port(desktop->gui, desktop->lock_viewport, GuiLayerStatusBarLeft); + + return desktop; +} + +void desktop_free(Desktop* desktop) { + furi_assert(desktop); + + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewMain); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewLockMenu); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewLocked); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewDebug); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewFirstStart); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewHwMismatch); + + view_dispatcher_free(desktop->view_dispatcher); + scene_manager_free(desktop->scene_manager); + + desktop_main_free(desktop->main_view); + desktop_lock_menu_free(desktop->lock_menu); + desktop_locked_free(desktop->locked_view); + desktop_debug_free(desktop->debug_view); + desktop_first_start_free(desktop->first_start_view); + desktop_hw_mismatch_free(desktop->hw_mismatch_view); + + furi_record_close("gui"); + desktop->gui = NULL; + + furi_thread_free(desktop->scene_thread); + + furi_record_close("menu"); + + free(desktop); +} + +static bool desktop_is_first_start() { + Storage* storage = furi_record_open("storage"); + bool exists = storage_common_stat(storage, "/int/first_start", NULL) == FSE_OK; + furi_record_close("storage"); + + return exists; +} + +int32_t desktop_srv(void* p) { + Desktop* desktop = desktop_alloc(); + + scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); + + if(desktop_is_first_start()) { + scene_manager_next_scene(desktop->scene_manager, DesktopSceneFirstStart); + } + + if(!furi_hal_version_do_i_belong_here()) { + scene_manager_next_scene(desktop->scene_manager, DesktopSceneHwMismatch); + } + + view_dispatcher_run(desktop->view_dispatcher); + desktop_free(desktop); + + return 0; +} diff --git a/applications/desktop/desktop.h b/applications/desktop/desktop.h new file mode 100644 index 00000000..f5608207 --- /dev/null +++ b/applications/desktop/desktop.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct Desktop Desktop; diff --git a/applications/desktop/desktop_i.h b/applications/desktop/desktop_i.h new file mode 100644 index 00000000..8f424183 --- /dev/null +++ b/applications/desktop/desktop_i.h @@ -0,0 +1,59 @@ +#pragma once + +#include "desktop.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "views/desktop_main.h" +#include "views/desktop_first_start.h" +#include "views/desktop_hw_mismatch.h" +#include "views/desktop_lock_menu.h" +#include "views/desktop_locked.h" +#include "views/desktop_debug.h" + +#include "scenes/desktop_scene.h" + +#include "desktop/desktop_settings/desktop_settings.h" + +#define HINT_TIMEOUT_L 2 +#define HINT_TIMEOUT_H 11 + +typedef enum { + DesktopViewMain, + DesktopViewLockMenu, + DesktopViewLocked, + DesktopViewDebug, + DesktopViewFirstStart, + DesktopViewHwMismatch, + DesktopViewTotal, +} DesktopViewEnum; + +struct Desktop { + // Scene + FuriThread* scene_thread; + // GUI + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + + DesktopFirstStartView* first_start_view; + DesktopHwMismatchView* hw_mismatch_view; + DesktopMainView* main_view; + DesktopLockMenuView* lock_menu; + DesktopLockedView* locked_view; + DesktopDebugView* debug_view; + DesktopSettings settings; + + ViewPort* lock_viewport; +}; + +Desktop* desktop_alloc(); + +void desktop_free(Desktop* desktop); diff --git a/applications/desktop/desktop_settings/desktop_settings.c b/applications/desktop/desktop_settings/desktop_settings.c new file mode 100644 index 00000000..5e170a09 --- /dev/null +++ b/applications/desktop/desktop_settings/desktop_settings.c @@ -0,0 +1,49 @@ +#include +#include +#include "desktop_settings.h" + +#define DESKTOP_SETTINGS_TAG "Desktop settings" +#define DESKTOP_SETTINGS_PATH "/int/desktop.settings" + +bool desktop_settings_load(DesktopSettings* desktop_settings) { + furi_assert(desktop_settings); + bool file_loaded = false; + DesktopSettings settings = {}; + + FURI_LOG_I(DESKTOP_SETTINGS_TAG, "Loading settings from \"%s\"", DESKTOP_SETTINGS_PATH); + FileWorker* file_worker = file_worker_alloc(true); + if(file_worker_open(file_worker, DESKTOP_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + if(file_worker_read(file_worker, &settings, sizeof(settings))) { + file_loaded = true; + } + } + file_worker_free(file_worker); + + if(file_loaded) { + if(settings.version != DESKTOP_SETTINGS_VER) { + FURI_LOG_E(DESKTOP_SETTINGS_TAG, "Settings version mismatch"); + } else { + osKernelLock(); + *desktop_settings = settings; + osKernelUnlock(); + } + } else { + FURI_LOG_E(DESKTOP_SETTINGS_TAG, "Settings load failed"); + } + return file_loaded; +} + +bool desktop_settings_save(DesktopSettings* desktop_settings) { + furi_assert(desktop_settings); + bool result = false; + + FileWorker* file_worker = file_worker_alloc(true); + if(file_worker_open(file_worker, DESKTOP_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { + if(file_worker_write(file_worker, desktop_settings, sizeof(DesktopSettings))) { + FURI_LOG_I(DESKTOP_SETTINGS_TAG, "Settings saved to \"%s\"", DESKTOP_SETTINGS_PATH); + result = true; + } + } + file_worker_free(file_worker); + return result; +} diff --git a/applications/desktop/desktop_settings/desktop_settings.h b/applications/desktop/desktop_settings/desktop_settings.h new file mode 100644 index 00000000..a2aead82 --- /dev/null +++ b/applications/desktop/desktop_settings/desktop_settings.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#define DESKTOP_SETTINGS_VER (0) + +typedef struct { + uint8_t version; + uint16_t favorite; +} DesktopSettings; + +bool desktop_settings_load(DesktopSettings* desktop_settings); + +bool desktop_settings_save(DesktopSettings* desktop_settings); diff --git a/applications/desktop/desktop_settings/desktop_settings_app.c b/applications/desktop/desktop_settings/desktop_settings_app.c new file mode 100644 index 00000000..3c7610f5 --- /dev/null +++ b/applications/desktop/desktop_settings/desktop_settings_app.c @@ -0,0 +1,65 @@ +#include "desktop_settings_app.h" + +static bool desktop_settings_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + DesktopSettingsApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool desktop_settings_back_event_callback(void* context) { + furi_assert(context); + DesktopSettingsApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +DesktopSettingsApp* desktop_settings_app_alloc() { + DesktopSettingsApp* app = furi_alloc(sizeof(DesktopSettingsApp)); + + app->settings.version = DESKTOP_SETTINGS_VER; + desktop_settings_load(&app->settings); + + app->gui = furi_record_open("gui"); + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, desktop_settings_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, desktop_settings_back_event_callback); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, DesktopSettingsAppViewMain, submenu_get_view(app->submenu)); + + view_dispatcher_add_view( + app->view_dispatcher, DesktopSettingsAppViewFavorite, submenu_get_view(app->submenu)); + + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart); + return app; +} + +void desktop_settings_app_free(DesktopSettingsApp* app) { + furi_assert(app); + // Variable item list + view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMain); + view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewFavorite); + submenu_free(app->submenu); + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + // Records + furi_record_close("gui"); + free(app); +} + +extern int32_t desktop_settings_app(void* p) { + DesktopSettingsApp* app = desktop_settings_app_alloc(); + view_dispatcher_run(app->view_dispatcher); + desktop_settings_save(&app->settings); + desktop_settings_app_free(app); + return 0; +} diff --git a/applications/desktop/desktop_settings/desktop_settings_app.h b/applications/desktop/desktop_settings/desktop_settings_app.h new file mode 100644 index 00000000..ba381cde --- /dev/null +++ b/applications/desktop/desktop_settings/desktop_settings_app.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "desktop_settings.h" +#include "scenes/desktop_settings_scene.h" + +typedef enum { + DesktopSettingsAppViewMain, + DesktopSettingsAppViewFavorite, +} DesktopSettingsAppView; + +typedef struct { + DesktopSettings settings; + Gui* gui; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + +} DesktopSettingsApp; diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene.c new file mode 100644 index 00000000..03e73e67 --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene.c @@ -0,0 +1,30 @@ +#include "desktop_settings_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const desktop_settings_on_enter_handlers[])(void*) = { +#include "desktop_settings_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 desktop_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "desktop_settings_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 desktop_settings_on_exit_handlers[])(void* context) = { +#include "desktop_settings_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers desktop_settings_scene_handlers = { + .on_enter_handlers = desktop_settings_on_enter_handlers, + .on_event_handlers = desktop_settings_on_event_handlers, + .on_exit_handlers = desktop_settings_on_exit_handlers, + .scene_num = DesktopSettingsAppSceneNum, +}; diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene.h b/applications/desktop/desktop_settings/scenes/desktop_settings_scene.h new file mode 100644 index 00000000..18fbb2c6 --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) DesktopSettingsAppScene##id, +typedef enum { +#include "desktop_settings_scene_config.h" + DesktopSettingsAppSceneNum, +} DesktopSettingsAppScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers desktop_settings_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "desktop_settings_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 "desktop_settings_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 "desktop_settings_scene_config.h" +#undef ADD_SCENE diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h new file mode 100644 index 00000000..a2abec0a --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h @@ -0,0 +1,2 @@ +ADD_SCENE(desktop_settings, start, Start) +ADD_SCENE(desktop_settings, favorite, Favorite) diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_favorite.c new file mode 100644 index 00000000..e8559017 --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -0,0 +1,47 @@ +#include "../desktop_settings_app.h" +#include "applications.h" + +static void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void desktop_settings_scene_favorite_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + submenu_clean(submenu); + + for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { + submenu_add_item( + submenu, + FLIPPER_APPS[i].name, + i, + desktop_settings_scene_favorite_submenu_callback, + app); + } + + submenu_set_header(app->submenu, "Quick access app:"); + submenu_set_selected_item(app->submenu, app->settings.favorite); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewFavorite); +} + +bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + default: + app->settings.favorite = event.event; + scene_manager_previous_scene(app->scene_manager); + consumed = true; + break; + } + } + return consumed; +} + +void desktop_settings_scene_favorite_on_exit(void* context) { + DesktopSettingsApp* app = context; + submenu_clean(app->submenu); +} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c new file mode 100755 index 00000000..33d66d0c --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c @@ -0,0 +1,53 @@ +#include "../desktop_settings_app.h" +#include "applications.h" + +enum DesktopSettingsStartSubmenuIndex { + DesktopSettingsStartSubmenuIndexFavorite, + DesktopSettingsStartSubmenuIndexPinSetup, +}; + +static void desktop_settings_scene_start_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void desktop_settings_scene_start_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Favorite App", + DesktopSettingsStartSubmenuIndexFavorite, + desktop_settings_scene_start_submenu_callback, + app); + + submenu_add_item( + submenu, + "PIN Setup", + DesktopSettingsStartSubmenuIndexPinSetup, + desktop_settings_scene_start_submenu_callback, + app); + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMain); +} + +bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopSettingsStartSubmenuIndexFavorite: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppViewFavorite); + consumed = true; + break; + } + } + return consumed; +} + +void desktop_settings_scene_start_on_exit(void* context) { + DesktopSettingsApp* app = context; + submenu_clean(app->submenu); +} diff --git a/applications/desktop/scenes/desktop_scene.c b/applications/desktop/scenes/desktop_scene.c new file mode 100644 index 00000000..8c9b5838 --- /dev/null +++ b/applications/desktop/scenes/desktop_scene.c @@ -0,0 +1,30 @@ +#include "desktop_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const desktop_on_enter_handlers[])(void*) = { +#include "desktop_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 desktop_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "desktop_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 desktop_on_exit_handlers[])(void* context) = { +#include "desktop_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers desktop_scene_handlers = { + .on_enter_handlers = desktop_on_enter_handlers, + .on_event_handlers = desktop_on_event_handlers, + .on_exit_handlers = desktop_on_exit_handlers, + .scene_num = DesktopSceneNum, +}; diff --git a/applications/desktop/scenes/desktop_scene.h b/applications/desktop/scenes/desktop_scene.h new file mode 100644 index 00000000..34b36eaf --- /dev/null +++ b/applications/desktop/scenes/desktop_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) DesktopScene##id, +typedef enum { +#include "desktop_scene_config.h" + DesktopSceneNum, +} DesktopScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers desktop_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "desktop_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 "desktop_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 "desktop_scene_config.h" +#undef ADD_SCENE diff --git a/applications/desktop/scenes/desktop_scene_config.h b/applications/desktop/scenes/desktop_scene_config.h new file mode 100644 index 00000000..067de7c4 --- /dev/null +++ b/applications/desktop/scenes/desktop_scene_config.h @@ -0,0 +1,6 @@ +ADD_SCENE(desktop, main, Main) +ADD_SCENE(desktop, lock_menu, LockMenu) +ADD_SCENE(desktop, locked, Locked) +ADD_SCENE(desktop, debug, Debug) +ADD_SCENE(desktop, first_start, FirstStart) +ADD_SCENE(desktop, hw_mismatch, HwMismatch) diff --git a/applications/desktop/scenes/desktop_scene_debug.c b/applications/desktop/scenes/desktop_scene_debug.c new file mode 100644 index 00000000..3ae4b852 --- /dev/null +++ b/applications/desktop/scenes/desktop_scene_debug.c @@ -0,0 +1,62 @@ +#include "../desktop_i.h" +#include "../views/desktop_debug.h" +#include +#include + +void desktop_scene_debug_callback(DesktopDebugEvent event, void* context) { + Desktop* desktop = (Desktop*)context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, event); +} + +void desktop_scene_debug_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + + desktop_debug_get_dolphin_data(desktop->debug_view); + + desktop_debug_set_callback(desktop->debug_view, desktop_scene_debug_callback, desktop); + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewDebug); +} + +bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + Dolphin* dolphin = furi_record_open("dolphin"); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopDebugEventExit: + scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); + dolphin_save(dolphin); + consumed = true; + break; + + case DesktopDebugEventDeed: + dolphin_deed(dolphin, DolphinDeedIButtonEmulate); + desktop_debug_get_dolphin_data(desktop->debug_view); + consumed = true; + break; + + case DesktopDebugEventWrongDeed: + dolphin_deed(dolphin, DolphinDeedWrong); + desktop_debug_get_dolphin_data(desktop->debug_view); + consumed = true; + break; + + case DesktopDebugEventSaveState: + dolphin_save(dolphin); + consumed = true; + break; + + default: + break; + } + } + + furi_record_close("dolphin"); + return consumed; +} + +void desktop_scene_debug_on_exit(void* context) { + Desktop* desktop = (Desktop*)context; + desktop_debug_reset_screen_idx(desktop->debug_view); +} diff --git a/applications/desktop/scenes/desktop_scene_first_start.c b/applications/desktop/scenes/desktop_scene_first_start.c new file mode 100644 index 00000000..98f6ef73 --- /dev/null +++ b/applications/desktop/scenes/desktop_scene_first_start.c @@ -0,0 +1,42 @@ +#include "../desktop_i.h" +#include "../views/desktop_first_start.h" + +void desktop_scene_first_start_callback(DesktopFirstStartEvent event, void* context) { + Desktop* desktop = (Desktop*)context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, event); +} + +void desktop_scene_first_start_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + DesktopFirstStartView* first_start_view = desktop->first_start_view; + + desktop_first_start_set_callback( + first_start_view, desktop_scene_first_start_callback, desktop); + + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewFirstStart); +} + +bool desktop_scene_first_start_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + bool consumed = false; + Storage* storage = NULL; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopFirstStartCompleted: + storage = furi_record_open("storage"); + storage_common_remove(storage, "/int/first_start"); + furi_record_close("storage"); + scene_manager_previous_scene(desktop->scene_manager); + consumed = true; + break; + + default: + break; + } + } + return consumed; +} + +void desktop_scene_first_start_on_exit(void* context) { +} diff --git a/applications/desktop/scenes/desktop_scene_hw_mismatch.c b/applications/desktop/scenes/desktop_scene_hw_mismatch.c new file mode 100644 index 00000000..224ddb58 --- /dev/null +++ b/applications/desktop/scenes/desktop_scene_hw_mismatch.c @@ -0,0 +1,37 @@ +#include "../desktop_i.h" +#include "../views/desktop_hw_mismatch.h" + +void desktop_scene_hw_mismatch_callback(DesktopHwMismatchEvent event, void* context) { + Desktop* desktop = (Desktop*)context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, event); +} + +void desktop_scene_hw_mismatch_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + + desktop_hw_mismatch_set_callback( + desktop->hw_mismatch_view, desktop_scene_hw_mismatch_callback, desktop); + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewHwMismatch); +} + +bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopHwMismatchEventExit: + scene_manager_previous_scene(desktop->scene_manager); + consumed = true; + break; + + default: + break; + } + } + return consumed; +} + +void desktop_scene_hw_mismatch_on_exit(void* context) { + // Desktop* desktop = (Desktop*)context; +} diff --git a/applications/desktop/scenes/desktop_scene_lock_menu.c b/applications/desktop/scenes/desktop_scene_lock_menu.c new file mode 100644 index 00000000..537469b5 --- /dev/null +++ b/applications/desktop/scenes/desktop_scene_lock_menu.c @@ -0,0 +1,42 @@ +#include "../desktop_i.h" +#include "../views/desktop_lock_menu.h" + +void desktop_scene_lock_menu_callback(DesktopLockMenuEvent event, void* context) { + Desktop* desktop = (Desktop*)context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, event); +} + +void desktop_scene_lock_menu_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + + desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu); +} + +bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopLockMenuEventLock: + scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); + consumed = true; + break; + + case DesktopLockMenuEventExit: + scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); + consumed = true; + break; + + default: + break; + } + } + return consumed; +} + +void desktop_scene_lock_menu_on_exit(void* context) { + Desktop* desktop = (Desktop*)context; + desktop_lock_menu_reset_idx(desktop->lock_menu); +} diff --git a/applications/desktop/scenes/desktop_scene_locked.c b/applications/desktop/scenes/desktop_scene_locked.c new file mode 100644 index 00000000..c0cfd3c9 --- /dev/null +++ b/applications/desktop/scenes/desktop_scene_locked.c @@ -0,0 +1,51 @@ +#include "../desktop_i.h" +#include "../views/desktop_locked.h" + +void desktop_scene_locked_callback(DesktopLockedEvent event, void* context) { + Desktop* desktop = (Desktop*)context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, event); +} + +void desktop_scene_locked_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + DesktopLockedView* locked_view = desktop->locked_view; + + desktop_locked_set_callback(locked_view, desktop_scene_locked_callback, desktop); + desktop_locked_reset_door_pos(locked_view); + desktop_locked_update_hint_timeout(locked_view); + + view_port_enabled_set(desktop->lock_viewport, true); + osTimerStart(locked_view->timer, 63); + + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLocked); +} + +bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopLockedEventUnlock: + scene_manager_set_scene_state( + desktop->scene_manager, DesktopSceneMain, DesktopMainEventUnlocked); + scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); + consumed = true; + break; + case DesktopLockedEventUpdate: + desktop_locked_manage_redraw(desktop->locked_view); + consumed = true; + default: + break; + } + } + + return consumed; +} + +void desktop_scene_locked_on_exit(void* context) { + Desktop* desktop = (Desktop*)context; + DesktopLockedView* locked_view = desktop->locked_view; + desktop_locked_reset_counter(desktop->locked_view); + osTimerStop(locked_view->timer); +} diff --git a/applications/desktop/scenes/desktop_scene_main.c b/applications/desktop/scenes/desktop_scene_main.c new file mode 100644 index 00000000..e63f8418 --- /dev/null +++ b/applications/desktop/scenes/desktop_scene_main.c @@ -0,0 +1,88 @@ +#include "../desktop_i.h" +#include "../views/desktop_main.h" +#include "applications.h" +#include +#define MAIN_VIEW_DEFAULT (0UL) + +static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) { + furi_assert(desktop); + furi_assert(flipper_app); + furi_assert(flipper_app->app); + furi_assert(flipper_app->name); + + if(furi_thread_get_state(desktop->scene_thread) != FuriThreadStateStopped) { + FURI_LOG_E("Desktop", "Thread is already running"); + return; + } + + 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); + + furi_thread_start(desktop->scene_thread); +} + +void desktop_scene_main_callback(DesktopMainEvent event, void* context) { + Desktop* desktop = (Desktop*)context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, event); +} + +void desktop_scene_main_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + DesktopMainView* main_view = desktop->main_view; + + desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop); + view_port_enabled_set(desktop->lock_viewport, false); + + if(scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneMain) == + DesktopMainEventUnlocked) { + desktop_main_unlocked(desktop->main_view); + } + + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewMain); +} + +bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopMainEventOpenMenu: + loader_show_menu(); + consumed = true; + break; + + case DesktopMainEventOpenLockMenu: + scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu); + consumed = true; + break; + + case DesktopMainEventOpenDebug: + scene_manager_next_scene(desktop->scene_manager, DesktopViewDebug); + consumed = true; + break; + + case DesktopMainEventOpenArchive: + desktop_switch_to_app(desktop, &FLIPPER_ARCHIVE); + consumed = true; + break; + case DesktopMainEventOpenFavorite: + desktop_settings_load(&desktop->settings); + desktop_switch_to_app(desktop, &FLIPPER_APPS[desktop->settings.favorite]); + consumed = true; + break; + + default: + break; + } + } + + return consumed; +} + +void desktop_scene_main_on_exit(void* context) { + Desktop* desktop = (Desktop*)context; + scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneMain, MAIN_VIEW_DEFAULT); + desktop_main_reset_hint(desktop->main_view); +} diff --git a/applications/desktop/views/desktop_debug.c b/applications/desktop/views/desktop_debug.c new file mode 100644 index 00000000..00c70d9b --- /dev/null +++ b/applications/desktop/views/desktop_debug.c @@ -0,0 +1,166 @@ +#include +#include "../desktop_i.h" +#include "desktop_debug.h" + +#include "applications/dolphin/helpers/dolphin_state.h" +#include "applications/dolphin/dolphin.h" + +void desktop_debug_set_callback( + DesktopDebugView* debug_view, + DesktopDebugViewCallback callback, + void* context) { + furi_assert(debug_view); + furi_assert(callback); + debug_view->callback = callback; + debug_view->context = context; +} + +void desktop_debug_render(Canvas* canvas, void* model) { + canvas_clear(canvas); + DesktopDebugViewModel* m = model; + const Version* ver; + char buffer[64]; + + static const char* headers[] = {"FW Version info:", "Boot Version info:", "Desktop info:"}; + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 13, headers[m->screen]); + canvas_set_font(canvas, FontSecondary); + + if(m->screen != DesktopViewStatsMeta) { + // Hardware version + const char* my_name = furi_hal_version_get_name_ptr(); + snprintf( + buffer, + sizeof(buffer), + "HW: %d.F%dB%dC%d %s", + furi_hal_version_get_hw_version(), + furi_hal_version_get_hw_target(), + furi_hal_version_get_hw_body(), + furi_hal_version_get_hw_connect(), + my_name ? my_name : "Unknown"); + canvas_draw_str(canvas, 5, 23, buffer); + + ver = m->screen == DesktopViewStatsBoot ? furi_hal_version_get_boot_version() : + furi_hal_version_get_firmware_version(); + + if(!ver) { + canvas_draw_str(canvas, 5, 33, "No info"); + return; + } + + snprintf( + buffer, + sizeof(buffer), + "%s [%s]", + version_get_version(ver), + version_get_builddate(ver)); + canvas_draw_str(canvas, 5, 33, buffer); + + snprintf( + buffer, + sizeof(buffer), + "%s [%s]", + version_get_githash(ver), + version_get_gitbranchnum(ver)); + canvas_draw_str(canvas, 5, 43, buffer); + + snprintf( + buffer, sizeof(buffer), "[%s] %s", version_get_target(ver), version_get_gitbranch(ver)); + canvas_draw_str(canvas, 5, 53, buffer); + + } else { + char buffer[64]; + + canvas_set_font(canvas, FontSecondary); + snprintf(buffer, 64, "Icounter: %ld", m->icounter); + canvas_draw_str(canvas, 5, 30, buffer); + snprintf(buffer, 64, "Butthurt: %ld", m->butthurt); + canvas_draw_str(canvas, 5, 40, buffer); + canvas_draw_str(canvas, 0, 53, "[< >] icounter value [ok] save"); + } +} + +View* desktop_debug_get_view(DesktopDebugView* debug_view) { + furi_assert(debug_view); + return debug_view->view; +} + +bool desktop_debug_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + DesktopDebugView* debug_view = context; + + if(event->type != InputTypeShort) return false; + DesktopViewStatsScreens current = 0; + with_view_model( + debug_view->view, (DesktopDebugViewModel * model) { + if(event->key == InputKeyDown) { + model->screen = (model->screen + 1) % DesktopViewStatsTotalCount; + } else if(event->key == InputKeyUp) { + model->screen = ((model->screen - 1) + DesktopViewStatsTotalCount) % + DesktopViewStatsTotalCount; + } + current = model->screen; + return true; + }); + + if(current == DesktopViewStatsMeta) { + if(event->key == InputKeyLeft) { + debug_view->callback(DesktopDebugEventWrongDeed, debug_view->context); + } else if(event->key == InputKeyRight) { + debug_view->callback(DesktopDebugEventDeed, debug_view->context); + } else if(event->key == InputKeyOk) { + debug_view->callback(DesktopDebugEventSaveState, debug_view->context); + } else { + return false; + } + } + + if(event->key == InputKeyBack) { + debug_view->callback(DesktopDebugEventExit, debug_view->context); + } + + return true; +} + +DesktopDebugView* desktop_debug_alloc() { + DesktopDebugView* debug_view = furi_alloc(sizeof(DesktopDebugView)); + debug_view->view = view_alloc(); + view_allocate_model(debug_view->view, ViewModelTypeLocking, sizeof(DesktopDebugViewModel)); + view_set_context(debug_view->view, debug_view); + view_set_draw_callback(debug_view->view, (ViewDrawCallback)desktop_debug_render); + view_set_input_callback(debug_view->view, desktop_debug_input); + + return debug_view; +} + +void desktop_debug_free(DesktopDebugView* debug_view) { + furi_assert(debug_view); + + view_free(debug_view->view); + free(debug_view); +} + +void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view) { + Dolphin* dolphin = furi_record_open("dolphin"); + DolphinDeedWeight stats = dolphin_stats(dolphin); + with_view_model( + debug_view->view, (DesktopDebugViewModel * model) { + model->icounter = stats.icounter; + model->butthurt = stats.butthurt; + return true; + }); + + furi_record_close("dolphin"); +} + +void desktop_debug_reset_screen_idx(DesktopDebugView* debug_view) { + with_view_model( + debug_view->view, (DesktopDebugViewModel * model) { + model->screen = 0; + return true; + }); +} diff --git a/applications/desktop/views/desktop_debug.h b/applications/desktop/views/desktop_debug.h new file mode 100644 index 00000000..02ce76a2 --- /dev/null +++ b/applications/desktop/views/desktop_debug.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +typedef enum { + DesktopDebugEventDeed, + DesktopDebugEventWrongDeed, + DesktopDebugEventSaveState, + DesktopDebugEventExit, +} DesktopDebugEvent; + +typedef struct DesktopDebugView DesktopDebugView; + +typedef void (*DesktopDebugViewCallback)(DesktopDebugEvent event, void* context); + +// Debug info +typedef enum { + DesktopViewStatsFw, + DesktopViewStatsBoot, + DesktopViewStatsMeta, + DesktopViewStatsTotalCount, +} DesktopViewStatsScreens; + +struct DesktopDebugView { + View* view; + DesktopDebugViewCallback callback; + void* context; +}; + +typedef struct { + uint32_t icounter; + uint32_t butthurt; + DesktopViewStatsScreens screen; +} DesktopDebugViewModel; + +void desktop_debug_set_callback( + DesktopDebugView* debug_view, + DesktopDebugViewCallback callback, + void* context); + +View* desktop_debug_get_view(DesktopDebugView* debug_view); + +DesktopDebugView* desktop_debug_alloc(); +void desktop_debug_free(DesktopDebugView* debug_view); + +void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view); +void desktop_debug_reset_screen_idx(DesktopDebugView* debug_view); \ No newline at end of file diff --git a/applications/desktop/views/desktop_first_start.c b/applications/desktop/views/desktop_first_start.c new file mode 100644 index 00000000..4c037f0b --- /dev/null +++ b/applications/desktop/views/desktop_first_start.c @@ -0,0 +1,107 @@ +#include +#include "../desktop_i.h" +#include "desktop_first_start.h" + +void desktop_first_start_set_callback( + DesktopFirstStartView* first_start_view, + DesktopFirstStartViewCallback callback, + void* context) { + furi_assert(first_start_view); + furi_assert(callback); + first_start_view->callback = callback; + first_start_view->context = context; +} + +void desktop_first_start_render(Canvas* canvas, void* model) { + DesktopFirstStartViewModel* m = model; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + uint8_t width = canvas_width(canvas); + uint8_t height = canvas_height(canvas); + const char* my_name = furi_hal_version_get_name_ptr(); + if(m->page == 0) { + canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart0_70x53); + elements_multiline_text_framed(canvas, 75, 20, "Hey m8,\npress > to\ncontinue"); + } else if(m->page == 1) { + canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart1_59x53); + elements_multiline_text_framed(canvas, 64, 20, "First Of All,\n... >"); + } else if(m->page == 2) { + canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart2_59x51); + elements_multiline_text_framed(canvas, 64, 20, "Thank you\nfor your\nsupport! >"); + } else if(m->page == 3) { + canvas_draw_icon(canvas, width - 57, height - 48, &I_DolphinFirstStart3_57x48); + elements_multiline_text_framed(canvas, 0, 20, "Kickstarter\ncampaign\nwas INSANE! >"); + } else if(m->page == 4) { + canvas_draw_icon(canvas, width - 67, height - 50, &I_DolphinFirstStart4_67x53); + elements_multiline_text_framed(canvas, 0, 17, "Now\nallow me\nto introduce\nmyself >"); + } else if(m->page == 5) { + char buf[64]; + snprintf( + buf, + 64, + "%s %s%s", + "I am", + my_name ? my_name : "Unknown", + ",\ncyberdesktop\nliving in your\npocket >"); + canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart5_54x49); + elements_multiline_text_framed(canvas, 60, 17, buf); + } else if(m->page == 6) { + canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart6_58x54); + elements_multiline_text_framed( + canvas, 63, 17, "I can grow\nsmart'n'cool\nif you use me\noften >"); + } else if(m->page == 7) { + canvas_draw_icon(canvas, width - 61, height - 48, &I_DolphinFirstStart7_61x51); + elements_multiline_text_framed( + canvas, 0, 17, "As long as\nyou read, write\nand emulate >"); + } else if(m->page == 8) { + canvas_draw_icon(canvas, width - 56, height - 48, &I_DolphinFirstStart8_56x51); + elements_multiline_text_framed( + canvas, 0, 17, "You can check\nmy level and\nmood in the\nPassport menu"); + } +} + +View* desktop_first_start_get_view(DesktopFirstStartView* first_start_view) { + furi_assert(first_start_view); + return first_start_view->view; +} + +bool desktop_first_start_input(InputEvent* event, void* context) { + furi_assert(event); + DesktopFirstStartView* first_start_view = context; + + if(event->type == InputTypeShort) { + DesktopFirstStartViewModel* model = view_get_model(first_start_view->view); + if(event->key == InputKeyLeft) { + if(model->page > 0) model->page--; + } else if(event->key == InputKeyRight) { + uint32_t page = ++model->page; + if(page > 8) { + first_start_view->callback(DesktopFirstStartCompleted, first_start_view->context); + } + } + view_commit_model(first_start_view->view, true); + } + + return true; +} + +DesktopFirstStartView* desktop_first_start_alloc() { + DesktopFirstStartView* first_start_view = furi_alloc(sizeof(DesktopFirstStartView)); + first_start_view->view = view_alloc(); + view_allocate_model( + first_start_view->view, ViewModelTypeLocking, sizeof(DesktopFirstStartViewModel)); + view_set_context(first_start_view->view, first_start_view); + view_set_draw_callback(first_start_view->view, (ViewDrawCallback)desktop_first_start_render); + view_set_input_callback(first_start_view->view, desktop_first_start_input); + + return first_start_view; +} + +void desktop_first_start_free(DesktopFirstStartView* first_start_view) { + furi_assert(first_start_view); + + view_free(first_start_view->view); + free(first_start_view); +} diff --git a/applications/desktop/views/desktop_first_start.h b/applications/desktop/views/desktop_first_start.h new file mode 100644 index 00000000..4cc5b54a --- /dev/null +++ b/applications/desktop/views/desktop_first_start.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include +#include + +typedef enum { + DesktopFirstStartCompleted, +} DesktopFirstStartEvent; + +typedef struct DesktopFirstStartView DesktopFirstStartView; + +typedef void (*DesktopFirstStartViewCallback)(DesktopFirstStartEvent event, void* context); + +struct DesktopFirstStartView { + View* view; + DesktopFirstStartViewCallback callback; + void* context; +}; + +typedef struct { + uint8_t page; +} DesktopFirstStartViewModel; + +void desktop_first_start_set_callback( + DesktopFirstStartView* main_view, + DesktopFirstStartViewCallback callback, + void* context); + +View* desktop_first_start_get_view(DesktopFirstStartView* main_view); + +DesktopFirstStartView* desktop_first_start_alloc(); +void desktop_first_start_free(DesktopFirstStartView* main_view); diff --git a/applications/desktop/views/desktop_hw_mismatch.c b/applications/desktop/views/desktop_hw_mismatch.c new file mode 100644 index 00000000..bf58d8f9 --- /dev/null +++ b/applications/desktop/views/desktop_hw_mismatch.c @@ -0,0 +1,66 @@ +#include +#include "../desktop_i.h" +#include +#include + +#include "desktop_hw_mismatch.h" + +void desktop_hw_mismatch_set_callback( + DesktopHwMismatchView* main_view, + DesktopHwMismatchViewCallback callback, + void* context) { + furi_assert(main_view); + furi_assert(callback); + main_view->callback = callback; + main_view->context = context; +} + +void desktop_hw_mismatch_render(Canvas* canvas, void* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 15, "!!!! HW Mismatch !!!!"); + + char buffer[64]; + canvas_set_font(canvas, FontSecondary); + snprintf(buffer, 64, "HW target: F%d", furi_hal_version_get_hw_target()); + canvas_draw_str(canvas, 5, 27, buffer); + canvas_draw_str(canvas, 5, 38, "FW target: " TARGET); +} + +View* desktop_hw_mismatch_get_view(DesktopHwMismatchView* hw_mismatch_view) { + furi_assert(hw_mismatch_view); + return hw_mismatch_view->view; +} + +bool desktop_hw_mismatch_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + DesktopHwMismatchView* hw_mismatch_view = context; + + if(event->type == InputTypeShort) { + hw_mismatch_view->callback(DesktopHwMismatchEventExit, hw_mismatch_view->context); + } + + return true; +} + +DesktopHwMismatchView* desktop_hw_mismatch_alloc() { + DesktopHwMismatchView* hw_mismatch_view = furi_alloc(sizeof(DesktopHwMismatchView)); + hw_mismatch_view->view = view_alloc(); + view_allocate_model( + hw_mismatch_view->view, ViewModelTypeLocking, sizeof(DesktopHwMismatchViewModel)); + view_set_context(hw_mismatch_view->view, hw_mismatch_view); + view_set_draw_callback(hw_mismatch_view->view, (ViewDrawCallback)desktop_hw_mismatch_render); + view_set_input_callback(hw_mismatch_view->view, desktop_hw_mismatch_input); + + return hw_mismatch_view; +} + +void desktop_hw_mismatch_free(DesktopHwMismatchView* hw_mismatch_view) { + furi_assert(hw_mismatch_view); + + view_free(hw_mismatch_view->view); + free(hw_mismatch_view); +} diff --git a/applications/desktop/views/desktop_hw_mismatch.h b/applications/desktop/views/desktop_hw_mismatch.h new file mode 100644 index 00000000..68042341 --- /dev/null +++ b/applications/desktop/views/desktop_hw_mismatch.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include +#include + +typedef enum { + DesktopHwMismatchEventExit, +} DesktopHwMismatchEvent; + +typedef struct DesktopHwMismatchView DesktopHwMismatchView; + +typedef void (*DesktopHwMismatchViewCallback)(DesktopHwMismatchEvent event, void* context); + +struct DesktopHwMismatchView { + View* view; + DesktopHwMismatchViewCallback callback; + void* context; +}; + +typedef struct { + IconAnimation* animation; + uint8_t scene_num; + uint8_t hint_timeout; + bool locked; +} DesktopHwMismatchViewModel; + +void desktop_hw_mismatch_set_callback( + DesktopHwMismatchView* hw_mismatch_view, + DesktopHwMismatchViewCallback callback, + void* context); + +View* desktop_hw_mismatch_get_view(DesktopHwMismatchView* hw_mismatch_view); + +DesktopHwMismatchView* desktop_hw_mismatch_alloc(); +void desktop_hw_mismatch_free(DesktopHwMismatchView* hw_mismatch_view); diff --git a/applications/desktop/views/desktop_lock_menu.c b/applications/desktop/views/desktop_lock_menu.c new file mode 100644 index 00000000..5bb065d6 --- /dev/null +++ b/applications/desktop/views/desktop_lock_menu.c @@ -0,0 +1,111 @@ +#include +#include "../desktop_i.h" +#include "desktop_lock_menu.h" + +void desktop_lock_menu_set_callback( + DesktopLockMenuView* lock_menu, + DesktopLockMenuViewCallback callback, + void* context) { + furi_assert(lock_menu); + furi_assert(callback); + lock_menu->callback = callback; + lock_menu->context = context; +} + +void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu) { + with_view_model( + lock_menu->view, (DesktopLockMenuViewModel * model) { + model->idx = 0; + return true; + }); +} + +static void lock_menu_callback(void* context, uint8_t index) { + furi_assert(context); + DesktopLockMenuView* lock_menu = context; + switch(index) { + case 0: // lock + lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); + default: // wip message + with_view_model( + lock_menu->view, (DesktopLockMenuViewModel * model) { + model->hint_timeout = HINT_TIMEOUT_L; + return true; + }); + break; + } +} + +void desktop_lock_menu_render(Canvas* canvas, void* model) { + const char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"}; + + DesktopLockMenuViewModel* m = model; + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, -57, 0, &I_DoorLeft_70x55); + canvas_draw_icon(canvas, 115, 0, &I_DoorRight_70x55); + canvas_set_font(canvas, FontSecondary); + + for(uint8_t i = 0; i < 3; ++i) { + canvas_draw_str_aligned( + canvas, + 64, + 13 + (i * 17), + AlignCenter, + AlignCenter, + (m->hint_timeout && m->idx == i && m->idx) ? "Not implemented" : Lockmenu_Items[i]); + if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15); + } +} + +View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu) { + furi_assert(lock_menu); + return lock_menu->view; +} + +bool desktop_lock_menu_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + DesktopLockMenuView* lock_menu = context; + uint8_t idx; + + if(event->type != InputTypeShort) return false; + with_view_model( + lock_menu->view, (DesktopLockMenuViewModel * model) { + model->hint_timeout = 0; // clear hint timeout + if(event->key == InputKeyUp) { + model->idx = CLAMP(model->idx - 1, 2, 0); + } else if(event->key == InputKeyDown) { + model->idx = CLAMP(model->idx + 1, 2, 0); + } + idx = model->idx; + return true; + }); + + if(event->key == InputKeyBack) { + lock_menu->callback(DesktopLockMenuEventExit, lock_menu->context); + } else if(event->key == InputKeyOk) { + lock_menu_callback(lock_menu, idx); + } + + return true; +} + +DesktopLockMenuView* desktop_lock_menu_alloc() { + DesktopLockMenuView* lock_menu = furi_alloc(sizeof(DesktopLockMenuView)); + lock_menu->view = view_alloc(); + view_allocate_model(lock_menu->view, ViewModelTypeLocking, sizeof(DesktopLockMenuViewModel)); + view_set_context(lock_menu->view, lock_menu); + view_set_draw_callback(lock_menu->view, (ViewDrawCallback)desktop_lock_menu_render); + view_set_input_callback(lock_menu->view, desktop_lock_menu_input); + + return lock_menu; +} + +void desktop_lock_menu_free(DesktopLockMenuView* lock_menu_view) { + furi_assert(lock_menu_view); + + view_free(lock_menu_view->view); + free(lock_menu_view); +} diff --git a/applications/desktop/views/desktop_lock_menu.h b/applications/desktop/views/desktop_lock_menu.h new file mode 100644 index 00000000..d851bf91 --- /dev/null +++ b/applications/desktop/views/desktop_lock_menu.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include +#include + +typedef enum { + DesktopLockMenuEventLock, + DesktopLockMenuEventUnlock, + DesktopLockMenuEventExit, +} DesktopLockMenuEvent; + +typedef struct DesktopLockMenuView DesktopLockMenuView; + +typedef void (*DesktopLockMenuViewCallback)(DesktopLockMenuEvent event, void* context); + +struct DesktopLockMenuView { + View* view; + DesktopLockMenuViewCallback callback; + void* context; +}; + +typedef struct { + uint8_t idx; + uint8_t hint_timeout; + bool locked; +} DesktopLockMenuViewModel; + +void desktop_lock_menu_set_callback( + DesktopLockMenuView* lock_menu, + DesktopLockMenuViewCallback callback, + void* context); + +View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu); +void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu); +DesktopLockMenuView* desktop_lock_menu_alloc(); +void desktop_lock_menu_free(DesktopLockMenuView* lock_menu); diff --git a/applications/desktop/views/desktop_locked.c b/applications/desktop/views/desktop_locked.c new file mode 100644 index 00000000..80ec099c --- /dev/null +++ b/applications/desktop/views/desktop_locked.c @@ -0,0 +1,171 @@ +#include +#include "../desktop_i.h" +#include "desktop_locked.h" + +static const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64}; + +void desktop_locked_set_callback( + DesktopLockedView* locked_view, + DesktopLockedViewCallback callback, + void* context) { + furi_assert(locked_view); + furi_assert(callback); + locked_view->callback = callback; + locked_view->context = context; +} + +void locked_view_timer_callback(void* context) { + DesktopLockedView* locked_view = context; + locked_view->callback(DesktopLockedEventUpdate, locked_view->context); +} + +// temporary locked screen animation managment +static void + desktop_scene_handler_set_scene(DesktopLockedView* locked_view, const Icon* icon_data) { + with_view_model( + locked_view->view, (DesktopLockedViewModel * model) { + if(model->animation) icon_animation_free(model->animation); + model->animation = icon_animation_alloc(icon_data); + icon_animation_start(model->animation); + return true; + }); +} + +void desktop_locked_update_hint_timeout(DesktopLockedView* locked_view) { + with_view_model( + locked_view->view, (DesktopLockedViewModel * model) { + model->hint_timeout = HINT_TIMEOUT_H; + return true; + }); +} + +void desktop_locked_reset_door_pos(DesktopLockedView* locked_view) { + with_view_model( + locked_view->view, (DesktopLockedViewModel * model) { + model->animation_seq_end = false; + model->door_left_x = -57; + model->door_right_x = 115; + return true; + }); +} + +void desktop_locked_manage_redraw(DesktopLockedView* locked_view) { + bool animation_seq_end; + + with_view_model( + locked_view->view, (DesktopLockedViewModel * model) { + model->animation_seq_end = !model->door_left_x; + animation_seq_end = model->animation_seq_end; + + if(!model->animation_seq_end) { + model->door_left_x = CLAMP(model->door_left_x + 5, 0, -57); + model->door_right_x = CLAMP(model->door_right_x - 5, 115, 60); + } + return true; + }); + + if(animation_seq_end) { + osTimerStop(locked_view->timer); + } +} + +void desktop_locked_reset_counter(DesktopLockedView* locked_view) { + locked_view->lock_count = 0; + locked_view->lock_lastpress = 0; + + with_view_model( + locked_view->view, (DesktopLockedViewModel * model) { + model->hint_timeout = 0; + return true; + }); +} + +void desktop_locked_render(Canvas* canvas, void* model) { + DesktopLockedViewModel* m = model; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + if(!m->animation_seq_end) { + canvas_draw_icon(canvas, m->door_left_x, 0, &I_DoorLeft_70x55); + canvas_draw_icon(canvas, m->door_right_x, 0, &I_DoorRight_70x55); + } + + if(m->animation && m->animation_seq_end) { + canvas_draw_icon_animation(canvas, 0, -3, m->animation); + } + + if(m->hint_timeout) { + m->hint_timeout--; + + if(!m->animation_seq_end) { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_framed(canvas, 42, 30, "Locked"); + } else { + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49); + elements_multiline_text(canvas, 65, 20, "To unlock\npress:"); + } + } +} + +View* desktop_locked_get_view(DesktopLockedView* locked_view) { + furi_assert(locked_view); + return locked_view->view; +} + +bool desktop_locked_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + DesktopLockedView* locked_view = context; + if(event->type == InputTypeShort) { + with_view_model( + locked_view->view, (DesktopLockedViewModel * model) { + model->hint_timeout = HINT_TIMEOUT_L; + return true; + }); + + if(event->key == InputKeyBack) { + uint32_t press_time = HAL_GetTick(); + + // check if pressed sequentially + if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) { + locked_view->lock_lastpress = press_time; + locked_view->lock_count = 0; + } else if(press_time - locked_view->lock_lastpress < UNLOCK_RST_TIMEOUT) { + locked_view->lock_lastpress = press_time; + locked_view->lock_count++; + } + + if(locked_view->lock_count == UNLOCK_CNT) { + locked_view->lock_count = 0; + locked_view->callback(DesktopLockedEventUnlock, locked_view->context); + } + } + } + // All events consumed + return true; +} + +DesktopLockedView* desktop_locked_alloc() { + DesktopLockedView* locked_view = furi_alloc(sizeof(DesktopLockedView)); + locked_view->view = view_alloc(); + locked_view->timer = + osTimerNew(locked_view_timer_callback, osTimerPeriodic, locked_view, NULL); + + view_allocate_model(locked_view->view, ViewModelTypeLocking, sizeof(DesktopLockedViewModel)); + view_set_context(locked_view->view, locked_view); + view_set_draw_callback(locked_view->view, (ViewDrawCallback)desktop_locked_render); + view_set_input_callback(locked_view->view, desktop_locked_input); + + desktop_scene_handler_set_scene(locked_view, idle_scenes[random() % COUNT_OF(idle_scenes)]); + return locked_view; +} + +void desktop_locked_free(DesktopLockedView* locked_view) { + furi_assert(locked_view); + osTimerDelete(locked_view->timer); + view_free(locked_view->view); + free(locked_view); +} diff --git a/applications/desktop/views/desktop_locked.h b/applications/desktop/views/desktop_locked.h new file mode 100644 index 00000000..bf8db392 --- /dev/null +++ b/applications/desktop/views/desktop_locked.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include +#include + +#define UNLOCK_RST_TIMEOUT 200 +#define UNLOCK_CNT 2 // 3 actually + +typedef enum { + DesktopLockedEventUnlock, + DesktopLockedEventUpdate, +} DesktopLockedEvent; + +typedef struct DesktopLockedView DesktopLockedView; + +typedef void (*DesktopLockedViewCallback)(DesktopLockedEvent event, void* context); + +struct DesktopLockedView { + View* view; + DesktopLockedViewCallback callback; + void* context; + + osTimerId_t timer; + uint8_t lock_count; + uint32_t lock_lastpress; +}; + +typedef struct { + IconAnimation* animation; + uint8_t scene_num; + int8_t door_left_x; + int8_t door_right_x; + uint8_t hint_timeout; + bool animation_seq_end; + +} DesktopLockedViewModel; + +void desktop_locked_set_callback( + DesktopLockedView* locked_view, + DesktopLockedViewCallback callback, + void* context); + +void desktop_locked_update_hint_timeout(DesktopLockedView* locked_view); +void desktop_locked_reset_counter(DesktopLockedView* locked_view); +void desktop_locked_reset_door_pos(DesktopLockedView* locked_view); +void desktop_locked_manage_redraw(DesktopLockedView* locked_view); + +View* desktop_locked_get_view(DesktopLockedView* locked_view); +DesktopLockedView* desktop_locked_alloc(); +void desktop_locked_free(DesktopLockedView* locked_view); +void desktop_main_unlocked(DesktopMainView* main_view); +void desktop_main_reset_hint(DesktopMainView* main_view); \ No newline at end of file diff --git a/applications/desktop/views/desktop_main.c b/applications/desktop/views/desktop_main.c new file mode 100644 index 00000000..171bb42e --- /dev/null +++ b/applications/desktop/views/desktop_main.c @@ -0,0 +1,116 @@ +#include +#include "../desktop_i.h" +#include "desktop_main.h" + +static const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64}; + +void desktop_main_set_callback( + DesktopMainView* main_view, + DesktopMainViewCallback callback, + void* context) { + furi_assert(main_view); + furi_assert(callback); + main_view->callback = callback; + main_view->context = context; +} + +void desktop_main_reset_hint(DesktopMainView* main_view) { + with_view_model( + main_view->view, (DesktopMainViewModel * model) { + model->hint_timeout = 0; + return true; + }); +} +// temporary main screen animation managment +void desktop_scene_handler_set_scene(DesktopMainView* main_view, const Icon* icon_data) { + with_view_model( + main_view->view, (DesktopMainViewModel * model) { + if(model->animation) icon_animation_free(model->animation); + model->animation = icon_animation_alloc(icon_data); + icon_animation_start(model->animation); + return true; + }); +} + +void desktop_scene_handler_switch_scene(DesktopMainView* main_view) { + with_view_model( + main_view->view, (DesktopMainViewModel * model) { + if(icon_animation_is_last_frame(model->animation)) { + if(model->animation) icon_animation_free(model->animation); + model->animation = icon_animation_alloc(idle_scenes[model->scene_num]); + icon_animation_start(model->animation); + model->scene_num = random() % COUNT_OF(idle_scenes); + } + return true; + }); +} + +void desktop_main_render(Canvas* canvas, void* model) { + canvas_clear(canvas); + DesktopMainViewModel* m = model; + + if(m->animation) { + canvas_draw_icon_animation(canvas, 0, -3, m->animation); + } + + if(m->unlocked && m->hint_timeout) { + m->hint_timeout = CLAMP(m->hint_timeout - 1, 2, 0); + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_framed(canvas, 42, 30, "Unlocked"); + } +} + +View* desktop_main_get_view(DesktopMainView* main_view) { + furi_assert(main_view); + return main_view->view; +} + +bool desktop_main_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + DesktopMainView* main_view = context; + + if(event->key == InputKeyOk && event->type == InputTypeShort) { + main_view->callback(DesktopMainEventOpenMenu, main_view->context); + } else if(event->key == InputKeyDown && event->type == InputTypeLong) { + main_view->callback(DesktopMainEventOpenDebug, main_view->context); + } else if(event->key == InputKeyUp && event->type == InputTypeShort) { + main_view->callback(DesktopMainEventOpenLockMenu, main_view->context); + } else if(event->key == InputKeyDown && event->type == InputTypeShort) { + main_view->callback(DesktopMainEventOpenArchive, main_view->context); + } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { + main_view->callback(DesktopMainEventOpenFavorite, main_view->context); + } + desktop_main_reset_hint(main_view); + + return true; +} + +DesktopMainView* desktop_main_alloc() { + DesktopMainView* main_view = furi_alloc(sizeof(DesktopMainView)); + main_view->view = view_alloc(); + view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(DesktopMainViewModel)); + view_set_context(main_view->view, main_view); + view_set_draw_callback(main_view->view, (ViewDrawCallback)desktop_main_render); + view_set_input_callback(main_view->view, desktop_main_input); + + desktop_scene_handler_set_scene(main_view, idle_scenes[random() % COUNT_OF(idle_scenes)]); + + return main_view; +} + +void desktop_main_free(DesktopMainView* main_view) { + furi_assert(main_view); + view_free(main_view->view); + free(main_view); +} + +void desktop_main_unlocked(DesktopMainView* main_view) { + with_view_model( + main_view->view, (DesktopMainViewModel * model) { + model->unlocked = true; + model->hint_timeout = 2; + return true; + }); +} diff --git a/applications/desktop/views/desktop_main.h b/applications/desktop/views/desktop_main.h new file mode 100644 index 00000000..eadd1e82 --- /dev/null +++ b/applications/desktop/views/desktop_main.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include + +typedef enum { + DesktopMainEventOpenMenu, + DesktopMainEventOpenLockMenu, + DesktopMainEventOpenDebug, + DesktopMainEventUnlocked, + DesktopMainEventOpenArchive, + DesktopMainEventOpenFavorite, +} DesktopMainEvent; + +typedef struct DesktopMainView DesktopMainView; + +typedef void (*DesktopMainViewCallback)(DesktopMainEvent event, void* context); + +struct DesktopMainView { + View* view; + DesktopMainViewCallback callback; + void* context; +}; + +typedef struct { + IconAnimation* animation; + uint8_t scene_num; + uint8_t hint_timeout; + bool unlocked; +} DesktopMainViewModel; + +void desktop_main_set_callback( + DesktopMainView* main_view, + DesktopMainViewCallback callback, + void* context); + +View* desktop_main_get_view(DesktopMainView* main_view); + +DesktopMainView* desktop_main_alloc(); +void desktop_main_free(DesktopMainView* main_view); diff --git a/applications/dialogs/dialogs-api-lock.h b/applications/dialogs/dialogs-api-lock.h index fe9e934f..ad3c78e5 100644 --- a/applications/dialogs/dialogs-api-lock.h +++ b/applications/dialogs/dialogs-api-lock.h @@ -1,8 +1,18 @@ #pragma once -#define API_LOCK_INIT_LOCKED() osSemaphoreNew(1, 0, NULL); + +typedef osEventFlagsId_t FuriApiLock; + +#define API_LOCK_EVENT (1U << 0) + +#define API_LOCK_INIT_LOCKED() osEventFlagsNew(NULL); + +#define API_LOCK_WAIT_UNTIL_UNLOCK(_lock) \ + osEventFlagsWait(_lock, API_LOCK_EVENT, osFlagsWaitAny, osWaitForever); + +#define API_LOCK_FREE(_lock) osEventFlagsDelete(_lock); + +#define API_LOCK_UNLOCK(_lock) osEventFlagsSet(_lock, API_LOCK_EVENT); #define API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(_lock) \ - osSemaphoreAcquire(_lock, osWaitForever); \ - osSemaphoreDelete(_lock); - -#define API_LOCK_UNLOCK(_lock) osSemaphoreRelease(_lock); \ No newline at end of file + API_LOCK_WAIT_UNTIL_UNLOCK(_lock); \ + API_LOCK_FREE(_lock); diff --git a/applications/dialogs/dialogs-api.c b/applications/dialogs/dialogs-api.c index 5ad9cedc..681724fa 100644 --- a/applications/dialogs/dialogs-api.c +++ b/applications/dialogs/dialogs-api.c @@ -10,8 +10,8 @@ bool dialog_file_select_show( char* result, uint8_t result_size, const char* preselected_filename) { - osSemaphoreId_t semaphore = API_LOCK_INIT_LOCKED(); - furi_check(semaphore != NULL); + FuriApiLock lock = API_LOCK_INIT_LOCKED(); + furi_check(lock != NULL); DialogsAppData data = { .file_select = { @@ -24,14 +24,14 @@ bool dialog_file_select_show( DialogsAppReturn return_data; DialogsAppMessage message = { - .semaphore = semaphore, + .lock = lock, .command = DialogsAppCommandFileOpen, .data = &data, .return_data = &return_data, }; furi_check(osMessageQueuePut(context->message_queue, &message, 0, osWaitForever) == osOK); - API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(semaphore); + API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock); return return_data.bool_value; } @@ -39,8 +39,8 @@ bool dialog_file_select_show( /****************** Message ******************/ DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* dialog_message) { - osSemaphoreId_t semaphore = API_LOCK_INIT_LOCKED(); - furi_check(semaphore != NULL); + FuriApiLock lock = API_LOCK_INIT_LOCKED(); + furi_check(lock != NULL); DialogsAppData data = { .dialog = { @@ -49,14 +49,14 @@ DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage DialogsAppReturn return_data; DialogsAppMessage message = { - .semaphore = semaphore, + .lock = lock, .command = DialogsAppCommandDialog, .data = &data, .return_data = &return_data, }; furi_check(osMessageQueuePut(context->message_queue, &message, 0, osWaitForever) == osOK); - API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(semaphore); + API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock); return return_data.dialog_value; } diff --git a/applications/dialogs/dialogs-message.h b/applications/dialogs/dialogs-message.h index 0bb6d86f..e3bd6c79 100644 --- a/applications/dialogs/dialogs-message.h +++ b/applications/dialogs/dialogs-message.h @@ -1,6 +1,7 @@ #pragma once #include #include "dialogs-i.h" +#include "dialogs-api-lock.h" #ifdef __cplusplus extern "C" { @@ -34,7 +35,7 @@ typedef enum { } DialogsAppCommand; typedef struct { - osSemaphoreId_t semaphore; + FuriApiLock lock; DialogsAppCommand command; DialogsAppData* data; DialogsAppReturn* return_data; diff --git a/applications/dialogs/dialogs-module-file-select.c b/applications/dialogs/dialogs-module-file-select.c index 1f2abb40..473f1451 100644 --- a/applications/dialogs/dialogs-module-file-select.c +++ b/applications/dialogs/dialogs-module-file-select.c @@ -3,7 +3,7 @@ #include typedef struct { - osSemaphoreId_t semaphore; + FuriApiLock lock; bool result; } DialogsAppFileSelectContext; @@ -11,14 +11,14 @@ static void dialogs_app_file_select_back_callback(void* context) { furi_assert(context); DialogsAppFileSelectContext* file_select_context = context; file_select_context->result = false; - API_LOCK_UNLOCK(file_select_context->semaphore); + API_LOCK_UNLOCK(file_select_context->lock); } static void dialogs_app_file_select_callback(bool result, void* context) { furi_assert(context); DialogsAppFileSelectContext* file_select_context = context; file_select_context->result = result; - API_LOCK_UNLOCK(file_select_context->semaphore); + API_LOCK_UNLOCK(file_select_context->lock); } bool dialogs_app_process_module_file_select(const DialogsAppMessageDataFileSelect* data) { @@ -27,7 +27,7 @@ bool dialogs_app_process_module_file_select(const DialogsAppMessageDataFileSelec DialogsAppFileSelectContext* file_select_context = furi_alloc(sizeof(DialogsAppFileSelectContext)); - file_select_context->semaphore = API_LOCK_INIT_LOCKED(); + file_select_context->lock = API_LOCK_INIT_LOCKED(); ViewHolder* view_holder = view_holder_alloc(); view_holder_attach_to_gui(view_holder, gui); @@ -45,14 +45,15 @@ bool dialogs_app_process_module_file_select(const DialogsAppMessageDataFileSelec view_holder_set_view(view_holder, file_select_get_view(file_select)); view_holder_start(view_holder); - API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(file_select_context->semaphore); + API_LOCK_WAIT_UNTIL_UNLOCK(file_select_context->lock); ret = file_select_context->result; - free(file_select_context); view_holder_stop(view_holder); view_holder_free(view_holder); file_select_free(file_select); + API_LOCK_FREE(file_select_context->lock); + free(file_select_context); furi_record_close("gui"); return ret; diff --git a/applications/dialogs/dialogs-module-message.c b/applications/dialogs/dialogs-module-message.c index 77e727ef..f31396df 100644 --- a/applications/dialogs/dialogs-module-message.c +++ b/applications/dialogs/dialogs-module-message.c @@ -3,7 +3,7 @@ #include typedef struct { - osSemaphoreId_t semaphore; + FuriApiLock lock; DialogMessageButton result; } DialogsAppMessageContext; @@ -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->semaphore); + API_LOCK_UNLOCK(message_context->lock); } static void dialogs_app_message_callback(DialogExResult result, void* context) { @@ -47,7 +47,7 @@ static void dialogs_app_message_callback(DialogExResult result, void* context) { message_context->result = DialogMessageButtonCenter; break; } - API_LOCK_UNLOCK(message_context->semaphore); + API_LOCK_UNLOCK(message_context->lock); } DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data) { @@ -55,7 +55,7 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa Gui* gui = furi_record_open("gui"); const DialogMessage* message = data->message; DialogsAppMessageContext* message_context = furi_alloc(sizeof(DialogsAppMessageContext)); - message_context->semaphore = API_LOCK_INIT_LOCKED(); + message_context->lock = API_LOCK_INIT_LOCKED(); ViewHolder* view_holder = view_holder_alloc(); view_holder_attach_to_gui(view_holder, gui); @@ -85,14 +85,15 @@ 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_AND_FREE(message_context->semaphore); + API_LOCK_WAIT_UNTIL_UNLOCK(message_context->lock); ret = message_context->result; - free(message_context); view_holder_stop(view_holder); view_holder_free(view_holder); dialog_ex_free(dialog_ex); + API_LOCK_FREE(message_context->lock); + free(message_context); furi_record_close("gui"); return ret; diff --git a/applications/dialogs/dialogs.c b/applications/dialogs/dialogs.c index 8395e852..1b64ca77 100644 --- a/applications/dialogs/dialogs.c +++ b/applications/dialogs/dialogs.c @@ -21,7 +21,7 @@ static void dialogs_app_process_message(DialogsApp* app, DialogsAppMessage* mess dialogs_app_process_module_message(&message->data->dialog); break; } - API_LOCK_UNLOCK(message->semaphore); + API_LOCK_UNLOCK(message->lock); } int32_t dialogs_srv(void* p) { diff --git a/applications/dolphin/dolphin.c b/applications/dolphin/dolphin.c index cdcef27e..a0d24df9 100644 --- a/applications/dolphin/dolphin.c +++ b/applications/dolphin/dolphin.c @@ -1,389 +1,9 @@ #include "dolphin_i.h" -#include -#include "applications.h" +#include -const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64}; - -static void dolphin_switch_to_app(Dolphin* dolphin, const FlipperApplication* flipper_app) { +bool dolphin_load(Dolphin* dolphin) { furi_assert(dolphin); - furi_assert(flipper_app); - furi_assert(flipper_app->app); - furi_assert(flipper_app->name); - - if(furi_thread_get_state(dolphin->scene_thread) != FuriThreadStateStopped) { - FURI_LOG_E("Dolphin", "Thread is already running"); - return; - } - - furi_thread_set_name(dolphin->scene_thread, flipper_app->name); - furi_thread_set_stack_size(dolphin->scene_thread, flipper_app->stack_size); - furi_thread_set_callback(dolphin->scene_thread, flipper_app->app); - - furi_thread_start(dolphin->scene_thread); -} - -// temporary main screen animation managment -void dolphin_scene_handler_set_scene(Dolphin* dolphin, const Icon* icon_data) { - with_view_model( - dolphin->idle_view_main, (DolphinViewMainModel * model) { - if(model->animation) icon_animation_free(model->animation); - model->animation = icon_animation_alloc(icon_data); - icon_animation_start(model->animation); - return true; - }); -} - -void dolphin_scene_handler_switch_scene(Dolphin* dolphin) { - with_view_model( - dolphin->idle_view_main, (DolphinViewMainModel * model) { - if(icon_animation_is_last_frame(model->animation)) { - if(model->animation) icon_animation_free(model->animation); - model->animation = icon_animation_alloc(idle_scenes[model->scene_num]); - icon_animation_start(model->animation); - model->scene_num = random() % COUNT_OF(idle_scenes); - } - return true; - }); -} - -bool dolphin_view_first_start_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - Dolphin* dolphin = context; - if(event->type == InputTypeShort) { - DolphinViewFirstStartModel* model = view_get_model(dolphin->idle_view_first_start); - if(event->key == InputKeyLeft) { - if(model->page > 0) model->page--; - } else if(event->key == InputKeyRight) { - uint32_t page = ++model->page; - if(page > 8) { - dolphin_save(dolphin); - view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); - } - } - view_commit_model(dolphin->idle_view_first_start, true); - } - // All evennts cosumed - return true; -} - -void dolphin_lock_handler(InputEvent* event, Dolphin* dolphin) { - furi_assert(event); - furi_assert(dolphin); - - with_view_model( - dolphin->idle_view_main, (DolphinViewMainModel * model) { - model->hint_timeout = HINT_TIMEOUT_L; - return true; - }); - - if(event->key == InputKeyBack && event->type == InputTypeShort) { - uint32_t press_time = HAL_GetTick(); - - // check if pressed sequentially - if(press_time - dolphin->lock_lastpress > UNLOCK_RST_TIMEOUT) { - dolphin->lock_lastpress = press_time; - dolphin->lock_count = 0; - } else if(press_time - dolphin->lock_lastpress < UNLOCK_RST_TIMEOUT) { - dolphin->lock_lastpress = press_time; - dolphin->lock_count++; - } - - if(dolphin->lock_count == 2) { - dolphin->locked = false; - dolphin->lock_count = 0; - - with_view_model( - dolphin->view_lockmenu, (DolphinViewLockMenuModel * model) { - model->locked = false; - model->door_left_x = -57; // move doors to default pos - model->door_right_x = 115; - return true; - }); - - with_view_model( - dolphin->idle_view_main, (DolphinViewMainModel * model) { - model->hint_timeout = HINT_TIMEOUT_L; // "unlocked" hint timeout - model->locked = false; - return true; - }); - - view_port_enabled_set(dolphin->lock_viewport, false); - } - } -} - -bool dolphin_view_idle_main_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - Dolphin* dolphin = context; - // unlocked - if(!dolphin->locked) { - if(event->key == InputKeyOk && event->type == InputTypeShort) { - with_value_mutex( - dolphin->menu_vm, (Menu * menu) { menu_ok(menu); }); - } else if(event->key == InputKeyUp && event->type == InputTypeShort) { - osTimerStart(dolphin->timeout_timer, 64); - view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewLockMenu); - } else if(event->key == InputKeyDown && event->type == InputTypeShort) { - dolphin_switch_to_app(dolphin, &FLIPPER_ARCHIVE); - } else if(event->key == InputKeyDown && event->type == InputTypeLong) { - view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewStats); - } else if(event->key == InputKeyBack && event->type == InputTypeShort) { - view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); - } - - with_view_model( - dolphin->idle_view_main, (DolphinViewMainModel * model) { - model->hint_timeout = 0; // clear hint timeout - return true; - }); - - } else { - // locked - - dolphin_lock_handler(event, dolphin); - dolphin_scene_handler_switch_scene(dolphin); - } - // All events consumed - return true; -} - -void lock_menu_refresh_handler(void* p) { - osMessageQueueId_t event_queue = p; - DolphinEvent event; - event.type = DolphinEventTypeTick; - // Some tick events may lost and we don't care. - osMessageQueuePut(event_queue, &event, 0, 0); -} - -static void lock_menu_callback(void* context, uint8_t index) { - furi_assert(context); - Dolphin* dolphin = context; - switch(index) { - // lock - case 0: - dolphin->locked = true; - - with_view_model( - dolphin->view_lockmenu, (DolphinViewLockMenuModel * model) { - model->locked = true; - model->exit_timeout = HINT_TIMEOUT_H; - return true; - }); - - with_view_model( - dolphin->idle_view_main, (DolphinViewMainModel * model) { - model->locked = true; - return true; - }); - break; - - default: - // wip message - with_view_model( - dolphin->view_lockmenu, (DolphinViewLockMenuModel * model) { - model->hint_timeout = HINT_TIMEOUT_H; - return true; - }); - break; - } -} - -static void lock_icon_callback(Canvas* canvas, void* context) { - furi_assert(context); - Dolphin* dolphin = context; - canvas_draw_icon_animation(canvas, 0, 0, dolphin->lock_icon); -} - -bool dolphin_view_lockmenu_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - Dolphin* dolphin = context; - - if(event->type != InputTypeShort) return false; - - DolphinViewLockMenuModel* model = view_get_model(dolphin->view_lockmenu); - - model->hint_timeout = 0; // clear hint timeout - - if(event->key == InputKeyUp) { - model->idx = CLAMP(model->idx - 1, 2, 0); - } else if(event->key == InputKeyDown) { - model->idx = CLAMP(model->idx + 1, 2, 0); - } else if(event->key == InputKeyOk) { - lock_menu_callback(context, model->idx); - } else if(event->key == InputKeyBack) { - model->idx = 0; - view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); - - if(random() % 100 > 50) - dolphin_scene_handler_set_scene( - dolphin, idle_scenes[random() % COUNT_OF(idle_scenes)]); - } - - view_commit_model(dolphin->view_lockmenu, true); - - return true; -} - -bool dolphin_view_idle_down_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - Dolphin* dolphin = context; - DolphinViewStatsScreens current; - - if(event->type != InputTypeShort) return false; - - DolphinViewStatsModel* model = view_get_model(dolphin->idle_view_dolphin_stats); - - current = model->screen; - - if(event->key == InputKeyDown) { - model->screen = (model->screen + 1) % DolphinViewStatsTotalCount; - } else if(event->key == InputKeyUp) { - model->screen = - ((model->screen - 1) + DolphinViewStatsTotalCount) % DolphinViewStatsTotalCount; - } - - view_commit_model(dolphin->idle_view_dolphin_stats, true); - - if(current == DolphinViewStatsMeta) { - if(event->key == InputKeyLeft) { - dolphin_deed(dolphin, DolphinDeedWrong); - } else if(event->key == InputKeyRight) { - dolphin_deed(dolphin, DolphinDeedIButtonRead); - } else if(event->key == InputKeyOk) { - dolphin_save(dolphin); - } else { - return false; - } - } - - if(event->key == InputKeyBack) { - view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); - } - - return true; -} - -Dolphin* dolphin_alloc() { - Dolphin* dolphin = furi_alloc(sizeof(Dolphin)); - // Message queue - dolphin->event_queue = osMessageQueueNew(8, sizeof(DolphinEvent), NULL); - furi_check(dolphin->event_queue); - // State - dolphin->state = dolphin_state_alloc(); - // Menu - dolphin->menu_vm = furi_record_open("menu"); - // Scene thread - dolphin->scene_thread = furi_thread_alloc(); - // GUI - dolphin->gui = furi_record_open("gui"); - // Dispatcher - dolphin->idle_view_dispatcher = view_dispatcher_alloc(); - - // First start View - dolphin->idle_view_first_start = view_alloc(); - view_allocate_model( - dolphin->idle_view_first_start, ViewModelTypeLockFree, sizeof(DolphinViewFirstStartModel)); - view_set_context(dolphin->idle_view_first_start, dolphin); - view_set_draw_callback(dolphin->idle_view_first_start, dolphin_view_first_start_draw); - view_set_input_callback(dolphin->idle_view_first_start, dolphin_view_first_start_input); - view_dispatcher_add_view( - dolphin->idle_view_dispatcher, DolphinViewFirstStart, dolphin->idle_view_first_start); - - // Main Idle View - dolphin->idle_view_main = view_alloc(); - view_set_context(dolphin->idle_view_main, dolphin); - view_allocate_model( - dolphin->idle_view_main, ViewModelTypeLockFree, sizeof(DolphinViewMainModel)); - - view_set_draw_callback(dolphin->idle_view_main, dolphin_view_idle_main_draw); - view_set_input_callback(dolphin->idle_view_main, dolphin_view_idle_main_input); - view_dispatcher_add_view( - dolphin->idle_view_dispatcher, DolphinViewIdleMain, dolphin->idle_view_main); - - // Lock Menu View - dolphin->view_lockmenu = view_alloc(); - view_set_context(dolphin->view_lockmenu, dolphin); - view_allocate_model( - dolphin->view_lockmenu, ViewModelTypeLockFree, sizeof(DolphinViewLockMenuModel)); - view_set_draw_callback(dolphin->view_lockmenu, dolphin_view_lockmenu_draw); - view_set_input_callback(dolphin->view_lockmenu, dolphin_view_lockmenu_input); - view_set_previous_callback(dolphin->view_lockmenu, dolphin_view_idle_back); - view_dispatcher_add_view( - dolphin->idle_view_dispatcher, DolphinViewLockMenu, dolphin->view_lockmenu); - - // default doors xpos - with_view_model( - dolphin->view_lockmenu, (DolphinViewLockMenuModel * model) { - model->door_left_x = -57; // defaults - model->door_right_x = 115; // defaults - return true; - }); - - dolphin->timeout_timer = - osTimerNew(lock_menu_refresh_handler, osTimerPeriodic, dolphin->event_queue, NULL); - - // Stats Idle View - dolphin->idle_view_dolphin_stats = view_alloc(); - view_set_context(dolphin->idle_view_dolphin_stats, dolphin); - view_allocate_model( - dolphin->idle_view_dolphin_stats, ViewModelTypeLockFree, sizeof(DolphinViewStatsModel)); - view_set_draw_callback(dolphin->idle_view_dolphin_stats, dolphin_view_idle_down_draw); - view_set_input_callback(dolphin->idle_view_dolphin_stats, dolphin_view_idle_down_input); - view_set_previous_callback(dolphin->idle_view_dolphin_stats, dolphin_view_idle_back); - view_dispatcher_add_view( - dolphin->idle_view_dispatcher, DolphinViewStats, dolphin->idle_view_dolphin_stats); - // HW Mismatch - dolphin->view_hw_mismatch = view_alloc(); - view_set_draw_callback(dolphin->view_hw_mismatch, dolphin_view_hw_mismatch_draw); - view_set_previous_callback(dolphin->view_hw_mismatch, dolphin_view_idle_back); - view_dispatcher_add_view( - dolphin->idle_view_dispatcher, DolphinViewHwMismatch, dolphin->view_hw_mismatch); - - // Lock icon - dolphin->lock_icon = icon_animation_alloc(&I_Lock_8x8); - dolphin->lock_viewport = view_port_alloc(); - view_port_set_width(dolphin->lock_viewport, icon_animation_get_width(dolphin->lock_icon)); - view_port_draw_callback_set(dolphin->lock_viewport, lock_icon_callback, dolphin); - view_port_enabled_set(dolphin->lock_viewport, false); - - // Main screen animation - dolphin_scene_handler_set_scene(dolphin, idle_scenes[random() % COUNT_OF(idle_scenes)]); - - view_dispatcher_attach_to_gui( - dolphin->idle_view_dispatcher, dolphin->gui, ViewDispatcherTypeWindow); - gui_add_view_port(dolphin->gui, dolphin->lock_viewport, GuiLayerStatusBarLeft); - - return dolphin; -} - -void dolphin_free(Dolphin* dolphin) { - furi_assert(dolphin); - - gui_remove_view_port(dolphin->gui, dolphin->lock_viewport); - view_port_free(dolphin->lock_viewport); - icon_animation_free(dolphin->lock_icon); - - osTimerDelete(dolphin->timeout_timer); - - view_dispatcher_free(dolphin->idle_view_dispatcher); - - furi_record_close("gui"); - dolphin->gui = NULL; - - furi_thread_free(dolphin->scene_thread); - - furi_record_close("menu"); - dolphin->menu_vm = NULL; - - dolphin_state_free(dolphin->state); - - osMessageQueueDelete(dolphin->event_queue); - - free(dolphin); + return dolphin_state_load(dolphin->state); } void dolphin_save(Dolphin* dolphin) { @@ -401,56 +21,53 @@ void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { furi_check(osMessageQueuePut(dolphin->event_queue, &event, 0, osWaitForever) == osOK); } -int32_t dolphin_srv() { +DolphinDeedWeight dolphin_stats(Dolphin* dolphin) { + DolphinDeedWeight stats; + stats.butthurt = dolphin_state_get_butthurt(dolphin->state); + stats.icounter = dolphin_state_get_icounter(dolphin->state); + + return stats; +} + +Dolphin* dolphin_alloc() { + Dolphin* dolphin = furi_alloc(sizeof(Dolphin)); + + dolphin->state = dolphin_state_alloc(); + dolphin->event_queue = osMessageQueueNew(8, sizeof(DolphinEvent), NULL); + + return dolphin; +} + +void dolphin_free(Dolphin* dolphin) { + furi_assert(dolphin); + + dolphin_state_free(dolphin->state); + osMessageQueueDelete(dolphin->event_queue); + + free(dolphin); +} + +int32_t dolphin_srv(void* p) { Dolphin* dolphin = dolphin_alloc(); - - if(dolphin_state_load(dolphin->state)) { - view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); - } else { - view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewFirstStart); - } - - with_view_model( - dolphin->idle_view_dolphin_stats, (DolphinViewStatsModel * model) { - model->icounter = dolphin_state_get_icounter(dolphin->state); - model->butthurt = dolphin_state_get_butthurt(dolphin->state); - return true; - }); - furi_record_create("dolphin", dolphin); - if(!furi_hal_version_do_i_belong_here()) { - view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewHwMismatch); - } - DolphinEvent event; while(1) { furi_check(osMessageQueueGet(dolphin->event_queue, &event, NULL, osWaitForever) == osOK); - - DolphinViewLockMenuModel* lock_model = view_get_model(dolphin->view_lockmenu); - - if(lock_model->locked && lock_model->exit_timeout == 0 && - osTimerIsRunning(dolphin->timeout_timer)) { - osTimerStop(dolphin->timeout_timer); - osDelay(1); // smol enterprise delay - view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); - } - - if(event.type == DolphinEventTypeTick) { - view_commit_model(dolphin->view_lockmenu, true); - - } else if(event.type == DolphinEventTypeDeed) { + switch(event.type) { + case DolphinEventTypeDeed: dolphin_state_on_deed(dolphin->state, event.deed); - with_view_model( - dolphin->idle_view_dolphin_stats, (DolphinViewStatsModel * model) { - model->icounter = dolphin_state_get_icounter(dolphin->state); - model->butthurt = dolphin_state_get_butthurt(dolphin->state); - return true; - }); - } else if(event.type == DolphinEventTypeSave) { + break; + + case DolphinEventTypeSave: dolphin_state_save(dolphin->state); + break; + + default: + break; } } + dolphin_free(dolphin); return 0; } diff --git a/applications/dolphin/dolphin.h b/applications/dolphin/dolphin.h index 676f5dc5..0351417b 100644 --- a/applications/dolphin/dolphin.h +++ b/applications/dolphin/dolphin.h @@ -1,11 +1,30 @@ #pragma once -#include "dolphin_deed.h" +#include "helpers/dolphin_deed.h" typedef struct Dolphin Dolphin; +/* Load Dolphin state + * Thread safe + */ + +bool dolphin_load(Dolphin* dolphin); + /* Deed complete notification. Call it on deed completion. * See dolphin_deed.h for available deeds. In futures it will become part of assets. * Thread safe */ + void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); + +/* Save Dolphin state (write to permanent memory) + * Thread safe + */ + +void dolphin_save(Dolphin* dolphin); + +/* Retrieve dolphin's icounter and butthurt values + * Thread safe + */ + +DolphinDeedWeight dolphin_stats(Dolphin* dolphin); \ No newline at end of file diff --git a/applications/dolphin/dolphin_i.h b/applications/dolphin/dolphin_i.h index cd562492..f9393984 100644 --- a/applications/dolphin/dolphin_i.h +++ b/applications/dolphin/dolphin_i.h @@ -1,22 +1,10 @@ #pragma once -#include "dolphin.h" -#include "dolphin_state.h" -#include "dolphin_views.h" - #include #include -#include -#include -#include -#include -#include -#include - -#define UNLOCK_RST_TIMEOUT 500 // keypress counter reset timeout (ms) -#define HINT_TIMEOUT_L 3 // low refresh rate timeout (app ticks) -#define HINT_TIMEOUT_H 40 // high refresh rate timeout (app ticks) +#include "dolphin.h" +#include "helpers/dolphin_state.h" typedef enum { DolphinEventTypeDeed, @@ -32,36 +20,12 @@ typedef struct { } DolphinEvent; struct Dolphin { - // Internal message queue - osMessageQueueId_t event_queue; // State DolphinState* state; - // Menu - ValueMutex* menu_vm; - // Scene - FuriThread* scene_thread; - // GUI - Gui* gui; - ViewDispatcher* idle_view_dispatcher; - View* idle_view_first_start; - View* idle_view_main; - View* idle_view_dolphin_stats; - View* view_hw_mismatch; - View* view_lockmenu; - ViewPort* lock_viewport; - IconAnimation* lock_icon; - - bool locked; - uint8_t lock_count; - uint32_t lock_lastpress; - osTimerId_t timeout_timer; + // Queue + osMessageQueueId_t event_queue; }; Dolphin* dolphin_alloc(); void dolphin_free(Dolphin* dolphin); - -/* Save Dolphin state (write to permanent memory) - * Thread safe - */ -void dolphin_save(Dolphin* dolphin); diff --git a/applications/dolphin/dolphin_views.c b/applications/dolphin/dolphin_views.c deleted file mode 100644 index 953671e7..00000000 --- a/applications/dolphin/dolphin_views.c +++ /dev/null @@ -1,197 +0,0 @@ -#include "dolphin_views.h" -#include -#include -#include -#include -#include - -static char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"}; - -void dolphin_view_first_start_draw(Canvas* canvas, void* model) { - DolphinViewFirstStartModel* m = model; - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - uint8_t width = canvas_width(canvas); - uint8_t height = canvas_height(canvas); - const char* my_name = furi_hal_version_get_name_ptr(); - if(m->page == 0) { - canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart0_70x53); - elements_multiline_text_framed(canvas, 75, 20, "Hey m8,\npress > to\ncontinue"); - } else if(m->page == 1) { - canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart1_59x53); - elements_multiline_text_framed(canvas, 64, 20, "First Of All,\n... >"); - } else if(m->page == 2) { - canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart2_59x51); - elements_multiline_text_framed(canvas, 64, 20, "Thank you\nfor your\nsupport! >"); - } else if(m->page == 3) { - canvas_draw_icon(canvas, width - 57, height - 48, &I_DolphinFirstStart3_57x48); - elements_multiline_text_framed(canvas, 0, 20, "Kickstarter\ncampaign\nwas INSANE! >"); - } else if(m->page == 4) { - canvas_draw_icon(canvas, width - 67, height - 50, &I_DolphinFirstStart4_67x53); - elements_multiline_text_framed(canvas, 0, 17, "Now\nallow me\nto introduce\nmyself >"); - } else if(m->page == 5) { - char buf[64]; - snprintf( - buf, - 64, - "%s %s%s", - "I am", - my_name ? my_name : "Unknown", - ",\ncyberdolphin\nliving in your\npocket >"); - canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart5_54x49); - elements_multiline_text_framed(canvas, 60, 17, buf); - } else if(m->page == 6) { - canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart6_58x54); - elements_multiline_text_framed( - canvas, 63, 17, "I can grow\nsmart'n'cool\nif you use me\noften >"); - } else if(m->page == 7) { - canvas_draw_icon(canvas, width - 61, height - 48, &I_DolphinFirstStart7_61x51); - elements_multiline_text_framed( - canvas, 0, 17, "As long as\nyou read, write\nand emulate >"); - } else if(m->page == 8) { - canvas_draw_icon(canvas, width - 56, height - 48, &I_DolphinFirstStart8_56x51); - elements_multiline_text_framed( - canvas, 0, 17, "You can check\nmy level and\nmood in the\nPassport menu"); - } -} - -void dolphin_view_idle_main_draw(Canvas* canvas, void* model) { - canvas_clear(canvas); - DolphinViewMainModel* m = model; - if(m->animation) { - canvas_draw_icon_animation(canvas, 0, -3, m->animation); - } - - if(m->hint_timeout) { - m->hint_timeout--; - if(m->locked) { - canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49); - elements_multiline_text(canvas, 65, 20, "To unlock\npress:"); - } else { - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_framed(canvas, 42, 30, "Unlocked"); - } - } -} - -void dolphin_view_lockmenu_draw(Canvas* canvas, void* model) { - DolphinViewLockMenuModel* m = model; - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_draw_icon(canvas, m->door_left_x, 0, &I_DoorLeft_70x55); - canvas_draw_icon(canvas, m->door_right_x, 0, &I_DoorRight_70x55); - canvas_set_font(canvas, FontSecondary); - - if(m->locked) { - m->exit_timeout--; - - m->door_left_x = CLAMP(m->door_left_x + 5, 0, -57); - m->door_right_x = CLAMP(m->door_right_x - 5, 115, 60); - - if(m->door_left_x > -10) { - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_framed(canvas, 42, 30, "Locked"); - } - - } else { - if(m->door_left_x == -57) { - for(uint8_t i = 0; i < 3; ++i) { - canvas_draw_str_aligned( - canvas, - 64, - 13 + (i * 17), - AlignCenter, - AlignCenter, - (m->hint_timeout && m->idx == i) ? "Not implemented" : Lockmenu_Items[i]); - if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15); - } - } - - if(m->hint_timeout) { - m->hint_timeout--; - } - } -} - -void dolphin_view_idle_down_draw(Canvas* canvas, void* model) { - DolphinViewStatsModel* m = model; - const Version* ver; - char buffer[64]; - - static const char* headers[] = {"FW Version info:", "Boot Version info:", "Dolphin info:"}; - - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 13, headers[m->screen]); - canvas_set_font(canvas, FontSecondary); - - if(m->screen != DolphinViewStatsMeta) { - // Hardware version - const char* my_name = furi_hal_version_get_name_ptr(); - snprintf( - buffer, - sizeof(buffer), - "HW: %d.F%dB%dC%d %s", - furi_hal_version_get_hw_version(), - furi_hal_version_get_hw_target(), - furi_hal_version_get_hw_body(), - furi_hal_version_get_hw_connect(), - my_name ? my_name : "Unknown"); - canvas_draw_str(canvas, 5, 23, buffer); - - ver = m->screen == DolphinViewStatsBoot ? furi_hal_version_get_boot_version() : - furi_hal_version_get_firmware_version(); - - if(!ver) { - canvas_draw_str(canvas, 5, 33, "No info"); - return; - } - - snprintf( - buffer, - sizeof(buffer), - "%s [%s]", - version_get_version(ver), - version_get_builddate(ver)); - canvas_draw_str(canvas, 5, 33, buffer); - - snprintf( - buffer, - sizeof(buffer), - "%s [%s]", - version_get_githash(ver), - version_get_gitbranchnum(ver)); - canvas_draw_str(canvas, 5, 43, buffer); - - snprintf( - buffer, sizeof(buffer), "[%s] %s", version_get_target(ver), version_get_gitbranch(ver)); - canvas_draw_str(canvas, 5, 53, buffer); - - } else { - char buffer[64]; - canvas_set_font(canvas, FontSecondary); - snprintf(buffer, 64, "Icounter: %ld", m->icounter); - canvas_draw_str(canvas, 5, 30, buffer); - snprintf(buffer, 64, "Butthurt: %ld", m->butthurt); - canvas_draw_str(canvas, 5, 40, buffer); - canvas_draw_str(canvas, 0, 53, "[< >] icounter value [ok] save"); - } -} - -void dolphin_view_hw_mismatch_draw(Canvas* canvas, void* model) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 15, "!!!! HW Mismatch !!!!"); - - char buffer[64]; - canvas_set_font(canvas, FontSecondary); - snprintf(buffer, 64, "HW target: F%d", furi_hal_version_get_hw_target()); - canvas_draw_str(canvas, 5, 27, buffer); - canvas_draw_str(canvas, 5, 38, "FW target: " TARGET); -} - -uint32_t dolphin_view_idle_back(void* context) { - return DolphinViewIdleMain; -} diff --git a/applications/dolphin/dolphin_views.h b/applications/dolphin/dolphin_views.h deleted file mode 100644 index 2f91b69a..00000000 --- a/applications/dolphin/dolphin_views.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -// Idle screen -typedef enum { - DolphinViewIdleMain, - DolphinViewFirstStart, - DolphinViewStats, - DolphinViewHwMismatch, - DolphinViewLockMenu, -} DolphinViewIdle; - -// Debug info -typedef enum { - DolphinViewStatsFw, - DolphinViewStatsBoot, - DolphinViewStatsMeta, - DolphinViewStatsTotalCount, -} DolphinViewStatsScreens; - -typedef struct { - uint32_t page; -} DolphinViewFirstStartModel; - -void dolphin_view_first_start_draw(Canvas* canvas, void* model); -bool dolphin_view_first_start_input(InputEvent* event, void* context); - -typedef struct { - uint32_t icounter; - uint32_t butthurt; - DolphinViewStatsScreens screen; -} DolphinViewStatsModel; - -typedef struct { - uint8_t idx; - int8_t door_left_x; - int8_t door_right_x; - uint8_t exit_timeout; - uint8_t hint_timeout; - - bool locked; -} DolphinViewLockMenuModel; - -typedef struct { - IconAnimation* animation; - uint8_t scene_num; - uint8_t hint_timeout; - bool locked; -} DolphinViewMainModel; - -void dolphin_view_idle_main_draw(Canvas* canvas, void* model); -bool dolphin_view_idle_main_input(InputEvent* event, void* context); - -void dolphin_view_idle_up_draw(Canvas* canvas, void* model); - -void dolphin_view_lockmenu_draw(Canvas* canvas, void* model); - -void dolphin_view_idle_down_draw(Canvas* canvas, void* model); - -void dolphin_view_hw_mismatch_draw(Canvas* canvas, void* model); - -uint32_t dolphin_view_idle_back(void* context); diff --git a/applications/dolphin/dolphin_deed.c b/applications/dolphin/helpers/dolphin_deed.c similarity index 100% rename from applications/dolphin/dolphin_deed.c rename to applications/dolphin/helpers/dolphin_deed.c diff --git a/applications/dolphin/dolphin_deed.h b/applications/dolphin/helpers/dolphin_deed.h similarity index 91% rename from applications/dolphin/dolphin_deed.h rename to applications/dolphin/helpers/dolphin_deed.h index 2af1829a..afbf94c2 100644 --- a/applications/dolphin/dolphin_deed.h +++ b/applications/dolphin/helpers/dolphin_deed.h @@ -16,6 +16,7 @@ typedef enum { typedef struct { int32_t icounter; // how many icounter get by Deed + int32_t butthurt; // how many icounter get by Deed uint32_t limit_value; // how many deeds in limit interval uint32_t limit_interval; // interval, in minutes } DolphinDeedWeight; diff --git a/applications/dolphin/dolphin_state.c b/applications/dolphin/helpers/dolphin_state.c similarity index 100% rename from applications/dolphin/dolphin_state.c rename to applications/dolphin/helpers/dolphin_state.c diff --git a/applications/dolphin/dolphin_state.h b/applications/dolphin/helpers/dolphin_state.h similarity index 100% rename from applications/dolphin/dolphin_state.h rename to applications/dolphin/helpers/dolphin_state.h diff --git a/applications/gpio-tester/gpio-tester.c b/applications/gpio-tester/gpio-tester.c index c088a195..e994ad73 100644 --- a/applications/gpio-tester/gpio-tester.c +++ b/applications/gpio-tester/gpio-tester.c @@ -18,6 +18,7 @@ static const GpioItem GPIO_PINS[] = { {"1.7: PC3", &gpio_ext_pc3}, {"2.7: PC1", &gpio_ext_pc1}, {"2.8: PC0", &gpio_ext_pc0}, + {"*.*: ALL", NULL}, }; static const size_t GPIO_PINS_COUNT = sizeof(GPIO_PINS) / sizeof(GPIO_PINS[0]); @@ -49,8 +50,20 @@ static void gpio_test_input_callback(InputEvent* input_event, void* ctx) { static void gpio_test_configure_pins(GpioMode mode) { for(size_t i = 0; i < GPIO_PINS_COUNT; i++) { + if(!GPIO_PINS[i].pin) continue; hal_gpio_write(GPIO_PINS[i].pin, false); - hal_gpio_init(GPIO_PINS[i].pin, mode, GpioPullNo, GpioSpeedLow); + hal_gpio_init(GPIO_PINS[i].pin, mode, GpioPullNo, GpioSpeedVeryHigh); + } +} + +static void gpio_test_set_pin(uint8_t index, bool level) { + if(GPIO_PINS[index].pin) { + hal_gpio_write(GPIO_PINS[index].pin, level); + } else { + for(size_t i = 0; i < GPIO_PINS_COUNT; i++) { + if(!GPIO_PINS[i].pin) continue; + hal_gpio_write(GPIO_PINS[i].pin, level); + } } } @@ -117,10 +130,10 @@ int32_t gpio_test_app(void* p) { } else { if(event.key == InputKeyOk) { if(event.type == InputTypePress) { - hal_gpio_write(GPIO_PINS[gpio_test->gpio_index].pin, true); + gpio_test_set_pin(gpio_test->gpio_index, true); notification_message(gpio_test->notification, &sequence_set_green_255); } else if(event.type == InputTypeRelease) { - hal_gpio_write(GPIO_PINS[gpio_test->gpio_index].pin, false); + gpio_test_set_pin(gpio_test->gpio_index, false); notification_message(gpio_test->notification, &sequence_reset_green); } } diff --git a/applications/gui/elements.c b/applications/gui/elements.c index 332baa71..8b1112b3 100644 --- a/applications/gui/elements.c +++ b/applications/gui/elements.c @@ -327,6 +327,17 @@ void elements_slightly_rounded_box( canvas_draw_rbox(canvas, x, y, width, height, 1); } +void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { + furi_assert(canvas); + canvas_draw_rframe(canvas, x + 4, y, width, height, 3); + uint8_t y_corner = y + height * 2 / 3; + canvas_draw_line(canvas, x, y_corner, x + 4, y_corner - 4); + canvas_draw_line(canvas, x, y_corner, x + 4, y_corner + 4); + canvas_set_color(canvas, ColorWhite); + canvas_draw_line(canvas, x + 4, y_corner - 3, x + 4, y_corner + 3); + canvas_set_color(canvas, ColorBlack); +} + void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width) { furi_assert(canvas); furi_assert(string); diff --git a/applications/gui/elements.h b/applications/gui/elements.h index a11f4875..04ad352b 100644 --- a/applications/gui/elements.h +++ b/applications/gui/elements.h @@ -8,8 +8,7 @@ extern "C" { #endif -/* - * Draw progress bar. +/** Draw progress bar. * @param x - progress bar position on X axis * @param y - progress bar position on Y axis * @param width - progress bar width @@ -24,8 +23,7 @@ void elements_progress_bar( uint8_t progress, uint8_t total); -/* - * Draw scrollbar on canvas at specific position. +/** Draw scrollbar on canvas at specific position. * @param x - scrollbar position on X axis * @param y - scrollbar position on Y axis * @param height - scrollbar height @@ -40,41 +38,35 @@ void elements_scrollbar_pos( uint16_t pos, uint16_t total); -/* - * Draw scrollbar on canvas. +/** Draw scrollbar on canvas. * width 3px, height equal to canvas height * @param pos - current element of total elements * @param total - total elements */ void elements_scrollbar(Canvas* canvas, uint16_t pos, uint16_t total); -/* - * Draw rounded frame +/** Draw rounded frame * @param x, y - top left corner coordinates * @param width, height - frame width and height */ void elements_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); -/* - * Draw button in left corner +/** Draw button in left corner * @param str - button text */ void elements_button_left(Canvas* canvas, const char* str); -/* - * Draw button in right corner +/** Draw button in right corner * @param str - button text */ void elements_button_right(Canvas* canvas, const char* str); -/* - * Draw button in center +/** Draw button in center * @param str - button text */ void elements_button_center(Canvas* canvas, const char* str); -/* - * Draw aligned multiline text +/** Draw aligned multiline text * @param x, y - coordinates based on align param * @param horizontal, vertical - aligment of multiline text * @param text - string (possible multiline) @@ -87,22 +79,19 @@ void elements_multiline_text_aligned( Align vertical, const char* text); -/* - * Draw multiline text +/** Draw multiline text * @param x, y - top left corner coordinates * @param text - string (possible multiline) */ void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* text); -/* - * Draw framed multiline text +/** Draw framed multiline text * @param x, y - top left corner coordinates * @param text - string (possible multiline) */ void elements_multiline_text_framed(Canvas* canvas, uint8_t x, uint8_t y, const char* text); -/* - * Draw slightly rounded frame +/** Draw slightly rounded frame * @param x, y - top left corner coordinates * @param width, height - size of frame */ @@ -113,8 +102,7 @@ void elements_slightly_rounded_frame( uint8_t width, uint8_t height); -/* - * Draw slightly rounded box +/** Draw slightly rounded box * @param x, y - top left corner coordinates * @param width, height - size of box */ @@ -125,8 +113,15 @@ void elements_slightly_rounded_box( uint8_t width, uint8_t height); -/* - * Trim string buffer to fit width in pixels +/** Draw bubble frame for text + * @param x - left x coordinates + * @param y - top y coordinate + * @param width - bubble width + * @param height - bubble height + */ +void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); + +/** Trim string buffer to fit width in pixels * @param string - string to trim * @param width - max width */ diff --git a/applications/gui/modules/empty_screen.c b/applications/gui/modules/empty_screen.c new file mode 100644 index 00000000..a887786d --- /dev/null +++ b/applications/gui/modules/empty_screen.c @@ -0,0 +1,34 @@ +#include "empty_screen.h" +#include + +struct EmptyScreen { + View* view; +}; + +static void empty_screen_view_draw_callback(Canvas* canvas, void* _model) { + canvas_clear(canvas); +} + +static bool empty_screen_view_input_callback(InputEvent* event, void* context) { + return false; +} + +EmptyScreen* empty_screen_alloc() { + EmptyScreen* empty_screen = furi_alloc(sizeof(EmptyScreen)); + empty_screen->view = view_alloc(); + view_set_context(empty_screen->view, empty_screen); + view_set_draw_callback(empty_screen->view, empty_screen_view_draw_callback); + view_set_input_callback(empty_screen->view, empty_screen_view_input_callback); + return empty_screen; +} + +void empty_screen_free(EmptyScreen* empty_screen) { + furi_assert(empty_screen); + view_free(empty_screen->view); + free(empty_screen); +} + +View* empty_screen_get_view(EmptyScreen* empty_screen) { + furi_assert(empty_screen); + return empty_screen->view; +} \ No newline at end of file diff --git a/applications/gui/modules/empty_screen.h b/applications/gui/modules/empty_screen.h new file mode 100644 index 00000000..a0b56219 --- /dev/null +++ b/applications/gui/modules/empty_screen.h @@ -0,0 +1,29 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Empty screen anonymous structure */ +typedef struct EmptyScreen EmptyScreen; + +/* Allocate and initialize empty screen + * This empty screen used to ask simple questions like Yes/ + */ +EmptyScreen* empty_screen_alloc(); + +/* Deinitialize and free empty screen + * @param empty_screen - Empty screen instance + */ +void empty_screen_free(EmptyScreen* empty_screen); + +/* Get empty screen view + * @param empty_screen - Empty screen instance + * @return View instance that can be used for embedding + */ +View* empty_screen_get_view(EmptyScreen* empty_screen); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/gui/modules/file_select.c b/applications/gui/modules/file_select.c index f41b95c9..4007fd95 100644 --- a/applications/gui/modules/file_select.c +++ b/applications/gui/modules/file_select.c @@ -1,7 +1,6 @@ #include "file_select.h" #include #include -#include #include #define FILENAME_COUNT 4 diff --git a/applications/gui/modules/menu.c b/applications/gui/modules/menu.c new file mode 100755 index 00000000..3fe709c7 --- /dev/null +++ b/applications/gui/modules/menu.c @@ -0,0 +1,211 @@ +#include "menu.h" + +#include +#include +#include + +struct Menu { + View* view; +}; + +typedef struct { + const char* label; + IconAnimation* icon; + uint32_t index; + MenuItemCallback callback; + void* callback_context; +} MenuItem; + +ARRAY_DEF(MenuItemArray, MenuItem, M_POD_OPLIST); + +typedef struct { + MenuItemArray_t items; + uint8_t position; +} MenuModel; + +static void menu_process_up(Menu* menu); +static void menu_process_down(Menu* menu); +static void menu_process_ok(Menu* menu); + +static void menu_draw_callback(Canvas* canvas, void* _model) { + MenuModel* model = _model; + + canvas_clear(canvas); + + uint8_t position = model->position; + size_t items_count = MenuItemArray_size(model->items); + if(items_count) { + MenuItem* item; + size_t shift_position; + // First line + canvas_set_font(canvas, FontSecondary); + shift_position = (0 + position + items_count - 1) % items_count; + item = MenuItemArray_get(model->items, shift_position); + if(item->icon) { + canvas_draw_icon_animation(canvas, 4, 3, item->icon); + icon_animation_stop(item->icon); + } + canvas_draw_str(canvas, 22, 14, item->label); + // Second line main + canvas_set_font(canvas, FontPrimary); + shift_position = (1 + position + items_count - 1) % items_count; + item = MenuItemArray_get(model->items, shift_position); + if(item->icon) { + canvas_draw_icon_animation(canvas, 4, 25, item->icon); + icon_animation_start(item->icon); + } + canvas_draw_str(canvas, 22, 36, item->label); + // Third line + canvas_set_font(canvas, FontSecondary); + shift_position = (2 + position + items_count - 1) % items_count; + item = MenuItemArray_get(model->items, shift_position); + if(item->icon) { + canvas_draw_icon_animation(canvas, 4, 47, item->icon); + icon_animation_stop(item->icon); + } + canvas_draw_str(canvas, 22, 58, item->label); + // Frame and scrollbar + elements_frame(canvas, 0, 21, 128 - 5, 21); + elements_scrollbar(canvas, position, items_count); + } else { + canvas_draw_str(canvas, 2, 32, "Empty"); + elements_scrollbar(canvas, 0, 0); + } +} + +static bool menu_input_callback(InputEvent* event, void* context) { + Menu* menu = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyUp) { + consumed = true; + menu_process_up(menu); + } else if(event->key == InputKeyDown) { + consumed = true; + menu_process_down(menu); + } else if(event->key == InputKeyOk) { + consumed = true; + menu_process_ok(menu); + } + } + + return consumed; +} + +Menu* menu_alloc() { + Menu* menu = furi_alloc(sizeof(Menu)); + menu->view = view_alloc(menu->view); + view_set_context(menu->view, menu); + view_allocate_model(menu->view, ViewModelTypeLocking, sizeof(MenuModel)); + view_set_draw_callback(menu->view, menu_draw_callback); + view_set_input_callback(menu->view, menu_input_callback); + + with_view_model( + menu->view, (MenuModel * model) { + MenuItemArray_init(model->items); + model->position = 0; + return true; + }); + + return menu; +} + +void menu_free(Menu* menu) { + furi_assert(menu); + with_view_model( + menu->view, (MenuModel * model) { + MenuItemArray_clear(model->items); + return true; + }); + view_free(menu->view); + free(menu); +} + +View* menu_get_view(Menu* menu) { + furi_assert(menu); + return (menu->view); +} + +void menu_add_item( + Menu* menu, + const char* label, + IconAnimation* icon, + uint32_t index, + MenuItemCallback callback, + void* context) { + furi_assert(menu); + furi_assert(label); + + MenuItem* item = NULL; + with_view_model( + menu->view, (MenuModel * model) { + item = MenuItemArray_push_new(model->items); + item->label = label; + item->icon = icon; + item->index = index; + item->callback = callback; + item->callback_context = context; + return true; + }); +} + +void menu_clean(Menu* menu) { + furi_assert(menu); + with_view_model( + menu->view, (MenuModel * model) { + MenuItemArray_clean(model->items); + model->position = 0; + return true; + }); +} + +void menu_set_selected_item(Menu* menu, uint32_t index) { + with_view_model( + menu->view, (MenuModel * model) { + if(index >= MenuItemArray_size(model->items)) { + return false; + } + + model->position = index; + return true; + }); +} + +static void menu_process_up(Menu* menu) { + with_view_model( + menu->view, (MenuModel * model) { + if(model->position > 0) { + model->position--; + } else { + model->position = MenuItemArray_size(model->items) - 1; + } + return true; + }); +} + +static void menu_process_down(Menu* menu) { + with_view_model( + menu->view, (MenuModel * model) { + if(model->position < MenuItemArray_size(model->items) - 1) { + model->position++; + } else { + model->position = 0; + } + return true; + }); +} + +static void menu_process_ok(Menu* menu) { + MenuItem* item = NULL; + with_view_model( + menu->view, (MenuModel * model) { + if(model->position < MenuItemArray_size(model->items)) { + item = MenuItemArray_get(model->items, model->position); + } + return true; + }); + if(item && item->callback) { + item->callback(item->callback_context, item->index); + } +} diff --git a/applications/gui/modules/menu.h b/applications/gui/modules/menu.h new file mode 100755 index 00000000..26c83c0c --- /dev/null +++ b/applications/gui/modules/menu.h @@ -0,0 +1,58 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Menu anonymous structure */ +typedef struct Menu Menu; +typedef void (*MenuItemCallback)(void* context, uint32_t index); + +/** Menu allocation and initialization + * @return Menu instance + */ +Menu* menu_alloc(); + +/** Free menu + * @param menu - Menu instance + */ +void menu_free(Menu* menu); + +/** Get Menu view + * @param menu - Menu instance + * @return View instance + */ +View* menu_get_view(Menu* menu); + +/** Add item to menu + * @param menu - Menu instance + * @param label - menu item string label + * @param icon - IconAnimation instance + * @param index - menu item index + * @param callback - MenuItemCallback instance + * @param context - pointer to context + */ +void menu_add_item( + Menu* menu, + const char* label, + IconAnimation* icon, + uint32_t index, + MenuItemCallback callback, + void* context); + +/** Clean menu + * Note: this function does not free menu instance + * @param menu - Menu instance + */ +void menu_clean(Menu* menu); + +/** Set current menu item + * @param submenu + * @param index + */ +void menu_set_selected_item(Menu* menu, uint32_t index); + +#ifdef __cplusplus +} +#endif diff --git a/applications/gui/modules/submenu.c b/applications/gui/modules/submenu.c index 7c12eebc..105ebd6a 100644 --- a/applications/gui/modules/submenu.c +++ b/applications/gui/modules/submenu.c @@ -1,23 +1,22 @@ #include "submenu.h" -#include "gui/canvas.h" + #include -#include #include -#include - -struct SubmenuItem { - const char* label; - uint32_t index; - SubmenuItemCallback callback; - void* callback_context; -}; - -ARRAY_DEF(SubmenuItemArray, SubmenuItem, M_POD_OPLIST); +#include struct Submenu { View* view; }; +typedef struct { + const char* label; + uint32_t index; + SubmenuItemCallback callback; + void* callback_context; +} SubmenuItem; + +ARRAY_DEF(SubmenuItemArray, SubmenuItem, M_POD_OPLIST); + typedef struct { SubmenuItemArray_t items; const char* header; @@ -149,7 +148,7 @@ View* submenu_get_view(Submenu* submenu) { return submenu->view; } -SubmenuItem* submenu_add_item( +void submenu_add_item( Submenu* submenu, const char* label, uint32_t index, @@ -168,8 +167,6 @@ SubmenuItem* submenu_add_item( item->callback_context = callback_context; return true; }); - - return item; } void submenu_clean(Submenu* submenu) { diff --git a/applications/gui/modules/submenu.h b/applications/gui/modules/submenu.h index 64cb9b96..50a9211e 100644 --- a/applications/gui/modules/submenu.h +++ b/applications/gui/modules/submenu.h @@ -7,7 +7,6 @@ extern "C" { /* Submenu anonymous structure */ typedef struct Submenu Submenu; -typedef struct SubmenuItem SubmenuItem; typedef void (*SubmenuItemCallback)(void* context, uint32_t index); /** @@ -36,9 +35,8 @@ View* submenu_get_view(Submenu* submenu); * @param index - menu item index, used for callback, may be the same with other items * @param callback - menu item callback * @param callback_context - menu item callback context - * @return SubmenuItem instance that can be used to modify or delete that item */ -SubmenuItem* submenu_add_item( +void submenu_add_item( Submenu* submenu, const char* label, uint32_t index, diff --git a/applications/gui/modules/text_input.c b/applications/gui/modules/text_input.c index 965f95ec..2b31f71b 100755 --- a/applications/gui/modules/text_input.c +++ b/applications/gui/modules/text_input.c @@ -369,15 +369,7 @@ TextInput* text_input_alloc() { view_set_draw_callback(text_input->view, text_input_view_draw_callback); view_set_input_callback(text_input->view, text_input_view_input_callback); - with_view_model( - text_input->view, (TextInputModel * model) { - model->text_buffer_size = 0; - model->header = ""; - model->selected_row = 0; - model->selected_column = 0; - model->clear_default_text = false; - return true; - }); + text_input_clean(text_input); return text_input; } @@ -388,6 +380,23 @@ void text_input_free(TextInput* text_input) { free(text_input); } +void text_input_clean(TextInput* text_input) { + furi_assert(text_input); + with_view_model( + text_input->view, (TextInputModel * model) { + model->text_buffer_size = 0; + model->header = ""; + model->selected_row = 0; + model->selected_column = 0; + model->clear_default_text = false; + model->text_buffer = NULL; + model->text_buffer_size = 0; + model->callback = NULL; + model->callback_context = NULL; + return true; + }); +} + View* text_input_get_view(TextInput* text_input) { furi_assert(text_input); return text_input->view; @@ -407,6 +416,11 @@ void text_input_set_result_callback( model->text_buffer = text_buffer; model->text_buffer_size = text_buffer_size; model->clear_default_text = clear_default_text; + if(text_buffer && text_buffer[0] != '\0') { + // Set focus on Save + model->selected_row = 2; + model->selected_column = 8; + } return true; }); } diff --git a/applications/gui/modules/text_input.h b/applications/gui/modules/text_input.h index d8e341cb..4ac7cd5a 100644 --- a/applications/gui/modules/text_input.h +++ b/applications/gui/modules/text_input.h @@ -9,32 +9,31 @@ extern "C" { typedef struct TextInput TextInput; typedef void (*TextInputCallback)(void* context); -/** - * @brief Allocate and initialize text input - * This text input is used to enter string - * +/** Allocate and initialize text input + * This text input is used to enter string + * @return TextInput instance */ TextInput* text_input_alloc(); -/** - * @brief Deinitialize and free text input - * - * @param text_input - Text input instance +/** Deinitialize and free text input + * @param text_input - TextInput instance */ void text_input_free(TextInput* text_input); -/** - * @brief Get text input view - * +/** Clean text input view + * Note: this function does not free memory * @param text_input - Text input instance + */ +void text_input_clean(TextInput* text_input); + +/** Get text input view + * @param text_input - TextInput instance * @return View instance that can be used for embedding */ View* text_input_get_view(TextInput* text_input); -/** - * @brief Set text input result callback - * - * @param text_input - Text input instance +/** Set text input result callback + * @param text_input - TextInput instance * @param callback - callback fn * @param callback_context - callback context * @param text_buffer - pointer to YOUR text buffer, that we going to modify @@ -49,10 +48,8 @@ void text_input_set_result_callback( size_t text_buffer_size, bool clear_default_text); -/** - * @brief Set text input header text - * - * @param text input - Text input instance +/** Set text input header text + * @param text_input - TextInput instance * @param text - text to be shown */ void text_input_set_header_text(TextInput* text_input, const char* text); diff --git a/applications/ibutton/helpers/key-emulator.cpp b/applications/ibutton/helpers/key-emulator.cpp index 09d775ea..7c8c5c97 100644 --- a/applications/ibutton/helpers/key-emulator.cpp +++ b/applications/ibutton/helpers/key-emulator.cpp @@ -144,7 +144,7 @@ void KeyEmulator::start_metakom_emulate(iButtonKey* key) { uint8_t* key_data = key->get_data(); // start pulse - pulse_data[0] = metakom_period_full * 4; + pulse_data[0] = metakom_period_full; // start triplet set_pulse_data_metakom(pd_index, metakom_period_zero); diff --git a/applications/ibutton/helpers/key-reader.cpp b/applications/ibutton/helpers/key-reader.cpp index 91ada058..fcb2a480 100644 --- a/applications/ibutton/helpers/key-reader.cpp +++ b/applications/ibutton/helpers/key-reader.cpp @@ -116,15 +116,11 @@ bool KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, u void KeyReader::start_comaparator(void) { // pulldown lf-rfid pins to prevent interference - // TODO open record - GpioPin rfid_pull_pin = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin}; - hal_gpio_init(&rfid_pull_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - hal_gpio_write(&rfid_pull_pin, false); + hal_gpio_init(&gpio_rfid_pull, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + hal_gpio_write(&gpio_rfid_pull, false); - // TODO open record - GpioPin rfid_out_pin = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; - hal_gpio_init(&rfid_out_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - hal_gpio_write(&rfid_out_pin, false); + hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + hal_gpio_write(&gpio_rfid_carrier_out, false); comparator_callback_pointer = cbc::obtain_connector(this, &KeyReader::comparator_trigger_callback); @@ -149,7 +145,7 @@ void KeyReader::comparator_trigger_callback(void* hcomp, void* comp_ctx) { _this->metakom_decoder.process_front( hal_gpio_get_rfid_in_level(), current_dwt_value - last_dwt_value); - last_dwt_value = DWT->CYCCNT; + last_dwt_value = current_dwt_value; } } diff --git a/applications/ibutton/helpers/pulse-sequencer.cpp b/applications/ibutton/helpers/pulse-sequencer.cpp index 21818a3b..328df7ac 100644 --- a/applications/ibutton/helpers/pulse-sequencer.cpp +++ b/applications/ibutton/helpers/pulse-sequencer.cpp @@ -54,7 +54,6 @@ void PulseSequencer::init_timer(uint32_t period) { Error_Handler(); } - HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn, 5, 0); HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn); hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); diff --git a/applications/ibutton/ibutton-cli.cpp b/applications/ibutton/ibutton-cli.cpp index 1fbccdab..3c26f5ec 100644 --- a/applications/ibutton/ibutton-cli.cpp +++ b/applications/ibutton/ibutton-cli.cpp @@ -248,6 +248,8 @@ void onewire_cli_search(Cli* cli) { printf("Search started\r\n"); onewire.start(); + furi_hal_power_enable_otg(); + while(!done) { if(onewire.search(address, true) != 1) { printf("Search finished\r\n"); @@ -263,6 +265,8 @@ void onewire_cli_search(Cli* cli) { } delay(100); } + + furi_hal_power_disable_otg(); onewire.stop(); } diff --git a/applications/ibutton/scene/ibutton-scene-save-name.cpp b/applications/ibutton/scene/ibutton-scene-save-name.cpp index 2fcd0804..65cd560a 100644 --- a/applications/ibutton/scene/ibutton-scene-save-name.cpp +++ b/applications/ibutton/scene/ibutton-scene-save-name.cpp @@ -48,8 +48,7 @@ bool iButtonSceneSaveName::on_event(iButtonApp* app, iButtonEvent* event) { void iButtonSceneSaveName::on_exit(iButtonApp* app) { TextInput* text_input = app->get_view_manager()->get_text_input(); - text_input_set_header_text(text_input, ""); - text_input_set_result_callback(text_input, NULL, NULL, NULL, 0, false); + text_input_clean(text_input); } void iButtonSceneSaveName::text_input_callback(void* context) { diff --git a/applications/loader/loader.c b/applications/loader/loader.c old mode 100644 new mode 100755 index 0ef11677..d121d139 --- a/applications/loader/loader.c +++ b/applications/loader/loader.c @@ -1,8 +1,11 @@ #include "loader_i.h" +#define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0) +#define LOADER_THREAD_FLAG_ALL (LOADER_THREAD_FLAG_SHOW_MENU) + static Loader* loader_instance = NULL; -static void loader_menu_callback(void* _ctx) { +static void loader_menu_callback(void* _ctx, uint32_t index) { const FlipperApplication* flipper_app = _ctx; furi_assert(flipper_app->app); @@ -27,6 +30,11 @@ static void loader_menu_callback(void* _ctx) { furi_thread_start(loader_instance->thread); } +static void loader_submenu_callback(void* context, uint32_t index) { + uint32_t view_id = (uint32_t)context; + view_dispatcher_switch_to_view(loader_instance->view_dispatcher, view_id); +} + static void loader_cli_callback(Cli* cli, string_t args, void* _ctx) { furi_assert(_ctx); const FlipperApplication* flipper_app = (FlipperApplication*)_ctx; @@ -60,6 +68,15 @@ bool loader_start(Loader* instance, const char* name, const char* args) { } } + if(!flipper_app) { + for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { + if(strcmp(FLIPPER_DEBUG_APPS[i].name, name) == 0) { + flipper_app = &FLIPPER_DEBUG_APPS[i]; + break; + } + } + } + if(!flipper_app) { FURI_LOG_E(LOADER_LOG_TAG, "Can't find application with name %s", name); return false; @@ -73,9 +90,11 @@ bool loader_start(Loader* instance, const char* name, const char* args) { } instance->current_app = flipper_app; + void* thread_args = NULL; if(args) { string_set_str(instance->args, args); string_strim(instance->args); + thread_args = (void*)string_get_cstr(instance->args); FURI_LOG_I(LOADER_LOG_TAG, "Start %s app with args: %s", name, args); } else { string_clean(instance->args); @@ -84,7 +103,7 @@ bool loader_start(Loader* instance, const char* name, const char* args) { furi_thread_set_name(instance->thread, flipper_app->name); furi_thread_set_stack_size(instance->thread, flipper_app->stack_size); - furi_thread_set_context(instance->thread, (void*)string_get_cstr(instance->args)); + furi_thread_set_context(instance->thread, thread_args); furi_thread_set_callback(instance->thread, flipper_app->app); return furi_thread_start(instance->thread); @@ -138,6 +157,14 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con } } +static uint32_t loader_hide_menu(void* context) { + return VIEW_NONE; +} + +static uint32_t loader_back_to_primary_menu(void* context) { + return LoaderMenuViewPrimary; +} + static Loader* loader_alloc() { Loader* instance = furi_alloc(sizeof(Loader)); @@ -150,10 +177,45 @@ static Loader* loader_alloc() { instance->mutex = osMutexNew(NULL); - instance->menu_vm = furi_record_open("menu"); - instance->cli = furi_record_open("cli"); + instance->loader_thread = osThreadGetId(); + + // Gui + instance->gui = furi_record_open("gui"); + instance->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_attach_to_gui( + instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); + // Primary menu + instance->primary_menu = menu_alloc(); + view_set_previous_callback(menu_get_view(instance->primary_menu), loader_hide_menu); + view_dispatcher_add_view( + instance->view_dispatcher, LoaderMenuViewPrimary, menu_get_view(instance->primary_menu)); + // Plugins menu + instance->plugins_menu = submenu_alloc(); + view_set_previous_callback( + submenu_get_view(instance->plugins_menu), loader_back_to_primary_menu); + view_dispatcher_add_view( + instance->view_dispatcher, + LoaderMenuViewPlugins, + submenu_get_view(instance->plugins_menu)); + // Debug menu + instance->debug_menu = submenu_alloc(); + view_set_previous_callback( + submenu_get_view(instance->debug_menu), loader_back_to_primary_menu); + view_dispatcher_add_view( + instance->view_dispatcher, LoaderMenuViewDebug, submenu_get_view(instance->debug_menu)); + // Settings menu + instance->settings_menu = submenu_alloc(); + view_set_previous_callback( + submenu_get_view(instance->settings_menu), loader_back_to_primary_menu); + view_dispatcher_add_view( + instance->view_dispatcher, + LoaderMenuViewSettings, + submenu_get_view(instance->settings_menu)); + + view_dispatcher_enable_queue(instance->view_dispatcher); + return instance; } @@ -162,133 +224,111 @@ static void loader_free(Loader* instance) { furi_record_close("cli"); - furi_record_close("menu"); - osMutexDelete(instance->mutex); string_clear(instance->args); furi_thread_free(instance->thread); + menu_free(loader_instance->primary_menu); + view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary); + submenu_free(loader_instance->plugins_menu); + view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPlugins); + submenu_free(loader_instance->debug_menu); + view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewDebug); + submenu_free(loader_instance->settings_menu); + view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewSettings); + view_dispatcher_free(loader_instance->view_dispatcher); + + furi_record_close("gui"); + free(instance); + instance = NULL; +} + +static void loader_add_cli_command(FlipperApplication* app) { + string_t cli_name; + string_init_printf(cli_name, "app_%s", app->name); + cli_add_command( + loader_instance->cli, + string_get_cstr(cli_name), + CliCommandFlagDefault, + loader_cli_callback, + app); + string_clear(cli_name); } static void loader_build_menu() { FURI_LOG_I(LOADER_LOG_TAG, "Building main menu"); - with_value_mutex( - loader_instance->menu_vm, (Menu * menu) { - for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { - // Add menu item - menu_item_add( - menu, - menu_item_alloc_function( - FLIPPER_APPS[i].name, - FLIPPER_APPS[i].icon ? icon_animation_alloc(FLIPPER_APPS[i].icon) : NULL, - loader_menu_callback, - (void*)&FLIPPER_APPS[i])); - - // Add cli command - string_t cli_name; - string_init_set_str(cli_name, "app_"); - string_cat_str(cli_name, FLIPPER_APPS[i].name); - cli_add_command( - loader_instance->cli, - string_get_cstr(cli_name), - CliCommandFlagDefault, - loader_cli_callback, - (void*)&FLIPPER_APPS[i]); - string_clear(cli_name); - } - }); + size_t i; + for(i = 0; i < FLIPPER_APPS_COUNT; i++) { + loader_add_cli_command((FlipperApplication*)&FLIPPER_APPS[i]); + menu_add_item( + loader_instance->primary_menu, + FLIPPER_APPS[i].name, + FLIPPER_APPS[i].icon ? icon_animation_alloc(FLIPPER_APPS[i].icon) : NULL, + i, + loader_menu_callback, + (void*)&FLIPPER_APPS[i]); + } + menu_add_item( + loader_instance->primary_menu, + "Plugins", + icon_animation_alloc(&A_Plugins_14), + i++, + loader_submenu_callback, + (void*)LoaderMenuViewPlugins); + menu_add_item( + loader_instance->primary_menu, + "Debug tools", + icon_animation_alloc(&A_Debug_14), + i++, + loader_submenu_callback, + (void*)LoaderMenuViewDebug); + menu_add_item( + loader_instance->primary_menu, + "Settings", + icon_animation_alloc(&A_Settings_14), + i++, + loader_submenu_callback, + (void*)LoaderMenuViewSettings); FURI_LOG_I(LOADER_LOG_TAG, "Building plugins menu"); - with_value_mutex( - loader_instance->menu_vm, (Menu * menu) { - MenuItem* menu_plugins = - menu_item_alloc_menu("Plugins", icon_animation_alloc(&A_Plugins_14)); - - for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) { - // Add menu item - menu_item_subitem_add( - menu_plugins, - menu_item_alloc_function( - FLIPPER_PLUGINS[i].name, - FLIPPER_PLUGINS[i].icon ? icon_animation_alloc(FLIPPER_PLUGINS[i].icon) : - NULL, - loader_menu_callback, - (void*)&FLIPPER_PLUGINS[i])); - - // Add cli command - string_t cli_name; - string_init_set_str(cli_name, "app_"); - string_cat_str(cli_name, FLIPPER_PLUGINS[i].name); - cli_add_command( - loader_instance->cli, - string_get_cstr(cli_name), - CliCommandFlagDefault, - loader_cli_callback, - (void*)&FLIPPER_PLUGINS[i]); - string_clear(cli_name); - } - - menu_item_add(menu, menu_plugins); - }); + for(i = 0; i < FLIPPER_PLUGINS_COUNT; i++) { + loader_add_cli_command((FlipperApplication*)&FLIPPER_PLUGINS[i]); + submenu_add_item( + loader_instance->plugins_menu, + FLIPPER_PLUGINS[i].name, + i, + loader_menu_callback, + (void*)&FLIPPER_PLUGINS[i]); + } FURI_LOG_I(LOADER_LOG_TAG, "Building debug menu"); - with_value_mutex( - loader_instance->menu_vm, (Menu * menu) { - MenuItem* menu_debug = - menu_item_alloc_menu("Debug tools", icon_animation_alloc(&A_Debug_14)); - - for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { - // Add menu item - menu_item_subitem_add( - menu_debug, - menu_item_alloc_function( - FLIPPER_DEBUG_APPS[i].name, - FLIPPER_DEBUG_APPS[i].icon ? - icon_animation_alloc(FLIPPER_DEBUG_APPS[i].icon) : - NULL, - loader_menu_callback, - (void*)&FLIPPER_DEBUG_APPS[i])); - - // Add cli command - string_t cli_name; - string_init_set_str(cli_name, "app_"); - string_cat_str(cli_name, FLIPPER_DEBUG_APPS[i].name); - cli_add_command( - loader_instance->cli, - string_get_cstr(cli_name), - CliCommandFlagDefault, - loader_cli_callback, - (void*)&FLIPPER_DEBUG_APPS[i]); - string_clear(cli_name); - } - - menu_item_add(menu, menu_debug); - }); + for(i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { + loader_add_cli_command((FlipperApplication*)&FLIPPER_DEBUG_APPS[i]); + submenu_add_item( + loader_instance->debug_menu, + FLIPPER_DEBUG_APPS[i].name, + i, + loader_menu_callback, + (void*)&FLIPPER_DEBUG_APPS[i]); + } FURI_LOG_I(LOADER_LOG_TAG, "Building settings menu"); - with_value_mutex( - loader_instance->menu_vm, (Menu * menu) { - MenuItem* menu_debug = - menu_item_alloc_menu("Settings", icon_animation_alloc(&A_Settings_14)); + for(i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { + submenu_add_item( + loader_instance->settings_menu, + FLIPPER_SETTINGS_APPS[i].name, + i, + loader_menu_callback, + (void*)&FLIPPER_SETTINGS_APPS[i]); + } +} - for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { - // Add menu item - menu_item_subitem_add( - menu_debug, - menu_item_alloc_function( - FLIPPER_SETTINGS_APPS[i].name, - FLIPPER_SETTINGS_APPS[i].icon ? - icon_animation_alloc(FLIPPER_SETTINGS_APPS[i].icon) : - NULL, - loader_menu_callback, - (void*)&FLIPPER_SETTINGS_APPS[i])); - } - - menu_item_add(menu, menu_debug); - }); +void loader_show_menu() { + furi_assert(loader_instance); + osThreadFlagsSet(loader_instance->loader_thread, LOADER_THREAD_FLAG_SHOW_MENU); } int32_t loader_srv(void* p) { @@ -300,15 +340,25 @@ int32_t loader_srv(void* p) { // Call on start hooks for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) { - (*FLIPPER_ON_SYSTEM_START[i])(); + FLIPPER_ON_SYSTEM_START[i](); } FURI_LOG_I(LOADER_LOG_TAG, "Started"); furi_record_create("loader", loader_instance); +#ifdef LOADER_AUTOSTART + loader_start(loader_instance, LOADER_AUTOSTART, NULL); +#endif + while(1) { - osThreadSuspend(osThreadGetId()); + uint32_t flags = osThreadFlagsWait(LOADER_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever); + if(flags & LOADER_THREAD_FLAG_SHOW_MENU) { + menu_set_selected_item(loader_instance->primary_menu, 0); + view_dispatcher_switch_to_view( + loader_instance->view_dispatcher, LoaderMenuViewPrimary); + view_dispatcher_run(loader_instance->view_dispatcher); + } } loader_free(loader_instance); diff --git a/applications/loader/loader.h b/applications/loader/loader.h index 5e81e927..568cfaa3 100644 --- a/applications/loader/loader.h +++ b/applications/loader/loader.h @@ -18,3 +18,6 @@ bool loader_lock(Loader* instance); /** Unlock application start */ void loader_unlock(Loader* instance); + +/** Show primary loader */ +void loader_show_menu(); diff --git a/applications/loader/loader_i.h b/applications/loader/loader_i.h index f5340434..8d44911f 100644 --- a/applications/loader/loader_i.h +++ b/applications/loader/loader_i.h @@ -3,20 +3,39 @@ #include #include #include -#include -#include + +#include + +#include +#include + #include #include #define LOADER_LOG_TAG "loader" struct Loader { + osThreadId_t loader_thread; FuriThread* thread; const FlipperApplication* current_app; string_t args; Cli* cli; - ValueMutex* menu_vm; + Gui* gui; + + ViewDispatcher* view_dispatcher; + Menu* primary_menu; + Submenu* plugins_menu; + Submenu* debug_menu; + Submenu* settings_menu; + size_t free_heap_size; osMutexId_t mutex; volatile uint8_t lock_semaphore; }; + +typedef enum { + LoaderMenuViewPrimary, + LoaderMenuViewPlugins, + LoaderMenuViewDebug, + LoaderMenuViewSettings, +} LoaderMenuView; diff --git a/applications/menu/menu.c b/applications/menu/menu.c deleted file mode 100644 index 544a8821..00000000 --- a/applications/menu/menu.c +++ /dev/null @@ -1,337 +0,0 @@ -#include "menu.h" -#include -#include - -#include -#include -#include - -#include "menu_event.h" -#include "menu_item.h" -#include - -struct Menu { - MenuEvent* event; - - // GUI - Gui* gui; - ViewPort* view_port; - IconAnimation* icon; - - // State - MenuItem* root; - MenuItem* settings; - MenuItem* current; -}; - -void menu_view_port_callback(Canvas* canvas, void* context); - -ValueMutex* menu_init() { - Menu* menu = furi_alloc(sizeof(Menu)); - - // Event dispatcher - menu->event = menu_event_alloc(); - - ValueMutex* menu_mutex = furi_alloc(sizeof(ValueMutex)); - if(menu_mutex == NULL || !init_mutex(menu_mutex, menu, sizeof(Menu))) { - printf("[menu_task] cannot create menu mutex\r\n"); - furi_crash(NULL); - } - - // OpenGui record - menu->gui = furi_record_open("gui"); - - // Allocate and configure view_port - menu->view_port = view_port_alloc(); - - // Open GUI and register fullscreen view_port - gui_add_view_port(menu->gui, menu->view_port, GuiLayerFullscreen); - - view_port_enabled_set(menu->view_port, false); - view_port_draw_callback_set(menu->view_port, menu_view_port_callback, menu_mutex); - view_port_input_callback_set(menu->view_port, menu_event_input_callback, menu->event); - - return menu_mutex; -} - -void menu_build_main(Menu* menu) { - furi_assert(menu); - // Root point - menu->root = menu_item_alloc_menu(NULL, NULL); -} - -void menu_item_add(Menu* menu, MenuItem* item) { - menu_item_subitem_add(menu->root, item); -} - -void menu_settings_item_add(Menu* menu, MenuItem* item) { - menu_item_subitem_add(menu->settings, item); -} - -void menu_draw_primary(Menu* menu, Canvas* canvas) { - size_t position = menu_item_get_position(menu->current); - MenuItemArray_t* items = menu_item_get_subitems(menu->current); - size_t items_count = MenuItemArray_size(*items); - if(items_count) { - MenuItem* item; - size_t shift_position; - // First line - canvas_set_font(canvas, FontSecondary); - shift_position = (0 + position + items_count - 1) % (MenuItemArray_size(*items)); - item = *MenuItemArray_get(*items, shift_position); - canvas_draw_icon_animation(canvas, 4, 3, menu_item_get_icon(item)); - canvas_draw_str(canvas, 22, 14, menu_item_get_label(item)); - // Second line main - canvas_set_font(canvas, FontPrimary); - shift_position = (1 + position + items_count - 1) % (MenuItemArray_size(*items)); - item = *MenuItemArray_get(*items, shift_position); - canvas_draw_icon_animation(canvas, 4, 25, menu_item_get_icon(item)); - canvas_draw_str(canvas, 22, 36, menu_item_get_label(item)); - // Third line - canvas_set_font(canvas, FontSecondary); - shift_position = (2 + position + items_count - 1) % (MenuItemArray_size(*items)); - item = *MenuItemArray_get(*items, shift_position); - canvas_draw_icon_animation(canvas, 4, 47, menu_item_get_icon(item)); - canvas_draw_str(canvas, 22, 58, menu_item_get_label(item)); - // Frame and scrollbar - // elements_frame(canvas, 0, 0, 128 - 5, 21); - elements_frame(canvas, 0, 21, 128 - 5, 21); - // elements_frame(canvas, 0, 42, 128 - 5, 21); - elements_scrollbar(canvas, position, items_count); - } else { - canvas_draw_str(canvas, 2, 32, "Empty"); - elements_scrollbar(canvas, 0, 0); - } -} - -void menu_draw_secondary(Menu* menu, Canvas* canvas) { - size_t position = 0; - size_t selected_position = menu_item_get_position(menu->current); - size_t window_position = menu_item_get_window_position(menu->current); - MenuItemArray_t* items = menu_item_get_subitems(menu->current); - const uint8_t items_on_screen = 4; - const uint8_t item_height = 16; - const uint8_t item_width = 123; - size_t items_count = MenuItemArray_size(*items); - MenuItemArray_it_t it; - - canvas_set_font(canvas, FontSecondary); - for(MenuItemArray_it(it, *items); !MenuItemArray_end_p(it); MenuItemArray_next(it)) { - size_t item_position = position - window_position; - - if(item_position < items_on_screen) { - if(position == selected_position) { - canvas_set_color(canvas, ColorBlack); - elements_slightly_rounded_box( - canvas, 0, (item_position * item_height) + 1, item_width, item_height - 2); - canvas_set_color(canvas, ColorWhite); - } else { - canvas_set_color(canvas, ColorBlack); - } - canvas_draw_str( - canvas, - 6, - (item_position * item_height) + item_height - 4, - menu_item_get_label(*MenuItemArray_ref(it))); - } - - position++; - } - - elements_scrollbar(canvas, selected_position, items_count); -} - -void menu_view_port_callback(Canvas* canvas, void* context) { - furi_assert(canvas); - furi_assert(context); - - Menu* menu = acquire_mutex((ValueMutex*)context, 100); // wait 10 ms to get mutex - if(menu == NULL) return; // redraw fail - - furi_assert(menu->current); - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // if top level - if(menu_item_get_parent(menu->current) == NULL) { - menu_draw_primary(menu, canvas); - } else { - menu_draw_secondary(menu, canvas); - } - - release_mutex((ValueMutex*)context, menu); -} - -void menu_set_icon(Menu* menu, IconAnimation* icon) { - furi_assert(menu); - - if(menu->icon) { - icon_animation_stop(menu->icon); - } - - menu->icon = icon; - - if(menu->icon) { - icon_animation_start(menu->icon); - } -} - -void menu_update(Menu* menu) { - furi_assert(menu); - - if(menu->current) { - size_t position = menu_item_get_position(menu->current); - MenuItemArray_t* items = menu_item_get_subitems(menu->current); - size_t items_count = MenuItemArray_size(*items); - if(items_count) { - MenuItem* item = *MenuItemArray_get(*items, position); - menu_set_icon(menu, menu_item_get_icon(item)); - } - } - - menu_event_activity_notify(menu->event); - view_port_update(menu->view_port); -} - -void menu_up(Menu* menu) { - furi_assert(menu); - size_t position = menu_item_get_position(menu->current); - size_t window_position = menu_item_get_window_position(menu->current); - MenuItemArray_t* items = menu_item_get_subitems(menu->current); - - const uint8_t items_on_screen = 4; - - if(position > 0) { - position--; - if(((position - window_position) < 1) && window_position > 0) { - window_position--; - } - } else { - position = MenuItemArray_size(*items) - 1; - if(position > (items_on_screen - 1)) { - window_position = position - (items_on_screen - 1); - } - } - - menu_item_set_position(menu->current, position); - menu_item_set_window_position(menu->current, window_position); - menu_update(menu); -} - -void menu_down(Menu* menu) { - furi_assert(menu); - size_t position = menu_item_get_position(menu->current); - size_t window_position = menu_item_get_window_position(menu->current); - MenuItemArray_t* items = menu_item_get_subitems(menu->current); - - const uint8_t items_on_screen = 4; - if(position < (MenuItemArray_size(*items) - 1)) { - position++; - if((position - window_position) > (items_on_screen - 2) && - window_position < (MenuItemArray_size(*items) - items_on_screen)) { - window_position++; - } - } else { - position = 0; - window_position = 0; - } - - menu_item_set_position(menu->current, position); - menu_item_set_window_position(menu->current, window_position); - menu_update(menu); -} - -void menu_ok(Menu* menu) { - furi_assert(menu); - - if(!menu->current) { - view_port_enabled_set(menu->view_port, true); - menu->current = menu->root; - menu_item_set_position(menu->current, 0); - menu_item_set_window_position(menu->current, 0); - menu_update(menu); - return; - } - - MenuItemArray_t* items = menu_item_get_subitems(menu->current); - if(!items || MenuItemArray_size(*items) == 0) { - return; - } - - size_t position = menu_item_get_position(menu->current); - MenuItem* item = *MenuItemArray_get(*items, position); - MenuItemType type = menu_item_get_type(item); - - if(type == MenuItemTypeMenu) { - menu->current = item; - menu_item_set_position(menu->current, 0); - menu_item_set_window_position(menu->current, 0); - menu_update(menu); - } else if(type == MenuItemTypeFunction) { - menu_item_function_call(item); - gui_send_view_port_back(menu->gui, menu->view_port); - } -} - -void menu_back(Menu* menu) { - furi_assert(menu); - MenuItem* parent = menu_item_get_parent(menu->current); - if(parent) { - menu->current = parent; - menu_update(menu); - } else { - menu_exit(menu); - } -} - -void menu_exit(Menu* menu) { - furi_assert(menu); - view_port_enabled_set(menu->view_port, false); - menu->current = NULL; - menu_update(menu); -} - -int32_t menu_srv(void* p) { - ValueMutex* menu_mutex = menu_init(); - - MenuEvent* menu_event = NULL; - { - Menu* menu = acquire_mutex_block(menu_mutex); - furi_check(menu); - - menu_build_main(menu); - - // immutable thread-safe object - menu_event = menu->event; - - release_mutex(menu_mutex, menu); - } - - furi_record_create("menu", menu_mutex); - - while(1) { - MenuMessage m = menu_event_next(menu_event); - - Menu* menu = acquire_mutex_block(menu_mutex); - - if(!menu->current && m.type != MenuMessageTypeOk) { - } else if(m.type == MenuMessageTypeUp) { - menu_up(menu); - } else if(m.type == MenuMessageTypeDown) { - menu_down(menu); - } else if(m.type == MenuMessageTypeOk) { - menu_ok(menu); - } else if(m.type == MenuMessageTypeBack) { - menu_back(menu); - } else if(m.type == MenuMessageTypeIdle) { - menu_exit(menu); - } else { - // TODO: fail somehow? - } - - release_mutex(menu_mutex, menu); - } - - return 0; -} diff --git a/applications/menu/menu.h b/applications/menu/menu.h deleted file mode 100644 index f8607fda..00000000 --- a/applications/menu/menu.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "menu/menu_item.h" - -typedef struct Menu Menu; -typedef struct MenuItem MenuItem; - -// Add menu item to root menu -void menu_item_add(Menu* menu, MenuItem* item); - -// Add menu item to settings menu -void menu_settings_item_add(Menu* menu, MenuItem* item); - -// Menu controls -void menu_up(Menu* menu); -void menu_down(Menu* menu); -void menu_ok(Menu* menu); -void menu_back(Menu* menu); -void menu_exit(Menu* menu); diff --git a/applications/menu/menu_event.c b/applications/menu/menu_event.c deleted file mode 100644 index e39b83de..00000000 --- a/applications/menu/menu_event.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "menu_event.h" - -#include -#include - -#include - -#define MENU_MESSAGE_MQUEUE_SIZE 8 - -struct MenuEvent { - osMessageQueueId_t mqueue; -}; - -void MenuEventimeout_callback(void* arg) { - MenuEvent* menu_event = arg; - MenuMessage message; - message.type = MenuMessageTypeIdle; - osMessageQueuePut(menu_event->mqueue, &message, 0, osWaitForever); -} - -MenuEvent* menu_event_alloc() { - MenuEvent* menu_event = furi_alloc(sizeof(MenuEvent)); - menu_event->mqueue = osMessageQueueNew(MENU_MESSAGE_MQUEUE_SIZE, sizeof(MenuMessage), NULL); - furi_check(menu_event->mqueue); - return menu_event; -} - -void menu_event_free(MenuEvent* menu_event) { - furi_assert(menu_event); - furi_check(osMessageQueueDelete(menu_event->mqueue) == osOK); - free(menu_event); -} - -void menu_event_activity_notify(MenuEvent* menu_event) { - furi_assert(menu_event); -} - -MenuMessage menu_event_next(MenuEvent* menu_event) { - furi_assert(menu_event); - MenuMessage message; - while(osMessageQueueGet(menu_event->mqueue, &message, NULL, osWaitForever) != osOK) { - }; - return message; -} - -void menu_event_input_callback(InputEvent* input_event, void* context) { - MenuEvent* menu_event = context; - MenuMessage message; - - if(input_event->type != InputTypeShort) return; - - if(input_event->key == InputKeyUp) { - message.type = MenuMessageTypeUp; - } else if(input_event->key == InputKeyDown) { - message.type = MenuMessageTypeDown; - } else if(input_event->key == InputKeyOk) { - message.type = MenuMessageTypeOk; - } else if(input_event->key == InputKeyBack) { - message.type = MenuMessageTypeBack; - } else { - message.type = MenuMessageTypeUnknown; - } - - osMessageQueuePut(menu_event->mqueue, &message, 0, osWaitForever); -} \ No newline at end of file diff --git a/applications/menu/menu_event.h b/applications/menu/menu_event.h deleted file mode 100644 index 031b8f4e..00000000 --- a/applications/menu/menu_event.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include - -typedef enum { - MenuMessageTypeUp = 0x00, - MenuMessageTypeDown = 0x01, - MenuMessageTypeLeft = 0x02, - MenuMessageTypeRight = 0x03, - MenuMessageTypeOk = 0x04, - MenuMessageTypeBack = 0x05, - MenuMessageTypeIdle = 0x06, - MenuMessageTypeUnknown = 0xFF, -} MenuMessageType; - -typedef struct { - MenuMessageType type; - void* data; -} MenuMessage; - -typedef struct MenuEvent MenuEvent; - -MenuEvent* menu_event_alloc(); - -void menu_event_free(MenuEvent* menu_event); - -void menu_event_activity_notify(MenuEvent* menu_event); - -MenuMessage menu_event_next(MenuEvent* menu_event); - -void menu_event_input_callback(InputEvent* input_event, void* context); diff --git a/applications/menu/menu_item.c b/applications/menu/menu_item.c deleted file mode 100644 index c87505f0..00000000 --- a/applications/menu/menu_item.c +++ /dev/null @@ -1,135 +0,0 @@ -#include "menu_item.h" -#include -#include -#include - -struct MenuItem { - MenuItemType type; - - const char* label; - IconAnimation* icon; - - size_t position; - size_t window_position; - MenuItem* parent; - void* data; - - // callback related - MenuItemCallback callback; - void* callback_context; -}; - -MenuItem* menu_item_alloc() { - MenuItem* menu_item = furi_alloc(sizeof(MenuItem)); - return menu_item; -} - -MenuItem* menu_item_alloc_menu(const char* label, IconAnimation* icon) { - MenuItem* menu_item = menu_item_alloc(); - - menu_item->type = MenuItemTypeMenu; - menu_item->label = label; - menu_item->icon = icon; - - MenuItemArray_t* items = furi_alloc(sizeof(MenuItemArray_t)); - MenuItemArray_init(*items); - menu_item->data = items; - - return menu_item; -} - -MenuItem* menu_item_alloc_function( - const char* label, - IconAnimation* icon, - MenuItemCallback callback, - void* context) { - MenuItem* menu_item = menu_item_alloc(); - - menu_item->type = MenuItemTypeFunction; - menu_item->label = label; - menu_item->icon = icon; - menu_item->callback = callback; - menu_item->callback_context = context; - menu_item->parent = NULL; - - return menu_item; -} - -void menu_item_release(MenuItem* menu_item) { - furi_assert(menu_item); - if(menu_item->type == MenuItemTypeMenu) { - //TODO: iterate and release - free(menu_item->data); - } - free(menu_item); -} - -MenuItem* menu_item_get_parent(MenuItem* menu_item) { - furi_assert(menu_item); - return menu_item->parent; -} - -void menu_item_subitem_add(MenuItem* menu_item, MenuItem* sub_item) { - furi_assert(menu_item); - furi_check(menu_item->type == MenuItemTypeMenu); - MenuItemArray_t* items = menu_item->data; - sub_item->parent = menu_item; - MenuItemArray_push_back(*items, sub_item); -} - -uint8_t menu_item_get_type(MenuItem* menu_item) { - furi_assert(menu_item); - return menu_item->type; -} - -void menu_item_set_position(MenuItem* menu_item, size_t position) { - furi_assert(menu_item); - menu_item->position = position; -} - -size_t menu_item_get_position(MenuItem* menu_item) { - furi_assert(menu_item); - return menu_item->position; -} - -void menu_item_set_window_position(MenuItem* menu_item, size_t window_position) { - furi_assert(menu_item); - menu_item->window_position = window_position; -} - -size_t menu_item_get_window_position(MenuItem* menu_item) { - furi_assert(menu_item); - return menu_item->window_position; -} - -void menu_item_set_label(MenuItem* menu_item, const char* label) { - furi_assert(menu_item); - menu_item->label = label; -} - -const char* menu_item_get_label(MenuItem* menu_item) { - furi_assert(menu_item); - return menu_item->label; -} - -void menu_item_set_icon(MenuItem* menu_item, IconAnimation* icon) { - furi_assert(menu_item); - menu_item->icon = icon; -} - -IconAnimation* menu_item_get_icon(MenuItem* menu_item) { - furi_assert(menu_item); - return menu_item->icon; -} - -MenuItemArray_t* menu_item_get_subitems(MenuItem* menu_item) { - furi_assert(menu_item); - furi_check(menu_item->type == MenuItemTypeMenu); - return menu_item->data; -} - -void menu_item_function_call(MenuItem* menu_item) { - furi_assert(menu_item); - furi_check(menu_item->type == MenuItemTypeFunction); - if(menu_item->callback) menu_item->callback(menu_item->callback_context); -} diff --git a/applications/menu/menu_item.h b/applications/menu/menu_item.h deleted file mode 100644 index 336f58a3..00000000 --- a/applications/menu/menu_item.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include -#include - -typedef enum { - MenuItemTypeMenu = 0x00, - MenuItemTypeFunction = 0x01, -} MenuItemType; - -typedef struct MenuItem MenuItem; -typedef void (*MenuItemCallback)(void* context); - -ARRAY_DEF(MenuItemArray, MenuItem*, M_PTR_OPLIST); - -MenuItem* menu_item_alloc_menu(const char* label, IconAnimation* icon); - -MenuItem* menu_item_alloc_function( - const char* label, - IconAnimation* icon, - MenuItemCallback callback, - void* context); - -void menu_item_release(MenuItem* menu_item); - -MenuItem* menu_item_get_parent(MenuItem* menu_item); - -void menu_item_subitem_add(MenuItem* menu_item, MenuItem* sub_item); - -MenuItemType menu_item_get_type(MenuItem* menu_item); - -void menu_item_set_position(MenuItem* menu_item, size_t position); -size_t menu_item_get_position(MenuItem* menu_item); - -void menu_item_set_window_position(MenuItem* menu_item, size_t window_position); -size_t menu_item_get_window_position(MenuItem* menu_item); - -void menu_item_set_label(MenuItem* menu_item, const char* label); -const char* menu_item_get_label(MenuItem* menu_item); - -void menu_item_set_icon(MenuItem* menu_item, IconAnimation* icon); -IconAnimation* menu_item_get_icon(MenuItem* menu_item); - -MenuItemArray_t* menu_item_get_subitems(MenuItem* menu_item); - -void menu_item_function_call(MenuItem* menu_item); diff --git a/applications/nfc/nfc_cli.c b/applications/nfc/nfc_cli.c index 0326048a..22325103 100755 --- a/applications/nfc/nfc_cli.c +++ b/applications/nfc/nfc_cli.c @@ -27,7 +27,7 @@ void nfc_cli_detect(Cli* cli, string_t args, void* context) { if(dev_cnt > 0) { printf("Found %d devices\r\n", dev_cnt); for(uint8_t i = 0; i < dev_cnt; i++) { - printf("%d found: %s ", i + 1, nfc_get_dev_type(dev_list[i].type)); + printf("%d found: %s ", i + 1, nfc_get_rfal_type(dev_list[i].type)); if(dev_list[i].type == RFAL_NFC_LISTEN_TYPE_NFCA) { printf("type: %s, ", nfc_get_nfca_type(dev_list[i].dev.nfca.type)); } diff --git a/applications/nfc/nfc_types.h b/applications/nfc/nfc_types.h index 46899b6d..6c37076a 100644 --- a/applications/nfc/nfc_types.h +++ b/applications/nfc/nfc_types.h @@ -6,19 +6,33 @@ #include #include "nfc_worker.h" -static inline const char* nfc_get_dev_type(rfalNfcDevType type) { +static inline const char* nfc_get_rfal_type(rfalNfcDevType type) { if(type == RFAL_NFC_LISTEN_TYPE_NFCA) { - return "NFC-A may be:"; + return "NFC-A"; } else if(type == RFAL_NFC_LISTEN_TYPE_NFCB) { - return "NFC-B may be:"; + return "NFC-B"; } else if(type == RFAL_NFC_LISTEN_TYPE_NFCF) { - return "NFC-F may be:"; + return "NFC-F"; } else if(type == RFAL_NFC_LISTEN_TYPE_NFCV) { - return "NFC-V may be:"; + return "NFC-V"; } else if(type == RFAL_NFC_LISTEN_TYPE_ST25TB) { - return "NFC-ST25TB may be:"; + return "NFC-ST25TB"; } else if(type == RFAL_NFC_LISTEN_TYPE_AP2P) { - return "NFC-AP2P may be:"; + return "NFC-AP2P"; + } else { + return "Unknown"; + } +} + +static inline const char* nfc_get_dev_type(NfcDeviceType type) { + if(type == NfcDeviceNfca) { + return "NFC-A may be:"; + } else if(type == NfcDeviceNfcb) { + return "NFC-B may be:"; + } else if(type == NfcDeviceNfcf) { + return "NFC-F may be:"; + } else if(type == NfcDeviceNfcv) { + return "NFC-V may be:"; } else { return "Unknown"; } diff --git a/applications/nfc/scenes/nfc_scene_card_menu.c b/applications/nfc/scenes/nfc_scene_card_menu.c index f58c212e..dbaefe4f 100755 --- a/applications/nfc/scenes/nfc_scene_card_menu.c +++ b/applications/nfc/scenes/nfc_scene_card_menu.c @@ -13,7 +13,7 @@ void nfc_scene_card_menu_submenu_callback(void* context, uint32_t index) { view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } -const void nfc_scene_card_menu_on_enter(void* context) { +void nfc_scene_card_menu_on_enter(void* context) { Nfc* nfc = (Nfc*)context; Submenu* submenu = nfc->submenu; @@ -41,7 +41,7 @@ const void nfc_scene_card_menu_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } -const bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -78,7 +78,7 @@ const bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) return false; } -const void nfc_scene_card_menu_on_exit(void* context) { +void nfc_scene_card_menu_on_exit(void* context) { Nfc* nfc = (Nfc*)context; submenu_clean(nfc->submenu); diff --git a/applications/nfc/scenes/nfc_scene_delete.c b/applications/nfc/scenes/nfc_scene_delete.c index 5c6fe254..993e1e37 100755 --- a/applications/nfc/scenes/nfc_scene_delete.c +++ b/applications/nfc/scenes/nfc_scene_delete.c @@ -66,7 +66,7 @@ void nfc_scene_delete_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } -const bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -85,7 +85,7 @@ const bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { return false; } -const void nfc_scene_delete_on_exit(void* context) { +void nfc_scene_delete_on_exit(void* context) { Nfc* nfc = (Nfc*)context; widget_clear(nfc->widget); diff --git a/applications/nfc/scenes/nfc_scene_delete_success.c b/applications/nfc/scenes/nfc_scene_delete_success.c index 51328a0a..96e70693 100755 --- a/applications/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/nfc/scenes/nfc_scene_delete_success.c @@ -7,7 +7,7 @@ void nfc_scene_delete_success_popup_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SAVE_SUCCESS_CUSTOM_EVENT); } -const void nfc_scene_delete_success_on_enter(void* context) { +void nfc_scene_delete_success_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -21,7 +21,7 @@ const void nfc_scene_delete_success_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); } -const bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -33,7 +33,7 @@ const bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent ev return false; } -const void nfc_scene_delete_success_on_exit(void* context) { +void nfc_scene_delete_success_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Clear view diff --git a/applications/nfc/scenes/nfc_scene_device_info.c b/applications/nfc/scenes/nfc_scene_device_info.c index 43082dcf..0b0067c0 100755 --- a/applications/nfc/scenes/nfc_scene_device_info.c +++ b/applications/nfc/scenes/nfc_scene_device_info.c @@ -127,7 +127,7 @@ void nfc_scene_device_info_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } -const bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDeviceInfo); @@ -162,7 +162,7 @@ const bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event return consumed; } -const void nfc_scene_device_info_on_exit(void* context) { +void nfc_scene_device_info_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Clear Custom Widget diff --git a/applications/nfc/scenes/nfc_scene_emulate_apdu_sequence.c b/applications/nfc/scenes/nfc_scene_emulate_apdu_sequence.c index 70288054..b08660b3 100644 --- a/applications/nfc/scenes/nfc_scene_emulate_apdu_sequence.c +++ b/applications/nfc/scenes/nfc_scene_emulate_apdu_sequence.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" -const void nfc_scene_emulate_apdu_sequence_on_enter(void* context) { +void nfc_scene_emulate_apdu_sequence_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -14,7 +14,7 @@ const void nfc_scene_emulate_apdu_sequence_on_enter(void* context) { nfc_worker_start(nfc->worker, NfcWorkerStateEmulateApdu, &nfc->dev.dev_data, NULL, nfc); } -const bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeTick) { @@ -24,7 +24,7 @@ const bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerE return false; } -const void nfc_scene_emulate_apdu_sequence_on_exit(void* context) { +void nfc_scene_emulate_apdu_sequence_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Stop worker diff --git a/applications/nfc/scenes/nfc_scene_emulate_mifare_ul.c b/applications/nfc/scenes/nfc_scene_emulate_mifare_ul.c index d1d52bef..cba09437 100755 --- a/applications/nfc/scenes/nfc_scene_emulate_mifare_ul.c +++ b/applications/nfc/scenes/nfc_scene_emulate_mifare_ul.c @@ -9,7 +9,7 @@ void nfc_emulate_mifare_ul_worker_callback(void* context) { nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_CHANGED); } -const void nfc_scene_emulate_mifare_ul_on_enter(void* context) { +void nfc_scene_emulate_mifare_ul_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -30,7 +30,7 @@ const void nfc_scene_emulate_mifare_ul_on_enter(void* context) { nfc); } -const bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; bool consumed = false; @@ -52,7 +52,7 @@ const bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent return consumed; } -const void nfc_scene_emulate_mifare_ul_on_exit(void* context) { +void nfc_scene_emulate_mifare_ul_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Clear view diff --git a/applications/nfc/scenes/nfc_scene_emulate_uid.c b/applications/nfc/scenes/nfc_scene_emulate_uid.c index 3810bec4..ccd27de5 100755 --- a/applications/nfc/scenes/nfc_scene_emulate_uid.c +++ b/applications/nfc/scenes/nfc_scene_emulate_uid.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" -const void nfc_scene_emulate_uid_on_enter(void* context) { +void nfc_scene_emulate_uid_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -35,7 +35,7 @@ const void nfc_scene_emulate_uid_on_enter(void* context) { nfc_worker_start(nfc->worker, NfcWorkerStateEmulate, &nfc->dev.dev_data, NULL, nfc); } -const bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeTick) { @@ -45,7 +45,7 @@ const bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event return false; } -const void nfc_scene_emulate_uid_on_exit(void* context) { +void nfc_scene_emulate_uid_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Stop worker diff --git a/applications/nfc/scenes/nfc_scene_file_select.c b/applications/nfc/scenes/nfc_scene_file_select.c index e75872a3..67894fbf 100755 --- a/applications/nfc/scenes/nfc_scene_file_select.c +++ b/applications/nfc/scenes/nfc_scene_file_select.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" -const void nfc_scene_file_select_on_enter(void* context) { +void nfc_scene_file_select_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Process file_select return if(nfc_file_select(&nfc->dev)) { @@ -10,9 +10,9 @@ const void nfc_scene_file_select_on_enter(void* context) { } } -const bool nfc_scene_file_select_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_file_select_on_event(void* context, SceneManagerEvent event) { return false; } -const void nfc_scene_file_select_on_exit(void* context) { +void nfc_scene_file_select_on_exit(void* context) { } diff --git a/applications/nfc/scenes/nfc_scene_mifare_ul_menu.c b/applications/nfc/scenes/nfc_scene_mifare_ul_menu.c index 70716618..5c31ab2c 100755 --- a/applications/nfc/scenes/nfc_scene_mifare_ul_menu.c +++ b/applications/nfc/scenes/nfc_scene_mifare_ul_menu.c @@ -11,7 +11,7 @@ void nfc_scene_mifare_ul_menu_submenu_callback(void* context, uint32_t index) { view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } -const void nfc_scene_mifare_ul_menu_on_enter(void* context) { +void nfc_scene_mifare_ul_menu_on_enter(void* context) { Nfc* nfc = (Nfc*)context; Submenu* submenu = nfc->submenu; @@ -25,7 +25,7 @@ const void nfc_scene_mifare_ul_menu_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } -const bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -49,7 +49,7 @@ const bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent ev return false; } -const void nfc_scene_mifare_ul_menu_on_exit(void* context) { +void nfc_scene_mifare_ul_menu_on_exit(void* context) { Nfc* nfc = (Nfc*)context; submenu_clean(nfc->submenu); diff --git a/applications/nfc/scenes/nfc_scene_not_implemented.c b/applications/nfc/scenes/nfc_scene_not_implemented.c index f23bbe4a..3167f9c0 100644 --- a/applications/nfc/scenes/nfc_scene_not_implemented.c +++ b/applications/nfc/scenes/nfc_scene_not_implemented.c @@ -6,7 +6,7 @@ void nfc_scene_not_implemented_dialog_callback(DialogExResult result, void* cont view_dispatcher_send_custom_event(nfc->view_dispatcher, result); } -const void nfc_scene_not_implemented_on_enter(void* context) { +void nfc_scene_not_implemented_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // TODO Set data from worker @@ -19,7 +19,7 @@ const void nfc_scene_not_implemented_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); } -const bool nfc_scene_not_implemented_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_not_implemented_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -30,7 +30,7 @@ const bool nfc_scene_not_implemented_on_event(void* context, SceneManagerEvent e return false; } -const void nfc_scene_not_implemented_on_exit(void* context) { +void nfc_scene_not_implemented_on_exit(void* context) { Nfc* nfc = (Nfc*)context; DialogEx* dialog_ex = nfc->dialog_ex; diff --git a/applications/nfc/scenes/nfc_scene_read_card.c b/applications/nfc/scenes/nfc_scene_read_card.c index 4b32c36d..fda69b05 100755 --- a/applications/nfc/scenes/nfc_scene_read_card.c +++ b/applications/nfc/scenes/nfc_scene_read_card.c @@ -7,7 +7,7 @@ void nfc_read_card_worker_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_READ_CARD_CUSTOM_EVENT); } -const void nfc_scene_read_card_on_enter(void* context) { +void nfc_scene_read_card_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -21,7 +21,7 @@ const void nfc_scene_read_card_on_enter(void* context) { nfc->worker, NfcWorkerStateDetect, &nfc->dev.dev_data, nfc_read_card_worker_callback, nfc); } -const bool nfc_scene_read_card_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_read_card_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -36,7 +36,7 @@ const bool nfc_scene_read_card_on_event(void* context, SceneManagerEvent event) return false; } -const void nfc_scene_read_card_on_exit(void* context) { +void nfc_scene_read_card_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Stop worker diff --git a/applications/nfc/scenes/nfc_scene_read_card_success.c b/applications/nfc/scenes/nfc_scene_read_card_success.c index 93223a70..b745eae3 100755 --- a/applications/nfc/scenes/nfc_scene_read_card_success.c +++ b/applications/nfc/scenes/nfc_scene_read_card_success.c @@ -63,7 +63,7 @@ void nfc_scene_read_card_success_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); } -const bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -77,7 +77,7 @@ const bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent return false; } -const void nfc_scene_read_card_success_on_exit(void* context) { +void nfc_scene_read_card_success_on_exit(void* context) { Nfc* nfc = (Nfc*)context; DialogEx* dialog_ex = nfc->dialog_ex; diff --git a/applications/nfc/scenes/nfc_scene_read_emv_app.c b/applications/nfc/scenes/nfc_scene_read_emv_app.c index ae00431b..d0198791 100755 --- a/applications/nfc/scenes/nfc_scene_read_emv_app.c +++ b/applications/nfc/scenes/nfc_scene_read_emv_app.c @@ -7,7 +7,7 @@ void nfc_read_emv_app_worker_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_READ_EMV_APP_CUSTOM_EVENT); } -const void nfc_scene_read_emv_app_on_enter(void* context) { +void nfc_scene_read_emv_app_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -25,7 +25,7 @@ const void nfc_scene_read_emv_app_on_enter(void* context) { nfc); } -const bool nfc_scene_read_emv_app_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_read_emv_app_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -40,7 +40,7 @@ const bool nfc_scene_read_emv_app_on_event(void* context, SceneManagerEvent even return false; } -const void nfc_scene_read_emv_app_on_exit(void* context) { +void nfc_scene_read_emv_app_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Stop worker diff --git a/applications/nfc/scenes/nfc_scene_read_emv_app_success.c b/applications/nfc/scenes/nfc_scene_read_emv_app_success.c index dc95bfe6..a3371086 100755 --- a/applications/nfc/scenes/nfc_scene_read_emv_app_success.c +++ b/applications/nfc/scenes/nfc_scene_read_emv_app_success.c @@ -45,7 +45,7 @@ void nfc_scene_read_emv_app_success_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); } -const bool nfc_scene_read_emv_app_success_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_read_emv_app_success_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -59,7 +59,7 @@ const bool nfc_scene_read_emv_app_success_on_event(void* context, SceneManagerEv return false; } -const void nfc_scene_read_emv_app_success_on_exit(void* context) { +void nfc_scene_read_emv_app_success_on_exit(void* context) { Nfc* nfc = (Nfc*)context; DialogEx* dialog_ex = nfc->dialog_ex; diff --git a/applications/nfc/scenes/nfc_scene_read_emv_data.c b/applications/nfc/scenes/nfc_scene_read_emv_data.c index 0aeb6fe0..44389e5a 100644 --- a/applications/nfc/scenes/nfc_scene_read_emv_data.c +++ b/applications/nfc/scenes/nfc_scene_read_emv_data.c @@ -7,7 +7,7 @@ void nfc_read_emv_data_worker_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_READ_EMV_DATA_CUSTOM_EVENT); } -const void nfc_scene_read_emv_data_on_enter(void* context) { +void nfc_scene_read_emv_data_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -27,7 +27,7 @@ const void nfc_scene_read_emv_data_on_enter(void* context) { nfc); } -const bool nfc_scene_read_emv_data_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_read_emv_data_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -42,7 +42,7 @@ const bool nfc_scene_read_emv_data_on_event(void* context, SceneManagerEvent eve return false; } -const void nfc_scene_read_emv_data_on_exit(void* context) { +void nfc_scene_read_emv_data_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Stop worker diff --git a/applications/nfc/scenes/nfc_scene_read_emv_data_success.c b/applications/nfc/scenes/nfc_scene_read_emv_data_success.c index c0b61111..8a98f5ae 100755 --- a/applications/nfc/scenes/nfc_scene_read_emv_data_success.c +++ b/applications/nfc/scenes/nfc_scene_read_emv_data_success.c @@ -113,7 +113,7 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } -const bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -132,7 +132,7 @@ const bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerE return false; } -const void nfc_scene_read_emv_data_success_on_exit(void* context) { +void nfc_scene_read_emv_data_success_on_exit(void* context) { Nfc* nfc = (Nfc*)context; widget_clear(nfc->widget); diff --git a/applications/nfc/scenes/nfc_scene_read_mifare_ul.c b/applications/nfc/scenes/nfc_scene_read_mifare_ul.c index d1997d51..95e890b5 100755 --- a/applications/nfc/scenes/nfc_scene_read_mifare_ul.c +++ b/applications/nfc/scenes/nfc_scene_read_mifare_ul.c @@ -7,7 +7,7 @@ void nfc_read_mifare_ul_worker_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_READ_MIFARE_UL_CUSTOM_EVENT); } -const void nfc_scene_read_mifare_ul_on_enter(void* context) { +void nfc_scene_read_mifare_ul_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -25,7 +25,7 @@ const void nfc_scene_read_mifare_ul_on_enter(void* context) { nfc); } -const bool nfc_scene_read_mifare_ul_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_read_mifare_ul_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -40,7 +40,7 @@ const bool nfc_scene_read_mifare_ul_on_event(void* context, SceneManagerEvent ev return false; } -const void nfc_scene_read_mifare_ul_on_exit(void* context) { +void nfc_scene_read_mifare_ul_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Stop worker diff --git a/applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c b/applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c index c0a2a9f3..f1324dfa 100755 --- a/applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c +++ b/applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c @@ -20,7 +20,7 @@ void nfc_scene_read_mifare_ul_success_text_box_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_SCENE_READ_MF_UL_CUSTOM_EVENT); } -const void nfc_scene_read_mifare_ul_success_on_enter(void* context) { +void nfc_scene_read_mifare_ul_success_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Clear device name @@ -76,7 +76,7 @@ const void nfc_scene_read_mifare_ul_success_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); } -const bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -112,7 +112,7 @@ const bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManager return false; } -const void nfc_scene_read_mifare_ul_success_on_exit(void* context) { +void nfc_scene_read_mifare_ul_success_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Clean dialog diff --git a/applications/nfc/scenes/nfc_scene_restore_original.c b/applications/nfc/scenes/nfc_scene_restore_original.c index 3229d023..12481592 100644 --- a/applications/nfc/scenes/nfc_scene_restore_original.c +++ b/applications/nfc/scenes/nfc_scene_restore_original.c @@ -7,7 +7,7 @@ void nfc_scene_restore_original_popup_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_RESTORE_ORIGINAL_CUSTOM_EVENT); } -const void nfc_scene_restore_original_on_enter(void* context) { +void nfc_scene_restore_original_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -21,7 +21,7 @@ const void nfc_scene_restore_original_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); } -const bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; bool consumed = false; @@ -33,7 +33,7 @@ const bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent return consumed; } -const void nfc_scene_restore_original_on_exit(void* context) { +void nfc_scene_restore_original_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Clear view diff --git a/applications/nfc/scenes/nfc_scene_run_emv_app_confirm.c b/applications/nfc/scenes/nfc_scene_run_emv_app_confirm.c index 609ccfc5..7d12aa4b 100755 --- a/applications/nfc/scenes/nfc_scene_run_emv_app_confirm.c +++ b/applications/nfc/scenes/nfc_scene_run_emv_app_confirm.c @@ -28,7 +28,7 @@ void nfc_scene_run_emv_app_confirm_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); } -const bool nfc_scene_run_emv_app_confirm_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_run_emv_app_confirm_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -42,7 +42,7 @@ const bool nfc_scene_run_emv_app_confirm_on_event(void* context, SceneManagerEve return false; } -const void nfc_scene_run_emv_app_confirm_on_exit(void* context) { +void nfc_scene_run_emv_app_confirm_on_exit(void* context) { Nfc* nfc = (Nfc*)context; DialogEx* dialog_ex = nfc->dialog_ex; diff --git a/applications/nfc/scenes/nfc_scene_save_name.c b/applications/nfc/scenes/nfc_scene_save_name.c index ca239f68..a30b24f6 100755 --- a/applications/nfc/scenes/nfc_scene_save_name.c +++ b/applications/nfc/scenes/nfc_scene_save_name.c @@ -9,7 +9,7 @@ void nfc_scene_save_name_text_input_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SAVE_NAME_CUSTOM_EVENT); } -const void nfc_scene_save_name_on_enter(void* context) { +void nfc_scene_save_name_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -32,12 +32,12 @@ const void nfc_scene_save_name_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); } -const bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) { - if(nfc->dev.dev_name) { + if(strcmp(nfc->dev.dev_name, "")) { nfc_device_delete(&nfc->dev); } if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetUid)) { @@ -56,10 +56,9 @@ const bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) return false; } -const void nfc_scene_save_name_on_exit(void* context) { +void nfc_scene_save_name_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Clear view - text_input_set_header_text(nfc->text_input, NULL); - text_input_set_result_callback(nfc->text_input, NULL, NULL, NULL, 0, false); + text_input_clean(nfc->text_input); } diff --git a/applications/nfc/scenes/nfc_scene_save_success.c b/applications/nfc/scenes/nfc_scene_save_success.c index 058388b6..57b08b07 100755 --- a/applications/nfc/scenes/nfc_scene_save_success.c +++ b/applications/nfc/scenes/nfc_scene_save_success.c @@ -7,7 +7,7 @@ void nfc_scene_save_success_popup_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SAVE_SUCCESS_CUSTOM_EVENT); } -const void nfc_scene_save_success_on_enter(void* context) { +void nfc_scene_save_success_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -21,7 +21,7 @@ const void nfc_scene_save_success_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); } -const bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; bool consumed = false; @@ -42,7 +42,7 @@ const bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent even return consumed; } -const void nfc_scene_save_success_on_exit(void* context) { +void nfc_scene_save_success_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Clear view diff --git a/applications/nfc/scenes/nfc_scene_saved_menu.c b/applications/nfc/scenes/nfc_scene_saved_menu.c index cd682783..a8e2ce7f 100755 --- a/applications/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/nfc/scenes/nfc_scene_saved_menu.c @@ -14,7 +14,7 @@ void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) { view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } -const void nfc_scene_saved_menu_on_enter(void* context) { +void nfc_scene_saved_menu_on_enter(void* context) { Nfc* nfc = (Nfc*)context; Submenu* submenu = nfc->submenu; @@ -42,7 +42,7 @@ const void nfc_scene_saved_menu_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } -const bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; bool consumed = false; @@ -78,7 +78,7 @@ const bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) return consumed; } -const void nfc_scene_saved_menu_on_exit(void* context) { +void nfc_scene_saved_menu_on_exit(void* context) { Nfc* nfc = (Nfc*)context; submenu_clean(nfc->submenu); diff --git a/applications/nfc/scenes/nfc_scene_scripts_menu.c b/applications/nfc/scenes/nfc_scene_scripts_menu.c index 327d32c6..c82c4564 100755 --- a/applications/nfc/scenes/nfc_scene_scripts_menu.c +++ b/applications/nfc/scenes/nfc_scene_scripts_menu.c @@ -11,7 +11,7 @@ void nfc_scene_scripts_menu_submenu_callback(void* context, uint32_t index) { view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } -const void nfc_scene_scripts_menu_on_enter(void* context) { +void nfc_scene_scripts_menu_on_enter(void* context) { Nfc* nfc = (Nfc*)context; Submenu* submenu = nfc->submenu; @@ -32,7 +32,7 @@ const void nfc_scene_scripts_menu_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } -const bool nfc_scene_scripts_menu_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_scripts_menu_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -52,7 +52,7 @@ const bool nfc_scene_scripts_menu_on_event(void* context, SceneManagerEvent even return false; } -const void nfc_scene_scripts_menu_on_exit(void* context) { +void nfc_scene_scripts_menu_on_exit(void* context) { Nfc* nfc = (Nfc*)context; submenu_clean(nfc->submenu); diff --git a/applications/nfc/scenes/nfc_scene_set_atqa.c b/applications/nfc/scenes/nfc_scene_set_atqa.c index 80ceea17..7936880c 100755 --- a/applications/nfc/scenes/nfc_scene_set_atqa.c +++ b/applications/nfc/scenes/nfc_scene_set_atqa.c @@ -8,7 +8,7 @@ void nfc_scene_set_atqa_byte_input_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SET_ATQA_CUSTOM_EVENT); } -const void nfc_scene_set_atqa_on_enter(void* context) { +void nfc_scene_set_atqa_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -24,7 +24,7 @@ const void nfc_scene_set_atqa_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); } -const bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -36,7 +36,7 @@ const bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) { return false; } -const void nfc_scene_set_atqa_on_exit(void* context) { +void nfc_scene_set_atqa_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Clear view diff --git a/applications/nfc/scenes/nfc_scene_set_sak.c b/applications/nfc/scenes/nfc_scene_set_sak.c index 27646cc6..5df999d0 100755 --- a/applications/nfc/scenes/nfc_scene_set_sak.c +++ b/applications/nfc/scenes/nfc_scene_set_sak.c @@ -8,7 +8,7 @@ void nfc_scene_set_sak_byte_input_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SET_SAK_CUSTOM_EVENT); } -const void nfc_scene_set_sak_on_enter(void* context) { +void nfc_scene_set_sak_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -24,7 +24,7 @@ const void nfc_scene_set_sak_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); } -const bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -36,7 +36,7 @@ const bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) { return false; } -const void nfc_scene_set_sak_on_exit(void* context) { +void nfc_scene_set_sak_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Clear view diff --git a/applications/nfc/scenes/nfc_scene_set_type.c b/applications/nfc/scenes/nfc_scene_set_type.c index 9798b8f7..7574123f 100755 --- a/applications/nfc/scenes/nfc_scene_set_type.c +++ b/applications/nfc/scenes/nfc_scene_set_type.c @@ -11,7 +11,7 @@ void nfc_scene_set_type_submenu_callback(void* context, uint32_t index) { view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } -const void nfc_scene_set_type_on_enter(void* context) { +void nfc_scene_set_type_on_enter(void* context) { Nfc* nfc = (Nfc*)context; Submenu* submenu = nfc->submenu; @@ -22,7 +22,7 @@ const void nfc_scene_set_type_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } -const bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -41,7 +41,7 @@ const bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { return false; } -const void nfc_scene_set_type_on_exit(void* context) { +void nfc_scene_set_type_on_exit(void* context) { Nfc* nfc = (Nfc*)context; submenu_clean(nfc->submenu); diff --git a/applications/nfc/scenes/nfc_scene_set_uid.c b/applications/nfc/scenes/nfc_scene_set_uid.c index dd448a78..7ecfbf32 100755 --- a/applications/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/nfc/scenes/nfc_scene_set_uid.c @@ -8,7 +8,7 @@ void nfc_scene_set_uid_byte_input_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SET_UID_CUSTOM_EVENT); } -const void nfc_scene_set_uid_on_enter(void* context) { +void nfc_scene_set_uid_on_enter(void* context) { Nfc* nfc = (Nfc*)context; // Setup view @@ -25,7 +25,7 @@ const void nfc_scene_set_uid_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); } -const bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; if(event.type == SceneManagerEventTypeCustom) { @@ -37,7 +37,7 @@ const bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { return false; } -const void nfc_scene_set_uid_on_exit(void* context) { +void nfc_scene_set_uid_on_exit(void* context) { Nfc* nfc = (Nfc*)context; // Clear view diff --git a/applications/nfc/scenes/nfc_scene_start.c b/applications/nfc/scenes/nfc_scene_start.c index 3b560c07..cdf7dbe2 100755 --- a/applications/nfc/scenes/nfc_scene_start.c +++ b/applications/nfc/scenes/nfc_scene_start.c @@ -14,7 +14,7 @@ void nfc_scene_start_submenu_callback(void* context, uint32_t index) { view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } -const void nfc_scene_start_on_enter(void* context) { +void nfc_scene_start_on_enter(void* context) { Nfc* nfc = (Nfc*)context; Submenu* submenu = nfc->submenu; @@ -38,7 +38,7 @@ const void nfc_scene_start_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } -const bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; bool consumed = false; @@ -70,7 +70,7 @@ const bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { return consumed; } -const void nfc_scene_start_on_exit(void* context) { +void nfc_scene_start_on_exit(void* context) { Nfc* nfc = (Nfc*)context; submenu_clean(nfc->submenu); diff --git a/applications/power/power.c b/applications/power/power.c deleted file mode 100644 index 6fc4077a..00000000 --- a/applications/power/power.c +++ /dev/null @@ -1,278 +0,0 @@ -#include "power.h" -#include "power_cli.h" -#include "power_views.h" - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define POWER_OFF_TIMEOUT 30 - -typedef enum { - PowerStateNotCharging, - PowerStateCharging, - PowerStateCharged, -} PowerState; - -struct Power { - ViewDispatcher* view_dispatcher; - View* info_view; - View* off_view; - View* disconnect_view; - - ViewPort* battery_view_port; - - Dialog* dialog; - - ValueMutex* menu_vm; - Cli* cli; - Bt* bt; - MenuItem* menu; - - PowerState state; -}; - -void power_draw_battery_callback(Canvas* canvas, void* context) { - furi_assert(context); - Power* power = context; - canvas_draw_icon(canvas, 0, 0, &I_Battery_26x8); - with_view_model( - power->info_view, (PowerInfoModel * model) { - canvas_draw_box(canvas, 2, 2, (float)model->charge / 100 * 20, 4); - return false; - }); -} - -uint32_t power_info_back_callback(void* context) { - return VIEW_NONE; -} - -void power_menu_off_callback(void* context) { - Power* power = context; - power_off(power); -} - -void power_menu_reset_dialog_result(DialogResult result, void* context) { - Power* power = context; - if(result == DialogResultLeft) { - power_reboot(power, PowerBootModeDfu); - } else if(result == DialogResultRight) { - power_reboot(power, PowerBootModeNormal); - } else if(result == DialogResultBack) { - view_dispatcher_switch_to_view(power->view_dispatcher, VIEW_NONE); - } -} - -void power_menu_reset_callback(void* context) { - Power* power = context; - dialog_set_result_callback(power->dialog, power_menu_reset_dialog_result); - dialog_set_header_text(power->dialog, "Reboot type"); - dialog_set_text(power->dialog, "Reboot where?"); - dialog_set_left_button_text(power->dialog, "DFU"); - dialog_set_right_button_text(power->dialog, "OS"); - view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewDialog); -} - -void power_menu_enable_otg_callback(void* context) { - furi_hal_power_enable_otg(); -} - -void power_menu_disable_otg_callback(void* context) { - furi_hal_power_disable_otg(); -} - -void power_menu_info_callback(void* context) { - Power* power = context; - view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewInfo); -} - -Power* power_alloc() { - Power* power = furi_alloc(sizeof(Power)); - - power->state = PowerStateNotCharging; - - power->menu_vm = furi_record_open("menu"); - - power->cli = furi_record_open("cli"); - power_cli_init(power->cli, power); - - power->bt = furi_record_open("bt"); - - power->menu = menu_item_alloc_menu("Power", icon_animation_alloc(&A_Power_14)); - menu_item_subitem_add( - power->menu, menu_item_alloc_function("Off", NULL, power_menu_off_callback, power)); - menu_item_subitem_add( - power->menu, menu_item_alloc_function("Reboot", NULL, power_menu_reset_callback, power)); - menu_item_subitem_add( - power->menu, - menu_item_alloc_function("Enable OTG", NULL, power_menu_enable_otg_callback, power)); - menu_item_subitem_add( - power->menu, - menu_item_alloc_function("Disable OTG", NULL, power_menu_disable_otg_callback, power)); - menu_item_subitem_add( - power->menu, menu_item_alloc_function("Info", NULL, power_menu_info_callback, power)); - - power->view_dispatcher = view_dispatcher_alloc(); - power->info_view = view_alloc(); - view_allocate_model(power->info_view, ViewModelTypeLockFree, sizeof(PowerInfoModel)); - view_set_draw_callback(power->info_view, power_info_draw_callback); - view_set_previous_callback(power->info_view, power_info_back_callback); - view_dispatcher_add_view(power->view_dispatcher, PowerViewInfo, power->info_view); - - power->off_view = view_alloc(); - view_allocate_model(power->off_view, ViewModelTypeLockFree, sizeof(PowerOffModel)); - view_set_draw_callback(power->off_view, power_off_draw_callback); - view_dispatcher_add_view(power->view_dispatcher, PowerViewOff, power->off_view); - - power->disconnect_view = view_alloc(); - view_set_draw_callback(power->disconnect_view, power_disconnect_draw_callback); - view_dispatcher_add_view(power->view_dispatcher, PowerViewDisconnect, power->disconnect_view); - - power->dialog = dialog_alloc(); - dialog_set_context(power->dialog, power); - view_dispatcher_add_view( - power->view_dispatcher, PowerViewDialog, dialog_get_view(power->dialog)); - - power->battery_view_port = view_port_alloc(); - - view_port_set_width(power->battery_view_port, icon_get_width(&I_Battery_26x8)); - view_port_draw_callback_set(power->battery_view_port, power_draw_battery_callback, power); - return power; -} - -void power_free(Power* power) { - furi_assert(power); - free(power); -} - -void power_off(Power* power) { - furi_assert(power); - furi_hal_power_off(); - view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewDisconnect); -} - -void power_reboot(Power* power, PowerBootMode mode) { - if(mode == PowerBootModeNormal) { - furi_hal_boot_set_mode(FuriHalBootModeNormal); - } else if(mode == PowerBootModeDfu) { - furi_hal_boot_set_mode(FuriHalBootModeDFU); - } - furi_hal_power_reset(); -} - -static void power_charging_indication_handler(Power* power, NotificationApp* notifications) { - if(furi_hal_power_is_charging()) { - if(furi_hal_power_get_pct() == 100) { - if(power->state != PowerStateCharged) { - notification_internal_message(notifications, &sequence_charged); - power->state = PowerStateCharged; - } - } else { - if(power->state != PowerStateCharging) { - notification_internal_message(notifications, &sequence_charging); - power->state = PowerStateCharging; - } - } - } - - if(!furi_hal_power_is_charging()) { - if(power->state != PowerStateNotCharging) { - notification_internal_message(notifications, &sequence_not_charging); - power->state = PowerStateNotCharging; - } - } -} - -int32_t power_srv(void* p) { - (void)p; - Power* power = power_alloc(); - - NotificationApp* notifications = furi_record_open("notification"); - Gui* gui = furi_record_open("gui"); - gui_add_view_port(gui, power->battery_view_port, GuiLayerStatusBarRight); - view_dispatcher_attach_to_gui(power->view_dispatcher, gui, ViewDispatcherTypeFullscreen); - - with_value_mutex( - power->menu_vm, (Menu * menu) { menu_item_add(menu, power->menu); }); - - furi_record_create("power", power); - uint8_t battery_level = 0; - uint8_t battery_level_prev = 0; - while(1) { - bool battery_low = false; - - with_view_model( - power->info_view, (PowerInfoModel * model) { - model->charge = furi_hal_power_get_pct(); - battery_level = model->charge; - model->health = furi_hal_power_get_bat_health_pct(); - model->capacity_remaining = furi_hal_power_get_battery_remaining_capacity(); - model->capacity_full = furi_hal_power_get_battery_full_capacity(); - model->current_charger = furi_hal_power_get_battery_current(FuriHalPowerICCharger); - model->current_gauge = furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge); - model->voltage_charger = furi_hal_power_get_battery_voltage(FuriHalPowerICCharger); - model->voltage_gauge = furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge); - model->voltage_vbus = furi_hal_power_get_usb_voltage(); - model->temperature_charger = - furi_hal_power_get_battery_temperature(FuriHalPowerICCharger); - model->temperature_gauge = - furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge); - - if(model->charge == 0 && model->voltage_vbus < 4.0f) { - battery_low = true; - } - - return true; - }); - - with_view_model( - power->off_view, (PowerOffModel * model) { - if(battery_low) { - if(model->poweroff_tick == 0) { - model->poweroff_tick = - osKernelGetTickCount() + osKernelGetTickFreq() * POWER_OFF_TIMEOUT; - } else { - if(osKernelGetTickCount() > model->poweroff_tick) { - power_off(power); - } - } - } else { - model->poweroff_tick = 0; - } - - if(model->battery_low != battery_low) { - model->battery_low = battery_low; - view_dispatcher_switch_to_view( - power->view_dispatcher, battery_low ? PowerViewOff : VIEW_NONE); - } - return true; - }); - - power_charging_indication_handler(power, notifications); - - if(battery_level_prev != battery_level) { - battery_level_prev = battery_level; - bt_update_battery_level(power->bt, battery_level); - } - - view_port_update(power->battery_view_port); - - osDelay(1024); - } - - return 0; -} diff --git a/applications/power/power.h b/applications/power/power.h deleted file mode 100644 index 9829eb4f..00000000 --- a/applications/power/power.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -typedef struct Power Power; - -typedef enum { - PowerBootModeNormal, - PowerBootModeDfu, -} PowerBootMode; - -/** Power off device - * @param power - Power instance - */ -void power_off(Power* power); - -/** Reboot device - * @param power - Power instance - * @param mode - PowerBootMode - */ -void power_reboot(Power* power, PowerBootMode mode); diff --git a/applications/power/power_cli.c b/applications/power/power_cli.c index c39cafde..6ac955e6 100644 --- a/applications/power/power_cli.c +++ b/applications/power/power_cli.c @@ -1,29 +1,31 @@ #include "power_cli.h" + +#include +#include #include void power_cli_poweroff(Cli* cli, string_t args, void* context) { - Power* power = context; - power_off(power); + power_off(); + printf("It's now safe to disconnect USB from your flipper\r\n"); + while(cli_getc(cli)) { + } } void power_cli_reboot(Cli* cli, string_t args, void* context) { - Power* power = context; - power_reboot(power, PowerBootModeNormal); + power_reboot(PowerBootModeNormal); } void power_cli_dfu(Cli* cli, string_t args, void* context) { - Power* power = context; - power_reboot(power, PowerBootModeDfu); + power_reboot(PowerBootModeDfu); } void power_cli_factory_reset(Cli* cli, string_t args, void* context) { - Power* power = context; printf("All data will be lost. Are you sure (y/n)?\r\n"); char c = cli_getc(cli); if(c == 'y' || c == 'Y') { printf("Data will be wiped after reboot.\r\n"); furi_hal_boot_set_flags(FuriHalBootFlagFactoryReset); - power_reboot(power, PowerBootModeNormal); + power_reboot(PowerBootModeNormal); } else { printf("Safe choice.\r\n"); } @@ -53,13 +55,17 @@ void power_cli_ext(Cli* cli, string_t args, void* context) { } } -void power_cli_init(Cli* cli, Power* power) { - cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, power); - cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, power); +void power_cli_init() { + Cli* cli = furi_record_open("cli"); + + cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, NULL); + cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, NULL); cli_add_command( - cli, "factory_reset", CliCommandFlagParallelSafe, power_cli_factory_reset, power); - cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, power); - cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, power); - cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, power); - cli_add_command(cli, "power_ext", CliCommandFlagParallelSafe, power_cli_ext, power); + cli, "factory_reset", CliCommandFlagParallelSafe, power_cli_factory_reset, NULL); + cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, NULL); + cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, NULL); + cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, NULL); + cli_add_command(cli, "power_ext", CliCommandFlagParallelSafe, power_cli_ext, NULL); + + furi_record_close("cli"); } diff --git a/applications/power/power_cli.h b/applications/power/power_cli.h index 5d8f208c..f92db05f 100644 --- a/applications/power/power_cli.h +++ b/applications/power/power_cli.h @@ -1,6 +1,3 @@ #pragma once -#include -#include "power.h" - -void power_cli_init(Cli* cli, Power* power); \ No newline at end of file +void power_cli_init(); diff --git a/applications/power/power_service/power.c b/applications/power/power_service/power.c new file mode 100755 index 00000000..db791a20 --- /dev/null +++ b/applications/power/power_service/power.c @@ -0,0 +1,179 @@ +#include "power_i.h" +#include "views/power_off.h" + +#include +#include +#include +#include + +#define POWER_OFF_TIMEOUT 90 + +void power_draw_battery_callback(Canvas* canvas, void* context) { + furi_assert(context); + Power* power = context; + canvas_draw_icon(canvas, 0, 0, &I_Battery_26x8); + canvas_draw_box(canvas, 2, 2, power->info.charge / 5, 4); +} + +static ViewPort* power_battery_view_port_alloc(Power* power) { + ViewPort* battery_view_port = view_port_alloc(); + view_port_set_width(battery_view_port, icon_get_width(&I_Battery_26x8)); + view_port_draw_callback_set(battery_view_port, power_draw_battery_callback, power); + gui_add_view_port(power->gui, battery_view_port, GuiLayerStatusBarRight); + return battery_view_port; +} + +Power* power_alloc() { + Power* power = furi_alloc(sizeof(Power)); + + // Records + power->notification = furi_record_open("notification"); + power->gui = furi_record_open("gui"); + + // Pubsub + init_pubsub(&power->event_pubsub); + + // State initialization + power->state = PowerStateNotCharging; + power->battery_low = false; + power->power_off_timeout = POWER_OFF_TIMEOUT; + power->info_mtx = osMutexNew(NULL); + + // Gui + power->view_dispatcher = view_dispatcher_alloc(); + power->power_off = power_off_alloc(); + view_dispatcher_add_view( + power->view_dispatcher, PowerViewOff, power_off_get_view(power->power_off)); + view_dispatcher_attach_to_gui( + power->view_dispatcher, power->gui, ViewDispatcherTypeFullscreen); + + // Battery view port + power->battery_view_port = power_battery_view_port_alloc(power); + + return power; +} + +void power_free(Power* power) { + furi_assert(power); + + // Records + furi_record_close("notification"); + furi_record_close("gui"); + + // Gui + view_dispatcher_remove_view(power->view_dispatcher, PowerViewOff); + power_off_free(power->power_off); + view_port_free(power->battery_view_port); + + // State + osMutexDelete(power->info_mtx); + free(power); +} + +static void power_check_charging_state(Power* power) { + if(furi_hal_power_is_charging()) { + if(power->info.charge == 100) { + if(power->state != PowerStateCharged) { + notification_internal_message(power->notification, &sequence_charged); + power->state = PowerStateCharged; + power->event.type = PowerEventTypeFullyCharged; + notify_pubsub(&power->event_pubsub, &power->event); + } + } else { + if(power->state != PowerStateCharging) { + notification_internal_message(power->notification, &sequence_charging); + power->state = PowerStateCharging; + power->event.type = PowerEventTypeStartCharging; + notify_pubsub(&power->event_pubsub, &power->event); + } + } + } else { + if(power->state != PowerStateNotCharging) { + notification_internal_message(power->notification, &sequence_not_charging); + power->state = PowerStateNotCharging; + power->event.type = PowerEventTypeStopCharging; + notify_pubsub(&power->event_pubsub, &power->event); + } + } +} + +static void power_update_info(Power* power) { + osMutexAcquire(power->info_mtx, osWaitForever); + PowerInfo* info = &power->info; + + info->charge = furi_hal_power_get_pct(); + info->health = furi_hal_power_get_bat_health_pct(); + info->capacity_remaining = furi_hal_power_get_battery_remaining_capacity(); + 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_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(); + info->temperature_charger = furi_hal_power_get_battery_temperature(FuriHalPowerICCharger); + info->temperature_gauge = furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge); + + osMutexRelease(power->info_mtx); +} + +static void power_check_low_battery(Power* power) { + // Check battery charge and vbus voltage + if((power->info.charge == 0) && (power->info.voltage_vbus < 4.0f)) { + if(!power->battery_low) { + view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewOff); + } + power->battery_low = true; + } else { + if(power->battery_low) { + view_dispatcher_switch_to_view(power->view_dispatcher, VIEW_NONE); + power->power_off_timeout = POWER_OFF_TIMEOUT; + } + power->battery_low = false; + } + // If battery low, update view and switch off power after timeout + if(power->battery_low) { + if(power->power_off_timeout) { + power_off_set_time_left(power->power_off, power->power_off_timeout--); + } else { + power_off(); + } + } +} + +static void power_check_battery_level_change(Power* power) { + if(power->battery_level != power->info.charge) { + power->battery_level = power->info.charge; + power->event.type = PowerEventTypeBatteryLevelChanged; + power->event.data.battery_level = power->battery_level; + notify_pubsub(&power->event_pubsub, &power->event); + } +} + +int32_t power_srv(void* p) { + (void)p; + Power* power = power_alloc(); + furi_record_create("power", power); + + while(1) { + // Update data from gauge and charger + power_update_info(power); + + // Check low battery level + power_check_low_battery(power); + + // Check and notify about charging state + power_check_charging_state(power); + + // Check and notify about battery level change + power_check_battery_level_change(power); + + // Update battery view port + view_port_update(power->battery_view_port); + + osDelay(1000); + } + + power_free(power); + + return 0; +} diff --git a/applications/power/power_service/power.h b/applications/power/power_service/power.h new file mode 100644 index 00000000..d3dec410 --- /dev/null +++ b/applications/power/power_service/power.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +typedef struct Power Power; + +typedef enum { + PowerBootModeNormal, + PowerBootModeDfu, +} PowerBootMode; + +typedef enum { + PowerEventTypeStopCharging, + PowerEventTypeStartCharging, + PowerEventTypeFullyCharged, + PowerEventTypeBatteryLevelChanged, +} PowerEventType; + +typedef union { + uint8_t battery_level; +} PowerEventData; + +typedef struct { + PowerEventType type; + PowerEventData data; +} PowerEvent; + +typedef struct { + float current_charger; + float current_gauge; + + float voltage_charger; + float voltage_gauge; + float voltage_vbus; + + uint32_t capacity_remaining; + uint32_t capacity_full; + + float temperature_charger; + float temperature_gauge; + + uint8_t charge; + uint8_t health; +} PowerInfo; + +/** Power off device + */ +void power_off(); + +/** Reboot device + * @param mode - PowerBootMode + */ +void power_reboot(PowerBootMode mode); + +/** Get power info + * @param power - Power instance + * @param info - PowerInfo instance + */ +void power_get_info(Power* power, PowerInfo* info); + +/** Get power event pubsub handler + * @param power - Power instance + */ +PubSub* power_get_pubsub(Power* power); diff --git a/applications/power/power_service/power_api.c b/applications/power/power_service/power_api.c new file mode 100644 index 00000000..388466d6 --- /dev/null +++ b/applications/power/power_service/power_api.c @@ -0,0 +1,31 @@ +#include "power_i.h" +#include +#include "furi-hal-power.h" +#include "furi-hal-boot.h" + +void power_off() { + furi_hal_power_off(); +} + +void power_reboot(PowerBootMode mode) { + if(mode == PowerBootModeNormal) { + furi_hal_boot_set_mode(FuriHalBootModeNormal); + } else if(mode == PowerBootModeDfu) { + furi_hal_boot_set_mode(FuriHalBootModeDFU); + } + furi_hal_power_reset(); +} + +void power_get_info(Power* power, PowerInfo* info) { + furi_assert(power); + furi_assert(info); + + osMutexAcquire(power->info_mtx, osWaitForever); + memcpy(info, &power->info, sizeof(power->info)); + osMutexRelease(power->info_mtx); +} + +PubSub* power_get_pubsub(Power* power) { + furi_assert(power); + return &power->event_pubsub; +} diff --git a/applications/power/power_service/power_i.h b/applications/power/power_service/power_i.h new file mode 100755 index 00000000..35bba2d8 --- /dev/null +++ b/applications/power/power_service/power_i.h @@ -0,0 +1,37 @@ +#pragma once + +#include "power.h" + +#include +#include +#include +#include "views/power_off.h" + +#include + +typedef enum { + PowerStateNotCharging, + PowerStateCharging, + PowerStateCharged, +} PowerState; + +struct Power { + ViewDispatcher* view_dispatcher; + PowerOff* power_off; + + ViewPort* battery_view_port; + Gui* gui; + NotificationApp* notification; + PubSub event_pubsub; + PowerEvent event; + + PowerState state; + PowerInfo info; + osMutexId_t info_mtx; + + bool battery_low; + uint8_t battery_level; + uint8_t power_off_timeout; +}; + +typedef enum { PowerViewOff } PowerView; diff --git a/applications/power/power_service/views/power_off.c b/applications/power/power_service/views/power_off.c new file mode 100755 index 00000000..73abc16b --- /dev/null +++ b/applications/power/power_service/views/power_off.c @@ -0,0 +1,57 @@ +#include "power_off.h" +#include +#include + +struct PowerOff { + View* view; +}; + +typedef struct { + uint32_t time_left_sec; +} PowerOffModel; + +static void power_off_draw_callback(Canvas* canvas, void* _model) { + furi_assert(_model); + PowerOffModel* model = _model; + char buff[32]; + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 1, AlignCenter, AlignTop, "Battery low!"); + canvas_draw_icon(canvas, 0, 18, &I_BatteryBody_52x28); + canvas_draw_icon(canvas, 16, 25, &I_FaceNopower_29x14); + elements_bubble(canvas, 54, 17, 70, 30); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned( + canvas, 70, 23, AlignLeft, AlignTop, "Connect me\n to charger."); + snprintf(buff, sizeof(buff), "Poweroff in %lds.", model->time_left_sec); + canvas_draw_str_aligned(canvas, 64, 60, AlignCenter, AlignBottom, buff); +} + +PowerOff* power_off_alloc() { + PowerOff* power_off = furi_alloc(sizeof(PowerOff)); + power_off->view = view_alloc(); + view_allocate_model(power_off->view, ViewModelTypeLocking, sizeof(PowerOffModel)); + view_set_draw_callback(power_off->view, power_off_draw_callback); + return power_off; +} + +void power_off_free(PowerOff* power_off) { + furi_assert(power_off); + view_free(power_off->view); + free(power_off); +} + +View* power_off_get_view(PowerOff* power_off) { + furi_assert(power_off); + return power_off->view; +} + +void power_off_set_time_left(PowerOff* power_off, uint8_t time_left) { + furi_assert(power_off); + with_view_model( + power_off->view, (PowerOffModel * model) { + model->time_left_sec = time_left; + return true; + }); +} diff --git a/applications/power/power_service/views/power_off.h b/applications/power/power_service/views/power_off.h new file mode 100644 index 00000000..2e2e91f7 --- /dev/null +++ b/applications/power/power_service/views/power_off.h @@ -0,0 +1,13 @@ +#pragma once + +typedef struct PowerOff PowerOff; + +#include + +PowerOff* power_off_alloc(); + +void power_off_free(PowerOff* power_off); + +View* power_off_get_view(PowerOff* power_off); + +void power_off_set_time_left(PowerOff* power_off, uint8_t time_left); diff --git a/applications/power/power_settings_app/power_settings_app.c b/applications/power/power_settings_app/power_settings_app.c new file mode 100755 index 00000000..69adcc9e --- /dev/null +++ b/applications/power/power_settings_app/power_settings_app.c @@ -0,0 +1,82 @@ +#include "power_settings_app.h" + +static bool power_settings_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + PowerSettingsApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool power_settings_back_event_callback(void* context) { + furi_assert(context); + PowerSettingsApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void power_settings_tick_event_callback(void* context) { + furi_assert(context); + PowerSettingsApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +PowerSettingsApp* power_settings_app_alloc() { + PowerSettingsApp* app = furi_alloc(sizeof(PowerSettingsApp)); + + // Records + app->gui = furi_record_open("gui"); + app->power = furi_record_open("power"); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&power_settings_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, power_settings_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, power_settings_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, power_settings_tick_event_callback, 2000); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Views + app->batery_info = battery_info_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + PowerSettingsAppViewBatteryInfo, + battery_info_get_view(app->batery_info)); + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, PowerSettingsAppViewSubmenu, submenu_get_view(app->submenu)); + app->dialog = dialog_ex_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, PowerSettingsAppViewDialog, dialog_ex_get_view(app->dialog)); + + // Set first scene + scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneStart); + return app; +} + +void power_settings_app_free(PowerSettingsApp* app) { + furi_assert(app); + // Views + view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewBatteryInfo); + battery_info_free(app->batery_info); + view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewDialog); + dialog_ex_free(app->dialog); + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + // Records + furi_record_close("power"); + furi_record_close("gui"); + free(app); +} + +extern int32_t power_settings_app(void* p) { + PowerSettingsApp* app = power_settings_app_alloc(); + view_dispatcher_run(app->view_dispatcher); + power_settings_app_free(app); + return 0; +} diff --git a/applications/power/power_settings_app/power_settings_app.h b/applications/power/power_settings_app/power_settings_app.h new file mode 100644 index 00000000..8429b54b --- /dev/null +++ b/applications/power/power_settings_app/power_settings_app.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "views/battery_info.h" +#include +#include + +#include "scenes/power_settings_scene.h" + +typedef struct { + Power* power; + Gui* gui; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + BatteryInfo* batery_info; + Submenu* submenu; + DialogEx* dialog; + PowerInfo info; +} PowerSettingsApp; + +typedef enum { + PowerSettingsAppViewBatteryInfo, + PowerSettingsAppViewSubmenu, + PowerSettingsAppViewDialog, +} PowerSettingsAppView; diff --git a/applications/power/power_settings_app/scenes/power_settinfs_scene_usb_disconnect.c b/applications/power/power_settings_app/scenes/power_settinfs_scene_usb_disconnect.c new file mode 100755 index 00000000..1a76edcb --- /dev/null +++ b/applications/power/power_settings_app/scenes/power_settinfs_scene_usb_disconnect.c @@ -0,0 +1,17 @@ +#include "../power_settings_app.h" + +void power_settings_scene_usb_disconnect_on_enter(void* context) { + PowerSettingsApp* app = context; + + dialog_ex_set_header( + app->dialog, "Disconnect USB for safe\nshutdown", 64, 26, AlignCenter, AlignTop); + + view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewDialog); +} + +bool power_settings_scene_usb_disconnect_on_event(void* context, SceneManagerEvent event) { + return true; +} + +void power_settings_scene_usb_disconnect_on_exit(void* context) { +} diff --git a/applications/power/power_settings_app/scenes/power_settings_scene.c b/applications/power/power_settings_app/scenes/power_settings_scene.c new file mode 100644 index 00000000..306bdbb6 --- /dev/null +++ b/applications/power/power_settings_app/scenes/power_settings_scene.c @@ -0,0 +1,30 @@ +#include "power_settings_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const power_settings_on_enter_handlers[])(void*) = { +#include "power_settings_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 power_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "power_settings_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 power_settings_on_exit_handlers[])(void* context) = { +#include "power_settings_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers power_settings_scene_handlers = { + .on_enter_handlers = power_settings_on_enter_handlers, + .on_event_handlers = power_settings_on_event_handlers, + .on_exit_handlers = power_settings_on_exit_handlers, + .scene_num = PowerSettingsAppSceneNum, +}; diff --git a/applications/power/power_settings_app/scenes/power_settings_scene.h b/applications/power/power_settings_app/scenes/power_settings_scene.h new file mode 100644 index 00000000..ebc0e3d0 --- /dev/null +++ b/applications/power/power_settings_app/scenes/power_settings_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) PowerSettingsAppScene##id, +typedef enum { +#include "power_settings_scene_config.h" + PowerSettingsAppSceneNum, +} PowerSettingsAppScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers power_settings_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "power_settings_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 "power_settings_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 "power_settings_scene_config.h" +#undef ADD_SCENE diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_battery_info.c b/applications/power/power_settings_app/scenes/power_settings_scene_battery_info.c new file mode 100755 index 00000000..4dd04f8c --- /dev/null +++ b/applications/power/power_settings_app/scenes/power_settings_scene_battery_info.c @@ -0,0 +1,34 @@ +#include "../power_settings_app.h" + +static void power_settings_scene_battery_info_update_model(PowerSettingsApp* app) { + power_get_info(app->power, &app->info); + BatteryInfoModel battery_info_data = { + .vbus_voltage = app->info.voltage_vbus, + .gauge_voltage = app->info.voltage_gauge, + .gauge_current = app->info.current_gauge, + .gauge_temperature = app->info.temperature_gauge, + .charge = app->info.charge, + .health = app->info.health, + }; + battery_info_set_data(app->batery_info, &battery_info_data); +} + +void power_settings_scene_battery_info_on_enter(void* context) { + PowerSettingsApp* app = context; + power_settings_scene_battery_info_update_model(app); + view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewBatteryInfo); +} + +bool power_settings_scene_battery_info_on_event(void* context, SceneManagerEvent event) { + PowerSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + power_settings_scene_battery_info_update_model(app); + consumed = true; + } + return consumed; +} + +void power_settings_scene_battery_info_on_exit(void* context) { +} diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_config.h b/applications/power/power_settings_app/scenes/power_settings_scene_config.h new file mode 100755 index 00000000..569e907a --- /dev/null +++ b/applications/power/power_settings_app/scenes/power_settings_scene_config.h @@ -0,0 +1,5 @@ +ADD_SCENE(power_settings, start, Start) +ADD_SCENE(power_settings, battery_info, BatteryInfo) +ADD_SCENE(power_settings, reboot, Reboot) +ADD_SCENE(power_settings, power_off, PowerOff) +ADD_SCENE(power_settings, usb_disconnect, UsbDisconnect) diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c new file mode 100755 index 00000000..84a7910a --- /dev/null +++ b/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c @@ -0,0 +1,48 @@ +#include "../power_settings_app.h" + +void power_settings_scene_power_off_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + PowerSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, result); +} + +void power_settings_scene_power_off_on_enter(void* context) { + PowerSettingsApp* app = context; + DialogEx* dialog = app->dialog; + + dialog_ex_set_header(dialog, "Turn off Device?", 64, 2, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog, " I will be\nwaiting for\n you here...", 78, 16, AlignLeft, AlignTop); + dialog_ex_set_icon(dialog, 21, 13, &I_Cry_dolph_55x52); + dialog_ex_set_left_button_text(dialog, "Back"); + dialog_ex_set_right_button_text(dialog, "OFF"); + dialog_ex_set_result_callback(dialog, power_settings_scene_power_off_dialog_callback); + dialog_ex_set_context(dialog, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewDialog); +} + +bool power_settings_scene_power_off_on_event(void* context, SceneManagerEvent event) { + PowerSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultLeft) { + scene_manager_previous_scene(app->scene_manager); + } else if(event.event == DialogExResultRight) { + power_off(); + // Check if USB is connected + power_get_info(app->power, &app->info); + if(app->info.voltage_vbus > 4.0f) { + scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneUsbDisconnect); + } + } + consumed = true; + } + return consumed; +} + +void power_settings_scene_power_off_on_exit(void* context) { + PowerSettingsApp* app = context; + dialog_ex_clean(app->dialog); +} diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_reboot.c b/applications/power/power_settings_app/scenes/power_settings_scene_reboot.c new file mode 100755 index 00000000..d6ec2d04 --- /dev/null +++ b/applications/power/power_settings_app/scenes/power_settings_scene_reboot.c @@ -0,0 +1,52 @@ +#include "../power_settings_app.h" + +enum PowerSettingsRebootSubmenuIndex { + PowerSettingsRebootSubmenuIndexDfu, + PowerSettingsRebootSubmenuIndexOs, +}; + +void power_settings_scene_reboot_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + PowerSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void power_settings_scene_reboot_on_enter(void* context) { + PowerSettingsApp* app = context; + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, "Reboot type"); + submenu_add_item( + submenu, + "Firmware upgrade", + PowerSettingsRebootSubmenuIndexDfu, + power_settings_scene_reboot_submenu_callback, + app); + submenu_add_item( + submenu, + "Flipper OS", + PowerSettingsRebootSubmenuIndexOs, + power_settings_scene_reboot_submenu_callback, + app); + + view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewSubmenu); +} + +bool power_settings_scene_reboot_on_event(void* context, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PowerSettingsRebootSubmenuIndexDfu) { + power_reboot(PowerBootModeDfu); + } else if(event.event == PowerSettingsRebootSubmenuIndexOs) { + power_reboot(PowerBootModeNormal); + } + consumed = true; + } + return consumed; +} + +void power_settings_scene_reboot_on_exit(void* context) { + PowerSettingsApp* app = context; + submenu_clean(app->submenu); +} diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_start.c b/applications/power/power_settings_app/scenes/power_settings_scene_start.c new file mode 100755 index 00000000..c2ae256f --- /dev/null +++ b/applications/power/power_settings_app/scenes/power_settings_scene_start.c @@ -0,0 +1,64 @@ +#include "../power_settings_app.h" + +enum PowerSettingsSubmenuIndex { + PowerSettingsSubmenuIndexBatteryInfo, + PowerSettingsSubmenuIndexReboot, + PowerSettingsSubmenuIndexOff, +}; + +static void power_settings_scene_start_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + PowerSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void power_settings_scene_start_on_enter(void* context) { + PowerSettingsApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Battery info", + PowerSettingsSubmenuIndexBatteryInfo, + power_settings_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "Reboot", + PowerSettingsSubmenuIndexReboot, + power_settings_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "Power OFF", + PowerSettingsSubmenuIndexOff, + power_settings_scene_start_submenu_callback, + app); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, PowerSettingsAppSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewSubmenu); +} + +bool power_settings_scene_start_on_event(void* context, SceneManagerEvent event) { + PowerSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PowerSettingsSubmenuIndexBatteryInfo) { + scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneBatteryInfo); + } else if(event.event == PowerSettingsSubmenuIndexReboot) { + scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneReboot); + } else if(event.event == PowerSettingsSubmenuIndexOff) { + scene_manager_next_scene(app->scene_manager, PowerSettingsAppScenePowerOff); + } + scene_manager_set_scene_state(app->scene_manager, PowerSettingsAppSceneStart, event.event); + consumed = true; + } + return consumed; +} + +void power_settings_scene_start_on_exit(void* context) { + PowerSettingsApp* app = context; + submenu_clean(app->submenu); +} diff --git a/applications/power/power_settings_app/views/battery_info.c b/applications/power/power_settings_app/views/battery_info.c new file mode 100644 index 00000000..599aefee --- /dev/null +++ b/applications/power/power_settings_app/views/battery_info.c @@ -0,0 +1,126 @@ +#include "battery_info.h" +#include +#include + +struct BatteryInfo { + View* view; +}; + +static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) { + canvas_draw_frame(canvas, x - 7, y + 7, 30, 13); + canvas_draw_icon(canvas, x, y, icon); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, x - 4, y + 16, 24, 6); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); +}; + +static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { + char emote[20] = {}; + char header[20] = {}; + char value[20] = {}; + + int32_t drain_current = data->gauge_current * (-1000); + uint32_t charge_current = data->gauge_current * 1000; + + // Draw battery + canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); + if(charge_current > 0) { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); + } else if(drain_current > 100) { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); + } else if(data->charge < 10) { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); + } else { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14); + } + + // Draw bubble + elements_bubble(canvas, 53, 0, 71, 39); + + // Set text + if(charge_current > 0) { + snprintf(emote, sizeof(emote), "%s", "Yummy!"); + snprintf(header, sizeof(header), "%s", "Charging at"); + snprintf( + value, + sizeof(value), + "%ld.%ldV %ldmA", + (uint32_t)(data->vbus_voltage), + (uint32_t)(data->vbus_voltage * 10) % 10, + charge_current); + } else if(drain_current > 0) { + snprintf(emote, sizeof(emote), "%s", drain_current > 100 ? "Oh no!" : "Om-nom-nom!"); + snprintf(header, sizeof(header), "%s", "Consumption is"); + snprintf( + value, sizeof(value), "%ld %s", drain_current, drain_current > 100 ? "mA!" : "mA"); + } else if(charge_current != 0 || drain_current != 0) { + snprintf(header, 20, "..."); + } else { + snprintf(header, sizeof(header), "Charged!"); + } + + canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); + canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); + canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); +}; + +static void battery_info_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + BatteryInfoModel* model = context; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + draw_battery(canvas, model, 0, 5); + + char batt_level[10]; + char temperature[10]; + char voltage[10]; + char health[10]; + + snprintf(batt_level, sizeof(batt_level), "%ld%%", (uint32_t)model->charge); + snprintf(temperature, sizeof(temperature), "%ld C", (uint32_t)model->gauge_temperature); + snprintf( + voltage, + sizeof(voltage), + "%ld.%01ld V", + (uint32_t)model->gauge_voltage, + (uint32_t)(model->gauge_voltage * 10) % 10); + snprintf(health, sizeof(health), "%d%%", model->health); + + draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level); + draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature); + draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage); + draw_stat(canvas, 104, 42, &I_Health_16x16, health); +} + +BatteryInfo* battery_info_alloc() { + BatteryInfo* battery_info = furi_alloc(sizeof(BatteryInfo)); + battery_info->view = view_alloc(); + view_set_context(battery_info->view, battery_info); + view_allocate_model(battery_info->view, ViewModelTypeLocking, sizeof(BatteryInfoModel)); + view_set_draw_callback(battery_info->view, battery_info_draw_callback); + + return battery_info; +} + +void battery_info_free(BatteryInfo* battery_info) { + furi_assert(battery_info); + view_free(battery_info->view); + free(battery_info); +} + +View* battery_info_get_view(BatteryInfo* battery_info) { + furi_assert(battery_info); + return battery_info->view; +} + +void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) { + furi_assert(battery_info); + furi_assert(data); + with_view_model( + battery_info->view, (BatteryInfoModel * model) { + memcpy(model, data, sizeof(BatteryInfoModel)); + return true; + }); +} diff --git a/applications/power/power_settings_app/views/battery_info.h b/applications/power/power_settings_app/views/battery_info.h new file mode 100644 index 00000000..e62aa44f --- /dev/null +++ b/applications/power/power_settings_app/views/battery_info.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +typedef struct BatteryInfo BatteryInfo; + +typedef struct { + float vbus_voltage; + float gauge_voltage; + float gauge_current; + float gauge_temperature; + uint8_t charge; + uint8_t health; +} BatteryInfoModel; + +BatteryInfo* battery_info_alloc(); + +void battery_info_free(BatteryInfo* battery_info); + +View* battery_info_get_view(BatteryInfo* battery_info); + +void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data); diff --git a/applications/power/power_views.c b/applications/power/power_views.c deleted file mode 100644 index c6248880..00000000 --- a/applications/power/power_views.c +++ /dev/null @@ -1,131 +0,0 @@ -#include "power_views.h" -#include - -static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) { - canvas_draw_frame(canvas, x - 7, y + 7, 30, 13); - canvas_draw_icon(canvas, x, y, icon); - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, x - 4, y + 16, 24, 6); - canvas_set_color(canvas, ColorBlack); - canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); -}; - -static void draw_battery(Canvas* canvas, PowerInfoModel* data, int x, int y) { - char emote[20]; - char header[20]; - char value[20]; - - int32_t drain_current = -data->current_gauge * 1000; - uint32_t charge_current = data->current_gauge * 1000; - // battery - canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); - if(charge_current > 0) { - canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); - } else if(drain_current > 100) { - canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); - } else if(data->charge < 10) { - canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); - } else { - canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14); - } - - //bubble - canvas_draw_frame(canvas, 57, 0, 71, 39); - canvas_draw_line(canvas, 53, 23, 57, 19); - canvas_draw_line(canvas, 53, 23, 57, 27); - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 57, 0, 2, 2); - canvas_draw_box(canvas, 57, 37, 2, 2); - canvas_draw_box(canvas, 126, 0, 2, 2); - canvas_draw_box(canvas, 126, 37, 2, 2); - canvas_draw_line(canvas, 57, 20, 57, 26); - canvas_set_color(canvas, ColorBlack); - canvas_draw_dot(canvas, 58, 1); - canvas_draw_dot(canvas, 58, 37); - canvas_draw_dot(canvas, 126, 1); - canvas_draw_dot(canvas, 126, 37); - - // text - if(charge_current > 0) { - snprintf(emote, sizeof(emote), "%s", "Yummy!"); - snprintf(header, sizeof(header), "%s", "Charging at"); - snprintf( - value, - sizeof(value), - "%ld.%ldV %ldmA", - (uint32_t)(data->voltage_vbus), - (uint32_t)(data->voltage_vbus * 10) % 10, - charge_current); - } else if(drain_current > 0) { - snprintf(emote, sizeof(emote), "%s", drain_current > 100 ? "Oh no!" : "Om-nom-nom!"); - snprintf(header, sizeof(header), "%s", "Consumption is"); - snprintf( - value, sizeof(value), "%ld %s", drain_current, drain_current > 100 ? "mA!" : "mA"); - } else if(charge_current != 0 || drain_current != 0) { - snprintf(header, 20, "%s", "..."); - memset(value, 0, sizeof(value)); - memset(emote, 0, sizeof(emote)); - } else { - snprintf(header, sizeof(header), "%s", "Charged!"); - memset(value, 0, sizeof(value)); - memset(emote, 0, sizeof(emote)); - } - - canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); - canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); - canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); -}; - -void power_info_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - PowerInfoModel* data = context; - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - draw_battery(canvas, data, 0, 5); - - char batt_level[10]; - char temperature[10]; - char voltage[10]; - char health[10]; - - snprintf(batt_level, sizeof(batt_level), "%ld%s", (uint32_t)data->charge, "%"); - snprintf(temperature, sizeof(temperature), "%ld %s", (uint32_t)data->temperature_gauge, "C"); - snprintf( - voltage, - sizeof(voltage), - "%ld.%01ld V", - (uint32_t)data->voltage_gauge, - (uint32_t)(data->voltage_gauge * 10) % 10); - snprintf(health, sizeof(health), "%d%s", data->health, "%"); - - draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level); - draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature); - draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage); - draw_stat(canvas, 104, 42, &I_Health_16x16, health); -} - -void power_off_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - PowerOffModel* model = context; - - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 15, "!!! Low Battery !!!"); - - char buffer[64]; - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 5, 30, "Connect to charger"); - snprintf( - buffer, - 64, - "Or poweroff in %lds", - (model->poweroff_tick - osKernelGetTickCount()) / osKernelGetTickFreq()); - canvas_draw_str(canvas, 5, 42, buffer); -} - -void power_disconnect_draw_callback(Canvas* canvas, void* context) { - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned( - canvas, 64, 32, AlignCenter, AlignCenter, "It's now safe to turn off\nyour flipper"); -} diff --git a/applications/power/power_views.h b/applications/power/power_views.h deleted file mode 100644 index 2aaa114f..00000000 --- a/applications/power/power_views.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -typedef enum { PowerViewInfo, PowerViewDialog, PowerViewOff, PowerViewDisconnect } PowerView; - -typedef struct { - float current_charger; - float current_gauge; - - float voltage_charger; - float voltage_gauge; - float voltage_vbus; - - uint32_t capacity_remaining; - uint32_t capacity_full; - - float temperature_charger; - float temperature_gauge; - - uint8_t charge; - uint8_t health; -} PowerInfoModel; - -typedef struct { - uint32_t poweroff_tick; - bool battery_low; -} PowerOffModel; - -void power_info_draw_callback(Canvas* canvas, void* context); - -void power_off_draw_callback(Canvas* canvas, void* context); - -void power_disconnect_draw_callback(Canvas* canvas, void* context); \ No newline at end of file diff --git a/applications/storage/storages/storage-ext.c b/applications/storage/storages/storage-ext.c index b29fdf7a..a434c2d8 100644 --- a/applications/storage/storages/storage-ext.c +++ b/applications/storage/storages/storage-ext.c @@ -99,7 +99,8 @@ FS_Error sd_unmount_card(StorageData* storage) { SDError error; storage_data_lock(storage); - error = storage->status = StorageStatusNotReady; + storage->status = StorageStatusNotReady; + error = FR_DISK_ERR; // TODO do i need to close the files? diff --git a/applications/storage/storages/storage-int.c b/applications/storage/storages/storage-int.c index 473ac4f5..87c02786 100644 --- a/applications/storage/storages/storage-int.c +++ b/applications/storage/storages/storage-int.c @@ -286,7 +286,7 @@ static bool storage_int_file_open( if(access_mode & FSAM_READ) flags |= LFS_O_RDONLY; if(access_mode & FSAM_WRITE) flags |= LFS_O_WRONLY; - if(open_mode & FSOM_OPEN_EXISTING) flags = flags; + if(open_mode & FSOM_OPEN_EXISTING) flags |= 0; if(open_mode & FSOM_OPEN_ALWAYS) flags |= LFS_O_CREAT; if(open_mode & FSOM_OPEN_APPEND) flags |= LFS_O_CREAT | LFS_O_APPEND; if(open_mode & FSOM_CREATE_NEW) flags |= LFS_O_CREAT | LFS_O_EXCL; diff --git a/applications/subghz/scenes/subghz_scene_config.h b/applications/subghz/scenes/subghz_scene_config.h index 070097e7..7958764d 100644 --- a/applications/subghz/scenes/subghz_scene_config.h +++ b/applications/subghz/scenes/subghz_scene_config.h @@ -7,6 +7,10 @@ ADD_SCENE(subghz, save_success, SaveSuccess) ADD_SCENE(subghz, saved, Saved) ADD_SCENE(subghz, transmitter, Transmitter) ADD_SCENE(subghz, show_error, ShowError) +ADD_SCENE(subghz, show_only_rx, ShowOnlyRx) +ADD_SCENE(subghz, saved_menu, SavedMenu) +ADD_SCENE(subghz, delete, Delete) +ADD_SCENE(subghz, delete_success, DeleteSuccess) ADD_SCENE(subghz, test, Test) ADD_SCENE(subghz, test_static, TestStatic) ADD_SCENE(subghz, test_carrier, TestCarrier) diff --git a/applications/subghz/scenes/subghz_scene_delete.c b/applications/subghz/scenes/subghz_scene_delete.c new file mode 100644 index 00000000..e97fd989 --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_delete.c @@ -0,0 +1,71 @@ +#include "../subghz_i.h" + +typedef enum { + SubGhzSceneDeleteInfoCustomEventDelete, +} SubGhzSceneDeleteInfoCustomEvent; + +void subghz_scene_delete_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + SubGhz* subghz = context; + if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzSceneDeleteInfoCustomEventDelete); + } +} + +void subghz_scene_delete_on_enter(void* context) { + SubGhz* subghz = context; + + char buffer_str[16]; + snprintf( + buffer_str, + sizeof(buffer_str), + "%03ld.%02ld", + subghz->txrx->frequency / 1000000 % 1000, + subghz->txrx->frequency / 10000 % 100); + widget_add_string_element( + subghz->widget, 78, 0, AlignLeft, AlignTop, FontSecondary, buffer_str); + if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async || + subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) { + snprintf(buffer_str, sizeof(buffer_str), "AM"); + } else if(subghz->txrx->preset == FuriHalSubGhzPreset2FSKAsync) { + snprintf(buffer_str, sizeof(buffer_str), "FM"); + } else { + furi_crash(NULL); + } + widget_add_string_element( + subghz->widget, 113, 0, AlignLeft, AlignTop, FontSecondary, buffer_str); + string_t text; + string_init(text); + subghz->txrx->protocol_result->to_string(subghz->txrx->protocol_result, text); + widget_add_string_multiline_element( + subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, string_get_cstr(text)); + string_clear(text); + + widget_add_button_element( + subghz->widget, GuiButtonTypeRight, "Delete", subghz_scene_delete_callback, subghz); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget); +} + +bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzSceneDeleteInfoCustomEventDelete) { + memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name)); + if(subghz_delete_file(subghz)) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); + } + return true; + } + } + return false; +} + +void subghz_scene_delete_on_exit(void* context) { + SubGhz* subghz = context; + widget_clear(subghz->widget); +} diff --git a/applications/subghz/scenes/subghz_scene_delete_success.c b/applications/subghz/scenes/subghz_scene_delete_success.c new file mode 100644 index 00000000..bfafb7e5 --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_delete_success.c @@ -0,0 +1,48 @@ +#include "../subghz_i.h" + +#define SCENE_DELETE_SUCCESS_CUSTOM_EVENT (0UL) + +void subghz_scene_delete_success_popup_callback(void* context) { + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_DELETE_SUCCESS_CUSTOM_EVENT); +} + +void subghz_scene_delete_success_on_enter(void* context) { + SubGhz* subghz = context; + + // Setup view + Popup* popup = subghz->popup; + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, subghz); + popup_set_callback(popup, subghz_scene_delete_success_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup); +} + +bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SCENE_DELETE_SUCCESS_CUSTOM_EVENT) { + return scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); + } + } + return false; +} + +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); +} diff --git a/applications/subghz/scenes/subghz_scene_receiver.c b/applications/subghz/scenes/subghz_scene_receiver.c index ffe510aa..f2d66e6b 100644 --- a/applications/subghz/scenes/subghz_scene_receiver.c +++ b/applications/subghz/scenes/subghz_scene_receiver.c @@ -60,7 +60,7 @@ void subghz_scene_add_to_history_callback(SubGhzProtocolCommon* parser, void* co string_clear(str_buff); } -const void subghz_scene_receiver_on_enter(void* context) { +void subghz_scene_receiver_on_enter(void* context) { SubGhz* subghz = context; string_t str_buff; @@ -95,7 +95,7 @@ const void subghz_scene_receiver_on_enter(void* context) { view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewReceiver); } -const bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { @@ -147,6 +147,6 @@ const bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event return false; } -const void subghz_scene_receiver_on_exit(void* context) { +void subghz_scene_receiver_on_exit(void* context) { // SubGhz* subghz = context; } diff --git a/applications/subghz/scenes/subghz_scene_receiver_config.c b/applications/subghz/scenes/subghz_scene_receiver_config.c index 276fc4b9..9586c8f3 100644 --- a/applications/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/subghz/scenes/subghz_scene_receiver_config.c @@ -102,7 +102,7 @@ void subghz_scene_receiver_config_callback(SubghzReceverEvent event, void* conte view_dispatcher_send_custom_event(subghz->view_dispatcher, event); } -const void subghz_scene_receiver_config_on_enter(void* context) { +void subghz_scene_receiver_config_on_enter(void* context) { SubGhz* subghz = context; VariableItem* item; uint8_t value_index; @@ -145,12 +145,12 @@ const void subghz_scene_receiver_config_on_enter(void* context) { view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewVariableItemList); } -const bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent event) { //SubGhz* subghz = context; return false; } -const void subghz_scene_receiver_config_on_exit(void* context) { +void subghz_scene_receiver_config_on_exit(void* context) { SubGhz* subghz = context; variable_item_list_clean(subghz->variable_item_list); } diff --git a/applications/subghz/scenes/subghz_scene_receiver_info.c b/applications/subghz/scenes/subghz_scene_receiver_info.c index 8f266337..f3a75e54 100644 --- a/applications/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/subghz/scenes/subghz_scene_receiver_info.c @@ -41,7 +41,7 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { return false; } -const void subghz_scene_receiver_info_on_enter(void* context) { +void subghz_scene_receiver_info_on_enter(void* context) { SubGhz* subghz = context; if(subghz_scene_receiver_info_update_parser(subghz)) { @@ -96,12 +96,11 @@ const void subghz_scene_receiver_info_on_enter(void* context) { view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget); } -const bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzSceneReceiverInfoCustomEventTxStart) { //CC1101 Stop RX -> Start TX - subghz->state_notifications = NOTIFICATION_TX_STATE; if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { subghz->txrx->hopper_state = SubGhzHopperStatePause; } @@ -112,7 +111,11 @@ const bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent return false; } if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) { - subghz_tx_start(subghz); + if(!subghz_tx_start(subghz)) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + } else { + subghz->state_notifications = NOTIFICATION_TX_STATE; + } } return true; } else if(event.event == SubGhzSceneReceiverInfoCustomEventTxStop) { @@ -145,6 +148,7 @@ const bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent } if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->to_save_string && strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) { + subghz_file_name_clear(subghz); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); } return true; @@ -167,7 +171,7 @@ const bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent return false; } -const void subghz_scene_receiver_info_on_exit(void* context) { +void subghz_scene_receiver_info_on_exit(void* context) { SubGhz* subghz = context; widget_clear(subghz->widget); } diff --git a/applications/subghz/scenes/subghz_scene_save_name.c b/applications/subghz/scenes/subghz_scene_save_name.c index b73f31b9..ee21238a 100644 --- a/applications/subghz/scenes/subghz_scene_save_name.c +++ b/applications/subghz/scenes/subghz_scene_save_name.c @@ -9,34 +9,42 @@ void subghz_scene_save_name_text_input_callback(void* context) { view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_SAVE_NAME_CUSTOM_EVENT); } -const void subghz_scene_save_name_on_enter(void* context) { +void subghz_scene_save_name_on_enter(void* context) { SubGhz* subghz = context; // Setup view TextInput* text_input = subghz->text_input; bool dev_name_empty = false; - set_random_name(subghz->text_store, sizeof(subghz->text_store)); - dev_name_empty = true; + if(!strcmp(subghz->file_name, "")) { + set_random_name(subghz->file_name, sizeof(subghz->file_name)); + dev_name_empty = true; + } else { + memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name)); + } text_input_set_header_text(text_input, "Name signal"); text_input_set_result_callback( text_input, subghz_scene_save_name_text_input_callback, subghz, - subghz->text_store, + subghz->file_name, 22, //Max len name dev_name_empty); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTextInput); } -const bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) { - if(strcmp(subghz->text_store, "") && - subghz_save_protocol_to_file(subghz, subghz->text_store)) { + if(strcmp(subghz->file_name, "") && + subghz_save_protocol_to_file(subghz, subghz->file_name)) { + if(strcmp(subghz->file_name_tmp, "")) { + subghz_delete_file(subghz); + } + subghz_file_name_clear(subghz); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); return true; } else { @@ -49,10 +57,9 @@ const bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent even return false; } -const void subghz_scene_save_name_on_exit(void* context) { +void subghz_scene_save_name_on_exit(void* context) { SubGhz* subghz = context; // Clear view - text_input_set_header_text(subghz->text_input, NULL); - text_input_set_result_callback(subghz->text_input, NULL, NULL, NULL, 0, false); + text_input_clean(subghz->text_input); } diff --git a/applications/subghz/scenes/subghz_scene_save_success.c b/applications/subghz/scenes/subghz_scene_save_success.c index 4d26ff54..dc267486 100644 --- a/applications/subghz/scenes/subghz_scene_save_success.c +++ b/applications/subghz/scenes/subghz_scene_save_success.c @@ -7,7 +7,7 @@ void subghz_scene_save_success_popup_callback(void* context) { view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_SAVE_SUCCESS_CUSTOM_EVENT); } -const void subghz_scene_save_success_on_enter(void* context) { +void subghz_scene_save_success_on_enter(void* context) { SubGhz* subghz = context; // Setup view @@ -21,7 +21,7 @@ const void subghz_scene_save_success_on_enter(void* context) { view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup); } -const bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) { @@ -36,7 +36,7 @@ const bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent e return false; } -const void subghz_scene_save_success_on_exit(void* context) { +void subghz_scene_save_success_on_exit(void* context) { SubGhz* subghz = context; // Clear view diff --git a/applications/subghz/scenes/subghz_scene_saved.c b/applications/subghz/scenes/subghz_scene_saved.c index 3bfec82a..68231ba6 100644 --- a/applications/subghz/scenes/subghz_scene_saved.c +++ b/applications/subghz/scenes/subghz_scene_saved.c @@ -1,20 +1,20 @@ #include "../subghz_i.h" -const void subghz_scene_saved_on_enter(void* context) { +void subghz_scene_saved_on_enter(void* context) { SubGhz* subghz = context; if(subghz_load_protocol_from_file(subghz)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu); } else { scene_manager_search_and_switch_to_previous_scene(subghz->scene_manager, SubGhzSceneStart); } } -const bool subghz_scene_saved_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_saved_on_event(void* context, SceneManagerEvent event) { // SubGhz* subghz = context; return false; } -const void subghz_scene_saved_on_exit(void* context) { +void subghz_scene_saved_on_exit(void* context) { // SubGhz* subghz = context; } diff --git a/applications/subghz/scenes/subghz_scene_saved_menu.c b/applications/subghz/scenes/subghz_scene_saved_menu.c new file mode 100644 index 00000000..59d7e3e8 --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_saved_menu.c @@ -0,0 +1,69 @@ +#include "../subghz_i.h" + +enum SubmenuIndex { + SubmenuIndexEmulate, + SubmenuIndexEdit, + SubmenuIndexDelete, +}; + +void subghz_scene_saved_menu_submenu_callback(void* context, uint32_t index) { + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, index); +} + +void subghz_scene_saved_menu_on_enter(void* context) { + SubGhz* subghz = context; + submenu_add_item( + subghz->submenu, + "Emulate", + SubmenuIndexEmulate, + subghz_scene_saved_menu_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Edit name", + SubmenuIndexEdit, + subghz_scene_saved_menu_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Delete", + SubmenuIndexDelete, + subghz_scene_saved_menu_submenu_callback, + subghz); + + submenu_set_selected_item( + subghz->submenu, + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSavedMenu)); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewMenu); +} + +bool subghz_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexEmulate) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexEmulate); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); + return true; + } else if(event.event == SubmenuIndexDelete) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexDelete); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDelete); + return true; + } else if(event.event == SubmenuIndexEdit) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexEdit); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + return true; + } + } + return false; +} + +void subghz_scene_saved_menu_on_exit(void* context) { + SubGhz* subghz = context; + submenu_clean(subghz->submenu); +} diff --git a/applications/subghz/scenes/subghz_scene_set_type.c b/applications/subghz/scenes/subghz_scene_set_type.c index 5993512b..4ec34e23 100644 --- a/applications/subghz/scenes/subghz_scene_set_type.c +++ b/applications/subghz/scenes/subghz_scene_set_type.c @@ -29,7 +29,7 @@ void subghz_scene_set_type_submenu_callback(void* context, uint32_t index) { view_dispatcher_send_custom_event(subghz->view_dispatcher, index); } -const void subghz_scene_set_type_on_enter(void* context) { +void subghz_scene_set_type_on_enter(void* context) { SubGhz* subghz = context; submenu_add_item( @@ -85,7 +85,7 @@ const void subghz_scene_set_type_on_enter(void* context) { view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewMenu); } -const bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; bool generated_protocol = false; @@ -174,6 +174,7 @@ const bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event if(generated_protocol) { subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92]; subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; + subghz_file_name_clear(subghz); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); return true; } @@ -182,7 +183,7 @@ const bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event return false; } -const void subghz_scene_set_type_on_exit(void* context) { +void subghz_scene_set_type_on_exit(void* context) { SubGhz* subghz = context; submenu_clean(subghz->submenu); } diff --git a/applications/subghz/scenes/subghz_scene_show_error.c b/applications/subghz/scenes/subghz_scene_show_error.c index 08c487a7..2599f24e 100644 --- a/applications/subghz/scenes/subghz_scene_show_error.c +++ b/applications/subghz/scenes/subghz_scene_show_error.c @@ -7,7 +7,7 @@ void subghz_scene_show_error_popup_callback(void* context) { view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT); } -const void subghz_scene_show_error_on_enter(void* context) { +void subghz_scene_show_error_on_enter(void* context) { SubGhz* subghz = context; // Setup view @@ -21,7 +21,7 @@ const void subghz_scene_show_error_on_enter(void* context) { view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup); } -const bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) { @@ -33,7 +33,7 @@ const bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent eve return false; } -const void subghz_scene_show_error_on_exit(void* context) { +void subghz_scene_show_error_on_exit(void* context) { SubGhz* subghz = context; // Clear view diff --git a/applications/subghz/scenes/subghz_scene_show_only_rx.c b/applications/subghz/scenes/subghz_scene_show_only_rx.c new file mode 100644 index 00000000..e59e1d68 --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_show_only_rx.c @@ -0,0 +1,53 @@ +#include "../subghz_i.h" + +#define SCENE_NO_MAN_CUSTOM_EVENT (11UL) + +void subghz_scene_show_only_rx_popup_callback(void* context) { + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT); +} + +void subghz_scene_show_only_rx_on_enter(void* context) { + SubGhz* subghz = context; + + // Setup view + Popup* popup = subghz->popup; + popup_set_icon(popup, 67, 12, &I_DolphinFirstStart7_61x51); + popup_set_text( + popup, + "This frequency can\nonly be used for RX\nin your region", + 38, + 40, + AlignCenter, + AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, subghz); + popup_set_callback(popup, subghz_scene_show_only_rx_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup); +} + +const bool subghz_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) { + scene_manager_previous_scene(subghz->scene_manager); + return true; + } + } + return false; +} + +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); +} diff --git a/applications/subghz/scenes/subghz_scene_start.c b/applications/subghz/scenes/subghz_scene_start.c index 2baac87a..b3713873 100644 --- a/applications/subghz/scenes/subghz_scene_start.c +++ b/applications/subghz/scenes/subghz_scene_start.c @@ -12,7 +12,7 @@ void subghz_scene_start_submenu_callback(void* context, uint32_t index) { view_dispatcher_send_custom_event(subghz->view_dispatcher, index); } -const void subghz_scene_start_on_enter(void* context) { +void subghz_scene_start_on_enter(void* context) { SubGhz* subghz = context; if(subghz->state_notifications == NOTIFICATION_STARTING_STATE) { subghz->state_notifications = NOTIFICATION_IDLE_STATE; @@ -36,7 +36,7 @@ const void subghz_scene_start_on_enter(void* context) { view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewMenu); } -const bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { @@ -65,7 +65,7 @@ const bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { return false; } -const void subghz_scene_start_on_exit(void* context) { +void subghz_scene_start_on_exit(void* context) { SubGhz* subghz = context; submenu_clean(subghz->submenu); } diff --git a/applications/subghz/scenes/subghz_scene_test.c b/applications/subghz/scenes/subghz_scene_test.c index 03f0b595..bef7e1e4 100644 --- a/applications/subghz/scenes/subghz_scene_test.c +++ b/applications/subghz/scenes/subghz_scene_test.c @@ -11,7 +11,7 @@ void subghz_scene_test_submenu_callback(void* context, uint32_t index) { view_dispatcher_send_custom_event(subghz->view_dispatcher, index); } -const void subghz_scene_test_on_enter(void* context) { +void subghz_scene_test_on_enter(void* context) { SubGhz* subghz = context; submenu_add_item( @@ -31,7 +31,7 @@ const void subghz_scene_test_on_enter(void* context) { view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewMenu); } -const bool subghz_scene_test_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_test_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { @@ -55,7 +55,7 @@ const bool subghz_scene_test_on_event(void* context, SceneManagerEvent event) { return false; } -const void subghz_scene_test_on_exit(void* context) { +void subghz_scene_test_on_exit(void* context) { SubGhz* subghz = context; submenu_clean(subghz->submenu); } diff --git a/applications/subghz/scenes/subghz_scene_test_carrier.c b/applications/subghz/scenes/subghz_scene_test_carrier.c index ef1fa1fc..e2fa6634 100644 --- a/applications/subghz/scenes/subghz_scene_test_carrier.c +++ b/applications/subghz/scenes/subghz_scene_test_carrier.c @@ -1,15 +1,30 @@ #include "../subghz_i.h" +#include "../views/subghz_test_carrier.h" -const void subghz_scene_test_carrier_on_enter(void* context) { +void subghz_scene_test_carrier_callback(SubghzTestCarrierEvent event, void* context) { + furi_assert(context); SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, event); +} + +void subghz_scene_test_carrier_on_enter(void* context) { + SubGhz* subghz = context; + subghz_test_carrier_set_callback( + subghz->subghz_test_carrier, subghz_scene_test_carrier_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTestCarrier); } -const bool subghz_scene_test_carrier_on_event(void* context, SceneManagerEvent event) { - // SubGhz* subghz = context; +bool subghz_scene_test_carrier_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubghzTestCarrierEventOnlyRx) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + return true; + } + } return false; } -const void subghz_scene_test_carrier_on_exit(void* context) { +void subghz_scene_test_carrier_on_exit(void* context) { // SubGhz* subghz = context; } diff --git a/applications/subghz/scenes/subghz_scene_test_packet.c b/applications/subghz/scenes/subghz_scene_test_packet.c index 7c275814..a49ec480 100644 --- a/applications/subghz/scenes/subghz_scene_test_packet.c +++ b/applications/subghz/scenes/subghz_scene_test_packet.c @@ -1,15 +1,30 @@ #include "../subghz_i.h" +#include "../views/subghz_test_packet.h" -const void subghz_scene_test_packet_on_enter(void* context) { +void subghz_scene_test_packet_callback(SubghzTestPacketEvent event, void* context) { + furi_assert(context); SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, event); +} + +void subghz_scene_test_packet_on_enter(void* context) { + SubGhz* subghz = context; + subghz_test_packet_set_callback( + subghz->subghz_test_packet, subghz_scene_test_packet_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTestPacket); } -const bool subghz_scene_test_packet_on_event(void* context, SceneManagerEvent event) { - // SubGhz* subghz = context; +bool subghz_scene_test_packet_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubghzTestPacketEventOnlyRx) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + return true; + } + } return false; } -const void subghz_scene_test_packet_on_exit(void* context) { +void subghz_scene_test_packet_on_exit(void* context) { // SubGhz* subghz = context; } diff --git a/applications/subghz/scenes/subghz_scene_test_static.c b/applications/subghz/scenes/subghz_scene_test_static.c index 6d1403f5..6c6099ec 100644 --- a/applications/subghz/scenes/subghz_scene_test_static.c +++ b/applications/subghz/scenes/subghz_scene_test_static.c @@ -1,15 +1,30 @@ #include "../subghz_i.h" +#include "../views/subghz_test_static.h" -const void subghz_scene_test_static_on_enter(void* context) { +void subghz_scene_test_static_callback(SubghzTestStaticEvent event, void* context) { + furi_assert(context); SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, event); +} + +void subghz_scene_test_static_on_enter(void* context) { + SubGhz* subghz = context; + subghz_test_static_set_callback( + subghz->subghz_test_static, subghz_scene_test_static_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewStatic); } -const bool subghz_scene_test_static_on_event(void* context, SceneManagerEvent event) { - // SubGhz* subghz = context; +bool subghz_scene_test_static_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubghzTestStaticEventOnlyRx) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + return true; + } + } return false; } -const void subghz_scene_test_static_on_exit(void* context) { +void subghz_scene_test_static_on_exit(void* context) { // SubGhz* subghz = context; } diff --git a/applications/subghz/scenes/subghz_scene_transmitter.c b/applications/subghz/scenes/subghz_scene_transmitter.c index 8e743250..85c61240 100644 --- a/applications/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/subghz/scenes/subghz_scene_transmitter.c @@ -55,7 +55,7 @@ static void subghz_scene_transmitter_update_data_show(void* context) { } } -const void subghz_scene_transmitter_on_enter(void* context) { +void subghz_scene_transmitter_on_enter(void* context) { SubGhz* subghz = context; subghz_transmitter_set_callback( subghz->subghz_transmitter, subghz_scene_transmitter_callback, subghz); @@ -64,18 +64,22 @@ const void subghz_scene_transmitter_on_enter(void* context) { view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTransmitter); } -const bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubghzTransmitterEventSendStart) { - subghz->state_notifications = NOTIFICATION_TX_STATE; + subghz->state_notifications = NOTIFICATION_IDLE_STATE; if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); } if((subghz->txrx->txrx_state == SubGhzTxRxStateIdle) || (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { - subghz_tx_start(subghz); - subghz_scene_transmitter_update_data_show(subghz); + if(!subghz_tx_start(subghz)) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + } else { + subghz->state_notifications = NOTIFICATION_TX_STATE; + subghz_scene_transmitter_update_data_show(subghz); + } } return true; } else if(event.event == SubghzTransmitterEventSendStop) { @@ -100,7 +104,7 @@ const bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent ev return false; } -const void subghz_scene_transmitter_on_exit(void* context) { +void subghz_scene_transmitter_on_exit(void* context) { SubGhz* subghz = context; subghz->state_notifications = NOTIFICATION_IDLE_STATE; diff --git a/applications/subghz/subghz_cli.c b/applications/subghz/subghz_cli.c index 46149d4e..1a33beab 100644 --- a/applications/subghz/subghz_cli.c +++ b/applications/subghz/subghz_cli.c @@ -48,12 +48,14 @@ void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_cc1101_g0, true); - furi_hal_subghz_tx(); - - printf("Transmitting at frequency %lu Hz\r\n", frequency); - printf("Press CTRL+C to stop\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - osDelay(250); + if(furi_hal_subghz_tx()) { + printf("Transmitting at frequency %lu Hz\r\n", frequency); + printf("Press CTRL+C to stop\r\n"); + while(!cli_cmd_interrupt_received(cli)) { + osDelay(250); + } + } else { + printf("This frequency can only be used for RX in your region\r\n"); } furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); @@ -179,7 +181,7 @@ static void subghz_cli_command_rx_callback(bool level, uint32_t duration, void* static void subghz_cli_command_rx_text_callback(string_t text, void* context) { SubGhzCliCommandRx* instance = context; instance->packet_count++; - printf(string_get_cstr(text)); + printf("%s", string_get_cstr(text)); } void subghz_cli_command_rx(Cli* cli, string_t args, void* context) { diff --git a/applications/subghz/subghz_i.c b/applications/subghz/subghz_i.c index 9a52586a..2f849e41 100644 --- a/applications/subghz/subghz_i.c +++ b/applications/subghz/subghz_i.c @@ -40,19 +40,19 @@ uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) { return value; } -uint32_t subghz_tx(SubGhz* subghz, uint32_t frequency) { +static bool subghz_tx(SubGhz* subghz, uint32_t frequency) { furi_assert(subghz); if(!furi_hal_subghz_is_frequency_valid(frequency)) { furi_crash(NULL); } furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); furi_hal_subghz_idle(); - uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); + furi_hal_subghz_set_frequency_and_path(frequency); hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_cc1101_g0, true); - furi_hal_subghz_tx(); + bool ret = furi_hal_subghz_tx(); subghz->txrx->txrx_state = SubGhzTxRxStateTx; - return value; + return ret; } void subghz_idle(SubGhz* subghz) { @@ -90,9 +90,10 @@ static void subghz_frequency_preset_to_str(SubGhz* subghz, string_t output) { (int)subghz->txrx->preset); } -void subghz_tx_start(SubGhz* subghz) { +bool subghz_tx_start(SubGhz* subghz) { furi_assert(subghz); + bool ret = false; subghz->txrx->encoder = subghz_protocol_encoder_common_alloc(); subghz->txrx->encoder->repeat = 200; //max repeat with the button held down //get upload @@ -105,16 +106,23 @@ void subghz_tx_start(SubGhz* subghz) { subghz_begin(subghz, FuriHalSubGhzPresetOok270Async); } if(subghz->txrx->frequency) { - subghz_tx(subghz, subghz->txrx->frequency); + ret = subghz_tx(subghz, subghz->txrx->frequency); } else { - subghz_tx(subghz, 433920000); + ret = subghz_tx(subghz, 433920000); } - //Start TX - furi_hal_subghz_start_async_tx( - subghz_protocol_encoder_common_yield, subghz->txrx->encoder); + if(ret) { + //Start TX + furi_hal_subghz_start_async_tx( + subghz_protocol_encoder_common_yield, subghz->txrx->encoder); + } } } + if(!ret) { + subghz_protocol_encoder_common_free(subghz->txrx->encoder); + subghz_idle(subghz); + } + return ret; } void subghz_tx_stop(SubGhz* subghz) { @@ -125,8 +133,9 @@ void subghz_tx_stop(SubGhz* subghz) { subghz_protocol_encoder_common_free(subghz->txrx->encoder); subghz_idle(subghz); //if protocol dynamic then we save the last upload - if(subghz->txrx->protocol_result->type_protocol == SubGhzProtocolCommonTypeDynamic) { - subghz_save_protocol_to_file(subghz, subghz->text_store); + if((subghz->txrx->protocol_result->type_protocol == SubGhzProtocolCommonTypeDynamic) && + (strcmp(subghz->file_name, ""))) { + subghz_save_protocol_to_file(subghz, subghz->file_name); } notification_message(subghz->notifications, &sequence_reset_red); } @@ -268,8 +277,8 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { file_worker, SUBGHZ_APP_PATH_FOLDER, SUBGHZ_APP_EXTENSION, - subghz->text_store, - sizeof(subghz->text_store), + subghz->file_name, + sizeof(subghz->file_name), NULL); if(res) { @@ -278,7 +287,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { protocol_file_name, "%s/%s%s", SUBGHZ_APP_PATH_FOLDER, - subghz->text_store, + subghz->file_name, SUBGHZ_APP_EXTENSION); } else { string_clear(temp_str); @@ -292,7 +301,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { do { if(!file_worker_open( file_worker, string_get_cstr(protocol_file_name), FSAM_READ, FSOM_OPEN_EXISTING)) { - break; + return res; } // Read and parse frequency from 1st line if(!file_worker_read_until(file_worker, temp_str, '\n')) { @@ -345,6 +354,40 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { return res; } +bool subghz_delete_file(SubGhz* subghz) { + furi_assert(subghz); + + bool result = true; + FileWorker* file_worker = file_worker_alloc(false); + string_t file_path; + + do { + // Get key file path + string_init_printf( + file_path, + "%s/%s%s", + SUBGHZ_APP_PATH_FOLDER, + subghz->file_name_tmp, + SUBGHZ_APP_EXTENSION); + // Delete original file + if(!file_worker_remove(file_worker, string_get_cstr(file_path))) { + result = false; + break; + } + } while(0); + + string_clear(file_path); + file_worker_close(file_worker); + file_worker_free(file_worker); + return result; +} + +void subghz_file_name_clear(SubGhz* subghz) { + furi_assert(subghz); + memset(subghz->file_name, 0, sizeof(subghz->file_name)); + memset(subghz->file_name_tmp, 0, sizeof(subghz->file_name_tmp)); +} + uint32_t subghz_random_serial(void) { static bool rand_generator_inited = false; diff --git a/applications/subghz/subghz_i.h b/applications/subghz/subghz_i.h index eecf98d3..dc1e5a3f 100644 --- a/applications/subghz/subghz_i.h +++ b/applications/subghz/subghz_i.h @@ -29,7 +29,7 @@ #include -#define SUBGHZ_TEXT_STORE_SIZE 128 +#define SUBGHZ_TEXT_STORE_SIZE 40 #define NOTIFICATION_STARTING_STATE 0u #define NOTIFICATION_IDLE_STATE 1u @@ -90,7 +90,8 @@ struct SubGhz { Popup* popup; TextInput* text_input; Widget* widget; - char text_store[SUBGHZ_TEXT_STORE_SIZE + 1]; + char file_name[SUBGHZ_TEXT_STORE_SIZE + 1]; + char file_name_tmp[SUBGHZ_TEXT_STORE_SIZE + 1]; uint8_t state_notifications; SubghzReceiver* subghz_receiver; @@ -121,10 +122,12 @@ void subghz_begin(SubGhz* subghz, FuriHalSubGhzPreset preset); uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency); void subghz_rx_end(SubGhz* subghz); void subghz_sleep(SubGhz* subghz); -void subghz_tx_start(SubGhz* subghz); +bool subghz_tx_start(SubGhz* subghz); void subghz_tx_stop(SubGhz* subghz); bool subghz_key_load(SubGhz* subghz, const char* file_path); bool subghz_save_protocol_to_file(SubGhz* subghz, const char* dev_name); bool subghz_load_protocol_from_file(SubGhz* subghz); +bool subghz_delete_file(SubGhz* subghz); +void subghz_file_name_clear(SubGhz* subghz); uint32_t subghz_random_serial(void); void subghz_hopper_update(SubGhz* subghz); diff --git a/applications/subghz/views/subghz_test_carrier.c b/applications/subghz/views/subghz_test_carrier.c index 82d11650..1217240e 100644 --- a/applications/subghz/views/subghz_test_carrier.c +++ b/applications/subghz/views/subghz_test_carrier.c @@ -9,6 +9,8 @@ struct SubghzTestCarrier { View* view; osTimerId timer; + SubghzTestCarrierCallback callback; + void* context; }; typedef enum { @@ -24,6 +26,16 @@ typedef struct { SubghzTestCarrierModelStatus status; } SubghzTestCarrierModel; +void subghz_test_carrier_set_callback( + SubghzTestCarrier* subghz_test_carrier, + SubghzTestCarrierCallback callback, + void* context) { + furi_assert(subghz_test_carrier); + furi_assert(callback); + subghz_test_carrier->callback = callback; + subghz_test_carrier->context = context; +} + void subghz_test_carrier_draw(Canvas* canvas, SubghzTestCarrierModel* model) { char buffer[64]; @@ -105,7 +117,11 @@ bool subghz_test_carrier_input(InputEvent* event, void* context) { } else { hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_cc1101_g0, true); - furi_hal_subghz_tx(); + if(!furi_hal_subghz_tx()) { + hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + subghz_test_carrier->callback( + SubghzTestCarrierEventOnlyRx, subghz_test_carrier->context); + } } return true; diff --git a/applications/subghz/views/subghz_test_carrier.h b/applications/subghz/views/subghz_test_carrier.h index f191f7af..b4522345 100644 --- a/applications/subghz/views/subghz_test_carrier.h +++ b/applications/subghz/views/subghz_test_carrier.h @@ -2,8 +2,19 @@ #include +typedef enum { + SubghzTestCarrierEventOnlyRx, +} SubghzTestCarrierEvent; + typedef struct SubghzTestCarrier SubghzTestCarrier; +typedef void (*SubghzTestCarrierCallback)(SubghzTestCarrierEvent event, void* context); + +void subghz_test_carrier_set_callback( + SubghzTestCarrier* subghz_test_carrier, + SubghzTestCarrierCallback callback, + void* context); + SubghzTestCarrier* subghz_test_carrier_alloc(); void subghz_test_carrier_free(SubghzTestCarrier* subghz_test_carrier); diff --git a/applications/subghz/views/subghz_test_packet.c b/applications/subghz/views/subghz_test_packet.c index f1c48179..2d4b4aea 100644 --- a/applications/subghz/views/subghz_test_packet.c +++ b/applications/subghz/views/subghz_test_packet.c @@ -16,12 +16,14 @@ struct SubghzTestPacket { SubGhzDecoderPrinceton* decoder; SubGhzEncoderPrinceton* encoder; - volatile size_t packet_rx; + SubghzTestPacketCallback callback; + void* context; }; typedef enum { SubghzTestPacketModelStatusRx, + SubghzTestPacketModelStatusOnlyRx, SubghzTestPacketModelStatusTx, } SubghzTestPacketModelStatus; @@ -36,6 +38,16 @@ typedef struct { volatile bool subghz_test_packet_overrun = false; +void subghz_test_packet_set_callback( + SubghzTestPacket* subghz_test_packet, + SubghzTestPacketCallback callback, + void* context) { + furi_assert(subghz_test_packet); + furi_assert(callback); + subghz_test_packet->callback = callback; + subghz_test_packet->context = context; +} + static void subghz_test_packet_rx_callback(bool level, uint32_t duration, void* context) { furi_assert(context); SubghzTestPacket* instance = context; @@ -57,7 +69,7 @@ static void subghz_test_packet_rssi_timer_callback(void* context) { if(model->status == SubghzTestPacketModelStatusRx) { model->rssi = furi_hal_subghz_get_rssi(); model->packets = instance->packet_rx; - } else { + } else if(model->status == SubghzTestPacketModelStatusTx) { model->packets = SUBGHZ_TEST_PACKET_COUNT - subghz_encoder_princeton_get_repeat_left(instance->encoder); } @@ -124,7 +136,7 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { instance->view, (SubghzTestPacketModel * model) { if(model->status == SubghzTestPacketModelStatusRx) { furi_hal_subghz_stop_async_rx(); - } else { + } else if(model->status == SubghzTestPacketModelStatusTx) { furi_hal_subghz_stop_async_tx(); } @@ -137,10 +149,10 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { } else if(event->key == InputKeyUp) { if(model->path < FuriHalSubGhzPath868) model->path++; } else if(event->key == InputKeyOk) { - if(model->status == SubghzTestPacketModelStatusTx) { - model->status = SubghzTestPacketModelStatusRx; - } else { + if(model->status == SubghzTestPacketModelStatusRx) { model->status = SubghzTestPacketModelStatusTx; + } else { + model->status = SubghzTestPacketModelStatusRx; } } @@ -151,8 +163,13 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { if(model->status == SubghzTestPacketModelStatusRx) { furi_hal_subghz_start_async_rx(subghz_test_packet_rx_callback, instance); } else { - subghz_encoder_princeton_set(instance->encoder, 0x00AABBCC, 1000); - furi_hal_subghz_start_async_tx(subghz_encoder_princeton_yield, instance->encoder); + subghz_encoder_princeton_set( + instance->encoder, 0x00AABBCC, SUBGHZ_TEST_PACKET_COUNT); + if(!furi_hal_subghz_start_async_tx( + subghz_encoder_princeton_yield, instance->encoder)) { + model->status = SubghzTestPacketModelStatusOnlyRx; + instance->callback(SubghzTestPacketEventOnlyRx, instance->context); + } } return true; @@ -195,7 +212,7 @@ void subghz_test_packet_exit(void* context) { instance->view, (SubghzTestPacketModel * model) { if(model->status == SubghzTestPacketModelStatusRx) { furi_hal_subghz_stop_async_rx(); - } else { + } else if(model->status == SubghzTestPacketModelStatusTx) { furi_hal_subghz_stop_async_tx(); } return true; @@ -240,4 +257,4 @@ void subghz_test_packet_free(SubghzTestPacket* instance) { View* subghz_test_packet_get_view(SubghzTestPacket* instance) { furi_assert(instance); return instance->view; -} +} \ No newline at end of file diff --git a/applications/subghz/views/subghz_test_packet.h b/applications/subghz/views/subghz_test_packet.h index 00fae10e..bd861ee9 100644 --- a/applications/subghz/views/subghz_test_packet.h +++ b/applications/subghz/views/subghz_test_packet.h @@ -2,8 +2,19 @@ #include +typedef enum { + SubghzTestPacketEventOnlyRx, +} SubghzTestPacketEvent; + typedef struct SubghzTestPacket SubghzTestPacket; +typedef void (*SubghzTestPacketCallback)(SubghzTestPacketEvent event, void* context); + +void subghz_test_packet_set_callback( + SubghzTestPacket* subghz_test_packet, + SubghzTestPacketCallback callback, + void* context); + SubghzTestPacket* subghz_test_packet_alloc(); void subghz_test_packet_free(SubghzTestPacket* subghz_test_packet); diff --git a/applications/subghz/views/subghz_test_static.c b/applications/subghz/views/subghz_test_static.c index 240dc215..aa701464 100644 --- a/applications/subghz/views/subghz_test_static.c +++ b/applications/subghz/views/subghz_test_static.c @@ -8,6 +8,11 @@ #include #include +typedef enum { + SubghzTestStaticStatusIDLE, + SubghzTestStaticStatusTX, +} SubghzTestStaticStatus; + static const uint32_t subghz_test_static_keys[] = { 0x0074BADE, 0x0074BADD, @@ -17,20 +22,28 @@ static const uint32_t subghz_test_static_keys[] = { struct SubghzTestStatic { View* view; + SubghzTestStaticStatus satus_tx; SubGhzEncoderPrinceton* encoder; + SubghzTestStaticCallback callback; + void* context; }; -typedef enum { - SubghzTestStaticStatusRx, - SubghzTestStaticStatusTx, -} SubghzTestStaticStatus; - typedef struct { uint8_t frequency; uint32_t real_frequency; uint8_t button; } SubghzTestStaticModel; +void subghz_test_static_set_callback( + SubghzTestStatic* subghz_test_static, + SubghzTestStaticCallback callback, + void* context) { + furi_assert(subghz_test_static); + furi_assert(callback); + subghz_test_static->callback = callback; + subghz_test_static->context = context; +} + void subghz_test_static_draw(Canvas* canvas, SubghzTestStaticModel* model) { char buffer[64]; @@ -79,22 +92,30 @@ bool subghz_test_static_input(InputEvent* event, void* context) { if(event->key == InputKeyOk) { NotificationApp* notification = furi_record_open("notification"); if(event->type == InputTypePress) { - notification_message_block(notification, &sequence_set_red_255); - - FURI_LOG_I("SubghzTestStatic", "TX Start"); furi_hal_subghz_idle(); furi_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); + if(!furi_hal_subghz_tx()) { + instance->callback(SubghzTestStaticEventOnlyRx, instance->context); + } else { + notification_message_block(notification, &sequence_set_red_255); - subghz_encoder_princeton_set( - instance->encoder, subghz_test_static_keys[model->button], 10000); + FURI_LOG_I("SubghzTestStatic", "TX Start"); - furi_hal_subghz_start_async_tx( - subghz_encoder_princeton_yield, instance->encoder); + subghz_encoder_princeton_set( + instance->encoder, subghz_test_static_keys[model->button], 10000); + + furi_hal_subghz_start_async_tx( + subghz_encoder_princeton_yield, instance->encoder); + instance->satus_tx = SubghzTestStaticStatusTX; + } } else if(event->type == InputTypeRelease) { - FURI_LOG_I("SubghzTestStatic", "TX Stop"); - furi_hal_subghz_stop_async_tx(); - - notification_message(notification, &sequence_reset_red); + if(instance->satus_tx == SubghzTestStaticStatusTX) { + FURI_LOG_I("SubghzTestStatic", "TX Stop"); + subghz_encoder_princeton_print_log(instance->encoder); + furi_hal_subghz_stop_async_tx(); + notification_message(notification, &sequence_reset_red); + } + instance->satus_tx = SubghzTestStaticStatusIDLE; } furi_record_close("notification"); } @@ -114,12 +135,14 @@ void subghz_test_static_enter(void* context) { hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_cc1101_g0, false); + instance->satus_tx = SubghzTestStaticStatusIDLE; with_view_model( instance->view, (SubghzTestStaticModel * model) { model->frequency = subghz_frequencies_433_92; model->real_frequency = subghz_frequencies[model->frequency]; model->button = 0; + return true; }); } @@ -156,4 +179,4 @@ void subghz_test_static_free(SubghzTestStatic* instance) { View* subghz_test_static_get_view(SubghzTestStatic* instance) { furi_assert(instance); return instance->view; -} +} \ No newline at end of file diff --git a/applications/subghz/views/subghz_test_static.h b/applications/subghz/views/subghz_test_static.h index ddcd8315..48346d8a 100644 --- a/applications/subghz/views/subghz_test_static.h +++ b/applications/subghz/views/subghz_test_static.h @@ -2,8 +2,19 @@ #include +typedef enum { + SubghzTestStaticEventOnlyRx, +} SubghzTestStaticEvent; + typedef struct SubghzTestStatic SubghzTestStatic; +typedef void (*SubghzTestStaticCallback)(SubghzTestStaticEvent event, void* context); + +void subghz_test_static_set_callback( + SubghzTestStatic* subghz_test_static, + SubghzTestStaticCallback callback, + void* context); + SubghzTestStatic* subghz_test_static_alloc(); void subghz_test_static_free(SubghzTestStatic* subghz_static); diff --git a/assets/compiled/assets_icons.c b/assets/compiled/assets_icons.c index 60282caf..2af951ef 100644 --- a/assets/compiled/assets_icons.c +++ b/assets/compiled/assets_icons.c @@ -2,6 +2,12 @@ #include +const uint8_t _I_Certification1_103x23_0[] = {0x3F,0xFE,0xF1,0x03,0x00,0x3C,0x00,0xE0,0x01,0x00,0x00,0xFC,0x01,0x3F,0xFE,0xF1,0x03,0x00,0x3F,0x00,0xF8,0x01,0xF8,0x0F,0xFF,0x07,0x3F,0xCE,0xF1,0x03,0xC0,0x3F,0x00,0xFE,0x01,0xF8,0xC7,0x03,0x1E,0x07,0xCE,0x71,0x00,0xE0,0x07,0x00,0x3F,0x00,0x18,0xE0,0x00,0x38,0x07,0xCE,0x71,0x00,0xF0,0x01,0x80,0x0F,0x00,0x18,0x70,0x00,0x70,0x07,0xCE,0x71,0x00,0x78,0x00,0xC0,0x03,0x00,0x18,0x30,0xF8,0x00,0x07,0xCE,0x71,0x00,0x38,0x00,0xC0,0x01,0x00,0x18,0x18,0xFE,0x03,0x07,0xCE,0x71,0x00,0x3C,0x00,0xE0,0x01,0x00,0x18,0x18,0x07,0x07,0x07,0xCE,0x71,0x00,0x1C,0x00,0xE0,0x00,0x00,0x18,0x0C,0x03,0x02,0x07,0xCE,0x71,0x00,0x1C,0x00,0xE0,0x00,0x00,0x18,0x8C,0x01,0x00,0x3F,0xFE,0x71,0x00,0x1C,0x00,0xE0,0x7F,0x00,0xF8,0x8F,0x01,0x00,0x3F,0xFE,0x71,0x00,0x1C,0x00,0xE0,0x7F,0x00,0xF8,0x8F,0x01,0x00,0x3F,0xFE,0x71,0x00,0x1C,0x00,0xE0,0x7F,0x00,0x18,0x8C,0x01,0x00,0x07,0xCE,0x71,0x00,0x1C,0x00,0xE0,0x00,0x00,0x18,0x8C,0x01,0x00,0x07,0xCE,0x71,0x00,0x1C,0x00,0xE0,0x00,0x00,0x18,0x0C,0x03,0x02,0x07,0xCE,0x71,0x00,0x3C,0x00,0xE0,0x01,0x00,0x18,0x18,0x07,0x07,0x07,0xCE,0x71,0x00,0x38,0x00,0xC0,0x01,0x00,0x18,0x18,0xFE,0x03,0x07,0xCE,0x71,0x00,0x78,0x00,0xC0,0x03,0x00,0x18,0x30,0xF8,0x60,0x07,0xCE,0x71,0x00,0xF0,0x01,0x80,0x0F,0x00,0x18,0x70,0x00,0x70,0x07,0xCE,0x71,0x00,0xE0,0x07,0x00,0x3F,0x00,0x18,0xE0,0x00,0x38,0x3F,0xCE,0xF1,0x03,0xC0,0x3F,0x00,0xFE,0x01,0x18,0xC0,0x03,0x1E,0x3F,0xCE,0xF1,0x03,0x00,0x3F,0x00,0xF8,0x01,0x08,0x00,0xFF,0x07,0x3F,0xCE,0xF1,0x03,0x00,0x3C,0x00,0xE0,0x01,0x00,0x00,0xFC,0x01,}; +const uint8_t *_I_Certification1_103x23[] = {_I_Certification1_103x23_0}; + +const uint8_t _I_Certification2_119x30_0[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x82,0xFF,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x13,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0xFE,0x00,0x00,0x18,0x00,0x0C,0x03,0x33,0x38,0x00,0x00,0x3E,0x00,0x00,0x80,0x01,0x03,0x00,0xF0,0xFF,0x07,0x03,0x33,0x1C,0x00,0x00,0x7F,0x00,0x00,0x60,0x00,0x0C,0x00,0x20,0x00,0x02,0x03,0x33,0x0F,0x00,0x80,0xE3,0x00,0x00,0x10,0x00,0x10,0x00,0x60,0x00,0x03,0x03,0xB3,0x03,0x00,0x80,0xC1,0x00,0x00,0x08,0x00,0x20,0x00,0xA0,0x80,0x02,0x03,0xF3,0x01,0x00,0xC0,0xC1,0x01,0x00,0x08,0x00,0x20,0x00,0x20,0x7D,0x02,0x03,0xF3,0x01,0x00,0xE0,0x80,0x03,0x00,0x04,0x18,0x40,0x00,0x20,0x22,0x02,0x03,0xB3,0x03,0x00,0x70,0x3E,0x07,0x00,0x04,0x17,0x40,0x00,0x40,0x14,0x01,0x03,0x33,0x0F,0x00,0x30,0x41,0x06,0x00,0xE2,0x10,0xF8,0x00,0x40,0x08,0x01,0xFF,0x33,0x1C,0x00,0xB8,0x00,0x0E,0x00,0x1E,0x10,0x07,0x00,0x40,0x14,0x01,0xFE,0x31,0x38,0x00,0x5C,0x00,0x1D,0x00,0x02,0xF0,0x00,0x00,0x40,0x22,0x01,0x00,0x00,0x00,0x00,0x2E,0x80,0x3A,0x00,0x02,0x00,0x00,0x00,0x40,0x41,0x01,0x00,0x00,0x00,0x00,0x26,0x42,0x32,0x00,0xC2,0xFF,0x07,0x00,0x80,0x80,0x00,0xFE,0xE3,0x1F,0x00,0x27,0x22,0x72,0x00,0xC2,0xFF,0x07,0x00,0xC0,0x80,0x01,0xFF,0xF3,0x3F,0x80,0x23,0x14,0xE2,0x00,0x02,0x00,0x00,0x00,0xA0,0xC0,0x03,0x03,0x30,0x30,0xC0,0x21,0x0C,0xC2,0x01,0xC4,0xFF,0x47,0x00,0x90,0x20,0x06,0x03,0x30,0x30,0xC0,0x40,0x00,0x81,0x01,0xC4,0xFF,0x47,0x00,0x88,0xA0,0x0A,0x03,0x30,0x30,0xE0,0x80,0x80,0x80,0x03,0x08,0x38,0x20,0x00,0x04,0x3F,0x12,0x03,0xF0,0x3F,0x70,0x00,0x41,0x00,0x07,0x08,0x38,0x20,0x00,0x02,0xC0,0x21,0x03,0xF0,0x3F,0x30,0x00,0x3E,0x00,0x06,0x10,0x38,0x10,0x00,0x01,0x00,0x40,0x03,0x30,0x30,0x70,0x00,0x00,0x00,0x07,0x60,0x00,0x0C,0x00,0x00,0x00,0x00,0xFF,0x33,0x30,0xE0,0xFF,0xFF,0xFF,0x03,0x80,0x01,0x03,0x00,0x00,0x00,0x00,0xFE,0x33,0x30,0xC0,0xFF,0xFF,0xFF,0x01,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0xFF,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0xFF,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0xFF,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0xFF,0x1F,}; +const uint8_t *_I_Certification2_119x30[] = {_I_Certification2_119x30_0}; + const uint8_t _A_WatchingTV_128x64_0[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x40,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x1F,0x00,0x00,0x00,0x00,0x02,0x00,0x40,0x11,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x60,0x00,0x00,0x00,0x00,0x02,0x00,0x40,0x11,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x80,0x00,0x00,0x00,0x00,0x04,0x00,0x20,0x09,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x20,0x06,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x02,0x00,0x00,0x00,0x10,0x00,0x20,0x06,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x00,0x10,0x00,0x20,0x0A,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x20,0x11,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x10,0x00,0x00,0x00,0x40,0x00,0xA0,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0x80,0x00,0x50,0x40,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x20,0x00,0x00,0x00,0x80,0x00,0x30,0x80,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x01,0x30,0x80,0x7F,0xE0,0x00,0x00,0x80,0x03,0x80,0x0F,0x20,0x00,0x00,0x00,0x00,0x02,0x10,0x00,0x80,0x9F,0x00,0x00,0x80,0x03,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x04,0x18,0x00,0x00,0x80,0x00,0x00,0x80,0x03,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x04,0x14,0x00,0x00,0x40,0x00,0x00,0x80,0x03,0xE0,0x3F,0x60,0x00,0x00,0x00,0x00,0x08,0x24,0x00,0x00,0x40,0x00,0x00,0xC0,0x01,0x20,0x67,0x40,0x00,0x00,0x00,0x00,0x10,0x22,0x00,0x00,0x30,0x00,0x00,0x60,0x00,0x60,0xA7,0xA0,0x00,0x00,0x00,0x00,0xF8,0x47,0x00,0x00,0x0C,0x00,0x00,0x18,0x00,0xC0,0x53,0xC1,0x00,0x00,0x00,0x00,0x06,0x58,0x00,0x00,0x03,0x00,0x00,0x0C,0x00,0x80,0xAF,0xA0,0x00,0x00,0x00,0x00,0x01,0x60,0x00,0xF0,0x00,0x00,0x00,0x06,0x00,0x00,0x50,0xD0,0x00,0x00,0x00,0x80,0x00,0x40,0xF8,0x0F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xA0,0x00,0x00,0x00,0x40,0x00,0x80,0x04,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0xD0,0x00,0x00,0xFF,0xFF,0x0F,0x80,0x04,0x00,0x00,0x00,0xF0,0x00,0x00,0x80,0x03,0xA8,0x00,0x00,0x55,0x55,0xF5,0xFF,0xFF,0xFF,0x0F,0x00,0xAE,0x00,0x00,0x60,0x04,0xD4,0x0F,0x00,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0x0E,0x80,0xD5,0x00,0x00,0x1C,0x00,0x78,0x35,0x00,0x55,0x55,0x55,0x55,0x55,0x55,0x0F,0xC0,0xAA,0xFF,0xE1,0x03,0x00,0x0C,0x6A,0x00,0xAA,0xAA,0xAA,0xAA,0xAA,0xEA,0x0A,0x60,0x81,0x01,0x1E,0x60,0x00,0x06,0xD4,0x00,0x55,0x55,0x55,0x55,0x55,0x75,0x0D,0x20,0x00,0x03,0x00,0x10,0x00,0x03,0xA8,0x01,0xAA,0xAA,0xAA,0xAA,0xAA,0xAE,0x0A,0x20,0x00,0x0F,0x00,0x2E,0x80,0x01,0x50,0x03,0x55,0x55,0x55,0x55,0x55,0x57,0x3D,0x3E,0x80,0xF5,0xFF,0x05,0x40,0x01,0xA8,0x02,0xAA,0xAA,0xAA,0xAA,0xEA,0xAA,0xCA,0x21,0x80,0xAA,0xAA,0x02,0xA0,0x00,0x50,0x03,0xFF,0xFF,0xFF,0xFF,0x7F,0x55,0x0D,0x20,0x80,0x55,0x55,0x01,0xD0,0x00,0xE8,0x07,0x88,0x88,0x88,0x88,0xB8,0xAA,0x0A,0x20,0x40,0xAA,0x02,0x00,0xAA,0x00,0x18,0x18,0x55,0x55,0x55,0x55,0x57,0x55,0x0D,0x20,0x40,0x00,0x00,0x00,0x55,0x00,0x04,0x20,0x22,0x22,0x22,0xA2,0xB3,0xAA,0x0A,0x40,0x40,0x00,0x00,0x80,0x6A,0x00,0x02,0x20,0x55,0x55,0x55,0x75,0x55,0x55,0x0D,0x80,0x40,0x00,0x00,0x40,0x7D,0x00,0x01,0x40,0x88,0x88,0x88,0x98,0xB8,0xAA,0x0A,0x00,0x21,0x00,0x00,0xA0,0x47,0x80,0x00,0x40,0x55,0x55,0x55,0x57,0x55,0x55,0x0D,0x00,0x3E,0x00,0x00,0xD0,0x40,0x80,0x00,0x40,0xFF,0xFF,0xFF,0x23,0xB2,0xAA,0x0A,0x00,0x00,0x00,0x00,0x6A,0x40,0x40,0x00,0x80,0x01,0x00,0x80,0x55,0x55,0x55,0x0D,0x00,0x00,0x00,0x40,0x35,0x40,0x40,0x00,0x80,0x01,0x00,0x80,0x88,0xB8,0xAA,0x0A,0x00,0x00,0x00,0xA0,0x1A,0x40,0x20,0x00,0x80,0x01,0x00,0x80,0x55,0x55,0x55,0x0D,0x00,0x00,0x00,0x54,0x15,0x40,0x20,0x00,0x80,0x31,0xFC,0x8F,0x22,0xB2,0xAA,0x0A,0x00,0x00,0x80,0xAA,0x0A,0x40,0x10,0x00,0x80,0x49,0x02,0x90,0x55,0x55,0x55,0x0D,0x00,0x00,0x50,0x55,0x0D,0x80,0x10,0x00,0x80,0x49,0x02,0x90,0x88,0xB8,0xAA,0x2A,0x00,0x80,0xAA,0xAA,0x0E,0x80,0x08,0x00,0x80,0x31,0x02,0x90,0x55,0x55,0x55,0x5D,0x55,0x55,0x55,0x55,0x01,0x00,0x07,0x00,0x80,0x01,0x02,0x90,0x22,0xB2,0xAA,0xF6,0xAA,0xEA,0xFF,0xFF,0x01,0x00,0x04,0x00,0x40,0x11,0xFC,0x8F,0x55,0x55,0x55,0x85,0xD7,0x1F,0x00,0x00,0x1E,0x00,0x04,0x00,0x40,0x29,0x00,0x80,0x88,0xB8,0xAA,0x06,0x6C,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x20,0x29,0x00,0x80,0x55,0x55,0x55,0x05,0x38,0x00,0x00,0x00,0x40,0x00,0x04,0x00,0x20,0x15,0x00,0x80,0x22,0xB2,0xAA,0x06,0x10,0x00,0x00,0x00,0x80,0x00,0x04,0x00,0x20,0x0B,0x00,0x80,0x55,0x55,0x55,0x05,0x10,0x00,0x00,0x00,0x00,0x03,0x04,0x00,0x10,}; const uint8_t _A_WatchingTV_128x64_1[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0xC0,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0xA0,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x1F,0x00,0x00,0x00,0x00,0x02,0x00,0xE0,0x10,0x00,0x00,0x00,0x00,0x00,0xC0,0x55,0x75,0x00,0x00,0x00,0x00,0x02,0x00,0xA0,0x10,0x00,0x00,0x00,0x00,0x00,0xA0,0xAA,0xAA,0x00,0x00,0x00,0x00,0x04,0x00,0x50,0x09,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x50,0x01,0x00,0x00,0x00,0x08,0x00,0xB0,0x05,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0xA0,0x02,0x00,0x00,0x00,0x10,0x00,0x50,0x02,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x40,0x05,0x00,0x00,0x00,0x10,0x00,0xB0,0x07,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x80,0x0A,0x00,0x00,0x00,0x20,0x00,0x50,0x09,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x15,0x00,0x00,0x00,0x40,0x00,0xB0,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x1A,0x00,0x00,0x00,0x80,0x00,0x58,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x34,0x00,0x00,0x00,0x80,0x00,0x28,0xC0,0xFF,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x28,0x00,0x00,0x00,0x00,0x01,0x38,0x00,0x00,0x83,0x03,0x00,0x80,0x03,0x80,0x0F,0x30,0x00,0x00,0x00,0x00,0x02,0x18,0x00,0x00,0xFC,0x02,0x00,0x80,0x03,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x04,0x18,0x00,0x00,0x00,0x02,0x00,0x80,0x03,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x04,0x14,0x00,0x00,0x00,0x01,0x00,0x80,0x03,0xE0,0xBF,0x60,0x00,0x00,0x00,0x00,0x08,0x24,0x00,0x00,0x80,0x00,0x00,0xC0,0x01,0xE0,0x67,0x51,0x00,0x00,0x00,0x00,0x10,0x22,0x00,0x00,0x40,0x00,0x00,0x60,0x00,0x60,0xA7,0xA0,0x00,0x00,0x00,0x00,0xF8,0x47,0x00,0x00,0x30,0x00,0x00,0x18,0x00,0xC0,0x53,0xD1,0x00,0x00,0x00,0x00,0x06,0x58,0x00,0x00,0x0C,0x00,0x00,0x0C,0x00,0x80,0xAF,0xA0,0x00,0x00,0x00,0x00,0x01,0x60,0xF0,0xFF,0x03,0x00,0x00,0x06,0x00,0x00,0x50,0xD0,0x00,0x00,0x00,0x80,0x00,0x40,0x08,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xA8,0x00,0x00,0x00,0x40,0x00,0x80,0x04,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0xD4,0x00,0x00,0xFF,0xFF,0x0F,0x80,0x04,0x00,0x00,0x00,0xF0,0x00,0x00,0x80,0x03,0xAA,0x00,0x00,0x55,0x55,0xF5,0xFF,0xFF,0xFF,0x0F,0x00,0xAE,0x00,0x00,0x60,0x04,0xD4,0x0F,0x00,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0x0E,0x80,0xD5,0x00,0x00,0x1C,0x00,0x7A,0x35,0x00,0x55,0x55,0x55,0x55,0x55,0x55,0x0F,0xC0,0xAA,0xFF,0xE1,0x03,0x00,0xAD,0x6A,0x00,0xAA,0xAA,0xAA,0xAA,0xAA,0xEA,0x0A,0x60,0xD5,0x01,0x1E,0x60,0x80,0x56,0xD5,0x00,0x55,0x55,0x55,0x55,0x55,0x75,0x0D,0xA0,0xAA,0x03,0x00,0x50,0x40,0x83,0xAA,0x01,0xAA,0xAA,0xAA,0xAA,0xAA,0xAE,0x0A,0x60,0x40,0x0F,0x00,0x2E,0xA0,0x01,0x55,0x03,0x55,0x55,0x55,0x55,0x55,0x57,0x3D,0x3E,0x80,0xF5,0xFF,0x15,0x50,0x01,0xAA,0x02,0xAA,0xAA,0xAA,0xAA,0xEA,0xAA,0xCA,0x21,0x80,0xAA,0xAA,0x0A,0xA8,0x00,0x55,0x03,0xFF,0xFF,0xFF,0xFF,0x7F,0x55,0x0D,0x20,0x80,0x55,0x55,0x05,0xD5,0x00,0xEA,0x07,0x88,0x88,0x88,0x88,0xB8,0xAA,0x0A,0x20,0xC0,0xAA,0xAA,0x82,0xAA,0x00,0x1D,0x18,0x55,0x55,0x55,0x55,0x57,0x55,0x0D,0x20,0x40,0x55,0x05,0x40,0x55,0x00,0x06,0x20,0x22,0x22,0x22,0xA2,0xB3,0xAA,0x0A,0x40,0x40,0x00,0x00,0xA0,0x6A,0x00,0x03,0x20,0x55,0x55,0x55,0x75,0x55,0x55,0x0D,0x80,0x40,0x00,0x00,0x50,0x7D,0x00,0x01,0x40,0x88,0x88,0x88,0x98,0xB8,0xAA,0x0A,0x00,0x21,0x00,0x00,0xA8,0x47,0x80,0x00,0x40,0x55,0x55,0x55,0x57,0x55,0x55,0x0D,0x00,0x3E,0x00,0x00,0xD5,0x40,0x80,0x00,0x40,0xFF,0xFF,0xFF,0x23,0xB2,0xAA,0x0A,0x00,0x00,0x00,0xA0,0x6A,0x40,0x40,0x00,0x80,0x01,0x00,0x80,0x55,0x55,0x55,0x0D,0x00,0x00,0x00,0x50,0x35,0x40,0x40,0x00,0x80,0x01,0x00,0x80,0x88,0xB8,0xAA,0x0A,0x00,0x00,0x00,0xAA,0x1A,0x40,0x20,0x00,0x80,0x01,0x00,0x80,0x55,0x55,0x55,0x0D,0x00,0x00,0x40,0x55,0x15,0x40,0x20,0x00,0x80,0x31,0xFC,0x8F,0x22,0xB2,0xAA,0x0A,0x00,0x00,0xA8,0xAA,0x0A,0x40,0x10,0x00,0x80,0x49,0x02,0x90,0x55,0x55,0x55,0x1D,0x00,0x40,0x55,0x55,0x0D,0x80,0x10,0x00,0x80,0x49,0x02,0x90,0x88,0xB8,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0x0E,0x80,0x08,0x00,0x80,0x31,0x02,0x90,0x55,0x55,0x55,0x5D,0x55,0x55,0x55,0x55,0x01,0x00,0x07,0x00,0x80,0x01,0x02,0x90,0x22,0xB2,0xAA,0xF6,0xAA,0xEA,0xFF,0xFF,0x01,0x00,0x04,0x00,0x40,0x11,0xFC,0x8F,0x55,0x55,0x55,0x85,0xD7,0x1F,0x00,0x00,0x1E,0x00,0x04,0x00,0x40,0x29,0x00,0x80,0x88,0xB8,0xAA,0x06,0x6C,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x20,0x29,0x00,0x80,0x55,0x55,0x55,0x05,0x38,0x00,0x00,0x00,0x40,0x00,0x04,0x00,0x20,0x15,0x00,0x80,0x22,0xB2,0xAA,0x06,0x10,0x00,0x00,0x00,0x80,0x00,0x04,0x00,0x20,0x0B,0x00,0x80,0x55,0x55,0x55,0x05,0x10,0x00,0x00,0x00,0x00,0x03,0x04,0x00,0x10,}; const uint8_t _A_WatchingTV_128x64_2[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x18,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x14,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x1F,0x00,0x00,0x00,0x00,0x02,0x00,0x1C,0x10,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x60,0x00,0x00,0x00,0x00,0x02,0x00,0x16,0x10,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x80,0x00,0x00,0x00,0x00,0x04,0x00,0x2A,0x08,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x36,0x04,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x02,0x00,0x00,0x00,0x10,0x00,0x62,0x02,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x00,0x10,0x00,0xC6,0x03,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x0A,0x07,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x10,0x00,0x00,0x00,0x40,0x00,0x86,0x08,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0x80,0x00,0x4A,0x08,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x20,0x00,0x00,0x00,0x80,0x00,0x24,0x08,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x01,0x3C,0xF0,0x3F,0x80,0x01,0x00,0x80,0x03,0x80,0x0F,0x20,0x00,0x00,0x00,0x00,0x02,0x18,0x00,0xC0,0x60,0x01,0x00,0x80,0x03,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x04,0x38,0x00,0x00,0x1F,0x01,0x00,0x80,0x03,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x04,0x14,0x00,0x00,0x80,0x00,0x00,0x80,0x03,0xE0,0x3F,0x60,0x00,0x00,0x00,0x00,0x08,0x14,0x00,0x00,0x40,0x00,0x00,0xC0,0x01,0x20,0x67,0x40,0x00,0x00,0x00,0x00,0x10,0x22,0x00,0x00,0x20,0x00,0x00,0x60,0x00,0x60,0xA7,0xA0,0x00,0x00,0x00,0x00,0xF8,0x27,0x00,0x00,0x18,0x00,0x00,0x18,0x00,0xC0,0x53,0xC1,0x00,0x00,0x00,0x00,0x06,0x58,0x00,0x00,0x06,0x00,0x00,0x0C,0x00,0x80,0xAF,0xA0,0x00,0x00,0x00,0x00,0x01,0x60,0x80,0xFF,0x01,0x00,0x00,0x06,0x00,0x00,0x50,0xD0,0x00,0x00,0x00,0x80,0x00,0x40,0x78,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xA0,0x00,0x00,0x00,0x40,0x00,0x80,0x04,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0xD0,0x00,0x00,0xFF,0xFF,0x0F,0x80,0x04,0x00,0x00,0x00,0xF0,0x00,0x00,0x80,0x03,0xA8,0x00,0x00,0x55,0x55,0xF5,0xFF,0xFF,0xFF,0x0F,0x00,0xAE,0x00,0x00,0x60,0x04,0xD4,0x0F,0x00,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0x0E,0x80,0xD5,0x00,0x00,0x1C,0x00,0x78,0x35,0x00,0x55,0x55,0x55,0x55,0x55,0x55,0x0F,0xC0,0xAA,0xFF,0xE1,0x03,0x00,0x0C,0x6A,0x00,0xAA,0xAA,0xAA,0xAA,0xAA,0xEA,0x0A,0x60,0x81,0x01,0x1E,0x60,0x00,0x06,0xD4,0x00,0x55,0x55,0x55,0x55,0x55,0x75,0x0D,0x20,0x00,0x03,0x00,0x10,0x00,0x03,0xA8,0x01,0xAA,0xAA,0xAA,0xAA,0xAA,0xAE,0x0A,0x20,0x00,0x0F,0x00,0x2E,0x80,0x01,0x50,0x03,0x55,0x55,0x55,0x55,0x55,0x57,0x3D,0x3E,0x80,0xF5,0xFF,0x05,0x40,0x01,0xA8,0x02,0xAA,0xAA,0xAA,0xAA,0xEA,0xAA,0xCA,0x21,0x80,0xAA,0xAA,0x02,0xA0,0x00,0x50,0x03,0xFF,0xFF,0xFF,0xFF,0x7F,0x55,0x0D,0x20,0x80,0x55,0x55,0x01,0xD0,0x00,0xE8,0x07,0x88,0x88,0x88,0x88,0xB8,0xAA,0x0A,0x20,0x40,0xAA,0x02,0x00,0xAA,0x00,0x18,0x18,0x55,0x55,0x55,0x55,0x57,0x55,0x0D,0x20,0x40,0x00,0x00,0x00,0x55,0x00,0x04,0x20,0x22,0x22,0x22,0xA2,0xB3,0xAA,0x0A,0x40,0x40,0x00,0x00,0x80,0x6A,0x00,0x02,0x20,0x55,0x55,0x55,0x75,0x55,0x55,0x0D,0x80,0x40,0x00,0x00,0x40,0x7D,0x00,0x01,0x40,0x88,0x88,0x88,0x98,0xB8,0xAA,0x0A,0x00,0x21,0x00,0x00,0xA0,0x47,0x80,0x00,0x40,0x55,0x55,0x55,0x57,0x55,0x55,0x0D,0x00,0x3E,0x00,0x00,0xD0,0x40,0x80,0x00,0x40,0xFF,0xFF,0xFF,0x23,0xB2,0xAA,0x0A,0x00,0x00,0x00,0x00,0x6A,0x40,0x40,0x00,0x80,0x01,0x00,0x80,0x55,0x55,0x55,0x0D,0x00,0x00,0x00,0x40,0x35,0x40,0x40,0x00,0x80,0x01,0x00,0x80,0x88,0xB8,0xAA,0x0A,0x00,0x00,0x00,0xA0,0x1A,0x40,0x20,0x00,0x80,0x01,0x00,0x80,0x55,0x55,0x55,0x0D,0x00,0x00,0x00,0x54,0x15,0x40,0x20,0x00,0x80,0x31,0xFC,0x8F,0x22,0xB2,0xAA,0x0A,0x00,0x00,0x80,0xAA,0x0A,0x40,0x10,0x00,0x80,0x49,0x02,0x90,0x55,0x55,0x55,0x0D,0x00,0x00,0x50,0x55,0x0D,0x80,0x10,0x00,0x80,0x49,0x02,0x90,0x88,0xB8,0xAA,0x2A,0x00,0x80,0xAA,0xAA,0x0E,0x80,0x08,0x00,0x80,0x31,0x02,0x90,0x55,0x55,0x55,0x5D,0x55,0x55,0x55,0x55,0x01,0x00,0x07,0x00,0x80,0x01,0x02,0x90,0x22,0xB2,0xAA,0xF6,0xAA,0xEA,0xFF,0xFF,0x01,0x00,0x04,0x00,0x40,0x11,0xFC,0x8F,0x55,0x55,0x55,0x85,0xD7,0x1F,0x00,0x00,0x1E,0x00,0x04,0x00,0x40,0x29,0x00,0x80,0x88,0xB8,0xAA,0x06,0x6C,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x20,0x29,0x00,0x80,0x55,0x55,0x55,0x05,0x38,0x00,0x00,0x00,0x40,0x00,0x04,0x00,0x20,0x15,0x00,0x80,0x22,0xB2,0xAA,0x06,0x10,0x00,0x00,0x00,0x80,0x00,0x04,0x00,0x20,0x0B,0x00,0x80,0x55,0x55,0x55,0x05,0x10,0x00,0x00,0x00,0x00,0x03,0x04,0x00,0x10,}; @@ -381,6 +387,9 @@ const uint8_t *_I_SDQuestion_35x43[] = {_I_SDQuestion_35x43_0}; const uint8_t _I_SDError_43x35_0[] = {0xFE,0xFF,0xFF,0xFF,0xFF,0x03,0x01,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x04,0x3D,0x00,0x00,0x00,0x00,0x04,0x3D,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x04,0x3D,0x30,0x18,0x0C,0x06,0x04,0x3D,0x60,0x0C,0x18,0x03,0x04,0x01,0xC0,0x06,0xB0,0x01,0x04,0x3D,0x80,0x03,0xE0,0x00,0x04,0x3D,0x80,0x03,0xE0,0x00,0x04,0x01,0xC0,0x06,0xB0,0x01,0x04,0x3D,0x60,0x0C,0x18,0x03,0x04,0x3D,0x30,0x18,0x0C,0x06,0x04,0x01,0x00,0x00,0x00,0x00,0x04,0x3D,0x00,0x00,0x00,0x00,0x04,0x3D,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x04,0x3D,0x00,0xFC,0x3F,0x00,0x04,0x3D,0x00,0xFF,0xFF,0x00,0x04,0x01,0x80,0x1F,0xF9,0x01,0x04,0x3D,0xC0,0x2F,0xF2,0x03,0x04,0x3D,0xC0,0x5F,0xE4,0x03,0x04,0x01,0x80,0x83,0xE4,0x01,0x04,0x3D,0x08,0x80,0x20,0x10,0x04,0x3D,0x08,0x00,0x11,0x10,0x04,0x01,0x30,0x00,0x0E,0x0C,0x04,0x3D,0x00,0x00,0x00,0x00,0x04,0x3D,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x04,0xFE,0xFF,0x00,0x00,0x00,0x04,0x00,0x00,0x01,0x1F,0x00,0x04,0x00,0x00,0x82,0x20,0x00,0x04,0x00,0x00,0xFC,0xC0,0xFF,0x03,}; const uint8_t *_I_SDError_43x35[] = {_I_SDError_43x35_0}; +const uint8_t _I_Cry_dolph_55x52_0[] = {0x00,0x00,0xF8,0xFF,0x01,0x00,0x00,0x00,0x00,0xFE,0xFF,0x0F,0x00,0x00,0x00,0x80,0x07,0x00,0x3E,0x00,0x00,0x00,0xE0,0x01,0x00,0xF0,0x00,0x00,0x00,0x70,0x00,0x00,0xC0,0x01,0x00,0x00,0x18,0x00,0x00,0x00,0x03,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x00,0x0C,0x00,0x00,0x03,0x00,0x00,0x00,0x18,0x00,0x80,0x01,0x00,0x00,0x00,0x18,0x00,0x80,0x01,0x00,0x00,0x00,0x30,0x00,0xC0,0x00,0x00,0x00,0x00,0x30,0x00,0xC0,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x00,0x00,0x00,0xC0,0x00,0x60,0x00,0x00,0x00,0x00,0xC0,0x00,0x30,0x00,0x00,0x00,0x00,0xC0,0x00,0x30,0x00,0x00,0x00,0x00,0x80,0x00,0x30,0x00,0x00,0x00,0x00,0x80,0x01,0x18,0x00,0x00,0x20,0x00,0x80,0x01,0x18,0x00,0x00,0x1E,0x00,0x88,0x01,0x18,0x00,0x00,0x00,0x00,0xF0,0x01,0x0C,0x00,0x00,0x00,0x00,0x80,0x01,0x0C,0x00,0x07,0x00,0x00,0x80,0x01,0x0C,0xA0,0x1F,0x00,0x00,0xE0,0x01,0x0C,0x50,0xFF,0x07,0x00,0xF0,0x01,0x0C,0xA0,0xFE,0x07,0x00,0xF0,0x01,0x0C,0x50,0xFF,0x07,0xB0,0xE3,0x01,0x0C,0xA0,0xF9,0x01,0x02,0x84,0x01,0x06,0xC0,0x08,0x00,0x00,0x80,0x01,0x06,0x40,0x04,0x00,0x00,0x80,0x01,0x06,0x40,0x04,0x00,0x00,0xA0,0x01,0x06,0x40,0x02,0x00,0x00,0xC0,0x01,0x06,0x80,0x01,0x00,0x00,0x80,0x01,0x06,0x00,0x00,0x00,0x00,0x00,0x03,0x06,0x00,0x80,0x07,0x00,0x00,0x06,0x06,0x00,0x60,0x18,0x00,0x00,0x0C,0x03,0x80,0x10,0x60,0x00,0x00,0x18,0x03,0x80,0x00,0x80,0x00,0x00,0x30,0x03,0x00,0x01,0x00,0x01,0x00,0x30,0x03,0x00,0x00,0x00,0x06,0x00,0x30,0x03,0x40,0x00,0x00,0x18,0x00,0x60,0x03,0x40,0x00,0x1A,0x60,0x00,0x60,0x03,0x08,0x00,0x75,0x80,0x01,0x60,0x03,0x00,0x00,0xE8,0x01,0x0E,0x60,0x03,0x40,0x40,0xD5,0x07,0xF0,0x3F,0x03,0x00,0x00,0xA0,0x3F,0x00,0x30,0x03,0x40,0x00,0x55,0xFF,0x01,0x18,0x03,0x48,0x00,0x80,0xFE,0x0F,0x0F,0x03,0x48,0x00,0x54,0xFD,0xFF,0x07,0x03,0x48,0x00,0x80,0xFA,0xF7,0x00,0x03,0x30,0x00,0x50,0xF5,0x6B,0x00,}; +const uint8_t *_I_Cry_dolph_55x52[] = {_I_Cry_dolph_55x52_0}; + const uint8_t _I_BadUsb_9x8_0[] = {0x01,0x01,0xBB,0x01,0xFE,0x00,0xFE,0x00,0xD6,0x00,0xD6,0x00,0x7C,0x00,0x38,0x00,}; const uint8_t *_I_BadUsb_9x8[] = {_I_BadUsb_9x8_0}; @@ -450,6 +459,8 @@ const uint8_t *_I_DolphinNice_96x59[] = {_I_DolphinNice_96x59_0}; const uint8_t _I_DolphinWait_61x59_0[] = {0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0x00,0x00,0x00,0xF0,0x01,0x70,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x80,0x01,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x01,0xF0,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x0C,0x03,0x00,0x00,0x01,0x00,0x40,0x00,0xF2,0x05,0x00,0x00,0x01,0x00,0x40,0x00,0xF9,0x0B,0x00,0x80,0x03,0x00,0x00,0x00,0x3D,0x0F,0x00,0x80,0x02,0x00,0x00,0x80,0x3C,0x17,0x00,0x80,0x07,0x00,0x10,0x80,0xFC,0x17,0x00,0x80,0x07,0x00,0x10,0x80,0xFC,0x17,0x00,0x80,0x07,0x00,0x08,0x80,0xFC,0x17,0x80,0xFF,0x7F,0x00,0x08,0x80,0xF8,0x0B,0x60,0x00,0x80,0x01,0x00,0x40,0xF3,0x09,0x1C,0x00,0x00,0x06,0x04,0xA8,0x02,0x04,0x03,0x00,0x00,0x08,0x04,0x54,0x0D,0x83,0x00,0x00,0x00,0x08,0x00,0xAA,0xFF,0x00,0x00,0x00,0x00,0x10,0x02,0xD5,0x38,0x00,0x00,0x00,0x00,0x10,0x82,0x2A,0x40,0x00,0x00,0x00,0x00,0x10,0x00,0x15,0x80,0x00,0x00,0x00,0x00,0x10,0x82,0x0A,0x00,0x00,0x00,0xF8,0x3F,0x10,0x03,0x0D,0x00,0x00,0x00,0x07,0xC0,0x17,0x81,0x0A,0x00,0x00,0xE0,0x00,0x00,0x08,0x03,0x0D,0x04,0x00,0x1C,0x00,0x00,0x08,0x81,0x1A,0x04,0x00,0x03,0x00,0x00,0x08,0x03,0x15,0x04,0xC0,0x00,0x00,0x00,0x08,0x01,0x0A,0x08,0x30,0x00,0x00,0x00,0x04,0x03,0x00,0x30,0x0C,0x00,0x00,0x00,0x02,0x01,0x00,0xC0,0x03,0x00,0x00,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x02,0x00,0x00,0x00,0x00,0x54,0x20,0x00,0x05,0x00,0x00,0x00,0x80,0xAA,0x1A,0x00,0x02,0x00,0x00,0x00,0x50,0x55,0x07,0x00,0x05,0x00,0x00,0x00,0xAA,0xAA,0x01,0x00,0x0A,0x00,0x00,0x40,0x7D,0xD5,0x01,0x00,0x15,0xFE,0xBF,0xEA,0x83,0xBF,0x01,0x00,0xEA,0x01,0xE0,0x3F,0x00,0x58,0x01,0x00,0x3D,0x00,0x80,0x63,0x00,0xA0,0x01,0x00,0x07,0x00,0xC0,0x80,0x00,0x40,0x01,0x00,0x00,0x00,0x40,0xBC,0x01,0x80,0x01,0x00,0x00,0x00,0x70,0x84,0x03,0x00,0x02,0x00,0x00,0x00,0x4E,0xC4,0x03,0x00,0x04,0x00,0x00,0x80,0xC1,0xF1,0x07,0x00,0x08,0x00,0x01,0x78,0x00,0xFF,0x0F,0x00,0x10,0x00,0x00,0x07,0x00,0xF0,0x0F,0x00,0x10,0x00,0x81,0x00,0x00,0xE0,0x1F,0x00,0x20,0x00,0x62,0x00,0x00,0xE0,0x1F,0x00,0x40,0x00,0x75,0x00,0x00,0xC0,0xFF,0x01,0x9F,0x00,0x6A,0x00,0x00,0xC0,0x03,0xFE,0xA0,0x00,0xF5,0x00,0x00,0x70,0x01,0x00,0x40,0x01,0xEA,0x1F,0x00,0xBE,0x02,0x00,0x40,0x02,0xD5,0xFF,0xFF,0x5F,0x05,0x00,0x20,0x02,}; const uint8_t *_I_DolphinWait_61x59[] = {_I_DolphinWait_61x59_0}; +const Icon I_Certification1_103x23 = {.width=103,.height=23,.frame_count=1,.frame_rate=0,.frames=_I_Certification1_103x23}; +const Icon I_Certification2_119x30 = {.width=119,.height=30,.frame_count=1,.frame_rate=0,.frames=_I_Certification2_119x30}; const Icon A_WatchingTV_128x64 = {.width=128,.height=64,.frame_count=5,.frame_rate=10,.frames=_A_WatchingTV_128x64}; const Icon A_Wink_128x64 = {.width=128,.height=64,.frame_count=9,.frame_rate=10,.frames=_A_Wink_128x64}; const Icon I_dir_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_dir_10px}; @@ -543,6 +554,7 @@ const Icon I_RFIDDolphinSend_97x61 = {.width=97,.height=61,.frame_count=1,.frame const Icon I_RFIDDolphinReceive_97x61 = {.width=97,.height=61,.frame_count=1,.frame_rate=0,.frames=_I_RFIDDolphinReceive_97x61}; const Icon I_SDQuestion_35x43 = {.width=35,.height=43,.frame_count=1,.frame_rate=0,.frames=_I_SDQuestion_35x43}; const Icon I_SDError_43x35 = {.width=43,.height=35,.frame_count=1,.frame_rate=0,.frames=_I_SDError_43x35}; +const Icon I_Cry_dolph_55x52 = {.width=55,.height=52,.frame_count=1,.frame_rate=0,.frames=_I_Cry_dolph_55x52}; const Icon I_BadUsb_9x8 = {.width=9,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_BadUsb_9x8}; const Icon I_PlaceholderR_30x13 = {.width=30,.height=13,.frame_count=1,.frame_rate=0,.frames=_I_PlaceholderR_30x13}; const Icon I_Background_128x8 = {.width=128,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Background_128x8}; diff --git a/assets/compiled/assets_icons.h b/assets/compiled/assets_icons.h index 312451f0..e16b3cf3 100644 --- a/assets/compiled/assets_icons.h +++ b/assets/compiled/assets_icons.h @@ -1,6 +1,8 @@ #pragma once #include +extern const Icon I_Certification1_103x23; +extern const Icon I_Certification2_119x30; extern const Icon A_WatchingTV_128x64; extern const Icon A_Wink_128x64; extern const Icon I_dir_10px; @@ -94,6 +96,7 @@ extern const Icon I_RFIDDolphinSend_97x61; extern const Icon I_RFIDDolphinReceive_97x61; extern const Icon I_SDQuestion_35x43; extern const Icon I_SDError_43x35; +extern const Icon I_Cry_dolph_55x52; extern const Icon I_BadUsb_9x8; extern const Icon I_PlaceholderR_30x13; extern const Icon I_Background_128x8; diff --git a/assets/icons/About/Certification1_103x23.png b/assets/icons/About/Certification1_103x23.png new file mode 100644 index 00000000..4713dc35 Binary files /dev/null and b/assets/icons/About/Certification1_103x23.png differ diff --git a/assets/icons/About/Certification2_119x30.png b/assets/icons/About/Certification2_119x30.png new file mode 100644 index 00000000..8b01b0d1 Binary files /dev/null and b/assets/icons/About/Certification2_119x30.png differ diff --git a/assets/icons/Settings/Cry_dolph_55x52.png b/assets/icons/Settings/Cry_dolph_55x52.png new file mode 100644 index 00000000..86d9db1b Binary files /dev/null and b/assets/icons/Settings/Cry_dolph_55x52.png differ diff --git a/assets/icons/SubGhz/lock_7x8.png b/assets/icons/SubGhz/Lock_7x8.png similarity index 100% rename from assets/icons/SubGhz/lock_7x8.png rename to assets/icons/SubGhz/Lock_7x8.png diff --git a/assets/icons/SubGhz/quest_7x8.png b/assets/icons/SubGhz/Quest_7x8.png similarity index 100% rename from assets/icons/SubGhz/quest_7x8.png rename to assets/icons/SubGhz/Quest_7x8.png diff --git a/assets/icons/SubGhz/unlock_7x8.png b/assets/icons/SubGhz/Unlock_7x8.png similarity index 100% rename from assets/icons/SubGhz/unlock_7x8.png rename to assets/icons/SubGhz/Unlock_7x8.png diff --git a/firmware/targets/f6/ble-glue/app_conf.h b/firmware/targets/f6/ble-glue/app_conf.h index bfd6f9dd..e3820e11 100644 --- a/firmware/targets/f6/ble-glue/app_conf.h +++ b/firmware/targets/f6/ble-glue/app_conf.h @@ -367,12 +367,12 @@ typedef enum /** * When set to 1, the traces are enabled in the BLE services */ -#define CFG_DEBUG_BLE_TRACE 1 +#define CFG_DEBUG_BLE_TRACE 0 /** * Enable or Disable traces in application */ -#define CFG_DEBUG_APP_TRACE 1 +#define CFG_DEBUG_APP_TRACE 0 #if (CFG_DEBUG_APP_TRACE != 0) #define APP_DBG_MSG PRINT_MESG_DBG diff --git a/firmware/targets/f6/ble-glue/app_debug.c b/firmware/targets/f6/ble-glue/app_debug.c new file mode 100644 index 00000000..b2793073 --- /dev/null +++ b/firmware/targets/f6/ble-glue/app_debug.c @@ -0,0 +1,375 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * File Name : app_debug.c + * Description : Debug capabilities source file for STM32WPAN Middleware + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2020 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Includes ------------------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +#include "utilities_common.h" + +#include "app_common.h" +#include "app_debug.h" +#include "shci.h" +#include "tl.h" +#include "dbg_trace.h" +#include +/* USER CODE END Includes */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN PTD */ +typedef PACKED_STRUCT +{ + GPIO_TypeDef* port; + uint16_t pin; + uint8_t enable; + uint8_t reserved; +} APPD_GpioConfig_t; +/* USER CODE END PTD */ + +/* Private defines -----------------------------------------------------------*/ +/* USER CODE BEGIN PD */ +#define GPIO_NBR_OF_RF_SIGNALS 9 +#define GPIO_CFG_NBR_OF_FEATURES 34 +#define NBR_OF_TRACES_CONFIG_PARAMETERS 4 +#define NBR_OF_GENERAL_CONFIG_PARAMETERS 4 + +/** + * THIS SHALL BE SET TO A VALUE DIFFERENT FROM 0 ONLY ON REQUEST FROM ST SUPPORT + */ +#define BLE_DTB_CFG 7 +#define SYS_DBG_CFG1 (SHCI_C2_DEBUG_OPTIONS_IPCORE_LP | SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN) +/* USER CODE END PD */ + +/* Private variables ---------------------------------------------------------*/ +/* USER CODE BEGIN PV */ +PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig={0, 0, 0, 0}; +PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig={BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}}; + +/** + * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT + * It provides timing information on the CPU2 activity. + * All configuration of (port, pin) is supported for each features and can be selected by the user + * depending on the availability + */ +static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] = +{ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_ISR - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_7, 1, 0}, /* BLE_STACK_TICK - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_CMD_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_ACL_DATA_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* SYS_CMD_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* RNG_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVM_PROCESS - Set on Entry / Reset on Exit */ + { GPIOB, LL_GPIO_PIN_3, 1, 0}, /* IPCC_GENERAL - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_EVT_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_ACL_DATA_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_SYS_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_SYS_EVT_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_CLI_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_OT_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_OT_ACK_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_CLI_ACK_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_MEM_MANAGER_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_TRACES_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_6, 1, 0}, /* HARD_FAULT - Set on Entry / Reset on Exit */ +/* From v1.1.1 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */ +/* From v1.2.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */ + { GPIOA, LL_GPIO_PIN_4, 1, 0}, /* PES_ACTIVITY - Set on Entry / Reset on Exit */ + { GPIOB, LL_GPIO_PIN_2, 1, 0}, /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */ +/* From v1.3.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_NO_DELAY - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_STACK_STORE_NVM_CB - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_WRITE_ONGOING - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_WRITE_COMPLETE - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_CLEANUP - Set on Entry / Reset on Exit */ +/* From v1.4.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_START - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_EOP - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_WRITE - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_ERASE - Set on Entry / Reset on Exit */ +}; + +/** + * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT + * This table is relevant only for BLE + * It provides timing information on BLE RF activity. + * New signals may be allocated at any location when requested by ST + * The GPIO allocated to each signal depend on the BLE_DTB_CFG value and cannot be changed + */ +#if( BLE_DTB_CFG == 7) +static const APPD_GpioConfig_t aRfConfigList[GPIO_NBR_OF_RF_SIGNALS] = +{ + { GPIOB, LL_GPIO_PIN_2, 0, 0}, /* DTB10 - Tx/Rx SPI */ + { GPIOB, LL_GPIO_PIN_7, 0, 0}, /* DTB11 - Tx/Tx SPI Clk */ + { GPIOA, LL_GPIO_PIN_8, 0, 0}, /* DTB12 - Tx/Rx Ready & SPI Select */ + { GPIOA, LL_GPIO_PIN_9, 0, 0}, /* DTB13 - Tx/Rx Start */ + { GPIOA, LL_GPIO_PIN_10, 0, 0}, /* DTB14 - FSM0 */ + { GPIOA, LL_GPIO_PIN_11, 0, 0}, /* DTB15 - FSM1 */ + { GPIOB, LL_GPIO_PIN_8, 0, 0}, /* DTB16 - FSM2 */ + { GPIOB, LL_GPIO_PIN_11, 0, 0}, /* DTB17 - FSM3 */ + { GPIOB, LL_GPIO_PIN_10, 0, 0}, /* DTB18 - FSM4 */ +}; +#endif +/* USER CODE END PV */ + +/* Global variables ----------------------------------------------------------*/ +/* USER CODE BEGIN GV */ +/* USER CODE END GV */ + +/* Private function prototypes -----------------------------------------------*/ +/* USER CODE BEGIN PFP */ +static void APPD_SetCPU2GpioConfig( void ); +static void APPD_BleDtbCfg( void ); +/* USER CODE END PFP */ + +/* Functions Definition ------------------------------------------------------*/ +void APPD_Init( void ) +{ +/* USER CODE BEGIN APPD_Init */ +#if (CFG_DEBUGGER_SUPPORTED == 1) + /** + * Keep debugger enabled while in any low power mode + */ + HAL_DBGMCU_EnableDBGSleepMode(); + HAL_DBGMCU_EnableDBGStopMode(); + + /***************** ENABLE DEBUGGER *************************************/ + LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); + +#else + GPIO_InitTypeDef gpio_config = {0}; + + gpio_config.Pull = GPIO_NOPULL; + gpio_config.Mode = GPIO_MODE_ANALOG; + + gpio_config.Pin = GPIO_PIN_15 | GPIO_PIN_14 | GPIO_PIN_13; + __HAL_RCC_GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOA, &gpio_config); + __HAL_RCC_GPIOA_CLK_DISABLE(); + + gpio_config.Pin = GPIO_PIN_4 | GPIO_PIN_3; + __HAL_RCC_GPIOB_CLK_ENABLE(); + HAL_GPIO_Init(GPIOB, &gpio_config); + __HAL_RCC_GPIOB_CLK_DISABLE(); + + HAL_DBGMCU_DisableDBGSleepMode(); + HAL_DBGMCU_DisableDBGStopMode(); + HAL_DBGMCU_DisableDBGStandbyMode(); + +#endif /* (CFG_DEBUGGER_SUPPORTED == 1) */ + +#if(CFG_DEBUG_TRACE != 0) + DbgTraceInit(); +#endif + + APPD_SetCPU2GpioConfig( ); + APPD_BleDtbCfg( ); + +/* USER CODE END APPD_Init */ + return; +} + +void APPD_EnableCPU2( void ) +{ +/* USER CODE BEGIN APPD_EnableCPU2 */ + SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket = + { + {{0,0,0}}, /**< Does not need to be initialized */ + {(uint8_t *)aGpioConfigList, + (uint8_t *)&APPD_TracesConfig, + (uint8_t *)&APPD_GeneralConfig, + GPIO_CFG_NBR_OF_FEATURES, + NBR_OF_TRACES_CONFIG_PARAMETERS, + NBR_OF_GENERAL_CONFIG_PARAMETERS} + }; + + /**< Traces channel initialization */ + TL_TRACES_Init( ); + + /** GPIO DEBUG Initialization */ + SHCI_C2_DEBUG_Init( &DebugCmdPacket ); + + // GPIO_InitTypeDef GPIO_InitStruct; + // GPIO_InitStruct.Pull = GPIO_NOPULL; + // GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + // GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + // GPIO_InitStruct.Pin = LL_GPIO_PIN_3; + // HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + // SHCI_C2_ExtpaConfig((uint32_t)GPIOC, LL_GPIO_PIN_3, EXT_PA_ENABLED_LOW, EXT_PA_ENABLED); + +/* USER CODE END APPD_EnableCPU2 */ + return; +} + +/************************************************************* + * + * LOCAL FUNCTIONS + * + *************************************************************/ +static void APPD_SetCPU2GpioConfig( void ) +{ +/* USER CODE BEGIN APPD_SetCPU2GpioConfig */ + GPIO_InitTypeDef gpio_config = {0}; + uint8_t local_loop; + uint16_t gpioa_pin_list; + uint16_t gpiob_pin_list; + uint16_t gpioc_pin_list; + + gpioa_pin_list = 0; + gpiob_pin_list = 0; + gpioc_pin_list = 0; + + for(local_loop = 0 ; local_loop < GPIO_CFG_NBR_OF_FEATURES; local_loop++) + { + if( aGpioConfigList[local_loop].enable != 0) + { + switch((uint32_t)aGpioConfigList[local_loop].port) + { + case (uint32_t)GPIOA: + gpioa_pin_list |= aGpioConfigList[local_loop].pin; + break; + + case (uint32_t)GPIOB: + gpiob_pin_list |= aGpioConfigList[local_loop].pin; + break; + + case (uint32_t)GPIOC: + gpioc_pin_list |= aGpioConfigList[local_loop].pin; + break; + + default: + break; + } + } + } + + gpio_config.Pull = GPIO_NOPULL; + gpio_config.Mode = GPIO_MODE_OUTPUT_PP; + gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + + if(gpioa_pin_list != 0) + { + gpio_config.Pin = gpioa_pin_list; + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_C2GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOA, &gpio_config); + HAL_GPIO_WritePin(GPIOA, gpioa_pin_list, GPIO_PIN_RESET); + } + + if(gpiob_pin_list != 0) + { + gpio_config.Pin = gpiob_pin_list; + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_C2GPIOB_CLK_ENABLE(); + HAL_GPIO_Init(GPIOB, &gpio_config); + HAL_GPIO_WritePin(GPIOB, gpiob_pin_list, GPIO_PIN_RESET); + } + + if(gpioc_pin_list != 0) + { + gpio_config.Pin = gpioc_pin_list; + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_C2GPIOC_CLK_ENABLE(); + HAL_GPIO_Init(GPIOC, &gpio_config); + HAL_GPIO_WritePin(GPIOC, gpioc_pin_list, GPIO_PIN_RESET); + } + +/* USER CODE END APPD_SetCPU2GpioConfig */ + return; +} + +static void APPD_BleDtbCfg( void ) +{ +/* USER CODE BEGIN APPD_BleDtbCfg */ +#if (BLE_DTB_CFG != 0) + GPIO_InitTypeDef gpio_config = {0}; + uint8_t local_loop; + uint16_t gpioa_pin_list; + uint16_t gpiob_pin_list; + + gpioa_pin_list = 0; + gpiob_pin_list = 0; + + for(local_loop = 0 ; local_loop < GPIO_NBR_OF_RF_SIGNALS; local_loop++) + { + if( aRfConfigList[local_loop].enable != 0) + { + switch((uint32_t)aRfConfigList[local_loop].port) + { + case (uint32_t)GPIOA: + gpioa_pin_list |= aRfConfigList[local_loop].pin; + break; + + case (uint32_t)GPIOB: + gpiob_pin_list |= aRfConfigList[local_loop].pin; + break; + + default: + break; + } + } + } + + gpio_config.Pull = GPIO_NOPULL; + gpio_config.Mode = GPIO_MODE_AF_PP; + gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + gpio_config.Alternate = GPIO_AF6_RF_DTB7; + + if(gpioa_pin_list != 0) + { + gpio_config.Pin = gpioa_pin_list; + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_C2GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOA, &gpio_config); + } + + if(gpiob_pin_list != 0) + { + gpio_config.Pin = gpiob_pin_list; + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_C2GPIOB_CLK_ENABLE(); + HAL_GPIO_Init(GPIOB, &gpio_config); + } +#endif + +/* USER CODE END APPD_BleDtbCfg */ + return; +} + +/************************************************************* + * + * WRAP FUNCTIONS + * +*************************************************************/ +#if(CFG_DEBUG_TRACE != 0) +void DbgOutputInit( void ) +{ +} + +void DbgOutputTraces( uint8_t *p_data, uint16_t size, void (*cb)(void) ) +{ + furi_hal_console_tx(p_data, size); + cb(); +} +#endif + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ \ No newline at end of file diff --git a/firmware/targets/f6/ble-glue/app_debug.h b/firmware/targets/f6/ble-glue/app_debug.h new file mode 100644 index 00000000..2cb31e53 --- /dev/null +++ b/firmware/targets/f6/ble-glue/app_debug.h @@ -0,0 +1,38 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * File Name : app_debug.h + * Description : Header for app_debug.c module + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2020 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __APP_DEBUG_H +#define __APP_DEBUG_H + +#ifdef __cplusplus +extern "C" { +#endif + + void APPD_Init( void ); + void APPD_EnableCPU2( void ); + +#ifdef __cplusplus +} +#endif + +#endif /*__APP_DEBUG_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ \ No newline at end of file diff --git a/firmware/targets/f6/ble-glue/app_entry.c b/firmware/targets/f6/ble-glue/app_entry.c index a970da32..5c8c0b2a 100644 --- a/firmware/targets/f6/ble-glue/app_entry.c +++ b/firmware/targets/f6/ble-glue/app_entry.c @@ -6,6 +6,7 @@ #include "tl.h" #include "cmsis_os.h" #include "shci_tl.h" +#include "app_debug.h" #include extern RTC_HandleTypeDef hrtc; @@ -177,16 +178,3 @@ void shci_cmd_resp_wait(uint32_t timeout) { UNUSED(timeout); osSemaphoreAcquire( SemShciId, osWaitForever ); } - -#if(CFG_DEBUG_TRACE != 0) -void DbgOutputInit( void ) -{ -} - -void DbgOutputTraces( uint8_t *p_data, uint16_t size, void (*cb)(void) ) -{ - furi_hal_console_tx(p_data, size); - cb(); -} -#endif - diff --git a/firmware/targets/f6/ble-glue/ble_dbg_conf.h b/firmware/targets/f6/ble-glue/ble_dbg_conf.h index 4eb0239b..7bb35216 100644 --- a/firmware/targets/f6/ble-glue/ble_dbg_conf.h +++ b/firmware/targets/f6/ble-glue/ble_dbg_conf.h @@ -25,13 +25,13 @@ * Enable or Disable traces from BLE */ -#define BLE_DBG_APP_EN 0 -#define BLE_DBG_DIS_EN 0 -#define BLE_DBG_HRS_EN 0 -#define BLE_DBG_SVCCTL_EN 0 -#define BLE_DBG_BLS_EN 0 -#define BLE_DBG_HTS_EN 0 -#define BLE_DBG_P2P_STM_EN 0 +#define BLE_DBG_APP_EN 1 +#define BLE_DBG_DIS_EN 1 +#define BLE_DBG_HRS_EN 1 +#define BLE_DBG_SVCCTL_EN 1 +#define BLE_DBG_BLS_EN 1 +#define BLE_DBG_HTS_EN 1 +#define BLE_DBG_P2P_STM_EN 1 /** * Macro definition diff --git a/firmware/targets/f6/ble-glue/tl_dbg_conf.h b/firmware/targets/f6/ble-glue/tl_dbg_conf.h index 4c38cfb5..842cba0e 100644 --- a/firmware/targets/f6/ble-glue/tl_dbg_conf.h +++ b/firmware/targets/f6/ble-glue/tl_dbg_conf.h @@ -33,26 +33,23 @@ extern "C" { #include "app_conf.h" /* required as some configuration used in dbg_trace.h are set there */ #include "dbg_trace.h" #include "hw_if.h" +#include /** * Enable or Disable traces * The raw data output is the hci binary packet format as specified by the BT specification * */ -#define TL_SHCI_CMD_DBG_EN 0 /* Reports System commands sent to CPU2 and the command response */ +#define TL_SHCI_CMD_DBG_EN 1 /* Reports System commands sent to CPU2 and the command response */ #define TL_SHCI_CMD_DBG_RAW_EN 0 /* Reports raw data System commands sent to CPU2 and the command response */ -#define TL_SHCI_EVT_DBG_EN 0 /* Reports System Asynchronous Events received from CPU2 */ +#define TL_SHCI_EVT_DBG_EN 1 /* Reports System Asynchronous Events received from CPU2 */ #define TL_SHCI_EVT_DBG_RAW_EN 0 /* Reports raw data System Asynchronous Events received from CPU2 */ -#define TL_HCI_CMD_DBG_EN 0 /* Reports BLE command sent to CPU2 and the command response */ +#define TL_HCI_CMD_DBG_EN 1 /* Reports BLE command sent to CPU2 and the command response */ #define TL_HCI_CMD_DBG_RAW_EN 0 /* Reports raw data BLE command sent to CPU2 and the command response */ -#define TL_HCI_EVT_DBG_EN 0 /* Reports BLE Asynchronous Events received from CPU2 */ +#define TL_HCI_EVT_DBG_EN 1 /* Reports BLE Asynchronous Events received from CPU2 */ #define TL_HCI_EVT_DBG_RAW_EN 0 /* Reports raw data BLE Asynchronous Events received from CPU2 */ -#define TL_MM_DBG_EN 0 /* Reports the informations of the buffer released to CPU2 */ - -/** - * Macro definition - */ +#define TL_MM_DBG_EN 1 /* Reports the informations of the buffer released to CPU2 */ /** * System Transport Layer @@ -66,7 +63,7 @@ extern "C" { #endif #if (TL_SHCI_CMD_DBG_RAW_EN != 0) -#define TL_SHCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx(_PDATA_, _SIZE_) +#define TL_SHCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) #else #define TL_SHCI_CMD_DBG_RAW(...) #endif @@ -80,7 +77,7 @@ extern "C" { #endif #if (TL_SHCI_EVT_DBG_RAW_EN != 0) -#define TL_SHCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx(_PDATA_, _SIZE_) +#define TL_SHCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) #else #define TL_SHCI_EVT_DBG_RAW(...) #endif @@ -97,7 +94,7 @@ extern "C" { #endif #if (TL_HCI_CMD_DBG_RAW_EN != 0) -#define TL_HCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx(_PDATA_, _SIZE_) +#define TL_HCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) #else #define TL_HCI_CMD_DBG_RAW(...) #endif @@ -111,7 +108,7 @@ extern "C" { #endif #if (TL_HCI_EVT_DBG_RAW_EN != 0) -#define TL_HCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx(_PDATA_, _SIZE_) +#define TL_HCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) #else #define TL_HCI_EVT_DBG_RAW(...) #endif diff --git a/firmware/targets/f6/furi-hal/furi-hal-bt.c b/firmware/targets/f6/furi-hal/furi-hal-bt.c index 7fab63ed..01f4b919 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-bt.c +++ b/firmware/targets/f6/furi-hal/furi-hal-bt.c @@ -26,6 +26,9 @@ void furi_hal_bt_start_advertising() { void furi_hal_bt_stop_advertising() { if(furi_hal_bt_is_active()) { gap_stop_advertising(); + while(furi_hal_bt_is_active()) { + osDelay(1); + } } } @@ -61,7 +64,7 @@ bool furi_hal_bt_is_active() { } bool furi_hal_bt_wait_startup() { - uint8_t counter = 0; + uint16_t counter = 0; while (!(APPE_Status() == BleGlueStatusStarted || APPE_Status() == BleGlueStatusBroken)) { osDelay(10); counter++; @@ -85,7 +88,9 @@ bool furi_hal_bt_lock_flash(bool erase_flag) { if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON); - while(LL_FLASH_IsActiveFlag_OperationSuspended()) {}; + while(LL_FLASH_IsActiveFlag_OperationSuspended()) { + osDelay(1); + }; __disable_irq(); diff --git a/firmware/targets/f6/furi-hal/furi-hal-console.c b/firmware/targets/f6/furi-hal/furi-hal-console.c index 6c67e0c8..b04a17a1 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-console.c +++ b/firmware/targets/f6/furi-hal/furi-hal-console.c @@ -41,10 +41,7 @@ void furi_hal_console_init() { FURI_LOG_I("FuriHalConsole", "Init OK"); } -void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { - if (!furi_hal_console_alive) - return; - +static void furi_hal_console_uart_tx(const uint8_t* buffer, size_t buffer_size) { while(buffer_size > 0) { while (!LL_USART_IsActiveFlag_TXE(USART1)); @@ -53,8 +50,27 @@ void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { buffer++; buffer_size--; } +} - /* Wait for TC flag to be raised for last char */ +void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { + if (!furi_hal_console_alive) + return; + + // Transmit data + furi_hal_console_uart_tx(buffer, buffer_size); + // Wait for TC flag to be raised for last char + while (!LL_USART_IsActiveFlag_TC(USART1)); +} + +void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size) { + if (!furi_hal_console_alive) + return; + + // Transmit data + furi_hal_console_uart_tx(buffer, buffer_size); + // Transmit new line symbols + furi_hal_console_uart_tx((const uint8_t*)"\r\n", 2); + // Wait for TC flag to be raised for last char while (!LL_USART_IsActiveFlag_TC(USART1)); } diff --git a/firmware/targets/f6/furi-hal/furi-hal-console.h b/firmware/targets/f6/furi-hal/furi-hal-console.h index 26ca4540..cd7ccae8 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-console.h +++ b/firmware/targets/f6/furi-hal/furi-hal-console.h @@ -11,6 +11,8 @@ void furi_hal_console_init(); void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size); +void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size); + /** * Printf-like plain uart interface * @warning Will not work in ISR context diff --git a/firmware/targets/f6/furi-hal/furi-hal-ibutton.c b/firmware/targets/f6/furi-hal/furi-hal-ibutton.c index 6e3c8bee..8a0af9f9 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-ibutton.c +++ b/firmware/targets/f6/furi-hal/furi-hal-ibutton.c @@ -3,12 +3,12 @@ void furi_hal_ibutton_start() { furi_hal_ibutton_pin_high(); - hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); } void furi_hal_ibutton_stop() { furi_hal_ibutton_pin_high(); - hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } void furi_hal_ibutton_pin_low() { diff --git a/firmware/targets/f6/furi-hal/furi-hal-os-timer.h b/firmware/targets/f6/furi-hal/furi-hal-os-timer.h index f210b539..8b7752e8 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-os-timer.h +++ b/firmware/targets/f6/furi-hal/furi-hal-os-timer.h @@ -20,6 +20,7 @@ static inline void furi_hal_os_timer_init() { } static inline void furi_hal_os_timer_continuous(uint32_t count) { + count--; // Enable timer LL_LPTIM_Enable(FURI_HAL_OS_TIMER); while(!LL_LPTIM_IsEnabled(FURI_HAL_OS_TIMER)); @@ -33,6 +34,7 @@ static inline void furi_hal_os_timer_continuous(uint32_t count) { } static inline void furi_hal_os_timer_single(uint32_t count) { + count--; // Enable timer LL_LPTIM_Enable(FURI_HAL_OS_TIMER); while(!LL_LPTIM_IsEnabled(FURI_HAL_OS_TIMER)); diff --git a/firmware/targets/f6/furi-hal/furi-hal-rfid.c b/firmware/targets/f6/furi-hal/furi-hal-rfid.c index 8b5266c3..a9bbc32c 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-rfid.c +++ b/firmware/targets/f6/furi-hal/furi-hal-rfid.c @@ -18,11 +18,11 @@ void furi_hal_rfid_pins_reset() { furi_hal_ibutton_stop(); // pulldown rfid antenna - hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_rfid_carrier_out, false); // from both sides - hal_gpio_init(&gpio_rfid_pull, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&gpio_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_rfid_pull, true); } @@ -36,7 +36,7 @@ void furi_hal_rfid_pins_emulate() { &gpio_rfid_pull, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); // pull rfid antenna from carrier side - hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_rfid_carrier_out, false); } @@ -46,7 +46,7 @@ void furi_hal_rfid_pins_read() { furi_hal_ibutton_pin_low(); // dont pull rfid antenna - hal_gpio_init(&gpio_rfid_pull, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&gpio_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_rfid_pull, false); // carrier pin to timer out @@ -58,7 +58,7 @@ void furi_hal_rfid_pins_read() { GpioAltFn1TIM1); // comparator in - hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } void furi_hal_rfid_tim_read(float freq, float duty_cycle) { diff --git a/firmware/targets/f6/furi-hal/furi-hal-subghz.c b/firmware/targets/f6/furi-hal/furi-hal-subghz.c index 4d1f0cbf..34446d8d 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-subghz.c +++ b/firmware/targets/f6/furi-hal/furi-hal-subghz.c @@ -1,4 +1,5 @@ #include "furi-hal-subghz.h" +#include "furi-hal-version.h" #include #include @@ -10,6 +11,7 @@ #include static volatile SubGhzState furi_hal_subghz_state = SubGhzStateInit; +static volatile SubGhzRegulation furi_hal_subghz_regulation = SubGhzRegulationTxRx; static const uint8_t furi_hal_subghz_preset_ook_270khz_async_regs[][2] = { // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration @@ -41,11 +43,11 @@ static const uint8_t furi_hal_subghz_preset_ook_270khz_async_regs[][2] = { 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCTRL0, + {CC1101_AGCCTRL0, 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCTRL1, + {CC1101_AGCCTRL1, 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours @@ -99,11 +101,15 @@ static const uint8_t furi_hal_subghz_preset_ook_650khz_async_regs[][2] = { 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCTRL0, - 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCTRL1, - 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + // {CC1101_AGCTRL0,0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary + // {CC1101_AGCTRL1,0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + // {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7. + {CC1101_AGCCTRL0, + 0x91}, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + {CC1101_AGCCTRL1, + 0x0}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours @@ -131,23 +137,21 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_regs[][2] = { /* GPIO GD0 */ {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - /* FIFO and internals */ - {CC1101_FIFOTHR, 0x47}, // The only important bit is ADC_RETENTION - - /* Packet engine */ - {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening - /* Frequency Synthesizer Control */ {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - // Modem Configuration - {CC1101_MDMCFG0, 0x00}, - {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) - {CC1101_MDMCFG3, 0x8B}, // Data rate is 19.5885 kBaud - {CC1101_MDMCFG4, 0x69}, // Rx BW filter is 270.833333 kHz + /* Packet engine */ + {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening + {CC1101_PKTCTRL1, 0x04}, - {CC1101_DEVIATN, 0x47}, //Deviation 47.607422 khz + // // Modem Configuration + {CC1101_MDMCFG0, 0x00}, + {CC1101_MDMCFG1, 0x2}, + {CC1101_MDMCFG2, 0x4}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) + {CC1101_MDMCFG3, 0x83}, // Data rate is 4.79794 kBaud + {CC1101_MDMCFG4, 0x67}, //Rx BW filter is 270.833333 kHz + //{ CC1101_DEVIATN, 0x14 }, //Deviation 4.760742 kHz + {CC1101_DEVIATN, 0x04}, //Deviation 2.380371 kHz /* Main Radio Control State Machine */ {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) @@ -157,18 +161,18 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_regs[][2] = { 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCTRL0, - 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCTRL1, + {CC1101_AGCCTRL0, + 0x91}, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + {CC1101_AGCCTRL1, 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours /* Frontend configuration */ {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer - {CC1101_FREND1, 0xB6}, // + {CC1101_FREND1, 0x56}, /* Frequency Synthesizer Calibration, valid for 433.92 */ {CC1101_FSCAL3, 0xE9}, @@ -281,7 +285,7 @@ void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { } else if(preset == FuriHalSubGhzPreset2FSKAsync) { furi_hal_subghz_load_registers(furi_hal_subghz_preset_2fsk_async_regs); furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); - }else { + } else { furi_crash(NULL); } } @@ -358,10 +362,12 @@ void furi_hal_subghz_rx() { furi_hal_spi_device_return(device); } -void furi_hal_subghz_tx() { +bool furi_hal_subghz_tx() { + if(furi_hal_subghz_regulation != SubGhzRegulationTxRx) return false; const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz); cc1101_switch_to_tx(device); furi_hal_spi_device_return(device); + return true; } float furi_hal_subghz_get_rssi() { @@ -385,6 +391,7 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value) { !(value >= 778999847 && value <= 928000000)) { return false; } + return true; } @@ -405,6 +412,46 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { uint32_t furi_hal_subghz_set_frequency(uint32_t value) { const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz); + //checking regional settings + bool txrx = false; + switch(furi_hal_version_get_hw_region()) { + case FuriHalVersionRegionEuRu: + //433,05..434,79; 868,15..868,55 + if(!(value >= 433050000 && value <= 434790000) && + !(value >= 868150000 && value <= 8680550000)) { + } else { + txrx = true; + } + break; + case FuriHalVersionRegionUsCaAu: + //304,10..315,25; 433,05..434,79; 915,00..928,00 + if(!(value >= 304100000 && value <= 315250000) && + !(value >= 433050000 && value <= 434790000) && + !(value >= 915000000 && value <= 928000000)) { + } else { + txrx = true; + } + break; + case FuriHalVersionRegionJp: + //312,00..315,25; 920,50..923,50 + if(!(value >= 312000000 && value <= 315250000) && + !(value >= 920500000 && value <= 923500000)) { + } else { + txrx = true; + } + break; + + default: + txrx = true; + break; + } + + if(txrx) { + furi_hal_subghz_regulation = SubGhzRegulationTxRx; + } else { + furi_hal_subghz_regulation = SubGhzRegulationOnlyRx; + } + uint32_t real_frequency = cc1101_set_frequency(device, value); cc1101_calibrate(device); @@ -482,7 +529,7 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* TIM_InitStruct.Prescaler = 64 - 1; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 0x7FFFFFFE; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; LL_TIM_Init(TIM2, &TIM_InitStruct); // Timer: advanced @@ -505,7 +552,7 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI); LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); + LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV32_N8); // ISR setup furi_hal_interrupt_set_timer_isr(TIM2, furi_hal_subghz_capture_ISR); @@ -610,6 +657,8 @@ 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; + //forcibly pulls the pin to the ground so that there is no carrier + hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow); } else { furi_hal_subghz_state = SubGhzStateAsyncTxEnd; LL_TIM_DisableCounter(TIM2); @@ -618,10 +667,13 @@ static void furi_hal_subghz_async_tx_timer_isr() { } } -void furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { +bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { furi_assert(furi_hal_subghz_state == SubGhzStateIdle); furi_assert(callback); + //If transmission is prohibited by regional settings + if(furi_hal_subghz_regulation != SubGhzRegulationTxRx) return false; + furi_hal_subghz_async_tx.callback = callback; furi_hal_subghz_async_tx.callback_context = context; @@ -696,6 +748,7 @@ void furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); + return true; } bool furi_hal_subghz_is_async_tx_complete() { diff --git a/firmware/targets/f7/ble-glue/app_conf.h b/firmware/targets/f7/ble-glue/app_conf.h index bfd6f9dd..e3820e11 100644 --- a/firmware/targets/f7/ble-glue/app_conf.h +++ b/firmware/targets/f7/ble-glue/app_conf.h @@ -367,12 +367,12 @@ typedef enum /** * When set to 1, the traces are enabled in the BLE services */ -#define CFG_DEBUG_BLE_TRACE 1 +#define CFG_DEBUG_BLE_TRACE 0 /** * Enable or Disable traces in application */ -#define CFG_DEBUG_APP_TRACE 1 +#define CFG_DEBUG_APP_TRACE 0 #if (CFG_DEBUG_APP_TRACE != 0) #define APP_DBG_MSG PRINT_MESG_DBG diff --git a/firmware/targets/f7/ble-glue/app_debug.c b/firmware/targets/f7/ble-glue/app_debug.c new file mode 100644 index 00000000..b2793073 --- /dev/null +++ b/firmware/targets/f7/ble-glue/app_debug.c @@ -0,0 +1,375 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * File Name : app_debug.c + * Description : Debug capabilities source file for STM32WPAN Middleware + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2020 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Includes ------------------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +#include "utilities_common.h" + +#include "app_common.h" +#include "app_debug.h" +#include "shci.h" +#include "tl.h" +#include "dbg_trace.h" +#include +/* USER CODE END Includes */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN PTD */ +typedef PACKED_STRUCT +{ + GPIO_TypeDef* port; + uint16_t pin; + uint8_t enable; + uint8_t reserved; +} APPD_GpioConfig_t; +/* USER CODE END PTD */ + +/* Private defines -----------------------------------------------------------*/ +/* USER CODE BEGIN PD */ +#define GPIO_NBR_OF_RF_SIGNALS 9 +#define GPIO_CFG_NBR_OF_FEATURES 34 +#define NBR_OF_TRACES_CONFIG_PARAMETERS 4 +#define NBR_OF_GENERAL_CONFIG_PARAMETERS 4 + +/** + * THIS SHALL BE SET TO A VALUE DIFFERENT FROM 0 ONLY ON REQUEST FROM ST SUPPORT + */ +#define BLE_DTB_CFG 7 +#define SYS_DBG_CFG1 (SHCI_C2_DEBUG_OPTIONS_IPCORE_LP | SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN) +/* USER CODE END PD */ + +/* Private variables ---------------------------------------------------------*/ +/* USER CODE BEGIN PV */ +PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig={0, 0, 0, 0}; +PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig={BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}}; + +/** + * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT + * It provides timing information on the CPU2 activity. + * All configuration of (port, pin) is supported for each features and can be selected by the user + * depending on the availability + */ +static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] = +{ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_ISR - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_7, 1, 0}, /* BLE_STACK_TICK - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_CMD_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_ACL_DATA_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* SYS_CMD_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* RNG_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVM_PROCESS - Set on Entry / Reset on Exit */ + { GPIOB, LL_GPIO_PIN_3, 1, 0}, /* IPCC_GENERAL - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_EVT_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_ACL_DATA_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_SYS_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_SYS_EVT_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_CLI_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_OT_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_OT_ACK_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_CLI_ACK_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_MEM_MANAGER_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_TRACES_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_6, 1, 0}, /* HARD_FAULT - Set on Entry / Reset on Exit */ +/* From v1.1.1 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */ +/* From v1.2.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */ + { GPIOA, LL_GPIO_PIN_4, 1, 0}, /* PES_ACTIVITY - Set on Entry / Reset on Exit */ + { GPIOB, LL_GPIO_PIN_2, 1, 0}, /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */ +/* From v1.3.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_NO_DELAY - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_STACK_STORE_NVM_CB - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_WRITE_ONGOING - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_WRITE_COMPLETE - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_CLEANUP - Set on Entry / Reset on Exit */ +/* From v1.4.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_START - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_EOP - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_WRITE - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_ERASE - Set on Entry / Reset on Exit */ +}; + +/** + * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT + * This table is relevant only for BLE + * It provides timing information on BLE RF activity. + * New signals may be allocated at any location when requested by ST + * The GPIO allocated to each signal depend on the BLE_DTB_CFG value and cannot be changed + */ +#if( BLE_DTB_CFG == 7) +static const APPD_GpioConfig_t aRfConfigList[GPIO_NBR_OF_RF_SIGNALS] = +{ + { GPIOB, LL_GPIO_PIN_2, 0, 0}, /* DTB10 - Tx/Rx SPI */ + { GPIOB, LL_GPIO_PIN_7, 0, 0}, /* DTB11 - Tx/Tx SPI Clk */ + { GPIOA, LL_GPIO_PIN_8, 0, 0}, /* DTB12 - Tx/Rx Ready & SPI Select */ + { GPIOA, LL_GPIO_PIN_9, 0, 0}, /* DTB13 - Tx/Rx Start */ + { GPIOA, LL_GPIO_PIN_10, 0, 0}, /* DTB14 - FSM0 */ + { GPIOA, LL_GPIO_PIN_11, 0, 0}, /* DTB15 - FSM1 */ + { GPIOB, LL_GPIO_PIN_8, 0, 0}, /* DTB16 - FSM2 */ + { GPIOB, LL_GPIO_PIN_11, 0, 0}, /* DTB17 - FSM3 */ + { GPIOB, LL_GPIO_PIN_10, 0, 0}, /* DTB18 - FSM4 */ +}; +#endif +/* USER CODE END PV */ + +/* Global variables ----------------------------------------------------------*/ +/* USER CODE BEGIN GV */ +/* USER CODE END GV */ + +/* Private function prototypes -----------------------------------------------*/ +/* USER CODE BEGIN PFP */ +static void APPD_SetCPU2GpioConfig( void ); +static void APPD_BleDtbCfg( void ); +/* USER CODE END PFP */ + +/* Functions Definition ------------------------------------------------------*/ +void APPD_Init( void ) +{ +/* USER CODE BEGIN APPD_Init */ +#if (CFG_DEBUGGER_SUPPORTED == 1) + /** + * Keep debugger enabled while in any low power mode + */ + HAL_DBGMCU_EnableDBGSleepMode(); + HAL_DBGMCU_EnableDBGStopMode(); + + /***************** ENABLE DEBUGGER *************************************/ + LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); + +#else + GPIO_InitTypeDef gpio_config = {0}; + + gpio_config.Pull = GPIO_NOPULL; + gpio_config.Mode = GPIO_MODE_ANALOG; + + gpio_config.Pin = GPIO_PIN_15 | GPIO_PIN_14 | GPIO_PIN_13; + __HAL_RCC_GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOA, &gpio_config); + __HAL_RCC_GPIOA_CLK_DISABLE(); + + gpio_config.Pin = GPIO_PIN_4 | GPIO_PIN_3; + __HAL_RCC_GPIOB_CLK_ENABLE(); + HAL_GPIO_Init(GPIOB, &gpio_config); + __HAL_RCC_GPIOB_CLK_DISABLE(); + + HAL_DBGMCU_DisableDBGSleepMode(); + HAL_DBGMCU_DisableDBGStopMode(); + HAL_DBGMCU_DisableDBGStandbyMode(); + +#endif /* (CFG_DEBUGGER_SUPPORTED == 1) */ + +#if(CFG_DEBUG_TRACE != 0) + DbgTraceInit(); +#endif + + APPD_SetCPU2GpioConfig( ); + APPD_BleDtbCfg( ); + +/* USER CODE END APPD_Init */ + return; +} + +void APPD_EnableCPU2( void ) +{ +/* USER CODE BEGIN APPD_EnableCPU2 */ + SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket = + { + {{0,0,0}}, /**< Does not need to be initialized */ + {(uint8_t *)aGpioConfigList, + (uint8_t *)&APPD_TracesConfig, + (uint8_t *)&APPD_GeneralConfig, + GPIO_CFG_NBR_OF_FEATURES, + NBR_OF_TRACES_CONFIG_PARAMETERS, + NBR_OF_GENERAL_CONFIG_PARAMETERS} + }; + + /**< Traces channel initialization */ + TL_TRACES_Init( ); + + /** GPIO DEBUG Initialization */ + SHCI_C2_DEBUG_Init( &DebugCmdPacket ); + + // GPIO_InitTypeDef GPIO_InitStruct; + // GPIO_InitStruct.Pull = GPIO_NOPULL; + // GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + // GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + // GPIO_InitStruct.Pin = LL_GPIO_PIN_3; + // HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + // SHCI_C2_ExtpaConfig((uint32_t)GPIOC, LL_GPIO_PIN_3, EXT_PA_ENABLED_LOW, EXT_PA_ENABLED); + +/* USER CODE END APPD_EnableCPU2 */ + return; +} + +/************************************************************* + * + * LOCAL FUNCTIONS + * + *************************************************************/ +static void APPD_SetCPU2GpioConfig( void ) +{ +/* USER CODE BEGIN APPD_SetCPU2GpioConfig */ + GPIO_InitTypeDef gpio_config = {0}; + uint8_t local_loop; + uint16_t gpioa_pin_list; + uint16_t gpiob_pin_list; + uint16_t gpioc_pin_list; + + gpioa_pin_list = 0; + gpiob_pin_list = 0; + gpioc_pin_list = 0; + + for(local_loop = 0 ; local_loop < GPIO_CFG_NBR_OF_FEATURES; local_loop++) + { + if( aGpioConfigList[local_loop].enable != 0) + { + switch((uint32_t)aGpioConfigList[local_loop].port) + { + case (uint32_t)GPIOA: + gpioa_pin_list |= aGpioConfigList[local_loop].pin; + break; + + case (uint32_t)GPIOB: + gpiob_pin_list |= aGpioConfigList[local_loop].pin; + break; + + case (uint32_t)GPIOC: + gpioc_pin_list |= aGpioConfigList[local_loop].pin; + break; + + default: + break; + } + } + } + + gpio_config.Pull = GPIO_NOPULL; + gpio_config.Mode = GPIO_MODE_OUTPUT_PP; + gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + + if(gpioa_pin_list != 0) + { + gpio_config.Pin = gpioa_pin_list; + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_C2GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOA, &gpio_config); + HAL_GPIO_WritePin(GPIOA, gpioa_pin_list, GPIO_PIN_RESET); + } + + if(gpiob_pin_list != 0) + { + gpio_config.Pin = gpiob_pin_list; + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_C2GPIOB_CLK_ENABLE(); + HAL_GPIO_Init(GPIOB, &gpio_config); + HAL_GPIO_WritePin(GPIOB, gpiob_pin_list, GPIO_PIN_RESET); + } + + if(gpioc_pin_list != 0) + { + gpio_config.Pin = gpioc_pin_list; + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_C2GPIOC_CLK_ENABLE(); + HAL_GPIO_Init(GPIOC, &gpio_config); + HAL_GPIO_WritePin(GPIOC, gpioc_pin_list, GPIO_PIN_RESET); + } + +/* USER CODE END APPD_SetCPU2GpioConfig */ + return; +} + +static void APPD_BleDtbCfg( void ) +{ +/* USER CODE BEGIN APPD_BleDtbCfg */ +#if (BLE_DTB_CFG != 0) + GPIO_InitTypeDef gpio_config = {0}; + uint8_t local_loop; + uint16_t gpioa_pin_list; + uint16_t gpiob_pin_list; + + gpioa_pin_list = 0; + gpiob_pin_list = 0; + + for(local_loop = 0 ; local_loop < GPIO_NBR_OF_RF_SIGNALS; local_loop++) + { + if( aRfConfigList[local_loop].enable != 0) + { + switch((uint32_t)aRfConfigList[local_loop].port) + { + case (uint32_t)GPIOA: + gpioa_pin_list |= aRfConfigList[local_loop].pin; + break; + + case (uint32_t)GPIOB: + gpiob_pin_list |= aRfConfigList[local_loop].pin; + break; + + default: + break; + } + } + } + + gpio_config.Pull = GPIO_NOPULL; + gpio_config.Mode = GPIO_MODE_AF_PP; + gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + gpio_config.Alternate = GPIO_AF6_RF_DTB7; + + if(gpioa_pin_list != 0) + { + gpio_config.Pin = gpioa_pin_list; + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_C2GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOA, &gpio_config); + } + + if(gpiob_pin_list != 0) + { + gpio_config.Pin = gpiob_pin_list; + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_C2GPIOB_CLK_ENABLE(); + HAL_GPIO_Init(GPIOB, &gpio_config); + } +#endif + +/* USER CODE END APPD_BleDtbCfg */ + return; +} + +/************************************************************* + * + * WRAP FUNCTIONS + * +*************************************************************/ +#if(CFG_DEBUG_TRACE != 0) +void DbgOutputInit( void ) +{ +} + +void DbgOutputTraces( uint8_t *p_data, uint16_t size, void (*cb)(void) ) +{ + furi_hal_console_tx(p_data, size); + cb(); +} +#endif + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ \ No newline at end of file diff --git a/firmware/targets/f7/ble-glue/app_debug.h b/firmware/targets/f7/ble-glue/app_debug.h new file mode 100644 index 00000000..2cb31e53 --- /dev/null +++ b/firmware/targets/f7/ble-glue/app_debug.h @@ -0,0 +1,38 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * File Name : app_debug.h + * Description : Header for app_debug.c module + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2020 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __APP_DEBUG_H +#define __APP_DEBUG_H + +#ifdef __cplusplus +extern "C" { +#endif + + void APPD_Init( void ); + void APPD_EnableCPU2( void ); + +#ifdef __cplusplus +} +#endif + +#endif /*__APP_DEBUG_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ \ No newline at end of file diff --git a/firmware/targets/f7/ble-glue/app_entry.c b/firmware/targets/f7/ble-glue/app_entry.c index a970da32..5c8c0b2a 100644 --- a/firmware/targets/f7/ble-glue/app_entry.c +++ b/firmware/targets/f7/ble-glue/app_entry.c @@ -6,6 +6,7 @@ #include "tl.h" #include "cmsis_os.h" #include "shci_tl.h" +#include "app_debug.h" #include extern RTC_HandleTypeDef hrtc; @@ -177,16 +178,3 @@ void shci_cmd_resp_wait(uint32_t timeout) { UNUSED(timeout); osSemaphoreAcquire( SemShciId, osWaitForever ); } - -#if(CFG_DEBUG_TRACE != 0) -void DbgOutputInit( void ) -{ -} - -void DbgOutputTraces( uint8_t *p_data, uint16_t size, void (*cb)(void) ) -{ - furi_hal_console_tx(p_data, size); - cb(); -} -#endif - diff --git a/firmware/targets/f7/ble-glue/ble_dbg_conf.h b/firmware/targets/f7/ble-glue/ble_dbg_conf.h index 4eb0239b..7bb35216 100644 --- a/firmware/targets/f7/ble-glue/ble_dbg_conf.h +++ b/firmware/targets/f7/ble-glue/ble_dbg_conf.h @@ -25,13 +25,13 @@ * Enable or Disable traces from BLE */ -#define BLE_DBG_APP_EN 0 -#define BLE_DBG_DIS_EN 0 -#define BLE_DBG_HRS_EN 0 -#define BLE_DBG_SVCCTL_EN 0 -#define BLE_DBG_BLS_EN 0 -#define BLE_DBG_HTS_EN 0 -#define BLE_DBG_P2P_STM_EN 0 +#define BLE_DBG_APP_EN 1 +#define BLE_DBG_DIS_EN 1 +#define BLE_DBG_HRS_EN 1 +#define BLE_DBG_SVCCTL_EN 1 +#define BLE_DBG_BLS_EN 1 +#define BLE_DBG_HTS_EN 1 +#define BLE_DBG_P2P_STM_EN 1 /** * Macro definition diff --git a/firmware/targets/f7/ble-glue/tl_dbg_conf.h b/firmware/targets/f7/ble-glue/tl_dbg_conf.h index 4c38cfb5..842cba0e 100644 --- a/firmware/targets/f7/ble-glue/tl_dbg_conf.h +++ b/firmware/targets/f7/ble-glue/tl_dbg_conf.h @@ -33,26 +33,23 @@ extern "C" { #include "app_conf.h" /* required as some configuration used in dbg_trace.h are set there */ #include "dbg_trace.h" #include "hw_if.h" +#include /** * Enable or Disable traces * The raw data output is the hci binary packet format as specified by the BT specification * */ -#define TL_SHCI_CMD_DBG_EN 0 /* Reports System commands sent to CPU2 and the command response */ +#define TL_SHCI_CMD_DBG_EN 1 /* Reports System commands sent to CPU2 and the command response */ #define TL_SHCI_CMD_DBG_RAW_EN 0 /* Reports raw data System commands sent to CPU2 and the command response */ -#define TL_SHCI_EVT_DBG_EN 0 /* Reports System Asynchronous Events received from CPU2 */ +#define TL_SHCI_EVT_DBG_EN 1 /* Reports System Asynchronous Events received from CPU2 */ #define TL_SHCI_EVT_DBG_RAW_EN 0 /* Reports raw data System Asynchronous Events received from CPU2 */ -#define TL_HCI_CMD_DBG_EN 0 /* Reports BLE command sent to CPU2 and the command response */ +#define TL_HCI_CMD_DBG_EN 1 /* Reports BLE command sent to CPU2 and the command response */ #define TL_HCI_CMD_DBG_RAW_EN 0 /* Reports raw data BLE command sent to CPU2 and the command response */ -#define TL_HCI_EVT_DBG_EN 0 /* Reports BLE Asynchronous Events received from CPU2 */ +#define TL_HCI_EVT_DBG_EN 1 /* Reports BLE Asynchronous Events received from CPU2 */ #define TL_HCI_EVT_DBG_RAW_EN 0 /* Reports raw data BLE Asynchronous Events received from CPU2 */ -#define TL_MM_DBG_EN 0 /* Reports the informations of the buffer released to CPU2 */ - -/** - * Macro definition - */ +#define TL_MM_DBG_EN 1 /* Reports the informations of the buffer released to CPU2 */ /** * System Transport Layer @@ -66,7 +63,7 @@ extern "C" { #endif #if (TL_SHCI_CMD_DBG_RAW_EN != 0) -#define TL_SHCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx(_PDATA_, _SIZE_) +#define TL_SHCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) #else #define TL_SHCI_CMD_DBG_RAW(...) #endif @@ -80,7 +77,7 @@ extern "C" { #endif #if (TL_SHCI_EVT_DBG_RAW_EN != 0) -#define TL_SHCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx(_PDATA_, _SIZE_) +#define TL_SHCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) #else #define TL_SHCI_EVT_DBG_RAW(...) #endif @@ -97,7 +94,7 @@ extern "C" { #endif #if (TL_HCI_CMD_DBG_RAW_EN != 0) -#define TL_HCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx(_PDATA_, _SIZE_) +#define TL_HCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) #else #define TL_HCI_CMD_DBG_RAW(...) #endif @@ -111,7 +108,7 @@ extern "C" { #endif #if (TL_HCI_EVT_DBG_RAW_EN != 0) -#define TL_HCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx(_PDATA_, _SIZE_) +#define TL_HCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) #else #define TL_HCI_EVT_DBG_RAW(...) #endif diff --git a/firmware/targets/f7/furi-hal/furi-hal-bt.c b/firmware/targets/f7/furi-hal/furi-hal-bt.c index 7fab63ed..01f4b919 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-bt.c +++ b/firmware/targets/f7/furi-hal/furi-hal-bt.c @@ -26,6 +26,9 @@ void furi_hal_bt_start_advertising() { void furi_hal_bt_stop_advertising() { if(furi_hal_bt_is_active()) { gap_stop_advertising(); + while(furi_hal_bt_is_active()) { + osDelay(1); + } } } @@ -61,7 +64,7 @@ bool furi_hal_bt_is_active() { } bool furi_hal_bt_wait_startup() { - uint8_t counter = 0; + uint16_t counter = 0; while (!(APPE_Status() == BleGlueStatusStarted || APPE_Status() == BleGlueStatusBroken)) { osDelay(10); counter++; @@ -85,7 +88,9 @@ bool furi_hal_bt_lock_flash(bool erase_flag) { if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON); - while(LL_FLASH_IsActiveFlag_OperationSuspended()) {}; + while(LL_FLASH_IsActiveFlag_OperationSuspended()) { + osDelay(1); + }; __disable_irq(); diff --git a/firmware/targets/f7/furi-hal/furi-hal-console.c b/firmware/targets/f7/furi-hal/furi-hal-console.c index 6c67e0c8..b04a17a1 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-console.c +++ b/firmware/targets/f7/furi-hal/furi-hal-console.c @@ -41,10 +41,7 @@ void furi_hal_console_init() { FURI_LOG_I("FuriHalConsole", "Init OK"); } -void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { - if (!furi_hal_console_alive) - return; - +static void furi_hal_console_uart_tx(const uint8_t* buffer, size_t buffer_size) { while(buffer_size > 0) { while (!LL_USART_IsActiveFlag_TXE(USART1)); @@ -53,8 +50,27 @@ void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { buffer++; buffer_size--; } +} - /* Wait for TC flag to be raised for last char */ +void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { + if (!furi_hal_console_alive) + return; + + // Transmit data + furi_hal_console_uart_tx(buffer, buffer_size); + // Wait for TC flag to be raised for last char + while (!LL_USART_IsActiveFlag_TC(USART1)); +} + +void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size) { + if (!furi_hal_console_alive) + return; + + // Transmit data + furi_hal_console_uart_tx(buffer, buffer_size); + // Transmit new line symbols + furi_hal_console_uart_tx((const uint8_t*)"\r\n", 2); + // Wait for TC flag to be raised for last char while (!LL_USART_IsActiveFlag_TC(USART1)); } diff --git a/firmware/targets/f7/furi-hal/furi-hal-console.h b/firmware/targets/f7/furi-hal/furi-hal-console.h index 26ca4540..cd7ccae8 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-console.h +++ b/firmware/targets/f7/furi-hal/furi-hal-console.h @@ -11,6 +11,8 @@ void furi_hal_console_init(); void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size); +void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size); + /** * Printf-like plain uart interface * @warning Will not work in ISR context diff --git a/firmware/targets/f7/furi-hal/furi-hal-ibutton.c b/firmware/targets/f7/furi-hal/furi-hal-ibutton.c index 6e3c8bee..8a0af9f9 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-ibutton.c +++ b/firmware/targets/f7/furi-hal/furi-hal-ibutton.c @@ -3,12 +3,12 @@ void furi_hal_ibutton_start() { furi_hal_ibutton_pin_high(); - hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); } void furi_hal_ibutton_stop() { furi_hal_ibutton_pin_high(); - hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } void furi_hal_ibutton_pin_low() { diff --git a/firmware/targets/f7/furi-hal/furi-hal-os-timer.h b/firmware/targets/f7/furi-hal/furi-hal-os-timer.h index f210b539..8b7752e8 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-os-timer.h +++ b/firmware/targets/f7/furi-hal/furi-hal-os-timer.h @@ -20,6 +20,7 @@ static inline void furi_hal_os_timer_init() { } static inline void furi_hal_os_timer_continuous(uint32_t count) { + count--; // Enable timer LL_LPTIM_Enable(FURI_HAL_OS_TIMER); while(!LL_LPTIM_IsEnabled(FURI_HAL_OS_TIMER)); @@ -33,6 +34,7 @@ static inline void furi_hal_os_timer_continuous(uint32_t count) { } static inline void furi_hal_os_timer_single(uint32_t count) { + count--; // Enable timer LL_LPTIM_Enable(FURI_HAL_OS_TIMER); while(!LL_LPTIM_IsEnabled(FURI_HAL_OS_TIMER)); diff --git a/firmware/targets/f7/furi-hal/furi-hal-rfid.c b/firmware/targets/f7/furi-hal/furi-hal-rfid.c index 2a968b7a..d0772231 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-rfid.c +++ b/firmware/targets/f7/furi-hal/furi-hal-rfid.c @@ -18,11 +18,11 @@ void furi_hal_rfid_pins_reset() { furi_hal_ibutton_stop(); // pulldown rfid antenna - hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_rfid_carrier_out, false); // from both sides - hal_gpio_init(&gpio_rfid_pull, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&gpio_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_rfid_pull, true); hal_gpio_init_simple(&gpio_rfid_carrier, GpioModeAnalog); @@ -38,7 +38,7 @@ void furi_hal_rfid_pins_emulate() { &gpio_rfid_pull, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); // pull rfid antenna from carrier side - hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_rfid_carrier_out, false); hal_gpio_init_ex( @@ -51,7 +51,7 @@ void furi_hal_rfid_pins_read() { furi_hal_ibutton_pin_low(); // dont pull rfid antenna - hal_gpio_init(&gpio_rfid_pull, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&gpio_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_rfid_pull, false); // carrier pin to timer out @@ -63,7 +63,7 @@ void furi_hal_rfid_pins_read() { GpioAltFn1TIM1); // comparator in - hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioSpeedLow, GpioPullNo); + hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } void furi_hal_rfid_tim_read(float freq, float duty_cycle) { diff --git a/firmware/targets/f7/furi-hal/furi-hal-subghz.c b/firmware/targets/f7/furi-hal/furi-hal-subghz.c index 7e8c12cd..34446d8d 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-subghz.c +++ b/firmware/targets/f7/furi-hal/furi-hal-subghz.c @@ -1,4 +1,5 @@ #include "furi-hal-subghz.h" +#include "furi-hal-version.h" #include #include @@ -10,6 +11,7 @@ #include static volatile SubGhzState furi_hal_subghz_state = SubGhzStateInit; +static volatile SubGhzRegulation furi_hal_subghz_regulation = SubGhzRegulationTxRx; static const uint8_t furi_hal_subghz_preset_ook_270khz_async_regs[][2] = { // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration @@ -41,11 +43,11 @@ static const uint8_t furi_hal_subghz_preset_ook_270khz_async_regs[][2] = { 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCTRL0, + {CC1101_AGCCTRL0, 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCTRL1, + {CC1101_AGCCTRL1, 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours @@ -99,11 +101,15 @@ static const uint8_t furi_hal_subghz_preset_ook_650khz_async_regs[][2] = { 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCTRL0, - 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCTRL1, - 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + // {CC1101_AGCTRL0,0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary + // {CC1101_AGCTRL1,0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + // {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7. + {CC1101_AGCCTRL0, + 0x91}, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + {CC1101_AGCCTRL1, + 0x0}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours @@ -131,23 +137,21 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_regs[][2] = { /* GPIO GD0 */ {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - /* FIFO and internals */ - {CC1101_FIFOTHR, 0x47}, // The only important bit is ADC_RETENTION - - /* Packet engine */ - {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening - /* Frequency Synthesizer Control */ {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - // Modem Configuration - {CC1101_MDMCFG0, 0x00}, - {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) - {CC1101_MDMCFG3, 0x8B}, // Data rate is 19.5885 kBaud - {CC1101_MDMCFG4, 0x69}, // Rx BW filter is 270.833333 kHz + /* Packet engine */ + {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening + {CC1101_PKTCTRL1, 0x04}, - {CC1101_DEVIATN, 0x47}, //Deviation 47.607422 khz + // // Modem Configuration + {CC1101_MDMCFG0, 0x00}, + {CC1101_MDMCFG1, 0x2}, + {CC1101_MDMCFG2, 0x4}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) + {CC1101_MDMCFG3, 0x83}, // Data rate is 4.79794 kBaud + {CC1101_MDMCFG4, 0x67}, //Rx BW filter is 270.833333 kHz + //{ CC1101_DEVIATN, 0x14 }, //Deviation 4.760742 kHz + {CC1101_DEVIATN, 0x04}, //Deviation 2.380371 kHz /* Main Radio Control State Machine */ {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) @@ -157,18 +161,18 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_regs[][2] = { 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCTRL0, - 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCTRL1, + {CC1101_AGCCTRL0, + 0x91}, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + {CC1101_AGCCTRL1, 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours /* Frontend configuration */ {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer - {CC1101_FREND1, 0xB6}, // + {CC1101_FREND1, 0x56}, /* Frequency Synthesizer Calibration, valid for 433.92 */ {CC1101_FSCAL3, 0xE9}, @@ -201,7 +205,9 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_patable[8] = { 0x00, 0x00, 0x00, - 0x00}; + 0x00 + +}; void furi_hal_subghz_init() { furi_assert(furi_hal_subghz_state == SubGhzStateInit); @@ -279,7 +285,7 @@ void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { } else if(preset == FuriHalSubGhzPreset2FSKAsync) { furi_hal_subghz_load_registers(furi_hal_subghz_preset_2fsk_async_regs); furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); - }else { + } else { furi_crash(NULL); } } @@ -356,10 +362,12 @@ void furi_hal_subghz_rx() { furi_hal_spi_device_return(device); } -void furi_hal_subghz_tx() { +bool furi_hal_subghz_tx() { + if(furi_hal_subghz_regulation != SubGhzRegulationTxRx) return false; const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz); cc1101_switch_to_tx(device); furi_hal_spi_device_return(device); + return true; } float furi_hal_subghz_get_rssi() { @@ -383,6 +391,7 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value) { !(value >= 778999847 && value <= 928000000)) { return false; } + return true; } @@ -403,6 +412,46 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { uint32_t furi_hal_subghz_set_frequency(uint32_t value) { const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz); + //checking regional settings + bool txrx = false; + switch(furi_hal_version_get_hw_region()) { + case FuriHalVersionRegionEuRu: + //433,05..434,79; 868,15..868,55 + if(!(value >= 433050000 && value <= 434790000) && + !(value >= 868150000 && value <= 8680550000)) { + } else { + txrx = true; + } + break; + case FuriHalVersionRegionUsCaAu: + //304,10..315,25; 433,05..434,79; 915,00..928,00 + if(!(value >= 304100000 && value <= 315250000) && + !(value >= 433050000 && value <= 434790000) && + !(value >= 915000000 && value <= 928000000)) { + } else { + txrx = true; + } + break; + case FuriHalVersionRegionJp: + //312,00..315,25; 920,50..923,50 + if(!(value >= 312000000 && value <= 315250000) && + !(value >= 920500000 && value <= 923500000)) { + } else { + txrx = true; + } + break; + + default: + txrx = true; + break; + } + + if(txrx) { + furi_hal_subghz_regulation = SubGhzRegulationTxRx; + } else { + furi_hal_subghz_regulation = SubGhzRegulationOnlyRx; + } + uint32_t real_frequency = cc1101_set_frequency(device, value); cc1101_calibrate(device); @@ -480,7 +529,7 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* TIM_InitStruct.Prescaler = 64 - 1; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 0x7FFFFFFE; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; LL_TIM_Init(TIM2, &TIM_InitStruct); // Timer: advanced @@ -503,7 +552,7 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI); LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); + LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV32_N8); // ISR setup furi_hal_interrupt_set_timer_isr(TIM2, furi_hal_subghz_capture_ISR); @@ -608,6 +657,8 @@ 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; + //forcibly pulls the pin to the ground so that there is no carrier + hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow); } else { furi_hal_subghz_state = SubGhzStateAsyncTxEnd; LL_TIM_DisableCounter(TIM2); @@ -616,10 +667,13 @@ static void furi_hal_subghz_async_tx_timer_isr() { } } -void furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { +bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { furi_assert(furi_hal_subghz_state == SubGhzStateIdle); furi_assert(callback); + //If transmission is prohibited by regional settings + if(furi_hal_subghz_regulation != SubGhzRegulationTxRx) return false; + furi_hal_subghz_async_tx.callback = callback; furi_hal_subghz_async_tx.callback_context = context; @@ -694,6 +748,7 @@ void furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); + return true; } bool furi_hal_subghz_is_async_tx_complete() { diff --git a/firmware/targets/furi-hal-include/furi-hal-subghz.h b/firmware/targets/furi-hal-include/furi-hal-subghz.h index 653a343c..38aa65da 100644 --- a/firmware/targets/furi-hal-include/furi-hal-subghz.h +++ b/firmware/targets/furi-hal-include/furi-hal-subghz.h @@ -26,17 +26,24 @@ typedef enum { /** SubGhz state */ typedef enum { - SubGhzStateInit, /** Init pending */ + SubGhzStateInit, /** Init pending */ - SubGhzStateIdle, /** Idle, energy save mode */ + SubGhzStateIdle, /** Idle, energy save mode */ - SubGhzStateAsyncRx, /** Async RX started */ + SubGhzStateAsyncRx, /** Async RX started */ + + SubGhzStateAsyncTx, /** Async TX started, DMA and timer is on */ + SubGhzStateAsyncTxLast, /** Async TX continue, DMA completed and timer got last value to go */ + SubGhzStateAsyncTxEnd, /** Async TX complete, cleanup needed */ - SubGhzStateAsyncTx, /** Async TX started, DMA and timer is on */ - SubGhzStateAsyncTxLast, /** Async TX continue, DMA completed and timer got last value to go */ - SubGhzStateAsyncTxEnd, /** Async TX complete, cleanup needed */ } SubGhzState; +/** SubGhz regulation, receive transmission on the current frequency for the region */ +typedef enum { + SubGhzRegulationOnlyRx, /**only Rx*/ + SubGhzRegulationTxRx, /**TxRx*/ +} SubGhzRegulation; + /** Initialize and switch to power save mode * Used by internal API-HAL initalization routine * Can be used to reinitialize device to safe state and send it to sleep @@ -100,8 +107,10 @@ void furi_hal_subghz_idle(); /** Switch to Recieve */ void furi_hal_subghz_rx(); -/** Switch to Transmit */ -void furi_hal_subghz_tx(); +/** Switch to Transmit +* @return true if the transfer is allowed by belonging to the region +*/ +bool furi_hal_subghz_tx(); /** Get RSSI value in dBm */ float furi_hal_subghz_get_rssi(); @@ -152,8 +161,9 @@ typedef LevelDuration (*FuriHalSubGhzAsyncTxCallback)(void* context); /** Start async TX * Initializes GPIO, TIM2 and DMA1 for signal output + * @return true if the transfer is allowed by belonging to the region */ -void furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context); +bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context); /** Wait for async transmission to complete */ bool furi_hal_subghz_is_async_tx_complete(); diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB index 9e2dd9db..9c78e7f2 160000 --- a/lib/STM32CubeWB +++ b/lib/STM32CubeWB @@ -1 +1 @@ -Subproject commit 9e2dd9db125fa53e0ec59f807eb8a052ab922583 +Subproject commit 9c78e7f25506db18c35c6be51ab5aa14c7e0a622 diff --git a/lib/app-scened-template/view-modules/submenu-vm.cpp b/lib/app-scened-template/view-modules/submenu-vm.cpp index 12c283f9..3bd5533c 100644 --- a/lib/app-scened-template/view-modules/submenu-vm.cpp +++ b/lib/app-scened-template/view-modules/submenu-vm.cpp @@ -16,12 +16,12 @@ void SubmenuVM::clean() { submenu_clean(submenu); } -SubmenuItem* SubmenuVM::add_item( +void SubmenuVM::add_item( const char* label, uint32_t index, SubmenuItemCallback callback, void* callback_context) { - return submenu_add_item(submenu, label, index, callback, callback_context); + submenu_add_item(submenu, label, index, callback, callback_context); } void SubmenuVM::set_selected_item(uint32_t index) { diff --git a/lib/app-scened-template/view-modules/submenu-vm.h b/lib/app-scened-template/view-modules/submenu-vm.h index d508d75e..b1bcd3ab 100644 --- a/lib/app-scened-template/view-modules/submenu-vm.h +++ b/lib/app-scened-template/view-modules/submenu-vm.h @@ -16,9 +16,8 @@ public: * @param index - menu item index, used for callback, may be the same with other items * @param callback - menu item callback * @param callback_context - menu item callback context - * @return SubmenuItem instance that can be used to modify or delete that item */ - SubmenuItem* add_item( + void add_item( const char* label, uint32_t index, SubmenuItemCallback callback, diff --git a/lib/app-scened-template/view-modules/text-input-vm.cpp b/lib/app-scened-template/view-modules/text-input-vm.cpp index 7a6f7d39..ed497344 100644 --- a/lib/app-scened-template/view-modules/text-input-vm.cpp +++ b/lib/app-scened-template/view-modules/text-input-vm.cpp @@ -13,8 +13,7 @@ View* TextInputVM::get_view() { } void TextInputVM::clean() { - set_result_callback(NULL, NULL, NULL, 0, false); - set_header_text(""); + text_input_clean(text_input); } void TextInputVM::set_result_callback( diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index 4e020b70..0d5a23df 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -174,7 +174,7 @@ uint8_t cc1101_read_fifo(const FuriHalSpiDevice* device, uint8_t* data, uint8_t* // First byte - packet length furi_hal_spi_bus_trx(device->bus, buff_tx, buff_rx, 2, CC1101_TIMEOUT); - *size = buff_rx[2]; + *size = buff_rx[1]; furi_hal_spi_bus_trx(device->bus, &buff_tx[1], data, *size, CC1101_TIMEOUT); cc1101_flush_rx(device); diff --git a/lib/drivers/cc1101_regs.h b/lib/drivers/cc1101_regs.h index 8305584d..6e71cc37 100644 --- a/lib/drivers/cc1101_regs.h +++ b/lib/drivers/cc1101_regs.h @@ -48,9 +48,9 @@ extern "C" { #define CC1101_MCSM0 0x18 /** Main Radio Control State Machine configuration */ #define CC1101_FOCCFG 0x19 /** Frequency Offset Compensation configuration */ #define CC1101_BSCFG 0x1A /** Bit Synchronization configuration */ -#define CC1101_AGCTRL2 0x1B /** AGC control */ -#define CC1101_AGCTRL1 0x1C /** AGC control */ -#define CC1101_AGCTRL0 0x1D /** AGC control */ +#define CC1101_AGCCTRL2 0x1B /** AGC control */ +#define CC1101_AGCCTRL1 0x1C /** AGC control */ +#define CC1101_AGCCTRL0 0x1D /** AGC control */ #define CC1101_WOREVT1 0x1E /** High byte Event 0 timeout */ #define CC1101_WOREVT0 0x1F /** Low byte Event 0 timeout */ #define CC1101_WORCTRL 0x20 /** Wake On Radio control */ diff --git a/lib/irda/encoder_decoder/sirc/irda_encoder_sirc.c b/lib/irda/encoder_decoder/sirc/irda_encoder_sirc.c index ca98e830..d1d3b0de 100644 --- a/lib/irda/encoder_decoder/sirc/irda_encoder_sirc.c +++ b/lib/irda/encoder_decoder/sirc/irda_encoder_sirc.c @@ -36,8 +36,7 @@ void irda_encoder_sirc_reset(void* encoder_ptr, const IrdaMessage* message) { IrdaStatus irda_encoder_sirc_encode_repeat(IrdaCommonEncoder* encoder, uint32_t* duration, bool* level) { furi_assert(encoder); - uint32_t timings_in_message = 1 + 2 + encoder->bits_to_encode * 2 - 1; - furi_assert(encoder->timings_encoded == timings_in_message); + furi_assert(encoder->timings_encoded == (1 + 2 + encoder->bits_to_encode * 2 - 1)); furi_assert(encoder->timings_sum < IRDA_SIRC_REPEAT_PERIOD); *duration = IRDA_SIRC_REPEAT_PERIOD - encoder->timings_sum; diff --git a/lib/subghz/protocols/subghz_protocol_princeton.c b/lib/subghz/protocols/subghz_protocol_princeton.c index 700d221f..7819d0dd 100644 --- a/lib/subghz/protocols/subghz_protocol_princeton.c +++ b/lib/subghz/protocols/subghz_protocol_princeton.c @@ -8,12 +8,17 @@ #define SUBGHZ_PT_SHORT 400 #define SUBGHZ_PT_LONG (SUBGHZ_PT_SHORT * 3) #define SUBGHZ_PT_GUARD (SUBGHZ_PT_SHORT * 30) +#define SUBGHZ_PT_COUNT_KEY 5 +#define SUBGHZ_PT_TIMEOUT 320 struct SubGhzEncoderPrinceton { uint32_t key; uint16_t te; size_t repeat; size_t front; + size_t count_key; + uint32_t time_high; + uint32_t time_low; }; typedef enum { @@ -45,8 +50,11 @@ void subghz_encoder_princeton_set(SubGhzEncoderPrinceton* instance, uint32_t key furi_assert(instance); instance->te = SUBGHZ_PT_SHORT; instance->key = key; - instance->repeat = repeat; + instance->repeat = repeat + 1; instance->front = 48; + instance->count_key = SUBGHZ_PT_COUNT_KEY + 7; + instance->time_high = 0; + instance->time_low = 0; } size_t subghz_encoder_princeton_get_repeat_left(SubGhzEncoderPrinceton* instance) { @@ -54,9 +62,25 @@ size_t subghz_encoder_princeton_get_repeat_left(SubGhzEncoderPrinceton* instance return instance->repeat; } +void subghz_encoder_princeton_print_log(void* context) { + SubGhzEncoderPrinceton* instance = context; + float duty_cycle = + ((float)instance->time_high / (instance->time_high + instance->time_low)) * 100; + FURI_LOG_I( + "EncoderPrinceton", + "Radio ON=%dus, OFF=%dus, DutyCycle=%d,%d%%", + instance->time_high, + instance->time_low, + (uint32_t)duty_cycle, + (uint32_t)((duty_cycle - (uint32_t)duty_cycle) * 100)); +} + LevelDuration subghz_encoder_princeton_yield(void* context) { SubGhzEncoderPrinceton* instance = context; - if(instance->repeat == 0) return level_duration_reset(); + if(instance->repeat == 0) { + subghz_encoder_princeton_print_log(instance); + return level_duration_reset(); + } size_t bit = instance->front / 2; bool level = !(instance->front % 2); @@ -68,11 +92,33 @@ LevelDuration subghz_encoder_princeton_yield(void* context) { bool value = (((uint8_t*)&instance->key)[2 - byte] >> (7 - bit_in_byte)) & 1; if(value) { ret = level_duration_make(level, level ? instance->te * 3 : instance->te); + if(level) + instance->time_high += instance->te * 3; + else + instance->time_low += instance->te; } else { ret = level_duration_make(level, level ? instance->te : instance->te * 3); + if(level) + instance->time_high += instance->te; + else + instance->time_low += instance->te * 3; } } else { - ret = level_duration_make(level, level ? instance->te : instance->te * 30); + if(--instance->count_key != 0) { + ret = level_duration_make(level, level ? instance->te : instance->te * 30); + if(level) + instance->time_high += instance->te; + else + instance->time_low += instance->te * 30; + } else { + instance->count_key = SUBGHZ_PT_COUNT_KEY + 6; + instance->front = 48; + ret = level_duration_make(level, level ? instance->te : SUBGHZ_PT_TIMEOUT * 1000); + if(level) + instance->time_high += instance->te; + else + instance->time_low += SUBGHZ_PT_TIMEOUT * 1000; + } } instance->front++; diff --git a/lib/subghz/protocols/subghz_protocol_princeton.h b/lib/subghz/protocols/subghz_protocol_princeton.h index fdc37a43..09a9f482 100644 --- a/lib/subghz/protocols/subghz_protocol_princeton.h +++ b/lib/subghz/protocols/subghz_protocol_princeton.h @@ -33,6 +33,11 @@ void subghz_encoder_princeton_set(SubGhzEncoderPrinceton* instance, uint32_t key */ size_t subghz_encoder_princeton_get_repeat_left(SubGhzEncoderPrinceton* instance); +/** Print encoder log + * @param instance - SubGhzEncoderPrinceton instance + */ +void subghz_encoder_princeton_print_log(void* context); + /** Get level duration * @param instance - SubGhzEncoderPrinceton instance * @return level duration diff --git a/lib/subghz/protocols/subghz_protocol_scher_khan.c b/lib/subghz/protocols/subghz_protocol_scher_khan.c new file mode 100644 index 00000000..b5124f93 --- /dev/null +++ b/lib/subghz/protocols/subghz_protocol_scher_khan.c @@ -0,0 +1,246 @@ +#include "subghz_protocol_scher_khan.h" + +//https://phreakerclub.com/72 +//https://phreakerclub.com/forum/showthread.php?t=7&page=2 +//https://phreakerclub.com/forum/showthread.php?t=274&highlight=magicar +//!!! https://phreakerclub.com/forum/showthread.php?t=489&highlight=magicar&page=5 + +struct SubGhzProtocolScherKhan { + SubGhzProtocolCommon common; + const char* protocol_name; +}; + +typedef enum { + ScherKhanDecoderStepReset = 0, + ScherKhanDecoderStepCheckPreambula, + ScherKhanDecoderStepSaveDuration, + ScherKhanDecoderStepCheckDuration, +} ScherKhanDecoderStep; + +SubGhzProtocolScherKhan* subghz_protocol_scher_khan_alloc(void) { + SubGhzProtocolScherKhan* instance = furi_alloc(sizeof(SubGhzProtocolScherKhan)); + + instance->common.name = "Scher-Khan"; + instance->common.code_min_count_bit_for_found = 35; + instance->common.te_short = 750; + instance->common.te_long = 1100; + instance->common.te_delta = 150; + instance->common.type_protocol = SubGhzProtocolCommonTypeDynamic; + instance->common.to_string = (SubGhzProtocolCommonToStr)subghz_protocol_scher_khan_to_str; + instance->common.to_load_protocol = + (SubGhzProtocolCommonLoadFromRAW)subghz_decoder_scher_khan_to_load_protocol; + + return instance; +} + +void subghz_protocol_scher_khan_free(SubGhzProtocolScherKhan* instance) { + furi_assert(instance); + free(instance); +} + +/** Send bit + * + * @param instance - SubGhzProtocolScherKhan instance + * @param bit - bit + */ +// void subghz_protocol_scher_khan_send_bit(SubGhzProtocolScherKhan* instance, uint8_t bit) { +// if(bit) { +// //send bit 1 +// SUBGHZ_TX_PIN_HIGH(); +// delay_us(instance->common.te_long); +// SUBGHZ_TX_PIN_LOW(); +// delay_us(instance->common.te_short); +// } else { +// //send bit 0 +// SUBGHZ_TX_PIN_HIGH(); +// delay_us(instance->common.te_short); +// SUBGHZ_TX_PIN_LOW(); +// delay_us(instance->common.te_long); +// } +// } + +// void subghz_protocol_scher_khan_send_key( +// SubGhzProtocolScherKhan* instance, +// uint64_t key, +// uint8_t bit, +// uint8_t repeat) { +// while(repeat--) { +// SUBGHZ_TX_PIN_HIGH(); +// //Send header +// delay_us(instance->common.te_long * 2); +// SUBGHZ_TX_PIN_LOW(); +// delay_us(instance->common.te_long * 2); +// //Send key data +// for(uint8_t i = bit; i > 0; i--) { +// subghz_protocol_scher_khan_send_bit(instance, bit_read(key, i - 1)); +// } +// } +// } + +void subghz_protocol_scher_khan_reset(SubGhzProtocolScherKhan* instance) { + instance->common.parser_step = ScherKhanDecoderStepReset; +} + +/** Analysis of received data + * + * @param instance SubGhzProtocolScherKhan instance + */ +void subghz_protocol_scher_khan_check_remote_controller(SubGhzProtocolScherKhan* instance) { + /* + * MAGICAR 51 bit 00000001A99121DE83C3 MAGIC CODE, Dinamic + * 0E8C1619E830C -> 000011101000110000010110 0001 1001 1110 1000001100001100 + * 0E8C1629D830D -> 000011101000110000010110 0010 1001 1101 1000001100001101 + * 0E8C1649B830E -> 000011101000110000010110 0100 1001 1011 1000001100001110 + * 0E8C16897830F -> 000011101000110000010110 1000 1001 0111 1000001100001111 + * Serial Key Ser ~Key CNT + */ + + switch(instance->common.code_last_count_bit) { + // case 35: //MAGIC CODE, Static + // instance->protocol_name = "MAGIC CODE, Static"; + // break; + case 51: //MAGIC CODE, Dinamic + instance->protocol_name = "MAGIC CODE, Dinamic"; + instance->common.serial = ((instance->common.code_last_found >> 24) & 0xFFFFFF0) | + ((instance->common.code_last_found >> 20) & 0x0F); + instance->common.btn = (instance->common.code_last_found >> 24) & 0x0F; + instance->common.cnt = instance->common.code_last_found & 0xFFFF; + break; + // case 57: //MAGIC CODE PRO / PRO2 + // instance->protocol_name = "MAGIC CODE PRO / PRO2"; + // break; + + default: + instance->protocol_name = "Unknown"; + instance->common.serial = 0; + instance->common.btn = 0; + instance->common.cnt = 0; + break; + } +} + +void subghz_protocol_scher_khan_parse( + SubGhzProtocolScherKhan* instance, + bool level, + uint32_t duration) { + switch(instance->common.parser_step) { + case ScherKhanDecoderStepReset: + if((level) && + (DURATION_DIFF(duration, instance->common.te_short * 2) < instance->common.te_delta)) { + instance->common.parser_step = ScherKhanDecoderStepCheckPreambula; + instance->common.te_last = duration; + instance->common.header_count = 0; + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + break; + case ScherKhanDecoderStepCheckPreambula: + if(level) { + if((DURATION_DIFF(duration, instance->common.te_short * 2) < + instance->common.te_delta) || + (DURATION_DIFF(duration, instance->common.te_short) < instance->common.te_delta)) { + instance->common.te_last = duration; + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + } else if( + (DURATION_DIFF(duration, instance->common.te_short * 2) < instance->common.te_delta) || + (DURATION_DIFF(duration, instance->common.te_short) < instance->common.te_delta)) { + if(DURATION_DIFF(instance->common.te_last, instance->common.te_short * 2) < + instance->common.te_delta) { + // Found header + instance->common.header_count++; + break; + } else if( + DURATION_DIFF(instance->common.te_last, instance->common.te_short) < + instance->common.te_delta) { + // Found start bit + if(instance->common.header_count >= 2) { + instance->common.parser_step = ScherKhanDecoderStepSaveDuration; + instance->common.code_found = 0; + instance->common.code_count_bit = 1; + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + break; + case ScherKhanDecoderStepSaveDuration: + if(level) { + if(duration >= (instance->common.te_long + instance->common.te_delta * 2)) { + //Found stop bit + instance->common.parser_step = ScherKhanDecoderStepReset; + if(instance->common.code_count_bit >= + instance->common.code_min_count_bit_for_found) { + instance->common.code_last_found = instance->common.code_found; + instance->common.code_last_count_bit = instance->common.code_count_bit; + if(instance->common.callback) + instance->common.callback( + (SubGhzProtocolCommon*)instance, instance->common.context); + } + instance->common.code_found = 0; + instance->common.code_count_bit = 0; + break; + } else { + instance->common.te_last = duration; + instance->common.parser_step = ScherKhanDecoderStepCheckDuration; + } + + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + break; + case ScherKhanDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->common.te_last, instance->common.te_short) < + instance->common.te_delta) && + (DURATION_DIFF(duration, instance->common.te_short) < instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 0); + instance->common.parser_step = ScherKhanDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->common.te_last, instance->common.te_long) < + instance->common.te_delta) && + (DURATION_DIFF(duration, instance->common.te_long) < instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 1); + instance->common.parser_step = ScherKhanDecoderStepSaveDuration; + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + break; + } +} + +void subghz_protocol_scher_khan_to_str(SubGhzProtocolScherKhan* instance, string_t output) { + subghz_protocol_scher_khan_check_remote_controller(instance); + + string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:%07lX Btn:%lX Cnt:%04X\r\n" + "Pt: %s\r\n", + instance->common.name, + instance->common.code_last_count_bit, + (uint32_t)(instance->common.code_last_found >> 32), + (uint32_t)instance->common.code_last_found, + instance->common.serial, + instance->common.btn, + instance->common.cnt, + instance->protocol_name); +} + +void subghz_decoder_scher_khan_to_load_protocol(SubGhzProtocolScherKhan* instance, void* context) { + furi_assert(context); + furi_assert(instance); + SubGhzProtocolCommonLoad* data = context; + instance->common.code_last_found = data->code_found; + instance->common.code_last_count_bit = data->code_count_bit; + subghz_protocol_scher_khan_check_remote_controller(instance); +} \ No newline at end of file diff --git a/lib/subghz/protocols/subghz_protocol_scher_khan.h b/lib/subghz/protocols/subghz_protocol_scher_khan.h new file mode 100644 index 00000000..3aa06701 --- /dev/null +++ b/lib/subghz/protocols/subghz_protocol_scher_khan.h @@ -0,0 +1,58 @@ +#pragma once + +#include "subghz_protocol_common.h" + +typedef struct SubGhzProtocolScherKhan SubGhzProtocolScherKhan; + +/** Allocate SubGhzProtocolScherKhan + * + * @return SubGhzProtocolScherKhan* + */ +SubGhzProtocolScherKhan* subghz_protocol_scher_khan_alloc(); + +/** Free SubGhzProtocolScherKhan + * + * @param instance + */ +void subghz_protocol_scher_khan_free(SubGhzProtocolScherKhan* instance); + +/** Sends the key on the air + * + * @param instance - SubGhzProtocolScherKhan instance + * @param key - key send + * @param bit - count bit key + * @param repeat - repeat send key + */ +void subghz_protocol_scher_khan_send_key(SubGhzProtocolScherKhan* instance, uint64_t key, uint8_t bit, uint8_t repeat); + +/** Reset internal state + * @param instance - SubGhzProtocolScherKhan instance + */ +void subghz_protocol_scher_khan_reset(SubGhzProtocolScherKhan* instance); + +/** Analysis of received data + * + * @param instance SubGhzProtocolScherKhan instance + */ +void subghz_protocol_scher_khan_check_remote_controller(SubGhzProtocolScherKhan* instance); + +/** Parse accepted duration + * + * @param instance - SubGhzProtocolScherKhan instance + * @param data - LevelDuration level_duration + */ +void subghz_protocol_scher_khan_parse(SubGhzProtocolScherKhan* instance, bool level, uint32_t duration); + +/** Outputting information from the parser + * + * @param instance - SubGhzProtocolScherKhan* instance + * @param output - output string + */ +void subghz_protocol_scher_khan_to_str(SubGhzProtocolScherKhan* instance, string_t output); + +/** Loading protocol from bin data + * + * @param instance - SubGhzProtocolScherKhan instance + * @param context - SubGhzProtocolCommonLoad context + */ +void subghz_decoder_scher_khan_to_load_protocol(SubGhzProtocolScherKhan* instance, void* context); diff --git a/lib/subghz/subghz_parser.c b/lib/subghz/subghz_parser.c index 6fc8f1f4..fed09fc5 100644 --- a/lib/subghz/subghz_parser.c +++ b/lib/subghz/subghz_parser.c @@ -12,6 +12,7 @@ #include "protocols/subghz_protocol_nero_sketch.h" #include "protocols/subghz_protocol_star_line.h" #include "protocols/subghz_protocol_nero_radio.h" +#include "protocols/subghz_protocol_scher_khan.h" #include "subghz_keystore.h" @@ -30,6 +31,7 @@ typedef enum { SubGhzProtocolTypeNeroSketch, SubGhzProtocolTypeStarLine, SubGhzProtocolTypeNeroRadio, + SubGhzProtocolTypeScherKhan, SubGhzProtocolTypeMax, } SubGhzProtocolType; @@ -54,7 +56,7 @@ static void subghz_parser_text_rx_callback(SubGhzProtocolCommon* parser, void* c if(instance->text_callback) { instance->text_callback(output, instance->text_callback_context); } else { - printf(string_get_cstr(output)); + printf("%s", string_get_cstr(output)); } string_clear(output); } @@ -93,6 +95,8 @@ SubGhzParser* subghz_parser_alloc() { (SubGhzProtocolCommon*)subghz_protocol_star_line_alloc(instance->keystore); instance->protocols[SubGhzProtocolTypeNeroRadio] = (SubGhzProtocolCommon*)subghz_protocol_nero_radio_alloc(); + instance->protocols[SubGhzProtocolTypeScherKhan] = + (SubGhzProtocolCommon*)subghz_protocol_scher_khan_alloc(); return instance; } @@ -120,6 +124,8 @@ void subghz_parser_free(SubGhzParser* instance) { (SubGhzProtocolStarLine*)instance->protocols[SubGhzProtocolTypeStarLine]); subghz_protocol_nero_radio_free( (SubGhzProtocolNeroRadio*)instance->protocols[SubGhzProtocolTypeNeroRadio]); + subghz_protocol_scher_khan_free( + (SubGhzProtocolScherKhan*)instance->protocols[SubGhzProtocolTypeScherKhan]); subghz_keystore_free(instance->keystore); @@ -199,6 +205,8 @@ void subghz_parser_reset(SubGhzParser* instance) { (SubGhzProtocolStarLine*)instance->protocols[SubGhzProtocolTypeStarLine]); subghz_protocol_nero_radio_reset( (SubGhzProtocolNeroRadio*)instance->protocols[SubGhzProtocolTypeNeroRadio]); + subghz_protocol_scher_khan_reset( + (SubGhzProtocolScherKhan*)instance->protocols[SubGhzProtocolTypeScherKhan]); } void subghz_parser_parse(SubGhzParser* instance, bool level, uint32_t duration) { @@ -232,4 +240,8 @@ void subghz_parser_parse(SubGhzParser* instance, bool level, uint32_t duration) (SubGhzProtocolNeroRadio*)instance->protocols[SubGhzProtocolTypeNeroRadio], level, duration); + subghz_protocol_scher_khan_parse( + (SubGhzProtocolScherKhan*)instance->protocols[SubGhzProtocolTypeScherKhan], + level, + duration); } diff --git a/lib/subghz/subghz_worker.c b/lib/subghz/subghz_worker.c index 626ec77f..8697b394 100644 --- a/lib/subghz/subghz_worker.c +++ b/lib/subghz/subghz_worker.c @@ -10,6 +10,10 @@ struct SubGhzWorker { volatile bool running; volatile bool overrun; + LevelDuration filter_level_duration; + bool filter_running; + uint16_t filter_duration; + SubGhzWorkerOverrunCallback overrun_callback; SubGhzWorkerPairCallback pair_callback; void* context; @@ -30,8 +34,8 @@ void subghz_worker_rx_callback(bool level, uint32_t duration, void* context) { instance->overrun = false; level_duration = level_duration_reset(); } - size_t ret = - xStreamBufferSendFromISR(instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken); + size_t ret = xStreamBufferSendFromISR( + instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken); if(sizeof(LevelDuration) != ret) instance->overrun = true; portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } @@ -46,15 +50,35 @@ static int32_t subghz_worker_thread_callback(void* context) { LevelDuration level_duration; while(instance->running) { - int ret = xStreamBufferReceive(instance->stream, &level_duration, sizeof(LevelDuration), 10); + int ret = + xStreamBufferReceive(instance->stream, &level_duration, sizeof(LevelDuration), 10); if(ret == sizeof(LevelDuration)) { if(level_duration_is_reset(level_duration)) { printf("."); - if (instance->overrun_callback) instance->overrun_callback(instance->context); + if(instance->overrun_callback) instance->overrun_callback(instance->context); } else { bool level = level_duration_get_level(level_duration); uint32_t duration = level_duration_get_duration(level_duration); - if (instance->pair_callback) instance->pair_callback(instance->context, level, duration); + + if(instance->filter_running) { + if((duration < instance->filter_duration) || + (instance->filter_level_duration.level == level)) { + instance->filter_level_duration.duration += duration; + + } else if(instance->filter_level_duration.level != level) { + if(instance->pair_callback) + instance->pair_callback( + instance->context, + instance->filter_level_duration.level, + instance->filter_level_duration.duration); + + instance->filter_level_duration.duration = duration; + instance->filter_level_duration.level = level; + } + } else { + if(instance->pair_callback) + instance->pair_callback(instance->context, level, duration); + } } } } @@ -70,9 +94,13 @@ SubGhzWorker* subghz_worker_alloc() { 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->stream = xStreamBufferCreate(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); + //setting filter + instance->filter_running = true; + instance->filter_duration = 20; + return instance; } @@ -85,7 +113,9 @@ void subghz_worker_free(SubGhzWorker* instance) { free(instance); } -void subghz_worker_set_overrun_callback(SubGhzWorker* instance, SubGhzWorkerOverrunCallback callback) { +void subghz_worker_set_overrun_callback( + SubGhzWorker* instance, + SubGhzWorkerOverrunCallback callback) { furi_assert(instance); instance->overrun_callback = callback; } diff --git a/scripts/flipper/copro.py b/scripts/flipper/copro.py index bb1b40f7..a7f3b95f 100644 --- a/scripts/flipper/copro.py +++ b/scripts/flipper/copro.py @@ -17,7 +17,7 @@ MANIFEST_TEMPLATE = { "type": 1, "major": 1, "minor": 12, - "sub": 0, + "sub": 1, "branch": 0, "release": 7, }, @@ -44,14 +44,14 @@ class Copro: raise Exception(f'"{self.mcu_copro}" doesn\'t exists') cube_manifest_file = os.path.join(self.cube_dir, "package.xml") cube_manifest = ET.parse(cube_manifest_file) - cube_version = cube_manifest.find("PackDescription") - if not cube_version: + cube_package = cube_manifest.find("PackDescription") + if not cube_package: raise Exception(f"Unknown Cube manifest format") - cube_version = cube_version.get("Release") + cube_version = cube_package.get("Patch") or cube_package.get("Release") if not cube_version or not cube_version.startswith("FW.WB"): raise Exception(f"Incorrect Cube package or version info") cube_version = cube_version.replace("FW.WB.", "", 1) - if cube_version != "1.12.0": + if cube_version != "1.12.1": raise Exception(f"Unknonwn cube version") self.version = cube_version diff --git a/scripts/ob.py b/scripts/ob.py index 64a200c1..be15a983 100755 --- a/scripts/ob.py +++ b/scripts/ob.py @@ -44,7 +44,13 @@ class Main: self.logger.addHandler(self.handler) # execute requested function self.loadOB() - self.args.func() + + return_code = self.args.func() + if isinstance(return_code, int): + return return_code + else: + self.logger.error(f"Forgotten return code") + return 255 def loadOB(self): self.logger.info(f"Loading Option Bytes data") @@ -70,11 +76,11 @@ class Main: except subprocess.CalledProcessError as e: self.logger.error(e.output.decode()) self.logger.error(f"Failed to call STM32_Programmer_CLI") - return + return 127 except Exception as e: self.logger.error(f"Failed to call STM32_Programmer_CLI") self.logger.exception(e) - return + return 126 ob_correct = True for line in output.decode().split("\n"): line = line.strip() @@ -98,9 +104,10 @@ class Main: ob_correct = False if ob_correct: self.logger.info(f"OB Check OK") + return 0 else: self.logger.error(f"OB Check FAIL") - exit(255) + return 255 def set(self): self.logger.info(f"Setting Option Bytes") @@ -124,12 +131,14 @@ class Main: except subprocess.CalledProcessError as e: self.logger.error(e.output.decode()) self.logger.error(f"Failed to call STM32_Programmer_CLI") - return + return 125 except Exception as e: self.logger.error(f"Failed to call STM32_Programmer_CLI") self.logger.exception(e) - return + return 124 + return 0 if __name__ == "__main__": - Main()() + return_code = Main()() + exit(return_code)