From 003a618b19b3b9d60f3cbd0ccd643a3684701a63 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 26 Sep 2024 22:14:20 +0300 Subject: [PATCH 01/10] Initial commit of Nfc Eink --- nfc_eink/application.fam | 16 + nfc_eink/icon.png | Bin 0 -> 304 bytes nfc_eink/nfc_eink_app.c | 275 +++++++++++ nfc_eink/nfc_eink_app.h | 129 +++++ .../goodisplay/eink_goodispaly_poller.c | 456 ++++++++++++++++++ .../goodisplay/eink_goodisplay.c | 68 +++ .../goodisplay/eink_goodisplay.h | 2 + .../goodisplay/eink_goodisplay_config.c | 85 ++++ .../goodisplay/eink_goodisplay_config.h | 131 +++++ .../goodisplay/eink_goodisplay_i.h | 134 +++++ .../goodisplay/eink_goodisplay_listener.c | 371 ++++++++++++++ nfc_eink/nfc_eink_screen/nfc_eink_screen.c | 411 ++++++++++++++++ nfc_eink/nfc_eink_screen/nfc_eink_screen.h | 41 ++ nfc_eink/nfc_eink_screen/nfc_eink_screen_i.h | 40 ++ .../nfc_eink_screen/nfc_eink_screen_infos.c | 184 +++++++ .../nfc_eink_screen/nfc_eink_screen_infos.h | 88 ++++ nfc_eink/nfc_eink_screen/nfc_eink_types.h | 25 + .../waveshare/eink_waveshare.c | 59 +++ .../waveshare/eink_waveshare.h | 3 + .../waveshare/eink_waveshare_config.c | 43 ++ .../waveshare/eink_waveshare_config.h | 16 + .../waveshare/eink_waveshare_i.h | 84 ++++ .../waveshare/eink_waveshare_listener.c | 232 +++++++++ .../waveshare/eink_waveshare_poller.c | 300 ++++++++++++ nfc_eink/scenes/scene_choose_manufacturer.c | 44 ++ nfc_eink/scenes/scene_choose_screen.c | 80 +++ nfc_eink/scenes/scene_config.h | 17 + nfc_eink/scenes/scene_delete.c | 52 ++ nfc_eink/scenes/scene_delete_success.c | 40 ++ nfc_eink/scenes/scene_emulate.c | 104 ++++ nfc_eink/scenes/scene_error.c | 42 ++ nfc_eink/scenes/scene_exit_confirm.c | 48 ++ nfc_eink/scenes/scene_file_select.c | 22 + nfc_eink/scenes/scene_info.c | 55 +++ nfc_eink/scenes/scene_save_name.c | 71 +++ nfc_eink/scenes/scene_save_success.c | 42 ++ nfc_eink/scenes/scene_screen_menu.c | 96 ++++ nfc_eink/scenes/scene_settings.c | 76 +++ nfc_eink/scenes/scene_show_image.c | 51 ++ nfc_eink/scenes/scene_start.c | 54 +++ nfc_eink/scenes/scene_write.c | 148 ++++++ nfc_eink/scenes/scene_write_done.c | 40 ++ nfc_eink/scenes/scenes.c | 30 ++ nfc_eink/scenes/scenes.h | 29 ++ nfc_eink/views/eink_progress.c | 93 ++++ nfc_eink/views/eink_progress.h | 13 + nfc_eink/views/image_scroll.c | 165 +++++++ nfc_eink/views/image_scroll.h | 18 + 48 files changed, 4623 insertions(+) create mode 100644 nfc_eink/application.fam create mode 100644 nfc_eink/icon.png create mode 100644 nfc_eink/nfc_eink_app.c create mode 100644 nfc_eink/nfc_eink_app.h create mode 100644 nfc_eink/nfc_eink_screen/goodisplay/eink_goodispaly_poller.c create mode 100644 nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay.c create mode 100644 nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay.h create mode 100644 nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.c create mode 100644 nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.h create mode 100644 nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_i.h create mode 100644 nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_listener.c create mode 100644 nfc_eink/nfc_eink_screen/nfc_eink_screen.c create mode 100644 nfc_eink/nfc_eink_screen/nfc_eink_screen.h create mode 100644 nfc_eink/nfc_eink_screen/nfc_eink_screen_i.h create mode 100644 nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.c create mode 100644 nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.h create mode 100644 nfc_eink/nfc_eink_screen/nfc_eink_types.h create mode 100644 nfc_eink/nfc_eink_screen/waveshare/eink_waveshare.c create mode 100644 nfc_eink/nfc_eink_screen/waveshare/eink_waveshare.h create mode 100644 nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.c create mode 100644 nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.h create mode 100644 nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_i.h create mode 100644 nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_listener.c create mode 100644 nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_poller.c create mode 100644 nfc_eink/scenes/scene_choose_manufacturer.c create mode 100644 nfc_eink/scenes/scene_choose_screen.c create mode 100644 nfc_eink/scenes/scene_config.h create mode 100644 nfc_eink/scenes/scene_delete.c create mode 100644 nfc_eink/scenes/scene_delete_success.c create mode 100644 nfc_eink/scenes/scene_emulate.c create mode 100644 nfc_eink/scenes/scene_error.c create mode 100644 nfc_eink/scenes/scene_exit_confirm.c create mode 100644 nfc_eink/scenes/scene_file_select.c create mode 100644 nfc_eink/scenes/scene_info.c create mode 100644 nfc_eink/scenes/scene_save_name.c create mode 100644 nfc_eink/scenes/scene_save_success.c create mode 100644 nfc_eink/scenes/scene_screen_menu.c create mode 100644 nfc_eink/scenes/scene_settings.c create mode 100644 nfc_eink/scenes/scene_show_image.c create mode 100644 nfc_eink/scenes/scene_start.c create mode 100644 nfc_eink/scenes/scene_write.c create mode 100644 nfc_eink/scenes/scene_write_done.c create mode 100644 nfc_eink/scenes/scenes.c create mode 100644 nfc_eink/scenes/scenes.h create mode 100644 nfc_eink/views/eink_progress.c create mode 100644 nfc_eink/views/eink_progress.h create mode 100644 nfc_eink/views/image_scroll.c create mode 100644 nfc_eink/views/image_scroll.h diff --git a/nfc_eink/application.fam b/nfc_eink/application.fam new file mode 100644 index 00000000..b228c35a --- /dev/null +++ b/nfc_eink/application.fam @@ -0,0 +1,16 @@ +App( + appid="nfc_eink", + name="NFC-Eink", + apptype=FlipperAppType.EXTERNAL, + targets=["f7"], + entry_point="nfc_eink", + icon="A_NFC_14", + stack_size=5 * 1024, + order=30, + sources=[ + "*.c*" + ], + fap_libs=["assets", "mbedtls"], + fap_icon="icon.png", + fap_category="NFC", +) \ No newline at end of file diff --git a/nfc_eink/icon.png b/nfc_eink/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6bc027111a7517a8cd53bb511c064b834027c5a9 GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)b(dHL6nbwD9yPZ!4!j_b)k${QZ;XFmLn zjqNsD+YPq1J8W%_*aXBie!OR3*tC!PwU_7Q9H4U564!{5l*E!$tK_0oAjM#0U}UIk zV5)0q5@Kj%Wo%$&Y@uynU}a#izM}XGiiX_$l+3hBs0L%8o)7~QD^p9LQiz6svOM}g O4Gf;HelF{r5}E+GUQp8j literal 0 HcmV?d00001 diff --git a/nfc_eink/nfc_eink_app.c b/nfc_eink/nfc_eink_app.c new file mode 100644 index 00000000..45179fac --- /dev/null +++ b/nfc_eink/nfc_eink_app.c @@ -0,0 +1,275 @@ +#include "nfc_eink_app.h" +#include +#include + +#define TAG "NfcEinkApp" + +#define NFC_EINK_SETTINGS_PATH NFC_EINK_APP_FOLDER "/.eink.settings" +#define NFC_EINK_SETTINGS_VERSION (1) +#define NFC_EINK_SETTINGS_MAGIC (0x1B) + +static bool nfc_is_hal_ready(void) { + if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) { + // No connection to the chip, show an error screen + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Error: NFC Chip Failed", 64, 0, AlignCenter, AlignTop); + dialog_message_set_text( + message, "Send error photo via\nsupport.flipper.net", 0, 63, AlignLeft, AlignBottom); + dialog_message_set_icon(message, &I_err_09, 128 - 25, 64 - 25); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + return false; + } else { + return true; + } +} + +static bool nfc_eink_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + NfcEinkApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool nfc_eink_app_back_event_callback(void* context) { + furi_assert(context); + NfcEinkApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void nfc_eink_app_tick_event_callback(void* context) { + furi_assert(context); + NfcEinkApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +static NfcEinkApp* nfc_eink_app_alloc() { + NfcEinkApp* instance = malloc(sizeof(NfcEinkApp)); + + // Open GUI record + instance->gui = furi_record_open(RECORD_GUI); + + // Open Dialogs record + instance->dialogs = furi_record_open(RECORD_DIALOGS); + + // Open Notification record + instance->notifications = furi_record_open(RECORD_NOTIFICATION); + + instance->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance); + view_dispatcher_set_custom_event_callback( + instance->view_dispatcher, nfc_eink_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + instance->view_dispatcher, nfc_eink_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + instance->view_dispatcher, nfc_eink_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui( + instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); + + // Submenu + instance->submenu = submenu_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcEinkViewMenu, submenu_get_view(instance->submenu)); + + // Popup + instance->popup = popup_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcEinkViewPopup, popup_get_view(instance->popup)); + + // Progress bar + instance->eink_progress = eink_progress_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, + NfcEinkViewProgress, + eink_progress_get_view(instance->eink_progress)); + + // Dialog + instance->dialog_ex = dialog_ex_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcEinkViewDialogEx, dialog_ex_get_view(instance->dialog_ex)); + + // Text Input + instance->text_input = text_input_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, + NfcEinkViewTextInput, + text_input_get_view(instance->text_input)); + + // Custom Widget + instance->widget = widget_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcEinkViewWidget, widget_get_view(instance->widget)); + + // Variable item list + instance->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, + NfcEinkViewVarItemList, + variable_item_list_get_view(instance->var_item_list)); + + // Image scroll + instance->image_scroll = image_scroll_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, + NfcEinkViewImageScroll, + image_scroll_get_view(instance->image_scroll)); + + instance->scene_manager = scene_manager_alloc(&nfc_eink_scene_handlers, instance); + instance->nfc = nfc_alloc(); + instance->file_path = furi_string_alloc(); + instance->file_name = furi_string_alloc(); + return instance; +} + +static void nfc_eink_app_free(NfcEinkApp* instance) { + furi_assert(instance); + + nfc_free(instance->nfc); + scene_manager_free(instance->scene_manager); + + // Submenu + view_dispatcher_remove_view(instance->view_dispatcher, NfcEinkViewMenu); + submenu_free(instance->submenu); + + // DialogEx + view_dispatcher_remove_view(instance->view_dispatcher, NfcEinkViewDialogEx); + dialog_ex_free(instance->dialog_ex); + + // Popup + view_dispatcher_remove_view(instance->view_dispatcher, NfcEinkViewPopup); + popup_free(instance->popup); + + // TextInput + view_dispatcher_remove_view(instance->view_dispatcher, NfcEinkViewTextInput); + text_input_free(instance->text_input); + + // Progress + view_dispatcher_remove_view(instance->view_dispatcher, NfcEinkViewProgress); + eink_progress_free(instance->eink_progress); + + // Custom Widget + view_dispatcher_remove_view(instance->view_dispatcher, NfcEinkViewWidget); + widget_free(instance->widget); + + // Var Item List + view_dispatcher_remove_view(instance->view_dispatcher, NfcEinkViewVarItemList); + variable_item_list_free(instance->var_item_list); + + view_dispatcher_remove_view(instance->view_dispatcher, NfcEinkViewImageScroll); + image_scroll_free(instance->image_scroll); + + view_dispatcher_free(instance->view_dispatcher); + + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + + furi_string_free(instance->file_path); + furi_string_free(instance->file_name); + + instance->dialogs = NULL; + instance->gui = NULL; + instance->notifications = NULL; + free(instance); +} + +static void nfc_eink_make_app_folders(const NfcEinkApp* instance) { + furi_assert(instance); + + Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage_simply_mkdir(storage, NFC_EINK_APP_FOLDER)) { + dialog_message_show_storage_error(instance->dialogs, "Cannot create\napp folder"); + } + furi_record_close(RECORD_STORAGE); +} + +static void nfc_eink_load_settings(NfcEinkApp* instance) { + NfcEinkSettings settings = {0}; + + if(!saved_struct_load( + NFC_EINK_SETTINGS_PATH, + &settings, + sizeof(NfcEinkSettings), + NFC_EINK_SETTINGS_MAGIC, + NFC_EINK_SETTINGS_VERSION)) { + FURI_LOG_D(TAG, "Failed to load settings, using defaults"); + nfc_eink_save_settings(instance); + } + + instance->settings.invert_image = settings.invert_image; + instance->settings.write_mode = settings.write_mode; +} + +void nfc_eink_save_settings(NfcEinkApp* instance) { + NfcEinkSettings settings = { + .invert_image = instance->settings.invert_image, + .write_mode = instance->settings.write_mode, + }; + + if(!saved_struct_save( + NFC_EINK_SETTINGS_PATH, + &settings, + sizeof(NfcEinkSettings), + NFC_EINK_SETTINGS_MAGIC, + NFC_EINK_SETTINGS_VERSION)) { + FURI_LOG_E(TAG, "Failed to save settings"); + } +} + +bool nfc_eink_load_from_file_select(NfcEinkApp* instance) { + furi_assert(instance); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, NFC_EINK_APP_EXTENSION, &I_Nfc_10px); + browser_options.base_path = NFC_EINK_APP_FOLDER; + browser_options.hide_dot_files = true; + + FuriString* tmp = furi_string_alloc(); + furi_string_reset(instance->file_path); + + bool success = false; + do { + // Input events and views are managed by file_browser + furi_string_printf(tmp, NFC_EINK_APP_FOLDER); + + if(!dialog_file_browser_show(instance->dialogs, instance->file_path, tmp, &browser_options)) + break; + + success = nfc_eink_screen_load_info( + furi_string_get_cstr(instance->file_path), &instance->info_temp); + path_extract_filename(instance->file_path, instance->file_name, false); + } while(!success); + + furi_string_free(tmp); + + return success; +} + +void nfc_eink_blink_emulate_start(NfcEinkApp* app) { + notification_message(app->notifications, &sequence_blink_start_magenta); +} + +void nfc_eink_blink_write_start(NfcEinkApp* app) { + notification_message(app->notifications, &sequence_blink_start_cyan); +} + +void nfc_eink_blink_stop(NfcEinkApp* app) { + notification_message(app->notifications, &sequence_blink_stop); +} + +int32_t nfc_eink(/* void* p */) { + if(!nfc_is_hal_ready()) return 0; + + NfcEinkApp* app = nfc_eink_app_alloc(); + + nfc_eink_make_app_folders(app); + nfc_eink_load_settings(app); + scene_manager_next_scene(app->scene_manager, NfcEinkAppSceneStart); + + view_dispatcher_run(app->view_dispatcher); + + nfc_eink_app_free(app); + return 0; +} diff --git a/nfc_eink/nfc_eink_app.h b/nfc_eink/nfc_eink_app.h new file mode 100644 index 00000000..cf8478e2 --- /dev/null +++ b/nfc_eink/nfc_eink_app.h @@ -0,0 +1,129 @@ +#pragma once + +#include +#include + +#include +#include + +#include + +#include <../applications/services/notification/notification_messages.h> + +#include <../applications/services/gui/gui.h> +#include <../applications/services/gui/view.h> +#include <../applications/services/gui/view_dispatcher.h> +#include <../applications/services/gui/scene_manager.h> +#include <../applications/services/gui/modules/submenu.h> +#include <../applications/services/gui/modules/empty_screen.h> +#include <../applications/services/gui/modules/dialog_ex.h> +#include <../applications/services/gui/modules/popup.h> +#include <../applications/services/gui/modules/loading.h> +#include <../applications/services/gui/modules/text_input.h> +#include <../applications/services/gui/modules/byte_input.h> +#include <../applications/services/gui/modules/text_box.h> +#include <../applications/services/gui/modules/widget.h> +#include <../applications/services/gui/modules/variable_item_list.h> + +#include <../applications/services/dialogs/dialogs.h> +#include <../applications/services/storage/storage.h> + +#include +#include +#include + +#include "nfc_eink_screen/nfc_eink_screen.h" +#include "scenes/scenes.h" + +#include "views/eink_progress.h" +#include "views/image_scroll.h" + +#define NFC_EINK_NAME_SIZE 22 +#define NFC_EINK_APP_EXTENSION ".eink" +#define NFC_EINK_APP_FOLDER_NAME "nfc_eink" + +#define NFC_EINK_APP_FOLDER EXT_PATH(NFC_EINK_APP_FOLDER_NAME) + +///TODO: remove all this shit below to _i.h file + +typedef enum { + NfcEinkAppCustomEventReserved = 100, + + NfcEinkAppCustomEventTextInputDone, + NfcEinkAppCustomEventTimerExpired, + NfcEinkAppCustomEventBlockProcessed, + NfcEinkAppCustomEventUpdating, + NfcEinkAppCustomEventProcessFinish, + NfcEinkAppCustomEventTargetDetected, + NfcEinkAppCustomEventTargetLost, + NfcEinkAppCustomEventUnknownError, + NfcEinkAppCustomEventExit, +} NfcEinkAppCustomEvents; + +typedef enum { + NfcEinkViewMenu, + NfcEinkViewDialogEx, + NfcEinkViewPopup, + NfcEinkViewLoading, + NfcEinkViewTextInput, + NfcEinkViewByteInput, + NfcEinkViewTextBox, + NfcEinkViewWidget, + NfcEinkViewVarItemList, + NfcEinkViewProgress, + NfcEinkViewImageScroll, +} NfcEinkView; + +typedef enum { + NfcEinkWriteModeStrict, + NfcEinkWriteModeResolution, + NfcEinkWriteModeManufacturer, + NfcEinkWriteModeFree +} NfcEinkWriteMode; + +typedef struct { + NfcEinkWriteMode write_mode; + bool invert_image; +} NfcEinkSettings; + +typedef struct { + Gui* gui; + DialogsApp* dialogs; + NotificationApp* notifications; + + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + + // Common Views + Submenu* submenu; + DialogEx* dialog_ex; + Popup* popup; + Loading* loading; + TextInput* text_input; + ByteInput* byte_input; + TextBox* text_box; + Widget* widget; + VariableItemList* var_item_list; + EinkProgress* eink_progress; + ImageScroll* image_scroll; + + Nfc* nfc; + + NfcListener* listener; + NfcPoller* poller; + NfcEinkScreen* screen; + const NfcEinkScreenInfo* info_temp; + EinkScreenInfoArray_t arr; + NfcEinkSettings settings; + + bool screen_loaded; + char text_store[50 + 1]; + FuriString* file_path; + FuriString* file_name; +} NfcEinkApp; + +bool nfc_eink_load_from_file_select(NfcEinkApp* instance); +void nfc_eink_blink_emulate_start(NfcEinkApp* app); +void nfc_eink_blink_write_start(NfcEinkApp* app); +void nfc_eink_blink_stop(NfcEinkApp* app); +void nfc_eink_save_settings(NfcEinkApp* instance); diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodispaly_poller.c b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodispaly_poller.c new file mode 100644 index 00000000..a563c08d --- /dev/null +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodispaly_poller.c @@ -0,0 +1,456 @@ +#include "eink_goodisplay_i.h" +#include "eink_goodisplay_config.h" +#include +#include +#include + +#define TAG "GD_Poller" + +typedef NfcCommand (*EinkGoodisplayStateHandler)(Iso14443_3aPoller* poller, NfcEinkScreen* screen); +typedef bool (*GoodisplaySendDataResponseValidatorHandler)( + const uint8_t* response, + const size_t response_len); + +static bool eink_goodisplay_validate_response( + const uint8_t* expected, + const size_t expected_len, + const uint8_t* response, + const size_t response_len) { + furi_assert(response); + furi_assert(expected); + if(expected_len != response_len) { + return false; + } + return memcmp(expected, response, response_len) == 0; +} + +static bool + eink_goodisplay_validate_C2_response(const uint8_t* response, const size_t response_len) { + const uint8_t exp_res[] = {0xC2}; + return eink_goodisplay_validate_response(exp_res, sizeof(exp_res), response, response_len); +} + +static bool + eink_goodisplay_validate_0x02_cmd_response(const uint8_t* response, const size_t response_len) { + const uint8_t exp_res[] = {0x02, 0x90, 0x00}; + return eink_goodisplay_validate_response(exp_res, sizeof(exp_res), response, response_len); +} + +static bool + eink_goodisplay_validate_0x03_cmd_response(const uint8_t* response, const size_t response_len) { + const uint8_t exp_res[] = {0x03, 0x90, 0x00}; + return eink_goodisplay_validate_response(exp_res, sizeof(exp_res), response, response_len); +} + +static bool eink_goodisplay_validate_read_fid_file_response( + const uint8_t* response, + const size_t response_len) { + furi_assert(response); + furi_assert(response_len > 0); + return response[0] == 0x02; +} + +static bool + eink_goodisplay_validate_0xB3_response(const uint8_t* response, const size_t response_len) { + const uint8_t exp_res[] = {0xA2}; + return eink_goodisplay_validate_response(exp_res, sizeof(exp_res), response, response_len); +} + +static bool + eink_goodisplay_validate_0xB2_response(const uint8_t* response, const size_t response_len) { + const uint8_t exp_res[] = {0xA3}; + return eink_goodisplay_validate_response(exp_res, sizeof(exp_res), response, response_len); +} + +#define eink_goodisplay_validate_0x13_response eink_goodisplay_validate_0xB2_response + +static bool eink_goodisplay_validate_update_cmd_response( + const uint8_t* response, + const size_t response_len) { + const uint8_t exp_res[] = {0xF2, 0x01}; + return eink_goodisplay_validate_response(exp_res, sizeof(exp_res), response, response_len); +} + +static bool eink_goodisplay_send_data( + Iso14443_3aPoller* poller, + const NfcEinkScreen* screen, + const uint8_t* data, + const size_t data_len, + const GoodisplaySendDataResponseValidatorHandler validator) { + furi_assert(poller); + furi_assert(screen); + furi_assert(data); + furi_assert(data_len > 0); + furi_assert(validator); + + bit_buffer_reset(screen->tx_buf); + bit_buffer_reset(screen->rx_buf); + + bit_buffer_append_bytes(screen->tx_buf, data, data_len); + + Iso14443_3aError error = iso14443_3a_poller_send_standard_frame( + poller, screen->tx_buf, screen->rx_buf, 0 /* EINK_SCREEN_ISO14443_POLLER_FWT_FC */); + + bool result = false; + if(error == Iso14443_3aErrorNone) { + size_t response_len = bit_buffer_get_size_bytes(screen->rx_buf); + const uint8_t* response = bit_buffer_get_data(screen->rx_buf); + result = validator(response, response_len); + } else { + FURI_LOG_E(TAG, "Iso14443_3aError: %02X", error); + } + return result; +} + +static NfcCommand eink_goodisplay_C2(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Send 0xC2"); + + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->poller_state = EinkGoodisplayPollerStateError; + do { + const uint8_t data[] = {0xC2}; + bool result = eink_goodisplay_send_data( + poller, screen, data, sizeof(data), eink_goodisplay_validate_C2_response); + + if(!result) break; + + ctx->poller_state = EinkGoodisplayPollerStateSelectNDEFTagApp; + } while(false); + + return NfcCommandReset; +} + +static NfcCommand + eink_goodisplay_select_application(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Select NDEF APP"); + + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->poller_state = EinkGoodisplayPollerStateError; + do { + const uint8_t data[] = { + 0x02, 0x00, 0xa4, 0x04, 0x00, 0x07, 0xd2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00}; + bool result = eink_goodisplay_send_data( + poller, screen, data, sizeof(data), eink_goodisplay_validate_0x02_cmd_response); + + if(!result) break; + + eink_goodisplay_on_target_detected(screen); + ctx->poller_state = EinkGoodisplayPollerStateSelectNDEFFile; + } while(false); + return NfcCommandContinue; +} + +static NfcCommand + eink_goodisplay_select_ndef_file(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Select NDEF File"); + + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->poller_state = EinkGoodisplayPollerStateError; + do { + const uint8_t data[] = {0x03, 0x00, 0xa4, 0x00, 0x0c, 0x02, 0xe1, 0x03}; + bool result = eink_goodisplay_send_data( + poller, screen, data, sizeof(data), eink_goodisplay_validate_0x03_cmd_response); + + if(!result) break; + + ctx->poller_state = EinkGoodisplayPollerStateReadFIDFileData; + } while(false); + + return NfcCommandContinue; +} + +static NfcCommand eink_goodisplay_read_fid_file(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Read FID"); + + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->poller_state = EinkGoodisplayPollerStateError; + do { + const uint8_t data[] = {0x02, 0x00, 0xb0, 0x00, 0x00, 0x0f}; + bool result = eink_goodisplay_send_data( + poller, screen, data, sizeof(data), eink_goodisplay_validate_read_fid_file_response); + + if(!result) break; + + ctx->poller_state = EinkGoodisplayPollerStateSelect0xE104File; + } while(false); + + return NfcCommandContinue; +} + +static NfcCommand + eink_goodisplay_select_E104_file(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Select 0xE104"); + + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->poller_state = EinkGoodisplayPollerStateError; + do { + const uint8_t data[] = {0x03, 0x00, 0xa4, 0x00, 0x0c, 0x02, 0xe1, 0x04}; + bool result = eink_goodisplay_send_data( + poller, screen, data, sizeof(data), eink_goodisplay_validate_0x03_cmd_response); + + if(!result) break; + + ctx->poller_state = EinkGoodisplayPollerStateRead0xE104FileData; + } while(false); + return NfcCommandContinue; +} + +static NfcCommand + eink_goodisplay_read_0xE104_file(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Read 0xE104"); + + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->poller_state = EinkGoodisplayPollerStateError; + do { + const uint8_t data[] = {0x02, 0x00, 0xb0, 0x00, 0x00, 0x02}; + bool result = eink_goodisplay_send_data( + poller, screen, data, sizeof(data), eink_goodisplay_validate_read_fid_file_response); + + if(!result) break; + + ctx->poller_state = EinkGoodisplayPollerStateDuplicateC2Cmd; + } while(false); + + return NfcCommandContinue; +} + +static NfcCommand eink_goodisplay_duplicate_C2(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + NfcCommand cmd = eink_goodisplay_C2(poller, screen); + + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->poller_state = EinkGoodisplayPollerStateDuplicateC2Cmd; + + static uint8_t cnt = 0; + cnt++; + if(cnt == 2) { + ctx->poller_state = EinkGoodisplayPollerStateSendConfigCmd; + cnt = 0; + } + + return cmd; +} + +static NfcCommand + eink_goodisplay_send_config_cmd(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Send config"); + + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->poller_state = EinkGoodisplayPollerStateError; + do { + const uint8_t config1[] = {0x02, 0xf0, 0xdb, 0x02, 0x00, 0x00}; + bool result = eink_goodisplay_send_data( + poller, screen, config1, sizeof(config1), eink_goodisplay_validate_0x02_cmd_response); + + if(!result) break; + + size_t config_length; + EinkGoodisplayConfigPack* config = eink_goodisplay_config_pack_alloc(&config_length); + eink_goodisplay_config_pack_set_by_screen_info(config, &screen->data->base); + + result = eink_goodisplay_send_data( + poller, + screen, + (uint8_t*)config, + config_length, + eink_goodisplay_validate_0x03_cmd_response); + + NfcEinkGoodisplayScreenResolution resolution = + config->eink_size_config.screen_data.screen_resolution; + NfcEinkGoodisplayScreenChannel channel = + config->eink_size_config.screen_data.screen_channel; + eink_goodisplay_config_pack_free(config); + + if(!result) break; + + uint8_t config3[] = {0x02, 0xf0, 0xda, 0x00, 0x00, 0x03, 0xf0, resolution, channel}; + + result = eink_goodisplay_send_data( + poller, screen, config3, sizeof(config3), eink_goodisplay_validate_0x02_cmd_response); + + if(!result) break; + + const uint8_t cmdb3[] = {0xb3}; + result = eink_goodisplay_send_data( + poller, screen, cmdb3, sizeof(cmdb3), eink_goodisplay_validate_0xB3_response); + + if(!result) break; + ctx->poller_state = EinkGoodisplayPollerStateSendDataCmd; + + } while(false); + + return NfcCommandContinue; +} + +static bool eink_goodisplay_send_image_data( + Iso14443_3aPoller* poller, + const NfcEinkScreen* screen, + const uint8_t* command_header, + const size_t command_header_length, + const uint8_t* data, + const size_t data_len, + const GoodisplaySendDataResponseValidatorHandler validator) { + furi_assert(command_header); + furi_assert(validator); + furi_assert(poller); + furi_assert(screen); + + size_t total_size = command_header_length + data_len; + uint8_t* buf = malloc(total_size); + memcpy(buf, command_header, command_header_length); + memcpy(buf + command_header_length, data, data_len); + + bool result = eink_goodisplay_send_data(poller, screen, buf, total_size, validator); + + free(buf); + return result; +} + +static NfcCommand eink_goodisplay_send_image(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + const NfcEinkScreenData* data = screen->data; + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + + bool status = true; + do { + FURI_LOG_D(TAG, "Send data: %d", ctx->block_number); + + uint8_t header[] = {0x13, 0xf0, 0xd2, 0x00, 0x00, 0xFA}; + header[4] = ctx->block_number; + status &= eink_goodisplay_send_image_data( + poller, + screen, + header, + sizeof(header), + &data->image_data[ctx->data_index], + data->base.data_block_size - 2, + eink_goodisplay_validate_0x13_response); + + ctx->data_index += data->base.data_block_size - 2; + + if(!status) { + ctx->poller_state = EinkGoodisplayPollerStateError; + break; + } + + uint8_t header2[] = {0x02}; + status &= eink_goodisplay_send_image_data( + poller, + screen, + header2, + sizeof(header2), + &data->image_data[ctx->data_index], + 2, + eink_goodisplay_validate_0x02_cmd_response); + ctx->data_index += 2; + + if(!status) { + ctx->poller_state = EinkGoodisplayPollerStateError; + break; + } + + ctx->block_number++; + ctx->poller_state = (ctx->block_number == screen->device->block_total) ? + EinkGoodisplayPollerStateApplyImage : + EinkGoodisplayPollerStateSendDataCmd; + } while(false); + eink_goodisplay_on_block_processed(screen); + screen->device->block_current = ctx->block_number; + + return NfcCommandContinue; +} + +static NfcCommand eink_goodisplay_begin_update(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->poller_state = EinkGoodisplayPollerStateError; + + const uint8_t apply[] = {0x03, 0xf0, 0xd4, 0x05, 0x80, 0x00}; + FURI_LOG_D(TAG, "Applying..."); + + bool result = eink_goodisplay_send_data( + poller, screen, apply, sizeof(apply), eink_goodisplay_validate_update_cmd_response); + + if(result) { + eink_goodisplay_on_updating(screen); + ctx->poller_state = EinkGoodisplayPollerStateWaitUpdateDone; + } + + return NfcCommandContinue; +} + +static NfcCommand + eink_goodisplay_wait_update_done(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->poller_state = EinkGoodisplayPollerStateWaitUpdateDone; + FURI_LOG_D(TAG, "Updating..."); + + const uint8_t update[] = {0xF2, 0x01}; + bool result = eink_goodisplay_send_data( + poller, screen, update, sizeof(update), eink_goodisplay_validate_0x03_cmd_response); + furi_delay_ms(1500); + + if(result) { + ctx->poller_state = EinkGoodisplayPollerStateSendDataDone; + } + return NfcCommandContinue; +} + +static NfcCommand + eink_goodisplay_finish_handler(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Finish"); + + uint8_t cnt = 0; + const uint8_t finish_cmd_cnt = 20; + do { + const uint8_t b2[] = {0xb2}; + bool result = eink_goodisplay_send_data( + poller, screen, b2, sizeof(b2), eink_goodisplay_validate_0xB2_response); + + if(!result) break; + cnt++; + } while(cnt < finish_cmd_cnt); + eink_goodisplay_on_done(screen); + iso14443_3a_poller_halt(poller); + return NfcCommandStop; +} + +static NfcCommand eink_goodisplay_error_handler(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_E(TAG, "Error!"); + eink_goodisplay_on_error(screen); + iso14443_3a_poller_halt(poller); + return NfcCommandStop; +} + +static const EinkGoodisplayStateHandler handlers[EinkGoodisplayPollerStateNum] = { + [EinkGoodisplayPollerStateSendC2Cmd] = eink_goodisplay_C2, + [EinkGoodisplayPollerStateSelectNDEFTagApp] = eink_goodisplay_select_application, + [EinkGoodisplayPollerStateSelectNDEFFile] = eink_goodisplay_select_ndef_file, + [EinkGoodisplayPollerStateReadFIDFileData] = eink_goodisplay_read_fid_file, + [EinkGoodisplayPollerStateSelect0xE104File] = eink_goodisplay_select_E104_file, + [EinkGoodisplayPollerStateRead0xE104FileData] = eink_goodisplay_read_0xE104_file, + [EinkGoodisplayPollerStateDuplicateC2Cmd] = eink_goodisplay_duplicate_C2, + [EinkGoodisplayPollerStateSendConfigCmd] = eink_goodisplay_send_config_cmd, + [EinkGoodisplayPollerStateSendDataCmd] = eink_goodisplay_send_image, + [EinkGoodisplayPollerStateApplyImage] = eink_goodisplay_begin_update, + [EinkGoodisplayPollerStateWaitUpdateDone] = eink_goodisplay_wait_update_done, + [EinkGoodisplayPollerStateSendDataDone] = eink_goodisplay_finish_handler, + [EinkGoodisplayPollerStateError] = eink_goodisplay_error_handler, +}; + +NfcCommand eink_goodisplay_poller_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + NfcEinkScreen* screen = context; + + NfcCommand command = NfcCommandContinue; + + Iso14443_4aPollerEvent* Iso14443_4a_event = event.event_data; + Iso14443_4aPoller* poller = event.instance; + + if(Iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + //bit_buffer_reset(screen->tx_buf); + //bit_buffer_reset(screen->rx_buf); + + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + command = handlers[ctx->poller_state](poller->iso14443_3a_poller, screen); + } + if(command == NfcCommandReset) iso14443_4a_poller_halt(poller); + return command; +} diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay.c b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay.c new file mode 100644 index 00000000..26f26a5b --- /dev/null +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay.c @@ -0,0 +1,68 @@ +#include "eink_goodisplay.h" +#include "eink_goodisplay_i.h" +#include "eink_goodisplay_config.h" +#include + +static NfcDevice* eink_goodisplay_nfc_device_4a_alloc() { + const uint8_t uid[] = {0xC0, 0xC9, 0x06, 0x7F}; + const uint8_t atqa[] = {0x04, 0x00}; + const uint8_t ats_data[] = {0x90, 0xC0, 0xC9, 0x06, 0x7F, 0x00}; + + Iso14443_4aData* iso14443_4a_edit_data = iso14443_4a_alloc(); + iso14443_4a_set_uid(iso14443_4a_edit_data, uid, sizeof(uid)); + + Iso14443_4aAtsData* _4aAtsData = &iso14443_4a_edit_data->ats_data; + _4aAtsData->tl = 0x0B; + _4aAtsData->t0 = 0x78; + _4aAtsData->ta_1 = 0x33; + _4aAtsData->tb_1 = 0xA0; + _4aAtsData->tc_1 = 0x02; + + _4aAtsData->t1_tk = simple_array_alloc(&simple_array_config_uint8_t); + simple_array_init(_4aAtsData->t1_tk, sizeof(ats_data)); + SimpleArrayData* ats_array = simple_array_get_data(_4aAtsData->t1_tk); + memcpy(ats_array, ats_data, sizeof(ats_data)); + + Iso14443_3aData* base_data = iso14443_4a_get_base_data(iso14443_4a_edit_data); + iso14443_3a_set_sak(base_data, 0x28); + iso14443_3a_set_atqa(base_data, atqa); + + NfcDevice* nfc_device = nfc_device_alloc(); + nfc_device_set_data(nfc_device, NfcProtocolIso14443_4a, iso14443_4a_edit_data); + + iso14443_4a_free(iso14443_4a_edit_data); + return nfc_device; +} + +static NfcEinkScreenDevice* eink_goodisplay_device_alloc() { + NfcEinkScreenDevice* device = malloc(sizeof(NfcEinkScreenDevice)); + + device->nfc_device = eink_goodisplay_nfc_device_4a_alloc(); + + NfcEinkScreenSpecificGoodisplayContext* context = + malloc(sizeof(NfcEinkScreenSpecificGoodisplayContext)); + + context->poller_state = EinkGoodisplayPollerStateSendC2Cmd; + context->listener_state = EinkGoodisplayListenerStateIdle; + + device->screen_context = context; + return device; +} + +static void eink_goodisplay_free(NfcEinkScreenDevice* instance) { + furi_assert(instance); + nfc_device_free(instance->nfc_device); + free(instance->screen_context); +} + +void eink_goodisplay_parse_config(NfcEinkScreen* screen, const uint8_t* data, uint8_t data_length) { + screen->device->screen_type = eink_goodisplay_config_get_screen_type(data, data_length); + eink_goodisplay_on_config_received(screen); +} + +const NfcEinkScreenHandlers goodisplay_handlers = { + .alloc = eink_goodisplay_device_alloc, + .free = eink_goodisplay_free, + .listener_callback = eink_goodisplay_listener_callback, + .poller_callback = eink_goodisplay_poller_callback, +}; diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay.h b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay.h new file mode 100644 index 00000000..48d54789 --- /dev/null +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay.h @@ -0,0 +1,2 @@ +#pragma once +#include diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.c b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.c new file mode 100644 index 00000000..bb012df7 --- /dev/null +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.c @@ -0,0 +1,85 @@ +#include "eink_goodisplay_config.h" + +#define TAG "GD_Config" + +const uint8_t default_config[] = { + 0x03, 0xf0, 0xdb, 0x00, 0x00, 0x67, 0xa0, 0x06, 0x00, 0x20, 0x00, 128, 0x01, 0x28, 0xa4, 0x01, + 0x0c, 0xa5, 0x02, 0x00, 0x0a, 0xa4, 0x01, 0x08, 0xa5, 0x02, 0x00, 0x0a, 0xa4, 0x01, 0x0c, 0xa5, + 0x02, 0x00, 0x0a, 0xa4, 0x01, 0x02, 0xa1, 0x01, 0x12, 0xa4, 0x01, 0x02, 0xa1, 0x04, 0x01, 0x27, + 0x01, 0x01, 0xa1, 0x02, 0x11, 0x01, 0xa1, 0x03, 0x44, 0x00, 0x0f, 0xa1, 0x05, 0x45, 0x27, 0x01, + 0x00, 0x00, 0xa1, 0x02, 0x3c, 0x05, 0xa1, 0x03, 0x21, 0x00, 0x80, 0xa1, 0x02, 0x18, 0x80, 0xa1, + 0x02, 0x4e, 0x00, 0xa1, 0x03, 0x4f, 0x27, 0x01, 0xa3, 0x01, 0x24, 0xa2, 0x02, 0x22, 0xf7, 0xa2, + 0x01, 0x20, 0xa4, 0x01, 0x02, 0xa2, 0x02, 0x10, 0x01, 0xa5, 0x02, 0x00, 0x0a}; + +EinkGoodisplayConfigPack* eink_goodisplay_config_pack_alloc(size_t* config_length) { + furi_assert(config_length); + + uint8_t config_size = sizeof(default_config); + uint8_t* config_ptr = malloc(config_size); + *config_length = config_size; + memcpy(config_ptr, default_config, config_size); + + EinkGoodisplayConfigPack* pack = (EinkGoodisplayConfigPack*)config_ptr; + return pack; +} + +void eink_goodisplay_config_pack_free(EinkGoodisplayConfigPack* pack) { + furi_assert(pack); + free(pack); +} + +static NfcEinkGoodisplayScreenResolution + eink_goodisplay_config_get_resolution(NfcEinkScreenSize screen_size) { + switch(screen_size) { + case NfcEinkScreenSize1n54inch: + return NfcEinkGoodisplayScreenResolution1n54inch; + case NfcEinkScreenSize2n13inch: + return NfcEinkGoodisplayScreenResolution2n13inch; + case NfcEinkScreenSize2n9inch: + return NfcEinkGoodisplayScreenResolution2n9inch; + case NfcEinkScreenSize3n71inch: + return NfcEinkGoodisplayScreenResolution3n71inch; + default: + FURI_LOG_E(TAG, "Resolution %02X is not supported", screen_size); + furi_crash(); + } +} + +void eink_goodisplay_config_pack_set_by_screen_info( + EinkGoodisplayConfigPack* pack, + const NfcEinkScreenInfo* info) { + furi_assert(pack); + furi_assert(info); + pack->eink_size_config.screen_data.height = __builtin_bswap16(info->height); + pack->eink_size_config.screen_data.width = __builtin_bswap16(info->width); + pack->eink_size_config.screen_data.screen_resolution = + eink_goodisplay_config_get_resolution(info->screen_size); + /*Additional goodisplay screen configs can be done here*/ + //pack->update_ctrl1.ram_bypass_inverse_option = 0x08; +} + +NfcEinkScreenType + eink_goodisplay_config_get_screen_type(const uint8_t* data, uint8_t data_length) { + furi_assert(data); + furi_assert(data_length >= sizeof(EinkGoodisplaySizeConfigPack)); + NfcEinkScreenType screen_type = NfcEinkScreenTypeUnknown; + + const EinkGoodisplaySizeConfigPack* config = (EinkGoodisplaySizeConfigPack*)(data); + + if(config->screen_data.screen_resolution == NfcEinkGoodisplayScreenResolution2n13inch) { + /* screen_type = screen_config->screen_channel == + NfcEinkGoodisplayScreenChannelBlackWhite ? + NfcEinkScreenTypeGoodisplay2n13inch : + NfcEinkScreenTypeUnknown; */ + screen_type = NfcEinkScreenTypeGoodisplay2n13inch; + } else if(config->screen_data.screen_resolution == NfcEinkGoodisplayScreenResolution2n9inch) { + screen_type = NfcEinkScreenTypeGoodisplay2n9inch; + } else if(config->screen_data.screen_resolution == NfcEinkGoodisplayScreenResolution1n54inch) { + screen_type = NfcEinkScreenTypeGoodisplay1n54inch; + } else if(config->screen_data.screen_resolution == NfcEinkGoodisplayScreenResolution3n71inch) { + screen_type = NfcEinkScreenTypeGoodisplay3n71inch; + } else + screen_type = NfcEinkScreenTypeUnknown; + + return screen_type; +} diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.h b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.h new file mode 100644 index 00000000..601f3387 --- /dev/null +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.h @@ -0,0 +1,131 @@ +#pragma once +#include "eink_goodisplay_i.h" + +typedef enum { + NfcEinkGoodisplayScreenResolution2n13inch = 0x00, + NfcEinkGoodisplayScreenResolution2n9inch = 0x01, + NfcEinkGoodisplayScreenResolution1n54inch = 0x12, + NfcEinkGoodisplayScreenResolution3n71inch = 0x51, +} NfcEinkGoodisplayScreenResolution; + +typedef enum { + NfcEinkGoodisplayScreenChannelBlackWhite = 0x20, + NfcEinkGoodisplayScreenChannelBlackWhiteRed = 0x30, +} NfcEinkGoodisplayScreenChannel; + +typedef struct { + NfcEinkGoodisplayScreenResolution screen_resolution; + NfcEinkGoodisplayScreenChannel screen_channel; + uint16_t height; + uint16_t width; +} FURI_PACKED NfcEinkGoodisplayScreenTypeData; + +typedef struct { + uint8_t inner_cmd_code; + uint8_t length; +} FURI_PACKED EinkGoodisplayConfigPackHeader; + +typedef struct { + EinkGoodisplayConfigPackHeader header; + uint8_t display_cmd_code; +} FURI_PACKED EinkGoodisplayCommandPackBase; + +typedef EinkGoodisplayCommandPackBase EinkGoodisplayCommandWaitBusy; +typedef EinkGoodisplayCommandPackBase EinkGoodisplayCommandSoftReset; +typedef EinkGoodisplayCommandPackBase EinkGoodisplayCommandWriteRam; +typedef EinkGoodisplayCommandPackBase EinkGoodisplayCommandMasterActivation; + +typedef struct { + EinkGoodisplayCommandPackBase base; + uint8_t value; +} FURI_PACKED EinkGoodisplayCommandWithOneArg; + +typedef struct { + EinkGoodisplayCommandPackBase base; + uint16_t MUX_gate; + uint8_t gate_scan_direction; +} FURI_PACKED EinkGoodisplayCommandDriverOutputCtrl; + +typedef struct { + EinkGoodisplayCommandPackBase base; + uint8_t data_entry_mode; +} FURI_PACKED EinkGoodisplayCommandDataEntryMode; + +typedef struct { + EinkGoodisplayCommandPackBase base; + uint8_t start_address; + uint8_t end_address; +} FURI_PACKED EinkGoodisplayCommandSetRamAddrBoundsX; + +typedef struct { + EinkGoodisplayCommandPackBase base; + uint8_t ram_bypass_inverse_option; + uint8_t source_output_mode; +} FURI_PACKED EinkGoodisplayCommandDisplayUpdateCtrl1; + +typedef struct { + EinkGoodisplayCommandPackBase base; + uint16_t start_address; + uint16_t end_address; +} FURI_PACKED EinkGoodisplayCommandSetRamAddrBoundsY; + +typedef EinkGoodisplayCommandWithOneArg EinkGoodisplayCommandBorderWaveformCtrl; +typedef EinkGoodisplayCommandWithOneArg EinkGoodisplayCommandTempSensorCtrl; +typedef EinkGoodisplayCommandWithOneArg EinkGoodisplayCommandSetRamAddrX; +typedef EinkGoodisplayCommandWithOneArg EinkGoodisplayCommandDisplayUpdateCtrl2; +typedef EinkGoodisplayCommandWithOneArg EinkGoodisplayCommandDisplayDeepSleepMode; +typedef EinkGoodisplayCommandWithOneArg EinkGoodisplayCommandUnknown; + +typedef struct { + EinkGoodisplayCommandPackBase base; + uint16_t value; +} FURI_PACKED EinkGoodisplayCommandSetRamAddrY; + +typedef struct { + EinkGoodisplayConfigPackHeader header; + NfcEinkGoodisplayScreenTypeData screen_data; +} FURI_PACKED EinkGoodisplaySizeConfigPack; + +typedef struct { + EinkGoodisplayCommandWaitBusy wait; + EinkGoodisplayCommandUnknown unknown; +} FURI_PACKED EinkGoodisplayWaitBusySequence; + +typedef struct { + EinkGoodisplayCommandWaitBusy wait_1; + EinkGoodisplayCommandSoftReset reset; + EinkGoodisplayCommandWaitBusy wait_2; +} FURI_PACKED EinkGoodisplayResetSequence; + +typedef struct { + uint8_t command_code; + APDU_Header apdu; + uint8_t data_length; + EinkGoodisplaySizeConfigPack eink_size_config; + EinkGoodisplayWaitBusySequence wait_busy[3]; + EinkGoodisplayResetSequence reset_sequence; + EinkGoodisplayCommandDriverOutputCtrl driver_output_ctrl; + EinkGoodisplayCommandDataEntryMode data_entry_mode; + EinkGoodisplayCommandSetRamAddrBoundsX x_address_bounds; + EinkGoodisplayCommandSetRamAddrBoundsY y_address_bounds; + EinkGoodisplayCommandBorderWaveformCtrl border_waveform_ctrl; + + EinkGoodisplayCommandDisplayUpdateCtrl1 update_ctrl1; + + EinkGoodisplayCommandTempSensorCtrl temp_sensor; + EinkGoodisplayCommandSetRamAddrX x_address_current; + EinkGoodisplayCommandSetRamAddrY y_address_current; + EinkGoodisplayCommandWriteRam write_ram; + EinkGoodisplayCommandDisplayUpdateCtrl2 update_ctrl2; + EinkGoodisplayCommandMasterActivation master_activation; + EinkGoodisplayCommandWaitBusy wait_after_update; + EinkGoodisplayCommandDisplayDeepSleepMode deep_sleep_mode; + EinkGoodisplayCommandUnknown unknown_cmd; +} FURI_PACKED EinkGoodisplayConfigPack; + +EinkGoodisplayConfigPack* eink_goodisplay_config_pack_alloc(size_t* config_length); +void eink_goodisplay_config_pack_free(EinkGoodisplayConfigPack* pack); +void eink_goodisplay_config_pack_set_by_screen_info( + EinkGoodisplayConfigPack* pack, + const NfcEinkScreenInfo* info); +NfcEinkScreenType eink_goodisplay_config_get_screen_type(const uint8_t* data, uint8_t data_length); diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_i.h b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_i.h new file mode 100644 index 00000000..72f75488 --- /dev/null +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_i.h @@ -0,0 +1,134 @@ +#pragma once + +#include "../nfc_eink_screen_i.h" +#include +#include +#include +#include +#include + +#define eink_goodisplay_on_done(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeFinish) + +#define eink_goodisplay_on_config_received(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeConfigurationReceived) + +#define eink_goodisplay_on_target_detected(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeTargetDetected) + +#define eink_goodisplay_on_target_lost(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeTargetLost) + +#define eink_goodisplay_on_block_processed(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeBlockProcessed) + +#define eink_goodisplay_on_updating(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeUpdating) + +#define eink_goodisplay_on_error(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeFailure) + +typedef struct { + uint8_t CLA_byte; + uint8_t CMD_code; + uint8_t P1; + uint8_t P2; +} FURI_PACKED APDU_Header; + +typedef struct { + APDU_Header header; + uint8_t data_length; + uint8_t data[]; +} FURI_PACKED APDU_Command; + +typedef APDU_Command APDU_Command_Select; + +typedef struct { + APDU_Header header; + uint8_t expected_length; +} FURI_PACKED APDU_Command_Read; + +typedef struct { + uint8_t command_code; + uint8_t command_data[]; +} FURI_PACKED NfcEinkScreenCommand; + +typedef struct { + uint16_t status; +} FURI_PACKED APDU_Response; + +typedef struct { + uint16_t data_length; ///TODO: remove this to CC_File struct + uint8_t data[]; +} FURI_PACKED APDU_Response_Read; + +typedef struct { + uint16_t length; + uint8_t ndef_message[]; +} FURI_PACKED NDEF_File; + +typedef struct { + uint8_t type; + uint8_t length; + uint16_t file_id; + uint16_t file_size; + uint8_t read_access_flags; + uint8_t write_access_flags; +} FURI_PACKED NDEF_File_Ctrl_TLV; + +typedef struct { + ///TODO: uint16_t data_length; //maybe this should be here instead of APDU_Response_Read + uint8_t mapping_version; + uint16_t r_apdu_max_size; + uint16_t c_apdu_max_size; + NDEF_File_Ctrl_TLV ctrl_file; +} FURI_PACKED CC_File; + +typedef struct { + uint8_t response_code; + union { + APDU_Response apdu_response; + APDU_Response_Read apdu_response_read; + } apdu_resp; +} FURI_PACKED NfcEinkGoodisplayScreenResponse; + +typedef enum { + EinkGoodisplayPollerStateSendC2Cmd, + EinkGoodisplayPollerStateSelectNDEFTagApp, + EinkGoodisplayPollerStateSelectNDEFFile, + EinkGoodisplayPollerStateReadFIDFileData, + EinkGoodisplayPollerStateSelect0xE104File, + EinkGoodisplayPollerStateRead0xE104FileData, + EinkGoodisplayPollerStateSendConfigCmd, + EinkGoodisplayPollerStateDuplicateC2Cmd, + EinkGoodisplayPollerStateSendDataCmd, + EinkGoodisplayPollerStateApplyImage, + EinkGoodisplayPollerStateWaitUpdateDone, + EinkGoodisplayPollerStateSendDataDone, + + EinkGoodisplayPollerStateError, + EinkGoodisplayPollerStateNum +} EinkGoodisplayPollerState; + +typedef enum { + EinkGoodisplayListenerStateIdle, + EinkGoodisplayListenerStateWaitingForConfig, + EinkGoodisplayListenerStateReadingBlocks, + EinkGoodisplayListenerStateUpdatedSuccefully, +} EinkGoodisplayListenerState; + +/// ----------------------- +typedef struct { + EinkGoodisplayPollerState poller_state; + EinkGoodisplayListenerState listener_state; + bool was_update; + uint8_t update_cnt; + uint8_t response_cnt; + + uint16_t block_number; + size_t data_index; +} NfcEinkScreenSpecificGoodisplayContext; + +void eink_goodisplay_parse_config(NfcEinkScreen* screen, const uint8_t* data, uint8_t data_length); +NfcCommand eink_goodisplay_listener_callback(NfcGenericEvent event, void* context); +NfcCommand eink_goodisplay_poller_callback(NfcGenericEvent event, void* context); diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_listener.c b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_listener.c new file mode 100644 index 00000000..d4cad916 --- /dev/null +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_listener.c @@ -0,0 +1,371 @@ +#include "eink_goodisplay_i.h" + +#define TAG "GD_Listener" + +typedef NfcCommand (*NfcEinkGoodisplayListenerCommandCallback)( + NfcEinkScreen* screen, + const APDU_Command* apdu, + NfcEinkGoodisplayScreenResponse* response, + uint8_t* response_length); + +typedef struct { + uint8_t cmd; + NfcEinkGoodisplayListenerCommandCallback callback; +} NfcEinkGoodisplayListenerCommandHandler; + +static inline uint8_t nfc_eink_screen_apdu_command_A4( + NfcEinkScreen* const screen, + const APDU_Command* command, + APDU_Response* resp) { + UNUSED(screen); + FURI_LOG_D(TAG, "A4"); + bool equal = false; + if(command->data_length == 0x07) { + const uint8_t app_select_data[] = {0xd2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00}; + equal = memcmp(command->data, app_select_data, 0x07) == 0; + + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->listener_state = EinkGoodisplayListenerStateWaitingForConfig; + eink_goodisplay_on_target_detected(screen); + } else if(command->data_length == 0x02 && command->data[0] == 0xE1) { + equal = command->data[1] == 0x03 || command->data[1] == 0x04; + } + uint16_t status = equal ? 0x9000 : 0x6A82; + resp->status = __builtin_bswap16(status); + + return 3; +} + +static inline uint8_t nfc_eink_screen_apdu_command_B0( + NfcEinkScreen* const screen, + const APDU_Command* cmd, + APDU_Response* resp) { + UNUSED(screen); + FURI_LOG_D(TAG, "B0"); + const APDU_Command_Read* command = (APDU_Command_Read*)cmd; + APDU_Response_Read* response = (APDU_Response_Read*)resp; + + uint16_t length = command->expected_length; + uint16_t* status = NULL; + + if(length == 0x000F) { + response->data_length = __builtin_bswap16(0x000F); + CC_File* cc_file = (CC_File*)response->data; + cc_file->mapping_version = 0x20; + cc_file->r_apdu_max_size = __builtin_bswap16(0x0100); + cc_file->c_apdu_max_size = __builtin_bswap16(0x00FA); + cc_file->ctrl_file.type = 0x04; + cc_file->ctrl_file.length = 0x06; + cc_file->ctrl_file.file_id = __builtin_bswap16(0xE104); + cc_file->ctrl_file.file_size = __builtin_bswap16(0x01F4); + cc_file->ctrl_file.read_access_flags = 0x00; + cc_file->ctrl_file.write_access_flags = 0x00; + + status = (uint16_t*)(response->data + length - 2); + } else if(length == 0x0002) { + response->data_length = 0x0000; + status = (uint16_t*)(response->data); + length = 2; + } + + *status = __builtin_bswap16(0x9000); + + return length + 1 + 2; +} + +static inline uint8_t nfc_eink_screen_apdu_command_DA( + NfcEinkScreen* const screen, + const APDU_Command* command, + APDU_Response* resp) { + UNUSED(screen); + FURI_LOG_D(TAG, "DA"); + + uint8_t length = 0; + + const APDU_Header* hdr = &command->header; + if(hdr->P1 == 0x00 && hdr->P2 == 0x00 && command->data_length == 0x03) { + resp->status = __builtin_bswap16(0x9000); + length = 2 + 1; + } + return length; +} + +static inline uint8_t nfc_eink_screen_apdu_command_DB( + NfcEinkScreen* const screen, + const APDU_Command* command, + APDU_Response* resp) { + FURI_LOG_D(TAG, "DB"); + const APDU_Header* hdr = &command->header; + uint8_t length = 0; + if(hdr->P1 == 0x02 && hdr->P2 == 0x00 && command->data_length == 0) { + resp->status = __builtin_bswap16(0x9000); + length = 2; + } else if(hdr->P1 == 0x00 && hdr->P2 == 0x00) { + ///TODO: Add here some saving and parsing logic for screen config + eink_goodisplay_parse_config(screen, command->data, command->data_length); + resp->status = __builtin_bswap16(0x9000); + length = 2; + } + + return length + 1; +} + +static inline uint8_t nfc_eink_screen_apdu_command_D2( + NfcEinkScreen* screen, + const APDU_Command* command, + APDU_Response* resp) { + UNUSED(resp); + + FURI_LOG_D(TAG, "F0 D2: %d", command->data_length); + if(screen->device->block_current < screen->device->block_total) { + uint8_t* data = screen->data->image_data + screen->device->received_data; + memcpy(data, command->data, command->data_length - 2); + screen->device->received_data += command->data_length - 2; + } + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->was_update = true; + + return 1; +} + +static inline uint8_t nfc_eink_screen_apdu_command_D4( + NfcEinkScreen* screen, + const APDU_Command* command, + APDU_Response* resp) { + FURI_LOG_D(TAG, "D4"); + UNUSED(command); + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + + resp->status = 0x0101; + ctx->update_cnt = 0; + return 2; +} + +static NfcCommand nfc_eink_goodisplay_command_handler_C2( + NfcEinkScreen* screen, + const APDU_Command* apdu, + NfcEinkGoodisplayScreenResponse* response, + uint8_t* response_length) { + FURI_LOG_D(TAG, "C2"); + UNUSED(screen); + UNUSED(apdu); + *response_length = 1; + response->response_code = 0xC2; + return NfcCommandSleep; +} + +static NfcCommand nfc_eink_goodisplay_command_handler_B3( + NfcEinkScreen* screen, + const APDU_Command* apdu, + NfcEinkGoodisplayScreenResponse* response, + uint8_t* response_length) { + FURI_LOG_D(TAG, "B3"); + UNUSED(screen); + UNUSED(apdu); + *response_length = 1; + response->response_code = 0xA2; + return NfcCommandContinue; +} + +static NfcCommand nfc_eink_goodisplay_command_handler_B2( + NfcEinkScreen* screen, + const APDU_Command* apdu, + NfcEinkGoodisplayScreenResponse* response, + uint8_t* response_length) { + UNUSED(apdu); + + NfcCommand command = NfcCommandContinue; + *response_length = 1; + response->response_code = 0xA3; + + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + ctx->was_update = false; + ctx->response_cnt++; + + FURI_LOG_D(TAG, "Done B2: %d", ctx->response_cnt); + if(ctx->response_cnt >= 20) { + ctx->listener_state = EinkGoodisplayListenerStateUpdatedSuccefully; + eink_goodisplay_on_done(screen); + command = NfcCommandStop; + } + return command; +} + +static uint8_t nfc_eink_goodisplay_command_handler_process_apdu( + NfcEinkScreen* screen, + const APDU_Command* apdu, + APDU_Response* resp) { + const APDU_Header* apdu_header = &apdu->header; + uint8_t response_length = 0; + if(apdu_header->CLA_byte == 0xF0 && apdu_header->CMD_code == 0xD2) { + response_length = nfc_eink_screen_apdu_command_D2(screen, apdu, resp); + } else if(apdu_header->CLA_byte == 0 && apdu_header->CMD_code == 0xA4) { + response_length = nfc_eink_screen_apdu_command_A4(screen, apdu, resp); + } else if(apdu_header->CLA_byte == 0 && apdu_header->CMD_code == 0xB0) { + response_length = nfc_eink_screen_apdu_command_B0(screen, apdu, resp); + } else if(apdu_header->CLA_byte == 0xF0 && apdu_header->CMD_code == 0xDB) { + response_length = nfc_eink_screen_apdu_command_DB(screen, apdu, resp); + } else if(apdu_header->CLA_byte == 0xF0 && apdu_header->CMD_code == 0xDA) { + response_length = nfc_eink_screen_apdu_command_DA(screen, apdu, resp); + } else if(apdu_header->CLA_byte == 0xF0 && apdu_header->CMD_code == 0xD4) { + response_length = nfc_eink_screen_apdu_command_D4(screen, apdu, resp); + } + + return response_length; +} + +static NfcCommand nfc_eink_goodisplay_command_handler_13( + NfcEinkScreen* screen, + const APDU_Command* apdu, + NfcEinkGoodisplayScreenResponse* response, + uint8_t* response_length) { + FURI_LOG_D(TAG, "13"); + + *response_length = nfc_eink_goodisplay_command_handler_process_apdu( + screen, apdu, &response->apdu_resp.apdu_response); + + response->response_code = 0xA3; + return NfcCommandContinue; +} + +static NfcCommand nfc_eink_goodisplay_command_handler_F2( + NfcEinkScreen* screen, + const APDU_Command* apdu, + NfcEinkGoodisplayScreenResponse* response, + uint8_t* response_length) { + FURI_LOG_D(TAG, "F2"); + UNUSED(apdu); + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + if(ctx->update_cnt < 4) { + response->response_code = 0xF2; + response->apdu_resp.apdu_response.status = 0x01; + *response_length = 2; + ctx->update_cnt++; + } else { + *response_length = 3; + response->response_code = 0x03; + response->apdu_resp.apdu_response.status = __builtin_bswap16(0x9000); + } + return NfcCommandContinue; +} + +static NfcCommand nfc_eink_goodisplay_command_handler_02( + NfcEinkScreen* screen, + const APDU_Command* apdu, + NfcEinkGoodisplayScreenResponse* response, + uint8_t* response_length) { + //FURI_LOG_D(TAG, "02"); + response->response_code = 0x02; + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + + if(!ctx->was_update) { + *response_length = nfc_eink_goodisplay_command_handler_process_apdu( + screen, apdu, &response->apdu_resp.apdu_response); + } else { + if(screen->device->block_current < screen->device->block_total) { + uint8_t* data = screen->data->image_data + screen->device->received_data; + ctx->listener_state = EinkGoodisplayListenerStateReadingBlocks; + memcpy(data, (uint8_t*)apdu, 2); + screen->device->received_data += 2; + screen->device->block_current++; + } + FURI_LOG_D(TAG, "Data: %d", screen->device->received_data); + *response_length = 3; + response->response_code = 0x02; + response->apdu_resp.apdu_response.status = __builtin_bswap16(0x9000); + } + return NfcCommandContinue; +} + +static NfcCommand nfc_eink_goodisplay_command_handler_03( + NfcEinkScreen* screen, + const APDU_Command* apdu, + NfcEinkGoodisplayScreenResponse* response, + uint8_t* response_length) { + UNUSED(screen); + NfcCommand command = NfcCommandContinue; + response->response_code = 0x03; //apdu->command_code; + + *response_length = nfc_eink_goodisplay_command_handler_process_apdu( + screen, apdu, &response->apdu_resp.apdu_response); + if(apdu->header.CLA_byte == 0xF0 && apdu->header.CMD_code == 0xD4) { + response->response_code = 0xF2; + } + + return command; +} + +static const NfcEinkGoodisplayListenerCommandHandler nfc_eink_goodisplay_commands[] = { + { + .cmd = 0xC2, + .callback = nfc_eink_goodisplay_command_handler_C2, + }, + { + .cmd = 0xB3, + .callback = nfc_eink_goodisplay_command_handler_B3, + }, + { + .cmd = 0xB2, + .callback = nfc_eink_goodisplay_command_handler_B2, + }, + { + .cmd = 0x13, + .callback = nfc_eink_goodisplay_command_handler_13, + }, + { + .cmd = 0xF2, + .callback = nfc_eink_goodisplay_command_handler_F2, + }, + { + .cmd = 0x02, + .callback = nfc_eink_goodisplay_command_handler_02, + }, + { + .cmd = 0x03, + .callback = nfc_eink_goodisplay_command_handler_03, + }, +}; + +static NfcEinkGoodisplayListenerCommandCallback eink_goodisplay_get_command_handler(uint8_t cmd) { + NfcEinkGoodisplayListenerCommandCallback callback = NULL; + for(size_t i = 0; i < COUNT_OF(nfc_eink_goodisplay_commands); i++) { + if(cmd != nfc_eink_goodisplay_commands[i].cmd) continue; + callback = nfc_eink_goodisplay_commands[i].callback; + break; + } + return callback; +} + +NfcCommand eink_goodisplay_listener_callback(NfcGenericEvent event, void* context) { + NfcCommand command = NfcCommandContinue; + NfcEinkScreen* screen = context; + Iso14443_4aListenerEvent* Iso14443_4a_event = event.event_data; + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + + if(Iso14443_4a_event->type == Iso14443_4aListenerEventTypeFieldOff) { + if(ctx->listener_state == EinkGoodisplayListenerStateReadingBlocks) + eink_goodisplay_on_target_lost(screen); + } else if(Iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) { + BitBuffer* buffer = Iso14443_4a_event->data->buffer; + + const NfcEinkScreenCommand* cmd = (NfcEinkScreenCommand*)bit_buffer_get_data(buffer); + + NfcEinkGoodisplayListenerCommandCallback handler = + eink_goodisplay_get_command_handler(cmd->command_code); + if(handler != NULL) { + NfcEinkGoodisplayScreenResponse* response = malloc(250); + uint8_t response_length = 0; + command = + handler(screen, (APDU_Command*)cmd->command_data, response, &response_length); + + bit_buffer_reset(screen->tx_buf); + bit_buffer_append_bytes(screen->tx_buf, (uint8_t*)response, response_length); + + iso14443_crc_append(Iso14443CrcTypeA, screen->tx_buf); + free(response); + + nfc_listener_tx(event.instance, screen->tx_buf); + } + } + return command; +} diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_screen.c b/nfc_eink/nfc_eink_screen/nfc_eink_screen.c new file mode 100644 index 00000000..e2d2f7da --- /dev/null +++ b/nfc_eink/nfc_eink_screen/nfc_eink_screen.c @@ -0,0 +1,411 @@ +#include "nfc_eink_screen_i.h" + +#include <../lib/flipper_format/flipper_format.h> +#include <../applications/services/storage/storage.h> + +#define TAG "NfcEinkScreen" + +///TODO: possibly move this closer to save/load functions +#define NFC_EINK_FORMAT_VERSION (1) +#define NFC_EINK_FILE_HEADER "Flipper NFC Eink screen" +#define NFC_EINK_DEVICE_UID_KEY "UID" +#define NFC_EINK_DEVICE_TYPE_KEY "NFC type" +#define NFC_EINK_SCREEN_MANUFACTURER_TYPE_KEY "Manufacturer type" +#define NFC_EINK_SCREEN_MANUFACTURER_NAME_KEY "Manufacturer name" +#define NFC_EINK_SCREEN_TYPE_KEY "Screen type" +#define NFC_EINK_SCREEN_NAME_KEY "Screen name" +#define NFC_EINK_SCREEN_WIDTH_KEY "Width" +#define NFC_EINK_SCREEN_HEIGHT_KEY "Height" +#define NFC_EINK_SCREEN_DATA_BLOCK_SIZE_KEY "Data block size" +#define NFC_EINK_SCREEN_DATA_TOTAL_KEY "Data total" +#define NFC_EINK_SCREEN_BLOCK_DATA_KEY "Block" + +///TODO: move this externs to separate files in screen folders for each type +extern const NfcEinkScreenHandlers waveshare_handlers; +extern const NfcEinkScreenHandlers goodisplay_handlers; + +typedef struct { + const NfcEinkScreenHandlers* handlers; + const char* name; +} NfcEinkScreenManufacturerDescriptor; + +static const NfcEinkScreenManufacturerDescriptor manufacturers[NfcEinkManufacturerNum] = { + [NfcEinkManufacturerWaveshare] = + { + .handlers = &waveshare_handlers, + .name = "Waveshare", + }, + [NfcEinkManufacturerGoodisplay] = + { + .handlers = &goodisplay_handlers, + .name = "Goodisplay", + }, +}; + +const char* nfc_eink_screen_get_manufacturer_name(NfcEinkManufacturer manufacturer) { + furi_assert(manufacturer < NfcEinkManufacturerNum); + return manufacturers[manufacturer].name; +} + +NfcEinkScreen* nfc_eink_screen_alloc(NfcEinkManufacturer manufacturer) { + furi_check(manufacturer < NfcEinkManufacturerNum); + + NfcEinkScreen* screen = malloc(sizeof(NfcEinkScreen)); + screen->handlers = manufacturers[manufacturer].handlers; + + screen->device = screen->handlers->alloc(); + + screen->data = malloc(sizeof(NfcEinkScreenData)); + + screen->tx_buf = bit_buffer_alloc(300); + screen->rx_buf = bit_buffer_alloc(300); + return screen; +} + +static inline uint16_t nfc_eink_screen_calculate_image_size(const NfcEinkScreenInfo* const info) { + furi_assert(info); + return info->width * (info->height % 8 == 0 ? (info->height / 8) : (info->height / 8 + 1)); +} + +void nfc_eink_screen_init(NfcEinkScreen* screen, NfcEinkScreenType type) { + furi_assert(type != NfcEinkScreenTypeUnknown); + furi_assert(type < NfcEinkScreenTypeNum); + + NfcEinkScreenData* data = screen->data; + NfcEinkScreenDevice* device = screen->device; + + const NfcEinkScreenInfo* info = nfc_eink_descriptor_get_by_type(type); + memcpy(&data->base, info, sizeof(NfcEinkScreenInfo)); + + data->image_size = nfc_eink_screen_calculate_image_size(info); + + device->block_total = data->image_size / info->data_block_size; + if(data->image_size % info->data_block_size != 0) device->block_total += 1; + size_t memory_size = device->block_total * data->base.data_block_size; + + data->image_data = malloc(memory_size); + ///TODO: this can be used as a config option, which will define the initial color of the screen + memset(data->image_data, 0xFF, memory_size); +} + +void nfc_eink_screen_free(NfcEinkScreen* screen) { + furi_check(screen); + + screen->handlers->free(screen->device); + free(screen->device); + + NfcEinkScreenData* data = screen->data; + free(data->image_data); + free(data); + + bit_buffer_free(screen->tx_buf); + bit_buffer_free(screen->rx_buf); + screen->handlers = NULL; + free(screen); +} + +void nfc_eink_screen_set_callback( + NfcEinkScreen* screen, + NfcEinkScreenEventCallback event_callback, + NfcEinkScreenEventContext event_context) { + furi_assert(screen); + furi_assert(event_callback); + screen->event_callback = event_callback; + screen->event_context = event_context; +} + +NfcDevice* nfc_eink_screen_get_nfc_device(const NfcEinkScreen* screen) { + furi_assert(screen); + return screen->device->nfc_device; +} + +const NfcEinkScreenInfo* nfc_eink_screen_get_image_info(const NfcEinkScreen* screen) { + furi_assert(screen); + return &screen->data->base; +} + +const uint8_t* nfc_eink_screen_get_image_data(const NfcEinkScreen* screen) { + furi_assert(screen); + return screen->data->image_data; +} + +uint16_t nfc_eink_screen_get_image_size(const NfcEinkScreen* screen) { + furi_assert(screen); + return screen->data->image_size; +} + +uint16_t nfc_eink_screen_get_received_size(const NfcEinkScreen* screen) { + furi_assert(screen); + return screen->device->received_data; +} + +NfcGenericCallback nfc_eink_screen_get_nfc_callback(const NfcEinkScreen* screen, NfcMode mode) { + furi_assert(screen); + furi_assert(mode < NfcModeNum); + + const NfcEinkScreenHandlers* handlers = screen->handlers; + return (mode == NfcModeListener) ? handlers->listener_callback : handlers->poller_callback; +} + +void nfc_eink_screen_get_progress(const NfcEinkScreen* screen, size_t* current, size_t* total) { + furi_assert(screen); + furi_assert(current); + furi_assert(total); + + *current = screen->device->block_current; + *total = screen->device->block_total; +} + +const char* nfc_eink_screen_get_name(const NfcEinkScreen* screen) { + furi_assert(screen); + return screen->data->base.name; +} + +bool nfc_eink_screen_load_info(const char* file_path, const NfcEinkScreenInfo** info) { + furi_assert(info); + furi_assert(file_path); + bool loaded = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + do { + if(!flipper_format_buffered_file_open_existing(ff, file_path)) break; + + uint32_t tmp = 0; + if(!flipper_format_read_uint32(ff, NFC_EINK_SCREEN_MANUFACTURER_TYPE_KEY, &tmp, 1)) break; + NfcEinkManufacturer manufacturer = tmp; + + if(!flipper_format_read_uint32(ff, NFC_EINK_SCREEN_TYPE_KEY, &tmp, 1)) break; + NfcEinkScreenType screen_type = tmp; + if(screen_type == NfcEinkScreenTypeUnknown || screen_type == NfcEinkScreenTypeNum) { + FURI_LOG_E(TAG, "Loaded screen type is invalid"); + break; + } + + uint32_t width = 0; + if(!flipper_format_read_uint32(ff, NFC_EINK_SCREEN_WIDTH_KEY, &width, 1)) break; + + uint32_t height = 0; + if(!flipper_format_read_uint32(ff, NFC_EINK_SCREEN_HEIGHT_KEY, &height, 1)) break; + + const NfcEinkScreenInfo* inf_tmp = nfc_eink_descriptor_get_by_type(screen_type); + + if(inf_tmp->screen_manufacturer != manufacturer) { + FURI_LOG_E(TAG, "Loaded manufacturer doesn't match with info field"); + break; + } + + if(inf_tmp->width != width || inf_tmp->height != height) { + FURI_LOG_E(TAG, "Loaded screen size doesn't match with info field"); + break; + } + + *info = inf_tmp; + loaded = true; + } while(false); + + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + return loaded; +} + +static uint8_t nfc_eink_screen_get_bytes_per_row(uint16_t height) { + return height % 8 == 0 ? (height / 8) : (height / 8 + 1); +} + +static bool nfc_eink_screen_save_block( + FlipperFormat* ff, + const uint16_t block_num, + const uint8_t* block, + const uint8_t block_size) { + FuriString* temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "%s %d", NFC_EINK_SCREEN_BLOCK_DATA_KEY, block_num); + bool block_saved = + flipper_format_write_hex(ff, furi_string_get_cstr(temp_str), block, block_size); + free(temp_str); + return block_saved; +} + +static bool nfc_eink_screen_load_block( + FlipperFormat* ff, + const uint16_t block_num, + uint8_t* block, + const uint8_t block_size) { + FuriString* temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "%s %d", NFC_EINK_SCREEN_BLOCK_DATA_KEY, block_num); + bool block_loaded = + flipper_format_read_hex(ff, furi_string_get_cstr(temp_str), block, block_size); + furi_string_free(temp_str); + return block_loaded; +} + +bool nfc_eink_screen_load_data( + const char* file_path, + NfcEinkScreen* destination, + const NfcEinkScreenInfo* source_info) { + furi_assert(destination); + furi_assert(destination->data->image_data); + furi_assert(source_info); + + bool loaded = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + do { + if(!flipper_format_buffered_file_open_existing(ff, file_path)) break; + + uint32_t block_data_size = 0; + if(!flipper_format_read_uint32( + ff, NFC_EINK_SCREEN_DATA_BLOCK_SIZE_KEY, &block_data_size, 1)) { + FURI_LOG_E(TAG, "Block size load failed"); + break; + } + + const uint8_t source_bytes_per_row = + nfc_eink_screen_get_bytes_per_row(source_info->height); + if(block_data_size != source_bytes_per_row) { + FURI_LOG_E(TAG, "Block size doesn't match to screen size"); + break; + } + + NfcEinkScreenData* data = destination->data; + + uint16_t dest_offset = 0; + uint8_t dest_bytes_per_row = nfc_eink_screen_get_bytes_per_row(data->base.height); + dest_bytes_per_row = MIN(dest_bytes_per_row, source_bytes_per_row); + + const uint16_t row_cnt = MIN(data->base.width, source_info->width); + uint8_t* block_buf = malloc(source_bytes_per_row); + for(uint16_t i = 0; i < row_cnt; i++) { + loaded = nfc_eink_screen_load_block(ff, i, block_buf, source_bytes_per_row); + if(!loaded) break; + + memcpy(&data->image_data[dest_offset], block_buf, dest_bytes_per_row); + dest_offset += dest_bytes_per_row; + } + free(block_buf); + } while(false); + + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + return loaded; +} + +bool nfc_eink_screen_save(const NfcEinkScreen* screen, const char* file_path) { + furi_assert(screen); + furi_assert(file_path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + FuriString* temp_str = furi_string_alloc(); + const NfcEinkScreenInfo* source_info = &screen->data->base; + + bool saved = false; + do { + if(!flipper_format_buffered_file_open_always(ff, file_path)) break; + + // Write header + if(!flipper_format_write_header_cstr(ff, NFC_EINK_FILE_HEADER, NFC_EINK_FORMAT_VERSION)) + break; + + // Write device type + NfcProtocol protocol = nfc_device_get_protocol(screen->device->nfc_device); + if(!flipper_format_write_string_cstr( + ff, NFC_EINK_DEVICE_TYPE_KEY, nfc_device_get_protocol_name(protocol))) + break; + + // Write UID + furi_string_printf( + temp_str, "%s size can be different depending on type", NFC_EINK_DEVICE_UID_KEY); + if(!flipper_format_write_comment(ff, temp_str)) break; + + size_t uid_len; + const uint8_t* uid = nfc_device_get_uid(screen->device->nfc_device, &uid_len); + if(!flipper_format_write_hex(ff, NFC_EINK_DEVICE_UID_KEY, uid, uid_len)) break; + + // Write manufacturer type + uint32_t buf = screen->data->base.screen_manufacturer; + if(!flipper_format_write_uint32(ff, NFC_EINK_SCREEN_MANUFACTURER_TYPE_KEY, &buf, 1)) break; + + // Write screen type + buf = screen->data->base.screen_type; + if(!flipper_format_write_uint32(ff, NFC_EINK_SCREEN_TYPE_KEY, &buf, 1)) break; + + // Write screen name + if(!flipper_format_write_string_cstr(ff, NFC_EINK_SCREEN_NAME_KEY, screen->data->base.name)) + break; + + // Write screen width + buf = screen->data->base.width; + if(!flipper_format_write_uint32(ff, NFC_EINK_SCREEN_WIDTH_KEY, &buf, 1)) break; + + // Write screen height + buf = screen->data->base.height; + if(!flipper_format_write_uint32(ff, NFC_EINK_SCREEN_HEIGHT_KEY, &buf, 1)) break; + + // Write data block size + furi_string_printf( + temp_str, + "%s may be different depending on type", + NFC_EINK_SCREEN_DATA_BLOCK_SIZE_KEY); + if(!flipper_format_write_comment(ff, temp_str)) break; + + const uint8_t source_bytes_per_row = + nfc_eink_screen_get_bytes_per_row(source_info->height); + buf = source_bytes_per_row; + if(!flipper_format_write_uint32(ff, NFC_EINK_SCREEN_DATA_BLOCK_SIZE_KEY, &buf, 1)) break; + + // Write image total size + buf = screen->data->image_size; + if(!flipper_format_write_uint32(ff, NFC_EINK_SCREEN_DATA_TOTAL_KEY, &buf, 1)) break; + + // Write image data + furi_string_printf(temp_str, "Data"); + if(!flipper_format_write_comment(ff, temp_str)) break; + + const uint16_t source_rows_cnt = source_info->width; + uint16_t source_offset = 0; + bool block_saved = false; + for(uint16_t i = 0; i < source_rows_cnt; i++) { + block_saved = nfc_eink_screen_save_block( + ff, i, &screen->data->image_data[source_offset], source_bytes_per_row); + if(!block_saved) break; + source_offset += source_bytes_per_row; + } + saved = block_saved; + } while(false); + + flipper_format_free(ff); + furi_string_free(temp_str); + furi_record_close(RECORD_STORAGE); + + return saved; +} + +bool nfc_eink_screen_delete(const char* file_path) { + furi_assert(file_path); + Storage* storage = furi_record_open(RECORD_STORAGE); + bool deleted = storage_simply_remove(storage, file_path); + furi_record_close(RECORD_STORAGE); + return deleted; +} + +static void nfc_eink_screen_event_invoke(NfcEinkScreen* instance, NfcEinkScreenEventType type) { + furi_assert(instance); + if(instance->event_callback != NULL) { + instance->event_callback(type, instance->event_context); + } +} + +void nfc_eink_screen_vendor_callback(NfcEinkScreen* instance, NfcEinkScreenEventType type) { + furi_assert(instance); + + if(type == NfcEinkScreenEventTypeConfigurationReceived) { + FURI_LOG_D(TAG, "Config received"); + nfc_eink_screen_init(instance, instance->device->screen_type); + } else + nfc_eink_screen_event_invoke(instance, type); +} diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_screen.h b/nfc_eink/nfc_eink_screen/nfc_eink_screen.h new file mode 100644 index 00000000..d851232e --- /dev/null +++ b/nfc_eink/nfc_eink_screen/nfc_eink_screen.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include + +#include "nfc_eink_types.h" + +typedef struct NfcEinkScreen NfcEinkScreen; + +const char* nfc_eink_screen_get_manufacturer_name(NfcEinkManufacturer type); + +NfcEinkScreen* nfc_eink_screen_alloc(NfcEinkManufacturer manufacturer); +void nfc_eink_screen_init(NfcEinkScreen* screen, NfcEinkScreenType type); +void nfc_eink_screen_free(NfcEinkScreen* screen); + +///TODO: maybe this function can be moved to nfc_eink_screen_alloc as a number of parameters +void nfc_eink_screen_set_callback( + NfcEinkScreen* screen, + NfcEinkScreenEventCallback event_callback, + NfcEinkScreenEventContext event_context); + +NfcDevice* nfc_eink_screen_get_nfc_device(const NfcEinkScreen* screen); +NfcGenericCallback nfc_eink_screen_get_nfc_callback(const NfcEinkScreen* screen, NfcMode mode); + +const NfcEinkScreenInfo* nfc_eink_screen_get_image_info(const NfcEinkScreen* screen); +const uint8_t* nfc_eink_screen_get_image_data(const NfcEinkScreen* screen); +uint16_t nfc_eink_screen_get_image_size(const NfcEinkScreen* screen); +uint16_t nfc_eink_screen_get_received_size(const NfcEinkScreen* screen); + +void nfc_eink_screen_get_progress(const NfcEinkScreen* screen, size_t* current, size_t* total); +const char* nfc_eink_screen_get_name(const NfcEinkScreen* screen); + +bool nfc_eink_screen_save(const NfcEinkScreen* screen, const char* file_path); +bool nfc_eink_screen_delete(const char* file_path); +bool nfc_eink_screen_load_info(const char* file_path, const NfcEinkScreenInfo** info); +bool nfc_eink_screen_load_data( + const char* file_path, + NfcEinkScreen* destination, + const NfcEinkScreenInfo* source_info); diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_screen_i.h b/nfc_eink/nfc_eink_screen/nfc_eink_screen_i.h new file mode 100644 index 00000000..68d67b6d --- /dev/null +++ b/nfc_eink/nfc_eink_screen/nfc_eink_screen_i.h @@ -0,0 +1,40 @@ +#include "nfc_eink_screen.h" + +typedef struct { + uint8_t* image_data; + uint16_t image_size; + NfcEinkScreenInfo base; +} NfcEinkScreenData; + +typedef void NfcEinkScreenSpecificContext; + +typedef struct { + NfcDevice* nfc_device; + uint16_t block_total; + uint16_t block_current; + uint16_t received_data; + NfcEinkScreenType screen_type; + NfcEinkScreenSpecificContext* screen_context; +} NfcEinkScreenDevice; + +typedef NfcEinkScreenDevice* (*EinkScreenAllocCallback)(); +typedef void (*EinkScreenFreeCallback)(NfcEinkScreenDevice* instance); + +typedef struct { + EinkScreenAllocCallback alloc; + EinkScreenFreeCallback free; + NfcGenericCallback listener_callback; + NfcGenericCallback poller_callback; +} NfcEinkScreenHandlers; + +struct NfcEinkScreen { + NfcEinkScreenData* data; + NfcEinkScreenDevice* device; + BitBuffer* tx_buf; + BitBuffer* rx_buf; + const NfcEinkScreenHandlers* handlers; + NfcEinkScreenEventCallback event_callback; + NfcEinkScreenEventContext event_context; +}; + +void nfc_eink_screen_vendor_callback(NfcEinkScreen* instance, NfcEinkScreenEventType type); diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.c b/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.c new file mode 100644 index 00000000..c2740f97 --- /dev/null +++ b/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.c @@ -0,0 +1,184 @@ +#include "nfc_eink_screen_infos.h" + +typedef bool (*NfcEinkDescriptorCompareDelegate)( + const NfcEinkScreenInfo* const a, + const NfcEinkScreenInfo* const b); + +static const NfcEinkScreenInfo screen_descriptors[] = { + { + .name = "Unknown", + .width = 0, + .height = 0, + .screen_size = NfcEinkScreenSizeUnknown, + .screen_manufacturer = NfcEinkManufacturerUnknown, + .screen_type = NfcEinkScreenTypeUnknown, + .data_block_size = 0, + }, + { + .name = "Waveshare 2.13 inch", + .width = 250, + .height = 122, + .screen_size = NfcEinkScreenSize2n13inch, + .screen_manufacturer = NfcEinkManufacturerWaveshare, + .screen_type = NfcEinkScreenTypeWaveshare2n13inch, + .data_block_size = 16, + }, + { + .name = "Waveshare 2.7 inch", + .width = 264, + .height = 176, + .screen_size = NfcEinkScreenSize2n7inch, + .screen_manufacturer = NfcEinkManufacturerWaveshare, + .screen_type = NfcEinkScreenTypeWaveshare2n7inch, + .data_block_size = 121, + }, + { + .name = "Waveshare 2.9 inch", + .width = 296, + .height = 128, + .screen_size = NfcEinkScreenSize2n9inch, + .screen_manufacturer = NfcEinkManufacturerWaveshare, + .screen_type = NfcEinkScreenTypeWaveshare2n9inch, + .data_block_size = 16, + }, + { + .name = "Waveshare 4.2 inch", + .width = 300, + .height = 400, + .screen_size = NfcEinkScreenSize4n2inch, + .screen_manufacturer = NfcEinkManufacturerWaveshare, + .screen_type = NfcEinkScreenTypeWaveshare4n2inch, + .data_block_size = 100, + }, + { + .name = "Waveshare 7.5 inch", + .width = 480, + .height = 800, + .screen_size = NfcEinkScreenSize7n5inch, + .screen_manufacturer = NfcEinkManufacturerWaveshare, + .screen_type = NfcEinkScreenTypeWaveshare7n5inch, + .data_block_size = 120, + }, + { + .name = "GDEY0154D67", + .width = 200, + .height = 200, + .screen_size = NfcEinkScreenSize1n54inch, + .screen_manufacturer = NfcEinkManufacturerGoodisplay, + .screen_type = NfcEinkScreenTypeGoodisplay1n54inch, + .data_block_size = 0xFA, + }, + { + .name = "GDEY0213B74", + .width = 250, + .height = 122, + .screen_size = NfcEinkScreenSize2n13inch, + .screen_manufacturer = NfcEinkManufacturerGoodisplay, + .screen_type = NfcEinkScreenTypeGoodisplay2n13inch, + .data_block_size = 0xFA, + }, + { + .name = "GDEY029T94", + .width = 296, + .height = 128, + .screen_size = NfcEinkScreenSize2n9inch, + .screen_manufacturer = NfcEinkManufacturerGoodisplay, + .screen_type = NfcEinkScreenTypeGoodisplay2n9inch, + .data_block_size = 0xFA, + }, + { + .name = "GDEY037T03", + .width = 416, + .height = 240, + .screen_size = NfcEinkScreenSize3n71inch, + .screen_manufacturer = NfcEinkManufacturerGoodisplay, + .screen_type = NfcEinkScreenTypeGoodisplay3n71inch, + .data_block_size = 0xFA, + }, +}; + +const NfcEinkScreenInfo* nfc_eink_descriptor_get_by_type(const NfcEinkScreenType type) { + furi_assert(type < NfcEinkScreenTypeNum); + furi_assert(type != NfcEinkScreenTypeUnknown); + + const NfcEinkScreenInfo* item = NULL; + for(uint8_t i = 0; i < COUNT_OF(screen_descriptors); i++) { + if(screen_descriptors[i].screen_type == type) { + item = &screen_descriptors[i]; + break; + } + } + return item; +} + +static uint8_t nfc_eink_descriptor_filter_by( + EinkScreenInfoArray_t result, + const NfcEinkScreenInfo* sample, + NfcEinkDescriptorCompareDelegate compare) { + uint8_t count = 0; + for(uint8_t i = 0; i < COUNT_OF(screen_descriptors); i++) { + if(compare(&screen_descriptors[i], sample)) { + EinkScreenInfoArray_push_back(result, &screen_descriptors[i]); + count++; + } + } + return count; +} + +static inline bool nfc_eink_descriptor_compare_by_manufacturer( + const NfcEinkScreenInfo* const a, + const NfcEinkScreenInfo* const b) { + return a->screen_manufacturer == b->screen_manufacturer; +} + +static inline bool nfc_eink_descriptor_compare_by_screen_size( + const NfcEinkScreenInfo* const a, + const NfcEinkScreenInfo* const b) { + return a->screen_size == b->screen_size; +} + +uint8_t nfc_eink_descriptor_filter_by_manufacturer( + EinkScreenInfoArray_t result, + NfcEinkManufacturer manufacturer) { + furi_assert(result); + furi_assert(manufacturer < NfcEinkManufacturerNum); + furi_assert(manufacturer != NfcEinkManufacturerUnknown); + + NfcEinkScreenInfo dummy = {.screen_manufacturer = manufacturer}; + return nfc_eink_descriptor_filter_by( + result, &dummy, nfc_eink_descriptor_compare_by_manufacturer); +} + +uint8_t nfc_eink_descriptor_filter_by_screen_size( + EinkScreenInfoArray_t result, + NfcEinkScreenSize screen_size) { + furi_assert(result); + furi_assert(screen_size != NfcEinkScreenSizeUnknown); + furi_assert(screen_size < NfcEinkScreenSizeNum); + + NfcEinkScreenInfo dummy = {.screen_size = screen_size}; + return nfc_eink_descriptor_filter_by( + result, &dummy, nfc_eink_descriptor_compare_by_screen_size); +} + +uint8_t nfc_eink_descriptor_filter_by_screen_type( + EinkScreenInfoArray_t result, + NfcEinkScreenType screen_type) { + furi_assert(result); + furi_assert(screen_type != NfcEinkScreenTypeUnknown); + furi_assert(screen_type < NfcEinkScreenTypeNum); + + EinkScreenInfoArray_push_back(result, nfc_eink_descriptor_get_by_type(screen_type)); + return 1; +} + +uint8_t nfc_eink_descriptor_get_all_usable(EinkScreenInfoArray_t result) { + furi_assert(result); + uint8_t count = 0; + for(uint8_t i = 0; i < COUNT_OF(screen_descriptors); i++) { + if(i == NfcEinkScreenTypeUnknown) continue; + EinkScreenInfoArray_push_back(result, &screen_descriptors[i]); + count++; + } + return count; +} diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.h b/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.h new file mode 100644 index 00000000..896a5687 --- /dev/null +++ b/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.h @@ -0,0 +1,88 @@ +#pragma once +#include +#include + +typedef enum { + NfcEinkScreenSizeUnknown, + NfcEinkScreenSize2n13inch, + NfcEinkScreenSize2n9inch, + NfcEinkScreenSize1n54inch, + NfcEinkScreenSize3n71inch, + NfcEinkScreenSize2n7inch, + NfcEinkScreenSize4n2inch, + NfcEinkScreenSize7n5inch, + + NfcEinkScreenSizeNum +} NfcEinkScreenSize; + +typedef enum { + NfcEinkManufacturerWaveshare, + NfcEinkManufacturerGoodisplay, + + NfcEinkManufacturerNum, + NfcEinkManufacturerUnknown +} NfcEinkManufacturer; + +/// TODO: Align all screens here by manufacturers before release +typedef enum { + NfcEinkScreenTypeUnknown, + NfcEinkScreenTypeGoodisplay2n13inch, + NfcEinkScreenTypeGoodisplay2n9inch, + + //NfcEinkTypeGoodisplay4n2inch, + + NfcEinkScreenTypeWaveshare2n13inch, + NfcEinkScreenTypeWaveshare2n9inch, + NfcEinkScreenTypeGoodisplay1n54inch, + NfcEinkScreenTypeGoodisplay3n71inch, + + NfcEinkScreenTypeWaveshare2n7inch, + NfcEinkScreenTypeWaveshare4n2inch, + NfcEinkScreenTypeWaveshare7n5inch, + + NfcEinkScreenTypeNum +} NfcEinkScreenType; + +typedef struct { + uint16_t width; + uint16_t height; + uint8_t data_block_size; + NfcEinkScreenType screen_type; + NfcEinkScreenSize screen_size; + NfcEinkManufacturer screen_manufacturer; + const char* name; +} NfcEinkScreenInfo; + +#define M_ARRAY_SIZE (sizeof(NfcEinkScreenInfo*) * NfcEinkScreenTypeNum) +#define M_INIT(a) ((a) = malloc(M_ARRAY_SIZE)) + +#define M_INIT_SET(new, old) \ + do { \ + M_INIT(new); \ + memcpy((void*)new, (void*)old, M_ARRAY_SIZE); \ + free((void*)old); \ + } while(false) + +//#define M_SET(a, b) (M_INIT(a); memcpy(a, b, M_ARRAY_SIZE)) +#define M_CLEAR(a) (free((void*)a)) + +#define M_DESCRIPTOR_ARRAY_OPLIST \ + (INIT(M_INIT), INIT_SET(M_INIT_SET), CLEAR(M_CLEAR), TYPE(const NfcEinkScreenInfo*)) + +ARRAY_DEF(EinkScreenInfoArray, const NfcEinkScreenInfo*, M_DESCRIPTOR_ARRAY_OPLIST); + +const NfcEinkScreenInfo* nfc_eink_descriptor_get_by_type(NfcEinkScreenType type); + +uint8_t nfc_eink_descriptor_get_all_usable(EinkScreenInfoArray_t result); + +uint8_t nfc_eink_descriptor_filter_by_manufacturer( + EinkScreenInfoArray_t result, + NfcEinkManufacturer manufacturer); + +uint8_t nfc_eink_descriptor_filter_by_screen_size( + EinkScreenInfoArray_t result, + NfcEinkScreenSize screen_size); + +uint8_t nfc_eink_descriptor_filter_by_screen_type( + EinkScreenInfoArray_t result, + NfcEinkScreenType screen_type); diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_types.h b/nfc_eink/nfc_eink_screen/nfc_eink_types.h new file mode 100644 index 00000000..e0dda98a --- /dev/null +++ b/nfc_eink/nfc_eink_screen/nfc_eink_types.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "nfc_eink_screen_infos.h" + +typedef enum { + NfcEinkScreenEventTypeTargetDetected, + NfcEinkScreenEventTypeTargetLost, + + NfcEinkScreenEventTypeConfigurationReceived, + NfcEinkScreenEventTypeBlockProcessed, + NfcEinkScreenEventTypeUpdating, + + NfcEinkScreenEventTypeFinish, + NfcEinkScreenEventTypeFailure, +} NfcEinkScreenEventType; + +typedef void (*NfcEinkScreenEventCallback)(NfcEinkScreenEventType type, void* context); + +typedef void* NfcEinkScreenEventContext; \ No newline at end of file diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare.c b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare.c new file mode 100644 index 00000000..7b5f338f --- /dev/null +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare.c @@ -0,0 +1,59 @@ +#include "eink_waveshare.h" +#include "eink_waveshare_i.h" +#include "eink_waveshare_config.h" + +static NfcDevice* eink_waveshare_nfc_device_alloc() { + //const uint8_t uid[] = {0x46, 0x53, 0x54, 0x5E, 0x31, 0x30, 0x6D}; //FSTN10m + const uint8_t uid[] = {0x57, 0x53, 0x44, 0x5A, 0x31, 0x30, 0x6D}; //WSDZ10m + const uint8_t atqa[] = {0x44, 0x00}; + + Iso14443_3aData* iso14443_3a_edit_data = iso14443_3a_alloc(); + iso14443_3a_set_uid(iso14443_3a_edit_data, uid, sizeof(uid)); + iso14443_3a_set_sak(iso14443_3a_edit_data, 0); + iso14443_3a_set_atqa(iso14443_3a_edit_data, atqa); + + NfcDevice* nfc_device = nfc_device_alloc(); + nfc_device_set_data(nfc_device, NfcProtocolIso14443_3a, iso14443_3a_edit_data); + + iso14443_3a_free(iso14443_3a_edit_data); + return nfc_device; +} + +static NfcEinkScreenDevice* eink_waveshare_alloc() { + NfcEinkScreenDevice* device = malloc(sizeof(NfcEinkScreenDevice)); + device->nfc_device = eink_waveshare_nfc_device_alloc(); + + NfcEinkWaveshareSpecificContext* ctx = malloc(sizeof(NfcEinkWaveshareSpecificContext)); + ctx->listener_state = NfcEinkWaveshareListenerStateIdle; + ctx->poller_state = EinkWavesharePollerStateInit; + + const uint8_t block0_3[] = { + 0x57, 0x53, 0x44, 0xC8, 0x5A, 0x31, 0x30, 0x6D, 0x36, 0, 0, 0, 0x00, 0x00, 0x00, 0x00}; + memcpy(ctx->buf, block0_3, sizeof(block0_3)); + + device->screen_context = ctx; + return device; +} + +static void eink_waveshare_free(NfcEinkScreenDevice* instance) { + furi_assert(instance); + nfc_device_free(instance->nfc_device); + free(instance->screen_context); +} + +void eink_waveshare_parse_config(NfcEinkScreen* screen, const uint8_t* data, uint8_t data_length) { + UNUSED(data_length); + + uint8_t protocol_type = data[0]; + + screen->device->screen_type = + eink_waveshare_config_translate_protocol_to_screen_type(protocol_type); + eink_waveshare_on_config_received(screen); +} + +const NfcEinkScreenHandlers waveshare_handlers = { + .alloc = eink_waveshare_alloc, + .free = eink_waveshare_free, + .listener_callback = eink_waveshare_listener_callback, + .poller_callback = eink_waveshare_poller_callback, +}; diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare.h b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare.h new file mode 100644 index 00000000..021e5283 --- /dev/null +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare.h @@ -0,0 +1,3 @@ +#pragma once +#include +#include "../nfc_eink_types.h" \ No newline at end of file diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.c b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.c new file mode 100644 index 00000000..0587b05f --- /dev/null +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.c @@ -0,0 +1,43 @@ +#include "eink_waveshare_config.h" + +#define TAG "WSH_Config" + +EinkScreenTypeWaveshare + eink_waveshare_config_translate_screen_type_to_protocol(NfcEinkScreenType common_screen_type) { + switch(common_screen_type) { + case NfcEinkScreenTypeWaveshare2n13inch: + return EinkScreenTypeWaveshare2n13inch; + case NfcEinkScreenTypeWaveshare2n7inch: + return EinkScreenTypeWaveshare2n7inch; + case NfcEinkScreenTypeWaveshare2n9inch: + return EinkScreenTypeWaveshare2n9inch; + case NfcEinkScreenTypeWaveshare4n2inch: + return EinkScreenTypeWaveshare4n2inch; + case NfcEinkScreenTypeWaveshare7n5inch: + return EinkScreenTypeWaveshare7n5inch; + default: + FURI_LOG_E(TAG, "Unknown waveshare screen type 0x%02X", common_screen_type); + furi_crash(); + } +} + +NfcEinkScreenType eink_waveshare_config_translate_protocol_to_screen_type( + EinkScreenTypeWaveshare protocol_screen_type) { + NfcEinkScreenType common_type = NfcEinkScreenTypeUnknown; + + if(protocol_screen_type == EinkScreenTypeWaveshare2n13inch) { + common_type = NfcEinkScreenTypeWaveshare2n13inch; + } else if(protocol_screen_type == EinkScreenTypeWaveshare2n9inch) { + common_type = NfcEinkScreenTypeWaveshare2n9inch; + } else if(protocol_screen_type == EinkScreenTypeWaveshare2n7inch) { + common_type = NfcEinkScreenTypeWaveshare2n7inch; + } else if(protocol_screen_type == EinkScreenTypeWaveshare4n2inch) { + common_type = NfcEinkScreenTypeWaveshare4n2inch; + } else if( + protocol_screen_type == EinkScreenTypeWaveshare7n5inch || + protocol_screen_type == EinkScreenTypeWaveshare7n5inchV2) { + common_type = NfcEinkScreenTypeWaveshare7n5inch; + } + + return common_type; +} diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.h b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.h new file mode 100644 index 00000000..11d211fe --- /dev/null +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.h @@ -0,0 +1,16 @@ +#pragma once +#include "eink_waveshare_i.h" + +typedef enum { + EinkScreenTypeWaveshare2n13inch = 4, + EinkScreenTypeWaveshare2n7inch = 16, + EinkScreenTypeWaveshare2n9inch = 7, + EinkScreenTypeWaveshare4n2inch = 10, + EinkScreenTypeWaveshare7n5inch = 12, + EinkScreenTypeWaveshare7n5inchV2 = 14, +} EinkScreenTypeWaveshare; + +EinkScreenTypeWaveshare + eink_waveshare_config_translate_screen_type_to_protocol(NfcEinkScreenType common_screen_type); +NfcEinkScreenType eink_waveshare_config_translate_protocol_to_screen_type( + EinkScreenTypeWaveshare protocol_screen_type); diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_i.h b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_i.h new file mode 100644 index 00000000..7ad477c7 --- /dev/null +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_i.h @@ -0,0 +1,84 @@ +#pragma once +#include "../nfc_eink_screen_i.h" +#include +#include +#include +#include + +#define EINK_WAVESHARE_COMMAND_SPECIFIC 0xCD +#define EINK_WAVESHARE_COMMAND_READ_PAGE 0x30 +#define EINK_WAVESHARE_COMMAND_WRITE_PAGE 0xA2 +#define EINK_WAVESHARE_COMMAND_FF_FE 0xFF +#define EINK_WAVESHARE_COMMAND_SELECT_TYPE 0x00 +#define EINK_WAVESHARE_COMMAND_INIT 0x0D +#define EINK_WAVESHARE_COMMAND_DATA_WRITE 0x08 +#define EINK_WAVESHARE_COMMAND_POWER_ON_V2 0x18 +#define EINK_WAVESHARE_COMMAND_DATA_WRITE_V2 0x19 +#define EINK_WAVESHARE_COMMAND_NORMAL_MODE 0x01 +#define EINK_WAVESHARE_COMMAND_SET_CONFIG_1 0x02 +#define EINK_WAVESHARE_COMMAND_POWER_ON 0x03 +#define EINK_WAVESHARE_COMMAND_POWER_OFF 0x04 +#define EINK_WAVESHARE_COMMAND_SET_CONFIG_2 0x05 +#define EINK_WAVESHARE_COMMAND_LOAD_TO_MAIN 0x06 +#define EINK_WAVESHARE_COMMAND_PREPARE_DATA 0x07 +#define EINK_WAVESHARE_COMMAND_REFRESH 0x09 +#define EINK_WAVESHARE_COMMAND_WAIT_FOR_READY 0x0A + +typedef enum { + NfcEinkWaveshareListenerStateIdle, + NfcEinkWaveshareListenerStateWaitingForConfig, + NfcEinkWaveshareListenerStateReadingBlocks, + NfcEinkWaveshareListenerStateUpdatedSuccefully, +} NfcEinkWaveshareListenerStates; + +typedef enum { + EinkWavesharePollerStateInit, + EinkWavesharePollerStateSelectType, + EinkWavesharePollerStateSetNormalMode, + EinkWavesharePollerStateSetConfig1, + EinkWavesharePollerStatePowerOn, + EinkWavesharePollerStateSetConfig2, + EinkWavesharePollerStateLoadToMain, + EinkWavesharePollerStatePrepareData, + EinkWavesharePollerStateSendImageData, + EinkWavesharePollerStatePowerOnV2, + EinkWavesharePollerStateRefresh, + EinkWavesharePollerStateWaitReady, + EinkWavesharePollerStateFinish, + EinkWavesharePollerStateError, + EinkWavesharePollerStateNum +} EinkWavesharePollerState; + +typedef struct { + NfcEinkWaveshareListenerStates listener_state; + EinkWavesharePollerState poller_state; + size_t data_index; + uint16_t block_number; // TODO: try to remove this + uint8_t buf[16 * 4]; +} NfcEinkWaveshareSpecificContext; + +#define eink_waveshare_on_done(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeFinish) + +#define eink_waveshare_on_config_received(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeConfigurationReceived) + +#define eink_waveshare_on_target_detected(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeTargetDetected) + +#define eink_waveshare_on_target_lost(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeTargetLost) + +#define eink_waveshare_on_block_processed(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeBlockProcessed) + +#define eink_waveshare_on_updating(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeUpdating) + +#define eink_waveshare_on_error(instance) \ + nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeFailure) + +NfcCommand eink_waveshare_listener_callback(NfcGenericEvent event, void* context); +NfcCommand eink_waveshare_poller_callback(NfcGenericEvent event, void* context); + +void eink_waveshare_parse_config(NfcEinkScreen* screen, const uint8_t* data, uint8_t data_length); diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_listener.c b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_listener.c new file mode 100644 index 00000000..712b1a12 --- /dev/null +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_listener.c @@ -0,0 +1,232 @@ +#include "eink_waveshare_i.h" + +#define TAG "WSH_Listener" + +typedef NfcCommand ( + *EinkWavashareListenerCommandCallback)(NfcEinkScreen* screen, const uint8_t* data); + +typedef struct { + uint8_t cmd; + EinkWavashareListenerCommandCallback callback; +} NfcEinkWaveshareListenerCommandHandler; + +static NfcCommand eink_waveshare_listener_FF_cmd(NfcEinkScreen* screen, const uint8_t* data) { + if(data[1] == 0xFE) { + bit_buffer_append_byte(screen->tx_buf, 0xEE); + bit_buffer_append_byte(screen->tx_buf, 0xFF); + } + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_listener_read_page_cmd(NfcEinkScreen* screen, const uint8_t* data) { + uint8_t index = data[1]; + FURI_LOG_D(TAG, "Read page: %d", index); + + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + bit_buffer_append_bytes(screen->tx_buf, &ctx->buf[index * 4], 4 * 4); + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_listener_write_page_cmd(NfcEinkScreen* screen, const uint8_t* data) { + uint8_t index = data[1]; + + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + uint8_t* blocks = ctx->buf; + + memcpy(&blocks[index * 4], &data[2], 4); + bit_buffer_append_byte(screen->tx_buf, 0x0A); + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_listener_default_cmd_response(NfcEinkScreen* screen, const uint8_t* data) { + UNUSED(data); + bit_buffer_append_byte(screen->tx_buf, 0x00); + bit_buffer_append_byte(screen->tx_buf, 0x00); + return NfcCommandContinue; +} + +static NfcCommand eink_waveshare_listener_unknown_cmd(NfcEinkScreen* screen, const uint8_t* data) { + FURI_LOG_D(TAG, "Unknown: %02X %02X", data[0], data[1]); + return eink_waveshare_listener_default_cmd_response(screen, data); +} + +static NfcCommand + eink_waveshare_listener_select_type_cmd(NfcEinkScreen* screen, const uint8_t* data) { + eink_waveshare_parse_config(screen, data + 2, 1); + return eink_waveshare_listener_default_cmd_response(screen, data); +} + +static NfcCommand + eink_waveshare_listener_power_off_cmd(NfcEinkScreen* screen, const uint8_t* data) { + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + ctx->listener_state = NfcEinkWaveshareListenerStateUpdatedSuccefully; + return eink_waveshare_listener_default_cmd_response(screen, data); +} + +static NfcCommand eink_waveshare_listener_init_cmd(NfcEinkScreen* screen, const uint8_t* data) { + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + ctx->listener_state = NfcEinkWaveshareListenerStateWaitingForConfig; + eink_waveshare_on_target_detected(screen); + return eink_waveshare_listener_default_cmd_response(screen, data); +} + +static NfcCommand + eink_waveshare_listener_data_write_cmd(NfcEinkScreen* screen, const uint8_t* data) { + NfcEinkScreenData* const screen_data = screen->data; + NfcEinkScreenDevice* const screen_device = screen->device; + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + memcpy(screen_data->image_data + screen_device->received_data, &data[3], data[2]); + screen_device->received_data += data[2]; + + ctx->listener_state = NfcEinkWaveshareListenerStateReadingBlocks; + return eink_waveshare_listener_default_cmd_response(screen, data); +} + +static NfcCommand + eink_waveshare_listener_wait_for_ready_cmd(NfcEinkScreen* screen, const uint8_t* data) { + UNUSED(data); + bit_buffer_append_byte(screen->tx_buf, 0xFF); + bit_buffer_append_byte(screen->tx_buf, 0x00); + return NfcCommandContinue; +} + +static const NfcEinkWaveshareListenerCommandHandler nfc_eink_waveshare_commands[] = { + { + .cmd = EINK_WAVESHARE_COMMAND_FF_FE, + .callback = eink_waveshare_listener_FF_cmd, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_WRITE_PAGE, + .callback = eink_waveshare_listener_write_page_cmd, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_READ_PAGE, + .callback = eink_waveshare_listener_read_page_cmd, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_SELECT_TYPE, + .callback = eink_waveshare_listener_select_type_cmd, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_POWER_OFF, + .callback = eink_waveshare_listener_power_off_cmd, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_INIT, + .callback = eink_waveshare_listener_init_cmd, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_DATA_WRITE, + .callback = eink_waveshare_listener_data_write_cmd, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_DATA_WRITE_V2, + .callback = eink_waveshare_listener_data_write_cmd, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_WAIT_FOR_READY, + .callback = eink_waveshare_listener_wait_for_ready_cmd, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_NORMAL_MODE, + .callback = eink_waveshare_listener_default_cmd_response, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_SET_CONFIG_1, + .callback = eink_waveshare_listener_default_cmd_response, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_POWER_ON, + .callback = eink_waveshare_listener_default_cmd_response, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_SET_CONFIG_2, + .callback = eink_waveshare_listener_default_cmd_response, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_LOAD_TO_MAIN, + .callback = eink_waveshare_listener_default_cmd_response, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_PREPARE_DATA, + .callback = eink_waveshare_listener_default_cmd_response, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_REFRESH, + .callback = eink_waveshare_listener_default_cmd_response, + }, + { + .cmd = EINK_WAVESHARE_COMMAND_POWER_ON_V2, + .callback = eink_waveshare_listener_default_cmd_response, + }, +}; + +static EinkWavashareListenerCommandCallback + eink_waveshare_listener_get_command_callback(const uint8_t* command) { + furi_assert(command); + uint8_t cmd_code = (command[0] == EINK_WAVESHARE_COMMAND_SPECIFIC) ? command[1] : command[0]; + + EinkWavashareListenerCommandCallback callback = eink_waveshare_listener_unknown_cmd; + for(size_t i = 0; i < COUNT_OF(nfc_eink_waveshare_commands); i++) { + if(nfc_eink_waveshare_commands[i].cmd != cmd_code) continue; + callback = nfc_eink_waveshare_commands[i].callback; + break; + } + return callback; +} + +NfcCommand eink_waveshare_listener_process(Nfc* nfc, NfcEinkScreen* screen, const uint8_t* data) { + NfcCommand command = NfcCommandContinue; + bit_buffer_reset(screen->tx_buf); + + EinkWavashareListenerCommandCallback handler = + eink_waveshare_listener_get_command_callback(data); + if(handler != NULL) { + command = handler(screen, data); + iso14443_crc_append(Iso14443CrcTypeA, screen->tx_buf); + } + + if(bit_buffer_get_size_bytes(screen->tx_buf) > 0) { + NfcError error = nfc_listener_tx(nfc, screen->tx_buf); + if(error != NfcErrorNone) { + FURI_LOG_E(TAG, "Tx error"); + } + } + + return command; +} + +NfcCommand eink_waveshare_listener_callback(NfcGenericEvent event, void* context) { + NfcCommand command = NfcCommandContinue; + NfcEinkScreen* screen = context; + Iso14443_3aListenerEvent* Iso14443_3a_event = event.event_data; + + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + if(Iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedData) { + FURI_LOG_D(TAG, "ReceivedData"); + } else if(Iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff) { + FURI_LOG_D(TAG, "FieldOff"); + if(ctx->listener_state == NfcEinkWaveshareListenerStateUpdatedSuccefully) + eink_waveshare_on_done(screen); + else if(ctx->listener_state != NfcEinkWaveshareListenerStateIdle) + eink_waveshare_on_target_lost(screen); + command = NfcCommandStop; + } else if(Iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted) { + FURI_LOG_D(TAG, "Halted"); + if(ctx->listener_state == NfcEinkWaveshareListenerStateUpdatedSuccefully) + eink_waveshare_on_done(screen); + } else if(Iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) { + BitBuffer* buffer = Iso14443_3a_event->data->buffer; + + const uint8_t* data = bit_buffer_get_data(buffer); + command = eink_waveshare_listener_process(event.instance, screen, data); + } + + return command; +} diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_poller.c b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_poller.c new file mode 100644 index 00000000..84f984df --- /dev/null +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_poller.c @@ -0,0 +1,300 @@ +#include "eink_waveshare_i.h" +#include "eink_waveshare_config.h" + +#define TAG "WSH_Poller" + +typedef NfcCommand ( + *EinkWavesharePollerStateHandler)(Iso14443_3aPoller* poller, NfcEinkScreen* screen); + +static bool eink_waveshare_send_data( + Iso14443_3aPoller* poller, + const NfcEinkScreen* screen, + const uint8_t* data, + const size_t data_len) { + furi_assert(poller); + furi_assert(screen); + furi_assert(data); + furi_assert(data_len > 0); + //furi_assert(validator); + + bit_buffer_reset(screen->tx_buf); + bit_buffer_reset(screen->rx_buf); + + bit_buffer_append_bytes(screen->tx_buf, data, data_len); + + Iso14443_3aError error = + iso14443_3a_poller_send_standard_frame(poller, screen->tx_buf, screen->rx_buf, 0); + + bool result = false; + if(error == Iso14443_3aErrorNone) { + size_t response_len = bit_buffer_get_size_bytes(screen->rx_buf); + const uint8_t* response = bit_buffer_get_data(screen->rx_buf); + + result = ((response_len == 2) && (response[0] == 0) && (response[1] == 0)) || + ((response_len == 2) && (response[0] == 0xFF) && (response[1] == 0)); + + //result = validator(response, response_len); + } else { + FURI_LOG_E(TAG, "Iso14443_3aError: %02X", error); + } + return result; +} + +static bool eink_waveshare_send_command( + Iso14443_3aPoller* poller, + NfcEinkScreen* screen, + const uint8_t command, + const uint8_t* command_data, + const size_t command_data_length) { + size_t data_len = command_data_length + 2; + uint8_t* data = malloc(data_len); + + data[0] = EINK_WAVESHARE_COMMAND_SPECIFIC; + data[1] = command; + + if(command_data_length > 0) { + furi_assert(command_data); + memcpy(&data[2], command_data, command_data_length); + } + + bool result = eink_waveshare_send_data(poller, screen, data, data_len); + free(data); + + return result; +} + +static EinkWavesharePollerState eink_waveshare_poller_state_generic_handler( + Iso14443_3aPoller* poller, + NfcEinkScreen* screen, + uint8_t command, + EinkWavesharePollerState success_state) { + bool result = eink_waveshare_send_command(poller, screen, command, NULL, 0); + return result ? success_state : EinkWavesharePollerStateError; +} + +static NfcCommand + eink_waveshare_poller_state_init(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Init"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + ctx->poller_state = eink_waveshare_poller_state_generic_handler( + poller, screen, EINK_WAVESHARE_COMMAND_INIT, EinkWavesharePollerStateSelectType); + if(ctx->poller_state != EinkWavesharePollerStateError) { + eink_waveshare_on_target_detected(screen); + } + + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_poller_state_select_type(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Select type"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + ctx->poller_state = EinkWavesharePollerStateError; + + do { + EinkScreenTypeWaveshare screen_type = + eink_waveshare_config_translate_screen_type_to_protocol( + screen->data->base.screen_type); + bool result = eink_waveshare_send_command( + poller, screen, EINK_WAVESHARE_COMMAND_SELECT_TYPE, &screen_type, sizeof(screen_type)); + if(!result) break; + + ctx->poller_state = EinkWavesharePollerStateSetNormalMode; + } while(false); + + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_poller_state_set_normal_mode(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Mode set"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + ctx->poller_state = eink_waveshare_poller_state_generic_handler( + poller, screen, EINK_WAVESHARE_COMMAND_NORMAL_MODE, EinkWavesharePollerStateSetConfig1); + + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_poller_state_set_config_1(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Set config 1"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + ctx->poller_state = eink_waveshare_poller_state_generic_handler( + poller, screen, EINK_WAVESHARE_COMMAND_SET_CONFIG_1, EinkWavesharePollerStatePowerOn); + + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_poller_state_power_on(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Power On"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + ctx->poller_state = eink_waveshare_poller_state_generic_handler( + poller, screen, EINK_WAVESHARE_COMMAND_POWER_ON, EinkWavesharePollerStateSetConfig2); + + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_poller_state_set_config_2(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Set config 2"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + ctx->poller_state = eink_waveshare_poller_state_generic_handler( + poller, screen, EINK_WAVESHARE_COMMAND_SET_CONFIG_2, EinkWavesharePollerStateLoadToMain); + + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_poller_state_load_to_main(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Load 2 main"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + ctx->poller_state = eink_waveshare_poller_state_generic_handler( + poller, screen, EINK_WAVESHARE_COMMAND_LOAD_TO_MAIN, EinkWavesharePollerStatePrepareData); + + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_poller_state_prepare_data(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Prepare data"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + ctx->poller_state = eink_waveshare_poller_state_generic_handler( + poller, screen, EINK_WAVESHARE_COMMAND_PREPARE_DATA, EinkWavesharePollerStateSendImageData); + + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_poller_state_send_image_data(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Send data"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + uint8_t block_size = screen->data->base.data_block_size; + size_t data_len = block_size + 3; + uint8_t* data = malloc(data_len); + + data[0] = EINK_WAVESHARE_COMMAND_SPECIFIC; + data[1] = EINK_WAVESHARE_COMMAND_DATA_WRITE; //or EINK_WAVESHARE_COMMAND_DATA_WRITE_V2 + data[2] = block_size; + memcpy(&data[3], &screen->data->image_data[ctx->data_index], block_size); + ctx->poller_state = EinkWavesharePollerStateError; + + do { + bool result = eink_waveshare_send_data(poller, screen, data, data_len); + if(!result) break; + + ctx->data_index += block_size; + ctx->block_number++; + ctx->poller_state = (ctx->block_number == screen->device->block_total) ? + EinkWavesharePollerStatePowerOnV2 : + EinkWavesharePollerStateSendImageData; + + } while(false); + + eink_waveshare_on_block_processed(screen); + screen->device->block_current = ctx->block_number; + + free(data); + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_poller_state_power_on_v2(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Power On v2"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + ctx->poller_state = eink_waveshare_poller_state_generic_handler( + poller, screen, EINK_WAVESHARE_COMMAND_POWER_ON_V2, EinkWavesharePollerStateRefresh); + + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_poller_state_refresh(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Refresh"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + ctx->poller_state = eink_waveshare_poller_state_generic_handler( + poller, screen, EINK_WAVESHARE_COMMAND_REFRESH, EinkWavesharePollerStateWaitReady); + + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_poller_state_wait_ready(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Wait ready"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + + ctx->poller_state = eink_waveshare_poller_state_generic_handler( + poller, screen, EINK_WAVESHARE_COMMAND_WAIT_FOR_READY, EinkWavesharePollerStateFinish); + + return NfcCommandContinue; +} + +static NfcCommand + eink_waveshare_poller_state_finish(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_D(TAG, "Finish"); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + NfcCommand command = NfcCommandContinue; + + ctx->poller_state = eink_waveshare_poller_state_generic_handler( + poller, screen, EINK_WAVESHARE_COMMAND_POWER_OFF, EinkWavesharePollerStateFinish); + if(ctx->poller_state == EinkWavesharePollerStateFinish) { + eink_waveshare_on_done(screen); + command = NfcCommandStop; + } else { + ctx->poller_state = EinkWavesharePollerStateError; + } + + return command; +} + +static NfcCommand + eink_waveshare_poller_state_error_handler(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + FURI_LOG_E(TAG, "Error!"); + UNUSED(poller); + eink_waveshare_on_error(screen); + return NfcCommandStop; +} + +static const EinkWavesharePollerStateHandler handlers[EinkWavesharePollerStateNum] = { + [EinkWavesharePollerStateInit] = eink_waveshare_poller_state_init, + [EinkWavesharePollerStateSelectType] = eink_waveshare_poller_state_select_type, + [EinkWavesharePollerStateSetNormalMode] = eink_waveshare_poller_state_set_normal_mode, + [EinkWavesharePollerStateSetConfig1] = eink_waveshare_poller_state_set_config_1, + [EinkWavesharePollerStatePowerOn] = eink_waveshare_poller_state_power_on, + [EinkWavesharePollerStateSetConfig2] = eink_waveshare_poller_state_set_config_2, + [EinkWavesharePollerStateLoadToMain] = eink_waveshare_poller_state_load_to_main, + [EinkWavesharePollerStatePrepareData] = eink_waveshare_poller_state_prepare_data, + [EinkWavesharePollerStateSendImageData] = eink_waveshare_poller_state_send_image_data, + [EinkWavesharePollerStatePowerOnV2] = eink_waveshare_poller_state_power_on_v2, + [EinkWavesharePollerStateRefresh] = eink_waveshare_poller_state_refresh, + [EinkWavesharePollerStateWaitReady] = eink_waveshare_poller_state_wait_ready, + [EinkWavesharePollerStateFinish] = eink_waveshare_poller_state_finish, + [EinkWavesharePollerStateError] = eink_waveshare_poller_state_error_handler, +}; + +NfcCommand eink_waveshare_poller_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + NfcEinkScreen* screen = context; + + NfcCommand command = NfcCommandContinue; + + Iso14443_3aPollerEvent* Iso14443_3a_event = event.event_data; + Iso14443_3aPoller* poller = event.instance; + + if(Iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + command = handlers[ctx->poller_state](poller, screen); + } + if(command == NfcCommandReset) iso14443_3a_poller_halt(poller); + return command; +} diff --git a/nfc_eink/scenes/scene_choose_manufacturer.c b/nfc_eink/scenes/scene_choose_manufacturer.c new file mode 100644 index 00000000..5dcde3a2 --- /dev/null +++ b/nfc_eink/scenes/scene_choose_manufacturer.c @@ -0,0 +1,44 @@ +#include "../nfc_eink_app.h" + +static void nfc_eink_scene_choose_manufacturer_submenu_callback(void* context, uint32_t index) { + NfcEinkApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, index); +} + +void nfc_eink_scene_choose_manufacturer_on_enter(void* context) { + NfcEinkApp* instance = context; + Submenu* submenu = instance->submenu; + + submenu_set_header(submenu, "Choose Manufacturer"); + for(uint8_t type = 0; type < NfcEinkManufacturerNum; type++) { + submenu_add_item( + submenu, + nfc_eink_screen_get_manufacturer_name(type), + type, + nfc_eink_scene_choose_manufacturer_submenu_callback, + instance); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewMenu); +} + +bool nfc_eink_scene_choose_manufacturer_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + SceneManager* scene_manager = instance->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const NfcEinkManufacturer screen_manufacturer = event.event; + instance->screen = + nfc_eink_screen_alloc(screen_manufacturer /* NfcEinkManufacturerWaveshare */); + consumed = true; + scene_manager_next_scene(scene_manager, NfcEinkAppSceneEmulate); + } + + return consumed; +} + +void nfc_eink_scene_choose_manufacturer_on_exit(void* context) { + NfcEinkApp* instance = context; + submenu_reset(instance->submenu); +} diff --git a/nfc_eink/scenes/scene_choose_screen.c b/nfc_eink/scenes/scene_choose_screen.c new file mode 100644 index 00000000..b348d2d7 --- /dev/null +++ b/nfc_eink/scenes/scene_choose_screen.c @@ -0,0 +1,80 @@ +#include "../nfc_eink_app.h" + +#define TAG "NfcEinkSceneChooseScreen" + +static void nfc_eink_scene_choose_screen_submenu_callback(void* context, uint32_t index) { + NfcEinkApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, index); +} + +static uint8_t nfc_eink_screen_info_filter_by_mode(NfcEinkApp* instance) { + uint8_t cnt = 0; + switch(instance->settings.write_mode) { + case NfcEinkWriteModeStrict: + cnt = nfc_eink_descriptor_filter_by_screen_type( + instance->arr, instance->info_temp->screen_type); + break; + case NfcEinkWriteModeResolution: + cnt = nfc_eink_descriptor_filter_by_screen_size( + instance->arr, instance->info_temp->screen_size); + break; + case NfcEinkWriteModeManufacturer: + cnt = nfc_eink_descriptor_filter_by_manufacturer( + instance->arr, instance->info_temp->screen_manufacturer); + break; + case NfcEinkWriteModeFree: + default: + cnt = nfc_eink_descriptor_get_all_usable(instance->arr); + break; + } + return cnt; +} + +void nfc_eink_scene_choose_screen_on_enter(void* context) { + NfcEinkApp* instance = context; + Submenu* submenu = instance->submenu; + + submenu_set_header(submenu, "Choose Screen Type"); + + EinkScreenInfoArray_init(instance->arr); + uint8_t cnt = nfc_eink_screen_info_filter_by_mode(instance); + + for(uint8_t i = 0; i < cnt; i++) { + const NfcEinkScreenInfo* item = *EinkScreenInfoArray_get(instance->arr, i); + submenu_add_item( + submenu, item->name, i, nfc_eink_scene_choose_screen_submenu_callback, instance); + FURI_LOG_W(TAG, "Item: %s, width: %d, height: %d", item->name, item->width, item->height); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewMenu); +} + +bool nfc_eink_scene_choose_screen_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + SceneManager* scene_manager = instance->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const uint32_t index = event.event; + const NfcEinkScreenInfo* item = *EinkScreenInfoArray_get(instance->arr, index); + instance->screen = nfc_eink_screen_alloc(item->screen_manufacturer); + nfc_eink_screen_init(instance->screen, item->screen_type); + + instance->screen_loaded = nfc_eink_screen_load_data( + furi_string_get_cstr(instance->file_path), instance->screen, instance->info_temp); + if(instance->screen_loaded) { + scene_manager_next_scene(scene_manager, NfcEinkAppSceneWrite); + } else { + FURI_LOG_E(TAG, "Unable to load image data"); + } + consumed = true; + } + + return consumed; +} + +void nfc_eink_scene_choose_screen_on_exit(void* context) { + NfcEinkApp* instance = context; + submenu_reset(instance->submenu); + EinkScreenInfoArray_clear(instance->arr); +} diff --git a/nfc_eink/scenes/scene_config.h b/nfc_eink/scenes/scene_config.h new file mode 100644 index 00000000..531de115 --- /dev/null +++ b/nfc_eink/scenes/scene_config.h @@ -0,0 +1,17 @@ +ADD_SCENE(nfc_eink, start, Start) +ADD_SCENE(nfc_eink, choose_manufacturer, ChooseManufacturer) +ADD_SCENE(nfc_eink, emulate, Emulate) +ADD_SCENE(nfc_eink, screen_menu, ScreenMenu) +ADD_SCENE(nfc_eink, show_image, ShowImage) +ADD_SCENE(nfc_eink, save_name, SaveName) +ADD_SCENE(nfc_eink, save_success, SaveSuccess) +ADD_SCENE(nfc_eink, file_select, FileSelect) +ADD_SCENE(nfc_eink, choose_screen, ChooseScreen) +ADD_SCENE(nfc_eink, write, Write) +ADD_SCENE(nfc_eink, write_done, WriteDone) +ADD_SCENE(nfc_eink, error, Error) +ADD_SCENE(nfc_eink, exit_confirm, ExitConfirm) +ADD_SCENE(nfc_eink, settings, Settings) +ADD_SCENE(nfc_eink, info, Info) +ADD_SCENE(nfc_eink, delete, Delete) +ADD_SCENE(nfc_eink, delete_success, DeleteSuccess) \ No newline at end of file diff --git a/nfc_eink/scenes/scene_delete.c b/nfc_eink/scenes/scene_delete.c new file mode 100644 index 00000000..d760d889 --- /dev/null +++ b/nfc_eink/scenes/scene_delete.c @@ -0,0 +1,52 @@ +#include "../nfc_eink_app.h" + +void nfc_eink_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { + NfcEinkApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_eink_scene_delete_on_enter(void* context) { + NfcEinkApp* app = context; + + // Setup Custom Widget view + FuriString* temp_str; + temp_str = furi_string_alloc(); + + furi_string_printf(temp_str, "\e#Delete %s?\e#", furi_string_get_cstr(app->file_name)); + widget_add_text_box_element( + app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, furi_string_get_cstr(temp_str), false); + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Cancel", nfc_eink_scene_delete_widget_callback, app); + widget_add_button_element( + app->widget, GuiButtonTypeRight, "Delete", nfc_eink_scene_delete_widget_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcEinkViewWidget); +} + +bool nfc_eink_scene_delete_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(instance->scene_manager); + } else if(event.event == GuiButtonTypeRight) { + if(nfc_eink_screen_delete(furi_string_get_cstr(instance->file_path))) { + scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneDeleteSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcEinkAppSceneStart); + } + consumed = true; + } + } + return consumed; +} + +void nfc_eink_scene_delete_on_exit(void* context) { + NfcEinkApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/nfc_eink/scenes/scene_delete_success.c b/nfc_eink/scenes/scene_delete_success.c new file mode 100644 index 00000000..12d4696d --- /dev/null +++ b/nfc_eink/scenes/scene_delete_success.c @@ -0,0 +1,40 @@ +#include "../nfc_eink_app.h" + +void nfc_eink_scene_delete_success_popup_callback(void* context) { + NfcEinkApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcEinkAppCustomEventExit); +} + +void nfc_eink_scene_delete_success_on_enter(void* context) { + NfcEinkApp* instance = context; + + // Setup view + Popup* popup = instance->popup; + popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62); + popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_eink_scene_delete_success_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewPopup); +} + +bool nfc_eink_scene_delete_success_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcEinkAppCustomEventExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcEinkAppSceneFileSelect); + } + } + return consumed; +} + +void nfc_eink_scene_delete_success_on_exit(void* context) { + NfcEinkApp* instance = context; + + // Clear view + popup_reset(instance->popup); +} diff --git a/nfc_eink/scenes/scene_emulate.c b/nfc_eink/scenes/scene_emulate.c new file mode 100644 index 00000000..ab1d0003 --- /dev/null +++ b/nfc_eink/scenes/scene_emulate.c @@ -0,0 +1,104 @@ +#include "../nfc_eink_app.h" + +#include +#include + +#define TAG "NfcEinkSceneEmulate" + +static void nfc_eink_stop_emulation(NfcEinkApp* instance, bool free_screen) { + furi_assert(instance); + nfc_listener_stop(instance->listener); + nfc_listener_free(instance->listener); + if(free_screen) { + nfc_eink_screen_free(instance->screen); + instance->screen_loaded = false; + } +} + +static void nfc_eink_emulate_callback(NfcEinkScreenEventType type, void* context) { + furi_assert(context); + NfcEinkApp* instance = context; + NfcEinkAppCustomEvents event = NfcEinkAppCustomEventProcessFinish; + + switch(type) { + case NfcEinkScreenEventTypeTargetDetected: + event = NfcEinkAppCustomEventTargetDetected; + FURI_LOG_D(TAG, "Target detected"); + break; + case NfcEinkScreenEventTypeFinish: + event = NfcEinkAppCustomEventProcessFinish; + break; + case NfcEinkScreenEventTypeTargetLost: + event = NfcEinkAppCustomEventTargetLost; + break; + case NfcEinkScreenEventTypeFailure: + event = NfcEinkAppCustomEventUnknownError; + break; + default: + FURI_LOG_E(TAG, "Event: %02X not implemented", type); + furi_crash(); + break; + } + + view_dispatcher_send_custom_event(instance->view_dispatcher, event); +} + +void nfc_eink_scene_emulate_on_enter(void* context) { + NfcEinkApp* instance = context; + + Widget* widget = instance->widget; + + widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_51x64); + widget_add_string_element(widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, "Emulating"); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewWidget); + + const NfcEinkScreen* screen = instance->screen; + nfc_eink_screen_set_callback(instance->screen, nfc_eink_emulate_callback, instance); + + NfcDevice* nfc_device = nfc_eink_screen_get_nfc_device(screen); + NfcProtocol protocol = nfc_device_get_protocol(nfc_device); + const NfcDeviceData* data = nfc_device_get_data(nfc_device, protocol); + instance->listener = nfc_listener_alloc(instance->nfc, protocol, data); + NfcGenericCallback cb = nfc_eink_screen_get_nfc_callback(screen, NfcModeListener); + nfc_listener_start(instance->listener, cb, instance->screen); + + nfc_eink_blink_emulate_start(instance); +} + +bool nfc_eink_scene_emulate_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + SceneManager* scene_manager = instance->scene_manager; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcEinkAppCustomEventProcessFinish) { + nfc_eink_stop_emulation(instance, false); + instance->screen_loaded = true; + scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneScreenMenu); + notification_message(instance->notifications, &sequence_success); + } else if(event.event == NfcEinkAppCustomEventTargetLost) { + nfc_eink_stop_emulation(instance, true); + scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneError); + notification_message(instance->notifications, &sequence_error); + } else if(event.event == NfcEinkAppCustomEventUnknownError) { + nfc_eink_stop_emulation(instance, true); + scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneError); + notification_message(instance->notifications, &sequence_error); + } + consumed = true; + } else if(event.type == SceneManagerEventTypeBack) { + nfc_eink_stop_emulation(instance, true); + scene_manager_previous_scene(scene_manager); + consumed = true; + } + + return consumed; +} + +void nfc_eink_scene_emulate_on_exit(void* context) { + NfcEinkApp* instance = context; + nfc_eink_blink_stop(instance); + widget_reset(instance->widget); +} diff --git a/nfc_eink/scenes/scene_error.c b/nfc_eink/scenes/scene_error.c new file mode 100644 index 00000000..c743f17a --- /dev/null +++ b/nfc_eink/scenes/scene_error.c @@ -0,0 +1,42 @@ +#include "../nfc_eink_app.h" + +void nfc_eink_scene_error_popup_callback(void* context) { + NfcEinkApp* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcEinkAppCustomEventTimerExpired); +} + +void nfc_eink_scene_error_on_enter(void* context) { + NfcEinkApp* instance = context; + + Popup* popup = instance->popup; + popup_set_icon(popup, 10, 14, &I_WarningDolphin_45x42); + popup_set_header(popup, "Error", 90, 26, AlignCenter, AlignCenter); + ///TODO: Text error should be determined from some error variable or from EinkScreen event + popup_set_text(popup, "Target Lost", 85, 40, AlignCenter, AlignCenter); + + popup_set_timeout(popup, 5000); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_eink_scene_error_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewPopup); +} + +bool nfc_eink_scene_error_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcEinkAppCustomEventTimerExpired) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcEinkAppSceneStart); + } + } + + return consumed; +} + +void nfc_eink_scene_error_on_exit(void* context) { + NfcEinkApp* nfc = context; + // Clear view + popup_reset(nfc->popup); +} diff --git a/nfc_eink/scenes/scene_exit_confirm.c b/nfc_eink/scenes/scene_exit_confirm.c new file mode 100644 index 00000000..33f69ce1 --- /dev/null +++ b/nfc_eink/scenes/scene_exit_confirm.c @@ -0,0 +1,48 @@ +#include "../nfc_eink_app.h" + +void nfc_eink_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) { + NfcEinkApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, result); +} + +void nfc_eink_scene_exit_confirm_on_enter(void* context) { + NfcEinkApp* instance = context; + DialogEx* dialog_ex = instance->dialog_ex; + + dialog_ex_set_left_button_text(dialog_ex, "Exit"); + dialog_ex_set_right_button_text(dialog_ex, "Stay"); + dialog_ex_set_header(dialog_ex, "Exit to Main Menu?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, "All unsaved data will be lost", 64, 12, AlignCenter, AlignTop); + dialog_ex_set_context(dialog_ex, instance); + dialog_ex_set_result_callback(dialog_ex, nfc_eink_scene_exit_confirm_dialog_callback); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewDialogEx); +} + +bool nfc_eink_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + consumed = scene_manager_previous_scene(instance->scene_manager); + } else if(event.event == DialogExResultLeft) { + nfc_eink_screen_free(instance->screen); + instance->screen_loaded = false; + scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcEinkAppSceneStart); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void nfc_eink_scene_exit_confirm_on_exit(void* context) { + NfcEinkApp* instance = context; + + // Clean view + dialog_ex_reset(instance->dialog_ex); +} diff --git a/nfc_eink/scenes/scene_file_select.c b/nfc_eink/scenes/scene_file_select.c new file mode 100644 index 00000000..114deaf6 --- /dev/null +++ b/nfc_eink/scenes/scene_file_select.c @@ -0,0 +1,22 @@ +#include "../nfc_eink_app.h" + +void nfc_eink_scene_file_select_on_enter(void* context) { + NfcEinkApp* instance = context; + + if(nfc_eink_load_from_file_select(instance)) { + scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneScreenMenu); + } else { + scene_manager_previous_scene(instance->scene_manager); + } +} + +bool nfc_eink_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + return consumed; +} + +void nfc_eink_scene_file_select_on_exit(void* context) { + UNUSED(context); +} diff --git a/nfc_eink/scenes/scene_info.c b/nfc_eink/scenes/scene_info.c new file mode 100644 index 00000000..e3b0aba0 --- /dev/null +++ b/nfc_eink/scenes/scene_info.c @@ -0,0 +1,55 @@ +#include "../nfc_eink_app.h" + +void nfc_eink_scene_info_on_enter(void* context) { + NfcEinkApp* instance = context; + + if(!instance->screen_loaded) { + const NfcEinkScreenInfo* info = instance->info_temp; + instance->screen = nfc_eink_screen_alloc(info->screen_manufacturer); + nfc_eink_screen_init(instance->screen, info->screen_type); + + if(!nfc_eink_screen_load_data( + furi_string_get_cstr(instance->file_path), instance->screen, info)) { + nfc_eink_screen_free(instance->screen); + instance->screen_loaded = false; + } else { + instance->screen_loaded = true; + } + } + const NfcEinkScreenInfo* info = nfc_eink_screen_get_image_info(instance->screen); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf(temp_str, "\e#Information\n"); + furi_string_cat_printf(temp_str, "Name: %s\n", info->name); + furi_string_cat_printf( + temp_str, + "Vendor: %s\n", + nfc_eink_screen_get_manufacturer_name(info->screen_manufacturer)); + furi_string_cat_printf(temp_str, "Resolution: %d x %d\n", info->width, info->height); + + furi_string_cat_printf( + temp_str, "Size: %d bytes\n", nfc_eink_screen_get_image_size(instance->screen)); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewWidget); +} + +bool nfc_eink_scene_info_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void nfc_eink_scene_info_on_exit(void* context) { + NfcEinkApp* instance = context; + widget_reset(instance->widget); + + if(!scene_manager_has_previous_scene(instance->scene_manager, NfcEinkAppSceneEmulate) && + instance->screen_loaded) { + nfc_eink_screen_free(instance->screen); + instance->screen_loaded = false; + } +} diff --git a/nfc_eink/scenes/scene_save_name.c b/nfc_eink/scenes/scene_save_name.c new file mode 100644 index 00000000..67c4465b --- /dev/null +++ b/nfc_eink/scenes/scene_save_name.c @@ -0,0 +1,71 @@ +#include "../nfc_eink_app.h" +#include "../nfc_eink_screen/nfc_eink_screen.h" +#include + +static void nfc_eink_text_input_callback(void* context) { + NfcEinkApp* instance = context; + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcEinkAppCustomEventTextInputDone); +} + +void nfc_eink_scene_save_name_on_enter(void* context) { + NfcEinkApp* instance = context; + TextInput* text_input = instance->text_input; + FuriString* folder_path = furi_string_alloc(); + + bool name_is_empty = furi_string_empty(instance->file_name); + if(name_is_empty) { + furi_string_set(folder_path, NFC_EINK_APP_FOLDER); + furi_string_set(instance->file_path, NFC_EINK_APP_FOLDER); + } else { + path_extract_dirname(furi_string_get_cstr(instance->file_path), folder_path); + } + + text_input_set_header_text(text_input, "Name the screen"); + text_input_set_result_callback( + text_input, + nfc_eink_text_input_callback, + instance, + instance->text_store, + NFC_EINK_NAME_SIZE, + name_is_empty); + + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + furi_string_get_cstr(folder_path), + NFC_EINK_APP_EXTENSION, + furi_string_get_cstr(instance->file_name)); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + furi_string_free(folder_path); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewTextInput); +} + +bool nfc_eink_scene_save_name_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcEinkAppCustomEventTextInputDone) { + if(!furi_string_empty(instance->file_name)) { + nfc_eink_screen_delete(furi_string_get_cstr(instance->file_path)); + furi_string_set(instance->file_path, NFC_EINK_APP_FOLDER); + } + + strcat(instance->text_store, NFC_EINK_APP_EXTENSION); + path_append(instance->file_path, instance->text_store); + + if(nfc_eink_screen_save(instance->screen, furi_string_get_cstr(instance->file_path))) { + scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneSaveSuccess); + dolphin_deed(DolphinDeedNfcSave); + } + consumed = true; + } + } + + return consumed; +} + +void nfc_eink_scene_save_name_on_exit(void* context) { + UNUSED(context); +} diff --git a/nfc_eink/scenes/scene_save_success.c b/nfc_eink/scenes/scene_save_success.c new file mode 100644 index 00000000..bae50d11 --- /dev/null +++ b/nfc_eink/scenes/scene_save_success.c @@ -0,0 +1,42 @@ +#include "../nfc_eink_app.h" + +void nfc_eink_scene_save_success_popup_callback(void* context) { + NfcEinkApp* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcEinkAppCustomEventTimerExpired); +} + +void nfc_eink_scene_save_success_on_enter(void* context) { + NfcEinkApp* instance = context; + + // Setup view + Popup* popup = instance->popup; + popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58); + popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_eink_scene_save_success_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewPopup); +} + +bool nfc_eink_scene_save_success_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcEinkAppCustomEventTimerExpired) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcEinkAppSceneStart); + } + } + + return consumed; +} + +void nfc_eink_scene_save_success_on_exit(void* context) { + NfcEinkApp* instance = context; + nfc_eink_screen_free(instance->screen); + instance->screen_loaded = false; + // Clear view + popup_reset(instance->popup); +} diff --git a/nfc_eink/scenes/scene_screen_menu.c b/nfc_eink/scenes/scene_screen_menu.c new file mode 100644 index 00000000..72679534 --- /dev/null +++ b/nfc_eink/scenes/scene_screen_menu.c @@ -0,0 +1,96 @@ +#include "../nfc_eink_app.h" + +#define TAG "NfcEinkSceneScreenMenu" + +typedef enum { + SubmenuIndexShow, + SubmenuIndexSave, + SubmenuIndexWrite, + SubmenuIndexDelete, + SubmenuIndexInfo, +} SubmenuIndex; + +static void nfc_eink_scene_screen_menu_submenu_callback(void* context, uint32_t index) { + NfcEinkApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, index); +} + +void nfc_eink_scene_screen_menu_on_enter(void* context) { + NfcEinkApp* instance = context; + Submenu* submenu = instance->submenu; + + bool after_emulation = + scene_manager_has_previous_scene(instance->scene_manager, NfcEinkAppSceneEmulate); + if(!after_emulation) { + submenu_add_item( + submenu, + "Write", + SubmenuIndexWrite, + nfc_eink_scene_screen_menu_submenu_callback, + instance); + submenu_add_item( + submenu, + "Delete", + SubmenuIndexDelete, + nfc_eink_scene_screen_menu_submenu_callback, + instance); + } else { + submenu_add_item( + submenu, + "Save", + SubmenuIndexSave, + nfc_eink_scene_screen_menu_submenu_callback, + instance); + } + submenu_add_item( + submenu, "Show", SubmenuIndexShow, nfc_eink_scene_screen_menu_submenu_callback, instance); + submenu_add_item( + submenu, "Info", SubmenuIndexInfo, nfc_eink_scene_screen_menu_submenu_callback, instance); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewMenu); +} + +static NfcEinkAppScene nfc_eink_scene_get_next_by_submenu_index(SubmenuIndex index) { + switch(index) { + case SubmenuIndexShow: + return NfcEinkAppSceneShowImage; + case SubmenuIndexSave: + return NfcEinkAppSceneSaveName; + case SubmenuIndexWrite: + return NfcEinkAppSceneChooseScreen; + case SubmenuIndexInfo: + return NfcEinkAppSceneInfo; + case SubmenuIndexDelete: + return NfcEinkAppSceneDelete; + default: + FURI_LOG_E(TAG, "Unknown scene index %d", index); + furi_crash(); + } +} + +bool nfc_eink_scene_screen_menu_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + SceneManager* scene_manager = instance->scene_manager; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const uint32_t submenu_index = event.event; + NfcEinkAppScene scene = nfc_eink_scene_get_next_by_submenu_index(submenu_index); + scene_manager_next_scene(scene_manager, scene); + consumed = true; + } else if(event.type == SceneManagerEventTypeBack) { + if(scene_manager_has_previous_scene(instance->scene_manager, NfcEinkAppSceneEmulate)) + scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneExitConfirm); + else + scene_manager_previous_scene(instance->scene_manager); + consumed = true; + } + + return consumed; +} + +void nfc_eink_scene_screen_menu_on_exit(void* context) { + NfcEinkApp* instance = context; + submenu_reset(instance->submenu); +} diff --git a/nfc_eink/scenes/scene_settings.c b/nfc_eink/scenes/scene_settings.c new file mode 100644 index 00000000..acadf2a0 --- /dev/null +++ b/nfc_eink/scenes/scene_settings.c @@ -0,0 +1,76 @@ +#include "../nfc_eink_app.h" + +static const char* nfc_eink_scene_settings_write_mode[] = { + "Strict", + "Size", + "Vendor", + "Free", +}; + +static const char* nfc_eink_scene_settings_invert_image[] = { + "OFF", + "ON", +}; + +static void nfc_eink_scene_settings_write_mode_change_callback(VariableItem* item) { + NfcEinkApp* instance = variable_item_get_context(item); + const uint8_t index = variable_item_get_current_value_index(item); + + instance->settings.write_mode = (NfcEinkWriteMode)index; + + variable_item_set_current_value_text(item, nfc_eink_scene_settings_write_mode[index]); +} + +static void nfc_eink_scene_settings_invert_image_change_callback(VariableItem* item) { + NfcEinkApp* instance = variable_item_get_context(item); + const uint8_t index = variable_item_get_current_value_index(item); + + instance->settings.invert_image = (index == 1); + + variable_item_set_current_value_text(item, nfc_eink_scene_settings_invert_image[index]); +} + +void nfc_eink_scene_settings_on_enter(void* context) { + NfcEinkApp* instance = context; + VariableItemList* var_item_list = instance->var_item_list; + + VariableItem* item; + uint8_t value_index = 0; + + item = variable_item_list_add( + var_item_list, + "Write mode", + COUNT_OF(nfc_eink_scene_settings_write_mode), + nfc_eink_scene_settings_write_mode_change_callback, + instance); + + value_index = instance->settings.write_mode; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, nfc_eink_scene_settings_write_mode[value_index]); + + item = variable_item_list_add( + var_item_list, + "Invert image", + COUNT_OF(nfc_eink_scene_settings_invert_image), + nfc_eink_scene_settings_invert_image_change_callback, + instance); + value_index = instance->settings.invert_image ? 1 : 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, nfc_eink_scene_settings_invert_image[value_index]); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewVarItemList); +} + +bool nfc_eink_scene_settings_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + + return false; +} + +void nfc_eink_scene_settings_on_exit(void* context) { + NfcEinkApp* instance = context; + + variable_item_list_reset(instance->var_item_list); + nfc_eink_save_settings(instance); +} diff --git a/nfc_eink/scenes/scene_show_image.c b/nfc_eink/scenes/scene_show_image.c new file mode 100644 index 00000000..7ec026b1 --- /dev/null +++ b/nfc_eink/scenes/scene_show_image.c @@ -0,0 +1,51 @@ +#include "../nfc_eink_app.h" + +void nfc_eink_scene_show_image_on_enter(void* context) { + NfcEinkApp* instance = context; + + if(!instance->screen_loaded) { + const NfcEinkScreenInfo* info = instance->info_temp; + instance->screen = nfc_eink_screen_alloc(info->screen_manufacturer); + nfc_eink_screen_init(instance->screen, info->screen_type); + + if(!nfc_eink_screen_load_data( + furi_string_get_cstr(instance->file_path), instance->screen, info)) { + nfc_eink_screen_free(instance->screen); + instance->screen_loaded = false; + } else { + instance->screen_loaded = true; + } + } + + ImageScroll* scroll = instance->image_scroll; + const NfcEinkScreenInfo* info = nfc_eink_screen_get_image_info(instance->screen); + + if(instance->screen_loaded) { + image_scroll_set_image( + scroll, + info->width, + info->height, + nfc_eink_screen_get_image_data(instance->screen), + instance->settings.invert_image); + } + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewImageScroll); +} + +bool nfc_eink_scene_show_image_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + + return false; +} + +void nfc_eink_scene_show_image_on_exit(void* context) { + NfcEinkApp* instance = context; + UNUSED(instance); + image_scroll_reset(instance->image_scroll); + + if(!scene_manager_has_previous_scene(instance->scene_manager, NfcEinkAppSceneEmulate) && + instance->screen_loaded) { + nfc_eink_screen_free(instance->screen); + instance->screen_loaded = false; + } +} diff --git a/nfc_eink/scenes/scene_start.c b/nfc_eink/scenes/scene_start.c new file mode 100644 index 00000000..ae60d28c --- /dev/null +++ b/nfc_eink/scenes/scene_start.c @@ -0,0 +1,54 @@ +#include "../nfc_eink_app.h" + +enum SubmenuIndex { + SubmenuIndexEmulate, + SubmenuIndexSaved, + SubmenuIndexSettings, +}; + +static void nfc_eink_scene_start_submenu_callback(void* context, uint32_t index) { + NfcEinkApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, index); +} + +void nfc_eink_scene_start_on_enter(void* context) { + NfcEinkApp* instance = context; + Submenu* submenu = instance->submenu; + + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, nfc_eink_scene_start_submenu_callback, instance); + submenu_add_item( + submenu, "Saved", SubmenuIndexSaved, nfc_eink_scene_start_submenu_callback, instance); + submenu_add_item( + submenu, "Settings", SubmenuIndexSettings, nfc_eink_scene_start_submenu_callback, instance); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewMenu); +} + +bool nfc_eink_scene_start_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + SceneManager* scene_manager = instance->scene_manager; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const uint32_t submenu_index = event.event; + if(submenu_index == SubmenuIndexEmulate) { + scene_manager_next_scene(scene_manager, NfcEinkAppSceneChooseManufacturer); + consumed = true; + } else if(submenu_index == SubmenuIndexSaved) { + scene_manager_next_scene(scene_manager, NfcEinkAppSceneFileSelect); + consumed = true; + } else if(submenu_index == SubmenuIndexSettings) { + scene_manager_next_scene(scene_manager, NfcEinkAppSceneSettings); + consumed = true; + } + } + + return consumed; +} + +void nfc_eink_scene_start_on_exit(void* context) { + NfcEinkApp* instance = context; + submenu_reset(instance->submenu); +} diff --git a/nfc_eink/scenes/scene_write.c b/nfc_eink/scenes/scene_write.c new file mode 100644 index 00000000..ebe7422b --- /dev/null +++ b/nfc_eink/scenes/scene_write.c @@ -0,0 +1,148 @@ +#include "../nfc_eink_app.h" + +#define TAG "NfcEinkSceneWrite" + +typedef enum { + NfcEinkAppSceneWriteStateWaitingForTarget, + NfcEinkAppSceneWriteStateWritingDataBlocks, + NfcEinkAppSceneWriteStateUpdatingScreen, +} NfcEinkAppSceneWriteStates; + +static void nfc_eink_write_callback(NfcEinkScreenEventType type, void* context) { + furi_assert(context); + NfcEinkApp* instance = context; + NfcEinkAppCustomEvents event = NfcEinkAppCustomEventProcessFinish; + switch(type) { + case NfcEinkScreenEventTypeTargetDetected: + event = NfcEinkAppCustomEventTargetDetected; + FURI_LOG_D(TAG, "Target detected"); + break; + case NfcEinkScreenEventTypeFinish: + event = NfcEinkAppCustomEventProcessFinish; + break; + + case NfcEinkScreenEventTypeBlockProcessed: + event = NfcEinkAppCustomEventBlockProcessed; + break; + + case NfcEinkScreenEventTypeFailure: + event = NfcEinkAppCustomEventUnknownError; + break; + + case NfcEinkScreenEventTypeUpdating: + event = NfcEinkAppCustomEventUpdating; + break; + default: + FURI_LOG_E(TAG, "Event: %02X not implemented", type); + furi_crash(); + break; + } + view_dispatcher_send_custom_event(instance->view_dispatcher, event); +} + +static void nfc_eink_scene_write_show_waiting(const NfcEinkApp* instance) { + popup_reset(instance->popup); + popup_set_header(instance->popup, "Waiting", 97, 15, AlignCenter, AlignTop); + popup_set_text( + instance->popup, "Apply eink next\nto Flipper's back", 94, 27, AlignCenter, AlignTop); + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewPopup); +} + +static void nfc_eink_scene_write_show_writing_data(const NfcEinkApp* instance) { + eink_progress_reset(instance->eink_progress); + FuriString* str = furi_string_alloc_printf( + "\e#Writting...\n\e#%s", nfc_eink_screen_get_name(instance->screen)); + + eink_progress_set_header(instance->eink_progress, furi_string_get_cstr(str)); + + size_t total = 0, current = 0; + nfc_eink_screen_get_progress(instance->screen, ¤t, &total); + + eink_progress_set_value(instance->eink_progress, current, total); + furi_string_free(str); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewProgress); +} + +static void nfc_eink_scene_write_show_updating(const NfcEinkApp* instance) { + NfcEinkAppSceneWriteStates state = (NfcEinkAppSceneWriteStates)scene_manager_get_scene_state( + instance->scene_manager, NfcEinkAppSceneWrite); + + if(state != NfcEinkAppSceneWriteStateUpdatingScreen) { + popup_reset(instance->popup); + popup_set_header(instance->popup, "Updating...", 80, 15, AlignCenter, AlignTop); + popup_set_text( + instance->popup, "Wait until\nupdate complete", 75, 30, AlignCenter, AlignTop); + popup_set_icon(instance->popup, 5, 11, &I_ArrowC_1_36x36); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewPopup); + } + + scene_manager_set_scene_state( + instance->scene_manager, NfcEinkAppSceneWrite, NfcEinkAppSceneWriteStateUpdatingScreen); +} + +void nfc_eink_scene_write_on_enter(void* context) { + NfcEinkApp* instance = context; + + nfc_eink_scene_write_show_waiting(instance); + scene_manager_set_scene_state( + instance->scene_manager, NfcEinkAppSceneWrite, NfcEinkAppSceneWriteStateWaitingForTarget); + + NfcEinkScreen* screen = instance->screen; + + nfc_eink_screen_set_callback(instance->screen, nfc_eink_write_callback, instance); + const NfcProtocol protocol = nfc_device_get_protocol(nfc_eink_screen_get_nfc_device(screen)); + instance->poller = nfc_poller_alloc(instance->nfc, protocol); + + NfcGenericCallback cb = nfc_eink_screen_get_nfc_callback(screen, NfcModePoller); + nfc_poller_start(instance->poller, cb, screen); + + nfc_eink_blink_write_start(instance); +} + +bool nfc_eink_scene_write_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + SceneManager* scene_manager = instance->scene_manager; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcEinkAppCustomEventProcessFinish) { + scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneWriteDone); + notification_message(instance->notifications, &sequence_success); + } else if(event.event == NfcEinkAppCustomEventTargetDetected) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcEinkAppSceneWrite, + NfcEinkAppSceneWriteStateWritingDataBlocks); + nfc_eink_scene_write_show_writing_data(instance); + } else if(event.event == NfcEinkAppCustomEventUnknownError) { + scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneError); + notification_message(instance->notifications, &sequence_error); + } else if(event.event == NfcEinkAppCustomEventBlockProcessed) { + size_t total = 0, current = 0; + nfc_eink_screen_get_progress(instance->screen, ¤t, &total); + eink_progress_set_value(instance->eink_progress, current, total); + } else if(event.event == NfcEinkAppCustomEventUpdating) { + nfc_eink_scene_write_show_updating(instance); + } + consumed = true; + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_previous_scene(scene_manager); + consumed = true; + } + + return consumed; +} + +void nfc_eink_scene_write_on_exit(void* context) { + NfcEinkApp* instance = context; + nfc_eink_blink_stop(instance); + popup_reset(instance->popup); + eink_progress_reset(instance->eink_progress); + + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + nfc_eink_screen_free(instance->screen); + instance->screen_loaded = false; +} diff --git a/nfc_eink/scenes/scene_write_done.c b/nfc_eink/scenes/scene_write_done.c new file mode 100644 index 00000000..257161dd --- /dev/null +++ b/nfc_eink/scenes/scene_write_done.c @@ -0,0 +1,40 @@ +#include "../nfc_eink_app.h" + +void nfc_eink_scene_write_done_popup_callback(void* context) { + NfcEinkApp* instance = context; + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcEinkAppCustomEventTimerExpired); +} + +void nfc_eink_scene_write_done_on_enter(void* context) { + NfcEinkApp* instance = context; + + // Setup view + Popup* popup = instance->popup; + popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58); + popup_set_header(popup, "Done!", 5, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_eink_scene_write_done_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewPopup); +} + +bool nfc_eink_scene_write_done_on_event(void* context, SceneManagerEvent event) { + NfcEinkApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcEinkAppCustomEventTimerExpired) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcEinkAppSceneChooseScreen); + } + } + return consumed; +} + +void nfc_eink_scene_write_done_on_exit(void* context) { + NfcEinkApp* instance = context; + // Clear view + popup_reset(instance->popup); +} diff --git a/nfc_eink/scenes/scenes.c b/nfc_eink/scenes/scenes.c new file mode 100644 index 00000000..b835627b --- /dev/null +++ b/nfc_eink/scenes/scenes.c @@ -0,0 +1,30 @@ +#include "scenes.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const nfc_eink_on_enter_handlers[])(void*) = { +#include "scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const nfc_eink_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const nfc_eink_on_exit_handlers[])(void* context) = { +#include "scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers nfc_eink_scene_handlers = { + .on_enter_handlers = nfc_eink_on_enter_handlers, + .on_event_handlers = nfc_eink_on_event_handlers, + .on_exit_handlers = nfc_eink_on_exit_handlers, + .scene_num = NfcEinkAppSceneNum, +}; diff --git a/nfc_eink/scenes/scenes.h b/nfc_eink/scenes/scenes.h new file mode 100644 index 00000000..ad3be1e5 --- /dev/null +++ b/nfc_eink/scenes/scenes.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) NfcEinkAppScene##id, +typedef enum { +#include "scene_config.h" + NfcEinkAppSceneNum, +} NfcEinkAppScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers nfc_eink_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "scene_config.h" +#undef ADD_SCENE diff --git a/nfc_eink/views/eink_progress.c b/nfc_eink/views/eink_progress.c new file mode 100644 index 00000000..9f74f24c --- /dev/null +++ b/nfc_eink/views/eink_progress.c @@ -0,0 +1,93 @@ +#include "eink_progress.h" + +#include + +struct EinkProgress { + View* view; + void* context; +}; + +typedef struct { + FuriString* header; + uint16_t blocks_total; + uint16_t blocks_current; +} EinkProgressViewModel; + +static void eink_progress_draw_callback(Canvas* canvas, void* model) { + EinkProgressViewModel* m = model; + + FuriString* str = furi_string_alloc_printf("%d / %d", m->blocks_current, m->blocks_total); + elements_text_box( + canvas, 24, 5, 90, 40, AlignCenter, AlignCenter, furi_string_get_cstr(m->header), false); + + float value = (m->blocks_total == 0) ? 0 : + ((float)(m->blocks_current) / (float)(m->blocks_total)); + elements_progress_bar_with_text(canvas, 0, 37, 127, value, furi_string_get_cstr(str)); + furi_string_free(str); +} + +View* eink_progress_get_view(EinkProgress* instance) { + furi_assert(instance); + return instance->view; +} + +EinkProgress* eink_progress_alloc(void) { + EinkProgress* instance = malloc(sizeof(EinkProgress)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(EinkProgressViewModel)); + view_set_draw_callback(instance->view, eink_progress_draw_callback); + //view_set_input_callback(instance->view, dict_attack_input_callback); + view_set_context(instance->view, instance); + with_view_model( + instance->view, + EinkProgressViewModel * model, + { + model->header = furi_string_alloc(); + model->blocks_current = 0; + model->blocks_total = 0; + }, + false); + + return instance; +} + +void eink_progress_set_header(EinkProgress* instance, const char* header) { + with_view_model( + instance->view, + EinkProgressViewModel * model, + { furi_string_set_str(model->header, header); }, + true); +} + +void eink_progress_set_value(EinkProgress* instance, size_t value, size_t total) { + with_view_model( + instance->view, + EinkProgressViewModel * model, + { + model->blocks_current = value; + model->blocks_total = total; + }, + true); +} + +void eink_progress_reset(EinkProgress* instance) { + with_view_model( + instance->view, + EinkProgressViewModel * model, + { + furi_string_reset(model->header); + model->blocks_current = 0; + model->blocks_total = 0; + }, + false); +} + +void eink_progress_free(EinkProgress* instance) { + furi_assert(instance); + + with_view_model( + instance->view, EinkProgressViewModel * model, { furi_string_free(model->header); }, false); + + view_free(instance->view); + free(instance); +} diff --git a/nfc_eink/views/eink_progress.h b/nfc_eink/views/eink_progress.h new file mode 100644 index 00000000..6ce8d609 --- /dev/null +++ b/nfc_eink/views/eink_progress.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +typedef struct EinkProgress EinkProgress; + +EinkProgress* eink_progress_alloc(void); +void eink_progress_free(EinkProgress* instance); +void eink_progress_reset(EinkProgress* instance); +View* eink_progress_get_view(EinkProgress* instance); +void eink_progress_set_value(EinkProgress* instance, size_t value, size_t total); +void eink_progress_set_header(EinkProgress* instance, const char* header); diff --git a/nfc_eink/views/image_scroll.c b/nfc_eink/views/image_scroll.c new file mode 100644 index 00000000..e4a07de2 --- /dev/null +++ b/nfc_eink/views/image_scroll.c @@ -0,0 +1,165 @@ +#include "image_scroll.h" + +#define IMAGE_WINDOW_WIDTH (128) +#define IMAGE_WINDOW_HEIGHT (64) +#define IMAGE_PIXELS_PER_BYTE (8) + +#define IMAGE_WINDOW_BYTES_PER_ROW (IMAGE_WINDOW_WIDTH / IMAGE_PIXELS_PER_BYTE) +#define IMAGE_WINDOW_SIZE_BYTES (IMAGE_WINDOW_BYTES_PER_ROW * IMAGE_WINDOW_HEIGHT) + +struct ImageScroll { + void* context; + View* view; +}; + +typedef struct { + bool image_set; + bool invert_image; + + int16_t pos_x; + int16_t pos_y; + + uint16_t width; + uint16_t height; + uint8_t row_size_bytes; + const uint8_t* image; + uint8_t view_image[IMAGE_WINDOW_SIZE_BYTES]; +} ImageScrollViewModel; + +static void scroll_draw_callback(Canvas* canvas, void* model) { + ImageScrollViewModel* m = model; + canvas_draw_xbm_mirrored(canvas, 0, 0, IMAGE_WINDOW_WIDTH, IMAGE_WINDOW_HEIGHT, m->view_image); +} + +static inline int16_t image_scroll_calculate_new_pos(int16_t pos, int8_t step, int16_t max_pos) { + int16_t new_pos = pos + step; + + if(new_pos > max_pos) new_pos = max_pos; + if(new_pos < 0) new_pos = 0; + + return new_pos; +} + +static void + image_scroll_reverse_copy_row(const uint8_t* array, uint8_t* reverse_array, bool invert_image) { + for(int i = 0; i < IMAGE_WINDOW_BYTES_PER_ROW; i++) { + reverse_array[i] = invert_image ? ~array[IMAGE_WINDOW_BYTES_PER_ROW - 1 - i] : + array[IMAGE_WINDOW_BYTES_PER_ROW - 1 - i]; + } +} + +static void image_scroll_copy_to_window(ImageScrollViewModel* model) { + uint16_t image_offset = model->pos_y * model->row_size_bytes + model->pos_x; + for(uint16_t i = 0, j = 0; j < IMAGE_WINDOW_SIZE_BYTES; + i += model->row_size_bytes, j += IMAGE_WINDOW_BYTES_PER_ROW) + image_scroll_reverse_copy_row( + model->image + image_offset + i, model->view_image + j, model->invert_image); +} + +static void image_scroll_move(ImageScroll* instance, int8_t step_x, int8_t step_y) { + ImageScrollViewModel* model = view_get_model(instance->view); + + if(!model->image_set) return; + UNUSED(step_x); + + model->pos_x = image_scroll_calculate_new_pos( + model->pos_x, step_x, model->row_size_bytes - IMAGE_WINDOW_BYTES_PER_ROW); + model->pos_y = + image_scroll_calculate_new_pos(model->pos_y, step_y, model->width - IMAGE_WINDOW_HEIGHT); + + image_scroll_copy_to_window(model); + view_commit_model(instance->view, true); + view_set_orientation(instance->view, ViewOrientationHorizontalFlip); +} + +static bool scroll_input_callback(InputEvent* event, void* context) { + ImageScroll* scroll = context; + + int8_t x = 0, y = 0; + bool consumed = false; + if(event->key == InputKeyUp) { + y = 1; + consumed = true; + } else if(event->key == InputKeyDown) { + y = -1; + consumed = true; + } else if(event->key == InputKeyLeft) { + x = 1; + consumed = true; + } else if(event->key == InputKeyRight) { + x = -1; + consumed = true; + } + + if(event->type == InputTypeRepeat) { + y *= 10; + x *= 10; + } + + image_scroll_move(scroll, x, y); + return consumed; +} + +ImageScroll* image_scroll_alloc(void) { + ImageScroll* scroll = malloc(sizeof(ImageScroll)); + + scroll->view = view_alloc(); + view_allocate_model(scroll->view, ViewModelTypeLockFree, sizeof(ImageScrollViewModel)); + view_set_draw_callback(scroll->view, scroll_draw_callback); + view_set_input_callback(scroll->view, scroll_input_callback); + view_set_context(scroll->view, scroll); + return scroll; +} + +void image_scroll_free(ImageScroll* instance) { + furi_assert(instance); + view_free(instance->view); + free(instance); +} + +View* image_scroll_get_view(ImageScroll* instance) { + furi_assert(instance); + return instance->view; +} + +void image_scroll_reset(ImageScroll* instance) { + furi_assert(instance); + ImageScrollViewModel* model = view_get_model(instance->view); + + model->image = NULL; + memset(model->view_image, 0, sizeof(model->view_image)); + model->pos_x = 0; + model->pos_y = 0; + model->width = 0; + model->height = 0; + model->row_size_bytes = 0; + + model->image_set = false; +} + +void image_scroll_set_image( + ImageScroll* instance, + uint16_t width, + uint16_t height, + const uint8_t* image, + bool invert_image) { + furi_assert(instance); + furi_assert(image); + + ImageScrollViewModel* model = view_get_model(instance->view); + if(!model->image_set) { + model->height = height; + model->width = width; + model->invert_image = invert_image; + model->image = image; + model->pos_x = 0; + model->pos_y = 0; + + model->row_size_bytes = model->height / IMAGE_PIXELS_PER_BYTE; + if(model->height % IMAGE_PIXELS_PER_BYTE != 0) model->row_size_bytes++; + + model->image_set = true; + + image_scroll_move(instance, 0, 0); + } +} diff --git a/nfc_eink/views/image_scroll.h b/nfc_eink/views/image_scroll.h new file mode 100644 index 00000000..1b8464c6 --- /dev/null +++ b/nfc_eink/views/image_scroll.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +typedef struct ImageScroll ImageScroll; + +ImageScroll* image_scroll_alloc(void); +void image_scroll_free(ImageScroll* instance); +View* image_scroll_get_view(ImageScroll* instance); + +void image_scroll_reset(ImageScroll* instance); +void image_scroll_set_image( + ImageScroll* instance, + uint16_t width, + uint16_t height, + const uint8_t* image, + bool invert_image); From 979e4528a0f40db61c5c5eba4afd0dedc5b4898d Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Sun, 29 Sep 2024 18:46:53 +0300 Subject: [PATCH 02/10] Assets added --- nfc_eink/application.fam | 4 +++- nfc_eink/assets/ArrowC_1_36x36.png | Bin 0 -> 195 bytes nfc_eink/assets/DolphinDone_80x58.png | Bin 0 -> 448 bytes nfc_eink/assets/DolphinMafia_119x62.png | Bin 0 -> 668 bytes nfc_eink/assets/DolphinSaved_92x58.png | Bin 0 -> 475 bytes nfc_eink/assets/NFC_dolphin_emulation_51x64.png | Bin 0 -> 389 bytes nfc_eink/assets/NFC_manual_60x50.png | Bin 0 -> 309 bytes nfc_eink/assets/Nfc_10px.png | Bin 0 -> 96 bytes nfc_eink/assets/WarningDolphin_45x42.png | Bin 0 -> 258 bytes nfc_eink/assets/err_09.png | Bin 0 -> 193 bytes .../goodisplay/eink_goodisplay_i.h | 1 - nfc_eink/nfc_eink_screen/nfc_eink_screen.h | 2 -- nfc_eink/nfc_eink_screen/nfc_eink_types.h | 1 - .../waveshare/eink_waveshare_poller.c | 2 -- nfc_eink/scenes/scene_choose_manufacturer.c | 2 +- nfc_eink/scenes/scene_choose_screen.c | 2 +- nfc_eink/scenes/scene_delete.c | 2 +- nfc_eink/scenes/scene_delete_success.c | 2 +- nfc_eink/scenes/scene_emulate.c | 2 +- nfc_eink/scenes/scene_error.c | 2 +- nfc_eink/scenes/scene_exit_confirm.c | 2 +- nfc_eink/scenes/scene_file_select.c | 2 +- nfc_eink/scenes/scene_info.c | 2 +- nfc_eink/scenes/scene_save_name.c | 3 ++- nfc_eink/scenes/scene_save_success.c | 2 +- nfc_eink/scenes/scene_screen_menu.c | 2 +- nfc_eink/scenes/scene_settings.c | 2 +- nfc_eink/scenes/scene_show_image.c | 2 +- nfc_eink/scenes/scene_start.c | 2 +- nfc_eink/scenes/scene_write.c | 2 +- nfc_eink/scenes/scene_write_done.c | 2 +- nfc_eink/views/eink_progress.c | 1 - 32 files changed, 21 insertions(+), 25 deletions(-) create mode 100644 nfc_eink/assets/ArrowC_1_36x36.png create mode 100644 nfc_eink/assets/DolphinDone_80x58.png create mode 100644 nfc_eink/assets/DolphinMafia_119x62.png create mode 100644 nfc_eink/assets/DolphinSaved_92x58.png create mode 100644 nfc_eink/assets/NFC_dolphin_emulation_51x64.png create mode 100644 nfc_eink/assets/NFC_manual_60x50.png create mode 100644 nfc_eink/assets/Nfc_10px.png create mode 100644 nfc_eink/assets/WarningDolphin_45x42.png create mode 100644 nfc_eink/assets/err_09.png diff --git a/nfc_eink/application.fam b/nfc_eink/application.fam index b228c35a..2fa637cf 100644 --- a/nfc_eink/application.fam +++ b/nfc_eink/application.fam @@ -10,7 +10,9 @@ App( sources=[ "*.c*" ], - fap_libs=["assets", "mbedtls"], fap_icon="icon.png", + fap_icon_assets="assets", + fap_description="Application for emulation and writing to NFC Eink tags", + fap_version="1.0", fap_category="NFC", ) \ No newline at end of file diff --git a/nfc_eink/assets/ArrowC_1_36x36.png b/nfc_eink/assets/ArrowC_1_36x36.png new file mode 100644 index 0000000000000000000000000000000000000000..0f4d3e1b2d247c2f5884c6d97eeac2e39e0127e9 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>`VBp6OsFEs^HU7jwEAr*6C&u#QQ>>$E$;raW? z4O%l9QiYAgmR|2#@Ihsw`G=Vbh2pONu3Xsji?>tvK>TKbytxIk>I=oXyyeY*`={}q zbxtW2e~}cQ6P#dqJlSl1)0{rm_Vo`vqm}PzKe{8ZJWKRA>qnd2Md$ylF#MG1akKj4 u-uKQg)=!Q3y!KS|X{k8AmW_YsAJ$i#VgGX1l}o2UuJd&Db6Mw<&;$Sk#!+7Y literal 0 HcmV?d00001 diff --git a/nfc_eink/assets/DolphinDone_80x58.png b/nfc_eink/assets/DolphinDone_80x58.png new file mode 100644 index 0000000000000000000000000000000000000000..881aaa8d2aa6bbe8d563d04c9a0f445cbbbe8be8 GIT binary patch literal 448 zcmV;x0YCnUP)Ly)qzZ!K4#8r(UBSXa znAOIRW>$#V-OSGYE$(XJseTS0hyOcBl4wiH02<8!1DMkGfFup2QZj%Ebe2E?8{hM=Dav;RfV4N%e7sc)sXx_pc2Fw=j81b1#%aC+R)BGZ^ANyboj05S znF4fD_c*OpN2!+mz~Ka73Km-T0Br1Ck(><}EsYD90W2`k-QPcC+Vg`HFK_vDlK}TE zzYj#*$7lY@+Tu08SFI$h1t11{gy$E~DyS4yy_fF#REjDaX?|eKuE-m}*acS3E#m{Y z939dqs;{4lugsnGfCup0+~*fSJ9j-}w?_^GzGm!hu?`#T7!NPVAmfYCejB;~N5<+) qEC8EEYaa^W+CWKv(*WEJaQz2JY`H0gasz1q0000U#jZy<&!_A`tCFAQ<8DFSUOt%d=8PI1A_%sLXL~5FbUG$k^52i7tX#@DCnc(-6QHYtNM)i*dKbWzwU91F+0|Dco&uSS zM59EdWWbG8@1*NeB^fCXRh9Cl&m>u}eCGL@4;e%86M!t!SlTt|zGTSaRY^;=Y~1*n zG&EJJ$4$QnYf#~5`>`nP9%xt9p=3z$9p#ah{QZf)C`n$`&kvgY`mzskw}+CCRM$#m0q&RE@Ro)WqWEjrTAt0Xhgm0*bwF7Pz(! zE(HRP|9yOvo?if#fPl9j?>=x{IBAwlJ1$<;!9N-&m1O8Dze?2~IviG#1zhFv8t)&z1=`{NK7Xz2RhR?#Ew-iT_j>F%C8Jz%@g?j0_MuWFSx~OH_=OeG z-_IBUVQZ%~$iDFI=6BDQyAXU_X~)@9>ug->j(|^Q639gV5wVv50000h zLU4nTfFWiQe`IEvnPV|CtN6N)bMHBa`;a85U@jE~0H5UnI%TRKQsD&9M6||9Hzsf? zbWno}1C+pcfCBIXEP>Gg{p%nH;{OyRsg>%jX5Edmcf(E$^<~#Vvx1{by`m_f(xtuI zf=^F+rRK)Jp&Qicu;Zi942t$y6a%yyOj;2zU=*e7Jp*DF)i5S~4_NXY;~rcCjF(-x zQD=}k&Qto{&805w8_XcMPy$%x!Qf^kb2FS6bh&GMg43-%1$Rz+1Y%d1N7Whc4<+x9|h2f#(IdC6i1h4|WVZ?x* zdiaU`{kuCDF0FGB!*7swb=;_)>+1u93x_#Go^v1_=(v$i^lO5&db8a~?fi5n276oF z1s9?WNjJAOmvp)?;KggkK(d3R@|6xGtuiDj(HBWoFo1#va5E(7OvO~H!XJk3$5-Ak R$IAc!002ovPDHLkV1g*#)yJi68K?Bw+@-J9ANh zAhZF@Cm9GQs0GLd0=OQa2jGq_|FQ?w*zM$X6y=NssCoyas3iqZ000$90-)`co+2It zPwLihmr+j%`61A+N4}aw4Qe=;p^O5)gBOfkt@cIm1p73K^okTjeQ>wzA*TEZ1tO)S z0N>aiM^OS9qSy6v0rcp6ke^Q>g@=##_A)A*1xQeHw3Y+9FBl;B?QfNvs5xH}K({=5 z4`_?M%N+ETt3j1zecpf#Rd6g$%d)Ticw$JHDY%kUw1w1d^})ERRD3!H^nw>ieSm)_?iti@PA4~|?Bc<7hjMqoqX>k@$A9{`txhEnb9q?mqpq}5O}_M?BybUH~Ubl*xG$n)=%bi| sd3?Bde3*EAa(H~?czFKHO8l2)V(8W9=;^QLx(m|k>FVdQ&MBb@03MSVFpmz7la^8%& zQid|uq9{3SMR=xfL^#v!nX-LIOty=}WY2vi7KY15rfEmfD}iTL-J~AiP4NYmE_SCl zUi``j%7~7k2|c0((0=W!O8_~x0QcD*k{%3^{{{1kW<>(v4?%%ijr%{U<^TWy07*qo IM6N<$f_vX?uK)l5 literal 0 HcmV?d00001 diff --git a/nfc_eink/assets/err_09.png b/nfc_eink/assets/err_09.png new file mode 100644 index 0000000000000000000000000000000000000000..7378aff724e92b684138e815f88e1e7fbc356d1d GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^k|4|oBpBXI+Hnp@b$GfshE&X9tYv8W&%jvYz`#`V zd_DuqiBF#xm>3>50Err)s#?kW3=E8+4h#(cul{dfFg*5xfwAvOz&WP#I_nu2jMe@$ zFnp<(U| #include -#include #include #include diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_screen.h b/nfc_eink/nfc_eink_screen/nfc_eink_screen.h index d851232e..15d97ca3 100644 --- a/nfc_eink/nfc_eink_screen/nfc_eink_screen.h +++ b/nfc_eink/nfc_eink_screen/nfc_eink_screen.h @@ -2,7 +2,6 @@ #include #include -#include #include #include "nfc_eink_types.h" @@ -15,7 +14,6 @@ NfcEinkScreen* nfc_eink_screen_alloc(NfcEinkManufacturer manufacturer); void nfc_eink_screen_init(NfcEinkScreen* screen, NfcEinkScreenType type); void nfc_eink_screen_free(NfcEinkScreen* screen); -///TODO: maybe this function can be moved to nfc_eink_screen_alloc as a number of parameters void nfc_eink_screen_set_callback( NfcEinkScreen* screen, NfcEinkScreenEventCallback event_callback, diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_types.h b/nfc_eink/nfc_eink_screen/nfc_eink_types.h index e0dda98a..cb9b4cbe 100644 --- a/nfc_eink/nfc_eink_screen/nfc_eink_types.h +++ b/nfc_eink/nfc_eink_screen/nfc_eink_types.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "nfc_eink_screen_infos.h" diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_poller.c b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_poller.c index 84f984df..5f1b7c71 100644 --- a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_poller.c +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_poller.c @@ -15,7 +15,6 @@ static bool eink_waveshare_send_data( furi_assert(screen); furi_assert(data); furi_assert(data_len > 0); - //furi_assert(validator); bit_buffer_reset(screen->tx_buf); bit_buffer_reset(screen->rx_buf); @@ -33,7 +32,6 @@ static bool eink_waveshare_send_data( result = ((response_len == 2) && (response[0] == 0) && (response[1] == 0)) || ((response_len == 2) && (response[0] == 0xFF) && (response[1] == 0)); - //result = validator(response, response_len); } else { FURI_LOG_E(TAG, "Iso14443_3aError: %02X", error); } diff --git a/nfc_eink/scenes/scene_choose_manufacturer.c b/nfc_eink/scenes/scene_choose_manufacturer.c index 5dcde3a2..b140e740 100644 --- a/nfc_eink/scenes/scene_choose_manufacturer.c +++ b/nfc_eink/scenes/scene_choose_manufacturer.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" static void nfc_eink_scene_choose_manufacturer_submenu_callback(void* context, uint32_t index) { NfcEinkApp* instance = context; diff --git a/nfc_eink/scenes/scene_choose_screen.c b/nfc_eink/scenes/scene_choose_screen.c index b348d2d7..d08fb339 100644 --- a/nfc_eink/scenes/scene_choose_screen.c +++ b/nfc_eink/scenes/scene_choose_screen.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" #define TAG "NfcEinkSceneChooseScreen" diff --git a/nfc_eink/scenes/scene_delete.c b/nfc_eink/scenes/scene_delete.c index d760d889..b32fe5b6 100644 --- a/nfc_eink/scenes/scene_delete.c +++ b/nfc_eink/scenes/scene_delete.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" void nfc_eink_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { NfcEinkApp* instance = context; diff --git a/nfc_eink/scenes/scene_delete_success.c b/nfc_eink/scenes/scene_delete_success.c index 12d4696d..0d04b204 100644 --- a/nfc_eink/scenes/scene_delete_success.c +++ b/nfc_eink/scenes/scene_delete_success.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" void nfc_eink_scene_delete_success_popup_callback(void* context) { NfcEinkApp* instance = context; diff --git a/nfc_eink/scenes/scene_emulate.c b/nfc_eink/scenes/scene_emulate.c index ab1d0003..2654e69e 100644 --- a/nfc_eink/scenes/scene_emulate.c +++ b/nfc_eink/scenes/scene_emulate.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" #include #include diff --git a/nfc_eink/scenes/scene_error.c b/nfc_eink/scenes/scene_error.c index c743f17a..08b8265b 100644 --- a/nfc_eink/scenes/scene_error.c +++ b/nfc_eink/scenes/scene_error.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" void nfc_eink_scene_error_popup_callback(void* context) { NfcEinkApp* nfc = context; diff --git a/nfc_eink/scenes/scene_exit_confirm.c b/nfc_eink/scenes/scene_exit_confirm.c index 33f69ce1..3b529711 100644 --- a/nfc_eink/scenes/scene_exit_confirm.c +++ b/nfc_eink/scenes/scene_exit_confirm.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" void nfc_eink_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) { NfcEinkApp* instance = context; diff --git a/nfc_eink/scenes/scene_file_select.c b/nfc_eink/scenes/scene_file_select.c index 114deaf6..0952149b 100644 --- a/nfc_eink/scenes/scene_file_select.c +++ b/nfc_eink/scenes/scene_file_select.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" void nfc_eink_scene_file_select_on_enter(void* context) { NfcEinkApp* instance = context; diff --git a/nfc_eink/scenes/scene_info.c b/nfc_eink/scenes/scene_info.c index e3b0aba0..8bd3b30e 100644 --- a/nfc_eink/scenes/scene_info.c +++ b/nfc_eink/scenes/scene_info.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" void nfc_eink_scene_info_on_enter(void* context) { NfcEinkApp* instance = context; diff --git a/nfc_eink/scenes/scene_save_name.c b/nfc_eink/scenes/scene_save_name.c index 67c4465b..66baa19c 100644 --- a/nfc_eink/scenes/scene_save_name.c +++ b/nfc_eink/scenes/scene_save_name.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" #include "../nfc_eink_screen/nfc_eink_screen.h" #include @@ -58,6 +58,7 @@ bool nfc_eink_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(nfc_eink_screen_save(instance->screen, furi_string_get_cstr(instance->file_path))) { scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneSaveSuccess); dolphin_deed(DolphinDeedNfcSave); + memset(instance->text_store, 0, sizeof(instance->text_store)); } consumed = true; } diff --git a/nfc_eink/scenes/scene_save_success.c b/nfc_eink/scenes/scene_save_success.c index bae50d11..39cf466b 100644 --- a/nfc_eink/scenes/scene_save_success.c +++ b/nfc_eink/scenes/scene_save_success.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" void nfc_eink_scene_save_success_popup_callback(void* context) { NfcEinkApp* nfc = context; diff --git a/nfc_eink/scenes/scene_screen_menu.c b/nfc_eink/scenes/scene_screen_menu.c index 72679534..bf760d17 100644 --- a/nfc_eink/scenes/scene_screen_menu.c +++ b/nfc_eink/scenes/scene_screen_menu.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" #define TAG "NfcEinkSceneScreenMenu" diff --git a/nfc_eink/scenes/scene_settings.c b/nfc_eink/scenes/scene_settings.c index acadf2a0..8031bb68 100644 --- a/nfc_eink/scenes/scene_settings.c +++ b/nfc_eink/scenes/scene_settings.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" static const char* nfc_eink_scene_settings_write_mode[] = { "Strict", diff --git a/nfc_eink/scenes/scene_show_image.c b/nfc_eink/scenes/scene_show_image.c index 7ec026b1..0a89c009 100644 --- a/nfc_eink/scenes/scene_show_image.c +++ b/nfc_eink/scenes/scene_show_image.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" void nfc_eink_scene_show_image_on_enter(void* context) { NfcEinkApp* instance = context; diff --git a/nfc_eink/scenes/scene_start.c b/nfc_eink/scenes/scene_start.c index ae60d28c..fd1dda33 100644 --- a/nfc_eink/scenes/scene_start.c +++ b/nfc_eink/scenes/scene_start.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" enum SubmenuIndex { SubmenuIndexEmulate, diff --git a/nfc_eink/scenes/scene_write.c b/nfc_eink/scenes/scene_write.c index ebe7422b..04a3e350 100644 --- a/nfc_eink/scenes/scene_write.c +++ b/nfc_eink/scenes/scene_write.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" #define TAG "NfcEinkSceneWrite" diff --git a/nfc_eink/scenes/scene_write_done.c b/nfc_eink/scenes/scene_write_done.c index 257161dd..bbc9260a 100644 --- a/nfc_eink/scenes/scene_write_done.c +++ b/nfc_eink/scenes/scene_write_done.c @@ -1,4 +1,4 @@ -#include "../nfc_eink_app.h" +#include "../nfc_eink_app_i.h" void nfc_eink_scene_write_done_popup_callback(void* context) { NfcEinkApp* instance = context; diff --git a/nfc_eink/views/eink_progress.c b/nfc_eink/views/eink_progress.c index 9f74f24c..104d3b17 100644 --- a/nfc_eink/views/eink_progress.c +++ b/nfc_eink/views/eink_progress.c @@ -36,7 +36,6 @@ EinkProgress* eink_progress_alloc(void) { instance->view = view_alloc(); view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(EinkProgressViewModel)); view_set_draw_callback(instance->view, eink_progress_draw_callback); - //view_set_input_callback(instance->view, dict_attack_input_callback); view_set_context(instance->view, instance); with_view_model( instance->view, From 421d820ccb886df650541e167795571cb6278c50 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Sun, 29 Sep 2024 18:47:31 +0300 Subject: [PATCH 03/10] nfc_eink_app_i.h added --- nfc_eink/nfc_eink_app.c | 2 +- nfc_eink/nfc_eink_app.h | 128 +-------------------- nfc_eink/nfc_eink_app_i.h | 128 +++++++++++++++++++++ nfc_eink/nfc_eink_screen/nfc_eink_screen.c | 4 - 4 files changed, 130 insertions(+), 132 deletions(-) create mode 100644 nfc_eink/nfc_eink_app_i.h diff --git a/nfc_eink/nfc_eink_app.c b/nfc_eink/nfc_eink_app.c index 45179fac..bf2a51c1 100644 --- a/nfc_eink/nfc_eink_app.c +++ b/nfc_eink/nfc_eink_app.c @@ -1,4 +1,4 @@ -#include "nfc_eink_app.h" +#include "nfc_eink_app_i.h" #include #include diff --git a/nfc_eink/nfc_eink_app.h b/nfc_eink/nfc_eink_app.h index cf8478e2..ff711206 100644 --- a/nfc_eink/nfc_eink_app.h +++ b/nfc_eink/nfc_eink_app.h @@ -1,129 +1,3 @@ #pragma once -#include -#include - -#include -#include - -#include - -#include <../applications/services/notification/notification_messages.h> - -#include <../applications/services/gui/gui.h> -#include <../applications/services/gui/view.h> -#include <../applications/services/gui/view_dispatcher.h> -#include <../applications/services/gui/scene_manager.h> -#include <../applications/services/gui/modules/submenu.h> -#include <../applications/services/gui/modules/empty_screen.h> -#include <../applications/services/gui/modules/dialog_ex.h> -#include <../applications/services/gui/modules/popup.h> -#include <../applications/services/gui/modules/loading.h> -#include <../applications/services/gui/modules/text_input.h> -#include <../applications/services/gui/modules/byte_input.h> -#include <../applications/services/gui/modules/text_box.h> -#include <../applications/services/gui/modules/widget.h> -#include <../applications/services/gui/modules/variable_item_list.h> - -#include <../applications/services/dialogs/dialogs.h> -#include <../applications/services/storage/storage.h> - -#include -#include -#include - -#include "nfc_eink_screen/nfc_eink_screen.h" -#include "scenes/scenes.h" - -#include "views/eink_progress.h" -#include "views/image_scroll.h" - -#define NFC_EINK_NAME_SIZE 22 -#define NFC_EINK_APP_EXTENSION ".eink" -#define NFC_EINK_APP_FOLDER_NAME "nfc_eink" - -#define NFC_EINK_APP_FOLDER EXT_PATH(NFC_EINK_APP_FOLDER_NAME) - -///TODO: remove all this shit below to _i.h file - -typedef enum { - NfcEinkAppCustomEventReserved = 100, - - NfcEinkAppCustomEventTextInputDone, - NfcEinkAppCustomEventTimerExpired, - NfcEinkAppCustomEventBlockProcessed, - NfcEinkAppCustomEventUpdating, - NfcEinkAppCustomEventProcessFinish, - NfcEinkAppCustomEventTargetDetected, - NfcEinkAppCustomEventTargetLost, - NfcEinkAppCustomEventUnknownError, - NfcEinkAppCustomEventExit, -} NfcEinkAppCustomEvents; - -typedef enum { - NfcEinkViewMenu, - NfcEinkViewDialogEx, - NfcEinkViewPopup, - NfcEinkViewLoading, - NfcEinkViewTextInput, - NfcEinkViewByteInput, - NfcEinkViewTextBox, - NfcEinkViewWidget, - NfcEinkViewVarItemList, - NfcEinkViewProgress, - NfcEinkViewImageScroll, -} NfcEinkView; - -typedef enum { - NfcEinkWriteModeStrict, - NfcEinkWriteModeResolution, - NfcEinkWriteModeManufacturer, - NfcEinkWriteModeFree -} NfcEinkWriteMode; - -typedef struct { - NfcEinkWriteMode write_mode; - bool invert_image; -} NfcEinkSettings; - -typedef struct { - Gui* gui; - DialogsApp* dialogs; - NotificationApp* notifications; - - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - - // Common Views - Submenu* submenu; - DialogEx* dialog_ex; - Popup* popup; - Loading* loading; - TextInput* text_input; - ByteInput* byte_input; - TextBox* text_box; - Widget* widget; - VariableItemList* var_item_list; - EinkProgress* eink_progress; - ImageScroll* image_scroll; - - Nfc* nfc; - - NfcListener* listener; - NfcPoller* poller; - NfcEinkScreen* screen; - const NfcEinkScreenInfo* info_temp; - EinkScreenInfoArray_t arr; - NfcEinkSettings settings; - - bool screen_loaded; - char text_store[50 + 1]; - FuriString* file_path; - FuriString* file_name; -} NfcEinkApp; - -bool nfc_eink_load_from_file_select(NfcEinkApp* instance); -void nfc_eink_blink_emulate_start(NfcEinkApp* app); -void nfc_eink_blink_write_start(NfcEinkApp* app); -void nfc_eink_blink_stop(NfcEinkApp* app); -void nfc_eink_save_settings(NfcEinkApp* instance); +typedef struct NfcEinkApp NfcEinkApp; \ No newline at end of file diff --git a/nfc_eink/nfc_eink_app_i.h b/nfc_eink/nfc_eink_app_i.h new file mode 100644 index 00000000..90f6e8eb --- /dev/null +++ b/nfc_eink/nfc_eink_app_i.h @@ -0,0 +1,128 @@ +#pragma once + +#include "nfc_eink_app.h" +#include +#include + +#include +#include +#include <../applications/services/notification/notification_messages.h> + +#include <../applications/services/gui/gui.h> +#include <../applications/services/gui/view.h> +#include <../applications/services/gui/view_dispatcher.h> +#include <../applications/services/gui/scene_manager.h> +#include <../applications/services/gui/modules/submenu.h> +#include <../applications/services/gui/modules/empty_screen.h> +#include <../applications/services/gui/modules/dialog_ex.h> +#include <../applications/services/gui/modules/popup.h> +#include <../applications/services/gui/modules/loading.h> +#include <../applications/services/gui/modules/text_input.h> +#include <../applications/services/gui/modules/byte_input.h> +#include <../applications/services/gui/modules/text_box.h> +#include <../applications/services/gui/modules/widget.h> +#include <../applications/services/gui/modules/variable_item_list.h> + +#include <../applications/services/dialogs/dialogs.h> +#include <../applications/services/storage/storage.h> + +#include +#include +#include + +#include "nfc_eink_icons.h" + +#include "nfc_eink_screen/nfc_eink_screen.h" +#include "scenes/scenes.h" + +#include "views/eink_progress.h" +#include "views/image_scroll.h" + +#define NFC_EINK_NAME_SIZE (50) +#define NFC_EINK_APP_EXTENSION ".eink" +#define NFC_EINK_APP_FOLDER_NAME "nfc_eink" + +#define NFC_EINK_APP_FOLDER EXT_PATH(NFC_EINK_APP_FOLDER_NAME) +#define NFC_EINK_APP_TEXT_STORE_SIZE (100) + +typedef enum { + NfcEinkAppCustomEventReserved = 100, + + NfcEinkAppCustomEventTextInputDone, + NfcEinkAppCustomEventTimerExpired, + NfcEinkAppCustomEventBlockProcessed, + NfcEinkAppCustomEventUpdating, + NfcEinkAppCustomEventProcessFinish, + NfcEinkAppCustomEventTargetDetected, + NfcEinkAppCustomEventTargetLost, + NfcEinkAppCustomEventUnknownError, + NfcEinkAppCustomEventExit, +} NfcEinkAppCustomEvents; + +typedef enum { + NfcEinkViewMenu, + NfcEinkViewDialogEx, + NfcEinkViewPopup, + NfcEinkViewLoading, + NfcEinkViewTextInput, + NfcEinkViewByteInput, + NfcEinkViewTextBox, + NfcEinkViewWidget, + NfcEinkViewVarItemList, + NfcEinkViewProgress, + NfcEinkViewImageScroll, +} NfcEinkView; + +typedef enum { + NfcEinkWriteModeStrict, + NfcEinkWriteModeResolution, + NfcEinkWriteModeManufacturer, + NfcEinkWriteModeFree +} NfcEinkWriteMode; + +typedef struct { + NfcEinkWriteMode write_mode; + bool invert_image; +} NfcEinkSettings; + +struct NfcEinkApp { + Gui* gui; + DialogsApp* dialogs; + NotificationApp* notifications; + + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + + // Common Views + Submenu* submenu; + DialogEx* dialog_ex; + Popup* popup; + Loading* loading; + TextInput* text_input; + ByteInput* byte_input; + TextBox* text_box; + Widget* widget; + VariableItemList* var_item_list; + EinkProgress* eink_progress; + ImageScroll* image_scroll; + + Nfc* nfc; + + NfcListener* listener; + NfcPoller* poller; + NfcEinkScreen* screen; + const NfcEinkScreenInfo* info_temp; + EinkScreenInfoArray_t arr; + NfcEinkSettings settings; + + bool screen_loaded; + char text_store[NFC_EINK_APP_TEXT_STORE_SIZE]; + FuriString* file_path; + FuriString* file_name; +}; + +bool nfc_eink_load_from_file_select(NfcEinkApp* instance); +void nfc_eink_blink_emulate_start(NfcEinkApp* app); +void nfc_eink_blink_write_start(NfcEinkApp* app); +void nfc_eink_blink_stop(NfcEinkApp* app); +void nfc_eink_save_settings(NfcEinkApp* instance); diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_screen.c b/nfc_eink/nfc_eink_screen/nfc_eink_screen.c index e2d2f7da..d961bec4 100644 --- a/nfc_eink/nfc_eink_screen/nfc_eink_screen.c +++ b/nfc_eink/nfc_eink_screen/nfc_eink_screen.c @@ -5,7 +5,6 @@ #define TAG "NfcEinkScreen" -///TODO: possibly move this closer to save/load functions #define NFC_EINK_FORMAT_VERSION (1) #define NFC_EINK_FILE_HEADER "Flipper NFC Eink screen" #define NFC_EINK_DEVICE_UID_KEY "UID" @@ -20,7 +19,6 @@ #define NFC_EINK_SCREEN_DATA_TOTAL_KEY "Data total" #define NFC_EINK_SCREEN_BLOCK_DATA_KEY "Block" -///TODO: move this externs to separate files in screen folders for each type extern const NfcEinkScreenHandlers waveshare_handlers; extern const NfcEinkScreenHandlers goodisplay_handlers; @@ -84,8 +82,6 @@ void nfc_eink_screen_init(NfcEinkScreen* screen, NfcEinkScreenType type) { size_t memory_size = device->block_total * data->base.data_block_size; data->image_data = malloc(memory_size); - ///TODO: this can be used as a config option, which will define the initial color of the screen - memset(data->image_data, 0xFF, memory_size); } void nfc_eink_screen_free(NfcEinkScreen* screen) { From eb6f8e366692bca6d14ad9fb75edabe58ff864b0 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Sun, 29 Sep 2024 18:48:24 +0300 Subject: [PATCH 04/10] All screen types are now aligned --- .../goodisplay/eink_goodisplay_config.c | 29 +++++------- .../goodisplay/eink_goodisplay_config.h | 1 - .../nfc_eink_screen/nfc_eink_screen_infos.c | 18 ++++---- .../nfc_eink_screen/nfc_eink_screen_infos.h | 45 ++++++++++++------- .../waveshare/eink_waveshare_config.c | 24 +++++----- .../waveshare/eink_waveshare_config.h | 4 +- 6 files changed, 64 insertions(+), 57 deletions(-) diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.c b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.c index bb012df7..4711a781 100644 --- a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.c +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.c @@ -3,13 +3,13 @@ #define TAG "GD_Config" const uint8_t default_config[] = { - 0x03, 0xf0, 0xdb, 0x00, 0x00, 0x67, 0xa0, 0x06, 0x00, 0x20, 0x00, 128, 0x01, 0x28, 0xa4, 0x01, - 0x0c, 0xa5, 0x02, 0x00, 0x0a, 0xa4, 0x01, 0x08, 0xa5, 0x02, 0x00, 0x0a, 0xa4, 0x01, 0x0c, 0xa5, - 0x02, 0x00, 0x0a, 0xa4, 0x01, 0x02, 0xa1, 0x01, 0x12, 0xa4, 0x01, 0x02, 0xa1, 0x04, 0x01, 0x27, - 0x01, 0x01, 0xa1, 0x02, 0x11, 0x01, 0xa1, 0x03, 0x44, 0x00, 0x0f, 0xa1, 0x05, 0x45, 0x27, 0x01, - 0x00, 0x00, 0xa1, 0x02, 0x3c, 0x05, 0xa1, 0x03, 0x21, 0x00, 0x80, 0xa1, 0x02, 0x18, 0x80, 0xa1, - 0x02, 0x4e, 0x00, 0xa1, 0x03, 0x4f, 0x27, 0x01, 0xa3, 0x01, 0x24, 0xa2, 0x02, 0x22, 0xf7, 0xa2, - 0x01, 0x20, 0xa4, 0x01, 0x02, 0xa2, 0x02, 0x10, 0x01, 0xa5, 0x02, 0x00, 0x0a}; + 0xf0, 0xdb, 0x00, 0x00, 0x67, 0xa0, 0x06, 0x00, 0x20, 0x00, 128, 0x01, 0x28, 0xa4, 0x01, 0x0c, + 0xa5, 0x02, 0x00, 0x0a, 0xa4, 0x01, 0x08, 0xa5, 0x02, 0x00, 0x0a, 0xa4, 0x01, 0x0c, 0xa5, 0x02, + 0x00, 0x0a, 0xa4, 0x01, 0x02, 0xa1, 0x01, 0x12, 0xa4, 0x01, 0x02, 0xa1, 0x04, 0x01, 0x27, 0x01, + 0x01, 0xa1, 0x02, 0x11, 0x01, 0xa1, 0x03, 0x44, 0x00, 0x0f, 0xa1, 0x05, 0x45, 0x27, 0x01, 0x00, + 0x00, 0xa1, 0x02, 0x3c, 0x05, 0xa1, 0x03, 0x21, 0x00, 0x80, 0xa1, 0x02, 0x18, 0x80, 0xa1, 0x02, + 0x4e, 0x00, 0xa1, 0x03, 0x4f, 0x27, 0x01, 0xa3, 0x01, 0x24, 0xa2, 0x02, 0x22, 0xf7, 0xa2, 0x01, + 0x20, 0xa4, 0x01, 0x02, 0xa2, 0x02, 0x10, 0x01, 0xa5, 0x02, 0x00, 0x0a}; EinkGoodisplayConfigPack* eink_goodisplay_config_pack_alloc(size_t* config_length) { furi_assert(config_length); @@ -67,19 +67,14 @@ NfcEinkScreenType const EinkGoodisplaySizeConfigPack* config = (EinkGoodisplaySizeConfigPack*)(data); if(config->screen_data.screen_resolution == NfcEinkGoodisplayScreenResolution2n13inch) { - /* screen_type = screen_config->screen_channel == - NfcEinkGoodisplayScreenChannelBlackWhite ? - NfcEinkScreenTypeGoodisplay2n13inch : - NfcEinkScreenTypeUnknown; */ - screen_type = NfcEinkScreenTypeGoodisplay2n13inch; + screen_type = NfcEinkScreenTypeGoodisplayEY2Color2n13inch; } else if(config->screen_data.screen_resolution == NfcEinkGoodisplayScreenResolution2n9inch) { - screen_type = NfcEinkScreenTypeGoodisplay2n9inch; + screen_type = NfcEinkScreenTypeGoodisplayEY2Color2n9inch; } else if(config->screen_data.screen_resolution == NfcEinkGoodisplayScreenResolution1n54inch) { - screen_type = NfcEinkScreenTypeGoodisplay1n54inch; + screen_type = NfcEinkScreenTypeGoodisplayEY2Color1n54inch; } else if(config->screen_data.screen_resolution == NfcEinkGoodisplayScreenResolution3n71inch) { - screen_type = NfcEinkScreenTypeGoodisplay3n71inch; - } else - screen_type = NfcEinkScreenTypeUnknown; + screen_type = NfcEinkScreenTypeGoodisplayEY2Color3n71inch; + } return screen_type; } diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.h b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.h index 601f3387..b0b3d412 100644 --- a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.h +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_config.h @@ -98,7 +98,6 @@ typedef struct { } FURI_PACKED EinkGoodisplayResetSequence; typedef struct { - uint8_t command_code; APDU_Header apdu; uint8_t data_length; EinkGoodisplaySizeConfigPack eink_size_config; diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.c b/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.c index c2740f97..bbff5232 100644 --- a/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.c +++ b/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.c @@ -20,7 +20,7 @@ static const NfcEinkScreenInfo screen_descriptors[] = { .height = 122, .screen_size = NfcEinkScreenSize2n13inch, .screen_manufacturer = NfcEinkManufacturerWaveshare, - .screen_type = NfcEinkScreenTypeWaveshare2n13inch, + .screen_type = NfcEinkScreenTypeWaveshare2Color2n13inch, .data_block_size = 16, }, { @@ -29,7 +29,7 @@ static const NfcEinkScreenInfo screen_descriptors[] = { .height = 176, .screen_size = NfcEinkScreenSize2n7inch, .screen_manufacturer = NfcEinkManufacturerWaveshare, - .screen_type = NfcEinkScreenTypeWaveshare2n7inch, + .screen_type = NfcEinkScreenTypeWaveshare2Color2n7inch, .data_block_size = 121, }, { @@ -38,7 +38,7 @@ static const NfcEinkScreenInfo screen_descriptors[] = { .height = 128, .screen_size = NfcEinkScreenSize2n9inch, .screen_manufacturer = NfcEinkManufacturerWaveshare, - .screen_type = NfcEinkScreenTypeWaveshare2n9inch, + .screen_type = NfcEinkScreenTypeWaveshare2Color2n9inch, .data_block_size = 16, }, { @@ -47,7 +47,7 @@ static const NfcEinkScreenInfo screen_descriptors[] = { .height = 400, .screen_size = NfcEinkScreenSize4n2inch, .screen_manufacturer = NfcEinkManufacturerWaveshare, - .screen_type = NfcEinkScreenTypeWaveshare4n2inch, + .screen_type = NfcEinkScreenTypeWaveshare2Color4n2inch, .data_block_size = 100, }, { @@ -56,7 +56,7 @@ static const NfcEinkScreenInfo screen_descriptors[] = { .height = 800, .screen_size = NfcEinkScreenSize7n5inch, .screen_manufacturer = NfcEinkManufacturerWaveshare, - .screen_type = NfcEinkScreenTypeWaveshare7n5inch, + .screen_type = NfcEinkScreenTypeWaveshare2Color7n5inch, .data_block_size = 120, }, { @@ -65,7 +65,7 @@ static const NfcEinkScreenInfo screen_descriptors[] = { .height = 200, .screen_size = NfcEinkScreenSize1n54inch, .screen_manufacturer = NfcEinkManufacturerGoodisplay, - .screen_type = NfcEinkScreenTypeGoodisplay1n54inch, + .screen_type = NfcEinkScreenTypeGoodisplayEY2Color1n54inch, .data_block_size = 0xFA, }, { @@ -74,7 +74,7 @@ static const NfcEinkScreenInfo screen_descriptors[] = { .height = 122, .screen_size = NfcEinkScreenSize2n13inch, .screen_manufacturer = NfcEinkManufacturerGoodisplay, - .screen_type = NfcEinkScreenTypeGoodisplay2n13inch, + .screen_type = NfcEinkScreenTypeGoodisplayEY2Color2n13inch, .data_block_size = 0xFA, }, { @@ -83,7 +83,7 @@ static const NfcEinkScreenInfo screen_descriptors[] = { .height = 128, .screen_size = NfcEinkScreenSize2n9inch, .screen_manufacturer = NfcEinkManufacturerGoodisplay, - .screen_type = NfcEinkScreenTypeGoodisplay2n9inch, + .screen_type = NfcEinkScreenTypeGoodisplayEY2Color2n9inch, .data_block_size = 0xFA, }, { @@ -92,7 +92,7 @@ static const NfcEinkScreenInfo screen_descriptors[] = { .height = 240, .screen_size = NfcEinkScreenSize3n71inch, .screen_manufacturer = NfcEinkManufacturerGoodisplay, - .screen_type = NfcEinkScreenTypeGoodisplay3n71inch, + .screen_type = NfcEinkScreenTypeGoodisplayEY2Color3n71inch, .data_block_size = 0xFA, }, }; diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.h b/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.h index 896a5687..f7916db3 100644 --- a/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.h +++ b/nfc_eink/nfc_eink_screen/nfc_eink_screen_infos.h @@ -23,22 +23,38 @@ typedef enum { NfcEinkManufacturerUnknown } NfcEinkManufacturer; -/// TODO: Align all screens here by manufacturers before release typedef enum { NfcEinkScreenTypeUnknown, - NfcEinkScreenTypeGoodisplay2n13inch, - NfcEinkScreenTypeGoodisplay2n9inch, - - //NfcEinkTypeGoodisplay4n2inch, - - NfcEinkScreenTypeWaveshare2n13inch, - NfcEinkScreenTypeWaveshare2n9inch, - NfcEinkScreenTypeGoodisplay1n54inch, - NfcEinkScreenTypeGoodisplay3n71inch, - - NfcEinkScreenTypeWaveshare2n7inch, - NfcEinkScreenTypeWaveshare4n2inch, - NfcEinkScreenTypeWaveshare7n5inch, + NfcEinkScreenTypeGoodisplayEY2Color1n54inch, + NfcEinkScreenTypeGoodisplayEY2Color2n13inch, + NfcEinkScreenTypeGoodisplayEY2Color2n9inch, + NfcEinkScreenTypeGoodisplayEY2Color3n71inch, + NfcEinkScreenTypeGoodisplayEY2Color4n2inch, + + NfcEinkScreenTypeGoodisplayEW2Color1n54inch, + NfcEinkScreenTypeGoodisplayEW2Color2n13inch, + NfcEinkScreenTypeGoodisplayEW2Color2n9inch, + NfcEinkScreenTypeGoodisplayEW2Color4n2inch, + + NfcEinkScreenTypeGoodisplayEY3Color1n54inch, + NfcEinkScreenTypeGoodisplayEY3Color2n13inch, + NfcEinkScreenTypeGoodisplayEY3Color2n9inch, + NfcEinkScreenTypeGoodisplayEY3Color4n2inch, + + NfcEinkScreenTypeGoodisplayEW3Color2n13inch, + NfcEinkScreenTypeGoodisplayEW3Color2n9inch, + NfcEinkScreenTypeGoodisplayEQ3Color4n2inch, + //----------------------------------------------- + NfcEinkScreenTypeWaveshare2Color2n13inch, + NfcEinkScreenTypeWaveshare2Color2n7inch, + NfcEinkScreenTypeWaveshare2Color2n9inch, + NfcEinkScreenTypeWaveshare2Color4n2inch, + NfcEinkScreenTypeWaveshare2Color7n5inch, + NfcEinkScreenTypeWaveshare2ColorHD7n5inch, + + NfcEinkScreenTypeWaveshare3Color1n54inch, + NfcEinkScreenTypeWaveshare3Color2n9inch, + //All new screens can be added here NfcEinkScreenTypeNum } NfcEinkScreenType; @@ -63,7 +79,6 @@ typedef struct { free((void*)old); \ } while(false) -//#define M_SET(a, b) (M_INIT(a); memcpy(a, b, M_ARRAY_SIZE)) #define M_CLEAR(a) (free((void*)a)) #define M_DESCRIPTOR_ARRAY_OPLIST \ diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.c b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.c index 0587b05f..3d49d18c 100644 --- a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.c +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.c @@ -5,15 +5,15 @@ EinkScreenTypeWaveshare eink_waveshare_config_translate_screen_type_to_protocol(NfcEinkScreenType common_screen_type) { switch(common_screen_type) { - case NfcEinkScreenTypeWaveshare2n13inch: + case NfcEinkScreenTypeWaveshare2Color2n13inch: return EinkScreenTypeWaveshare2n13inch; - case NfcEinkScreenTypeWaveshare2n7inch: + case NfcEinkScreenTypeWaveshare2Color2n7inch: return EinkScreenTypeWaveshare2n7inch; - case NfcEinkScreenTypeWaveshare2n9inch: + case NfcEinkScreenTypeWaveshare2Color2n9inch: return EinkScreenTypeWaveshare2n9inch; - case NfcEinkScreenTypeWaveshare4n2inch: + case NfcEinkScreenTypeWaveshare2Color4n2inch: return EinkScreenTypeWaveshare4n2inch; - case NfcEinkScreenTypeWaveshare7n5inch: + case NfcEinkScreenTypeWaveshare2Color7n5inch: return EinkScreenTypeWaveshare7n5inch; default: FURI_LOG_E(TAG, "Unknown waveshare screen type 0x%02X", common_screen_type); @@ -26,17 +26,15 @@ NfcEinkScreenType eink_waveshare_config_translate_protocol_to_screen_type( NfcEinkScreenType common_type = NfcEinkScreenTypeUnknown; if(protocol_screen_type == EinkScreenTypeWaveshare2n13inch) { - common_type = NfcEinkScreenTypeWaveshare2n13inch; + common_type = NfcEinkScreenTypeWaveshare2Color2n13inch; } else if(protocol_screen_type == EinkScreenTypeWaveshare2n9inch) { - common_type = NfcEinkScreenTypeWaveshare2n9inch; + common_type = NfcEinkScreenTypeWaveshare2Color2n9inch; } else if(protocol_screen_type == EinkScreenTypeWaveshare2n7inch) { - common_type = NfcEinkScreenTypeWaveshare2n7inch; + common_type = NfcEinkScreenTypeWaveshare2Color2n7inch; } else if(protocol_screen_type == EinkScreenTypeWaveshare4n2inch) { - common_type = NfcEinkScreenTypeWaveshare4n2inch; - } else if( - protocol_screen_type == EinkScreenTypeWaveshare7n5inch || - protocol_screen_type == EinkScreenTypeWaveshare7n5inchV2) { - common_type = NfcEinkScreenTypeWaveshare7n5inch; + common_type = NfcEinkScreenTypeWaveshare2Color4n2inch; + } else if(protocol_screen_type == EinkScreenTypeWaveshare7n5inch) { + common_type = NfcEinkScreenTypeWaveshare2Color7n5inch; } return common_type; diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.h b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.h index 11d211fe..cf3e8ab5 100644 --- a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.h +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_config.h @@ -6,8 +6,8 @@ typedef enum { EinkScreenTypeWaveshare2n7inch = 16, EinkScreenTypeWaveshare2n9inch = 7, EinkScreenTypeWaveshare4n2inch = 10, - EinkScreenTypeWaveshare7n5inch = 12, - EinkScreenTypeWaveshare7n5inchV2 = 14, + EinkScreenTypeWaveshare7n5inchHD = 12, + EinkScreenTypeWaveshare7n5inch = 14, } EinkScreenTypeWaveshare; EinkScreenTypeWaveshare From de3d95e4a021ebc02c5acb81ff4a9be68db7dcd3 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Sun, 29 Sep 2024 18:48:34 +0300 Subject: [PATCH 05/10] New goodisplay poller logic --- .../goodisplay/eink_goodispaly_poller.c | 252 +++++++----------- 1 file changed, 98 insertions(+), 154 deletions(-) diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodispaly_poller.c b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodispaly_poller.c index a563c08d..61647e82 100644 --- a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodispaly_poller.c +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodispaly_poller.c @@ -1,12 +1,14 @@ #include "eink_goodisplay_i.h" #include "eink_goodisplay_config.h" #include -#include #include #define TAG "GD_Poller" -typedef NfcCommand (*EinkGoodisplayStateHandler)(Iso14443_3aPoller* poller, NfcEinkScreen* screen); +#define EINK_GOODISPLAY_SCREEN_UPDATE_COMMAND_DELAY_US (150000) +#define EINK_GOODISPLAY_SCREEN_BW_UPDATE_COMMAND_CNT (20) + +typedef NfcCommand (*EinkGoodisplayStateHandler)(Iso14443_4aPoller* poller, NfcEinkScreen* screen); typedef bool (*GoodisplaySendDataResponseValidatorHandler)( const uint8_t* response, const size_t response_len); @@ -25,20 +27,8 @@ static bool eink_goodisplay_validate_response( } static bool - eink_goodisplay_validate_C2_response(const uint8_t* response, const size_t response_len) { - const uint8_t exp_res[] = {0xC2}; - return eink_goodisplay_validate_response(exp_res, sizeof(exp_res), response, response_len); -} - -static bool - eink_goodisplay_validate_0x02_cmd_response(const uint8_t* response, const size_t response_len) { - const uint8_t exp_res[] = {0x02, 0x90, 0x00}; - return eink_goodisplay_validate_response(exp_res, sizeof(exp_res), response, response_len); -} - -static bool - eink_goodisplay_validate_0x03_cmd_response(const uint8_t* response, const size_t response_len) { - const uint8_t exp_res[] = {0x03, 0x90, 0x00}; + eink_goodisplay_validate_cmd_response(const uint8_t* response, const size_t response_len) { + const uint8_t exp_res[] = {0x90, 0x00}; return eink_goodisplay_validate_response(exp_res, sizeof(exp_res), response, response_len); } @@ -47,32 +37,18 @@ static bool eink_goodisplay_validate_read_fid_file_response( const size_t response_len) { furi_assert(response); furi_assert(response_len > 0); - return response[0] == 0x02; + return response[0] == 0x00 && response[1] == 0x0F; } -static bool - eink_goodisplay_validate_0xB3_response(const uint8_t* response, const size_t response_len) { - const uint8_t exp_res[] = {0xA2}; - return eink_goodisplay_validate_response(exp_res, sizeof(exp_res), response, response_len); -} - -static bool - eink_goodisplay_validate_0xB2_response(const uint8_t* response, const size_t response_len) { - const uint8_t exp_res[] = {0xA3}; - return eink_goodisplay_validate_response(exp_res, sizeof(exp_res), response, response_len); -} - -#define eink_goodisplay_validate_0x13_response eink_goodisplay_validate_0xB2_response - -static bool eink_goodisplay_validate_update_cmd_response( +static bool eink_goodisplay_validate_read_E104_cmd_response( const uint8_t* response, const size_t response_len) { - const uint8_t exp_res[] = {0xF2, 0x01}; + const uint8_t exp_res[] = {0x00, 0x00, 0x90, 0x00}; return eink_goodisplay_validate_response(exp_res, sizeof(exp_res), response, response_len); } static bool eink_goodisplay_send_data( - Iso14443_3aPoller* poller, + Iso14443_4aPoller* poller, const NfcEinkScreen* screen, const uint8_t* data, const size_t data_len, @@ -85,34 +61,32 @@ static bool eink_goodisplay_send_data( bit_buffer_reset(screen->tx_buf); bit_buffer_reset(screen->rx_buf); - bit_buffer_append_bytes(screen->tx_buf, data, data_len); - Iso14443_3aError error = iso14443_3a_poller_send_standard_frame( - poller, screen->tx_buf, screen->rx_buf, 0 /* EINK_SCREEN_ISO14443_POLLER_FWT_FC */); + Iso14443_4aError error = iso14443_4a_poller_send_block(poller, screen->tx_buf, screen->rx_buf); bool result = false; - if(error == Iso14443_3aErrorNone) { + if(error == Iso14443_4aErrorNone) { size_t response_len = bit_buffer_get_size_bytes(screen->rx_buf); const uint8_t* response = bit_buffer_get_data(screen->rx_buf); result = validator(response, response_len); } else { - FURI_LOG_E(TAG, "Iso14443_3aError: %02X", error); + FURI_LOG_E(TAG, "Iso14443_4aError: %02X", error); } return result; } -static NfcCommand eink_goodisplay_C2(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { +static NfcCommand eink_goodisplay_C2(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { FURI_LOG_D(TAG, "Send 0xC2"); NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; ctx->poller_state = EinkGoodisplayPollerStateError; do { - const uint8_t data[] = {0xC2}; - bool result = eink_goodisplay_send_data( - poller, screen, data, sizeof(data), eink_goodisplay_validate_C2_response); - - if(!result) break; + bit_buffer_reset(screen->tx_buf); + bit_buffer_reset(screen->rx_buf); + Iso14443_4aError error = iso14443_4a_poller_send_supervisory_block( + poller, true, screen->tx_buf, screen->rx_buf); + if(error != Iso14443_4aErrorNone) break; ctx->poller_state = EinkGoodisplayPollerStateSelectNDEFTagApp; } while(false); @@ -121,16 +95,16 @@ static NfcCommand eink_goodisplay_C2(Iso14443_3aPoller* poller, NfcEinkScreen* s } static NfcCommand - eink_goodisplay_select_application(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + eink_goodisplay_select_application(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { FURI_LOG_D(TAG, "Select NDEF APP"); NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; ctx->poller_state = EinkGoodisplayPollerStateError; do { const uint8_t data[] = { - 0x02, 0x00, 0xa4, 0x04, 0x00, 0x07, 0xd2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00}; + 0x00, 0xa4, 0x04, 0x00, 0x07, 0xd2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00}; bool result = eink_goodisplay_send_data( - poller, screen, data, sizeof(data), eink_goodisplay_validate_0x02_cmd_response); + poller, screen, data, sizeof(data), eink_goodisplay_validate_cmd_response); if(!result) break; @@ -141,15 +115,15 @@ static NfcCommand } static NfcCommand - eink_goodisplay_select_ndef_file(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + eink_goodisplay_select_ndef_file(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { FURI_LOG_D(TAG, "Select NDEF File"); NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; ctx->poller_state = EinkGoodisplayPollerStateError; do { - const uint8_t data[] = {0x03, 0x00, 0xa4, 0x00, 0x0c, 0x02, 0xe1, 0x03}; + const uint8_t data[] = {0x00, 0xa4, 0x00, 0x0c, 0x02, 0xe1, 0x03}; bool result = eink_goodisplay_send_data( - poller, screen, data, sizeof(data), eink_goodisplay_validate_0x03_cmd_response); + poller, screen, data, sizeof(data), eink_goodisplay_validate_cmd_response); if(!result) break; @@ -159,13 +133,13 @@ static NfcCommand return NfcCommandContinue; } -static NfcCommand eink_goodisplay_read_fid_file(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { +static NfcCommand eink_goodisplay_read_fid_file(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { FURI_LOG_D(TAG, "Read FID"); NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; ctx->poller_state = EinkGoodisplayPollerStateError; do { - const uint8_t data[] = {0x02, 0x00, 0xb0, 0x00, 0x00, 0x0f}; + const uint8_t data[] = {0x00, 0xb0, 0x00, 0x00, 0x0f}; bool result = eink_goodisplay_send_data( poller, screen, data, sizeof(data), eink_goodisplay_validate_read_fid_file_response); @@ -178,15 +152,15 @@ static NfcCommand eink_goodisplay_read_fid_file(Iso14443_3aPoller* poller, NfcEi } static NfcCommand - eink_goodisplay_select_E104_file(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + eink_goodisplay_select_E104_file(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { FURI_LOG_D(TAG, "Select 0xE104"); NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; ctx->poller_state = EinkGoodisplayPollerStateError; do { - const uint8_t data[] = {0x03, 0x00, 0xa4, 0x00, 0x0c, 0x02, 0xe1, 0x04}; + const uint8_t data[] = {0x00, 0xa4, 0x00, 0x0c, 0x02, 0xe1, 0x04}; bool result = eink_goodisplay_send_data( - poller, screen, data, sizeof(data), eink_goodisplay_validate_0x03_cmd_response); + poller, screen, data, sizeof(data), eink_goodisplay_validate_cmd_response); if(!result) break; @@ -196,15 +170,15 @@ static NfcCommand } static NfcCommand - eink_goodisplay_read_0xE104_file(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + eink_goodisplay_read_0xE104_file(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { FURI_LOG_D(TAG, "Read 0xE104"); NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; ctx->poller_state = EinkGoodisplayPollerStateError; do { - const uint8_t data[] = {0x02, 0x00, 0xb0, 0x00, 0x00, 0x02}; + const uint8_t data[] = {0x00, 0xb0, 0x00, 0x00, 0x02}; bool result = eink_goodisplay_send_data( - poller, screen, data, sizeof(data), eink_goodisplay_validate_read_fid_file_response); + poller, screen, data, sizeof(data), eink_goodisplay_validate_read_E104_cmd_response); if(!result) break; @@ -214,7 +188,7 @@ static NfcCommand return NfcCommandContinue; } -static NfcCommand eink_goodisplay_duplicate_C2(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { +static NfcCommand eink_goodisplay_duplicate_C2(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { NfcCommand cmd = eink_goodisplay_C2(poller, screen); NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; @@ -231,15 +205,15 @@ static NfcCommand eink_goodisplay_duplicate_C2(Iso14443_3aPoller* poller, NfcEin } static NfcCommand - eink_goodisplay_send_config_cmd(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + eink_goodisplay_send_config_cmd(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { FURI_LOG_D(TAG, "Send config"); NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; ctx->poller_state = EinkGoodisplayPollerStateError; do { - const uint8_t config1[] = {0x02, 0xf0, 0xdb, 0x02, 0x00, 0x00}; + const uint8_t config1[] = {0xf0, 0xdb, 0x02, 0x00, 0x00}; bool result = eink_goodisplay_send_data( - poller, screen, config1, sizeof(config1), eink_goodisplay_validate_0x02_cmd_response); + poller, screen, config1, sizeof(config1), eink_goodisplay_validate_cmd_response); if(!result) break; @@ -248,11 +222,7 @@ static NfcCommand eink_goodisplay_config_pack_set_by_screen_info(config, &screen->data->base); result = eink_goodisplay_send_data( - poller, - screen, - (uint8_t*)config, - config_length, - eink_goodisplay_validate_0x03_cmd_response); + poller, screen, (uint8_t*)config, config_length, eink_goodisplay_validate_cmd_response); NfcEinkGoodisplayScreenResolution resolution = config->eink_size_config.screen_data.screen_resolution; @@ -262,16 +232,17 @@ static NfcCommand if(!result) break; - uint8_t config3[] = {0x02, 0xf0, 0xda, 0x00, 0x00, 0x03, 0xf0, resolution, channel}; + uint8_t config3[] = {0xf0, 0xda, 0x00, 0x00, 0x03, 0xf0, resolution, channel}; result = eink_goodisplay_send_data( - poller, screen, config3, sizeof(config3), eink_goodisplay_validate_0x02_cmd_response); + poller, screen, config3, sizeof(config3), eink_goodisplay_validate_cmd_response); if(!result) break; - const uint8_t cmdb3[] = {0xb3}; - result = eink_goodisplay_send_data( - poller, screen, cmdb3, sizeof(cmdb3), eink_goodisplay_validate_0xB3_response); + bit_buffer_reset(screen->tx_buf); + bit_buffer_reset(screen->rx_buf); + result = iso14443_4a_poller_send_receive_ready_block( + poller, false, screen->tx_buf, screen->rx_buf) == Iso14443_4aErrorNone; if(!result) break; ctx->poller_state = EinkGoodisplayPollerStateSendDataCmd; @@ -281,72 +252,54 @@ static NfcCommand return NfcCommandContinue; } -static bool eink_goodisplay_send_image_data( - Iso14443_3aPoller* poller, +static bool eink_goodisplay_send_image_data_block( + Iso14443_4aPoller* poller, const NfcEinkScreen* screen, - const uint8_t* command_header, - const size_t command_header_length, - const uint8_t* data, - const size_t data_len, - const GoodisplaySendDataResponseValidatorHandler validator) { - furi_assert(command_header); - furi_assert(validator); + uint16_t block_num) { furi_assert(poller); furi_assert(screen); + NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; + const NfcEinkScreenData* data = screen->data; - size_t total_size = command_header_length + data_len; - uint8_t* buf = malloc(total_size); - memcpy(buf, command_header, command_header_length); - memcpy(buf + command_header_length, data, data_len); + uint8_t header[] = {0xf0, 0xd2, 0x00, 0x00, 0xFA}; + *((uint16_t*)(header + 2)) = __builtin_bswap16(block_num); + bool result = false; + do { + bit_buffer_reset(screen->tx_buf); + bit_buffer_append_bytes(screen->tx_buf, header, sizeof(header)); + bit_buffer_append_bytes( + screen->tx_buf, &data->image_data[ctx->data_index], data->base.data_block_size - 2); + result = iso14443_4a_poller_send_chain_block(poller, screen->tx_buf, screen->rx_buf) == + Iso14443_4aErrorNone; + if(!result) break; + ctx->data_index += data->base.data_block_size - 2; - bool result = eink_goodisplay_send_data(poller, screen, buf, total_size, validator); + bit_buffer_reset(screen->tx_buf); + bit_buffer_append_bytes(screen->tx_buf, &data->image_data[ctx->data_index], 2); + + result = iso14443_4a_poller_send_block(poller, screen->tx_buf, screen->rx_buf) == + Iso14443_4aErrorNone; + if(!result) break; + + ctx->data_index += 2; + } while(false); - free(buf); return result; } -static NfcCommand eink_goodisplay_send_image(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { - const NfcEinkScreenData* data = screen->data; +static NfcCommand eink_goodisplay_send_image(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; bool status = true; do { FURI_LOG_D(TAG, "Send data: %d", ctx->block_number); - uint8_t header[] = {0x13, 0xf0, 0xd2, 0x00, 0x00, 0xFA}; - header[4] = ctx->block_number; - status &= eink_goodisplay_send_image_data( - poller, - screen, - header, - sizeof(header), - &data->image_data[ctx->data_index], - data->base.data_block_size - 2, - eink_goodisplay_validate_0x13_response); - - ctx->data_index += data->base.data_block_size - 2; + status &= eink_goodisplay_send_image_data_block(poller, screen, ctx->block_number); if(!status) { ctx->poller_state = EinkGoodisplayPollerStateError; break; } - - uint8_t header2[] = {0x02}; - status &= eink_goodisplay_send_image_data( - poller, - screen, - header2, - sizeof(header2), - &data->image_data[ctx->data_index], - 2, - eink_goodisplay_validate_0x02_cmd_response); - ctx->data_index += 2; - - if(!status) { - ctx->poller_state = EinkGoodisplayPollerStateError; - break; - } - ctx->block_number++; ctx->poller_state = (ctx->block_number == screen->device->block_total) ? EinkGoodisplayPollerStateApplyImage : @@ -358,18 +311,17 @@ static NfcCommand eink_goodisplay_send_image(Iso14443_3aPoller* poller, NfcEinkS return NfcCommandContinue; } -static NfcCommand eink_goodisplay_begin_update(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { +static NfcCommand eink_goodisplay_begin_update(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; ctx->poller_state = EinkGoodisplayPollerStateError; - const uint8_t apply[] = {0x03, 0xf0, 0xd4, 0x05, 0x80, 0x00}; + const uint8_t apply[] = {0xf0, 0xd4, 0x05, 0x80, 0x00}; FURI_LOG_D(TAG, "Applying..."); - + eink_goodisplay_on_updating(screen); bool result = eink_goodisplay_send_data( - poller, screen, apply, sizeof(apply), eink_goodisplay_validate_update_cmd_response); + poller, screen, apply, sizeof(apply), eink_goodisplay_validate_cmd_response); if(result) { - eink_goodisplay_on_updating(screen); ctx->poller_state = EinkGoodisplayPollerStateWaitUpdateDone; } @@ -377,45 +329,40 @@ static NfcCommand eink_goodisplay_begin_update(Iso14443_3aPoller* poller, NfcEin } static NfcCommand - eink_goodisplay_wait_update_done(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + eink_goodisplay_wait_update_done(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; - ctx->poller_state = EinkGoodisplayPollerStateWaitUpdateDone; FURI_LOG_D(TAG, "Updating..."); - const uint8_t update[] = {0xF2, 0x01}; - bool result = eink_goodisplay_send_data( - poller, screen, update, sizeof(update), eink_goodisplay_validate_0x03_cmd_response); - furi_delay_ms(1500); - - if(result) { - ctx->poller_state = EinkGoodisplayPollerStateSendDataDone; - } + uint8_t cnt = 0; + const uint8_t update_cmd_cnt = EINK_GOODISPLAY_SCREEN_BW_UPDATE_COMMAND_CNT; + ctx->poller_state = EinkGoodisplayPollerStateSendDataDone; + do { + bit_buffer_reset(screen->tx_buf); + bit_buffer_reset(screen->rx_buf); + Iso14443_4aError error = iso14443_4a_poller_send_receive_ready_block( + poller, false, screen->tx_buf, screen->rx_buf); + if(error != Iso14443_4aErrorNone) { + ctx->poller_state = EinkGoodisplayPollerStateError; + break; + } + furi_delay_us(EINK_GOODISPLAY_SCREEN_UPDATE_COMMAND_DELAY_US); + cnt++; + } while(cnt < update_cmd_cnt); return NfcCommandContinue; } static NfcCommand - eink_goodisplay_finish_handler(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + eink_goodisplay_finish_handler(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { FURI_LOG_D(TAG, "Finish"); - - uint8_t cnt = 0; - const uint8_t finish_cmd_cnt = 20; - do { - const uint8_t b2[] = {0xb2}; - bool result = eink_goodisplay_send_data( - poller, screen, b2, sizeof(b2), eink_goodisplay_validate_0xB2_response); - - if(!result) break; - cnt++; - } while(cnt < finish_cmd_cnt); + UNUSED(poller); eink_goodisplay_on_done(screen); - iso14443_3a_poller_halt(poller); return NfcCommandStop; } -static NfcCommand eink_goodisplay_error_handler(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { +static NfcCommand eink_goodisplay_error_handler(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { FURI_LOG_E(TAG, "Error!"); + UNUSED(poller); eink_goodisplay_on_error(screen); - iso14443_3a_poller_halt(poller); return NfcCommandStop; } @@ -445,12 +392,9 @@ NfcCommand eink_goodisplay_poller_callback(NfcGenericEvent event, void* context) Iso14443_4aPoller* poller = event.instance; if(Iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { - //bit_buffer_reset(screen->tx_buf); - //bit_buffer_reset(screen->rx_buf); - NfcEinkScreenSpecificGoodisplayContext* ctx = screen->device->screen_context; - command = handlers[ctx->poller_state](poller->iso14443_3a_poller, screen); + command = handlers[ctx->poller_state](poller, screen); } - if(command == NfcCommandReset) iso14443_4a_poller_halt(poller); + if(command == NfcCommandReset || command == NfcCommandStop) iso14443_4a_poller_halt(poller); return command; } From cd24df9918188853405a2df5323ff42ae274f4a8 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Mon, 21 Oct 2024 14:11:03 +0300 Subject: [PATCH 06/10] New api fix --- nfc_eink/views/image_scroll.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nfc_eink/views/image_scroll.c b/nfc_eink/views/image_scroll.c index e4a07de2..aa3cf5cd 100644 --- a/nfc_eink/views/image_scroll.c +++ b/nfc_eink/views/image_scroll.c @@ -28,7 +28,8 @@ typedef struct { static void scroll_draw_callback(Canvas* canvas, void* model) { ImageScrollViewModel* m = model; - canvas_draw_xbm_mirrored(canvas, 0, 0, IMAGE_WINDOW_WIDTH, IMAGE_WINDOW_HEIGHT, m->view_image); + canvas_draw_xbm_ex( + canvas, 0, 0, IMAGE_WINDOW_WIDTH, IMAGE_WINDOW_HEIGHT, IconRotation180, m->view_image); } static inline int16_t image_scroll_calculate_new_pos(int16_t pos, int8_t step, int16_t max_pos) { From 950decfe03617ad5d4c95035d67f75c1adce863e Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Mon, 21 Oct 2024 14:12:11 +0300 Subject: [PATCH 07/10] New error processing logic and api --- nfc_eink/nfc_eink_app_i.h | 4 +-- .../goodisplay/eink_goodispaly_poller.c | 4 +-- .../goodisplay/eink_goodisplay_i.h | 3 -- .../goodisplay/eink_goodisplay_listener.c | 2 +- nfc_eink/nfc_eink_screen/nfc_eink_screen.c | 30 ++++++++++++++----- nfc_eink/nfc_eink_screen/nfc_eink_screen.h | 1 + nfc_eink/nfc_eink_screen/nfc_eink_screen_i.h | 2 ++ nfc_eink/nfc_eink_screen/nfc_eink_types.h | 9 +++++- .../waveshare/eink_waveshare_i.h | 3 -- .../waveshare/eink_waveshare_listener.c | 2 +- 10 files changed, 39 insertions(+), 21 deletions(-) diff --git a/nfc_eink/nfc_eink_app_i.h b/nfc_eink/nfc_eink_app_i.h index 90f6e8eb..4433e6d6 100644 --- a/nfc_eink/nfc_eink_app_i.h +++ b/nfc_eink/nfc_eink_app_i.h @@ -54,8 +54,7 @@ typedef enum { NfcEinkAppCustomEventUpdating, NfcEinkAppCustomEventProcessFinish, NfcEinkAppCustomEventTargetDetected, - NfcEinkAppCustomEventTargetLost, - NfcEinkAppCustomEventUnknownError, + NfcEinkAppCustomEventError, NfcEinkAppCustomEventExit, } NfcEinkAppCustomEvents; @@ -111,6 +110,7 @@ struct NfcEinkApp { NfcListener* listener; NfcPoller* poller; NfcEinkScreen* screen; + NfcEinkScreenError last_error; const NfcEinkScreenInfo* info_temp; EinkScreenInfoArray_t arr; NfcEinkSettings settings; diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodispaly_poller.c b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodispaly_poller.c index 61647e82..034e140f 100644 --- a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodispaly_poller.c +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodispaly_poller.c @@ -360,9 +360,9 @@ static NfcCommand } static NfcCommand eink_goodisplay_error_handler(Iso14443_4aPoller* poller, NfcEinkScreen* screen) { - FURI_LOG_E(TAG, "Error!"); UNUSED(poller); - eink_goodisplay_on_error(screen); + FURI_LOG_E(TAG, "Error during writing!"); + nfc_eink_screen_set_error(screen, NfcEinkScreenErrorUnableToWrite); return NfcCommandStop; } diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_i.h b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_i.h index 6af355aa..5e9359af 100644 --- a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_i.h +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_i.h @@ -24,9 +24,6 @@ #define eink_goodisplay_on_updating(instance) \ nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeUpdating) -#define eink_goodisplay_on_error(instance) \ - nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeFailure) - typedef struct { uint8_t CLA_byte; uint8_t CMD_code; diff --git a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_listener.c b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_listener.c index d4cad916..eeff867f 100644 --- a/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_listener.c +++ b/nfc_eink/nfc_eink_screen/goodisplay/eink_goodisplay_listener.c @@ -344,7 +344,7 @@ NfcCommand eink_goodisplay_listener_callback(NfcGenericEvent event, void* contex if(Iso14443_4a_event->type == Iso14443_4aListenerEventTypeFieldOff) { if(ctx->listener_state == EinkGoodisplayListenerStateReadingBlocks) - eink_goodisplay_on_target_lost(screen); + nfc_eink_screen_set_error(screen, NfcEinkScreenErrorTargetLost); } else if(Iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) { BitBuffer* buffer = Iso14443_4a_event->data->buffer; diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_screen.c b/nfc_eink/nfc_eink_screen/nfc_eink_screen.c index d961bec4..b9534c57 100644 --- a/nfc_eink/nfc_eink_screen/nfc_eink_screen.c +++ b/nfc_eink/nfc_eink_screen/nfc_eink_screen.c @@ -49,6 +49,7 @@ NfcEinkScreen* nfc_eink_screen_alloc(NfcEinkManufacturer manufacturer) { furi_check(manufacturer < NfcEinkManufacturerNum); NfcEinkScreen* screen = malloc(sizeof(NfcEinkScreen)); + screen->error = NfcEinkScreenErrorNone; screen->handlers = manufacturers[manufacturer].handlers; screen->device = screen->handlers->alloc(); @@ -157,6 +158,22 @@ const char* nfc_eink_screen_get_name(const NfcEinkScreen* screen) { return screen->data->base.name; } +static void nfc_eink_screen_event_invoke(NfcEinkScreen* instance, NfcEinkScreenEventType type) { + furi_assert(instance); + if(instance->event_callback != NULL) { + instance->event_callback(type, instance->event_context); + } +} + +NfcEinkScreenError nfc_eink_screen_get_error(const NfcEinkScreen* screen) { + return screen->error; +} + +void nfc_eink_screen_set_error(NfcEinkScreen* instance, NfcEinkScreenError error) { + instance->error = error; + nfc_eink_screen_event_invoke(instance, NfcEinkScreenEventTypeError); +} + bool nfc_eink_screen_load_info(const char* file_path, const NfcEinkScreenInfo** info) { furi_assert(info); furi_assert(file_path); @@ -389,19 +406,16 @@ bool nfc_eink_screen_delete(const char* file_path) { return deleted; } -static void nfc_eink_screen_event_invoke(NfcEinkScreen* instance, NfcEinkScreenEventType type) { - furi_assert(instance); - if(instance->event_callback != NULL) { - instance->event_callback(type, instance->event_context); - } -} - void nfc_eink_screen_vendor_callback(NfcEinkScreen* instance, NfcEinkScreenEventType type) { furi_assert(instance); if(type == NfcEinkScreenEventTypeConfigurationReceived) { FURI_LOG_D(TAG, "Config received"); - nfc_eink_screen_init(instance, instance->device->screen_type); + + if(instance->device->screen_type == NfcEinkScreenTypeUnknown) { + nfc_eink_screen_set_error(instance, NfcEinkScreenErrorUnsupportedScreen); + } else + nfc_eink_screen_init(instance, instance->device->screen_type); } else nfc_eink_screen_event_invoke(instance, type); } diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_screen.h b/nfc_eink/nfc_eink_screen/nfc_eink_screen.h index 15d97ca3..8ff89321 100644 --- a/nfc_eink/nfc_eink_screen/nfc_eink_screen.h +++ b/nfc_eink/nfc_eink_screen/nfc_eink_screen.h @@ -29,6 +29,7 @@ uint16_t nfc_eink_screen_get_received_size(const NfcEinkScreen* screen); void nfc_eink_screen_get_progress(const NfcEinkScreen* screen, size_t* current, size_t* total); const char* nfc_eink_screen_get_name(const NfcEinkScreen* screen); +NfcEinkScreenError nfc_eink_screen_get_error(const NfcEinkScreen* screen); bool nfc_eink_screen_save(const NfcEinkScreen* screen, const char* file_path); bool nfc_eink_screen_delete(const char* file_path); diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_screen_i.h b/nfc_eink/nfc_eink_screen/nfc_eink_screen_i.h index 68d67b6d..fc566945 100644 --- a/nfc_eink/nfc_eink_screen/nfc_eink_screen_i.h +++ b/nfc_eink/nfc_eink_screen/nfc_eink_screen_i.h @@ -30,6 +30,7 @@ typedef struct { struct NfcEinkScreen { NfcEinkScreenData* data; NfcEinkScreenDevice* device; + NfcEinkScreenError error; BitBuffer* tx_buf; BitBuffer* rx_buf; const NfcEinkScreenHandlers* handlers; @@ -38,3 +39,4 @@ struct NfcEinkScreen { }; void nfc_eink_screen_vendor_callback(NfcEinkScreen* instance, NfcEinkScreenEventType type); +void nfc_eink_screen_set_error(NfcEinkScreen* instance, NfcEinkScreenError error); \ No newline at end of file diff --git a/nfc_eink/nfc_eink_screen/nfc_eink_types.h b/nfc_eink/nfc_eink_screen/nfc_eink_types.h index cb9b4cbe..228daf3b 100644 --- a/nfc_eink/nfc_eink_screen/nfc_eink_types.h +++ b/nfc_eink/nfc_eink_screen/nfc_eink_types.h @@ -7,6 +7,13 @@ #include "nfc_eink_screen_infos.h" +typedef enum { + NfcEinkScreenErrorNone, + NfcEinkScreenErrorUnableToWrite, + NfcEinkScreenErrorTargetLost, + NfcEinkScreenErrorUnsupportedScreen, +} NfcEinkScreenError; + typedef enum { NfcEinkScreenEventTypeTargetDetected, NfcEinkScreenEventTypeTargetLost, @@ -16,7 +23,7 @@ typedef enum { NfcEinkScreenEventTypeUpdating, NfcEinkScreenEventTypeFinish, - NfcEinkScreenEventTypeFailure, + NfcEinkScreenEventTypeError, } NfcEinkScreenEventType; typedef void (*NfcEinkScreenEventCallback)(NfcEinkScreenEventType type, void* context); diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_i.h b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_i.h index 7ad477c7..1a2161a9 100644 --- a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_i.h +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_i.h @@ -75,9 +75,6 @@ typedef struct { #define eink_waveshare_on_updating(instance) \ nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeUpdating) -#define eink_waveshare_on_error(instance) \ - nfc_eink_screen_vendor_callback(instance, NfcEinkScreenEventTypeFailure) - NfcCommand eink_waveshare_listener_callback(NfcGenericEvent event, void* context); NfcCommand eink_waveshare_poller_callback(NfcGenericEvent event, void* context); diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_listener.c b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_listener.c index 712b1a12..00a48710 100644 --- a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_listener.c +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_listener.c @@ -215,7 +215,7 @@ NfcCommand eink_waveshare_listener_callback(NfcGenericEvent event, void* context if(ctx->listener_state == NfcEinkWaveshareListenerStateUpdatedSuccefully) eink_waveshare_on_done(screen); else if(ctx->listener_state != NfcEinkWaveshareListenerStateIdle) - eink_waveshare_on_target_lost(screen); + nfc_eink_screen_set_error(screen, NfcEinkScreenErrorTargetLost); command = NfcCommandStop; } else if(Iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted) { FURI_LOG_D(TAG, "Halted"); From 84f9ae582636ce8c2b2d076b8bf1ddbc2d196e7b Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Mon, 21 Oct 2024 14:13:31 +0300 Subject: [PATCH 08/10] Waceshare poller fixes and retry mechanism --- .../waveshare/eink_waveshare_i.h | 2 + .../waveshare/eink_waveshare_poller.c | 120 +++++++++++++++--- 2 files changed, 104 insertions(+), 18 deletions(-) diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_i.h b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_i.h index 1a2161a9..3d63f49b 100644 --- a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_i.h +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_i.h @@ -44,6 +44,7 @@ typedef enum { EinkWavesharePollerStatePowerOnV2, EinkWavesharePollerStateRefresh, EinkWavesharePollerStateWaitReady, + EinkWavesharePollerStateRetry, EinkWavesharePollerStateFinish, EinkWavesharePollerStateError, EinkWavesharePollerStateNum @@ -52,6 +53,7 @@ typedef enum { typedef struct { NfcEinkWaveshareListenerStates listener_state; EinkWavesharePollerState poller_state; + uint8_t poller_retry_cnt; size_t data_index; uint16_t block_number; // TODO: try to remove this uint8_t buf[16 * 4]; diff --git a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_poller.c b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_poller.c index 5f1b7c71..c0b11254 100644 --- a/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_poller.c +++ b/nfc_eink/nfc_eink_screen/waveshare/eink_waveshare_poller.c @@ -3,18 +3,39 @@ #define TAG "WSH_Poller" +#define EINK_WAVESHARE_POLLER_MAX_RETRY_CNT (5) +#define EINK_WAVESHARE_POLLER_FWT (60000) +#define EINK_WAVESHARE_POLLER_FWT_INFINITE (0) + typedef NfcCommand ( *EinkWavesharePollerStateHandler)(Iso14443_3aPoller* poller, NfcEinkScreen* screen); +typedef bool (*EinkWavesharePollerResponseValidatorHandler)( + const uint8_t* response, + const size_t response_len); + +static bool eink_waveshare_default_validator(const uint8_t* response, const size_t response_len) { + return ((response_len == 2) && (response[0] == 0) && (response[1] == 0)) || + ((response_len == 2) && (response[0] == 0xFF) && (response[1] == 0)); +} + +static bool + eink_waveshare_refresh_cmd_validator(const uint8_t* response, const size_t response_len) { + return ((response_len == 2) && (response[0] == 0xFF) && (response[1] == 0)); +} + static bool eink_waveshare_send_data( Iso14443_3aPoller* poller, const NfcEinkScreen* screen, const uint8_t* data, - const size_t data_len) { + const size_t data_len, + EinkWavesharePollerResponseValidatorHandler validator, + uint32_t timeout) { furi_assert(poller); furi_assert(screen); furi_assert(data); furi_assert(data_len > 0); + furi_assert(validator); bit_buffer_reset(screen->tx_buf); bit_buffer_reset(screen->rx_buf); @@ -22,15 +43,14 @@ static bool eink_waveshare_send_data( bit_buffer_append_bytes(screen->tx_buf, data, data_len); Iso14443_3aError error = - iso14443_3a_poller_send_standard_frame(poller, screen->tx_buf, screen->rx_buf, 0); + iso14443_3a_poller_send_standard_frame(poller, screen->tx_buf, screen->rx_buf, timeout); bool result = false; if(error == Iso14443_3aErrorNone) { size_t response_len = bit_buffer_get_size_bytes(screen->rx_buf); const uint8_t* response = bit_buffer_get_data(screen->rx_buf); - result = ((response_len == 2) && (response[0] == 0) && (response[1] == 0)) || - ((response_len == 2) && (response[0] == 0xFF) && (response[1] == 0)); + result = validator(response, response_len); } else { FURI_LOG_E(TAG, "Iso14443_3aError: %02X", error); @@ -38,12 +58,14 @@ static bool eink_waveshare_send_data( return result; } -static bool eink_waveshare_send_command( +static bool eink_waveshare_send_command_ex( Iso14443_3aPoller* poller, NfcEinkScreen* screen, const uint8_t command, const uint8_t* command_data, - const size_t command_data_length) { + const size_t command_data_length, + EinkWavesharePollerResponseValidatorHandler validator, + uint32_t timeout) { size_t data_len = command_data_length + 2; uint8_t* data = malloc(data_len); @@ -55,12 +77,28 @@ static bool eink_waveshare_send_command( memcpy(&data[2], command_data, command_data_length); } - bool result = eink_waveshare_send_data(poller, screen, data, data_len); + bool result = eink_waveshare_send_data(poller, screen, data, data_len, validator, timeout); free(data); return result; } +static bool eink_waveshare_send_command( + Iso14443_3aPoller* poller, + NfcEinkScreen* screen, + const uint8_t command, + const uint8_t* command_data, + const size_t command_data_length) { + return eink_waveshare_send_command_ex( + poller, + screen, + command, + command_data, + command_data_length, + eink_waveshare_default_validator, + EINK_WAVESHARE_POLLER_FWT_INFINITE); +} + static EinkWavesharePollerState eink_waveshare_poller_state_generic_handler( Iso14443_3aPoller* poller, NfcEinkScreen* screen, @@ -183,10 +221,17 @@ static NfcCommand data[1] = EINK_WAVESHARE_COMMAND_DATA_WRITE; //or EINK_WAVESHARE_COMMAND_DATA_WRITE_V2 data[2] = block_size; memcpy(&data[3], &screen->data->image_data[ctx->data_index], block_size); - ctx->poller_state = EinkWavesharePollerStateError; + ctx->poller_state = EinkWavesharePollerStateRetry; do { - bool result = eink_waveshare_send_data(poller, screen, data, data_len); + bool result = eink_waveshare_send_data( + poller, + screen, + data, + data_len, + eink_waveshare_default_validator, + EINK_WAVESHARE_POLLER_FWT); + if(!result) break; ctx->data_index += block_size; @@ -195,12 +240,12 @@ static NfcCommand EinkWavesharePollerStatePowerOnV2 : EinkWavesharePollerStateSendImageData; + eink_waveshare_on_block_processed(screen); + screen->device->block_current = ctx->block_number; } while(false); - eink_waveshare_on_block_processed(screen); - screen->device->block_current = ctx->block_number; - free(data); + furi_delay_ms(10); return NfcCommandContinue; } @@ -212,6 +257,10 @@ static NfcCommand ctx->poller_state = eink_waveshare_poller_state_generic_handler( poller, screen, EINK_WAVESHARE_COMMAND_POWER_ON_V2, EinkWavesharePollerStateRefresh); + if(ctx->poller_state == EinkWavesharePollerStateRefresh) { + eink_waveshare_on_updating(screen); + furi_delay_ms(200); + } return NfcCommandContinue; } @@ -222,6 +271,7 @@ static NfcCommand ctx->poller_state = eink_waveshare_poller_state_generic_handler( poller, screen, EINK_WAVESHARE_COMMAND_REFRESH, EinkWavesharePollerStateWaitReady); + furi_delay_ms(500); return NfcCommandContinue; } @@ -231,12 +281,43 @@ static NfcCommand FURI_LOG_D(TAG, "Wait ready"); NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; - ctx->poller_state = eink_waveshare_poller_state_generic_handler( - poller, screen, EINK_WAVESHARE_COMMAND_WAIT_FOR_READY, EinkWavesharePollerStateFinish); - + if(eink_waveshare_send_command_ex( + poller, + screen, + EINK_WAVESHARE_COMMAND_WAIT_FOR_READY, + NULL, + 0, + eink_waveshare_refresh_cmd_validator, + EINK_WAVESHARE_POLLER_FWT)) { + ctx->poller_state = EinkWavesharePollerStateFinish; + } else { + ctx->poller_state = EinkWavesharePollerStateWaitReady; + furi_delay_ms(100); + } return NfcCommandContinue; } +static NfcCommand + eink_waveshare_poller_state_retry(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { + UNUSED(poller); + NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; + NfcCommand command = NfcCommandContinue; + if(ctx->poller_retry_cnt < EINK_WAVESHARE_POLLER_MAX_RETRY_CNT) { + FURI_LOG_E(TAG, "Retrying..."); + ctx->poller_retry_cnt++; + ctx->poller_state = EinkWavesharePollerStateInit; + ctx->data_index = 0; + ctx->block_number = 0; + screen->device->block_current = 0; + furi_delay_ms(1000); + command = NfcCommandReset; + } else { + ctx->poller_state = EinkWavesharePollerStateError; + } + + return command; +} + static NfcCommand eink_waveshare_poller_state_finish(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { FURI_LOG_D(TAG, "Finish"); @@ -257,9 +338,10 @@ static NfcCommand static NfcCommand eink_waveshare_poller_state_error_handler(Iso14443_3aPoller* poller, NfcEinkScreen* screen) { - FURI_LOG_E(TAG, "Error!"); UNUSED(poller); - eink_waveshare_on_error(screen); + FURI_LOG_E(TAG, "Error during writing!"); + nfc_eink_screen_set_error(screen, NfcEinkScreenErrorUnableToWrite); + return NfcCommandStop; } @@ -276,6 +358,7 @@ static const EinkWavesharePollerStateHandler handlers[EinkWavesharePollerStateNu [EinkWavesharePollerStatePowerOnV2] = eink_waveshare_poller_state_power_on_v2, [EinkWavesharePollerStateRefresh] = eink_waveshare_poller_state_refresh, [EinkWavesharePollerStateWaitReady] = eink_waveshare_poller_state_wait_ready, + [EinkWavesharePollerStateRetry] = eink_waveshare_poller_state_retry, [EinkWavesharePollerStateFinish] = eink_waveshare_poller_state_finish, [EinkWavesharePollerStateError] = eink_waveshare_poller_state_error_handler, }; @@ -292,7 +375,8 @@ NfcCommand eink_waveshare_poller_callback(NfcGenericEvent event, void* context) if(Iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { NfcEinkWaveshareSpecificContext* ctx = screen->device->screen_context; command = handlers[ctx->poller_state](poller, screen); + } else if(Iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { + command = NfcCommandReset; } - if(command == NfcCommandReset) iso14443_3a_poller_halt(poller); return command; } From 86beecaf6f7d01132585b6697b7553d0cd6dd9b5 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Mon, 21 Oct 2024 14:14:22 +0300 Subject: [PATCH 09/10] Scenes adjustments according to new error logic in eink screens --- nfc_eink/scenes/scene_choose_screen.c | 2 +- nfc_eink/scenes/scene_emulate.c | 15 +++++---------- nfc_eink/scenes/scene_error.c | 23 +++++++++++++++++++++-- nfc_eink/scenes/scene_write.c | 7 ++++--- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/nfc_eink/scenes/scene_choose_screen.c b/nfc_eink/scenes/scene_choose_screen.c index d08fb339..b4351a8f 100644 --- a/nfc_eink/scenes/scene_choose_screen.c +++ b/nfc_eink/scenes/scene_choose_screen.c @@ -43,7 +43,7 @@ void nfc_eink_scene_choose_screen_on_enter(void* context) { const NfcEinkScreenInfo* item = *EinkScreenInfoArray_get(instance->arr, i); submenu_add_item( submenu, item->name, i, nfc_eink_scene_choose_screen_submenu_callback, instance); - FURI_LOG_W(TAG, "Item: %s, width: %d, height: %d", item->name, item->width, item->height); + FURI_LOG_D(TAG, "Item: %s, width: %d, height: %d", item->name, item->width, item->height); } view_dispatcher_switch_to_view(instance->view_dispatcher, NfcEinkViewMenu); diff --git a/nfc_eink/scenes/scene_emulate.c b/nfc_eink/scenes/scene_emulate.c index 2654e69e..a9783a65 100644 --- a/nfc_eink/scenes/scene_emulate.c +++ b/nfc_eink/scenes/scene_emulate.c @@ -7,6 +7,7 @@ static void nfc_eink_stop_emulation(NfcEinkApp* instance, bool free_screen) { furi_assert(instance); + furi_delay_ms(5); nfc_listener_stop(instance->listener); nfc_listener_free(instance->listener); if(free_screen) { @@ -28,11 +29,8 @@ static void nfc_eink_emulate_callback(NfcEinkScreenEventType type, void* context case NfcEinkScreenEventTypeFinish: event = NfcEinkAppCustomEventProcessFinish; break; - case NfcEinkScreenEventTypeTargetLost: - event = NfcEinkAppCustomEventTargetLost; - break; - case NfcEinkScreenEventTypeFailure: - event = NfcEinkAppCustomEventUnknownError; + case NfcEinkScreenEventTypeError: + event = NfcEinkAppCustomEventError; break; default: FURI_LOG_E(TAG, "Event: %02X not implemented", type); @@ -78,11 +76,8 @@ bool nfc_eink_scene_emulate_on_event(void* context, SceneManagerEvent event) { instance->screen_loaded = true; scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneScreenMenu); notification_message(instance->notifications, &sequence_success); - } else if(event.event == NfcEinkAppCustomEventTargetLost) { - nfc_eink_stop_emulation(instance, true); - scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneError); - notification_message(instance->notifications, &sequence_error); - } else if(event.event == NfcEinkAppCustomEventUnknownError) { + } else if(event.event == NfcEinkAppCustomEventError) { + instance->last_error = nfc_eink_screen_get_error(instance->screen); nfc_eink_stop_emulation(instance, true); scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneError); notification_message(instance->notifications, &sequence_error); diff --git a/nfc_eink/scenes/scene_error.c b/nfc_eink/scenes/scene_error.c index 08b8265b..38fdb819 100644 --- a/nfc_eink/scenes/scene_error.c +++ b/nfc_eink/scenes/scene_error.c @@ -1,5 +1,23 @@ #include "../nfc_eink_app_i.h" +#define TAG "NfcEinkSceneError" + +static const char* nfc_eink_scene_error_get_text_from_code(NfcEinkScreenError error) { + if(error == NfcEinkScreenErrorUnsupportedScreen) + return "Unsupported\nscreen"; + else if(error == NfcEinkScreenErrorUnableToWrite) + return "Unable to write"; + else if(error == NfcEinkScreenErrorTargetLost) + return "Target lost"; + else if(error == NfcEinkScreenErrorNone) { + FURI_LOG_W(TAG, "No error, but on error screen"); + return "None"; + } else { + FURI_LOG_W(TAG, "Unknown error: %02X", error); + return "Unknown error"; + } +} + void nfc_eink_scene_error_popup_callback(void* context) { NfcEinkApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcEinkAppCustomEventTimerExpired); @@ -11,8 +29,9 @@ void nfc_eink_scene_error_on_enter(void* context) { Popup* popup = instance->popup; popup_set_icon(popup, 10, 14, &I_WarningDolphin_45x42); popup_set_header(popup, "Error", 90, 26, AlignCenter, AlignCenter); - ///TODO: Text error should be determined from some error variable or from EinkScreen event - popup_set_text(popup, "Target Lost", 85, 40, AlignCenter, AlignCenter); + + const char* msg = nfc_eink_scene_error_get_text_from_code(instance->last_error); + popup_set_text(popup, msg, 85, 40, AlignCenter, AlignCenter); popup_set_timeout(popup, 5000); popup_set_context(popup, instance); diff --git a/nfc_eink/scenes/scene_write.c b/nfc_eink/scenes/scene_write.c index 04a3e350..0a017171 100644 --- a/nfc_eink/scenes/scene_write.c +++ b/nfc_eink/scenes/scene_write.c @@ -25,8 +25,8 @@ static void nfc_eink_write_callback(NfcEinkScreenEventType type, void* context) event = NfcEinkAppCustomEventBlockProcessed; break; - case NfcEinkScreenEventTypeFailure: - event = NfcEinkAppCustomEventUnknownError; + case NfcEinkScreenEventTypeError: + event = NfcEinkAppCustomEventError; break; case NfcEinkScreenEventTypeUpdating: @@ -116,7 +116,8 @@ bool nfc_eink_scene_write_on_event(void* context, SceneManagerEvent event) { NfcEinkAppSceneWrite, NfcEinkAppSceneWriteStateWritingDataBlocks); nfc_eink_scene_write_show_writing_data(instance); - } else if(event.event == NfcEinkAppCustomEventUnknownError) { + } else if(event.event == NfcEinkAppCustomEventError) { + instance->last_error = nfc_eink_screen_get_error(instance->screen); scene_manager_next_scene(instance->scene_manager, NfcEinkAppSceneError); notification_message(instance->notifications, &sequence_error); } else if(event.event == NfcEinkAppCustomEventBlockProcessed) { From 00fed42cd097bc04f4f297149dfe159ee5fcfd07 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Thu, 14 Nov 2024 13:49:55 +0300 Subject: [PATCH 10/10] strcat replaced to strlcat --- nfc_eink/scenes/scene_save_name.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nfc_eink/scenes/scene_save_name.c b/nfc_eink/scenes/scene_save_name.c index 66baa19c..1862b0d9 100644 --- a/nfc_eink/scenes/scene_save_name.c +++ b/nfc_eink/scenes/scene_save_name.c @@ -52,7 +52,7 @@ bool nfc_eink_scene_save_name_on_event(void* context, SceneManagerEvent event) { furi_string_set(instance->file_path, NFC_EINK_APP_FOLDER); } - strcat(instance->text_store, NFC_EINK_APP_EXTENSION); + strlcat(instance->text_store, NFC_EINK_APP_EXTENSION, sizeof(instance->text_store)); path_append(instance->file_path, instance->text_store); if(nfc_eink_screen_save(instance->screen, furi_string_get_cstr(instance->file_path))) {