From f63cf194519ae977c479cbb75017117b3964e597 Mon Sep 17 00:00:00 2001 From: Seger End Date: Tue, 31 Dec 2024 09:58:41 +0100 Subject: [PATCH 01/27] New ufbt version --- .gitignore | 5 +- app.c | 495 +++++++++++++++++++++++++++++++++++++++++++++++++++++ ldtoypad.c | 18 +- 3 files changed, 514 insertions(+), 4 deletions(-) create mode 100644 app.c diff --git a/.gitignore b/.gitignore index e2a15a1..dd6a475 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ dist/* .vscode .clang-format -.editorconfig \ No newline at end of file +.clangd +.editorconfig +.env +.ufbt diff --git a/app.c b/app.c new file mode 100644 index 0000000..672d9f2 --- /dev/null +++ b/app.c @@ -0,0 +1,495 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "skeleton_app_icons.h" + +#define TAG "Skeleton" + +// Change this to BACKLIGHT_AUTO if you don't want the backlight to be continuously on. +#define BACKLIGHT_ON 1 + +// Our application menu has 3 items. You can add more items if you want. +typedef enum { + SkeletonSubmenuIndexConfigure, + SkeletonSubmenuIndexGame, + SkeletonSubmenuIndexAbout, +} SkeletonSubmenuIndex; + +// Each view is a screen we show the user. +typedef enum { + SkeletonViewSubmenu, // The menu when the app starts + SkeletonViewTextInput, // Input for configuring text settings + SkeletonViewConfigure, // The configuration screen + SkeletonViewGame, // The main screen + SkeletonViewAbout, // The about screen with directions, link to social channel, etc. +} SkeletonView; + +typedef enum { + SkeletonEventIdRedrawScreen = 0, // Custom event to redraw the screen + SkeletonEventIdOkPressed = 42, // Custom event to process OK button getting pressed down +} SkeletonEventId; + +typedef struct { + ViewDispatcher* view_dispatcher; // Switches between our views + NotificationApp* notifications; // Used for controlling the backlight + Submenu* submenu; // The application menu + TextInput* text_input; // The text input screen + VariableItemList* variable_item_list_config; // The configuration screen + View* view_game; // The main screen + Widget* widget_about; // The about screen + + VariableItem* setting_2_item; // The name setting item (so we can update the text) + char* temp_buffer; // Temporary buffer for text input + uint32_t temp_buffer_size; // Size of temporary buffer + + FuriTimer* timer; // Timer for redrawing the screen +} SkeletonApp; + +typedef struct { + uint32_t setting_1_index; // The team color setting index + FuriString* setting_2_name; // The name setting + uint8_t x; // The x coordinate +} SkeletonGameModel; + +/** + * @brief Callback for exiting the application. + * @details This function is called when user press back button. We return VIEW_NONE to + * indicate that we want to exit the application. + * @param _context The context - unused + * @return next view id +*/ +static uint32_t skeleton_navigation_exit_callback(void* _context) { + UNUSED(_context); + return VIEW_NONE; +} + +/** + * @brief Callback for returning to submenu. + * @details This function is called when user press back button. We return VIEW_NONE to + * indicate that we want to navigate to the submenu. + * @param _context The context - unused + * @return next view id +*/ +static uint32_t skeleton_navigation_submenu_callback(void* _context) { + UNUSED(_context); + return SkeletonViewSubmenu; +} + +/** + * @brief Callback for returning to configure screen. + * @details This function is called when user press back button. We return VIEW_NONE to + * indicate that we want to navigate to the configure screen. + * @param _context The context - unused + * @return next view id +*/ +static uint32_t skeleton_navigation_configure_callback(void* _context) { + UNUSED(_context); + return SkeletonViewConfigure; +} + +/** + * @brief Handle submenu item selection. + * @details This function is called when user selects an item from the submenu. + * @param context The context - SkeletonApp object. + * @param index The SkeletonSubmenuIndex item that was clicked. +*/ +static void skeleton_submenu_callback(void* context, uint32_t index) { + SkeletonApp* app = (SkeletonApp*)context; + switch(index) { + case SkeletonSubmenuIndexConfigure: + view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewConfigure); + break; + case SkeletonSubmenuIndexGame: + view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewGame); + break; + case SkeletonSubmenuIndexAbout: + view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewAbout); + break; + default: + break; + } +} + +/** + * Our 1st sample setting is a team color. We have 3 options: red, green, and blue. +*/ +static const char* setting_1_config_label = "Team color"; +static uint8_t setting_1_values[] = {1, 2, 4}; +static char* setting_1_names[] = {"Red", "Green", "Blue"}; +static void skeleton_setting_1_change(VariableItem* item) { + SkeletonApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, setting_1_names[index]); + SkeletonGameModel* model = view_get_model(app->view_game); + model->setting_1_index = index; +} + +/** + * Our 2nd sample setting is a text field. When the user clicks OK on the configuration + * setting we use a text input screen to allow the user to enter a name. This function is + * called when the user clicks OK on the text input screen. +*/ +static const char* setting_2_config_label = "Name"; +static const char* setting_2_entry_text = "Enter name"; +static const char* setting_2_default_value = "Bob"; +static void skeleton_setting_2_text_updated(void* context) { + SkeletonApp* app = (SkeletonApp*)context; + bool redraw = true; + with_view_model( + app->view_game, + SkeletonGameModel * model, + { + furi_string_set(model->setting_2_name, app->temp_buffer); + variable_item_set_current_value_text( + app->setting_2_item, furi_string_get_cstr(model->setting_2_name)); + }, + redraw); + view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewConfigure); +} + +/** + * @brief Callback when item in configuration screen is clicked. + * @details This function is called when user clicks OK on an item in the configuration screen. + * If the item clicked is our text field then we switch to the text input screen. + * @param context The context - SkeletonApp object. + * @param index - The index of the item that was clicked. +*/ +static void skeleton_setting_item_clicked(void* context, uint32_t index) { + SkeletonApp* app = (SkeletonApp*)context; + index++; // The index starts at zero, but we want to start at 1. + + // Our configuration UI has the 2nd item as a text field. + if(index == 2) { + // Header to display on the text input screen. + text_input_set_header_text(app->text_input, setting_2_entry_text); + + // Copy the current name into the temporary buffer. + bool redraw = false; + with_view_model( + app->view_game, + SkeletonGameModel * model, + { + strncpy( + app->temp_buffer, + furi_string_get_cstr(model->setting_2_name), + app->temp_buffer_size); + }, + redraw); + + // Configure the text input. When user enters text and clicks OK, skeleton_setting_text_updated be called. + bool clear_previous_text = false; + text_input_set_result_callback( + app->text_input, + skeleton_setting_2_text_updated, + app, + app->temp_buffer, + app->temp_buffer_size, + clear_previous_text); + + // Pressing the BACK button will reload the configure screen. + view_set_previous_callback( + text_input_get_view(app->text_input), skeleton_navigation_configure_callback); + + // Show text input dialog. + view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewTextInput); + } +} + +/** + * @brief Callback for drawing the game screen. + * @details This function is called when the screen needs to be redrawn, like when the model gets updated. + * @param canvas The canvas to draw on. + * @param model The model - MyModel object. +*/ +static void skeleton_view_game_draw_callback(Canvas* canvas, void* model) { + SkeletonGameModel* my_model = (SkeletonGameModel*)model; + canvas_draw_icon(canvas, my_model->x, 20, &I_glyph_1_14x40); + canvas_draw_str(canvas, 1, 10, "LEFT/RIGHT to change x"); + FuriString* xstr = furi_string_alloc(); + furi_string_printf(xstr, "x: %u OK=play tone", my_model->x); + canvas_draw_str(canvas, 44, 24, furi_string_get_cstr(xstr)); + furi_string_printf(xstr, "random: %u", (uint8_t)(furi_hal_random_get() % 256)); + canvas_draw_str(canvas, 44, 36, furi_string_get_cstr(xstr)); + furi_string_printf( + xstr, + "team: %s (%u)", + setting_1_names[my_model->setting_1_index], + setting_1_values[my_model->setting_1_index]); + canvas_draw_str(canvas, 44, 48, furi_string_get_cstr(xstr)); + furi_string_printf(xstr, "name: %s", furi_string_get_cstr(my_model->setting_2_name)); + canvas_draw_str(canvas, 44, 60, furi_string_get_cstr(xstr)); + furi_string_free(xstr); +} + +/** + * @brief Callback for timer elapsed. + * @details This function is called when the timer is elapsed. We use this to queue a redraw event. + * @param context The context - SkeletonApp object. +*/ +static void skeleton_view_game_timer_callback(void* context) { + SkeletonApp* app = (SkeletonApp*)context; + view_dispatcher_send_custom_event(app->view_dispatcher, SkeletonEventIdRedrawScreen); +} + +/** + * @brief Callback when the user starts the game screen. + * @details This function is called when the user enters the game screen. We start a timer to + * redraw the screen periodically (so the random number is refreshed). + * @param context The context - SkeletonApp object. +*/ +static void skeleton_view_game_enter_callback(void* context) { + uint32_t period = furi_ms_to_ticks(200); + SkeletonApp* app = (SkeletonApp*)context; + furi_assert(app->timer == NULL); + app->timer = + furi_timer_alloc(skeleton_view_game_timer_callback, FuriTimerTypePeriodic, context); + furi_timer_start(app->timer, period); +} + +/** + * @brief Callback when the user exits the game screen. + * @details This function is called when the user exits the game screen. We stop the timer. + * @param context The context - SkeletonApp object. +*/ +static void skeleton_view_game_exit_callback(void* context) { + SkeletonApp* app = (SkeletonApp*)context; + furi_timer_stop(app->timer); + furi_timer_free(app->timer); + app->timer = NULL; +} + +/** + * @brief Callback for custom events. + * @details This function is called when a custom event is sent to the view dispatcher. + * @param event The event id - SkeletonEventId value. + * @param context The context - SkeletonApp object. +*/ +static bool skeleton_view_game_custom_event_callback(uint32_t event, void* context) { + SkeletonApp* app = (SkeletonApp*)context; + switch(event) { + case SkeletonEventIdRedrawScreen: + // Redraw screen by passing true to last parameter of with_view_model. + { + bool redraw = true; + with_view_model( + app->view_game, SkeletonGameModel * _model, { UNUSED(_model); }, redraw); + return true; + } + case SkeletonEventIdOkPressed: + // Process the OK button. We play a tone based on the x coordinate. + if(furi_hal_speaker_acquire(500)) { + float frequency; + bool redraw = false; + with_view_model( + app->view_game, + SkeletonGameModel * model, + { frequency = model->x * 100 + 100; }, + redraw); + furi_hal_speaker_start(frequency, 1.0); + furi_delay_ms(100); + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } + return true; + default: + return false; + } +} + +/** + * @brief Callback for game screen input. + * @details This function is called when the user presses a button while on the game screen. + * @param event The event - InputEvent object. + * @param context The context - SkeletonApp object. + * @return true if the event was handled, false otherwise. +*/ +static bool skeleton_view_game_input_callback(InputEvent* event, void* context) { + SkeletonApp* app = (SkeletonApp*)context; + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + // Left button clicked, reduce x coordinate. + bool redraw = true; + with_view_model( + app->view_game, + SkeletonGameModel * model, + { + if(model->x > 0) { + model->x--; + } + }, + redraw); + } else if(event->key == InputKeyRight) { + // Right button clicked, increase x coordinate. + bool redraw = true; + with_view_model( + app->view_game, + SkeletonGameModel * model, + { + // Should we have some maximum value? + model->x++; + }, + redraw); + } + } else if(event->type == InputTypePress) { + if(event->key == InputKeyOk) { + // We choose to send a custom event when user presses OK button. skeleton_custom_event_callback will + // handle our SkeletonEventIdOkPressed event. We could have just put the code from + // skeleton_custom_event_callback here, it's a matter of preference. + view_dispatcher_send_custom_event(app->view_dispatcher, SkeletonEventIdOkPressed); + return true; + } + } + + return false; +} + +/** + * @brief Allocate the skeleton application. + * @details This function allocates the skeleton application resources. + * @return SkeletonApp object. +*/ +static SkeletonApp* skeleton_app_alloc() { + SkeletonApp* app = (SkeletonApp*)malloc(sizeof(SkeletonApp)); + + Gui* gui = furi_record_open(RECORD_GUI); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + app->submenu = submenu_alloc(); + submenu_add_item( + app->submenu, "Config", SkeletonSubmenuIndexConfigure, skeleton_submenu_callback, app); + submenu_add_item( + app->submenu, "Play", SkeletonSubmenuIndexGame, skeleton_submenu_callback, app); + submenu_add_item( + app->submenu, "About", SkeletonSubmenuIndexAbout, skeleton_submenu_callback, app); + view_set_previous_callback(submenu_get_view(app->submenu), skeleton_navigation_exit_callback); + view_dispatcher_add_view( + app->view_dispatcher, SkeletonViewSubmenu, submenu_get_view(app->submenu)); + view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewSubmenu); + + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, SkeletonViewTextInput, text_input_get_view(app->text_input)); + app->temp_buffer_size = 32; + app->temp_buffer = (char*)malloc(app->temp_buffer_size); + + app->variable_item_list_config = variable_item_list_alloc(); + variable_item_list_reset(app->variable_item_list_config); + VariableItem* item = variable_item_list_add( + app->variable_item_list_config, + setting_1_config_label, + COUNT_OF(setting_1_values), + skeleton_setting_1_change, + app); + uint8_t setting_1_index = 0; + variable_item_set_current_value_index(item, setting_1_index); + variable_item_set_current_value_text(item, setting_1_names[setting_1_index]); + + FuriString* setting_2_name = furi_string_alloc(); + furi_string_set_str(setting_2_name, setting_2_default_value); + app->setting_2_item = variable_item_list_add( + app->variable_item_list_config, setting_2_config_label, 1, NULL, NULL); + variable_item_set_current_value_text( + app->setting_2_item, furi_string_get_cstr(setting_2_name)); + variable_item_list_set_enter_callback( + app->variable_item_list_config, skeleton_setting_item_clicked, app); + + view_set_previous_callback( + variable_item_list_get_view(app->variable_item_list_config), + skeleton_navigation_submenu_callback); + view_dispatcher_add_view( + app->view_dispatcher, + SkeletonViewConfigure, + variable_item_list_get_view(app->variable_item_list_config)); + + app->view_game = view_alloc(); + view_set_draw_callback(app->view_game, skeleton_view_game_draw_callback); + view_set_input_callback(app->view_game, skeleton_view_game_input_callback); + view_set_previous_callback(app->view_game, skeleton_navigation_submenu_callback); + view_set_enter_callback(app->view_game, skeleton_view_game_enter_callback); + view_set_exit_callback(app->view_game, skeleton_view_game_exit_callback); + view_set_context(app->view_game, app); + view_set_custom_callback(app->view_game, skeleton_view_game_custom_event_callback); + view_allocate_model(app->view_game, ViewModelTypeLockFree, sizeof(SkeletonGameModel)); + SkeletonGameModel* model = view_get_model(app->view_game); + model->setting_1_index = setting_1_index; + model->setting_2_name = setting_2_name; + model->x = 0; + view_dispatcher_add_view(app->view_dispatcher, SkeletonViewGame, app->view_game); + + app->widget_about = widget_alloc(); + widget_add_text_scroll_element( + app->widget_about, + 0, + 0, + 128, + 64, + "This is a sample application.\n---\nReplace code and message\nwith your content!\n\nauthor: @codeallnight\nhttps://discord.com/invite/NsjCvqwPAd\nhttps://youtube.com/@MrDerekJamison"); + view_set_previous_callback( + widget_get_view(app->widget_about), skeleton_navigation_submenu_callback); + view_dispatcher_add_view( + app->view_dispatcher, SkeletonViewAbout, widget_get_view(app->widget_about)); + + app->notifications = furi_record_open(RECORD_NOTIFICATION); + +#ifdef BACKLIGHT_ON + notification_message(app->notifications, &sequence_display_backlight_enforce_on); +#endif + + return app; +} + +/** + * @brief Free the skeleton application. + * @details This function frees the skeleton application resources. + * @param app The skeleton application object. +*/ +static void skeleton_app_free(SkeletonApp* app) { +#ifdef BACKLIGHT_ON + notification_message(app->notifications, &sequence_display_backlight_enforce_auto); +#endif + furi_record_close(RECORD_NOTIFICATION); + + view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewTextInput); + text_input_free(app->text_input); + free(app->temp_buffer); + view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewAbout); + widget_free(app->widget_about); + view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewGame); + view_free(app->view_game); + view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewConfigure); + variable_item_list_free(app->variable_item_list_config); + view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_free(app->view_dispatcher); + furi_record_close(RECORD_GUI); + + free(app); +} + +/** + * @brief Main function for skeleton application. + * @details This function is the entry point for the skeleton application. It should be defined in + * application.fam as the entry_point setting. + * @param _p Input parameter - unused + * @return 0 - Success +*/ +int32_t main_skeleton_app(void* _p) { + UNUSED(_p); + + SkeletonApp* app = skeleton_app_alloc(); + view_dispatcher_run(app->view_dispatcher); + + skeleton_app_free(app); + return 0; +} \ No newline at end of file diff --git a/ldtoypad.c b/ldtoypad.c index 0b47d2d..f5f7712 100644 --- a/ldtoypad.c +++ b/ldtoypad.c @@ -68,7 +68,6 @@ LDToyPadApp* ldtoypad_app_alloc() { // View dispatcher app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); // Submenu view @@ -122,6 +121,8 @@ void ldtoypad_app_free(LDToyPadApp* app) { // Notification turn LED blue // notification_internal_message(app->notifications, &sequence_reset_blue); + // Free more things + // Free all the views view_dispatcher_remove_view(app->view_dispatcher, LDToyPadView_Submenu); submenu_free(app->submenu); @@ -138,7 +139,18 @@ void ldtoypad_app_free(LDToyPadApp* app) { // // Close records furi_record_close(RECORD_GUI); - app->gui = NULL; + + free(app->gui); + + free(app->submenu); + + free(app->dialog); + + free(app->ldtoypad_emulate_view); + + free(app->view_dispatcher); + + // app->gui = NULL; // furi_record_close(RECORD_NOTIFICATION); // app->notifications = NULL; @@ -155,4 +167,4 @@ int32_t ldtoypad_app(void* p) { ldtoypad_app_free(app); return 0; -} \ No newline at end of file +} From c34ab7ef7e87bdf6736c06a710a141254be646c2 Mon Sep 17 00:00:00 2001 From: Seger End Date: Tue, 31 Dec 2024 10:56:28 +0100 Subject: [PATCH 02/27] Added skeleton app --- app.c | 495 --------------------------------------- application.fam | 6 +- ldtoypad.c | 527 ++++++++++++++++++++++++++++++++---------- ldtoypad.h | 50 ++-- views/EmulateToyPad.c | 2 + views/Settings.c | 1 - views/Settings.h | 0 7 files changed, 440 insertions(+), 641 deletions(-) delete mode 100644 app.c delete mode 100644 views/Settings.c delete mode 100644 views/Settings.h diff --git a/app.c b/app.c deleted file mode 100644 index 672d9f2..0000000 --- a/app.c +++ /dev/null @@ -1,495 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "skeleton_app_icons.h" - -#define TAG "Skeleton" - -// Change this to BACKLIGHT_AUTO if you don't want the backlight to be continuously on. -#define BACKLIGHT_ON 1 - -// Our application menu has 3 items. You can add more items if you want. -typedef enum { - SkeletonSubmenuIndexConfigure, - SkeletonSubmenuIndexGame, - SkeletonSubmenuIndexAbout, -} SkeletonSubmenuIndex; - -// Each view is a screen we show the user. -typedef enum { - SkeletonViewSubmenu, // The menu when the app starts - SkeletonViewTextInput, // Input for configuring text settings - SkeletonViewConfigure, // The configuration screen - SkeletonViewGame, // The main screen - SkeletonViewAbout, // The about screen with directions, link to social channel, etc. -} SkeletonView; - -typedef enum { - SkeletonEventIdRedrawScreen = 0, // Custom event to redraw the screen - SkeletonEventIdOkPressed = 42, // Custom event to process OK button getting pressed down -} SkeletonEventId; - -typedef struct { - ViewDispatcher* view_dispatcher; // Switches between our views - NotificationApp* notifications; // Used for controlling the backlight - Submenu* submenu; // The application menu - TextInput* text_input; // The text input screen - VariableItemList* variable_item_list_config; // The configuration screen - View* view_game; // The main screen - Widget* widget_about; // The about screen - - VariableItem* setting_2_item; // The name setting item (so we can update the text) - char* temp_buffer; // Temporary buffer for text input - uint32_t temp_buffer_size; // Size of temporary buffer - - FuriTimer* timer; // Timer for redrawing the screen -} SkeletonApp; - -typedef struct { - uint32_t setting_1_index; // The team color setting index - FuriString* setting_2_name; // The name setting - uint8_t x; // The x coordinate -} SkeletonGameModel; - -/** - * @brief Callback for exiting the application. - * @details This function is called when user press back button. We return VIEW_NONE to - * indicate that we want to exit the application. - * @param _context The context - unused - * @return next view id -*/ -static uint32_t skeleton_navigation_exit_callback(void* _context) { - UNUSED(_context); - return VIEW_NONE; -} - -/** - * @brief Callback for returning to submenu. - * @details This function is called when user press back button. We return VIEW_NONE to - * indicate that we want to navigate to the submenu. - * @param _context The context - unused - * @return next view id -*/ -static uint32_t skeleton_navigation_submenu_callback(void* _context) { - UNUSED(_context); - return SkeletonViewSubmenu; -} - -/** - * @brief Callback for returning to configure screen. - * @details This function is called when user press back button. We return VIEW_NONE to - * indicate that we want to navigate to the configure screen. - * @param _context The context - unused - * @return next view id -*/ -static uint32_t skeleton_navigation_configure_callback(void* _context) { - UNUSED(_context); - return SkeletonViewConfigure; -} - -/** - * @brief Handle submenu item selection. - * @details This function is called when user selects an item from the submenu. - * @param context The context - SkeletonApp object. - * @param index The SkeletonSubmenuIndex item that was clicked. -*/ -static void skeleton_submenu_callback(void* context, uint32_t index) { - SkeletonApp* app = (SkeletonApp*)context; - switch(index) { - case SkeletonSubmenuIndexConfigure: - view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewConfigure); - break; - case SkeletonSubmenuIndexGame: - view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewGame); - break; - case SkeletonSubmenuIndexAbout: - view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewAbout); - break; - default: - break; - } -} - -/** - * Our 1st sample setting is a team color. We have 3 options: red, green, and blue. -*/ -static const char* setting_1_config_label = "Team color"; -static uint8_t setting_1_values[] = {1, 2, 4}; -static char* setting_1_names[] = {"Red", "Green", "Blue"}; -static void skeleton_setting_1_change(VariableItem* item) { - SkeletonApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, setting_1_names[index]); - SkeletonGameModel* model = view_get_model(app->view_game); - model->setting_1_index = index; -} - -/** - * Our 2nd sample setting is a text field. When the user clicks OK on the configuration - * setting we use a text input screen to allow the user to enter a name. This function is - * called when the user clicks OK on the text input screen. -*/ -static const char* setting_2_config_label = "Name"; -static const char* setting_2_entry_text = "Enter name"; -static const char* setting_2_default_value = "Bob"; -static void skeleton_setting_2_text_updated(void* context) { - SkeletonApp* app = (SkeletonApp*)context; - bool redraw = true; - with_view_model( - app->view_game, - SkeletonGameModel * model, - { - furi_string_set(model->setting_2_name, app->temp_buffer); - variable_item_set_current_value_text( - app->setting_2_item, furi_string_get_cstr(model->setting_2_name)); - }, - redraw); - view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewConfigure); -} - -/** - * @brief Callback when item in configuration screen is clicked. - * @details This function is called when user clicks OK on an item in the configuration screen. - * If the item clicked is our text field then we switch to the text input screen. - * @param context The context - SkeletonApp object. - * @param index - The index of the item that was clicked. -*/ -static void skeleton_setting_item_clicked(void* context, uint32_t index) { - SkeletonApp* app = (SkeletonApp*)context; - index++; // The index starts at zero, but we want to start at 1. - - // Our configuration UI has the 2nd item as a text field. - if(index == 2) { - // Header to display on the text input screen. - text_input_set_header_text(app->text_input, setting_2_entry_text); - - // Copy the current name into the temporary buffer. - bool redraw = false; - with_view_model( - app->view_game, - SkeletonGameModel * model, - { - strncpy( - app->temp_buffer, - furi_string_get_cstr(model->setting_2_name), - app->temp_buffer_size); - }, - redraw); - - // Configure the text input. When user enters text and clicks OK, skeleton_setting_text_updated be called. - bool clear_previous_text = false; - text_input_set_result_callback( - app->text_input, - skeleton_setting_2_text_updated, - app, - app->temp_buffer, - app->temp_buffer_size, - clear_previous_text); - - // Pressing the BACK button will reload the configure screen. - view_set_previous_callback( - text_input_get_view(app->text_input), skeleton_navigation_configure_callback); - - // Show text input dialog. - view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewTextInput); - } -} - -/** - * @brief Callback for drawing the game screen. - * @details This function is called when the screen needs to be redrawn, like when the model gets updated. - * @param canvas The canvas to draw on. - * @param model The model - MyModel object. -*/ -static void skeleton_view_game_draw_callback(Canvas* canvas, void* model) { - SkeletonGameModel* my_model = (SkeletonGameModel*)model; - canvas_draw_icon(canvas, my_model->x, 20, &I_glyph_1_14x40); - canvas_draw_str(canvas, 1, 10, "LEFT/RIGHT to change x"); - FuriString* xstr = furi_string_alloc(); - furi_string_printf(xstr, "x: %u OK=play tone", my_model->x); - canvas_draw_str(canvas, 44, 24, furi_string_get_cstr(xstr)); - furi_string_printf(xstr, "random: %u", (uint8_t)(furi_hal_random_get() % 256)); - canvas_draw_str(canvas, 44, 36, furi_string_get_cstr(xstr)); - furi_string_printf( - xstr, - "team: %s (%u)", - setting_1_names[my_model->setting_1_index], - setting_1_values[my_model->setting_1_index]); - canvas_draw_str(canvas, 44, 48, furi_string_get_cstr(xstr)); - furi_string_printf(xstr, "name: %s", furi_string_get_cstr(my_model->setting_2_name)); - canvas_draw_str(canvas, 44, 60, furi_string_get_cstr(xstr)); - furi_string_free(xstr); -} - -/** - * @brief Callback for timer elapsed. - * @details This function is called when the timer is elapsed. We use this to queue a redraw event. - * @param context The context - SkeletonApp object. -*/ -static void skeleton_view_game_timer_callback(void* context) { - SkeletonApp* app = (SkeletonApp*)context; - view_dispatcher_send_custom_event(app->view_dispatcher, SkeletonEventIdRedrawScreen); -} - -/** - * @brief Callback when the user starts the game screen. - * @details This function is called when the user enters the game screen. We start a timer to - * redraw the screen periodically (so the random number is refreshed). - * @param context The context - SkeletonApp object. -*/ -static void skeleton_view_game_enter_callback(void* context) { - uint32_t period = furi_ms_to_ticks(200); - SkeletonApp* app = (SkeletonApp*)context; - furi_assert(app->timer == NULL); - app->timer = - furi_timer_alloc(skeleton_view_game_timer_callback, FuriTimerTypePeriodic, context); - furi_timer_start(app->timer, period); -} - -/** - * @brief Callback when the user exits the game screen. - * @details This function is called when the user exits the game screen. We stop the timer. - * @param context The context - SkeletonApp object. -*/ -static void skeleton_view_game_exit_callback(void* context) { - SkeletonApp* app = (SkeletonApp*)context; - furi_timer_stop(app->timer); - furi_timer_free(app->timer); - app->timer = NULL; -} - -/** - * @brief Callback for custom events. - * @details This function is called when a custom event is sent to the view dispatcher. - * @param event The event id - SkeletonEventId value. - * @param context The context - SkeletonApp object. -*/ -static bool skeleton_view_game_custom_event_callback(uint32_t event, void* context) { - SkeletonApp* app = (SkeletonApp*)context; - switch(event) { - case SkeletonEventIdRedrawScreen: - // Redraw screen by passing true to last parameter of with_view_model. - { - bool redraw = true; - with_view_model( - app->view_game, SkeletonGameModel * _model, { UNUSED(_model); }, redraw); - return true; - } - case SkeletonEventIdOkPressed: - // Process the OK button. We play a tone based on the x coordinate. - if(furi_hal_speaker_acquire(500)) { - float frequency; - bool redraw = false; - with_view_model( - app->view_game, - SkeletonGameModel * model, - { frequency = model->x * 100 + 100; }, - redraw); - furi_hal_speaker_start(frequency, 1.0); - furi_delay_ms(100); - furi_hal_speaker_stop(); - furi_hal_speaker_release(); - } - return true; - default: - return false; - } -} - -/** - * @brief Callback for game screen input. - * @details This function is called when the user presses a button while on the game screen. - * @param event The event - InputEvent object. - * @param context The context - SkeletonApp object. - * @return true if the event was handled, false otherwise. -*/ -static bool skeleton_view_game_input_callback(InputEvent* event, void* context) { - SkeletonApp* app = (SkeletonApp*)context; - if(event->type == InputTypeShort) { - if(event->key == InputKeyLeft) { - // Left button clicked, reduce x coordinate. - bool redraw = true; - with_view_model( - app->view_game, - SkeletonGameModel * model, - { - if(model->x > 0) { - model->x--; - } - }, - redraw); - } else if(event->key == InputKeyRight) { - // Right button clicked, increase x coordinate. - bool redraw = true; - with_view_model( - app->view_game, - SkeletonGameModel * model, - { - // Should we have some maximum value? - model->x++; - }, - redraw); - } - } else if(event->type == InputTypePress) { - if(event->key == InputKeyOk) { - // We choose to send a custom event when user presses OK button. skeleton_custom_event_callback will - // handle our SkeletonEventIdOkPressed event. We could have just put the code from - // skeleton_custom_event_callback here, it's a matter of preference. - view_dispatcher_send_custom_event(app->view_dispatcher, SkeletonEventIdOkPressed); - return true; - } - } - - return false; -} - -/** - * @brief Allocate the skeleton application. - * @details This function allocates the skeleton application resources. - * @return SkeletonApp object. -*/ -static SkeletonApp* skeleton_app_alloc() { - SkeletonApp* app = (SkeletonApp*)malloc(sizeof(SkeletonApp)); - - Gui* gui = furi_record_open(RECORD_GUI); - - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen); - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - - app->submenu = submenu_alloc(); - submenu_add_item( - app->submenu, "Config", SkeletonSubmenuIndexConfigure, skeleton_submenu_callback, app); - submenu_add_item( - app->submenu, "Play", SkeletonSubmenuIndexGame, skeleton_submenu_callback, app); - submenu_add_item( - app->submenu, "About", SkeletonSubmenuIndexAbout, skeleton_submenu_callback, app); - view_set_previous_callback(submenu_get_view(app->submenu), skeleton_navigation_exit_callback); - view_dispatcher_add_view( - app->view_dispatcher, SkeletonViewSubmenu, submenu_get_view(app->submenu)); - view_dispatcher_switch_to_view(app->view_dispatcher, SkeletonViewSubmenu); - - app->text_input = text_input_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, SkeletonViewTextInput, text_input_get_view(app->text_input)); - app->temp_buffer_size = 32; - app->temp_buffer = (char*)malloc(app->temp_buffer_size); - - app->variable_item_list_config = variable_item_list_alloc(); - variable_item_list_reset(app->variable_item_list_config); - VariableItem* item = variable_item_list_add( - app->variable_item_list_config, - setting_1_config_label, - COUNT_OF(setting_1_values), - skeleton_setting_1_change, - app); - uint8_t setting_1_index = 0; - variable_item_set_current_value_index(item, setting_1_index); - variable_item_set_current_value_text(item, setting_1_names[setting_1_index]); - - FuriString* setting_2_name = furi_string_alloc(); - furi_string_set_str(setting_2_name, setting_2_default_value); - app->setting_2_item = variable_item_list_add( - app->variable_item_list_config, setting_2_config_label, 1, NULL, NULL); - variable_item_set_current_value_text( - app->setting_2_item, furi_string_get_cstr(setting_2_name)); - variable_item_list_set_enter_callback( - app->variable_item_list_config, skeleton_setting_item_clicked, app); - - view_set_previous_callback( - variable_item_list_get_view(app->variable_item_list_config), - skeleton_navigation_submenu_callback); - view_dispatcher_add_view( - app->view_dispatcher, - SkeletonViewConfigure, - variable_item_list_get_view(app->variable_item_list_config)); - - app->view_game = view_alloc(); - view_set_draw_callback(app->view_game, skeleton_view_game_draw_callback); - view_set_input_callback(app->view_game, skeleton_view_game_input_callback); - view_set_previous_callback(app->view_game, skeleton_navigation_submenu_callback); - view_set_enter_callback(app->view_game, skeleton_view_game_enter_callback); - view_set_exit_callback(app->view_game, skeleton_view_game_exit_callback); - view_set_context(app->view_game, app); - view_set_custom_callback(app->view_game, skeleton_view_game_custom_event_callback); - view_allocate_model(app->view_game, ViewModelTypeLockFree, sizeof(SkeletonGameModel)); - SkeletonGameModel* model = view_get_model(app->view_game); - model->setting_1_index = setting_1_index; - model->setting_2_name = setting_2_name; - model->x = 0; - view_dispatcher_add_view(app->view_dispatcher, SkeletonViewGame, app->view_game); - - app->widget_about = widget_alloc(); - widget_add_text_scroll_element( - app->widget_about, - 0, - 0, - 128, - 64, - "This is a sample application.\n---\nReplace code and message\nwith your content!\n\nauthor: @codeallnight\nhttps://discord.com/invite/NsjCvqwPAd\nhttps://youtube.com/@MrDerekJamison"); - view_set_previous_callback( - widget_get_view(app->widget_about), skeleton_navigation_submenu_callback); - view_dispatcher_add_view( - app->view_dispatcher, SkeletonViewAbout, widget_get_view(app->widget_about)); - - app->notifications = furi_record_open(RECORD_NOTIFICATION); - -#ifdef BACKLIGHT_ON - notification_message(app->notifications, &sequence_display_backlight_enforce_on); -#endif - - return app; -} - -/** - * @brief Free the skeleton application. - * @details This function frees the skeleton application resources. - * @param app The skeleton application object. -*/ -static void skeleton_app_free(SkeletonApp* app) { -#ifdef BACKLIGHT_ON - notification_message(app->notifications, &sequence_display_backlight_enforce_auto); -#endif - furi_record_close(RECORD_NOTIFICATION); - - view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewTextInput); - text_input_free(app->text_input); - free(app->temp_buffer); - view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewAbout); - widget_free(app->widget_about); - view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewGame); - view_free(app->view_game); - view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewConfigure); - variable_item_list_free(app->variable_item_list_config); - view_dispatcher_remove_view(app->view_dispatcher, SkeletonViewSubmenu); - submenu_free(app->submenu); - view_dispatcher_free(app->view_dispatcher); - furi_record_close(RECORD_GUI); - - free(app); -} - -/** - * @brief Main function for skeleton application. - * @details This function is the entry point for the skeleton application. It should be defined in - * application.fam as the entry_point setting. - * @param _p Input parameter - unused - * @return 0 - Success -*/ -int32_t main_skeleton_app(void* _p) { - UNUSED(_p); - - SkeletonApp* app = skeleton_app_alloc(); - view_dispatcher_run(app->view_dispatcher); - - skeleton_app_free(app); - return 0; -} \ No newline at end of file diff --git a/application.fam b/application.fam index 30380e1..1c04ed0 100644 --- a/application.fam +++ b/application.fam @@ -5,12 +5,10 @@ App( name="LEGO Dimensions Toy Pad", # Displayed in menus apptype=FlipperAppType.EXTERNAL, entry_point="ldtoypad_app", - stack_size=2 * 1024, + stack_size=4 * 1024, fap_category="USB", requires=[ "gui", - "dialogs", - "notifications", ], # Optional values order=60, @@ -18,6 +16,6 @@ App( fap_icon="ldtoypad.png", # 10x10 1-bit PNG fap_description="USB Toy Pad emulator for Lego Dimensions", fap_author="Seger", - # fap_weburl="https://github.com/user/ldtoypad", + fap_weburl="https://github.com/SegerEnd/Flipper-Zero-LD-Toypad-Emulator", fap_icon_assets="images", # Image assets to compile for this application ) diff --git a/ldtoypad.c b/ldtoypad.c index f5f7712..f13deee 100644 --- a/ldtoypad.c +++ b/ldtoypad.c @@ -1,170 +1,455 @@ -#include "ldtoypad.h" -#include +// #include #include +// #include +// #include +#include +#include +#include +#include +#include +// #include // #include /* generated by fbt from .png files in images folder */ #include -// #include "LDToyPad_app.h" +#include "ldtoypad.h" -// #include -// #include -// #include -// #include - -// #include -// #include - -enum UsbDebugSubmenuIndex { - UsbHidSubmenuIndexEmulateToyPad, - UsbHidSubmenuIndexSettings, -}; - -void ldtoypad_submenu_callback(void* context, uint32_t index) { - furi_assert(context); - LDToyPadApp* app = context; - if(index == UsbHidSubmenuIndexEmulateToyPad) { - app->view_id = LDToyPadView_EmulateToyPad; - view_dispatcher_switch_to_view(app->view_dispatcher, LDToyPadView_EmulateToyPad); - } else if(index == UsbHidSubmenuIndexSettings) { - app->view_id = LDToyPadView_Settings; - view_dispatcher_switch_to_view(app->view_dispatcher, LDToyPadView_Settings); +// Our application menu has 3 items. You can add more items if you want. +typedef enum { + EmulateToyPadSubmenuIndex, + SettingsSubmenuIndex, + AboutSubmenuIndex, +} AppSubmenuIndex; + +typedef enum { + AppEventIdRedrawScreen = 0, // Custom event to redraw the screen + AppEventIdOkPressed = 42, // Custom event to process OK button getting pressed down +} AppEventId; + +typedef struct { + uint32_t setting_1_index; // The team color setting index + FuriString* setting_2_name; // The name setting + uint8_t x; // The x coordinate +} AppGameModel; + +/** + * @brief Callback for exiting the application. + * @details This function is called when user press back button. We return VIEW_NONE to + * indicate that we want to exit the application. + * @param _context The context - unused + * @return next view id +*/ +static uint32_t ldtoypad_navigation_exit_callback(void* _context) { + UNUSED(_context); + return VIEW_NONE; +} + +/** + * @brief Callback for returning to submenu. + * @details This function is called when user press back button. We return VIEW_NONE to + * indicate that we want to navigate to the submenu. + * @param _context The context - unused + * @return next view id +*/ +static uint32_t ldtoypad_navigation_submenu_callback(void* _context) { + UNUSED(_context); + return ViewSubmenu; +} + +/** + * @brief Callback for returning to configure screen. + * @details This function is called when user press back button. We return VIEW_NONE to + * indicate that we want to navigate to the configure screen. + * @param _context The context - unused + * @return next view id +*/ +static uint32_t ldtoypad_navigation_configure_callback(void* _context) { + UNUSED(_context); + return ViewConfigure; +} + +/** + * @brief Handle submenu item selection. + * @details This function is called when user selects an item from the submenu. + * @param context The context - LDToyPadApp object. + * @param index The AppSubmenuIndex item that was clicked. +*/ +static void ldtoypad_submenu_callback(void* context, uint32_t index) { + LDToyPadApp* app = (LDToyPadApp*)context; + switch(index) { + case EmulateToyPadSubmenuIndex: + view_dispatcher_switch_to_view(app->view_dispatcher, ViewGame); + break; + case SettingsSubmenuIndex: + view_dispatcher_switch_to_view(app->view_dispatcher, ViewConfigure); + break; + case AboutSubmenuIndex: + view_dispatcher_switch_to_view(app->view_dispatcher, ViewAbout); + break; + default: + break; } } -uint32_t ldtoypad_exit_confirm_view(void* context) { - UNUSED(context); - return LDToyPadView_ExitConfirm; +/** + * Our 1st sample setting is a team color. We have 3 options: red, green, and blue. +*/ +static const char* setting_1_config_label = "Team color"; +static uint8_t setting_1_values[] = {1, 2, 4}; +static char* setting_1_names[] = {"Red", "Green", "Blue"}; +static void ldtoypad_setting_1_change(VariableItem* item) { + LDToyPadApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, setting_1_names[index]); + AppGameModel* model = view_get_model(app->view_game); + model->setting_1_index = index; } -uint32_t ldtoypad_exit(void* context) { - UNUSED(context); - return VIEW_NONE; +/** + * Our 2nd sample setting is a text field. When the user clicks OK on the configuration + * setting we use a text input screen to allow the user to enter a name. This function is + * called when the user clicks OK on the text input screen. +*/ +static const char* setting_2_config_label = "Name"; +static const char* setting_2_entry_text = "Enter name"; +static const char* setting_2_default_value = "Bob"; +static void ldtoypad_setting_2_text_updated(void* context) { + LDToyPadApp* app = (LDToyPadApp*)context; + bool redraw = true; + with_view_model( + app->view_game, + AppGameModel * model, + { + furi_string_set(model->setting_2_name, app->temp_buffer); + variable_item_set_current_value_text( + app->setting_2_item, furi_string_get_cstr(model->setting_2_name)); + }, + redraw); + view_dispatcher_switch_to_view(app->view_dispatcher, ViewConfigure); +} + +/** + * @brief Callback when item in configuration screen is clicked. + * @details This function is called when user clicks OK on an item in the configuration screen. + * If the item clicked is our text field then we switch to the text input screen. + * @param context The context - LDToyPadApp object. + * @param index - The index of the item that was clicked. +*/ +static void ldtoypad_setting_item_clicked(void* context, uint32_t index) { + LDToyPadApp* app = (LDToyPadApp*)context; + index++; // The index starts at zero, but we want to start at 1. + + // Our configuration UI has the 2nd item as a text field. + if(index == 2) { + // Header to display on the text input screen. + text_input_set_header_text(app->text_input, setting_2_entry_text); + + // Copy the current name into the temporary buffer. + bool redraw = false; + with_view_model( + app->view_game, + AppGameModel * model, + { + strncpy( + app->temp_buffer, + furi_string_get_cstr(model->setting_2_name), + app->temp_buffer_size); + }, + redraw); + + // Configure the text input. When user enters text and clicks OK, ldtoypad_setting_text_updated be called. + bool clear_previous_text = false; + text_input_set_result_callback( + app->text_input, + ldtoypad_setting_2_text_updated, + app, + app->temp_buffer, + app->temp_buffer_size, + clear_previous_text); + + // Pressing the BACK button will reload the configure screen. + view_set_previous_callback( + text_input_get_view(app->text_input), ldtoypad_navigation_configure_callback); + + // Show text input dialog. + view_dispatcher_switch_to_view(app->view_dispatcher, ViewTextInput); + } +} + +/** + * @brief Callback for drawing the game screen. + * @details This function is called when the screen needs to be redrawn, like when the model gets updated. + * @param canvas The canvas to draw on. + * @param model The model - MyModel object. +*/ +static void ldtoypad_view_game_draw_callback(Canvas* canvas, void* model) { + AppGameModel* my_model = (AppGameModel*)model; + // canvas_draw_icon(canvas, my_model->x, 20, &I_glyph_1_14x40); + canvas_draw_str(canvas, 1, 10, "LEFT/RIGHT to change x"); + FuriString* xstr = furi_string_alloc(); + furi_string_printf(xstr, "x: %u OK=play tone", my_model->x); + canvas_draw_str(canvas, 44, 24, furi_string_get_cstr(xstr)); + furi_string_printf(xstr, "random: %u", (uint8_t)(furi_hal_random_get() % 256)); + canvas_draw_str(canvas, 44, 36, furi_string_get_cstr(xstr)); + furi_string_printf( + xstr, + "team: %s (%u)", + setting_1_names[my_model->setting_1_index], + setting_1_values[my_model->setting_1_index]); + canvas_draw_str(canvas, 44, 48, furi_string_get_cstr(xstr)); + furi_string_printf(xstr, "name: %s", furi_string_get_cstr(my_model->setting_2_name)); + canvas_draw_str(canvas, 44, 60, furi_string_get_cstr(xstr)); + furi_string_free(xstr); +} + +/** + * @brief Callback for timer elapsed. + * @details This function is called when the timer is elapsed. We use this to queue a redraw event. + * @param context The context - LDToyPadApp object. +*/ +static void ldtoypad_view_game_timer_callback(void* context) { + LDToyPadApp* app = (LDToyPadApp*)context; + view_dispatcher_send_custom_event(app->view_dispatcher, AppEventIdRedrawScreen); +} + +/** + * @brief Callback when the user starts the game screen. + * @details This function is called when the user enters the game screen. We start a timer to + * redraw the screen periodically (so the random number is refreshed). + * @param context The context - LDToyPadApp object. +*/ +static void ldtoypad_view_game_enter_callback(void* context) { + uint32_t period = furi_ms_to_ticks(200); + LDToyPadApp* app = (LDToyPadApp*)context; + furi_assert(app->timer == NULL); + app->timer = + furi_timer_alloc(ldtoypad_view_game_timer_callback, FuriTimerTypePeriodic, context); + furi_timer_start(app->timer, period); +} + +/** + * @brief Callback when the user exits the game screen. + * @details This function is called when the user exits the game screen. We stop the timer. + * @param context The context - LDToyPadApp object. +*/ +static void ldtoypad_view_game_exit_callback(void* context) { + LDToyPadApp* app = (LDToyPadApp*)context; + furi_timer_stop(app->timer); + furi_timer_free(app->timer); + app->timer = NULL; } -void ldtoypad_dialog_callback(DialogExResult result, void* context) { - furi_assert(context); - LDToyPadApp* app = context; - if(result == DialogExResultLeft) { - view_dispatcher_stop( - app->view_dispatcher); // Stop the view dispatcher when Result is DialogExResultLeft - } else if(result == DialogExResultRight) { - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show the last view - } else if(result == DialogExResultCenter) { - view_dispatcher_switch_to_view( - app->view_dispatcher, LDToyPadView_Submenu); // Show the submenu +/** + * @brief Callback for custom events. + * @details This function is called when a custom event is sent to the view dispatcher. + * @param event The event id - AppEventId value. + * @param context The context - LDToyPadApp object. +*/ +static bool ldtoypad_view_game_custom_event_callback(uint32_t event, void* context) { + LDToyPadApp* app = (LDToyPadApp*)context; + switch(event) { + case AppEventIdRedrawScreen: + // Redraw screen by passing true to last parameter of with_view_model. + { + bool redraw = true; + with_view_model(app->view_game, AppGameModel * _model, { UNUSED(_model); }, redraw); + return true; + } + case AppEventIdOkPressed: + // Process the OK button. We play a tone based on the x coordinate. + if(furi_hal_speaker_acquire(500)) { + float frequency; + bool redraw = false; + with_view_model( + app->view_game, + AppGameModel * model, + { frequency = model->x * 100 + 100; }, + redraw); + furi_hal_speaker_start(frequency, 1.0); + furi_delay_ms(100); + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } + return true; + default: + return false; } } -LDToyPadApp* ldtoypad_app_alloc() { - LDToyPadApp* app = malloc(sizeof(LDToyPadApp)); +/** + * @brief Callback for game screen input. + * @details This function is called when the user presses a button while on the game screen. + * @param event The event - InputEvent object. + * @param context The context - LDToyPadApp object. + * @return true if the event was handled, false otherwise. +*/ +static bool ldtoypad_view_game_input_callback(InputEvent* event, void* context) { + LDToyPadApp* app = (LDToyPadApp*)context; + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + // Left button clicked, reduce x coordinate. + bool redraw = true; + with_view_model( + app->view_game, + AppGameModel * model, + { + if(model->x > 0) { + model->x--; + } + }, + redraw); + } else if(event->key == InputKeyRight) { + // Right button clicked, increase x coordinate. + bool redraw = true; + with_view_model( + app->view_game, + AppGameModel * model, + { + // Should we have some maximum value? + model->x++; + }, + redraw); + } + } else if(event->type == InputTypePress) { + if(event->key == InputKeyOk) { + // We choose to send a custom event when user presses OK button. ldtoypad_custom_event_callback will + // handle our AppEventIdOkPressed event. We could have just put the code from + // ldtoypad_custom_event_callback here, it's a matter of preference. + view_dispatcher_send_custom_event(app->view_dispatcher, AppEventIdOkPressed); + return true; + } + } + + return false; +} - // Gui - app->gui = furi_record_open(RECORD_GUI); +/** + * @brief Allocate the ldtoypad application. Set up the views and resources. + * @details This function allocates the ldtoypad application resources. + * @return LDToyPadApp object. +*/ +static LDToyPadApp* ldtoypad_app_alloc() { + LDToyPadApp* app = (LDToyPadApp*)malloc(sizeof(LDToyPadApp)); - // Notifications - // app->notifications = furi_record_open(RECORD_NOTIFICATION); + Gui* gui = furi_record_open(RECORD_GUI); - // View dispatcher app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - // Submenu view app->submenu = submenu_alloc(); - - // Add items to the submenu - submenu_add_item( - app->submenu, - "Emulate Toy Pad", - UsbHidSubmenuIndexEmulateToyPad, - ldtoypad_submenu_callback, - app); submenu_add_item( - app->submenu, "Settings", UsbHidSubmenuIndexSettings, ldtoypad_submenu_callback, app); + app->submenu, "Emulate", EmulateToyPadSubmenuIndex, ldtoypad_submenu_callback, app); + submenu_add_item(app->submenu, "Config", SettingsSubmenuIndex, ldtoypad_submenu_callback, app); + submenu_add_item(app->submenu, "About", AboutSubmenuIndex, ldtoypad_submenu_callback, app); - view_set_previous_callback(submenu_get_view(app->submenu), ldtoypad_exit); + view_set_previous_callback(submenu_get_view(app->submenu), ldtoypad_navigation_exit_callback); + view_dispatcher_add_view(app->view_dispatcher, ViewSubmenu, submenu_get_view(app->submenu)); + view_dispatcher_switch_to_view(app->view_dispatcher, ViewSubmenu); - // Add the submenu view to the view dispatcher + app->text_input = text_input_alloc(); view_dispatcher_add_view( - app->view_dispatcher, LDToyPadView_Submenu, submenu_get_view(app->submenu)); - - // Dialog view for exit confirmation - app->dialog = dialog_ex_alloc(); - dialog_ex_set_result_callback(app->dialog, ldtoypad_dialog_callback); - dialog_ex_set_context(app->dialog, app); - dialog_ex_set_left_button_text(app->dialog, "Exit"); - dialog_ex_set_right_button_text(app->dialog, "Stay"); - dialog_ex_set_center_button_text(app->dialog, "Menu"); - dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); - view_dispatcher_add_view( - app->view_dispatcher, LDToyPadView_ExitConfirm, dialog_ex_get_view(app->dialog)); + app->view_dispatcher, ViewTextInput, text_input_get_view(app->text_input)); + app->temp_buffer_size = 32; + app->temp_buffer = (char*)malloc(app->temp_buffer_size); + + app->variable_item_list_config = variable_item_list_alloc(); + variable_item_list_reset(app->variable_item_list_config); + VariableItem* item = variable_item_list_add( + app->variable_item_list_config, + setting_1_config_label, + COUNT_OF(setting_1_values), + ldtoypad_setting_1_change, + app); + uint8_t setting_1_index = 0; + variable_item_set_current_value_index(item, setting_1_index); + variable_item_set_current_value_text(item, setting_1_names[setting_1_index]); + + FuriString* setting_2_name = furi_string_alloc(); + furi_string_set_str(setting_2_name, setting_2_default_value); + app->setting_2_item = variable_item_list_add( + app->variable_item_list_config, setting_2_config_label, 1, NULL, NULL); + variable_item_set_current_value_text( + app->setting_2_item, furi_string_get_cstr(setting_2_name)); + variable_item_list_set_enter_callback( + app->variable_item_list_config, ldtoypad_setting_item_clicked, app); - // Emulate ToyPad view - app->ldtoypad_emulate_view = ldtoypad_emulate_alloc(app->view_dispatcher); view_set_previous_callback( - ldtoypad_emulate_get_view(app->ldtoypad_emulate_view), ldtoypad_exit_confirm_view); + variable_item_list_get_view(app->variable_item_list_config), + ldtoypad_navigation_submenu_callback); view_dispatcher_add_view( app->view_dispatcher, - LDToyPadView_EmulateToyPad, - ldtoypad_emulate_get_view(app->ldtoypad_emulate_view)); + ViewConfigure, + variable_item_list_get_view(app->variable_item_list_config)); + + app->view_game = view_alloc(); + view_set_draw_callback(app->view_game, ldtoypad_view_game_draw_callback); + view_set_input_callback(app->view_game, ldtoypad_view_game_input_callback); + view_set_previous_callback(app->view_game, ldtoypad_navigation_submenu_callback); + view_set_enter_callback(app->view_game, ldtoypad_view_game_enter_callback); + view_set_exit_callback(app->view_game, ldtoypad_view_game_exit_callback); + view_set_context(app->view_game, app); + view_set_custom_callback(app->view_game, ldtoypad_view_game_custom_event_callback); + view_allocate_model(app->view_game, ViewModelTypeLockFree, sizeof(AppGameModel)); + AppGameModel* model = view_get_model(app->view_game); + model->setting_1_index = setting_1_index; + model->setting_2_name = setting_2_name; + model->x = 0; + view_dispatcher_add_view(app->view_dispatcher, ViewGame, app->view_game); + + app->widget_about = widget_alloc(); + widget_add_text_scroll_element( + app->widget_about, + 0, + 0, + 128, + 64, + "This is a WIP application.\n---\nauthor: Seger\nhttps://github.com/SegerEnd"); + view_set_previous_callback( + widget_get_view(app->widget_about), ldtoypad_navigation_submenu_callback); - app->view_id = LDToyPadView_Submenu; - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + view_dispatcher_add_view(app->view_dispatcher, ViewAbout, widget_get_view(app->widget_about)); return app; } -void ldtoypad_app_free(LDToyPadApp* app) { - furi_assert(app); - - // Notification turn LED blue - // notification_internal_message(app->notifications, &sequence_reset_blue); - - // Free more things - - // Free all the views - view_dispatcher_remove_view(app->view_dispatcher, LDToyPadView_Submenu); +/** + * @brief Free the ldtoypad application. + * @details This function frees the ldtoypad application resources. + * @param app The ldtoypad application object. +*/ +static void ldtoypad_app_free(LDToyPadApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, ViewTextInput); + text_input_free(app->text_input); + free(app->temp_buffer); + view_dispatcher_remove_view(app->view_dispatcher, ViewAbout); + widget_free(app->widget_about); + view_dispatcher_remove_view(app->view_dispatcher, ViewGame); + view_free(app->view_game); + view_dispatcher_remove_view(app->view_dispatcher, ViewConfigure); + variable_item_list_free(app->variable_item_list_config); + view_dispatcher_remove_view(app->view_dispatcher, ViewSubmenu); submenu_free(app->submenu); - - view_dispatcher_remove_view(app->view_dispatcher, LDToyPadView_ExitConfirm); - dialog_ex_free(app->dialog); - - view_dispatcher_remove_view(app->view_dispatcher, LDToyPadView_EmulateToyPad); - ldtoypad_emulate_free(app->ldtoypad_emulate_view); - // view_dispatcher_remove_view(app->view_dispatcher, LDToyPadView_Settings); - // ldtoypad_settings_free(app->ldtoypad_settings_view); - view_dispatcher_free(app->view_dispatcher); - - // // Close records furi_record_close(RECORD_GUI); - free(app->gui); - - free(app->submenu); - - free(app->dialog); - - free(app->ldtoypad_emulate_view); - - free(app->view_dispatcher); - - // app->gui = NULL; - // furi_record_close(RECORD_NOTIFICATION); - // app->notifications = NULL; - - // Free rest free(app); } -int32_t ldtoypad_app(void* p) { - UNUSED(p); - LDToyPadApp* app = ldtoypad_app_alloc(); +/** + * @brief Main function for ldtoypad application. + * @details This function is the entry point for the ldtoypad application. It should be defined in + * application.fam as the entry_point setting. + * @param _p Input parameter - unused + * @return 0 - Success +*/ +int32_t ldtoypad_app(void* _p) { + UNUSED(_p); + LDToyPadApp* app = ldtoypad_app_alloc(); view_dispatcher_run(app->view_dispatcher); ldtoypad_app_free(app); - return 0; } diff --git a/ldtoypad.h b/ldtoypad.h index a8b7ba1..5a47415 100644 --- a/ldtoypad.h +++ b/ldtoypad.h @@ -8,33 +8,43 @@ extern "C" { #include #include -#include +// #include #include // Include the used views -#include -#include -#include "views/EmulateToyPad.h" -#include "views/Settings.h" +// #include +// #include +// #include "views/EmulateToyPad.h" +// #include "views/Settings.h" typedef struct { - Gui* gui; - // NotificationApp* notifications; - ViewDispatcher* view_dispatcher; - Submenu* submenu; - DialogEx* dialog; - LDToyPadEmulateView* ldtoypad_emulate_view; - uint32_t view_id; + ViewDispatcher* view_dispatcher; // Switches between our views + // NotificationApp* notifications; // Used for controlling the backlight + Submenu* submenu; // The application menu + TextInput* text_input; // The text input screen + VariableItemList* variable_item_list_config; // The configuration screen + + // View* view_game; // The main screen + View* view_emulator; // The emulator screen + + Widget* widget_about; // The about screen + + VariableItem* setting_2_item; // The name setting item (so we can update the text) + char* temp_buffer; // Temporary buffer for text input + uint32_t temp_buffer_size; // Size of temporary buffer + + FuriTimer* timer; // Timer for redrawing the screen } LDToyPadApp; -typedef enum LDToyPadView { - LDToyPadView_Submenu, - LDToyPadView_EmulateToyPad, - LDToyPadView_Settings, - LDToyPadView_ExitConfirm, - LDToyPadView_SelectionMenu, -} LDToyPadView; +// Each view is a screen we show the user. +typedef enum { + ViewSubmenu, // The menu when the app starts + ViewTextInput, // Input for configuring text settings + ViewConfigure, // The configuration screen + ViewGame, // The main screen + ViewAbout, // The about screen with directions, link to social channel, etc. +} Views; #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/views/EmulateToyPad.c b/views/EmulateToyPad.c index 07b94a1..4c7e799 100644 --- a/views/EmulateToyPad.c +++ b/views/EmulateToyPad.c @@ -17,10 +17,12 @@ struct LDToyPadEmulateView { View* view; }; +// Previous USB mode FuriHalUsbInterface* usb_mode_prev = NULL; #define numBoxes 7 // the number of boxes (7 boxes always) +// The selected pad on the toypad uint8_t selectedBox = 0; // Variable to keep track of which toypad box is selected Submenu* selectionMenu; // The submenu to select minifigures and vehicles for each selection box diff --git a/views/Settings.c b/views/Settings.c deleted file mode 100644 index 50f0783..0000000 --- a/views/Settings.c +++ /dev/null @@ -1 +0,0 @@ -#include "Settings.h" \ No newline at end of file diff --git a/views/Settings.h b/views/Settings.h deleted file mode 100644 index e69de29..0000000 From 0d7f9cbfdf5a151a97df06e7012a5f64e1407df4 Mon Sep 17 00:00:00 2001 From: Seger End Date: Tue, 31 Dec 2024 11:53:43 +0100 Subject: [PATCH 03/27] Made emulate toypad scene --- ldtoypad.c | 26 +- ldtoypad.h | 4 +- ...{EmulateToyPad.c => EmulateToyPad_scene.c} | 249 +++++++++--------- ...{EmulateToyPad.h => EmulateToyPad_scene.h} | 12 +- 4 files changed, 152 insertions(+), 139 deletions(-) rename views/{EmulateToyPad.c => EmulateToyPad_scene.c} (61%) rename views/{EmulateToyPad.h => EmulateToyPad_scene.h} (57%) diff --git a/ldtoypad.c b/ldtoypad.c index f13deee..9f80581 100644 --- a/ldtoypad.c +++ b/ldtoypad.c @@ -15,6 +15,8 @@ #include "ldtoypad.h" +#include "views/EmulateToyPad_scene.h" + // Our application menu has 3 items. You can add more items if you want. typedef enum { EmulateToyPadSubmenuIndex, @@ -79,7 +81,7 @@ static void ldtoypad_submenu_callback(void* context, uint32_t index) { LDToyPadApp* app = (LDToyPadApp*)context; switch(index) { case EmulateToyPadSubmenuIndex: - view_dispatcher_switch_to_view(app->view_dispatcher, ViewGame); + view_dispatcher_switch_to_view(app->view_dispatcher, ViewEmulate); break; case SettingsSubmenuIndex: view_dispatcher_switch_to_view(app->view_dispatcher, ViewConfigure); @@ -396,7 +398,12 @@ static LDToyPadApp* ldtoypad_app_alloc() { model->setting_1_index = setting_1_index; model->setting_2_name = setting_2_name; model->x = 0; - view_dispatcher_add_view(app->view_dispatcher, ViewGame, app->view_game); + // view_dispatcher_add_view(app->view_dispatcher, ViewEmulate, app->view_game); + app->view_scene_emulate = ldtoypad_scene_emulate_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + ViewEmulate, + ldtoypad_scene_emulate_get_view(app->view_scene_emulate)); app->widget_about = widget_alloc(); widget_add_text_scroll_element( @@ -423,17 +430,30 @@ static void ldtoypad_app_free(LDToyPadApp* app) { view_dispatcher_remove_view(app->view_dispatcher, ViewTextInput); text_input_free(app->text_input); free(app->temp_buffer); + view_dispatcher_remove_view(app->view_dispatcher, ViewAbout); widget_free(app->widget_about); - view_dispatcher_remove_view(app->view_dispatcher, ViewGame); + + view_dispatcher_remove_view(app->view_dispatcher, ViewEmulate); view_free(app->view_game); + ldtoypad_scene_emulate_free(app->view_scene_emulate); + free(app->view_scene_emulate); + view_dispatcher_remove_view(app->view_dispatcher, ViewConfigure); variable_item_list_free(app->variable_item_list_config); + view_dispatcher_remove_view(app->view_dispatcher, ViewSubmenu); submenu_free(app->submenu); + view_dispatcher_free(app->view_dispatcher); furi_record_close(RECORD_GUI); + app->gui = NULL; + //app->notification = NULL; + + // Remove whatever is left + memzero(app, sizeof(LDToyPadApp)); + free(app); } diff --git a/ldtoypad.h b/ldtoypad.h index 5a47415..2e57def 100644 --- a/ldtoypad.h +++ b/ldtoypad.h @@ -25,7 +25,7 @@ typedef struct { VariableItemList* variable_item_list_config; // The configuration screen // View* view_game; // The main screen - View* view_emulator; // The emulator screen + LDToyPadSceneEmulate* view_scene_emulate; // The emulator screen Widget* widget_about; // The about screen @@ -41,7 +41,7 @@ typedef enum { ViewSubmenu, // The menu when the app starts ViewTextInput, // Input for configuring text settings ViewConfigure, // The configuration screen - ViewGame, // The main screen + ViewEmulate, // The main screen ViewAbout, // The about screen with directions, link to social channel, etc. } Views; diff --git a/views/EmulateToyPad.c b/views/EmulateToyPad_scene.c similarity index 61% rename from views/EmulateToyPad.c rename to views/EmulateToyPad_scene.c index 4c7e799..8524c2f 100644 --- a/views/EmulateToyPad.c +++ b/views/EmulateToyPad_scene.c @@ -1,4 +1,4 @@ -#include "EmulateToyPad.h" +#include "EmulateToyPad_scene.h" #include "../ldtoypad.h" @@ -13,19 +13,46 @@ #include // #include // Not yet needed apparently -struct LDToyPadEmulateView { +#define numBoxes 7 // the number of boxes (7 boxes always) + +// Selection box icon +uint8_t I_selectionBox[] = {0xf8, 0xff, 0x00, 0x06, 0x00, 0x01, 0x03, 0x00, 0x02, 0x03, 0x00, + 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, + 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, + 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, + 0x02, 0x07, 0x00, 0x03, 0xfe, 0xff, 0x01, 0xfc, 0xff, 0x00}; + +// Selection circle icon +uint8_t I_selectionCircle[] = {0x80, 0x7f, 0x00, 0xf0, 0xff, 0x03, 0xf8, 0xc0, 0x07, 0x3c, 0x00, + 0x0f, 0x0c, 0x00, 0x0c, 0x06, 0x00, 0x18, 0x07, 0x00, 0x38, 0x07, + 0x00, 0x38, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x07, 0x00, 0x38, + 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x3e, 0x00, 0x1f, 0xf8, 0xc0, + 0x07, 0xf0, 0xff, 0x03, 0x80, 0x7f, 0x00}; + +// Define box information (coordinates and dimensions) for each box (7 boxes total) +int boxInfo[numBoxes][2] = { + {18, 26}, // Selection 0 (box) + {50, 20}, // Selection 1 (circle) + {85, 27}, // Selection 2 (box) + {16, 40}, // Selection 3 (box) + {35, 41}, // Selection 4 (box) + {70, 41}, // Selection 5 (box) + {86, 40} // Selection 6 (box) +}; + +struct LDToyPadSceneEmulate { View* view; + // LDToyPadSceneEmulateCallback callback; + // void* context; }; // Previous USB mode FuriHalUsbInterface* usb_mode_prev = NULL; -#define numBoxes 7 // the number of boxes (7 boxes always) - // The selected pad on the toypad uint8_t selectedBox = 0; // Variable to keep track of which toypad box is selected -Submenu* selectionMenu; // The submenu to select minifigures and vehicles for each selection box +// Submenu* selectionMenu; // The submenu to select minifigures and vehicles for each selection box typedef struct { bool left_pressed; @@ -35,9 +62,11 @@ typedef struct { bool ok_pressed; bool back_pressed; bool connected; -} LDToyPadEmulateViewModel; -ViewDispatcher* ldtoypad_view_dispatcher; + // uint8_t selectedBox = 0; +} LDToyPadSceneEmulateModel; + +// ViewDispatcher* ldtoypad_view_dispatcher; Minifigure minifigures[] = { {1, "Batman"}, @@ -49,14 +78,19 @@ Minifigure minifigures[] = { {7, "Bart Simpson"}, {8, "Benny"}}; -void selectionMenu_callback(void* context, uint32_t index) { - UNUSED(context); - UNUSED(index); +// void selectionMenu_callback(void* context, uint32_t index) { +// UNUSED(context); +// UNUSED(index); - view_dispatcher_switch_to_view(ldtoypad_view_dispatcher, LDToyPadView_EmulateToyPad); -} +// view_dispatcher_switch_to_view(ldtoypad_view_dispatcher, LDToyPadView_EmulateToyPad); +// } + +bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { + furi_assert(context); + LDToyPadEmulateView* instance = context; + + // bool consumed = false; -static void ldtoypad_process(LDToyPadEmulateView* ldtoypad_emulate_view, InputEvent* event) { with_view_model( ldtoypad_emulate_view->view, LDToyPadEmulateViewModel * model, @@ -64,24 +98,22 @@ static void ldtoypad_process(LDToyPadEmulateView* ldtoypad_emulate_view, InputEv if(event->key == InputKeyOk) { if(event->type == InputTypePress) { model->ok_pressed = true; - // selectedBox++; - // if(selectedBox > numBoxes) { - // selectedBox = 0; - // } - submenu_reset(selectionMenu); - submenu_set_header(selectionMenu, "Select minifig/vehicle"); + // submenu_reset(selectionMenu); + + // submenu_set_header(selectionMenu, "Select minifig/vehicle"); + + // for(int i = 0; minifigures[i].name != NULL; i++) { + // submenu_add_item( + // selectionMenu, + // minifigures[i].name, + // minifigures[i].id, + // selectionMenu_callback, + // minifigures); + // } + // view_dispatcher_switch_to_view( + // ldtoypad_view_dispatcher, LDToyPadView_SelectionMenu); - for(int i = 0; minifigures[i].name != NULL; i++) { - submenu_add_item( - selectionMenu, - minifigures[i].name, - minifigures[i].id, - selectionMenu_callback, - minifigures); - } - view_dispatcher_switch_to_view( - ldtoypad_view_dispatcher, LDToyPadView_SelectionMenu); } else if(event->type == InputTypeRelease) { model->ok_pressed = false; } @@ -147,62 +179,27 @@ static void ldtoypad_process(LDToyPadEmulateView* ldtoypad_emulate_view, InputEv } }, true); -} -static bool ldtoypad_input_callback(InputEvent* event, void* context) { - furi_assert(context); - LDToyPadEmulateView* ldtoypad_emulate_view = context; - bool consumed = false; - - if(event->type == InputTypeLong && event->key == InputKeyBack) { - // furi_hal_hid_kb_release_all(); - } else { - ldtoypad_process(ldtoypad_emulate_view, event); - consumed = true; - } + // if(event->type == InputTypeLong && event->key == InputKeyBack) { + // // furi_hal_hid_kb_release_all(); + // } else { + // ldtoypad_process(instance, event); + // consumed = true; + // } - return consumed; + return true; } -// Create selection box icon -uint8_t I_selectionBox[] = {0xf8, 0xff, 0x00, 0x06, 0x00, 0x01, 0x03, 0x00, 0x02, 0x03, 0x00, - 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, - 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, - 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, - 0x02, 0x07, 0x00, 0x03, 0xfe, 0xff, 0x01, 0xfc, 0xff, 0x00}; -// Create selection circle icon -uint8_t I_selectionCircle[] = {0x80, 0x7f, 0x00, 0xf0, 0xff, 0x03, 0xf8, 0xc0, 0x07, 0x3c, 0x00, - 0x0f, 0x0c, 0x00, 0x0c, 0x06, 0x00, 0x18, 0x07, 0x00, 0x38, 0x07, - 0x00, 0x38, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x07, 0x00, 0x38, - 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x3e, 0x00, 0x1f, 0xf8, 0xc0, - 0x07, 0xf0, 0xff, 0x03, 0x80, 0x7f, 0x00}; - -// Define box information (coordinates and dimensions) for each box (7 boxes total) -int boxInfo[numBoxes][2] = { - {18, 26}, // Selection 0 (box) - {50, 20}, // Selection 1 (circle) - {85, 27}, // Selection 2 (box) - {16, 40}, // Selection 3 (box) - {35, 41}, // Selection 4 (box) - {70, 41}, // Selection 5 (box) - {86, 40} // Selection 6 (box) - // Add more boxes as needed -}; - -static void ldtoypad_draw_callback(Canvas* canvas, void* context) { +void ldtoypad_scene_emulate_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - LDToyPadEmulateViewModel* model = context; + LDToyPadSceneEmulateModel* model = context; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); // Draw the toypad layout on the background canvas_draw_icon(canvas, 10, 13, &I_toypad); - // Test drawing selection box icon at (x: 2, y: 2) - // canvas_draw_xbm(canvas, 2, 2, 18, 18, I_selectionBox); - - // Display selected box - // uint8_t y = boxInfo[selectedBox][1]; - // canvas_draw_xbm(canvas, x, y, 18, 18, I_selectionBox); // Draw highlighted box - // Get position for the selected box uint8_t x = boxInfo[selectedBox][0]; uint8_t y = boxInfo[selectedBox][1]; @@ -213,30 +210,26 @@ static void ldtoypad_draw_callback(Canvas* canvas, void* context) { canvas_draw_xbm(canvas, x, y, 18, 18, I_selectionBox); // Draw highlighted box } - // canvas_draw_xbm(canvas, 50, 20, 22, 17, I_selectionCircle); - canvas_set_font(canvas, FontPrimary); // Displaying the connected USB status if(model->connected) { // elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "USB Connected"); - elements_multiline_text_aligned( - canvas, 1, 1, AlignLeft, AlignTop, "Await wakeup from game"); + elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "Awaiting"); } else { elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "USB Not Connected"); - if(furi_hal_usb_get_config() == &usb_hid_ldtoypad) { - model->connected = true; - } + // if(furi_hal_usb_get_config() == &usb_hid_ldtoypad) { + // model->connected = true; + // } } // Testing pressing buttons - // if(model->ok_pressed) { - // canvas_set_color(canvas, ColorWhite); - // canvas_draw_box(canvas, 43, 28, 64, 16); - // canvas_set_color(canvas, ColorBlack); - // elements_multiline_text_aligned(canvas, 45, 30, AlignLeft, AlignTop, "OK pressed"); - // // hid_toypad_send(); - // } + if(model->ok_pressed) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 43, 28, 64, 16); + canvas_set_color(canvas, ColorBlack); + elements_multiline_text_aligned(canvas, 45, 30, AlignLeft, AlignTop, "OK pressed"); + } // Draw a box behind the text hold to exit canvas_set_color(canvas, ColorWhite); @@ -245,27 +238,13 @@ static void ldtoypad_draw_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontSecondary); elements_multiline_text_aligned(canvas, 80, 55, AlignLeft, AlignTop, "Hold to exit"); - // Testing reading the USB. - // canvas_set_color(canvas, ColorWhite); - // canvas_draw_box(canvas, 0, 0, 40, 16); - // canvas_set_color(canvas, ColorBlack); - // canvas_set_font(canvas, FontPrimary); - // int32_t value = hid_toypad_read_OUT(); - // // Convert the integer value to a string - // char valueStr[40]; // Adjust the array size as needed - // snprintf(valueStr, sizeof(valueStr), "%ld", (long)value); - // elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, valueStr); + // Debugging text for watching the USB endpoints // Now for USB info also but below the other one canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 0, 16, 120, 16); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontPrimary); - // int32_t value2 = hid_toypad_read_IN(); - // // Convert the integer value to a string - // char value2Str[40]; // Adjust the array size as needed - // snprintf(value2Str, sizeof(value2Str), "%ld", (long)value2); - // elements_multiline_text_aligned(canvas, 1, 17, AlignLeft, AlignTop, value2Str); // from get_debug_text() function display the text elements_multiline_text_aligned(canvas, 1, 17, AlignLeft, AlignTop, "ep_in: "); @@ -275,12 +254,13 @@ static void ldtoypad_draw_callback(Canvas* canvas, void* context) { canvas_draw_box(canvas, 0, 32, 120, 16); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontPrimary); + // say ep_out before the text elements_multiline_text_aligned(canvas, 1, 33, AlignLeft, AlignTop, "ep_out: "); elements_multiline_text_aligned(canvas, 40, 33, AlignLeft, AlignTop, get_debug_text_ep_out()); } -void ldtoypad_enter_callback(void* context) { +void ldtoypad_scene_emulate_enter_callback(void* context) { UNUSED(context); // furi_assert(context); @@ -289,33 +269,43 @@ void ldtoypad_enter_callback(void* context) { furi_check(furi_hal_usb_set_config(&usb_hid_ldtoypad, NULL) == true); } -uint32_t selectionMenu_prev_callback(void* context) { - UNUSED(context); - return LDToyPadView_EmulateToyPad; +void ldtoypad_scene_emulate_exit_callback(void* context) { + // UNUSED(context); + furi_assert(context); + + ldtoypad_scene_emulate_free(context); } -LDToyPadEmulateView* ldtoypad_emulate_alloc(ViewDispatcher* view_dispatcher) { - ldtoypad_view_dispatcher = view_dispatcher; - LDToyPadEmulateView* ldtoypad_emulate_view = malloc(sizeof(LDToyPadEmulateView)); - ldtoypad_emulate_view->view = view_alloc(); - view_set_context(ldtoypad_emulate_view->view, ldtoypad_emulate_view); - view_allocate_model( - ldtoypad_emulate_view->view, ViewModelTypeLocking, sizeof(LDToyPadEmulateViewModel)); - view_set_draw_callback(ldtoypad_emulate_view->view, ldtoypad_draw_callback); - view_set_input_callback(ldtoypad_emulate_view->view, ldtoypad_input_callback); - view_set_enter_callback(ldtoypad_emulate_view->view, ldtoypad_enter_callback); +// uint32_t selectionMenu_prev_callback(void* context) { +// UNUSED(context); +// return LDToyPadView_EmulateToyPad; +// } + +LDToyPadEmulateView* ldtoypad_scene_emulate_alloc() { + LDToyPadSceneEmulate* instance = malloc(sizeof(LDToyPadSceneEmulate)); + instance->view = view_alloc(); + + // ldtoypad_view_dispatcher = view_dispatcher; + + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(LDToyPadSceneEmulateModel)); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)ldtoypad_scene_emulate_draw_callback); + view_set_input_callback(instance->view, ldtoypad_scene_emulate_input_callback); + view_set_enter_callback(instance->view, ldtoypad_scene_emulate_enter_callback); + view_set_exit_callback(instance->view, ldtoypad_scene_emulate_exit_callback); // Allocate the submenu - selectionMenu = submenu_alloc(); - view_set_previous_callback(submenu_get_view(selectionMenu), selectionMenu_prev_callback); - view_dispatcher_add_view( - ldtoypad_view_dispatcher, LDToyPadView_SelectionMenu, submenu_get_view(selectionMenu)); + // selectionMenu = submenu_alloc(); + // view_set_previous_callback(submenu_get_view(selectionMenu), selectionMenu_prev_callback); + // view_dispatcher_add_view( + // ldtoypad_view_dispatcher, LDToyPadView_SelectionMenu, submenu_get_view(selectionMenu)); + // Items for the submenu as characters and vehicles - return ldtoypad_emulate_view; + return instance; } -void ldtoypad_emulate_free(LDToyPadEmulateView* ldtoypad_emulate_view) { +void ldtoypad_scene_emulate_free(LDToyPadEmulateView* ldtoypad_emulate_view) { furi_assert(ldtoypad_emulate_view); view_free(ldtoypad_emulate_view->view); view_free(submenu_get_view(selectionMenu)); @@ -324,13 +314,14 @@ void ldtoypad_emulate_free(LDToyPadEmulateView* ldtoypad_emulate_view) { if(usb_mode_prev != NULL) { furi_hal_usb_set_config(usb_mode_prev, NULL); } + free(usb_mode_prev); // submenu_free(selectionMenu); free(ldtoypad_emulate_view); } -View* ldtoypad_emulate_get_view(LDToyPadEmulateView* ldtoypad_emulate_view) { - furi_assert(ldtoypad_emulate_view); - return ldtoypad_emulate_view->view; -} \ No newline at end of file +View* ldtoypad_scene_emulate_get_view(LDToyPadSceneEmulate* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/views/EmulateToyPad.h b/views/EmulateToyPad_scene.h similarity index 57% rename from views/EmulateToyPad.h rename to views/EmulateToyPad_scene.h index b4fd8af..c81c8bf 100644 --- a/views/EmulateToyPad.h +++ b/views/EmulateToyPad_scene.h @@ -4,14 +4,16 @@ #include #include -typedef struct LDToyPadEmulateView LDToyPadEmulateView; +typedef struct LDToyPadSceneEmulate LDToyPadSceneEmulate; // LDToyPadEmulateView* usb_hid_dirpad_alloc(); // This is the original old function name from: https://github.com/huuck/FlipperZeroUSBKeyboard/blob/main/views/usb_hid_dirpad.h -LDToyPadEmulateView* ldtoypad_emulate_alloc(ViewDispatcher* view_dispatcher); +LDToyPadSceneEmulate* ldtoypad_scene_emulate_alloc(); -void ldtoypad_emulate_free(LDToyPadEmulateView* ldtoypad_emulate); +void ldtoypad_scene_emulate_free(LDToyPadSceneEmulate* ldtoypad_emulate); -View* ldtoypad_emulate_get_view(LDToyPadEmulateView* ldtoypad_emulate); +View* ldtoypad_scene_emulate_get_view(LDToyPadSceneEmulate* ldtoypad_scene_emulate); + +// View* ldtoypad_emulate_get_view(LDToyPadEmulateView* ldtoypad_emulate); // void ldtoypad_emulate_set_connected_status(LDToyPadEmulateView* ldtoypad_emulate, bool connected); @@ -20,4 +22,4 @@ typedef struct { const char* name; // const char* world; // const char* abilities; -} Minifigure; \ No newline at end of file +} Minifigure; From 1c4eda74c48ea1b2a4dc8d4ce9861cb14fa70f0b Mon Sep 17 00:00:00 2001 From: Seger End Date: Tue, 31 Dec 2024 12:56:09 +0100 Subject: [PATCH 04/27] Save --- ldtoypad.c | 198 ++++-------------------------------- ldtoypad.h | 11 ++ views/EmulateToyPad_scene.c | 18 +--- views/EmulateToyPad_scene.h | 12 +++ 4 files changed, 46 insertions(+), 193 deletions(-) diff --git a/ldtoypad.c b/ldtoypad.c index 9f80581..d7572a8 100644 --- a/ldtoypad.c +++ b/ldtoypad.c @@ -1,22 +1,8 @@ -// #include -#include -// #include -// #include -#include -#include -#include -#include -#include -// #include -// #include +#include "ldtoypad.h" /* generated by fbt from .png files in images folder */ #include -#include "ldtoypad.h" - -#include "views/EmulateToyPad_scene.h" - // Our application menu has 3 items. You can add more items if you want. typedef enum { EmulateToyPadSubmenuIndex, @@ -104,7 +90,7 @@ static void ldtoypad_setting_1_change(VariableItem* item) { LDToyPadApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, setting_1_names[index]); - AppGameModel* model = view_get_model(app->view_game); + LDToyPadSceneEmulateModel* model = view_get_model(app->view_scene_emulate->view); model->setting_1_index = index; } @@ -121,7 +107,7 @@ static void ldtoypad_setting_2_text_updated(void* context) { bool redraw = true; with_view_model( app->view_game, - AppGameModel * model, + LDToyPadSceneEmulateModel * model, { furi_string_set(model->setting_2_name, app->temp_buffer); variable_item_set_current_value_text( @@ -179,153 +165,6 @@ static void ldtoypad_setting_item_clicked(void* context, uint32_t index) { } } -/** - * @brief Callback for drawing the game screen. - * @details This function is called when the screen needs to be redrawn, like when the model gets updated. - * @param canvas The canvas to draw on. - * @param model The model - MyModel object. -*/ -static void ldtoypad_view_game_draw_callback(Canvas* canvas, void* model) { - AppGameModel* my_model = (AppGameModel*)model; - // canvas_draw_icon(canvas, my_model->x, 20, &I_glyph_1_14x40); - canvas_draw_str(canvas, 1, 10, "LEFT/RIGHT to change x"); - FuriString* xstr = furi_string_alloc(); - furi_string_printf(xstr, "x: %u OK=play tone", my_model->x); - canvas_draw_str(canvas, 44, 24, furi_string_get_cstr(xstr)); - furi_string_printf(xstr, "random: %u", (uint8_t)(furi_hal_random_get() % 256)); - canvas_draw_str(canvas, 44, 36, furi_string_get_cstr(xstr)); - furi_string_printf( - xstr, - "team: %s (%u)", - setting_1_names[my_model->setting_1_index], - setting_1_values[my_model->setting_1_index]); - canvas_draw_str(canvas, 44, 48, furi_string_get_cstr(xstr)); - furi_string_printf(xstr, "name: %s", furi_string_get_cstr(my_model->setting_2_name)); - canvas_draw_str(canvas, 44, 60, furi_string_get_cstr(xstr)); - furi_string_free(xstr); -} - -/** - * @brief Callback for timer elapsed. - * @details This function is called when the timer is elapsed. We use this to queue a redraw event. - * @param context The context - LDToyPadApp object. -*/ -static void ldtoypad_view_game_timer_callback(void* context) { - LDToyPadApp* app = (LDToyPadApp*)context; - view_dispatcher_send_custom_event(app->view_dispatcher, AppEventIdRedrawScreen); -} - -/** - * @brief Callback when the user starts the game screen. - * @details This function is called when the user enters the game screen. We start a timer to - * redraw the screen periodically (so the random number is refreshed). - * @param context The context - LDToyPadApp object. -*/ -static void ldtoypad_view_game_enter_callback(void* context) { - uint32_t period = furi_ms_to_ticks(200); - LDToyPadApp* app = (LDToyPadApp*)context; - furi_assert(app->timer == NULL); - app->timer = - furi_timer_alloc(ldtoypad_view_game_timer_callback, FuriTimerTypePeriodic, context); - furi_timer_start(app->timer, period); -} - -/** - * @brief Callback when the user exits the game screen. - * @details This function is called when the user exits the game screen. We stop the timer. - * @param context The context - LDToyPadApp object. -*/ -static void ldtoypad_view_game_exit_callback(void* context) { - LDToyPadApp* app = (LDToyPadApp*)context; - furi_timer_stop(app->timer); - furi_timer_free(app->timer); - app->timer = NULL; -} - -/** - * @brief Callback for custom events. - * @details This function is called when a custom event is sent to the view dispatcher. - * @param event The event id - AppEventId value. - * @param context The context - LDToyPadApp object. -*/ -static bool ldtoypad_view_game_custom_event_callback(uint32_t event, void* context) { - LDToyPadApp* app = (LDToyPadApp*)context; - switch(event) { - case AppEventIdRedrawScreen: - // Redraw screen by passing true to last parameter of with_view_model. - { - bool redraw = true; - with_view_model(app->view_game, AppGameModel * _model, { UNUSED(_model); }, redraw); - return true; - } - case AppEventIdOkPressed: - // Process the OK button. We play a tone based on the x coordinate. - if(furi_hal_speaker_acquire(500)) { - float frequency; - bool redraw = false; - with_view_model( - app->view_game, - AppGameModel * model, - { frequency = model->x * 100 + 100; }, - redraw); - furi_hal_speaker_start(frequency, 1.0); - furi_delay_ms(100); - furi_hal_speaker_stop(); - furi_hal_speaker_release(); - } - return true; - default: - return false; - } -} - -/** - * @brief Callback for game screen input. - * @details This function is called when the user presses a button while on the game screen. - * @param event The event - InputEvent object. - * @param context The context - LDToyPadApp object. - * @return true if the event was handled, false otherwise. -*/ -static bool ldtoypad_view_game_input_callback(InputEvent* event, void* context) { - LDToyPadApp* app = (LDToyPadApp*)context; - if(event->type == InputTypeShort) { - if(event->key == InputKeyLeft) { - // Left button clicked, reduce x coordinate. - bool redraw = true; - with_view_model( - app->view_game, - AppGameModel * model, - { - if(model->x > 0) { - model->x--; - } - }, - redraw); - } else if(event->key == InputKeyRight) { - // Right button clicked, increase x coordinate. - bool redraw = true; - with_view_model( - app->view_game, - AppGameModel * model, - { - // Should we have some maximum value? - model->x++; - }, - redraw); - } - } else if(event->type == InputTypePress) { - if(event->key == InputKeyOk) { - // We choose to send a custom event when user presses OK button. ldtoypad_custom_event_callback will - // handle our AppEventIdOkPressed event. We could have just put the code from - // ldtoypad_custom_event_callback here, it's a matter of preference. - view_dispatcher_send_custom_event(app->view_dispatcher, AppEventIdOkPressed); - return true; - } - } - - return false; -} - /** * @brief Allocate the ldtoypad application. Set up the views and resources. * @details This function allocates the ldtoypad application resources. @@ -385,21 +224,23 @@ static LDToyPadApp* ldtoypad_app_alloc() { ViewConfigure, variable_item_list_get_view(app->variable_item_list_config)); - app->view_game = view_alloc(); - view_set_draw_callback(app->view_game, ldtoypad_view_game_draw_callback); - view_set_input_callback(app->view_game, ldtoypad_view_game_input_callback); - view_set_previous_callback(app->view_game, ldtoypad_navigation_submenu_callback); - view_set_enter_callback(app->view_game, ldtoypad_view_game_enter_callback); - view_set_exit_callback(app->view_game, ldtoypad_view_game_exit_callback); - view_set_context(app->view_game, app); - view_set_custom_callback(app->view_game, ldtoypad_view_game_custom_event_callback); - view_allocate_model(app->view_game, ViewModelTypeLockFree, sizeof(AppGameModel)); - AppGameModel* model = view_get_model(app->view_game); + app->view_scene_emulate = ldtoypad_scene_emulate_alloc(); + + // This is allready happening in ldtoypad_scene_emulate_alloc + + // view_set_draw_callback(app->view_scene_emulate->view, ldtoypad_view_game_draw_callback); + // view_set_input_callback(app->view_scene_emulate->view, ldtoypad_view_game_input_callback); + // view_set_previous_callback(app->view_scene_emulate->view, ldtoypad_navigation_submenu_callback); + // view_set_context(app->view_scene_emulate->view, app); + // view_set_custom_callback(app->view_game, ldtoypad_view_game_custom_event_callback); + // view_allocate_model(app->view_game, ViewModelTypeLockFree, sizeof(AppGameModel)); + + LDToyPadSceneEmulateModel* model = view_get_model(app->view_scene_emulate->view); + model->setting_1_index = setting_1_index; model->setting_2_name = setting_2_name; model->x = 0; // view_dispatcher_add_view(app->view_dispatcher, ViewEmulate, app->view_game); - app->view_scene_emulate = ldtoypad_scene_emulate_alloc(); view_dispatcher_add_view( app->view_dispatcher, ViewEmulate, @@ -435,8 +276,9 @@ static void ldtoypad_app_free(LDToyPadApp* app) { widget_free(app->widget_about); view_dispatcher_remove_view(app->view_dispatcher, ViewEmulate); - view_free(app->view_game); + // view_free(app->view_game); ldtoypad_scene_emulate_free(app->view_scene_emulate); + view_free(app->view_scene_emulate->view); free(app->view_scene_emulate); view_dispatcher_remove_view(app->view_dispatcher, ViewConfigure); @@ -448,11 +290,11 @@ static void ldtoypad_app_free(LDToyPadApp* app) { view_dispatcher_free(app->view_dispatcher); furi_record_close(RECORD_GUI); - app->gui = NULL; + // app->gui = NULL; //app->notification = NULL; // Remove whatever is left - memzero(app, sizeof(LDToyPadApp)); + // memzero(app, sizeof(LDToyPadApp)); free(app); } diff --git a/ldtoypad.h b/ldtoypad.h index 2e57def..61600b7 100644 --- a/ldtoypad.h +++ b/ldtoypad.h @@ -5,11 +5,20 @@ extern "C" { #endif #include +#include #include #include + +#include +#include +#include + // #include +// #include #include +#include +#include // Include the used views // #include @@ -17,6 +26,8 @@ extern "C" { // #include "views/EmulateToyPad.h" // #include "views/Settings.h" +#include "views/EmulateToyPad_scene.h" + typedef struct { ViewDispatcher* view_dispatcher; // Switches between our views // NotificationApp* notifications; // Used for controlling the backlight diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index 8524c2f..e4b5bc1 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -54,18 +54,6 @@ uint8_t selectedBox = 0; // Variable to keep track of which toypad box is select // Submenu* selectionMenu; // The submenu to select minifigures and vehicles for each selection box -typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool ok_pressed; - bool back_pressed; - bool connected; - - // uint8_t selectedBox = 0; -} LDToyPadSceneEmulateModel; - // ViewDispatcher* ldtoypad_view_dispatcher; Minifigure minifigures[] = { @@ -87,13 +75,13 @@ Minifigure minifigures[] = { bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { furi_assert(context); - LDToyPadEmulateView* instance = context; + LDToyPadSceneEmulate* instance = context; // bool consumed = false; with_view_model( - ldtoypad_emulate_view->view, - LDToyPadEmulateViewModel * model, + instance->view, + LDToyPadSceneEmulateModel * model, { if(event->key == InputKeyOk) { if(event->type == InputTypePress) { diff --git a/views/EmulateToyPad_scene.h b/views/EmulateToyPad_scene.h index c81c8bf..6dd4f22 100644 --- a/views/EmulateToyPad_scene.h +++ b/views/EmulateToyPad_scene.h @@ -23,3 +23,15 @@ typedef struct { // const char* world; // const char* abilities; } Minifigure; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool back_pressed; + bool connected; + + // uint8_t selectedBox = 0; +} LDToyPadSceneEmulateModel; \ No newline at end of file From 72f5f015c3b9b0cbec23714f0ea55478b21089b1 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Tue, 31 Dec 2024 13:45:11 +0100 Subject: [PATCH 05/27] Why doesnt it draw? --- ldtoypad.c | 17 +++--- usb/usb_toypad.c | 11 ++-- views/EmulateToyPad_scene.c | 119 ++++++++++++++++++------------------ views/EmulateToyPad_scene.h | 6 +- 4 files changed, 81 insertions(+), 72 deletions(-) diff --git a/ldtoypad.c b/ldtoypad.c index d7572a8..c4e12cf 100644 --- a/ldtoypad.c +++ b/ldtoypad.c @@ -90,7 +90,8 @@ static void ldtoypad_setting_1_change(VariableItem* item) { LDToyPadApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, setting_1_names[index]); - LDToyPadSceneEmulateModel* model = view_get_model(app->view_scene_emulate->view); + LDToyPadSceneEmulateModel* model = + view_get_model(ldtoypad_scene_emulate_get_view(app->view_scene_emulate)); model->setting_1_index = index; } @@ -106,7 +107,7 @@ static void ldtoypad_setting_2_text_updated(void* context) { LDToyPadApp* app = (LDToyPadApp*)context; bool redraw = true; with_view_model( - app->view_game, + ldtoypad_scene_emulate_get_view(app->view_scene_emulate), LDToyPadSceneEmulateModel * model, { furi_string_set(model->setting_2_name, app->temp_buffer); @@ -136,8 +137,8 @@ static void ldtoypad_setting_item_clicked(void* context, uint32_t index) { // Copy the current name into the temporary buffer. bool redraw = false; with_view_model( - app->view_game, - AppGameModel * model, + ldtoypad_scene_emulate_get_view(app->view_scene_emulate), + LDToyPadSceneEmulateModel * model, { strncpy( app->temp_buffer, @@ -235,7 +236,8 @@ static LDToyPadApp* ldtoypad_app_alloc() { // view_set_custom_callback(app->view_game, ldtoypad_view_game_custom_event_callback); // view_allocate_model(app->view_game, ViewModelTypeLockFree, sizeof(AppGameModel)); - LDToyPadSceneEmulateModel* model = view_get_model(app->view_scene_emulate->view); + LDToyPadSceneEmulateModel* model = + view_get_model(ldtoypad_scene_emulate_get_view(app->view_scene_emulate)); model->setting_1_index = setting_1_index; model->setting_2_name = setting_2_name; @@ -278,7 +280,7 @@ static void ldtoypad_app_free(LDToyPadApp* app) { view_dispatcher_remove_view(app->view_dispatcher, ViewEmulate); // view_free(app->view_game); ldtoypad_scene_emulate_free(app->view_scene_emulate); - view_free(app->view_scene_emulate->view); + view_free(ldtoypad_scene_emulate_get_view(app->view_scene_emulate)); free(app->view_scene_emulate); view_dispatcher_remove_view(app->view_dispatcher, ViewConfigure); @@ -308,10 +310,11 @@ static void ldtoypad_app_free(LDToyPadApp* app) { */ int32_t ldtoypad_app(void* _p) { UNUSED(_p); - LDToyPadApp* app = ldtoypad_app_alloc(); + view_dispatcher_run(app->view_dispatcher); ldtoypad_app_free(app); + return 0; } diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index 1d9ff9f..d61c519 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -5,9 +5,9 @@ #include "usb.h" #include "usb_hid.h" -#define HID_EP_IN 0x81 +#define HID_EP_IN 0x81 #define HID_EP_OUT 0x01 -#define HID_EP_SZ 0x20 +#define HID_EP_SZ 0x20 #define HID_INTERVAL 2 @@ -186,7 +186,8 @@ static void* hid_set_string_descr(char* str) { struct usb_string_descriptor* dev_str_desc = malloc(len * 2 + 2); dev_str_desc->bLength = len * 2 + 2; dev_str_desc->bDescriptorType = USB_DTYPE_STRING; - for(size_t i = 0; i < len; i++) dev_str_desc->wString[i] = str[i]; + for(size_t i = 0; i < len; i++) + dev_str_desc->wString[i] = str[i]; return dev_str_desc; } @@ -377,7 +378,7 @@ static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) { usbd_reg_endpoint(dev, HID_EP_IN, hid_txrx_ep_callback); usbd_ep_config(dev, HID_EP_OUT, USB_EPTYPE_INTERRUPT, HID_EP_SZ); usbd_reg_endpoint(dev, HID_EP_OUT, hid_txrx_ep_callback); - usbd_ep_write(dev, HID_EP_SZ, 0, 0); + usbd_ep_write(dev, HID_EP_IN, 0, 0); // int8_t initPacket[32] = {0x55, 0x0f, 0xb0, 0x01, 0x28, 0x63, 0x29, 0x20, 0x4c, 0x45, 0x47, // 0x4f, 0x20, 0x32, 0x30, 0x31, 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -496,4 +497,4 @@ int32_t hid_toypad_read_IN() { // // } // // return 888; // return usbd_getinfo(usb_dev); -// } \ No newline at end of file +// } diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index e4b5bc1..90e9b45 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -178,29 +178,31 @@ bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { return true; } -void ldtoypad_scene_emulate_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - LDToyPadSceneEmulateModel* model = context; +static void ldtoypad_scene_emulate_draw_callback(Canvas* canvas, void* _model) { + // furi_assert(_model); + LDToyPadSceneEmulateModel* model = _model; canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); - // Draw the toypad layout on the background - canvas_draw_icon(canvas, 10, 13, &I_toypad); + canvas_draw_str(canvas, 2, 7, "Emulate ToyPad"); - // Get position for the selected box - uint8_t x = boxInfo[selectedBox][0]; - uint8_t y = boxInfo[selectedBox][1]; - // Check if the selectedBox is 1 (circle) and draw the circle, This is hardcoded for now. - if(selectedBox == 1) { - canvas_draw_xbm(canvas, x, y, 22, 17, I_selectionCircle); // Draw highlighted circle - } else { - canvas_draw_xbm(canvas, x, y, 18, 18, I_selectionBox); // Draw highlighted box - } + // // Draw the toypad layout on the background + // canvas_draw_icon(canvas, 10, 13, &I_toypad); - canvas_set_font(canvas, FontPrimary); + // // Get position for the selected box + // uint8_t x = boxInfo[selectedBox][0]; + // uint8_t y = boxInfo[selectedBox][1]; + // // Check if the selectedBox is 1 (circle) and draw the circle, This is hardcoded for now. + // if(selectedBox == 1) { + // canvas_draw_xbm(canvas, x, y, 22, 17, I_selectionCircle); // Draw highlighted circle + // } else { + // canvas_draw_xbm(canvas, x, y, 18, 18, I_selectionBox); // Draw highlighted box + // } - // Displaying the connected USB status + // canvas_set_font(canvas, FontPrimary); + + // // Displaying the connected USB status if(model->connected) { // elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "USB Connected"); elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "Awaiting"); @@ -211,50 +213,49 @@ void ldtoypad_scene_emulate_draw_callback(Canvas* canvas, void* context) { // } } - // Testing pressing buttons - if(model->ok_pressed) { - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 43, 28, 64, 16); - canvas_set_color(canvas, ColorBlack); - elements_multiline_text_aligned(canvas, 45, 30, AlignLeft, AlignTop, "OK pressed"); - } - - // Draw a box behind the text hold to exit - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 78, 53, 75, 16); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 80, 55, AlignLeft, AlignTop, "Hold to exit"); - - // Debugging text for watching the USB endpoints - - // Now for USB info also but below the other one - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 0, 16, 120, 16); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - - // from get_debug_text() function display the text - elements_multiline_text_aligned(canvas, 1, 17, AlignLeft, AlignTop, "ep_in: "); - elements_multiline_text_aligned(canvas, 40, 17, AlignLeft, AlignTop, get_debug_text_ep_in()); - - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 0, 32, 120, 16); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - - // say ep_out before the text - elements_multiline_text_aligned(canvas, 1, 33, AlignLeft, AlignTop, "ep_out: "); - elements_multiline_text_aligned(canvas, 40, 33, AlignLeft, AlignTop, get_debug_text_ep_out()); + // // Testing pressing buttons + // // if(model->ok_pressed) { + // // canvas_set_color(canvas, ColorWhite); + // // canvas_draw_box(canvas, 43, 28, 64, 16); + // // canvas_set_color(canvas, ColorBlack); + // // elements_multiline_text_aligned(canvas, 45, 30, AlignLeft, AlignTop, "OK pressed"); + // // } + + // // Draw a box behind the text hold to exit + // canvas_set_color(canvas, ColorWhite); + // canvas_draw_box(canvas, 78, 53, 75, 16); + // canvas_set_color(canvas, ColorBlack); + // canvas_set_font(canvas, FontSecondary); + // elements_multiline_text_aligned(canvas, 80, 55, AlignLeft, AlignTop, "Hold to exit"); + // canvas_set_color(canvas, ColorWhite); + // canvas_draw_box(canvas, 0, 16, 120, 16); + // canvas_set_color(canvas, ColorBlack); + // canvas_set_font(canvas, FontPrimary); + + // // from get_debug_text() function display the text + // elements_multiline_text_aligned(canvas, 1, 17, AlignLeft, AlignTop, "ep_in: "); + // elements_multiline_text_aligned(canvas, 40, 17, AlignLeft, AlignTop, get_debug_text_ep_in()); + + // canvas_set_color(canvas, ColorWhite); + // canvas_draw_box(canvas, 0, 32, 120, 16); + // canvas_set_color(canvas, ColorBlack); + // canvas_set_font(canvas, FontPrimary); + + // // say ep_out before the text + // elements_multiline_text_aligned(canvas, 1, 33, AlignLeft, AlignTop, "ep_out: "); + // elements_multiline_text_aligned(canvas, 40, 33, AlignLeft, AlignTop, get_debug_text_ep_out()); + // // Debugging text for watching the USB endpoints + + // // Now for USB info also but below the other one } void ldtoypad_scene_emulate_enter_callback(void* context) { UNUSED(context); // furi_assert(context); - usb_mode_prev = furi_hal_usb_get_config(); - furi_hal_usb_unlock(); - furi_check(furi_hal_usb_set_config(&usb_hid_ldtoypad, NULL) == true); + // usb_mode_prev = furi_hal_usb_get_config(); + // furi_hal_usb_unlock(); + // furi_check(furi_hal_usb_set_config(&usb_hid_ldtoypad, NULL) == true); } void ldtoypad_scene_emulate_exit_callback(void* context) { @@ -269,15 +270,15 @@ void ldtoypad_scene_emulate_exit_callback(void* context) { // return LDToyPadView_EmulateToyPad; // } -LDToyPadEmulateView* ldtoypad_scene_emulate_alloc() { +LDToyPadSceneEmulate* ldtoypad_scene_emulate_alloc() { LDToyPadSceneEmulate* instance = malloc(sizeof(LDToyPadSceneEmulate)); instance->view = view_alloc(); // ldtoypad_view_dispatcher = view_dispatcher; - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(LDToyPadSceneEmulateModel)); view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)ldtoypad_scene_emulate_draw_callback); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(LDToyPadSceneEmulateModel)); + view_set_draw_callback(instance->view, ldtoypad_scene_emulate_draw_callback); view_set_input_callback(instance->view, ldtoypad_scene_emulate_input_callback); view_set_enter_callback(instance->view, ldtoypad_scene_emulate_enter_callback); view_set_exit_callback(instance->view, ldtoypad_scene_emulate_exit_callback); @@ -293,10 +294,10 @@ LDToyPadEmulateView* ldtoypad_scene_emulate_alloc() { return instance; } -void ldtoypad_scene_emulate_free(LDToyPadEmulateView* ldtoypad_emulate_view) { +void ldtoypad_scene_emulate_free(LDToyPadSceneEmulate* ldtoypad_emulate_view) { furi_assert(ldtoypad_emulate_view); view_free(ldtoypad_emulate_view->view); - view_free(submenu_get_view(selectionMenu)); + // view_free(submenu_get_view(selectionMenu)); // // Change back profile if(usb_mode_prev != NULL) { diff --git a/views/EmulateToyPad_scene.h b/views/EmulateToyPad_scene.h index 6dd4f22..34a1d5d 100644 --- a/views/EmulateToyPad_scene.h +++ b/views/EmulateToyPad_scene.h @@ -34,4 +34,8 @@ typedef struct { bool connected; // uint8_t selectedBox = 0; -} LDToyPadSceneEmulateModel; \ No newline at end of file + + uint32_t setting_1_index; // The team color setting index + FuriString* setting_2_name; // The name setting + uint8_t x; // The x coordinate +} LDToyPadSceneEmulateModel; From 4d301b0889920bf835fb5db28c51c9e8625bc52a Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Tue, 31 Dec 2024 14:52:47 +0100 Subject: [PATCH 06/27] Fixed memory free crash --- ldtoypad.c | 8 +- views/EmulateToyPad_scene.c | 202 ++++++++++++++++++++++-------------- 2 files changed, 130 insertions(+), 80 deletions(-) diff --git a/ldtoypad.c b/ldtoypad.c index c4e12cf..3c943da 100644 --- a/ldtoypad.c +++ b/ldtoypad.c @@ -277,19 +277,19 @@ static void ldtoypad_app_free(LDToyPadApp* app) { view_dispatcher_remove_view(app->view_dispatcher, ViewAbout); widget_free(app->widget_about); - view_dispatcher_remove_view(app->view_dispatcher, ViewEmulate); - // view_free(app->view_game); ldtoypad_scene_emulate_free(app->view_scene_emulate); - view_free(ldtoypad_scene_emulate_get_view(app->view_scene_emulate)); + view_dispatcher_remove_view(app->view_dispatcher, ViewEmulate); + // view_free(ldtoypad_scene_emulate_get_view(app->view_scene_emulate)); // This is allready happening in ldtoypad_scene_emulate_free free(app->view_scene_emulate); view_dispatcher_remove_view(app->view_dispatcher, ViewConfigure); + variable_item_list_free(app->variable_item_list_config); view_dispatcher_remove_view(app->view_dispatcher, ViewSubmenu); submenu_free(app->submenu); - view_dispatcher_free(app->view_dispatcher); + // view_dispatcher_free(app->view_dispatcher); // this is causing a crash furi_record_close(RECORD_GUI); // app->gui = NULL; diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index 90e9b45..3794e3f 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -74,15 +74,22 @@ Minifigure minifigures[] = { // } bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { - furi_assert(context); LDToyPadSceneEmulate* instance = context; + furi_assert(instance); - // bool consumed = false; + bool consumed = false; with_view_model( instance->view, LDToyPadSceneEmulateModel * model, { + // if(event->key == InputKeyBack) { + // if(event->type == InputTypePress) { + // model->back_pressed = true; + // } else if(event->type == InputTypeRelease) { + // model->back_pressed = false; + // } + // } if(event->key == InputKeyOk) { if(event->type == InputTypePress) { model->ok_pressed = true; @@ -101,84 +108,78 @@ bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { // } // view_dispatcher_switch_to_view( // ldtoypad_view_dispatcher, LDToyPadView_SelectionMenu); + consumed = true; } else if(event->type == InputTypeRelease) { model->ok_pressed = false; } } // make user loop through boxes with InputKeyLeft, InputKeyRight, InputKeyUp, InputKeyDown - if(event->key == InputKeyLeft) { - if(event->type == InputTypePress) { - model->left_pressed = true; - if(selectedBox == 0) { - selectedBox = numBoxes; - } - selectedBox--; - } else if(event->type == InputTypeRelease) { - model->left_pressed = false; - } - } - if(event->key == InputKeyRight) { - if(event->type == InputTypePress) { - model->right_pressed = true; - selectedBox++; - if(selectedBox >= numBoxes) { - selectedBox = 0; - } - } else if(event->type == InputTypeRelease) { - model->right_pressed = false; - } - } - if(event->key == InputKeyUp) { - if(event->type == InputTypePress) { - model->up_pressed = true; - if(selectedBox == 0) { - selectedBox = 3; - } else if(selectedBox >= 4) { - selectedBox -= 4; - } else { - selectedBox = (numBoxes - 3) + selectedBox; - } - if(selectedBox >= numBoxes) { - selectedBox = 0; - } - } else if(event->type == InputTypeRelease) { - model->up_pressed = false; - } - } - - if(event->key == InputKeyDown) { - if(event->type == InputTypePress) { - model->down_pressed = true; - if(selectedBox == 2) { - selectedBox = 6; - } else if(selectedBox == 3) { - selectedBox = 0; - } else if(selectedBox == 5) { - selectedBox = 2; - } else if(selectedBox < (numBoxes - 3)) { - selectedBox += 3; - } else { - selectedBox = selectedBox - (numBoxes - 3); - } - } else if(event->type == InputTypeRelease) { - model->down_pressed = false; - } - } + // if(event->key == InputKeyLeft) { + // if(event->type == InputTypePress) { + // model->left_pressed = true; + // if(selectedBox == 0) { + // selectedBox = numBoxes; + // } + // selectedBox--; + // } else if(event->type == InputTypeRelease) { + // model->left_pressed = false; + // } + // } + // if(event->key == InputKeyRight) { + // if(event->type == InputTypePress) { + // model->right_pressed = true; + // selectedBox++; + // if(selectedBox >= numBoxes) { + // selectedBox = 0; + // } + // } else if(event->type == InputTypeRelease) { + // model->right_pressed = false; + // } + // } + // if(event->key == InputKeyUp) { + // if(event->type == InputTypePress) { + // model->up_pressed = true; + // if(selectedBox == 0) { + // selectedBox = 3; + // } else if(selectedBox >= 4) { + // selectedBox -= 4; + // } else { + // selectedBox = (numBoxes - 3) + selectedBox; + // } + // if(selectedBox >= numBoxes) { + // selectedBox = 0; + // } + // } else if(event->type == InputTypeRelease) { + // model->up_pressed = false; + // } + // } + + // if(event->key == InputKeyDown) { + // if(event->type == InputTypePress) { + // model->down_pressed = true; + // if(selectedBox == 2) { + // selectedBox = 6; + // } else if(selectedBox == 3) { + // selectedBox = 0; + // } else if(selectedBox == 5) { + // selectedBox = 2; + // } else if(selectedBox < (numBoxes - 3)) { + // selectedBox += 3; + // } else { + // selectedBox = selectedBox - (numBoxes - 3); + // } + // } else if(event->type == InputTypeRelease) { + // model->down_pressed = false; + // } + // } }, true); - // if(event->type == InputTypeLong && event->key == InputKeyBack) { - // // furi_hal_hid_kb_release_all(); - // } else { - // ldtoypad_process(instance, event); - // consumed = true; - // } - - return true; + return consumed; } -static void ldtoypad_scene_emulate_draw_callback(Canvas* canvas, void* _model) { +void ldtoypad_scene_emulate_draw_callback(Canvas* canvas, void* _model) { // furi_assert(_model); LDToyPadSceneEmulateModel* model = _model; @@ -251,18 +252,23 @@ static void ldtoypad_scene_emulate_draw_callback(Canvas* canvas, void* _model) { void ldtoypad_scene_emulate_enter_callback(void* context) { UNUSED(context); - // furi_assert(context); + furi_assert(context); - // usb_mode_prev = furi_hal_usb_get_config(); - // furi_hal_usb_unlock(); - // furi_check(furi_hal_usb_set_config(&usb_hid_ldtoypad, NULL) == true); + usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid_ldtoypad, NULL) == true); } void ldtoypad_scene_emulate_exit_callback(void* context) { // UNUSED(context); furi_assert(context); - ldtoypad_scene_emulate_free(context); + if(usb_mode_prev != NULL) { + furi_hal_usb_set_config(usb_mode_prev, NULL); + } + free(usb_mode_prev); + + // ldtoypad_scene_emulate_free(context); } // uint32_t selectionMenu_prev_callback(void* context) { @@ -270,6 +276,48 @@ void ldtoypad_scene_emulate_exit_callback(void* context) { // return LDToyPadView_EmulateToyPad; // } +static void ldtoypad_scene_emulate_draw_render_callback(Canvas* canvas, void* context) { + // UNUSED(context); + LDToyPadSceneEmulateModel* model = context; + + canvas_clear(canvas); + canvas_set_font(canvas, FontSecondary); + + // canvas_draw_str_aligned(canvas, (128 - 40), 5, AlignCenter, AlignTop, "Select here"); + canvas_draw_str(canvas, 2, 7, "Emulate ToyPad"); + + canvas_draw_icon(canvas, 10, 13, &I_toypad); + + // canvas_set_font(canvas, FontPrimary); + // elements_button_left(canvas, "Prev"); + // elements_button_center(canvas, "OK"); + // elements_button_right(canvas, "Next"); + + if(model->connected) { + // elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "USB Connected"); + elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "Awaiting"); + } else { + elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "USB Not Connected"); + // if(furi_hal_usb_get_config() == &usb_hid_ldtoypad) { + // model->connected = true; + // } + } + + // Testing pressing buttons + if(model->ok_pressed) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 43, 28, 64, 16); + canvas_set_color(canvas, ColorBlack); + elements_multiline_text_aligned(canvas, 45, 30, AlignLeft, AlignTop, "OK pressed"); + } +} + +static uint32_t ldtoypad_scene_emulate_navigation_submenu_callback(void* context) { + UNUSED(context); + + return ViewSubmenu; +} + LDToyPadSceneEmulate* ldtoypad_scene_emulate_alloc() { LDToyPadSceneEmulate* instance = malloc(sizeof(LDToyPadSceneEmulate)); instance->view = view_alloc(); @@ -277,11 +325,13 @@ LDToyPadSceneEmulate* ldtoypad_scene_emulate_alloc() { // ldtoypad_view_dispatcher = view_dispatcher; view_set_context(instance->view, instance); - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(LDToyPadSceneEmulateModel)); - view_set_draw_callback(instance->view, ldtoypad_scene_emulate_draw_callback); + view_allocate_model(instance->view, ViewModelTypeLockFree, sizeof(LDToyPadSceneEmulateModel)); + // view_set_draw_callback(instance->view, ldtoypad_scene_emulate_draw_callback); + view_set_draw_callback(instance->view, ldtoypad_scene_emulate_draw_render_callback); view_set_input_callback(instance->view, ldtoypad_scene_emulate_input_callback); view_set_enter_callback(instance->view, ldtoypad_scene_emulate_enter_callback); view_set_exit_callback(instance->view, ldtoypad_scene_emulate_exit_callback); + view_set_previous_callback(instance->view, ldtoypad_scene_emulate_navigation_submenu_callback); // Allocate the submenu // selectionMenu = submenu_alloc(); From d5a04db09e7df19b272d5c308cd087f76b237af4 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Tue, 31 Dec 2024 15:52:05 +0100 Subject: [PATCH 07/27] made usb device available --- usb/usb_toypad.c | 7 ++ usb/usb_toypad.h | 2 + views/EmulateToyPad_scene.c | 150 +++++++++++++++++++++--------------- views/EmulateToyPad_scene.h | 5 ++ 4 files changed, 101 insertions(+), 63 deletions(-) diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index d61c519..e460cfd 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -5,6 +5,8 @@ #include "usb.h" #include "usb_hid.h" +#include "../views/EmulateToyPad_scene.h" + #define HID_EP_IN 0x81 #define HID_EP_OUT 0x01 #define HID_EP_SZ 0x20 @@ -192,11 +194,16 @@ static void* hid_set_string_descr(char* str) { return dev_str_desc; } +usbd_device* get_usb_device() { + return usb_dev; +} + static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { UNUSED(intf); FuriHalUsbHidConfig* cfg = (FuriHalUsbHidConfig*)ctx; if(hid_semaphore == NULL) hid_semaphore = furi_semaphore_alloc(1, 1); usb_dev = dev; + // hid_report.keyboard.report_id = ReportIdKeyboard; // hid_report.mouse.report_id = ReportIdMouse; // hid_report.consumer.report_id = ReportIdConsumer; diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index 16f60fe..c224b7d 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -21,6 +21,8 @@ int32_t hid_toypad_read_IN(); char* get_debug_text_ep_in(); char* get_debug_text_ep_out(); +usbd_device* get_usb_device(); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index 3794e3f..60a5008 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -2,8 +2,6 @@ #include "../ldtoypad.h" -#include "../usb/usb_toypad.h" - #include #include @@ -115,64 +113,64 @@ bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { } } // make user loop through boxes with InputKeyLeft, InputKeyRight, InputKeyUp, InputKeyDown - // if(event->key == InputKeyLeft) { - // if(event->type == InputTypePress) { - // model->left_pressed = true; - // if(selectedBox == 0) { - // selectedBox = numBoxes; - // } - // selectedBox--; - // } else if(event->type == InputTypeRelease) { - // model->left_pressed = false; - // } - // } - // if(event->key == InputKeyRight) { - // if(event->type == InputTypePress) { - // model->right_pressed = true; - // selectedBox++; - // if(selectedBox >= numBoxes) { - // selectedBox = 0; - // } - // } else if(event->type == InputTypeRelease) { - // model->right_pressed = false; - // } - // } - // if(event->key == InputKeyUp) { - // if(event->type == InputTypePress) { - // model->up_pressed = true; - // if(selectedBox == 0) { - // selectedBox = 3; - // } else if(selectedBox >= 4) { - // selectedBox -= 4; - // } else { - // selectedBox = (numBoxes - 3) + selectedBox; - // } - // if(selectedBox >= numBoxes) { - // selectedBox = 0; - // } - // } else if(event->type == InputTypeRelease) { - // model->up_pressed = false; - // } - // } + if(event->key == InputKeyLeft) { + if(event->type == InputTypePress) { + model->left_pressed = true; + if(selectedBox == 0) { + selectedBox = numBoxes; + } + selectedBox--; + } else if(event->type == InputTypeRelease) { + model->left_pressed = false; + } + } + if(event->key == InputKeyRight) { + if(event->type == InputTypePress) { + model->right_pressed = true; + selectedBox++; + if(selectedBox >= numBoxes) { + selectedBox = 0; + } + } else if(event->type == InputTypeRelease) { + model->right_pressed = false; + } + } + if(event->key == InputKeyUp) { + if(event->type == InputTypePress) { + model->up_pressed = true; + if(selectedBox == 0) { + selectedBox = 3; + } else if(selectedBox >= 4) { + selectedBox -= 4; + } else { + selectedBox = (numBoxes - 3) + selectedBox; + } + if(selectedBox >= numBoxes) { + selectedBox = 0; + } + } else if(event->type == InputTypeRelease) { + model->up_pressed = false; + } + } - // if(event->key == InputKeyDown) { - // if(event->type == InputTypePress) { - // model->down_pressed = true; - // if(selectedBox == 2) { - // selectedBox = 6; - // } else if(selectedBox == 3) { - // selectedBox = 0; - // } else if(selectedBox == 5) { - // selectedBox = 2; - // } else if(selectedBox < (numBoxes - 3)) { - // selectedBox += 3; - // } else { - // selectedBox = selectedBox - (numBoxes - 3); - // } - // } else if(event->type == InputTypeRelease) { - // model->down_pressed = false; - // } - // } + if(event->key == InputKeyDown) { + if(event->type == InputTypePress) { + model->down_pressed = true; + if(selectedBox == 2) { + selectedBox = 6; + } else if(selectedBox == 3) { + selectedBox = 0; + } else if(selectedBox == 5) { + selectedBox = 2; + } else if(selectedBox < (numBoxes - 3)) { + selectedBox += 3; + } else { + selectedBox = selectedBox - (numBoxes - 3); + } + } else if(event->type == InputTypeRelease) { + model->down_pressed = false; + } + } }, true); @@ -252,11 +250,12 @@ void ldtoypad_scene_emulate_draw_callback(Canvas* canvas, void* _model) { void ldtoypad_scene_emulate_enter_callback(void* context) { UNUSED(context); - furi_assert(context); + // furi_assert(context); usb_mode_prev = furi_hal_usb_get_config(); furi_hal_usb_unlock(); furi_check(furi_hal_usb_set_config(&usb_hid_ldtoypad, NULL) == true); + // get usb device } void ldtoypad_scene_emulate_exit_callback(void* context) { @@ -280,15 +279,39 @@ static void ldtoypad_scene_emulate_draw_render_callback(Canvas* canvas, void* co // UNUSED(context); LDToyPadSceneEmulateModel* model = context; + // when the usb device is not set in modek, set it + if(model->usbDevice == NULL) { + model->usbDevice = get_usb_device(); + model->connection_status = "USB device setting..."; + } + if(model->usbDevice == NULL) { + model->connection_status = "USB not yet connected"; + } else if(!model->connected) { + model->connection_status = "Trying to connect USB"; + } + + // poll the USB status here + // usbd_poll(model->usbDevice); + canvas_clear(canvas); canvas_set_font(canvas, FontSecondary); // canvas_draw_str_aligned(canvas, (128 - 40), 5, AlignCenter, AlignTop, "Select here"); - canvas_draw_str(canvas, 2, 7, "Emulate ToyPad"); + // canvas_draw_str(canvas, 2, 7, "Emulate ToyPad"); canvas_draw_icon(canvas, 10, 13, &I_toypad); - // canvas_set_font(canvas, FontPrimary); + // Get position for the selected box + uint8_t x = boxInfo[selectedBox][0]; + uint8_t y = boxInfo[selectedBox][1]; + // Check if the selectedBox is 1 (circle) and draw the circle, This is hardcoded for now. + if(selectedBox == 1) { + canvas_draw_xbm(canvas, x, y, 22, 17, I_selectionCircle); // Draw highlighted circle + } else { + canvas_draw_xbm(canvas, x, y, 18, 18, I_selectionBox); // Draw highlighted box + } + + canvas_set_font(canvas, FontPrimary); // elements_button_left(canvas, "Prev"); // elements_button_center(canvas, "OK"); // elements_button_right(canvas, "Next"); @@ -297,7 +320,8 @@ static void ldtoypad_scene_emulate_draw_render_callback(Canvas* canvas, void* co // elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "USB Connected"); elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "Awaiting"); } else { - elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "USB Not Connected"); + elements_multiline_text_aligned( + canvas, 1, 1, AlignLeft, AlignTop, model->connection_status); // if(furi_hal_usb_get_config() == &usb_hid_ldtoypad) { // model->connected = true; // } diff --git a/views/EmulateToyPad_scene.h b/views/EmulateToyPad_scene.h index 34a1d5d..8907b29 100644 --- a/views/EmulateToyPad_scene.h +++ b/views/EmulateToyPad_scene.h @@ -4,6 +4,8 @@ #include #include +#include "../usb/usb_toypad.h" + typedef struct LDToyPadSceneEmulate LDToyPadSceneEmulate; // LDToyPadEmulateView* usb_hid_dirpad_alloc(); // This is the original old function name from: https://github.com/huuck/FlipperZeroUSBKeyboard/blob/main/views/usb_hid_dirpad.h @@ -32,9 +34,12 @@ typedef struct { bool ok_pressed; bool back_pressed; bool connected; + char* connection_status; // uint8_t selectedBox = 0; + usbd_device* usbDevice; + uint32_t setting_1_index; // The team color setting index FuriString* setting_2_name; // The name setting uint8_t x; // The x coordinate From 1bef15cd997c6e9c7645e472909dd00c3ce13a46 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Tue, 31 Dec 2024 16:39:06 +0100 Subject: [PATCH 08/27] Got a response LEGO 2014 --- usb/usb_toypad.c | 162 ++++++++++++++++-------------------- usb/usb_toypad.h | 2 + views/EmulateToyPad_scene.c | 21 +++++ 3 files changed, 94 insertions(+), 91 deletions(-) diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index e460cfd..3010afc 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -7,6 +7,12 @@ #include "../views/EmulateToyPad_scene.h" +#define CMD_WAKE 0xB0 +#define CMD_READ 0xD2 +#define CMD_MODEL 0xD4 +#define CMD_SEED 0xB1 +#define CMD_CHAL 0xB3 + #define HID_EP_IN 0x81 #define HID_EP_OUT 0x01 #define HID_EP_SZ 0x20 @@ -276,46 +282,12 @@ static void hid_on_suspend(usbd_device* dev) { } // create a string variablethat contains the text: nothing to debug yet -char debug_text_ep_in[] = "nothing to debug yet"; - -char debug_text_ep_out[] = "nothing to debug yet"; - -static void hid_tx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { - // UNUSED(dev); - // UNUSED(ep); - // if(event == usbd_evt_eptx) { - // furi_semaphore_release(hid_semaphore); - // } - UNUSED(dev); - UNUSED(event); - UNUSED(ep); - - uint16_t len = 32; - uint8_t data[len]; - usbd_ep_read(dev, ep, data, len); - - // check if endpoint is HID_EP_IN or HID_EP_OUT - if(ep == HID_EP_IN) { - // snprintf(debug_text_ep_in, sizeof(debug_text_ep_in), "tx ep: %ld", (long)event); - snprintf(debug_text_ep_in, sizeof(debug_text_ep_in), "tx ep: %ld", (long)data); - } else if(ep == HID_EP_OUT) { - // snprintf(debug_text_ep_out, sizeof(debug_text_ep_out), "tx ep: %ld", (long)event); - snprintf(debug_text_ep_out, sizeof(debug_text_ep_out), "tx ep: %ld", (long)data); - } - - // snprintf(debug_text_ep_out, sizeof(debug_text_ep_out), "tx: %ld", (long)event); - // snprintf(debug_text_ep_out, sizeof(debug_text_ep_out), "tx endpoint: %ld", (long)ep); +char debug_text_ep_in[HID_EP_SZ] = "nothing"; - // furi_semaphore_release(hid_semaphore); +// char debug_text_ep_out[] = "nothing to debug yet"; +char debug_text_ep_out[HID_EP_SZ] = "nothing"; - // else if(boot_protocol == true) { - // usbd_ep_read(usb_dev, ep, &led_state, sizeof(led_state)); - // } else { - // struct HidReportLED leds; - // usbd_ep_read(usb_dev, ep, &leds, sizeof(leds)); - // led_state = leds.led_state; - // } -} +char debug_text[HID_EP_SZ] = " "; // a function that returns a pointer to the string char* get_debug_text_ep_in() { @@ -324,48 +296,56 @@ char* get_debug_text_ep_in() { char* get_debug_text_ep_out() { return debug_text_ep_out; } +char* get_debug_text() { + return debug_text; +} -static void hid_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { - UNUSED(dev); - UNUSED(event); +void hid_in_callback(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(ep); + UNUSED(event); + // Handle the IN endpoint - uint16_t len = 32; - uint8_t data[len]; - usbd_ep_read(dev, ep, data, len); - - // save data to debug_text append 'data: ' to the front of the string - // snprintf(debug_text_ep_in, sizeof(debug_text_ep_in), "rx data: %ld", (long)data); - - if(ep == HID_EP_IN) { - snprintf(debug_text_ep_in, sizeof(debug_text_ep_in), "rx ep: %ld", (long)data); - } else if(ep == HID_EP_OUT) { - snprintf(debug_text_ep_out, sizeof(debug_text_ep_out), "rx ep: %ld", (long)data); - } - int8_t initPacket[32] = {0x55, 0x0f, 0xb0, 0x01, 0x28, 0x63, 0x29, 0x20, 0x4c, 0x45, 0x47, - 0x4f, 0x20, 0x32, 0x30, 0x31, 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - usbd_ep_write(dev, HID_EP_OUT, initPacket, sizeof(initPacket)); - - if(callback != NULL) { - // callback(HidRequest, cb_ctx); - } + int len = usbd_ep_read(dev, HID_EP_IN, debug_text_ep_in, HID_EP_SZ); + if(len <= 0) return; } -static void hid_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { - // set debug_text_ep_in to the text "hello world" - // strcpy(debug_text_ep_in, "test 2 hello world"); - - // set the debug_text_ep_in to event variable value - // snprintf(debug_text_ep_in, sizeof(debug_text_ep_in), "txrx ev: %ld", (long)event); +void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(ep); + UNUSED(event); - if(event == usbd_evt_eptx) { - hid_tx_ep_callback(dev, event, ep); - } else if(event == usbd_evt_eprx) { - hid_rx_ep_callback(dev, event, ep); - } else { - hid_rx_ep_callback(dev, event, ep); + uint8_t req[HID_EP_SZ] = {0}; + // uint8_t res[HID_EP_SZ] = {0}; + // size_t res_size = 0; + + // Read data from the OUT endpoint + int len = usbd_ep_read(dev, HID_EP_OUT, req, HID_EP_SZ); + + // Make from the data a string and save it to the debug_text_ep_in string + sprintf(debug_text_ep_out, "%s", req); + + if(len <= 0) return; + + uint8_t cmd = req[0]; // The first byte is the command ID + switch(cmd) { + case CMD_WAKE: + // handle_cmd_wake(req + 1, res, &res_size); + sprintf(debug_text, "CMD_WAKE"); + break; + case CMD_READ: + // handle_cmd_read(req + 1, res, &res_size); + sprintf(debug_text, "CMD_READ"); + break; + case CMD_MODEL: + // handle_cmd_model(req + 1, res, &res_size); + sprintf(debug_text, "CMD_READ"); + break; + default: + sprintf(debug_text, "Unknown command"); + return; } + + // Write the response to the IN endpoint + // usbd_ep_write(dev, HID_EP_IN, res, res_size); } /* Configure endpoints */ @@ -382,10 +362,10 @@ static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) { case 1: /* configuring device */ usbd_ep_config(dev, HID_EP_IN, USB_EPTYPE_INTERRUPT, HID_EP_SZ); - usbd_reg_endpoint(dev, HID_EP_IN, hid_txrx_ep_callback); + usbd_reg_endpoint(dev, HID_EP_IN, hid_in_callback); usbd_ep_config(dev, HID_EP_OUT, USB_EPTYPE_INTERRUPT, HID_EP_SZ); - usbd_reg_endpoint(dev, HID_EP_OUT, hid_txrx_ep_callback); - usbd_ep_write(dev, HID_EP_IN, 0, 0); + usbd_reg_endpoint(dev, HID_EP_OUT, hid_out_callback); + // usbd_ep_write(dev, HID_EP_IN, 0, 0); // int8_t initPacket[32] = {0x55, 0x0f, 0xb0, 0x01, 0x28, 0x63, 0x29, 0x20, 0x4c, 0x45, 0x47, // 0x4f, 0x20, 0x32, 0x30, 0x31, 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -480,23 +460,23 @@ int32_t hid_toypad_read_IN() { // return 888; } -// int32_t hid_toypad_read_OUT() { -// uint16_t len = 32; -// uint8_t data[len]; // declare data as an array of the appropriate size -// // Check if the device is connected then read the packet -// if(hid_connected) { -// int32_t result = -// usbd_ep_read(usb_dev, HID_EP_OUT, data, len); // pass data as the buffer to read into +int32_t hid_toypad_read_OUT() { + uint16_t len = 32; + uint8_t data[len]; // declare data as an array of the appropriate size + // Check if the device is connected then read the packet + // if(hid_connected) { + int32_t result = + usbd_ep_read(usb_dev, HID_EP_OUT, data, len); // pass data as the buffer to read into -// // int8_t initPacket[32] = {0x55, 0x0f, 0xb0, 0x01, 0x28, 0x63, 0x29, 0x20, 0x4c, 0x45, 0x47, -// // 0x4f, 0x20, 0x32, 0x30, 0x31, 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, -// // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -// // usbd_ep_write(usb_dev, HID_EP_OUT, initPacket, sizeof(initPacket)); + // int8_t initPacket[32] = {0x55, 0x0f, 0xb0, 0x01, 0x28, 0x63, 0x29, 0x20, 0x4c, 0x45, 0x47, + // 0x4f, 0x20, 0x32, 0x30, 0x31, 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + // usbd_ep_write(usb_dev, HID_EP_OUT, initPacket, sizeof(initPacket)); -// return result; -// } -// return 999; -// } + return result; + // } + // return 999; +} // uint32_t hid_ldtoypad_usbinfo() { // // if(hid_connected) { diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index c224b7d..0353463 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -21,6 +21,8 @@ int32_t hid_toypad_read_IN(); char* get_debug_text_ep_in(); char* get_debug_text_ep_out(); +char* get_debug_text(); + usbd_device* get_usb_device(); #ifdef __cplusplus diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index 60a5008..f5fd144 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -334,6 +334,27 @@ static void ldtoypad_scene_emulate_draw_render_callback(Canvas* canvas, void* co canvas_set_color(canvas, ColorBlack); elements_multiline_text_aligned(canvas, 45, 30, AlignLeft, AlignTop, "OK pressed"); } + + // canvas_set_color(canvas, ColorWhite); + // canvas_draw_box(canvas, 0, 16, 120, 16); + // canvas_set_color(canvas, ColorBlack); + + // elements_multiline_text_aligned(canvas, 1, 17, AlignLeft, AlignTop, "ep_in: "); + // elements_multiline_text_aligned(canvas, 40, 17, AlignLeft, AlignTop, get_debug_text_ep_in()); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, 16, 120, 16); + canvas_set_color(canvas, ColorBlack); + + elements_multiline_text_aligned(canvas, 1, 17, AlignLeft, AlignTop, "Debug: "); + elements_multiline_text_aligned(canvas, 40, 17, AlignLeft, AlignTop, get_debug_text()); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, 32, 120, 16); + canvas_set_color(canvas, ColorBlack); + + elements_multiline_text_aligned(canvas, 1, 33, AlignLeft, AlignTop, "ep_out: "); + elements_multiline_text_aligned(canvas, 40, 33, AlignLeft, AlignTop, get_debug_text_ep_out()); } static uint32_t ldtoypad_scene_emulate_navigation_submenu_callback(void* context) { From 438f53dd347dbf47c806492a572821f1e75a837b Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Tue, 31 Dec 2024 17:15:15 +0100 Subject: [PATCH 09/27] Try to get the correct CMD --- usb/usb_toypad.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index 3010afc..60c4526 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -323,9 +323,12 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { // Make from the data a string and save it to the debug_text_ep_in string sprintf(debug_text_ep_out, "%s", req); + uint8_t cmd = req[0]; // Command ID is the first byte + // uint8_t cid = req[1]; + // uint8_t* payload = &req[2]; // Payload starts from the third byte + if(len <= 0) return; - uint8_t cmd = req[0]; // The first byte is the command ID switch(cmd) { case CMD_WAKE: // handle_cmd_wake(req + 1, res, &res_size); @@ -340,7 +343,7 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { sprintf(debug_text, "CMD_READ"); break; default: - sprintf(debug_text, "Unknown command"); + snprintf(debug_text, HID_EP_SZ, "U: %02X", cmd); return; } From 97dfee8bd6b09d2a3c800a814c2e28dd01e27f1d Mon Sep 17 00:00:00 2001 From: Seger End <76704606+SegerEnd@users.noreply.github.com> Date: Tue, 31 Dec 2024 19:05:19 +0100 Subject: [PATCH 10/27] Adres frame and request typedef to header --- usb/usb_toypad.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index 0353463..beefe81 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -14,6 +14,20 @@ extern FuriHalUsbInterface usb_hid_ldtoypad; // HidRequest, // } HidEvent; +typedef struct { + unsigned char type; + unsigned char len; + unsigned char payload[HID_EP_SZ]; + unsigned char chksum; +} Frame; + +typedef struct { + Frame frame; + unsigned char cmd; + unsigned char cid; + unsigned char payload[HID_EP_SZ - 2]; // Assuming the payload is smaller than HID_EP_SZ +} Request; + int32_t hid_toypad_read_IN(); // int32_t hid_toypad_read_OUT(); // uint32_t hid_ldtoypad_usbinfo(); @@ -27,4 +41,4 @@ usbd_device* get_usb_device(); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif From ec6cc22796afc8589fe9fc50e15fed68e0b24e68 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Wed, 1 Jan 2025 13:28:30 +0100 Subject: [PATCH 11/27] Getting the cmd --- usb/usb_toypad.c | 150 ++++++++++++++++++++++++++--------------------- usb/usb_toypad.h | 30 +++++++--- 2 files changed, 106 insertions(+), 74 deletions(-) diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index 60c4526..fc7e64e 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -15,7 +15,6 @@ #define HID_EP_IN 0x81 #define HID_EP_OUT 0x01 -#define HID_EP_SZ 0x20 #define HID_INTERVAL 2 @@ -25,6 +24,50 @@ #define USB_EP0_SIZE 64 PLACE_IN_SECTION("MB_MEM2") static uint32_t ubuf[0x20]; +// Function to parse a Frame from a buffer +void parse_frame(Frame* frame, unsigned char* buf, int len) { + UNUSED(len); + frame->type = buf[0]; + frame->len = buf[1]; + memcpy(frame->payload, buf + 2, frame->len); + frame->chksum = buf[frame->len + 2]; +} + +// Function to calculate checksum +unsigned char calculate_checksum(unsigned char* buf, int len) { + unsigned char sum = 0; + for(int i = 0; i < len; i++) { + sum += buf[i]; + } + return sum % 256; +} + +// Function to build a Frame into a buffer +int build_frame(Frame* frame, unsigned char* buf) { + buf[0] = frame->type; + buf[1] = frame->len; + memcpy(buf + 2, frame->payload, frame->len); + buf[frame->len + 2] = calculate_checksum(buf, frame->len + 2); + return frame->len + 3; +} + +// Function to parse a Response from a Frame +void parse_response(Response* response, Frame* frame) { + response->frame = *frame; + response->cid = frame->payload[0]; + response->payload_len = frame->len - 1; + memcpy(response->payload, frame->payload + 1, response->payload_len); +} + +// Function to build a Response into a Frame +int build_response(Response* response, unsigned char* buf) { + response->frame.type = 0x55; + response->frame.len = response->payload_len + 1; + response->frame.payload[0] = response->cid; + memcpy(response->frame.payload + 1, response->payload, response->payload_len); + return build_frame(&response->frame, buf); +} + /* String descriptors */ enum UsbDevDescStr { UsbDevLang = 0, @@ -313,26 +356,43 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(ep); UNUSED(event); - uint8_t req[HID_EP_SZ] = {0}; - // uint8_t res[HID_EP_SZ] = {0}; - // size_t res_size = 0; + unsigned char req_buf[HID_EP_SZ] = {0}; // Read data from the OUT endpoint - int len = usbd_ep_read(dev, HID_EP_OUT, req, HID_EP_SZ); + int32_t len = usbd_ep_read(dev, HID_EP_OUT, req_buf, HID_EP_SZ); // Make from the data a string and save it to the debug_text_ep_in string - sprintf(debug_text_ep_out, "%s", req); + sprintf(debug_text_ep_out, "%s", req_buf); + + if(len <= 0) return; - uint8_t cmd = req[0]; // Command ID is the first byte + // uint8_t cmd = req_buf[2]; // Command ID // uint8_t cid = req[1]; - // uint8_t* payload = &req[2]; // Payload starts from the third byte + // uint8_t* payload = &req[3]; // Payload starts from the third byte - if(len <= 0) return; + Frame frame; + parse_frame(&frame, req_buf, len); + + Request request; + request.cmd = frame.payload[0]; + request.cid = frame.payload[1]; + request.payload_len = frame.len - 2; + memcpy(request.payload, frame.payload + 2, request.payload_len); - switch(cmd) { + Response response; + response.cid = request.cid; + response.payload_len = 0; + + switch(request.cmd) { case CMD_WAKE: // handle_cmd_wake(req + 1, res, &res_size); sprintf(debug_text, "CMD_WAKE"); + + // int8_t wake_payload[HID_EP_SZ] = {0x55, 0x0f, 0xb0, 0x01, 0x28, 0x63, 0x29, 0x20, + // 0x4c, 0x45, 0x47, 0x4f, 0x20, 0x32, 0x30, 0x31, + // 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + break; case CMD_READ: // handle_cmd_read(req + 1, res, &res_size); @@ -343,12 +403,22 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { sprintf(debug_text, "CMD_READ"); break; default: - snprintf(debug_text, HID_EP_SZ, "U: %02X", cmd); + snprintf(debug_text, HID_EP_SZ, "U: %02X", request.cmd); return; } - // Write the response to the IN endpoint - // usbd_ep_write(dev, HID_EP_IN, res, res_size); + // Make the response + unsigned char res_buf[HID_EP_SZ]; + + // check if the response is empty + if(response.payload_len == 0) return; + + int res_len = build_response(&response, res_buf); + + if(res_len <= 0) return; + + // Send the response + usbd_ep_write(dev, HID_EP_IN, res_buf, res_len); } /* Configure endpoints */ @@ -432,59 +502,7 @@ static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_cal return usbd_fail; } -// Function only to test right now this is not in the final code -// int32_t hid_toypad_send() { -// // Check if the device is connected then write the packet -// if(hid_connected) { -// // int32_t result = usbd_ep_write(usb_dev, HID_EP_IN, &data, sizeof(data)); // int8_t data[32] = {0x55, 0x0f, 0xb0, 0x01, 0x28, 0x63, 0x29, 0x20, 0x4c, 0x45, 0x47, // 0x4f, 0x20, 0x32, 0x30, 0x31, 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -// int32_t result = usbd_ep_write(usb_dev, HID_EP_IN, data, sizeof(data)); -// return result; -// } -// return -1; -// } - -int32_t hid_toypad_read_IN() { - uint16_t len = 32; - uint8_t data[len]; // declare data as an array of the appropriate size - // Check if the device is connected then read the packet - // if(hid_connected) { - int32_t result = usbd_ep_read(usb_dev, HID_EP_IN, data, len); - - // int8_t initPacket[32] = {0x55, 0x0f, 0xb0, 0x01, 0x28, 0x63, 0x29, 0x20, 0x4c, 0x45, 0x47, - // 0x4f, 0x20, 0x32, 0x30, 0x31, 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // usbd_ep_write(usb_dev, HID_EP_OUT, initPacket, sizeof(initPacket)); - - return result; - // } - // return 888; -} - -int32_t hid_toypad_read_OUT() { - uint16_t len = 32; - uint8_t data[len]; // declare data as an array of the appropriate size - // Check if the device is connected then read the packet - // if(hid_connected) { - int32_t result = - usbd_ep_read(usb_dev, HID_EP_OUT, data, len); // pass data as the buffer to read into - - // int8_t initPacket[32] = {0x55, 0x0f, 0xb0, 0x01, 0x28, 0x63, 0x29, 0x20, 0x4c, 0x45, 0x47, - // 0x4f, 0x20, 0x32, 0x30, 0x31, 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // usbd_ep_write(usb_dev, HID_EP_OUT, initPacket, sizeof(initPacket)); - - return result; - // } - // return 999; -} - -// uint32_t hid_ldtoypad_usbinfo() { -// // if(hid_connected) { -// // return usbd_getinfo(usb_dev); -// // } -// // return 888; -// return usbd_getinfo(usb_dev); -// } +// int32_t length = usbd_ep_write(usb_dev, HID_EP_IN, data, sizeof(data)); diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index beefe81..069455f 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -6,13 +6,7 @@ extern "C" { #endif -extern FuriHalUsbInterface usb_hid_ldtoypad; - -// typedef enum { -// HidDisconnected, -// HidConnected, -// HidRequest, -// } HidEvent; +#define HID_EP_SZ 0x20 typedef struct { unsigned char type; @@ -21,13 +15,33 @@ typedef struct { unsigned char chksum; } Frame; +// Define a Response structure +typedef struct { + Frame frame; + unsigned char cid; + unsigned char payload[HID_EP_SZ]; + int payload_len; + // int _cancel; + // int _preventDefault; +} Response; + +// Define a Request structure typedef struct { Frame frame; unsigned char cmd; unsigned char cid; - unsigned char payload[HID_EP_SZ - 2]; // Assuming the payload is smaller than HID_EP_SZ + unsigned char payload[HID_EP_SZ - 2]; + int payload_len; } Request; +extern FuriHalUsbInterface usb_hid_ldtoypad; + +// typedef enum { +// HidDisconnected, +// HidConnected, +// HidRequest, +// } HidEvent; + int32_t hid_toypad_read_IN(); // int32_t hid_toypad_read_OUT(); // uint32_t hid_ldtoypad_usbinfo(); From c3efff59b04165c7449d91834885618571950293 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Wed, 1 Jan 2025 16:12:44 +0100 Subject: [PATCH 12/27] Changes --- usb/usb_toypad.c | 183 ++++++++++++++++++++++++++++++++++++++++++++--- usb/usb_toypad.h | 24 +++++++ 2 files changed, 197 insertions(+), 10 deletions(-) diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index fc7e64e..4731536 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -7,11 +7,25 @@ #include "../views/EmulateToyPad_scene.h" -#define CMD_WAKE 0xB0 -#define CMD_READ 0xD2 -#define CMD_MODEL 0xD4 -#define CMD_SEED 0xB1 -#define CMD_CHAL 0xB3 +// Define all the possible commands +#define CMD_WAKE 0xB0 +#define CMD_READ 0xD2 +#define CMD_MODEL 0xD4 +#define CMD_SEED 0xB1 +#define CMD_CHAL 0xB3 +#define CMD_COL 0xC0 +#define CMD_GETCOL 0xC1 +#define CMD_FADE 0xC2 +#define CMD_FLASH 0xC3 +#define CMD_FADRD 0xC4 +#define CMD_FADAL 0xC6 +#define CMD_FLSAL 0xC7 +#define CMD_COLAL 0xC8 +#define CMD_TGLST 0xD0 +#define CMD_WRITE 0xD3 +#define CMD_PWD 0xE1 +#define CMD_ACTIVE 0xE5 +#define CMD_LEDSQ 0xFF #define HID_EP_IN 0x81 #define HID_EP_OUT 0x01 @@ -247,6 +261,88 @@ usbd_device* get_usb_device() { return usb_dev; } +ToyPadEmu* emulator; +ToyPadEmu* get_emulator() { + return emulator; +} +void set_emulator(ToyPadEmu* emu) { + if(emu == NULL) return; + + emulator = emu; +} + +// Generate random UID +void ToyPadEmu_randomUID(char* uid) { + srand(furi_get_tick()); // Set the seed to random value + uid[0] = 0x04; // vendor id = NXP + uid[6] = 0x80; // Set last byte to 0x80 + for(int i = 1; i < 6; i++) { + uid[i] = rand() % 256; + } + uid[7] = '\0'; // null terminate +} + +void ToyPadEmu_init(ToyPadEmu* emu) { + emu->token_count = 0; + + // Set default TEA key + uint8_t default_tea_key[16] = { + 0x55, + 0xFE, + 0xF6, + 0xB0, + 0x62, + 0xBF, + 0x0B, + 0x41, + 0xC9, + 0xB3, + 0x7C, + 0xB4, + 0x97, + 0x3E, + 0x29, + 0x7B}; + memcpy(emu->tea_key, default_tea_key, 16); +} + +// Add a token to a pad +bool ToyPadEmu_place( + ToyPadEmu* emu, + const uint8_t* token_data, + int pad, + int index, + const char* uid) { + if(emu->token_count > 7) { + return false; + } + + Token new_token; + new_token.index = index; + new_token.pad = pad; + strncpy(new_token.uid, uid, sizeof(new_token.uid)); + memcpy(new_token.token, token_data, sizeof(new_token.token)); + + emu->tokens[emu->token_count++] = new_token; + + return true; +} + +// Remove a token +bool ToyPadEmu_remove(ToyPadEmu* emu, int index) { + for(int i = 0; i < emu->token_count; i++) { + if(emu->tokens[i].index == index) { + // Shift tokens + for(int j = i; j < emu->token_count - 1; j++) { + emu->tokens[j] = emu->tokens[j + 1]; + } + emu->token_count--; + return true; + } + } + return false; +} + static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { UNUSED(intf); FuriHalUsbHidConfig* cfg = (FuriHalUsbHidConfig*)ctx; @@ -392,6 +488,22 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { // 0x4c, 0x45, 0x47, 0x4f, 0x20, 0x32, 0x30, 0x31, // 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + // unsigned char wake_payload[HID_EP_SZ] = {0x55, 0x0f, 0xb0, 0x01, 0x28, 0x63, 0x29, 0x20, + // 0x4c, 0x45, 0x47, 0x4f, 0x20, 0x32, 0x30, 0x31, + // 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + unsigned char wake_payload[HID_EP_SZ] = {0x55, 0x19, 0x01, 0x00, 0x2f, 0x02, 0x01, 0x02, + 0x02, 0x04, 0x02, 0xf5, 0x00, 0x19, 0x8b, 0x54, + 0x4d, 0xb4, 0xcd, 0xae, 0x45, 0x24, 0x80, 0x0e, + 0x00, 0xf0, 0x25, 0x20, 0x00, 0x00, 0x00, 0x00}; + + // furi_delay_ms(50); + + usbd_ep_write(dev, HID_EP_IN, wake_payload, sizeof(wake_payload)); + + // response.payload_len = sizeof(wake_payload); + // memcpy(response.payload, wake_payload, response.payload_len); break; case CMD_READ: @@ -400,22 +512,73 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { break; case CMD_MODEL: // handle_cmd_model(req + 1, res, &res_size); - sprintf(debug_text, "CMD_READ"); + sprintf(debug_text, "CMD_MODEL"); + break; + case CMD_SEED: + sprintf(debug_text, "CMD_SEED"); + break; + case CMD_WRITE: + sprintf(debug_text, "CMD_WRITE"); + break; + case CMD_CHAL: + sprintf(debug_text, "CMD_CHAL"); + break; + case CMD_COL: + sprintf(debug_text, "CMD_COL"); + break; + case CMD_GETCOL: + sprintf(debug_text, "CMD_GETCOL"); + break; + case CMD_FADE: + sprintf(debug_text, "CMD_FADE"); + break; + case CMD_FLASH: + sprintf(debug_text, "CMD_FLASH"); + break; + case CMD_FADRD: + sprintf(debug_text, "CMD_FADRD"); + break; + case CMD_FADAL: + sprintf(debug_text, "CMD_FADAL"); + break; + case CMD_FLSAL: + sprintf(debug_text, "CMD_FLSAL"); + break; + case CMD_COLAL: + sprintf(debug_text, "CMD_COLAL"); + break; + case CMD_TGLST: + sprintf(debug_text, "CMD_TGLST"); + break; + case CMD_PWD: + sprintf(debug_text, "CMD_PWD"); + break; + case CMD_ACTIVE: + sprintf(debug_text, "CMD_ACTIVE"); + break; + case CMD_LEDSQ: + sprintf(debug_text, "CMD_LEDSQ"); break; default: snprintf(debug_text, HID_EP_SZ, "U: %02X", request.cmd); + snprintf(debug_text, HID_EP_SZ, "ERR: %02X", request.cmd); + return; + } + + // check if the response is empty + if(response.payload_len == 0) { + // sprintf(debug_text, "Empty payload_len"); return; } // Make the response unsigned char res_buf[HID_EP_SZ]; - // check if the response is empty - if(response.payload_len == 0) return; - int res_len = build_response(&response, res_buf); - if(res_len <= 0) return; + if(res_len <= 0) { + return; + } // Send the response usbd_ep_write(dev, HID_EP_IN, res_buf, res_len); diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index 069455f..5b6232a 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #ifdef __cplusplus extern "C" { @@ -8,6 +9,20 @@ extern "C" { #define HID_EP_SZ 0x20 +typedef struct { + int index; + int pad; + char uid[8]; + char token[16]; +} Token; + +typedef struct { + Token tokens[10]; + int token_count; + void (*transport_write)(const char* data); + uint8_t tea_key[16]; +} ToyPadEmu; + typedef struct { unsigned char type; unsigned char len; @@ -34,6 +49,15 @@ typedef struct { int payload_len; } Request; +// Event structure +typedef struct { + Frame* frame; + unsigned char pad; + unsigned char index; + unsigned char dir; + unsigned char* uid; +} Event; + extern FuriHalUsbInterface usb_hid_ldtoypad; // typedef enum { From 7ca291a6806e2e8e110f4878ac552779f7ebad35 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Wed, 1 Jan 2025 16:56:28 +0100 Subject: [PATCH 13/27] testing --- burtle.c | 34 ++++++++++++++++ tea.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++ tea.h | 25 ++++++++++++ usb/usb_toypad.c | 22 ++++++++++ usb/usb_toypad.h | 12 +++--- 5 files changed, 191 insertions(+), 6 deletions(-) create mode 100644 burtle.c create mode 100644 tea.c create mode 100644 tea.h diff --git a/burtle.c b/burtle.c new file mode 100644 index 0000000..9e8c90a --- /dev/null +++ b/burtle.c @@ -0,0 +1,34 @@ +#include + +typedef struct { + uint32_t a, b, c, d; +} Burtle; + +// Rotate left function, equivalent to the `rot` function in JavaScript +uint32_t rotate(uint32_t value, uint32_t bits) { + return ((value << bits) | (value >> (32 - bits))) & 0xFFFFFFFF; +} + +// Initialize the Burtle structure with a seed +void burtle_init(Burtle* burtle, uint32_t seed) { + burtle->a = 0xf1ea5eed; + burtle->b = seed; + burtle->c = seed; + burtle->d = seed; + + // Run the rand function 42 times to initialize the state + for(int i = 0; i < 42; ++i) { + burtle_rand(burtle); + } +} + +// Random number generation function +uint32_t burtle_rand(Burtle* burtle) { + uint32_t e = burtle->a - rotate(burtle->b, 21); + burtle->a = (burtle->b ^ rotate(burtle->c, 19)) & 0xFFFFFFFF; + burtle->b = (burtle->c + rotate(burtle->d, 6)) & 0xFFFFFFFF; + burtle->c = (burtle->d + e) & 0xFFFFFFFF; + burtle->d = (e + burtle->a) & 0xFFFFFFFF; + + return burtle->d; +} diff --git a/tea.c b/tea.c new file mode 100644 index 0000000..a8938b6 --- /dev/null +++ b/tea.c @@ -0,0 +1,104 @@ +#include +#include +#include + +#define DELTA 0x9E3779B9 + +void flipBytes(uint8_t* buf, size_t length, uint8_t* out) { + for(size_t i = 0; i < length; i += 4) { + uint32_t value = + ((uint32_t)buf[i] | (uint32_t)buf[i + 1] << 8 | (uint32_t)buf[i + 2] << 16 | + (uint32_t)buf[i + 3] << 24); + value = (value >> 0) & 0xFFFFFFFF; // This is just to keep the value unsigned + out[i] = (uint8_t)(value >> 24); + out[i + 1] = (uint8_t)(value >> 16); + out[i + 2] = (uint8_t)(value >> 8); + out[i + 3] = (uint8_t)value; + } +} + +void encrypt(uint8_t* buffer, uint8_t* key, uint8_t* out) { + if(!key) { + return; + } + + uint32_t v0 = + ((uint32_t)buffer[0] | (uint32_t)buffer[1] << 8 | (uint32_t)buffer[2] << 16 | + (uint32_t)buffer[3] << 24); + uint32_t v1 = + ((uint32_t)buffer[4] | (uint32_t)buffer[5] << 8 | (uint32_t)buffer[6] << 16 | + (uint32_t)buffer[7] << 24); + + uint32_t k0 = + ((uint32_t)key[0] | (uint32_t)key[1] << 8 | (uint32_t)key[2] << 16 | + (uint32_t)key[3] << 24); + uint32_t k1 = + ((uint32_t)key[4] | (uint32_t)key[5] << 8 | (uint32_t)key[6] << 16 | + (uint32_t)key[7] << 24); + uint32_t k2 = + ((uint32_t)key[8] | (uint32_t)key[9] << 8 | (uint32_t)key[10] << 16 | + (uint32_t)key[11] << 24); + uint32_t k3 = + ((uint32_t)key[12] | (uint32_t)key[13] << 8 | (uint32_t)key[14] << 16 | + (uint32_t)key[15] << 24); + + uint32_t sum = 0; + for(int i = 0; i < 32; ++i) { + sum += DELTA; + sum &= 0xFFFFFFFF; + v0 += (((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)) & 0xFFFFFFFF; + v1 += (((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)) & 0xFFFFFFFF; + } + + out[0] = (uint8_t)(v0 >> 24); + out[1] = (uint8_t)(v0 >> 16); + out[2] = (uint8_t)(v0 >> 8); + out[3] = (uint8_t)v0; + out[4] = (uint8_t)(v1 >> 24); + out[5] = (uint8_t)(v1 >> 16); + out[6] = (uint8_t)(v1 >> 8); + out[7] = (uint8_t)v1; +} + +void decrypt(uint8_t* buffer, uint8_t* key, uint8_t* out) { + if(!key) { + return; + } + + uint32_t v0 = + ((uint32_t)buffer[0] | (uint32_t)buffer[1] << 8 | (uint32_t)buffer[2] << 16 | + (uint32_t)buffer[3] << 24); + uint32_t v1 = + ((uint32_t)buffer[4] | (uint32_t)buffer[5] << 8 | (uint32_t)buffer[6] << 16 | + (uint32_t)buffer[7] << 24); + + uint32_t k0 = + ((uint32_t)key[0] | (uint32_t)key[1] << 8 | (uint32_t)key[2] << 16 | + (uint32_t)key[3] << 24); + uint32_t k1 = + ((uint32_t)key[4] | (uint32_t)key[5] << 8 | (uint32_t)key[6] << 16 | + (uint32_t)key[7] << 24); + uint32_t k2 = + ((uint32_t)key[8] | (uint32_t)key[9] << 8 | (uint32_t)key[10] << 16 | + (uint32_t)key[11] << 24); + uint32_t k3 = + ((uint32_t)key[12] | (uint32_t)key[13] << 8 | (uint32_t)key[14] << 16 | + (uint32_t)key[15] << 24); + + uint32_t sum = 0xC6EF3720; + for(int i = 0; i < 32; ++i) { + v1 -= (((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)) & 0xFFFFFFFF; + v0 -= (((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)) & 0xFFFFFFFF; + sum -= DELTA; + sum &= 0xFFFFFFFF; + } + + out[0] = (uint8_t)(v0 >> 24); + out[1] = (uint8_t)(v0 >> 16); + out[2] = (uint8_t)(v0 >> 8); + out[3] = (uint8_t)v0; + out[4] = (uint8_t)(v1 >> 24); + out[5] = (uint8_t)(v1 >> 16); + out[6] = (uint8_t)(v1 >> 8); + out[7] = (uint8_t)v1; +} \ No newline at end of file diff --git a/tea.h b/tea.h new file mode 100644 index 0000000..dd315a7 --- /dev/null +++ b/tea.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Function to flip bytes in a buffer (from little-endian to big-endian) +void flipBytes(uint8_t* buf, size_t length, uint8_t* out); + +// TEA encryption function +// Encrypts 64-bit data (v) using a 128-bit key (k) +// Output is stored in out +void encrypt(uint8_t* buffer, uint8_t* key, uint8_t* out); + +// TEA decryption function +// Decrypts 64-bit data (v) using a 128-bit key (k) +// Output is stored in out +void decrypt(uint8_t* buffer, uint8_t* key, uint8_t* out); + +#ifdef __cplusplus +} +#endif diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index 4731536..bca1688 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -6,6 +6,7 @@ #include "usb_hid.h" #include "../views/EmulateToyPad_scene.h" +#include "../tea.h" // Define all the possible commands #define CMD_WAKE 0xB0 @@ -516,6 +517,23 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { break; case CMD_SEED: sprintf(debug_text, "CMD_SEED"); + + // decrypt the payload with the TEA + uint32_t* payload = (uint32_t*)request.payload; + tea_decrypt(payload, emulator->tea_key); + + // unsigned char decrypted_payload[sizeof(request.payload)]; + // tea_decrypt(request.payload, emulator->tea_key, decrypted_payload); + // memcpy(request.payload, decrypted_payload, sizeof(request.payload)); + + // converted Javascript code to C + // var seed = request.payload.readUInt32LE(0) var conf = request.payload.readUInt32BE(4) + uint32_t seed = request.payload[0] | request.payload[1] << 8 | request.payload[2] << 16 | + request.payload[3] << 24; + + uint32_t conf = request.payload[4] | request.payload[5] << 8 | request.payload[6] << 16 | + request.payload[7] << 24; + break; case CMD_WRITE: sprintf(debug_text, "CMD_WRITE"); @@ -570,6 +588,10 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { // sprintf(debug_text, "Empty payload_len"); return; } + if(response.payload_len > HID_EP_SZ) { + sprintf(debug_text, "Payload too big"); + return; + } // Make the response unsigned char res_buf[HID_EP_SZ]; diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index 5b6232a..9c8948b 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -34,7 +34,7 @@ typedef struct { typedef struct { Frame frame; unsigned char cid; - unsigned char payload[HID_EP_SZ]; + unsigned char payload[HID_EP_SZ - 1]; int payload_len; // int _cancel; // int _preventDefault; @@ -51,11 +51,11 @@ typedef struct { // Event structure typedef struct { - Frame* frame; - unsigned char pad; - unsigned char index; - unsigned char dir; - unsigned char* uid; + uint8_t pad; + uint8_t index; + uint8_t dir; + char uid[16]; // UID as a string + Frame frame; } Event; extern FuriHalUsbInterface usb_hid_ldtoypad; From 56c739c9b41faf4659c90a9676f3505737abfb8c Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Wed, 1 Jan 2025 16:59:24 +0100 Subject: [PATCH 14/27] fixed tea --- tea.c | 4 ++-- tea.h | 4 ++-- usb/usb_toypad.c | 8 +++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tea.c b/tea.c index a8938b6..c28b268 100644 --- a/tea.c +++ b/tea.c @@ -17,7 +17,7 @@ void flipBytes(uint8_t* buf, size_t length, uint8_t* out) { } } -void encrypt(uint8_t* buffer, uint8_t* key, uint8_t* out) { +void tea_encrypt(uint8_t* buffer, uint8_t* key, uint8_t* out) { if(!key) { return; } @@ -60,7 +60,7 @@ void encrypt(uint8_t* buffer, uint8_t* key, uint8_t* out) { out[7] = (uint8_t)v1; } -void decrypt(uint8_t* buffer, uint8_t* key, uint8_t* out) { +void tea_decrypt(uint8_t* buffer, uint8_t* key, uint8_t* out) { if(!key) { return; } diff --git a/tea.h b/tea.h index dd315a7..373e0f2 100644 --- a/tea.h +++ b/tea.h @@ -13,12 +13,12 @@ void flipBytes(uint8_t* buf, size_t length, uint8_t* out); // TEA encryption function // Encrypts 64-bit data (v) using a 128-bit key (k) // Output is stored in out -void encrypt(uint8_t* buffer, uint8_t* key, uint8_t* out); +void tea_encrypt(uint8_t* buffer, uint8_t* key, uint8_t* out); // TEA decryption function // Decrypts 64-bit data (v) using a 128-bit key (k) // Output is stored in out -void decrypt(uint8_t* buffer, uint8_t* key, uint8_t* out); +void tea_decrypt(uint8_t* buffer, uint8_t* key, uint8_t* out); #ifdef __cplusplus } diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index bca1688..ff1e676 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -518,9 +518,8 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { case CMD_SEED: sprintf(debug_text, "CMD_SEED"); - // decrypt the payload with the TEA - uint32_t* payload = (uint32_t*)request.payload; - tea_decrypt(payload, emulator->tea_key); + // decrypt the request.payload with the TEA + tea_decrypt(request.payload, emulator->tea_key, request.payload); // unsigned char decrypted_payload[sizeof(request.payload)]; // tea_decrypt(request.payload, emulator->tea_key, decrypted_payload); @@ -534,6 +533,9 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { uint32_t conf = request.payload[4] | request.payload[5] << 8 | request.payload[6] << 16 | request.payload[7] << 24; + UNUSED(seed); + UNUSED(conf); + break; case CMD_WRITE: sprintf(debug_text, "CMD_WRITE"); From 66ff81d05ebf416ba943ef37a337ec02277a23de Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Wed, 1 Jan 2025 22:54:18 +0100 Subject: [PATCH 15/27] changes --- burtle.c | 27 +++++++++------------ burtle.h | 19 +++++++++++++++ tea.c | 4 +-- usb/usb_toypad.c | 63 ++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 83 insertions(+), 30 deletions(-) create mode 100644 burtle.h diff --git a/burtle.c b/burtle.c index 9e8c90a..1841be4 100644 --- a/burtle.c +++ b/burtle.c @@ -1,14 +1,22 @@ #include - -typedef struct { - uint32_t a, b, c, d; -} Burtle; +#include "burtle.h" // Rotate left function, equivalent to the `rot` function in JavaScript uint32_t rotate(uint32_t value, uint32_t bits) { return ((value << bits) | (value >> (32 - bits))) & 0xFFFFFFFF; } +// Random number generation function +uint32_t burtle_rand(Burtle* burtle) { + uint32_t e = burtle->a - rotate(burtle->b, 21); + burtle->a = (burtle->b ^ rotate(burtle->c, 19)) & 0xFFFFFFFF; + burtle->b = (burtle->c + rotate(burtle->d, 6)) & 0xFFFFFFFF; + burtle->c = (burtle->d + e) & 0xFFFFFFFF; + burtle->d = (e + burtle->a) & 0xFFFFFFFF; + + return burtle->d; +} + // Initialize the Burtle structure with a seed void burtle_init(Burtle* burtle, uint32_t seed) { burtle->a = 0xf1ea5eed; @@ -21,14 +29,3 @@ void burtle_init(Burtle* burtle, uint32_t seed) { burtle_rand(burtle); } } - -// Random number generation function -uint32_t burtle_rand(Burtle* burtle) { - uint32_t e = burtle->a - rotate(burtle->b, 21); - burtle->a = (burtle->b ^ rotate(burtle->c, 19)) & 0xFFFFFFFF; - burtle->b = (burtle->c + rotate(burtle->d, 6)) & 0xFFFFFFFF; - burtle->c = (burtle->d + e) & 0xFFFFFFFF; - burtle->d = (e + burtle->a) & 0xFFFFFFFF; - - return burtle->d; -} diff --git a/burtle.h b/burtle.h new file mode 100644 index 0000000..abc47bf --- /dev/null +++ b/burtle.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t a, b, c, d; +} Burtle; + +void burtle_init(Burtle* burtle, uint32_t seed); +uint32_t burtle_rand(Burtle* burtle); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/tea.c b/tea.c index c28b268..1ee26d3 100644 --- a/tea.c +++ b/tea.c @@ -85,7 +85,7 @@ void tea_decrypt(uint8_t* buffer, uint8_t* key, uint8_t* out) { ((uint32_t)key[12] | (uint32_t)key[13] << 8 | (uint32_t)key[14] << 16 | (uint32_t)key[15] << 24); - uint32_t sum = 0xC6EF3720; + uint32_t sum = 0xC6EF3720; // Correct initialization for decryption for(int i = 0; i < 32; ++i) { v1 -= (((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)) & 0xFFFFFFFF; v0 -= (((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)) & 0xFFFFFFFF; @@ -101,4 +101,4 @@ void tea_decrypt(uint8_t* buffer, uint8_t* key, uint8_t* out) { out[5] = (uint8_t)(v1 >> 16); out[6] = (uint8_t)(v1 >> 8); out[7] = (uint8_t)v1; -} \ No newline at end of file +} diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index ff1e676..db7f618 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -7,6 +7,7 @@ #include "../views/EmulateToyPad_scene.h" #include "../tea.h" +#include "../burtle.h" // Define all the possible commands #define CMD_WAKE 0xB0 @@ -449,6 +450,34 @@ void hid_in_callback(usbd_device* dev, uint8_t event, uint8_t ep) { if(len <= 0) return; } +Burtle* burtle; // Define the Burtle object + +// Function to convert a little-endian to uint32_t +uint32_t readUInt32LE(uint8_t* buffer) { + return (buffer[0]) | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); +} + +// Function to convert a big-endian to uint32_t +uint32_t readUInt32BE(uint8_t* buffer) { + return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; +} + +// Function to write uint32_t to little-endian +void writeUInt32LE(uint8_t* buffer, uint32_t value) { + buffer[0] = value & 0xFF; + buffer[1] = (value >> 8) & 0xFF; + buffer[2] = (value >> 16) & 0xFF; + buffer[3] = (value >> 24) & 0xFF; +} + +// Function to write uint32_t to big-endian +void writeUInt32BE(uint8_t* buffer, uint32_t value) { + buffer[0] = (value >> 24) & 0xFF; + buffer[1] = (value >> 16) & 0xFF; + buffer[2] = (value >> 8) & 0xFF; + buffer[3] = value & 0xFF; +} + void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(ep); UNUSED(event); @@ -470,6 +499,10 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { Frame frame; parse_frame(&frame, req_buf, len); + if(frame.len == 0) { + return; + } + Request request; request.cmd = frame.payload[0]; request.cid = frame.payload[1]; @@ -521,20 +554,24 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { // decrypt the request.payload with the TEA tea_decrypt(request.payload, emulator->tea_key, request.payload); - // unsigned char decrypted_payload[sizeof(request.payload)]; - // tea_decrypt(request.payload, emulator->tea_key, decrypted_payload); - // memcpy(request.payload, decrypted_payload, sizeof(request.payload)); + // // converted Javascript code to C + // // var seed = request.payload.readUInt32LE(0) var conf = request.payload.readUInt32BE(4) + // uint32_t seed = request.payload[0] | request.payload[1] << 8 | request.payload[2] << 16 | + // request.payload[3] << 24; + + // uint32_t conf = request.payload[4] | request.payload[5] << 8 | request.payload[6] << 16 | + // request.payload[7] << 24; + + // burtle_init(burtle, seed); - // converted Javascript code to C - // var seed = request.payload.readUInt32LE(0) var conf = request.payload.readUInt32BE(4) - uint32_t seed = request.payload[0] | request.payload[1] << 8 | request.payload[2] << 16 | - request.payload[3] << 24; + // memset(request.payload, 0, 8); // Fill the payload with 0 with a length of 8 + // writeUInt32BE(request.payload, conf); // Write the conf to the payload - uint32_t conf = request.payload[4] | request.payload[5] << 8 | request.payload[6] << 16 | - request.payload[7] << 24; + // // UNUSED(seed); + // UNUSED(conf); - UNUSED(seed); - UNUSED(conf); + // // encrypt the request.payload with the TEA + // tea_encrypt(request.payload, emulator->tea_key, request.payload); break; case CMD_WRITE: @@ -580,8 +617,8 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { sprintf(debug_text, "CMD_LEDSQ"); break; default: - snprintf(debug_text, HID_EP_SZ, "U: %02X", request.cmd); - snprintf(debug_text, HID_EP_SZ, "ERR: %02X", request.cmd); + // snprintf(debug_text, HID_EP_SZ, "ERR: %02X", request.cmd); + sprintf(debug_text, "Not a valid command"); return; } From e90354dbbf70f25891ef630033e83c4497dabffe Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Wed, 1 Jan 2025 23:34:14 +0100 Subject: [PATCH 16/27] why does it crash at results in tea? --- tea.c | 170 +++++++++++++++++++++++------------------------ tea.h | 4 +- usb/usb_toypad.c | 8 ++- usb/usb_toypad.h | 2 + 4 files changed, 95 insertions(+), 89 deletions(-) diff --git a/tea.c b/tea.c index 1ee26d3..e088633 100644 --- a/tea.c +++ b/tea.c @@ -4,101 +4,101 @@ #define DELTA 0x9E3779B9 -void flipBytes(uint8_t* buf, size_t length, uint8_t* out) { - for(size_t i = 0; i < length; i += 4) { - uint32_t value = - ((uint32_t)buf[i] | (uint32_t)buf[i + 1] << 8 | (uint32_t)buf[i + 2] << 16 | - (uint32_t)buf[i + 3] << 24); - value = (value >> 0) & 0xFFFFFFFF; // This is just to keep the value unsigned - out[i] = (uint8_t)(value >> 24); - out[i + 1] = (uint8_t)(value >> 16); - out[i + 2] = (uint8_t)(value >> 8); - out[i + 3] = (uint8_t)value; +#include "./usb/usb_toypad.h" + +// Encipher function: core TEA encryption logic +void encipher(uint32_t* v, uint32_t* k, uint32_t* result) { + uint32_t v0 = v[0], v1 = v[1]; + uint32_t sum = 0; + + for(int i = 0; i < 32; i++) { + sum += DELTA; + v0 += (((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1])); + v1 += (((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3])); } + + result[0] = v0; + result[1] = v1; } -void tea_encrypt(uint8_t* buffer, uint8_t* key, uint8_t* out) { - if(!key) { - return; +// Decipher function: core TEA decryption logic +void decipher(uint32_t* v, uint32_t* k, uint32_t* result) { + uint32_t v0 = v[0], v1 = v[1]; + uint32_t sum = 0xC6EF3720; + + for(int i = 0; i < 32; i++) { + v1 -= (((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3])); + v0 -= (((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1])); + sum -= DELTA; } - uint32_t v0 = - ((uint32_t)buffer[0] | (uint32_t)buffer[1] << 8 | (uint32_t)buffer[2] << 16 | - (uint32_t)buffer[3] << 24); - uint32_t v1 = - ((uint32_t)buffer[4] | (uint32_t)buffer[5] << 8 | (uint32_t)buffer[6] << 16 | - (uint32_t)buffer[7] << 24); - - uint32_t k0 = - ((uint32_t)key[0] | (uint32_t)key[1] << 8 | (uint32_t)key[2] << 16 | - (uint32_t)key[3] << 24); - uint32_t k1 = - ((uint32_t)key[4] | (uint32_t)key[5] << 8 | (uint32_t)key[6] << 16 | - (uint32_t)key[7] << 24); - uint32_t k2 = - ((uint32_t)key[8] | (uint32_t)key[9] << 8 | (uint32_t)key[10] << 16 | - (uint32_t)key[11] << 24); - uint32_t k3 = - ((uint32_t)key[12] | (uint32_t)key[13] << 8 | (uint32_t)key[14] << 16 | - (uint32_t)key[15] << 24); + result[0] = v0; + result[1] = v1; +} - uint32_t sum = 0; - for(int i = 0; i < 32; ++i) { - sum += DELTA; - sum &= 0xFFFFFFFF; - v0 += (((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)) & 0xFFFFFFFF; - v1 += (((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)) & 0xFFFFFFFF; - } +// Load 32-bit value safely (portable) +static uint32_t bytes_to_uint32(const uint8_t* buf) { + return (uint32_t)buf[0] | ((uint32_t)buf[1] << 8) | ((uint32_t)buf[2] << 16) | + ((uint32_t)buf[3] << 24); +} - out[0] = (uint8_t)(v0 >> 24); - out[1] = (uint8_t)(v0 >> 16); - out[2] = (uint8_t)(v0 >> 8); - out[3] = (uint8_t)v0; - out[4] = (uint8_t)(v1 >> 24); - out[5] = (uint8_t)(v1 >> 16); - out[6] = (uint8_t)(v1 >> 8); - out[7] = (uint8_t)v1; +// Store 32-bit value safely (portable) +static void uint32_to_bytes(uint32_t value, uint8_t* buf) { + buf[0] = (uint8_t)(value & 0xFF); + buf[1] = (uint8_t)((value >> 8) & 0xFF); + buf[2] = (uint8_t)((value >> 16) & 0xFF); + buf[3] = (uint8_t)((value >> 24) & 0xFF); } -void tea_decrypt(uint8_t* buffer, uint8_t* key, uint8_t* out) { - if(!key) { +// Encryption function +void tea_encrypt(const uint8_t* buffer, const uint8_t* key, uint8_t* out) { + if(!buffer || !key || !out) return; + + uint32_t v[2] = {bytes_to_uint32(buffer), bytes_to_uint32(buffer + 4)}; + uint32_t k[4] = { + bytes_to_uint32(key), + bytes_to_uint32(key + 4), + bytes_to_uint32(key + 8), + bytes_to_uint32(key + 12)}; + + uint32_t result[2]; + encipher(v, k, result); + + uint32_to_bytes(result[0], out); + uint32_to_bytes(result[1], out + 4); +} + +// Decryption function +void tea_decrypt(const uint8_t* buffer, const uint8_t* key, uint8_t* out) { + if(!buffer || !key || !out) return; + + uint32_t v[2] = {bytes_to_uint32(buffer), bytes_to_uint32(buffer + 4)}; + uint32_t k[4] = { + bytes_to_uint32(key), + bytes_to_uint32(key + 4), + bytes_to_uint32(key + 8), + bytes_to_uint32(key + 12)}; + + // dechiper the buffer + uint32_t result[2]; + decipher(v, k, result); + + // if(k[0] == 0 || v[0] == 0) { + // return; + // } + + if(!out || out == NULL) { + set_debug_text("Error: output pointer is NULL"); return; } - - uint32_t v0 = - ((uint32_t)buffer[0] | (uint32_t)buffer[1] << 8 | (uint32_t)buffer[2] << 16 | - (uint32_t)buffer[3] << 24); - uint32_t v1 = - ((uint32_t)buffer[4] | (uint32_t)buffer[5] << 8 | (uint32_t)buffer[6] << 16 | - (uint32_t)buffer[7] << 24); - - uint32_t k0 = - ((uint32_t)key[0] | (uint32_t)key[1] << 8 | (uint32_t)key[2] << 16 | - (uint32_t)key[3] << 24); - uint32_t k1 = - ((uint32_t)key[4] | (uint32_t)key[5] << 8 | (uint32_t)key[6] << 16 | - (uint32_t)key[7] << 24); - uint32_t k2 = - ((uint32_t)key[8] | (uint32_t)key[9] << 8 | (uint32_t)key[10] << 16 | - (uint32_t)key[11] << 24); - uint32_t k3 = - ((uint32_t)key[12] | (uint32_t)key[13] << 8 | (uint32_t)key[14] << 16 | - (uint32_t)key[15] << 24); - - uint32_t sum = 0xC6EF3720; // Correct initialization for decryption - for(int i = 0; i < 32; ++i) { - v1 -= (((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)) & 0xFFFFFFFF; - v0 -= (((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)) & 0xFFFFFFFF; - sum -= DELTA; - sum &= 0xFFFFFFFF; + // if(result == NULL) { + // set_debug_text("Error: result is NULL"); + // return; + // } + if(result[0] == 0 || result[1] == 0) { + set_debug_text("Error: result is 0"); + return; } - - out[0] = (uint8_t)(v0 >> 24); - out[1] = (uint8_t)(v0 >> 16); - out[2] = (uint8_t)(v0 >> 8); - out[3] = (uint8_t)v0; - out[4] = (uint8_t)(v1 >> 24); - out[5] = (uint8_t)(v1 >> 16); - out[6] = (uint8_t)(v1 >> 8); - out[7] = (uint8_t)v1; + // uint32_to_bytes(result[0], out); + // uint32_to_bytes(result[1], out + 4); } diff --git a/tea.h b/tea.h index 373e0f2..04d86c1 100644 --- a/tea.h +++ b/tea.h @@ -13,12 +13,12 @@ void flipBytes(uint8_t* buf, size_t length, uint8_t* out); // TEA encryption function // Encrypts 64-bit data (v) using a 128-bit key (k) // Output is stored in out -void tea_encrypt(uint8_t* buffer, uint8_t* key, uint8_t* out); +void tea_encrypt(const uint8_t* buffer, const uint8_t* key, uint8_t* out); // TEA decryption function // Decrypts 64-bit data (v) using a 128-bit key (k) // Output is stored in out -void tea_decrypt(uint8_t* buffer, uint8_t* key, uint8_t* out); +void tea_decrypt(const uint8_t* buffer, const uint8_t* key, uint8_t* out); #ifdef __cplusplus } diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index db7f618..fa525ec 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -430,6 +430,10 @@ char debug_text_ep_out[HID_EP_SZ] = "nothing"; char debug_text[HID_EP_SZ] = " "; +void set_debug_text(char* text) { + sprintf(debug_text, "%s", text); +} + // a function that returns a pointer to the string char* get_debug_text_ep_in() { return debug_text_ep_in; @@ -549,7 +553,7 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { sprintf(debug_text, "CMD_MODEL"); break; case CMD_SEED: - sprintf(debug_text, "CMD_SEED"); + // sprintf(debug_text, "CMD_SEED"); // decrypt the request.payload with the TEA tea_decrypt(request.payload, emulator->tea_key, request.payload); @@ -581,7 +585,7 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { sprintf(debug_text, "CMD_CHAL"); break; case CMD_COL: - sprintf(debug_text, "CMD_COL"); + // sprintf(debug_text, "CMD_COL"); break; case CMD_GETCOL: sprintf(debug_text, "CMD_GETCOL"); diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index 9c8948b..f0c1863 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -75,6 +75,8 @@ char* get_debug_text_ep_out(); char* get_debug_text(); +void set_debug_text(char* text); + usbd_device* get_usb_device(); #ifdef __cplusplus From 4492f87356a19c7adc6e10fd76ff7cd3d9d94299 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Thu, 2 Jan 2025 00:13:49 +0100 Subject: [PATCH 17/27] I didnt set the Tea key :( --- tea.c | 6 +-- usb/usb_toypad.c | 122 ++++++++++++++++++++++++++++++++++++----------- usb/usb_toypad.h | 2 +- 3 files changed, 95 insertions(+), 35 deletions(-) diff --git a/tea.c b/tea.c index e088633..8369885 100644 --- a/tea.c +++ b/tea.c @@ -70,7 +70,7 @@ void tea_encrypt(const uint8_t* buffer, const uint8_t* key, uint8_t* out) { // Decryption function void tea_decrypt(const uint8_t* buffer, const uint8_t* key, uint8_t* out) { - if(!buffer || !key || !out) return; + furi_assert(key); uint32_t v[2] = {bytes_to_uint32(buffer), bytes_to_uint32(buffer + 4)}; uint32_t k[4] = { @@ -83,10 +83,6 @@ void tea_decrypt(const uint8_t* buffer, const uint8_t* key, uint8_t* out) { uint32_t result[2]; decipher(v, k, result); - // if(k[0] == 0 || v[0] == 0) { - // return; - // } - if(!out || out == NULL) { set_debug_text("Error: output pointer is NULL"); return; diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index fa525ec..854dc14 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -40,6 +40,41 @@ #define USB_EP0_SIZE 64 PLACE_IN_SECTION("MB_MEM2") static uint32_t ubuf[0x20]; +// Function to parse a Frame into a Request +void parse_request(Request* request, Frame* f) { + if(request == NULL || f == NULL) return; + + request->frame = *f; + uint8_t* p = f->payload; + + request->cmd = p[0]; + request->cid = p[1]; + memcpy(request->payload, p + 2, f->len - 2); // Copy payload, excluding cmd and cid +} + +// Function to build a Frame from a Request +// int build_request(Request* request, uint8_t* out_buf) { +// if(request == NULL || out_buf == NULL) return -1; + +// // Build the frame from the Request +// uint8_t b[HID_EP_SZ + 2]; +// b[0] = request->cmd; +// b[1] = request->cid; +// memcpy(b + 2, request->payload, request->frame.len - 2); + +// // Set Frame type and payload +// request->frame.type = 0x55; +// request->frame.len = request->frame.len; // Should be payload length + 2 (cmd, cid) +// memcpy(request->frame.payload, b, request->frame.len); + +// // Assuming checksum is calculated similarly +// request->frame.chksum = 0; // TODO: Add checksum calculation here + +// // Copy the full frame to the output buffer +// memcpy(out_buf, &request->frame, sizeof(Frame)); +// return sizeof(Frame); // Return size of the built frame +// } + // Function to parse a Frame from a buffer void parse_frame(Frame* frame, unsigned char* buf, int len) { UNUSED(len); @@ -267,10 +302,8 @@ ToyPadEmu* emulator; ToyPadEmu* get_emulator() { return emulator; } -void set_emulator(ToyPadEmu* emu) { - if(emu == NULL) return; - - emulator = emu; +void alloc_emulator() { + if(emulator == NULL) emulator = malloc(sizeof(ToyPadEmu)); } // Generate random UID @@ -288,24 +321,25 @@ void ToyPadEmu_init(ToyPadEmu* emu) { emu->token_count = 0; // Set default TEA key - uint8_t default_tea_key[16] = { - 0x55, - 0xFE, - 0xF6, - 0xB0, - 0x62, - 0xBF, - 0x0B, - 0x41, - 0xC9, - 0xB3, - 0x7C, - 0xB4, - 0x97, - 0x3E, - 0x29, - 0x7B}; - memcpy(emu->tea_key, default_tea_key, 16); + // uint8_t default_tea_key[16] = { + // 0x55, + // 0xFE, + // 0xF6, + // 0xB0, + // 0x62, + // 0xBF, + // 0x0B, + // 0x41, + // 0xC9, + // 0xB3, + // 0x7C, + // 0xB4, + // 0x97, + // 0x3E, + // 0x29, + // 0x7B}; + + // memcpy(emu->tea_key, default_tea_key, sizeof(emu->tea_key)); } // Add a token to a pad @@ -351,6 +385,8 @@ static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { if(hid_semaphore == NULL) hid_semaphore = furi_semaphore_alloc(1, 1); usb_dev = dev; + alloc_emulator(); + // hid_report.keyboard.report_id = ReportIdKeyboard; // hid_report.mouse.report_id = ReportIdMouse; // hid_report.consumer.report_id = ReportIdConsumer; @@ -399,6 +435,8 @@ static void hid_deinit(usbd_device* dev) { free(usb_hid_ldtoypad.str_manuf_descr); free(usb_hid_ldtoypad.str_prod_descr); + + free(emulator); } static void hid_on_wakeup(usbd_device* dev) { @@ -508,10 +546,14 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { } Request request; - request.cmd = frame.payload[0]; - request.cid = frame.payload[1]; - request.payload_len = frame.len - 2; - memcpy(request.payload, frame.payload + 2, request.payload_len); + + // parse request + parse_request(&request, &frame); + + // request.cmd = frame.payload[0]; + // request.cid = frame.payload[1]; + // request.payload_len = frame.len - 2; + // memcpy(request.payload, frame.payload + 2, request.payload_len); Response response; response.cid = request.cid; @@ -522,6 +564,28 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { // handle_cmd_wake(req + 1, res, &res_size); sprintf(debug_text, "CMD_WAKE"); + ToyPadEmu_init(emulator); // Initialize the emulator / setup tea key + + uint8_t default_tea_key[16] = { + 0x55, + 0xFE, + 0xF6, + 0xB0, + 0x62, + 0xBF, + 0x0B, + 0x41, + 0xC9, + 0xB3, + 0x7C, + 0xB4, + 0x97, + 0x3E, + 0x29, + 0x7B}; + + memcpy(emulator->tea_key, default_tea_key, sizeof(emulator->tea_key)); + // int8_t wake_payload[HID_EP_SZ] = {0x55, 0x0f, 0xb0, 0x01, 0x28, 0x63, 0x29, 0x20, // 0x4c, 0x45, 0x47, 0x4f, 0x20, 0x32, 0x30, 0x31, // 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -553,10 +617,10 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { sprintf(debug_text, "CMD_MODEL"); break; case CMD_SEED: - // sprintf(debug_text, "CMD_SEED"); + sprintf(debug_text, "CMD_SEED"); // decrypt the request.payload with the TEA - tea_decrypt(request.payload, emulator->tea_key, request.payload); + // tea_decrypt(request.payload, emulator->tea_key, request.payload); // // converted Javascript code to C // // var seed = request.payload.readUInt32LE(0) var conf = request.payload.readUInt32BE(4) @@ -585,7 +649,7 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { sprintf(debug_text, "CMD_CHAL"); break; case CMD_COL: - // sprintf(debug_text, "CMD_COL"); + sprintf(debug_text, "CMD_COL"); break; case CMD_GETCOL: sprintf(debug_text, "CMD_GETCOL"); diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index f0c1863..4cf24d6 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -26,7 +26,7 @@ typedef struct { typedef struct { unsigned char type; unsigned char len; - unsigned char payload[HID_EP_SZ]; + unsigned char payload[HID_EP_SZ - 2]; unsigned char chksum; } Frame; From 4e614a48d3ee00f547698f98ecc99e4a91275a6a Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Thu, 2 Jan 2025 00:45:23 +0100 Subject: [PATCH 18/27] Now the response... --- tea.c | 4 +-- usb/usb_toypad.c | 53 ++++++++++++++++++++----------------- views/EmulateToyPad_scene.c | 8 ++++++ 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/tea.c b/tea.c index 8369885..499b1cd 100644 --- a/tea.c +++ b/tea.c @@ -95,6 +95,6 @@ void tea_decrypt(const uint8_t* buffer, const uint8_t* key, uint8_t* out) { set_debug_text("Error: result is 0"); return; } - // uint32_to_bytes(result[0], out); - // uint32_to_bytes(result[1], out + 4); + uint32_to_bytes(result[0], out); + uint32_to_bytes(result[1], out + 4); } diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index 854dc14..95b77a2 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -302,9 +302,8 @@ ToyPadEmu* emulator; ToyPadEmu* get_emulator() { return emulator; } -void alloc_emulator() { - if(emulator == NULL) emulator = malloc(sizeof(ToyPadEmu)); -} + +Burtle* burtle; // Define the Burtle object // Generate random UID void ToyPadEmu_randomUID(char* uid) { @@ -385,7 +384,8 @@ static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { if(hid_semaphore == NULL) hid_semaphore = furi_semaphore_alloc(1, 1); usb_dev = dev; - alloc_emulator(); + if(emulator == NULL) emulator = malloc(sizeof(ToyPadEmu)); + if(burtle == NULL) burtle = malloc(sizeof(Burtle)); // hid_report.keyboard.report_id = ReportIdKeyboard; // hid_report.mouse.report_id = ReportIdMouse; @@ -437,6 +437,7 @@ static void hid_deinit(usbd_device* dev) { free(usb_hid_ldtoypad.str_prod_descr); free(emulator); + free(burtle); } static void hid_on_wakeup(usbd_device* dev) { @@ -488,12 +489,15 @@ void hid_in_callback(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(event); // Handle the IN endpoint - int len = usbd_ep_read(dev, HID_EP_IN, debug_text_ep_in, HID_EP_SZ); + unsigned char in_buf[HID_EP_SZ] = {0}; + + int len = usbd_ep_read(dev, HID_EP_IN, in_buf, HID_EP_SZ); + + sprintf(debug_text_ep_in, "%s", in_buf); + if(len <= 0) return; } -Burtle* burtle; // Define the Burtle object - // Function to convert a little-endian to uint32_t uint32_t readUInt32LE(uint8_t* buffer) { return (buffer[0]) | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); @@ -620,26 +624,23 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { sprintf(debug_text, "CMD_SEED"); // decrypt the request.payload with the TEA - // tea_decrypt(request.payload, emulator->tea_key, request.payload); - - // // converted Javascript code to C - // // var seed = request.payload.readUInt32LE(0) var conf = request.payload.readUInt32BE(4) - // uint32_t seed = request.payload[0] | request.payload[1] << 8 | request.payload[2] << 16 | - // request.payload[3] << 24; + tea_decrypt(request.payload, emulator->tea_key, request.payload); - // uint32_t conf = request.payload[4] | request.payload[5] << 8 | request.payload[6] << 16 | - // request.payload[7] << 24; + // converted Javascript code to C + // var seed = request.payload.readUInt32LE(0) var conf = request.payload.readUInt32BE(4) + uint32_t seed = request.payload[0] | request.payload[1] << 8 | request.payload[2] << 16 | + request.payload[3] << 24; - // burtle_init(burtle, seed); + uint32_t conf = request.payload[4] | request.payload[5] << 8 | request.payload[6] << 16 | + request.payload[7] << 24; - // memset(request.payload, 0, 8); // Fill the payload with 0 with a length of 8 - // writeUInt32BE(request.payload, conf); // Write the conf to the payload + burtle_init(burtle, seed); - // // UNUSED(seed); - // UNUSED(conf); + memset(response.payload, 0, 8); // Fill the payload with 0 with a length of 8 + writeUInt32BE(response.payload, conf); // Write the conf to the payload - // // encrypt the request.payload with the TEA - // tea_encrypt(request.payload, emulator->tea_key, request.payload); + // encrypt the request.payload with the TEA + tea_encrypt(response.payload, emulator->tea_key, response.payload); break; case CMD_WRITE: @@ -691,7 +692,7 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { } // check if the response is empty - if(response.payload_len == 0) { + if(sizeof(response.payload) == 0) { // sprintf(debug_text, "Empty payload_len"); return; } @@ -703,14 +704,16 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { // Make the response unsigned char res_buf[HID_EP_SZ]; - int res_len = build_response(&response, res_buf); + build_response(&response, res_buf); + int res_len = build_frame(&response.frame, res_buf); if(res_len <= 0) { + sprintf(debug_text, "res_len is 0"); return; } // Send the response - usbd_ep_write(dev, HID_EP_IN, res_buf, res_len); + usbd_ep_write(dev, HID_EP_IN, res_buf, sizeof(res_buf)); } /* Configure endpoints */ diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index f5fd144..2836f6c 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -355,6 +355,14 @@ static void ldtoypad_scene_emulate_draw_render_callback(Canvas* canvas, void* co elements_multiline_text_aligned(canvas, 1, 33, AlignLeft, AlignTop, "ep_out: "); elements_multiline_text_aligned(canvas, 40, 33, AlignLeft, AlignTop, get_debug_text_ep_out()); + + // ep in + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, 48, 120, 16); + canvas_set_color(canvas, ColorBlack); + + elements_multiline_text_aligned(canvas, 1, 49, AlignLeft, AlignTop, "ep_in: "); + elements_multiline_text_aligned(canvas, 40, 49, AlignLeft, AlignTop, get_debug_text_ep_in()); } static uint32_t ldtoypad_scene_emulate_navigation_submenu_callback(void* context) { From f827870d65334fadcbe15e05ec8f9cb19c1a27b0 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Thu, 2 Jan 2025 13:22:14 +0100 Subject: [PATCH 19/27] It now recognizes as a portal --- burtle.c | 42 +++++++++------------ burtle.h | 4 +- tea.c | 8 ++-- usb/usb_toypad.c | 75 ++++++++++++++++++++----------------- usb/usb_toypad.h | 2 +- views/EmulateToyPad_scene.c | 10 ++--- 6 files changed, 70 insertions(+), 71 deletions(-) diff --git a/burtle.c b/burtle.c index 1841be4..94c0140 100644 --- a/burtle.c +++ b/burtle.c @@ -1,31 +1,25 @@ -#include #include "burtle.h" -// Rotate left function, equivalent to the `rot` function in JavaScript -uint32_t rotate(uint32_t value, uint32_t bits) { - return ((value << bits) | (value >> (32 - bits))) & 0xFFFFFFFF; +// Helper function for rotation +static uint32_t rot(uint32_t a, int b) { + return (a << b) | (a >> (32 - b)); } -// Random number generation function -uint32_t burtle_rand(Burtle* burtle) { - uint32_t e = burtle->a - rotate(burtle->b, 21); - burtle->a = (burtle->b ^ rotate(burtle->c, 19)) & 0xFFFFFFFF; - burtle->b = (burtle->c + rotate(burtle->d, 6)) & 0xFFFFFFFF; - burtle->c = (burtle->d + e) & 0xFFFFFFFF; - burtle->d = (e + burtle->a) & 0xFFFFFFFF; - - return burtle->d; -} - -// Initialize the Burtle structure with a seed -void burtle_init(Burtle* burtle, uint32_t seed) { - burtle->a = 0xf1ea5eed; - burtle->b = seed; - burtle->c = seed; - burtle->d = seed; - - // Run the rand function 42 times to initialize the state +// Constructor equivalent +void burtle_init(Burtle* b, uint32_t seed) { + b->a = 0xf1ea5eed; + b->b = b->c = b->d = seed; for(int i = 0; i < 42; ++i) { - burtle_rand(burtle); + burtle_rand(b); // Initialize with 42 iterations } } + +// rand method +uint32_t burtle_rand(Burtle* b) { + uint32_t e = b->a - rot(b->b, 21); + b->a = b->b ^ rot(b->c, 19); + b->b = b->c + rot(b->d, 6); + b->c = b->d + e; + b->d = e + b->a; + return b->d; +} diff --git a/burtle.h b/burtle.h index abc47bf..45cedd8 100644 --- a/burtle.h +++ b/burtle.h @@ -11,8 +11,8 @@ typedef struct { uint32_t a, b, c, d; } Burtle; -void burtle_init(Burtle* burtle, uint32_t seed); -uint32_t burtle_rand(Burtle* burtle); +void burtle_init(Burtle* b, uint32_t seed); +uint32_t burtle_rand(Burtle* b); #ifdef __cplusplus } diff --git a/tea.c b/tea.c index 499b1cd..0f159c8 100644 --- a/tea.c +++ b/tea.c @@ -91,10 +91,10 @@ void tea_decrypt(const uint8_t* buffer, const uint8_t* key, uint8_t* out) { // set_debug_text("Error: result is NULL"); // return; // } - if(result[0] == 0 || result[1] == 0) { - set_debug_text("Error: result is 0"); - return; - } + // if(result[0] == 0 || result[1] == 0) { + // set_debug_text("Error: result is 0"); + // return; + // } uint32_to_bytes(result[0], out); uint32_to_bytes(result[1], out + 4); } diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index 95b77a2..4c4427f 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -52,29 +52,6 @@ void parse_request(Request* request, Frame* f) { memcpy(request->payload, p + 2, f->len - 2); // Copy payload, excluding cmd and cid } -// Function to build a Frame from a Request -// int build_request(Request* request, uint8_t* out_buf) { -// if(request == NULL || out_buf == NULL) return -1; - -// // Build the frame from the Request -// uint8_t b[HID_EP_SZ + 2]; -// b[0] = request->cmd; -// b[1] = request->cid; -// memcpy(b + 2, request->payload, request->frame.len - 2); - -// // Set Frame type and payload -// request->frame.type = 0x55; -// request->frame.len = request->frame.len; // Should be payload length + 2 (cmd, cid) -// memcpy(request->frame.payload, b, request->frame.len); - -// // Assuming checksum is calculated similarly -// request->frame.chksum = 0; // TODO: Add checksum calculation here - -// // Copy the full frame to the output buffer -// memcpy(out_buf, &request->frame, sizeof(Frame)); -// return sizeof(Frame); // Return size of the built frame -// } - // Function to parse a Frame from a buffer void parse_frame(Frame* frame, unsigned char* buf, int len) { UNUSED(len); @@ -498,14 +475,14 @@ void hid_in_callback(usbd_device* dev, uint8_t event, uint8_t ep) { if(len <= 0) return; } -// Function to convert a little-endian to uint32_t -uint32_t readUInt32LE(uint8_t* buffer) { - return (buffer[0]) | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); +uint32_t readUInt32LE(const unsigned char* buffer, int offset) { + return (uint32_t)buffer[offset] | ((uint32_t)buffer[offset + 1] << 8) | + ((uint32_t)buffer[offset + 2] << 16) | ((uint32_t)buffer[offset + 3] << 24); } -// Function to convert a big-endian to uint32_t -uint32_t readUInt32BE(uint8_t* buffer) { - return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; +uint32_t readUInt32BE(const unsigned char* buffer, int offset) { + return ((uint32_t)buffer[offset] << 24) | ((uint32_t)buffer[offset + 1] << 16) | + ((uint32_t)buffer[offset + 2] << 8) | (uint32_t)buffer[offset + 3]; } // Function to write uint32_t to little-endian @@ -551,6 +528,8 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { Request request; + memset(&request, 0, sizeof(Request)); + // parse request parse_request(&request, &frame); @@ -560,9 +539,13 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { // memcpy(request.payload, frame.payload + 2, request.payload_len); Response response; + memset(&response, 0, sizeof(Response)); + response.cid = request.cid; response.payload_len = 0; + uint32_t conf; + switch(request.cmd) { case CMD_WAKE: // handle_cmd_wake(req + 1, res, &res_size); @@ -626,13 +609,9 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { // decrypt the request.payload with the TEA tea_decrypt(request.payload, emulator->tea_key, request.payload); - // converted Javascript code to C - // var seed = request.payload.readUInt32LE(0) var conf = request.payload.readUInt32BE(4) - uint32_t seed = request.payload[0] | request.payload[1] << 8 | request.payload[2] << 16 | - request.payload[3] << 24; + uint32_t seed = readUInt32LE(request.payload, 0); - uint32_t conf = request.payload[4] | request.payload[5] << 8 | request.payload[6] << 16 | - request.payload[7] << 24; + conf = readUInt32BE(request.payload, 4); burtle_init(burtle, seed); @@ -642,12 +621,38 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { // encrypt the request.payload with the TEA tea_encrypt(response.payload, emulator->tea_key, response.payload); + response.payload_len = 8; + break; case CMD_WRITE: sprintf(debug_text, "CMD_WRITE"); break; case CMD_CHAL: sprintf(debug_text, "CMD_CHAL"); + + // decrypt the request.payload with the TEA + tea_decrypt(request.payload, emulator->tea_key, request.payload); + + // get conf + conf = readUInt32BE(request.payload, 0); + + // make a new buffer for the response of 8 + memset(response.payload, 0, 8); + + // get a rand from the burtle + uint32_t rand = burtle_rand(burtle); + + // write the rand to the response payload as Int32LE + writeUInt32LE(response.payload, rand); + + // write the conf to the response payload as Int32BE + writeUInt32BE(response.payload + 4, conf); + + // encrypt the response.payload with the TEA + tea_encrypt(response.payload, emulator->tea_key, response.payload); + + response.payload_len = 8; + break; case CMD_COL: sprintf(debug_text, "CMD_COL"); diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index 4cf24d6..c128608 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -34,7 +34,7 @@ typedef struct { typedef struct { Frame frame; unsigned char cid; - unsigned char payload[HID_EP_SZ - 1]; + unsigned char payload[HID_EP_SZ]; int payload_len; // int _cancel; // int _preventDefault; diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index 2836f6c..d6c0e25 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -357,12 +357,12 @@ static void ldtoypad_scene_emulate_draw_render_callback(Canvas* canvas, void* co elements_multiline_text_aligned(canvas, 40, 33, AlignLeft, AlignTop, get_debug_text_ep_out()); // ep in - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 0, 48, 120, 16); - canvas_set_color(canvas, ColorBlack); + // canvas_set_color(canvas, ColorWhite); + // canvas_draw_box(canvas, 0, 48, 120, 16); + // canvas_set_color(canvas, ColorBlack); - elements_multiline_text_aligned(canvas, 1, 49, AlignLeft, AlignTop, "ep_in: "); - elements_multiline_text_aligned(canvas, 40, 49, AlignLeft, AlignTop, get_debug_text_ep_in()); + // elements_multiline_text_aligned(canvas, 1, 49, AlignLeft, AlignTop, "ep_in: "); + // elements_multiline_text_aligned(canvas, 40, 49, AlignLeft, AlignTop, get_debug_text_ep_in()); } static uint32_t ldtoypad_scene_emulate_navigation_submenu_callback(void* context) { From 00ff3e3668a7780ccddf0603cb0b78078901f0f4 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Thu, 2 Jan 2025 14:44:36 +0100 Subject: [PATCH 20/27] Made submenu --- ldtoypad.c | 34 ++++++++++++++- ldtoypad.h | 6 ++- minifigures.c | 11 +++++ minifigures.h | 18 ++++++++ usb/usb_toypad.c | 19 +++++---- usb/usb_toypad.h | 3 +- views/EmulateToyPad_scene.c | 82 ++++++++++++++++++++----------------- views/EmulateToyPad_scene.h | 12 +++--- 8 files changed, 129 insertions(+), 56 deletions(-) create mode 100644 minifigures.c create mode 100644 minifigures.h diff --git a/ldtoypad.c b/ldtoypad.c index 3c943da..8edf122 100644 --- a/ldtoypad.c +++ b/ldtoypad.c @@ -3,6 +3,8 @@ /* generated by fbt from .png files in images folder */ #include +#include "minifigures.h" + // Our application menu has 3 items. You can add more items if you want. typedef enum { EmulateToyPadSubmenuIndex, @@ -166,6 +168,11 @@ static void ldtoypad_setting_item_clicked(void* context, uint32_t index) { } } +static uint32_t minifigures_submenu_previous_callback(void* context) { + UNUSED(context); + return ViewEmulate; +} + /** * @brief Allocate the ldtoypad application. Set up the views and resources. * @details This function allocates the ldtoypad application resources. @@ -225,7 +232,7 @@ static LDToyPadApp* ldtoypad_app_alloc() { ViewConfigure, variable_item_list_get_view(app->variable_item_list_config)); - app->view_scene_emulate = ldtoypad_scene_emulate_alloc(); + app->view_scene_emulate = ldtoypad_scene_emulate_alloc(app); // This is allready happening in ldtoypad_scene_emulate_alloc @@ -261,6 +268,28 @@ static LDToyPadApp* ldtoypad_app_alloc() { view_dispatcher_add_view(app->view_dispatcher, ViewAbout, widget_get_view(app->widget_about)); + // create a minifigure selection screen + app->submenu_minifigure_selection = submenu_alloc(); + // get the minifigures from minifigures.h + for(int i = 0; minifigures[i].name != NULL; i++) { + submenu_add_item( + app->submenu_minifigure_selection, + minifigures[i].name, + minifigures[i].id, + minifigures_submenu_callback, + app); + } + view_set_previous_callback( + submenu_get_view(app->submenu_minifigure_selection), + minifigures_submenu_previous_callback); + + view_dispatcher_add_view( + app->view_dispatcher, + ViewMinifigureSelection, + submenu_get_view(app->submenu_minifigure_selection)); + + submenu_set_header(app->submenu_minifigure_selection, "Select minifigure"); + return app; } @@ -289,6 +318,9 @@ static void ldtoypad_app_free(LDToyPadApp* app) { view_dispatcher_remove_view(app->view_dispatcher, ViewSubmenu); submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, ViewMinifigureSelection); + submenu_free(app->submenu_minifigure_selection); + // view_dispatcher_free(app->view_dispatcher); // this is causing a crash furi_record_close(RECORD_GUI); diff --git a/ldtoypad.h b/ldtoypad.h index 61600b7..b70d57c 100644 --- a/ldtoypad.h +++ b/ldtoypad.h @@ -44,7 +44,10 @@ typedef struct { char* temp_buffer; // Temporary buffer for text input uint32_t temp_buffer_size; // Size of temporary buffer - FuriTimer* timer; // Timer for redrawing the screen + // FuriTimer* timer; // Timer for redrawing the screen + + Submenu* submenu_minifigure_selection; // The minifigure selection screen + } LDToyPadApp; // Each view is a screen we show the user. @@ -54,6 +57,7 @@ typedef enum { ViewConfigure, // The configuration screen ViewEmulate, // The main screen ViewAbout, // The about screen with directions, link to social channel, etc. + ViewMinifigureSelection, // The minifigure selection screen } Views; #ifdef __cplusplus diff --git a/minifigures.c b/minifigures.c new file mode 100644 index 0000000..7fe277c --- /dev/null +++ b/minifigures.c @@ -0,0 +1,11 @@ +#include "minifigures.h" + +Minifigure minifigures[] = { + {1, "Batman"}, + {2, "Gandalf"}, + {3, "Wyldstyle"}, + {4, "Aquaman"}, + {5, "Bad Cop"}, + {6, "Bane"}, + {7, "Bart Simpson"}, + {8, "Benny"}}; \ No newline at end of file diff --git a/minifigures.h b/minifigures.h new file mode 100644 index 0000000..fde1a4f --- /dev/null +++ b/minifigures.h @@ -0,0 +1,18 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int id; + const char* name; + // const char* world; + // const char* abilities; +} Minifigure; + +extern Minifigure minifigures[]; + +#ifdef __cplusplus +} +#endif diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index 4c4427f..ad13b9e 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -318,22 +318,23 @@ void ToyPadEmu_init(ToyPadEmu* emu) { // memcpy(emu->tea_key, default_tea_key, sizeof(emu->tea_key)); } +Token createCharacter(int id, const char* uid) { + Token token; // Declare a token structure + memset(token.data, 0, sizeof(token.data)); // Fill the array with zeros + strncpy(token.uid, uid, sizeof(token.uid)); // Set the UID + token.id = id; // Set the ID + return token; // Return the created token +} + // Add a token to a pad -bool ToyPadEmu_place( - ToyPadEmu* emu, - const uint8_t* token_data, - int pad, - int index, - const char* uid) { +bool ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid) { if(emu->token_count > 7) { return false; } - Token new_token; + Token new_token = createCharacter(index, uid); new_token.index = index; new_token.pad = pad; - strncpy(new_token.uid, uid, sizeof(new_token.uid)); - memcpy(new_token.token, token_data, sizeof(new_token.token)); emu->tokens[emu->token_count++] = new_token; diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index c128608..f804bfe 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -11,9 +11,10 @@ extern "C" { typedef struct { int index; + int id; int pad; char uid[8]; - char token[16]; + unsigned char data[180]; } Token; typedef struct { diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index d6c0e25..25f8ff3 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -13,6 +13,10 @@ #define numBoxes 7 // the number of boxes (7 boxes always) +LDToyPadApp* app; + +FuriHalUsbInterface* usb_mode_prev = NULL; + // Selection box icon uint8_t I_selectionBox[] = {0xf8, 0xff, 0x00, 0x06, 0x00, 0x01, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, @@ -44,9 +48,6 @@ struct LDToyPadSceneEmulate { // void* context; }; -// Previous USB mode -FuriHalUsbInterface* usb_mode_prev = NULL; - // The selected pad on the toypad uint8_t selectedBox = 0; // Variable to keep track of which toypad box is selected @@ -54,16 +55,6 @@ uint8_t selectedBox = 0; // Variable to keep track of which toypad box is select // ViewDispatcher* ldtoypad_view_dispatcher; -Minifigure minifigures[] = { - {1, "Batman"}, - {2, "Gandalf"}, - {3, "Wyldstyle"}, - {4, "Aquaman"}, - {5, "Bad Cop"}, - {6, "Bane"}, - {7, "Bart Simpson"}, - {8, "Benny"}}; - // void selectionMenu_callback(void* context, uint32_t index) { // UNUSED(context); // UNUSED(index); @@ -81,20 +72,20 @@ bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { instance->view, LDToyPadSceneEmulateModel * model, { - // if(event->key == InputKeyBack) { - // if(event->type == InputTypePress) { - // model->back_pressed = true; - // } else if(event->type == InputTypeRelease) { - // model->back_pressed = false; - // } - // } + if(model->selected_minifigure != 0) { + // create a minifigure from the selected minifigure + model->selected_minifigure = 0; + } + + // when the OK button is pressed, we want to switch to the minifigure selection screen for the selected box if(event->key == InputKeyOk) { if(event->type == InputTypePress) { model->ok_pressed = true; - // submenu_reset(selectionMenu); + // set current view to minifigure selection screen + view_dispatcher_switch_to_view(app->view_dispatcher, ViewMinifigureSelection); - // submenu_set_header(selectionMenu, "Select minifig/vehicle"); + // submenu_reset(selectionMenu); // for(int i = 0; minifigures[i].name != NULL; i++) { // submenu_add_item( @@ -106,7 +97,9 @@ bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { // } // view_dispatcher_switch_to_view( // ldtoypad_view_dispatcher, LDToyPadView_SelectionMenu); + consumed = true; + return consumed; } else if(event->type == InputTypeRelease) { model->ok_pressed = false; @@ -250,22 +243,13 @@ void ldtoypad_scene_emulate_draw_callback(Canvas* canvas, void* _model) { void ldtoypad_scene_emulate_enter_callback(void* context) { UNUSED(context); - // furi_assert(context); - usb_mode_prev = furi_hal_usb_get_config(); - furi_hal_usb_unlock(); - furi_check(furi_hal_usb_set_config(&usb_hid_ldtoypad, NULL) == true); - // get usb device + // not used yet } void ldtoypad_scene_emulate_exit_callback(void* context) { - // UNUSED(context); - furi_assert(context); - - if(usb_mode_prev != NULL) { - furi_hal_usb_set_config(usb_mode_prev, NULL); - } - free(usb_mode_prev); + UNUSED(context); + // LDToyPadSceneEmulateModel* model = context; // ldtoypad_scene_emulate_free(context); } @@ -368,15 +352,28 @@ static void ldtoypad_scene_emulate_draw_render_callback(Canvas* canvas, void* co static uint32_t ldtoypad_scene_emulate_navigation_submenu_callback(void* context) { UNUSED(context); + // if(usb_mode_prev != NULL) { + // furi_hal_usb_set_config(usb_mode_prev, NULL); + // free(usb_mode_prev); + // } + return ViewSubmenu; } -LDToyPadSceneEmulate* ldtoypad_scene_emulate_alloc() { +LDToyPadSceneEmulate* ldtoypad_scene_emulate_alloc(LDToyPadApp* new_app) { + furi_assert(new_app); // check if app is set; + app = new_app; + LDToyPadSceneEmulate* instance = malloc(sizeof(LDToyPadSceneEmulate)); instance->view = view_alloc(); // ldtoypad_view_dispatcher = view_dispatcher; + usb_mode_prev = furi_hal_usb_get_config(); + + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid_ldtoypad, NULL) == true); + view_set_context(instance->view, instance); view_allocate_model(instance->view, ViewModelTypeLockFree, sizeof(LDToyPadSceneEmulateModel)); // view_set_draw_callback(instance->view, ldtoypad_scene_emulate_draw_callback); @@ -408,8 +405,6 @@ void ldtoypad_scene_emulate_free(LDToyPadSceneEmulate* ldtoypad_emulate_view) { } free(usb_mode_prev); - // submenu_free(selectionMenu); - free(ldtoypad_emulate_view); } @@ -417,3 +412,16 @@ View* ldtoypad_scene_emulate_get_view(LDToyPadSceneEmulate* instance) { furi_assert(instance); return instance->view; } + +void minifigures_submenu_callback(void* context, uint32_t index) { + LDToyPadApp* app = (LDToyPadApp*)context; + + // set current view to minifigure number to the selected index + with_view_model( + ldtoypad_scene_emulate_get_view(app->view_scene_emulate), + LDToyPadSceneEmulateModel * model, + { model->selected_minifigure = index; }, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, ViewEmulate); +} diff --git a/views/EmulateToyPad_scene.h b/views/EmulateToyPad_scene.h index 8907b29..dfe5214 100644 --- a/views/EmulateToyPad_scene.h +++ b/views/EmulateToyPad_scene.h @@ -19,13 +19,6 @@ View* ldtoypad_scene_emulate_get_view(LDToyPadSceneEmulate* ldtoypad_scene_emula // void ldtoypad_emulate_set_connected_status(LDToyPadEmulateView* ldtoypad_emulate, bool connected); -typedef struct { - int id; - const char* name; - // const char* world; - // const char* abilities; -} Minifigure; - typedef struct { bool left_pressed; bool up_pressed; @@ -35,6 +28,9 @@ typedef struct { bool back_pressed; bool connected; char* connection_status; + bool minifigure_submenu; + + uint32_t selected_minifigure; // uint8_t selectedBox = 0; @@ -44,3 +40,5 @@ typedef struct { FuriString* setting_2_name; // The name setting uint8_t x; // The x coordinate } LDToyPadSceneEmulateModel; + +void minifigures_submenu_callback(void* context, uint32_t index); From be38e2261bc519db01273516a0993ba16ce72d0a Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Thu, 2 Jan 2025 16:39:52 +0100 Subject: [PATCH 21/27] Why does it crash? --- usb/usb_toypad.c | 73 +++++++++++++++++++++++++++++++++++-- usb/usb_toypad.h | 31 +++++++++++++--- views/EmulateToyPad_scene.c | 24 ++++++++++-- views/EmulateToyPad_scene.h | 2 +- 4 files changed, 115 insertions(+), 15 deletions(-) diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index ad13b9e..0c6817a 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -96,6 +96,41 @@ int build_response(Response* response, unsigned char* buf) { return build_frame(&response->frame, buf); } +void Event_init(Event* event, unsigned char* data, int len) { + if(data && len > 0) { + Frame frame; + parse_frame(&frame, data, len); + event->pad = frame.payload[0]; + event->index = frame.payload[2]; + event->dir = frame.payload[3]; + memcpy(event->uid, frame.payload + 4, 16); + event->frame = frame; + } else { + event->pad = 0; + event->index = 0; + event->dir = 0; + memset(event->uid, 0, sizeof(event->uid)); + } +} + +// Function to build the event into a frame +int Event_build(Event* event, unsigned char* buf) { + unsigned char b[11] = {0}; + b[0] = event->pad; + b[1] = 0; + b[2] = event->index; + b[3] = event->dir & 0x1; // Direction is either 0 or 1 + memcpy(b + 4, event->uid, sizeof(event->uid)); + + // Update the event's frame + event->frame.type = 0x56; + event->frame.len = 11; + memcpy(event->frame.payload, b, 11); + + // Build the frame and return the size of the frame + return build_frame(&event->frame, buf); +} + /* String descriptors */ enum UsbDevDescStr { UsbDevLang = 0, @@ -320,16 +355,16 @@ void ToyPadEmu_init(ToyPadEmu* emu) { Token createCharacter(int id, const char* uid) { Token token; // Declare a token structure - memset(token.data, 0, sizeof(token.data)); // Fill the array with zeros + // memset(token.data, 0, sizeof(token.data)); // Fill the array with zeros strncpy(token.uid, uid, sizeof(token.uid)); // Set the UID token.id = id; // Set the ID return token; // Return the created token } // Add a token to a pad -bool ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid) { +void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid) { if(emu->token_count > 7) { - return false; + return; } Token new_token = createCharacter(index, uid); @@ -338,7 +373,29 @@ bool ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid) { emu->tokens[emu->token_count++] = new_token; - return true; + // send to usb + // make a event + Event event; + Event_init(&event, NULL, 0); + + // set the pad + event.pad = pad; + event.index = index; + memcpy(event.uid, uid, 8); + + // build the event + unsigned char buf[HID_EP_SZ]; + int len = Event_build(&event, buf); + + if(len == 0) { + set_debug_text("Length of event is 0"); + return; + } + + // send the event + usbd_ep_write(usb_dev, HID_EP_IN, buf, sizeof(buf)); + + return; } // Remove a token @@ -506,6 +563,8 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(ep); UNUSED(event); + usb_dev = dev; + unsigned char req_buf[HID_EP_SZ] = {0}; // Read data from the OUT endpoint @@ -722,6 +781,12 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { usbd_ep_write(dev, HID_EP_IN, res_buf, sizeof(res_buf)); } +// function to write a buffer to the HID EP_IN +void write_to_ep_in(unsigned char* buffer) { + sprintf(debug_text, "Writing to EP_IN"); + usbd_ep_write(usb_dev, HID_EP_IN, buffer, sizeof(buffer)); +} + /* Configure endpoints */ static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) { switch(cfg) { diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index f804bfe..abce675 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -13,8 +13,8 @@ typedef struct { int index; int id; int pad; - char uid[8]; - unsigned char data[180]; + char uid[7]; + // unsigned char data[180]; } Token; typedef struct { @@ -51,12 +51,13 @@ typedef struct { } Request; // Event structure + typedef struct { - uint8_t pad; - uint8_t index; - uint8_t dir; - char uid[16]; // UID as a string Frame frame; + int pad; + int index; + int dir; + char uid[7]; } Event; extern FuriHalUsbInterface usb_hid_ldtoypad; @@ -80,6 +81,24 @@ void set_debug_text(char* text); usbd_device* get_usb_device(); +void ToyPadEmu_randomUID(char* uid); + +Token createCharacter(int id, const char* uid); + +void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid); + +void Event_init(Event* event, unsigned char* data, int len); + +int Event_build(Event* event, unsigned char* buf); + +void write_to_ep_in(unsigned char* buffer); + +int build_frame(Frame* frame, unsigned char* buf); + +int build_response(Response* response, unsigned char* buf); + +ToyPadEmu* get_emulator(); + #ifdef __cplusplus } #endif diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index 25f8ff3..7ac95c2 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -62,6 +62,15 @@ uint8_t selectedBox = 0; // Variable to keep track of which toypad box is select // view_dispatcher_switch_to_view(ldtoypad_view_dispatcher, LDToyPadView_EmulateToyPad); // } +void send_minifigure(uint32_t minifigure_index) { + // create a minifigure from the selected minifigure + char uid[7]; + ToyPadEmu_randomUID(uid); + + // place the minifigure on the selected box + ToyPadEmu_place(get_emulator(), selectedBox, minifigure_index, uid); +} + bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { LDToyPadSceneEmulate* instance = context; furi_assert(instance); @@ -72,9 +81,9 @@ bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { instance->view, LDToyPadSceneEmulateModel * model, { - if(model->selected_minifigure != 0) { - // create a minifigure from the selected minifigure - model->selected_minifigure = 0; + if(model->selected_minifigure_index != 0) { + send_minifigure(model->selected_minifigure_index); + model->selected_minifigure_index = 0; } // when the OK button is pressed, we want to switch to the minifigure selection screen for the selected box @@ -416,11 +425,18 @@ View* ldtoypad_scene_emulate_get_view(LDToyPadSceneEmulate* instance) { void minifigures_submenu_callback(void* context, uint32_t index) { LDToyPadApp* app = (LDToyPadApp*)context; + // print index of selected minifigure as debug text + char debug_text[10]; + // convert the long unsigned int to a string + snprintf(debug_text, 10, "%ld", index); + // set the debug text + set_debug_text(debug_text); + // set current view to minifigure number to the selected index with_view_model( ldtoypad_scene_emulate_get_view(app->view_scene_emulate), LDToyPadSceneEmulateModel * model, - { model->selected_minifigure = index; }, + { model->selected_minifigure_index = index + 1; }, true); view_dispatcher_switch_to_view(app->view_dispatcher, ViewEmulate); diff --git a/views/EmulateToyPad_scene.h b/views/EmulateToyPad_scene.h index dfe5214..24743ed 100644 --- a/views/EmulateToyPad_scene.h +++ b/views/EmulateToyPad_scene.h @@ -30,7 +30,7 @@ typedef struct { char* connection_status; bool minifigure_submenu; - uint32_t selected_minifigure; + uint32_t selected_minifigure_index; // uint8_t selectedBox = 0; From 26e3f8690a1c11707c486b4503e1070ee44898c3 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Thu, 2 Jan 2025 18:34:52 +0100 Subject: [PATCH 22/27] Made screen draw itself with timer --- ldtoypad.c | 6 +++++ ldtoypad.h | 4 +-- usb/usb_toypad.c | 17 ++++++------ usb/usb_toypad.h | 4 +-- views/EmulateToyPad_scene.c | 54 +++++++++++++++++++++++++++---------- views/EmulateToyPad_scene.h | 2 ++ 6 files changed, 60 insertions(+), 27 deletions(-) diff --git a/ldtoypad.c b/ldtoypad.c index 8edf122..6d3bf6d 100644 --- a/ldtoypad.c +++ b/ldtoypad.c @@ -173,6 +173,11 @@ static uint32_t minifigures_submenu_previous_callback(void* context) { return ViewEmulate; } +ViewDispatcher* view_dispatcher; + +ViewDispatcher* get_view_dispatcher() { + return view_dispatcher; +} /** * @brief Allocate the ldtoypad application. Set up the views and resources. * @details This function allocates the ldtoypad application resources. @@ -184,6 +189,7 @@ static LDToyPadApp* ldtoypad_app_alloc() { Gui* gui = furi_record_open(RECORD_GUI); app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher = app->view_dispatcher; view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen); view_dispatcher_set_event_callback_context(app->view_dispatcher, app); diff --git a/ldtoypad.h b/ldtoypad.h index b70d57c..b680160 100644 --- a/ldtoypad.h +++ b/ldtoypad.h @@ -44,8 +44,6 @@ typedef struct { char* temp_buffer; // Temporary buffer for text input uint32_t temp_buffer_size; // Size of temporary buffer - // FuriTimer* timer; // Timer for redrawing the screen - Submenu* submenu_minifigure_selection; // The minifigure selection screen } LDToyPadApp; @@ -60,6 +58,8 @@ typedef enum { ViewMinifigureSelection, // The minifigure selection screen } Views; +ViewDispatcher* get_view_dispatcher(); + #ifdef __cplusplus } #endif diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index 0c6817a..dada8df 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -319,13 +319,12 @@ Burtle* burtle; // Define the Burtle object // Generate random UID void ToyPadEmu_randomUID(char* uid) { - srand(furi_get_tick()); // Set the seed to random value + // srand(furi_get_tick()); // Set the seed to random value uid[0] = 0x04; // vendor id = NXP uid[6] = 0x80; // Set last byte to 0x80 for(int i = 1; i < 6; i++) { uid[i] = rand() % 256; } - uid[7] = '\0'; // null terminate } void ToyPadEmu_init(ToyPadEmu* emu) { @@ -367,21 +366,19 @@ void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid) { return; } - Token new_token = createCharacter(index, uid); - new_token.index = index; - new_token.pad = pad; + // Token new_token = createCharacter(index, uid); + // new_token.index = index; + // new_token.pad = pad; - emu->tokens[emu->token_count++] = new_token; + // emu->tokens[emu->token_count++] = new_token; - // send to usb - // make a event Event event; Event_init(&event, NULL, 0); // set the pad event.pad = pad; event.index = index; - memcpy(event.uid, uid, 8); + memcpy(event.uid, uid, sizeof(event.uid)); // build the event unsigned char buf[HID_EP_SZ]; @@ -392,6 +389,8 @@ void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid) { return; } + emu->token_count++; + // send the event usbd_ep_write(usb_dev, HID_EP_IN, buf, sizeof(buf)); diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index abce675..3f845f6 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -13,7 +13,7 @@ typedef struct { int index; int id; int pad; - char uid[7]; + char uid[6]; // unsigned char data[180]; } Token; @@ -57,7 +57,7 @@ typedef struct { int pad; int index; int dir; - char uid[7]; + char uid[6]; } Event; extern FuriHalUsbInterface usb_hid_ldtoypad; diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index 7ac95c2..58fecfd 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -46,6 +46,9 @@ struct LDToyPadSceneEmulate { View* view; // LDToyPadSceneEmulateCallback callback; // void* context; + + // timer + FuriTimer* timer; // Timer for redrawing the screen }; // The selected pad on the toypad @@ -64,9 +67,11 @@ uint8_t selectedBox = 0; // Variable to keep track of which toypad box is select void send_minifigure(uint32_t minifigure_index) { // create a minifigure from the selected minifigure - char uid[7]; + char uid[6]; ToyPadEmu_randomUID(uid); + set_debug_text(uid); + // place the minifigure on the selected box ToyPadEmu_place(get_emulator(), selectedBox, minifigure_index, uid); } @@ -250,18 +255,13 @@ void ldtoypad_scene_emulate_draw_callback(Canvas* canvas, void* _model) { // // Now for USB info also but below the other one } -void ldtoypad_scene_emulate_enter_callback(void* context) { - UNUSED(context); - - // not used yet -} - -void ldtoypad_scene_emulate_exit_callback(void* context) { - UNUSED(context); - // LDToyPadSceneEmulateModel* model = context; +// void ldtoypad_scene_emulate_enter_callback(void* context) { +// UNUSED(context); +// } - // ldtoypad_scene_emulate_free(context); -} +// void ldtoypad_scene_emulate_exit_callback(void* context) { +// UNUSED(context); +// } // uint32_t selectionMenu_prev_callback(void* context) { // UNUSED(context); @@ -369,8 +369,30 @@ static uint32_t ldtoypad_scene_emulate_navigation_submenu_callback(void* context return ViewSubmenu; } +void ldtoypad_scene_emulate_view_game_timer_callback(void* context) { + UNUSED(context); + // LDToyPadSceneEmulate* app = (LDToyPadSceneEmulate*)context; + view_dispatcher_send_custom_event(get_view_dispatcher(), 0); +} + +void ldtoypad_scene_emulate_enter_callback(void* context) { + uint32_t period = furi_ms_to_ticks(150); + LDToyPadSceneEmulate* app = (LDToyPadSceneEmulate*)context; + furi_assert(app->timer == NULL); + app->timer = furi_timer_alloc( + ldtoypad_scene_emulate_view_game_timer_callback, FuriTimerTypePeriodic, app); + furi_timer_start(app->timer, period); +} + +void ldtoypad_scene_emulate_exit_callback(void* context) { + LDToyPadSceneEmulate* app = (LDToyPadSceneEmulate*)context; + furi_timer_stop(app->timer); + furi_timer_free(app->timer); + app->timer = NULL; +} + LDToyPadSceneEmulate* ldtoypad_scene_emulate_alloc(LDToyPadApp* new_app) { - furi_assert(new_app); // check if app is set; + furi_assert(new_app); app = new_app; LDToyPadSceneEmulate* instance = malloc(sizeof(LDToyPadSceneEmulate)); @@ -388,9 +410,13 @@ LDToyPadSceneEmulate* ldtoypad_scene_emulate_alloc(LDToyPadApp* new_app) { // view_set_draw_callback(instance->view, ldtoypad_scene_emulate_draw_callback); view_set_draw_callback(instance->view, ldtoypad_scene_emulate_draw_render_callback); view_set_input_callback(instance->view, ldtoypad_scene_emulate_input_callback); + // view_set_enter_callback(instance->view, ldtoypad_scene_emulate_enter_callback); + // view_set_exit_callback(instance->view, ldtoypad_scene_emulate_exit_callback); + view_set_previous_callback(instance->view, ldtoypad_scene_emulate_navigation_submenu_callback); + view_set_enter_callback(instance->view, ldtoypad_scene_emulate_enter_callback); + view_set_exit_callback(instance->view, ldtoypad_scene_emulate_exit_callback); - view_set_previous_callback(instance->view, ldtoypad_scene_emulate_navigation_submenu_callback); // Allocate the submenu // selectionMenu = submenu_alloc(); diff --git a/views/EmulateToyPad_scene.h b/views/EmulateToyPad_scene.h index 24743ed..739c84b 100644 --- a/views/EmulateToyPad_scene.h +++ b/views/EmulateToyPad_scene.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include From 0fbb096feb3c839180afdc39bd17ad2f4e4156c3 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Thu, 2 Jan 2025 19:57:00 +0100 Subject: [PATCH 23/27] testing --- usb/usb_toypad.c | 149 +++++++++++++++++++++++++++--------- usb/usb_toypad.h | 2 + views/EmulateToyPad_scene.c | 89 ++++++++++++++------- 3 files changed, 173 insertions(+), 67 deletions(-) diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index dada8df..cfef730 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -40,6 +40,72 @@ #define USB_EP0_SIZE 64 PLACE_IN_SECTION("MB_MEM2") static uint32_t ubuf[0x20]; +bool connected_status = false; +bool get_connected_status() { + return connected_status; +} + +// create a string variablethat contains the text: nothing to debug yet +char debug_text_ep_in[HID_EP_SZ] = "nothing"; + +// char debug_text_ep_out[] = "nothing to debug yet"; +char debug_text_ep_out[HID_EP_SZ] = "nothing"; + +char debug_text[HID_EP_SZ] = " "; + +void set_debug_text(char* text) { + sprintf(debug_text, "%s", text); +} + +// a function that returns a pointer to the string +char* get_debug_text_ep_in() { + return debug_text_ep_in; +} +char* get_debug_text_ep_out() { + return debug_text_ep_out; +} +char* get_debug_text() { + return debug_text; +} + +// a function to convert an array of bytes to a string like 0x00, 0x01, 0x02, 0x03 +void hexArrayToString(const unsigned char* array, int size, char* outputBuffer, int bufferSize) { + int currentLength = 0; + + for(int i = 0; i < size; i++) { + // Get the two nibbles (4-bit halves) of the current byte + unsigned char highNibble = (array[i] >> 4) & 0x0F; // High 4 bits + unsigned char lowNibble = array[i] & 0x0F; // Low 4 bits + + // Convert nibbles to hex characters + char highChar = (highNibble < 10) ? ('0' + highNibble) : ('A' + highNibble - 10); + char lowChar = (lowNibble < 10) ? ('0' + lowNibble) : ('A' + lowNibble - 10); + + // Append "0x", the hex characters, and a space to the buffer + if(currentLength + 4 < + bufferSize) { // Ensure enough space for "0x", two chars, and a space + outputBuffer[currentLength++] = '0'; + outputBuffer[currentLength++] = 'x'; + outputBuffer[currentLength++] = highChar; + outputBuffer[currentLength++] = lowChar; + + // Add a space after each hex value, except the last one + if(i < size - 1 && currentLength < bufferSize) { + outputBuffer[currentLength++] = ' '; + } + } else { + break; // Stop if there isn't enough space + } + } + + // Null-terminate the string + if(currentLength < bufferSize) { + outputBuffer[currentLength] = '\0'; + } else if(bufferSize > 0) { + outputBuffer[bufferSize - 1] = '\0'; + } +} + // Function to parse a Frame into a Request void parse_request(Request* request, Frame* f) { if(request == NULL || f == NULL) return; @@ -119,13 +185,14 @@ int Event_build(Event* event, unsigned char* buf) { b[0] = event->pad; b[1] = 0; b[2] = event->index; - b[3] = event->dir & 0x1; // Direction is either 0 or 1 - memcpy(b + 4, event->uid, sizeof(event->uid)); + b[3] = event->dir; + memcpy(b + 4, event->uid, 6); // Update the event's frame event->frame.type = 0x56; - event->frame.len = 11; - memcpy(event->frame.payload, b, 11); + event->frame.len = sizeof(b); + // Copy the event's payload into the frame + memcpy(event->frame.payload, b, sizeof(b)); // Build the frame and return the size of the frame return build_frame(&event->frame, buf); @@ -372,18 +439,31 @@ void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid) { // emu->tokens[emu->token_count++] = new_token; - Event event; - Event_init(&event, NULL, 0); + Event* event = malloc(sizeof(Event)); + Event_init(event, NULL, 0); // set the pad - event.pad = pad; - event.index = index; - memcpy(event.uid, uid, sizeof(event.uid)); + event->pad = pad; + event->index = index; + + // index to string for set debug text + // char index_str[10]; + // snprintf(index_str, 10, "%d", event->index); + // // also add event->pad to the string + // char pad_str[10]; + // snprintf(pad_str, 10, "%d", event->pad); + // snprintf( + // index_str, sizeof(index_str) + sizeof(pad_str), "I: %d, P: %s", event->index, pad_str); + + // set_debug_text(index_str); + // return; + + memcpy(event->uid, uid, sizeof(event->uid)); // build the event unsigned char buf[HID_EP_SZ]; - int len = Event_build(&event, buf); + int len = Event_build(event, buf); if(len == 0) { set_debug_text("Length of event is 0"); return; @@ -391,9 +471,18 @@ void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid) { emu->token_count++; + // convert the buffer to a string + char string_debug[HID_EP_SZ]; + hexArrayToString(buf, HID_EP_SZ, string_debug, sizeof(string_debug)); + + // set the debug_text_ep_in to the string + memcpy(debug_text_ep_in, string_debug, sizeof(debug_text_ep_in)); + // send the event usbd_ep_write(usb_dev, HID_EP_IN, buf, sizeof(buf)); + free(event); + return; } @@ -495,41 +584,19 @@ static void hid_on_suspend(usbd_device* dev) { } } -// create a string variablethat contains the text: nothing to debug yet -char debug_text_ep_in[HID_EP_SZ] = "nothing"; - -// char debug_text_ep_out[] = "nothing to debug yet"; -char debug_text_ep_out[HID_EP_SZ] = "nothing"; - -char debug_text[HID_EP_SZ] = " "; - -void set_debug_text(char* text) { - sprintf(debug_text, "%s", text); -} - -// a function that returns a pointer to the string -char* get_debug_text_ep_in() { - return debug_text_ep_in; -} -char* get_debug_text_ep_out() { - return debug_text_ep_out; -} -char* get_debug_text() { - return debug_text; -} - void hid_in_callback(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(ep); UNUSED(event); + UNUSED(dev); // Handle the IN endpoint - unsigned char in_buf[HID_EP_SZ] = {0}; + // unsigned char in_buf[HID_EP_SZ] = {0}; - int len = usbd_ep_read(dev, HID_EP_IN, in_buf, HID_EP_SZ); + // int len = usbd_ep_read(dev, HID_EP_IN, in_buf, HID_EP_SZ); - sprintf(debug_text_ep_in, "%s", in_buf); + // sprintf(debug_text_ep_in, "%s", in_buf); - if(len <= 0) return; + // if(len <= 0) return; } uint32_t readUInt32LE(const unsigned char* buffer, int offset) { @@ -569,8 +636,12 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { // Read data from the OUT endpoint int32_t len = usbd_ep_read(dev, HID_EP_OUT, req_buf, HID_EP_SZ); - // Make from the data a string and save it to the debug_text_ep_in string + // char hexValues[HID_EP_SZ]; + // hexArrayToString(req_buf, sizeof(hexValues), hexValues, HID_EP_SZ); + + // Make from the data a string and save it to the debug_text_ep_out string sprintf(debug_text_ep_out, "%s", req_buf); + // sprintf(debug_text_ep_out, "%s", hexValues); if(len <= 0) return; @@ -648,6 +719,8 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { // furi_delay_ms(50); + connected_status = true; + usbd_ep_write(dev, HID_EP_IN, wake_payload, sizeof(wake_payload)); // response.payload_len = sizeof(wake_payload); diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index 3f845f6..43ff576 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -99,6 +99,8 @@ int build_response(Response* response, unsigned char* buf); ToyPadEmu* get_emulator(); +bool get_connected_status(); + #ifdef __cplusplus } #endif diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index 58fecfd..ae1601c 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -73,7 +73,7 @@ void send_minifigure(uint32_t minifigure_index) { set_debug_text(uid); // place the minifigure on the selected box - ToyPadEmu_place(get_emulator(), selectedBox, minifigure_index, uid); + ToyPadEmu_place(get_emulator(), selectedBox + 1, minifigure_index, uid); } bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { @@ -86,11 +86,6 @@ bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { instance->view, LDToyPadSceneEmulateModel * model, { - if(model->selected_minifigure_index != 0) { - send_minifigure(model->selected_minifigure_index); - model->selected_minifigure_index = 0; - } - // when the OK button is pressed, we want to switch to the minifigure selection screen for the selected box if(event->key == InputKeyOk) { if(event->type == InputTypePress) { @@ -209,14 +204,13 @@ void ldtoypad_scene_emulate_draw_callback(Canvas* canvas, void* _model) { // canvas_set_font(canvas, FontPrimary); // // Displaying the connected USB status - if(model->connected) { - // elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "USB Connected"); - elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "Awaiting"); + if(get_connected_status()) { + model->connected = true; + elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "USB Awoken"); + } else if(model->connected) { + elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "USB Connected"); } else { elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "USB Not Connected"); - // if(furi_hal_usb_get_config() == &usb_hid_ldtoypad) { - // model->connected = true; - // } } // // Testing pressing buttons @@ -277,12 +271,22 @@ static void ldtoypad_scene_emulate_draw_render_callback(Canvas* canvas, void* co model->usbDevice = get_usb_device(); model->connection_status = "USB device setting..."; } - if(model->usbDevice == NULL) { + if(model->connected) { + model->connection_status = "Connected to game"; + } else if(get_connected_status()) { + model->connected = true; + model->connection_status = "USB Awoken"; + } else if(model->usbDevice == NULL) { model->connection_status = "USB not yet connected"; } else if(!model->connected) { model->connection_status = "Trying to connect USB"; } + if(model->selected_minifigure_index != 0) { + send_minifigure(model->selected_minifigure_index); + model->selected_minifigure_index = 0; + } + // poll the USB status here // usbd_poll(model->usbDevice); @@ -309,16 +313,7 @@ static void ldtoypad_scene_emulate_draw_render_callback(Canvas* canvas, void* co // elements_button_center(canvas, "OK"); // elements_button_right(canvas, "Next"); - if(model->connected) { - // elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "USB Connected"); - elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, "Awaiting"); - } else { - elements_multiline_text_aligned( - canvas, 1, 1, AlignLeft, AlignTop, model->connection_status); - // if(furi_hal_usb_get_config() == &usb_hid_ldtoypad) { - // model->connected = true; - // } - } + elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, model->connection_status); // Testing pressing buttons if(model->ok_pressed) { @@ -342,12 +337,27 @@ static void ldtoypad_scene_emulate_draw_render_callback(Canvas* canvas, void* co elements_multiline_text_aligned(canvas, 1, 17, AlignLeft, AlignTop, "Debug: "); elements_multiline_text_aligned(canvas, 40, 17, AlignLeft, AlignTop, get_debug_text()); - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 0, 32, 120, 16); - canvas_set_color(canvas, ColorBlack); + // canvas_set_color(canvas, ColorWhite); + // canvas_draw_box(canvas, 0, 32, 120, 16); + // canvas_set_color(canvas, ColorBlack); + + // elements_multiline_text_aligned(canvas, 1, 33, AlignLeft, AlignTop, "ep_out: "); + // elements_multiline_text_aligned(canvas, 40, 33, AlignLeft, AlignTop, get_debug_text_ep_out()); + + if(get_debug_text_ep_in() != NULL && strcmp(get_debug_text_ep_in(), "nothing") != 0) { + canvas_set_color(canvas, ColorWhite); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + elements_multiline_text_aligned(canvas, 1, 1, AlignLeft, AlignTop, get_debug_text_ep_in()); + } + + // canvas_set_color(canvas, ColorWhite); + // canvas_draw_box(canvas, 0, 32, 120, 16); + // canvas_set_color(canvas, ColorBlack); - elements_multiline_text_aligned(canvas, 1, 33, AlignLeft, AlignTop, "ep_out: "); - elements_multiline_text_aligned(canvas, 40, 33, AlignLeft, AlignTop, get_debug_text_ep_out()); + // elements_multiline_text_aligned(canvas, 1, 33, AlignLeft, AlignTop, "send: "); + // elements_multiline_text_aligned(canvas, 40, 33, AlignLeft, AlignTop, get_debug_text_ep_in()); // ep in // canvas_set_color(canvas, ColorWhite); @@ -391,6 +401,25 @@ void ldtoypad_scene_emulate_exit_callback(void* context) { app->timer = NULL; } +static bool ldtoypad_scene_emulate_custom_event_callback(uint32_t event, void* context) { + LDToyPadSceneEmulate* scene = (LDToyPadSceneEmulate*)context; + switch(event) { + case 0: + // Redraw screen by passing true to last parameter of with_view_model. + { + bool redraw = true; + with_view_model( + ldtoypad_scene_emulate_get_view(scene), + LDToyPadSceneEmulateModel * _model, + { UNUSED(_model); }, + redraw); + return true; + } + default: + return false; + } +} + LDToyPadSceneEmulate* ldtoypad_scene_emulate_alloc(LDToyPadApp* new_app) { furi_assert(new_app); app = new_app; @@ -418,6 +447,8 @@ LDToyPadSceneEmulate* ldtoypad_scene_emulate_alloc(LDToyPadApp* new_app) { view_set_exit_callback(instance->view, ldtoypad_scene_emulate_exit_callback); + view_set_custom_callback(instance->view, ldtoypad_scene_emulate_custom_event_callback); + // Allocate the submenu // selectionMenu = submenu_alloc(); // view_set_previous_callback(submenu_get_view(selectionMenu), selectionMenu_prev_callback); @@ -462,7 +493,7 @@ void minifigures_submenu_callback(void* context, uint32_t index) { with_view_model( ldtoypad_scene_emulate_get_view(app->view_scene_emulate), LDToyPadSceneEmulateModel * model, - { model->selected_minifigure_index = index + 1; }, + { model->selected_minifigure_index = index; }, true); view_dispatcher_switch_to_view(app->view_dispatcher, ViewEmulate); From 8db8fc546d50cc58efd50cd8c8e6e9a92c78fa31 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Fri, 3 Jan 2025 00:05:24 +0100 Subject: [PATCH 24/27] Why does it not work? --- usb/usb_toypad.c | 124 +++++++++++++++++++----------------- usb/usb_toypad.h | 10 ++- views/EmulateToyPad_scene.c | 28 ++++---- 3 files changed, 82 insertions(+), 80 deletions(-) diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index cfef730..9e5189a 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -46,7 +46,7 @@ bool get_connected_status() { } // create a string variablethat contains the text: nothing to debug yet -char debug_text_ep_in[HID_EP_SZ] = "nothing"; +char debug_text_ep_in[HID_EP_SZ * 4] = "nothing"; // char debug_text_ep_out[] = "nothing to debug yet"; char debug_text_ep_out[HID_EP_SZ] = "nothing"; @@ -128,12 +128,16 @@ void parse_frame(Frame* frame, unsigned char* buf, int len) { } // Function to calculate checksum -unsigned char calculate_checksum(unsigned char* buf, int len) { - unsigned char sum = 0; - for(int i = 0; i < len; i++) { - sum += buf[i]; +void calculate_checksum(uint8_t* buf, int length) { + uint8_t checksum = 0; + + // Calculate checksum (up to 'length') + for(int i = 0; i < length; i++) { + checksum = (checksum + buf[i]) % 256; } - return sum % 256; + + // Assign checksum to the last position + buf[length] = checksum; } // Function to build a Frame into a buffer @@ -141,7 +145,8 @@ int build_frame(Frame* frame, unsigned char* buf) { buf[0] = frame->type; buf[1] = frame->len; memcpy(buf + 2, frame->payload, frame->len); - buf[frame->len + 2] = calculate_checksum(buf, frame->len + 2); + // buf[frame->len + 2] = calculate_checksum(buf, frame->len + 2); + calculate_checksum(buf, frame->len + 2); return frame->len + 3; } @@ -163,39 +168,64 @@ int build_response(Response* response, unsigned char* buf) { } void Event_init(Event* event, unsigned char* data, int len) { - if(data && len > 0) { - Frame frame; - parse_frame(&frame, data, len); - event->pad = frame.payload[0]; - event->index = frame.payload[2]; - event->dir = frame.payload[3]; - memcpy(event->uid, frame.payload + 4, 16); - event->frame = frame; - } else { - event->pad = 0; - event->index = 0; - event->dir = 0; - memset(event->uid, 0, sizeof(event->uid)); - } + UNUSED(data); + UNUSED(len); + // if(data && len > 0) { + // Frame frame; + // parse_frame(&frame, data, len); + // event->pad = frame.payload[0]; + // event->index = frame.payload[2]; + // event->dir = frame.payload[3]; + // memcpy(event->uid, frame.payload + 4, 16); + // event->frame = frame; + // } else { + // event->pad = 0; + // event->index = 0; + // event->dir = 0; + // memset(event->uid, 0, sizeof(event->uid)); + // memset(&event->frame, 0, sizeof(event->frame)); + // } + event->pad = 0; + event->index = 0; + event->dir = 0; + memset(event->uid, 0, sizeof(event->uid)); + memset(&event->frame, 0, sizeof(event->frame)); } // Function to build the event into a frame int Event_build(Event* event, unsigned char* buf) { + // fill the buffer with empty bytes + memset(buf, 0, HID_EP_SZ); + memset(event->frame.payload, 0, sizeof(event->frame.payload)); + unsigned char b[11] = {0}; b[0] = event->pad; b[1] = 0; b[2] = event->index; b[3] = event->dir; - memcpy(b + 4, event->uid, 6); + memcpy(b + 4, event->uid, 7); + // for(int i = 0; i < 6; i++) { + // b[i + 4] = event->uid[i]; + // } // Update the event's frame event->frame.type = 0x56; - event->frame.len = sizeof(b); + // event->frame.len = sizeof(b); // Copy the event's payload into the frame memcpy(event->frame.payload, b, sizeof(b)); + // event->frame.len = sizeof(event->frame.payload); + event->frame.len = 11; + // Build the frame and return the size of the frame - return build_frame(&event->frame, buf); + // return build_frame(&event->frame, buf); + + buf[0] = event->frame.type; + buf[1] = event->frame.len; + memcpy(buf + 2, event->frame.payload, event->frame.len); + // buf[event->frame.len + 2] = calculate_checksum(buf, event->frame.len + 2); + calculate_checksum(buf, event->frame.len + 2); + return event->frame.len + 3; } /* String descriptors */ @@ -385,13 +415,13 @@ ToyPadEmu* get_emulator() { Burtle* burtle; // Define the Burtle object // Generate random UID -void ToyPadEmu_randomUID(char* uid) { - // srand(furi_get_tick()); // Set the seed to random value +void ToyPadEmu_randomUID(unsigned char* uid) { + srand(furi_get_tick()); // Set the seed to random value uid[0] = 0x04; // vendor id = NXP - uid[6] = 0x80; // Set last byte to 0x80 - for(int i = 1; i < 6; i++) { + for(int i = 1; i < 6; i++) { // Fill the middle 4 bytes uid[i] = rand() % 256; } + uid[6] = 0x80; // Set the last byte to 0x80 } void ToyPadEmu_init(ToyPadEmu* emu) { @@ -428,16 +458,15 @@ Token createCharacter(int id, const char* uid) { } // Add a token to a pad -void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid) { +void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, unsigned char* uid) { if(emu->token_count > 7) { return; } - - // Token new_token = createCharacter(index, uid); - // new_token.index = index; - // new_token.pad = pad; - - // emu->tokens[emu->token_count++] = new_token; + // when uid is null, generate a random one + if(uid == NULL) { + uid = malloc(7); // Dynamically allocate memory for uid + ToyPadEmu_randomUID(uid); + } Event* event = malloc(sizeof(Event)); Event_init(event, NULL, 0); @@ -445,19 +474,6 @@ void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid) { // set the pad event->pad = pad; event->index = index; - - // index to string for set debug text - // char index_str[10]; - // snprintf(index_str, 10, "%d", event->index); - // // also add event->pad to the string - // char pad_str[10]; - // snprintf(pad_str, 10, "%d", event->pad); - // snprintf( - // index_str, sizeof(index_str) + sizeof(pad_str), "I: %d, P: %s", event->index, pad_str); - - // set_debug_text(index_str); - // return; - memcpy(event->uid, uid, sizeof(event->uid)); // build the event @@ -465,16 +481,15 @@ void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid) { int len = Event_build(event, buf); if(len == 0) { - set_debug_text("Length of event is 0"); + // set_debug_text("Length of event is 0"); return; } emu->token_count++; // convert the buffer to a string - char string_debug[HID_EP_SZ]; + char string_debug[HID_EP_SZ * 4]; hexArrayToString(buf, HID_EP_SZ, string_debug, sizeof(string_debug)); - // set the debug_text_ep_in to the string memcpy(debug_text_ep_in, string_debug, sizeof(debug_text_ep_in)); @@ -482,6 +497,7 @@ void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid) { usbd_ep_write(usb_dev, HID_EP_IN, buf, sizeof(buf)); free(event); + free(uid); // Free the dynamically allocated uid return; } @@ -853,12 +869,6 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { usbd_ep_write(dev, HID_EP_IN, res_buf, sizeof(res_buf)); } -// function to write a buffer to the HID EP_IN -void write_to_ep_in(unsigned char* buffer) { - sprintf(debug_text, "Writing to EP_IN"); - usbd_ep_write(usb_dev, HID_EP_IN, buffer, sizeof(buffer)); -} - /* Configure endpoints */ static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) { switch(cfg) { diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index 43ff576..411e803 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -13,7 +13,7 @@ typedef struct { int index; int id; int pad; - char uid[6]; + char uid[7]; // unsigned char data[180]; } Token; @@ -57,7 +57,7 @@ typedef struct { int pad; int index; int dir; - char uid[6]; + int uid[7]; } Event; extern FuriHalUsbInterface usb_hid_ldtoypad; @@ -81,18 +81,16 @@ void set_debug_text(char* text); usbd_device* get_usb_device(); -void ToyPadEmu_randomUID(char* uid); +void ToyPadEmu_randomUID(unsigned char* uid); Token createCharacter(int id, const char* uid); -void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, const char* uid); +void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, unsigned char* uid); void Event_init(Event* event, unsigned char* data, int len); int Event_build(Event* event, unsigned char* buf); -void write_to_ep_in(unsigned char* buffer); - int build_frame(Frame* frame, unsigned char* buf); int build_response(Response* response, unsigned char* buf); diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index ae1601c..6e3fccb 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -66,14 +66,10 @@ uint8_t selectedBox = 0; // Variable to keep track of which toypad box is select // } void send_minifigure(uint32_t minifigure_index) { - // create a minifigure from the selected minifigure - char uid[6]; - ToyPadEmu_randomUID(uid); - - set_debug_text(uid); + // set_debug_text(uid); // place the minifigure on the selected box - ToyPadEmu_place(get_emulator(), selectedBox + 1, minifigure_index, uid); + ToyPadEmu_place(get_emulator(), selectedBox + 1, minifigure_index, NULL); } bool ldtoypad_scene_emulate_input_callback(InputEvent* event, void* context) { @@ -282,11 +278,6 @@ static void ldtoypad_scene_emulate_draw_render_callback(Canvas* canvas, void* co model->connection_status = "Trying to connect USB"; } - if(model->selected_minifigure_index != 0) { - send_minifigure(model->selected_minifigure_index); - model->selected_minifigure_index = 0; - } - // poll the USB status here // usbd_poll(model->usbDevice); @@ -483,17 +474,20 @@ void minifigures_submenu_callback(void* context, uint32_t index) { LDToyPadApp* app = (LDToyPadApp*)context; // print index of selected minifigure as debug text - char debug_text[10]; - // convert the long unsigned int to a string - snprintf(debug_text, 10, "%ld", index); - // set the debug text - set_debug_text(debug_text); + // char debug_text[10]; + // // convert the long unsigned int to a string + // snprintf(debug_text, 10, "%ld", index); + // // set the debug text + // set_debug_text(debug_text); // set current view to minifigure number to the selected index with_view_model( ldtoypad_scene_emulate_get_view(app->view_scene_emulate), LDToyPadSceneEmulateModel * model, - { model->selected_minifigure_index = index; }, + { + model->selected_minifigure_index = index; + send_minifigure(model->selected_minifigure_index); + }, true); view_dispatcher_switch_to_view(app->view_dispatcher, ViewEmulate); From 6487ba79a49b283456c59df1234ae0fd0a0289e8 Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Fri, 3 Jan 2025 00:49:22 +0100 Subject: [PATCH 25/27] Update usb_toypad.c --- usb/usb_toypad.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index 9e5189a..71aa089 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -225,7 +225,7 @@ int Event_build(Event* event, unsigned char* buf) { memcpy(buf + 2, event->frame.payload, event->frame.len); // buf[event->frame.len + 2] = calculate_checksum(buf, event->frame.len + 2); calculate_checksum(buf, event->frame.len + 2); - return event->frame.len + 3; + return event->frame.len + 2; } /* String descriptors */ @@ -416,9 +416,9 @@ Burtle* burtle; // Define the Burtle object // Generate random UID void ToyPadEmu_randomUID(unsigned char* uid) { - srand(furi_get_tick()); // Set the seed to random value uid[0] = 0x04; // vendor id = NXP for(int i = 1; i < 6; i++) { // Fill the middle 4 bytes + srand(furi_get_tick()); // Set the seed to random value uid[i] = rand() % 256; } uid[6] = 0x80; // Set the last byte to 0x80 @@ -728,13 +728,12 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { // 0x34, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + // I don't know why this is, but it seems to work. Found it in a log file of https://github.com/woodenphone/lego_dimensions_protocol/blob/master/logs/USB%20capture%20snippet.txt#L31 unsigned char wake_payload[HID_EP_SZ] = {0x55, 0x19, 0x01, 0x00, 0x2f, 0x02, 0x01, 0x02, 0x02, 0x04, 0x02, 0xf5, 0x00, 0x19, 0x8b, 0x54, 0x4d, 0xb4, 0xcd, 0xae, 0x45, 0x24, 0x80, 0x0e, 0x00, 0xf0, 0x25, 0x20, 0x00, 0x00, 0x00, 0x00}; - // furi_delay_ms(50); - connected_status = true; usbd_ep_write(dev, HID_EP_IN, wake_payload, sizeof(wake_payload)); @@ -774,6 +773,38 @@ void hid_out_callback(usbd_device* dev, uint8_t event, uint8_t ep) { break; case CMD_WRITE: sprintf(debug_text, "CMD_WRITE"); + + // int ind = request.payload[0]; + // int page = request.payload[1]; + + // // data + // unsigned char data[32]; + // memcpy(data, request.payload + 2, 32); + + // // The ID is stored in page 24 + // if(page == 24 || page == 36) { + // uint16_t id = data[0] | (data[1] << 8); // Read 16-bit integer (little-endian) + // // const char* name = getNameFromID(id); + // } + + // // Prepare response payload + // response.payload[0] = 0x00; + + // Token* token = findTokenByIndex(&tp, ind); + // if(token) { + // // Copy bytes from req.payload to token.token (4 * page, from position 2 to 6) + // int start = 2; + // int end = 6; + // if(start < req->payload_length && end <= req->payload_length) { + // memcpy(&token->token[4 * page], &req->payload[start], end - start); + // printf("Token updated for index %d, page %d\n", ind, page); + // } else { + // printf("Invalid range for copy operation\n"); + // } + // } else { + // printf("Token not found for index: %d\n", ind); + // } + break; case CMD_CHAL: sprintf(debug_text, "CMD_CHAL"); From 5424385f84bb94ff0f9f1d5fe133af999f9372aa Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Fri, 3 Jan 2025 11:43:26 +0100 Subject: [PATCH 26/27] Why does it detect only for a second??? --- usb/usb_toypad.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index 71aa089..4b9c84c 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -128,16 +128,20 @@ void parse_frame(Frame* frame, unsigned char* buf, int len) { } // Function to calculate checksum -void calculate_checksum(uint8_t* buf, int length) { +void calculate_checksum(uint8_t* buf, int length, int place) { uint8_t checksum = 0; + if(place == -1) { + place = length; + } + // Calculate checksum (up to 'length') for(int i = 0; i < length; i++) { checksum = (checksum + buf[i]) % 256; } // Assign checksum to the last position - buf[length] = checksum; + buf[place] = checksum; } // Function to build a Frame into a buffer @@ -146,7 +150,7 @@ int build_frame(Frame* frame, unsigned char* buf) { buf[1] = frame->len; memcpy(buf + 2, frame->payload, frame->len); // buf[frame->len + 2] = calculate_checksum(buf, frame->len + 2); - calculate_checksum(buf, frame->len + 2); + calculate_checksum(buf, frame->len + 2, -1); return frame->len + 3; } @@ -224,7 +228,7 @@ int Event_build(Event* event, unsigned char* buf) { buf[1] = event->frame.len; memcpy(buf + 2, event->frame.payload, event->frame.len); // buf[event->frame.len + 2] = calculate_checksum(buf, event->frame.len + 2); - calculate_checksum(buf, event->frame.len + 2); + calculate_checksum(buf, event->frame.len, event->frame.len + 2); return event->frame.len + 2; } @@ -485,13 +489,13 @@ void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, unsigned char* uid) { return; } - emu->token_count++; + // emu->token_count++; // convert the buffer to a string - char string_debug[HID_EP_SZ * 4]; - hexArrayToString(buf, HID_EP_SZ, string_debug, sizeof(string_debug)); - // set the debug_text_ep_in to the string - memcpy(debug_text_ep_in, string_debug, sizeof(debug_text_ep_in)); + // char string_debug[HID_EP_SZ * 4]; + // hexArrayToString(buf, HID_EP_SZ, string_debug, sizeof(string_debug)); + // // set the debug_text_ep_in to the string + // memcpy(debug_text_ep_in, string_debug, sizeof(debug_text_ep_in)); // send the event usbd_ep_write(usb_dev, HID_EP_IN, buf, sizeof(buf)); From 9659409c65b2536c70a7d57a43fdff7719405e7e Mon Sep 17 00:00:00 2001 From: SegerEnd Date: Fri, 3 Jan 2025 15:03:01 +0100 Subject: [PATCH 27/27] Debugging --- usb/usb_toypad.c | 15 +++++++++------ usb/usb_toypad.h | 5 +++-- views/EmulateToyPad_scene.c | 25 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/usb/usb_toypad.c b/usb/usb_toypad.c index 4b9c84c..54d7e37 100644 --- a/usb/usb_toypad.c +++ b/usb/usb_toypad.c @@ -171,9 +171,7 @@ int build_response(Response* response, unsigned char* buf) { return build_frame(&response->frame, buf); } -void Event_init(Event* event, unsigned char* data, int len) { - UNUSED(data); - UNUSED(len); +void Event_init(Event* event) { // if(data && len > 0) { // Frame frame; // parse_frame(&frame, data, len); @@ -453,10 +451,15 @@ void ToyPadEmu_init(ToyPadEmu* emu) { // memcpy(emu->tea_key, default_tea_key, sizeof(emu->tea_key)); } -Token createCharacter(int id, const char* uid) { +Token createCharacter(int id, unsigned char* uid) { Token token; // Declare a token structure // memset(token.data, 0, sizeof(token.data)); // Fill the array with zeros - strncpy(token.uid, uid, sizeof(token.uid)); // Set the UID + + if(uid == NULL) { + uid = malloc(7); // Dynamically allocate memory for uid + ToyPadEmu_randomUID(uid); + } + token.id = id; // Set the ID return token; // Return the created token } @@ -473,7 +476,7 @@ void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, unsigned char* uid) { } Event* event = malloc(sizeof(Event)); - Event_init(event, NULL, 0); + Event_init(event); // set the pad event->pad = pad; diff --git a/usb/usb_toypad.h b/usb/usb_toypad.h index 411e803..8ea4a1a 100644 --- a/usb/usb_toypad.h +++ b/usb/usb_toypad.h @@ -15,6 +15,7 @@ typedef struct { int pad; char uid[7]; // unsigned char data[180]; + char token[32]; } Token; typedef struct { @@ -83,11 +84,11 @@ usbd_device* get_usb_device(); void ToyPadEmu_randomUID(unsigned char* uid); -Token createCharacter(int id, const char* uid); +Token createCharacter(int id, unsigned char* uid); void ToyPadEmu_place(ToyPadEmu* emu, int pad, int index, unsigned char* uid); -void Event_init(Event* event, unsigned char* data, int len); +void Event_init(Event* event); int Event_build(Event* event, unsigned char* buf); diff --git a/views/EmulateToyPad_scene.c b/views/EmulateToyPad_scene.c index 6e3fccb..d74100d 100644 --- a/views/EmulateToyPad_scene.c +++ b/views/EmulateToyPad_scene.c @@ -269,6 +269,31 @@ static void ldtoypad_scene_emulate_draw_render_callback(Canvas* canvas, void* co } if(model->connected) { model->connection_status = "Connected to game"; + + // send 56 0b 01 00 00 00 04 9a 74 6a 0b 40 80 a9 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + // char buffer[32]; + + // memset(buffer, 0, sizeof(buffer)); + + // // set the data to the buffer + // buffer[0] = 0x56; + // buffer[1] = 0x0b; + // buffer[2] = 0x01; + // buffer[3] = 0x00; + // buffer[4] = 0x00; + // buffer[5] = 0x00; + // buffer[6] = 0x04; + // buffer[7] = 0x9a; + // buffer[8] = 0x74; + // buffer[9] = 0x6a; + // buffer[10] = 0x0b; + // buffer[11] = 0x40; + // buffer[12] = 0x80; + // buffer[13] = 0xa9; + + // usbd_ep_write(model->usbDevice, 0x81, buffer, sizeof(buffer)); + } else if(get_connected_status()) { model->connected = true; model->connection_status = "USB Awoken";