diff --git a/application.fam b/application.fam index 0d6b414..c302a05 100644 --- a/application.fam +++ b/application.fam @@ -19,6 +19,7 @@ App( "seader_worker.c", "seader_credential.c", "seader_icons.c", + "views/*.c", ], fap_icon="icons/logo.png", fap_category="NFC", diff --git a/icons/ArrowUpEmpty_14x15.png b/icons/ArrowUpEmpty_14x15.png new file mode 100644 index 0000000..261c6d8 Binary files /dev/null and b/icons/ArrowUpEmpty_14x15.png differ diff --git a/icons/ArrowUpFilled_14x15.png b/icons/ArrowUpFilled_14x15.png new file mode 100644 index 0000000..fa35eb2 Binary files /dev/null and b/icons/ArrowUpFilled_14x15.png differ diff --git a/scenes/seader_scene_config.h b/scenes/seader_scene_config.h index 2d8dcb5..5231b95 100644 --- a/scenes/seader_scene_config.h +++ b/scenes/seader_scene_config.h @@ -14,3 +14,4 @@ ADD_SCENE(seader, delete, Delete) ADD_SCENE(seader, delete_success, DeleteSuccess) ADD_SCENE(seader, credential_info, CredentialInfo) ADD_SCENE(seader, sam_info, SamInfo) +ADD_SCENE(seader, uart, Uart) diff --git a/scenes/seader_scene_sam_present.c b/scenes/seader_scene_sam_present.c index 1e1d041..61b0ef0 100644 --- a/scenes/seader_scene_sam_present.c +++ b/scenes/seader_scene_sam_present.c @@ -66,14 +66,28 @@ bool seader_scene_sam_present_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexReadPicopass) { - scene_manager_set_scene_state( - seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexReadPicopass); - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadPicopass); + if(seader->is_debug_enabled) { + seader->credential->type = SeaderCredentialTypePicopass; + scene_manager_set_scene_state( + seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexReadPicopass); + scene_manager_next_scene(seader->scene_manager, SeaderSceneUart); + } else { + scene_manager_set_scene_state( + seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexReadPicopass); + scene_manager_next_scene(seader->scene_manager, SeaderSceneReadPicopass); + } consumed = true; } else if(event.event == SubmenuIndexRead14a) { - scene_manager_set_scene_state( - seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexRead14a); - scene_manager_next_scene(seader->scene_manager, SeaderSceneRead14a); + if(seader->is_debug_enabled) { + seader->credential->type = SeaderCredentialType14A; + scene_manager_set_scene_state( + seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexRead14a); + scene_manager_next_scene(seader->scene_manager, SeaderSceneUart); + } else { + scene_manager_set_scene_state( + seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexRead14a); + scene_manager_next_scene(seader->scene_manager, SeaderSceneRead14a); + } consumed = true; } else if(event.event == SubmenuIndexSamInfo) { scene_manager_set_scene_state( diff --git a/scenes/seader_scene_uart.c b/scenes/seader_scene_uart.c new file mode 100644 index 0000000..ccd7be8 --- /dev/null +++ b/scenes/seader_scene_uart.c @@ -0,0 +1,101 @@ +#include "../seader_i.h" +#include "../seader_bridge.h" +#define TAG "SeaderSceneUart" + +typedef struct { + SeaderUartConfig cfg; + SeaderUartState state; +} SceneUartBridge; + +static SceneUartBridge* scene_uart; + +void seader_uart_worker_callback(SeaderWorkerEvent event, void* context) { + UNUSED(event); + Seader* seader = context; + view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit); +} + +void seader_scene_uart_callback(SeaderCustomEvent event, void* context) { + furi_assert(context); + Seader* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void seader_scene_uart_on_enter(void* context) { + Seader* app = context; + uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, SeaderViewUart); + if(prev_state == 0) { + scene_uart = malloc(sizeof(SceneUartBridge)); + scene_uart->cfg.uart_ch = 0; + scene_uart->cfg.flow_pins = 0; + scene_uart->cfg.baudrate_mode = 0; + scene_uart->cfg.baudrate = 0; + } + + seader_uart_get_config(app->uart, &scene_uart->cfg); + seader_uart_get_state(app->uart, &scene_uart->state); + + seader_uart_view_set_callback(app->seader_uart_view, seader_scene_uart_callback, app); + scene_manager_set_scene_state(app->scene_manager, SeaderSceneUart, 0); + view_dispatcher_switch_to_view(app->view_dispatcher, SeaderViewUart); + notification_message(app->notifications, &sequence_display_backlight_enforce_on); + + Seader* seader = app; + + if(seader->credential->type == SeaderCredentialTypePicopass) { + seader_worker_start( + seader->worker, + SeaderWorkerStateReadPicopass, + seader->uart, + seader->credential, + seader_uart_worker_callback, + seader); + } else if(seader->credential->type == SeaderCredentialType14A) { + seader_worker_start( + seader->worker, + SeaderWorkerStateRead14a, + seader->uart, + seader->credential, + seader_uart_worker_callback, + seader); + } +} + +bool seader_scene_uart_on_event(void* context, SceneManagerEvent event) { + Seader* app = context; + Seader* seader = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SeaderCustomEventWorkerExit) { + scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); + consumed = true; + } else if(event.type == SceneManagerEventTypeTick) { + scene_manager_set_scene_state(app->scene_manager, SeaderSceneUart, 1); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeTick) { + uint32_t tx_cnt_last = scene_uart->state.tx_cnt; + uint32_t rx_cnt_last = scene_uart->state.rx_cnt; + seader_uart_get_state(app->uart, &scene_uart->state); + if(seader->credential->type == SeaderCredentialTypePicopass) { + scene_uart->state.protocol = FrameProtocol_iclass; + } else if(seader->credential->type == SeaderCredentialType14A) { + scene_uart->state.protocol = FrameProtocol_nfc; + } + seader_uart_view_update_state(app->seader_uart_view, &scene_uart->cfg, &scene_uart->state); + if(tx_cnt_last != scene_uart->state.tx_cnt) { + notification_message(app->notifications, &sequence_blink_blue_10); + } + if(rx_cnt_last != scene_uart->state.rx_cnt) { + notification_message(app->notifications, &sequence_blink_green_10); + } + } + return consumed; +} + +void seader_scene_uart_on_exit(void* context) { + Seader* app = context; + seader_worker_stop(app->worker); + notification_message(app->notifications, &sequence_display_backlight_enforce_auto); +} diff --git a/seader.c b/seader.c index ba74eab..9d566d7 100644 --- a/seader.c +++ b/seader.c @@ -78,6 +78,12 @@ Seader* seader_alloc() { view_dispatcher_add_view( seader->view_dispatcher, SeaderViewWidget, widget_get_view(seader->widget)); + seader->seader_uart_view = seader_uart_view_alloc(); + view_dispatcher_add_view( + seader->view_dispatcher, + SeaderViewUart, + seader_uart_view_get_view(seader->seader_uart_view)); + return seader; } @@ -114,6 +120,9 @@ void seader_free(Seader* seader) { view_dispatcher_remove_view(seader->view_dispatcher, SeaderViewWidget); widget_free(seader->widget); + view_dispatcher_remove_view(seader->view_dispatcher, SeaderViewUart); + seader_uart_view_free(seader->seader_uart_view); + // Worker seader_worker_stop(seader->worker); seader_worker_free(seader->worker); diff --git a/seader_bridge.h b/seader_bridge.h index 26f1920..7beabb6 100644 --- a/seader_bridge.h +++ b/seader_bridge.h @@ -21,7 +21,7 @@ typedef struct { typedef struct { uint32_t rx_cnt; uint32_t tx_cnt; - uint32_t baudrate_cur; + uint8_t protocol; } SeaderUartState; struct SeaderUartBridge { diff --git a/seader_custom_event.h b/seader_custom_event.h new file mode 100644 index 0000000..5749764 --- /dev/null +++ b/seader_custom_event.h @@ -0,0 +1,5 @@ +#pragma once + +typedef enum { + SeaderStartEventNone = 0, +} SeaderCustomEvent; diff --git a/seader_i.h b/seader_i.h index 1f67344..e152fdc 100644 --- a/seader_i.h +++ b/seader_i.h @@ -33,6 +33,7 @@ #include #include "scenes/seader_scene.h" +#include "views/seader_uart_view.h" #include "seader_bridge.h" #include "seader.h" @@ -93,6 +94,9 @@ struct Seader { Loading* loading; TextInput* text_input; Widget* widget; + + //Custom views + SeaderUartView* seader_uart_view; }; typedef enum { @@ -101,6 +105,7 @@ typedef enum { SeaderViewLoading, SeaderViewTextInput, SeaderViewWidget, + SeaderViewUart, } SeaderView; void seader_text_store_set(Seader* seader, const char* text, ...); diff --git a/seader_worker.c b/seader_worker.c index 3d6f317..33dbd38 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -871,6 +871,7 @@ int32_t seader_worker_task(void* context) { FURI_LOG_D(TAG, "Read Picopass"); requestPacs = true; seader_credential_clear(seader_worker->credential); + seader_worker->credential->type = SeaderCredentialTypePicopass; seader_worker_enable_field(); if(picopass_card_read(seader_worker) != ERR_NONE) { // Turn off if cancelled / no card found @@ -880,6 +881,7 @@ int32_t seader_worker_task(void* context) { FURI_LOG_D(TAG, "Read 14a"); requestPacs = true; seader_credential_clear(seader_worker->credential); + seader_worker->credential->type = SeaderCredentialType14A; nfc_scene_field_on_enter(); if(!detect_nfc(seader_worker)) { // Turn off if cancelled / no card found diff --git a/uart.c b/uart.c index b048e9f..7afd90a 100644 --- a/uart.c +++ b/uart.c @@ -33,7 +33,6 @@ void seader_uart_serial_deinit(SeaderUartBridge* seader_uart, uint8_t uart_ch) { void seader_uart_set_baudrate(SeaderUartBridge* seader_uart, uint32_t baudrate) { if(baudrate != 0) { furi_hal_uart_set_br(seader_uart->cfg.uart_ch, baudrate); - seader_uart->st.baudrate_cur = baudrate; } else { FURI_LOG_I(TAG, "No baudrate specified"); } diff --git a/views/seader_uart_view.c b/views/seader_uart_view.c new file mode 100644 index 0000000..1d1908a --- /dev/null +++ b/views/seader_uart_view.c @@ -0,0 +1,152 @@ +#include "../seader_bridge.h" +#include "../seader_i.h" +#include +#include + +struct SeaderUartView { + View* view; + SeaderUartViewCallback callback; + void* context; +}; + +typedef struct { + uint32_t tx_cnt; + uint32_t rx_cnt; + bool tx_active; + bool rx_active; + uint8_t protocol; +} SeaderUartViewModel; + +static void seader_uart_view_draw_callback(Canvas* canvas, void* _model) { + SeaderUartViewModel* model = _model; + char temp_str[18]; + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, "PICC <=> SAM"); + canvas_draw_str(canvas, 3, 25, "TX:"); + canvas_draw_str(canvas, 3, 42, "RX:"); + + if(model->protocol == FrameProtocol_iclass) { + canvas_draw_str_aligned(canvas, 64, 62, AlignCenter, AlignBottom, "Detecting picopass"); + } else if(model->protocol == FrameProtocol_nfc) { + canvas_draw_str_aligned(canvas, 64, 62, AlignCenter, AlignBottom, "Detecting 14a"); + } + + if(model->tx_cnt < 100000000) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "B."); + canvas_set_font(canvas, FontKeyboard); + snprintf(temp_str, 18, "%lu", model->tx_cnt); + canvas_draw_str_aligned(canvas, 116, 24, AlignRight, AlignBottom, temp_str); + } else { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "KiB."); + canvas_set_font(canvas, FontKeyboard); + snprintf(temp_str, 18, "%lu", model->tx_cnt / 1024); + canvas_draw_str_aligned(canvas, 111, 24, AlignRight, AlignBottom, temp_str); + } + + if(model->rx_cnt < 100000000) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "B."); + canvas_set_font(canvas, FontKeyboard); + snprintf(temp_str, 18, "%lu", model->rx_cnt); + canvas_draw_str_aligned(canvas, 116, 41, AlignRight, AlignBottom, temp_str); + } else { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "KiB."); + canvas_set_font(canvas, FontKeyboard); + snprintf(temp_str, 18, "%lu", model->rx_cnt / 1024); + canvas_draw_str_aligned(canvas, 111, 41, AlignRight, AlignBottom, temp_str); + } + + if(model->tx_active) { + canvas_draw_icon(canvas, 48, 14, &I_ArrowUpFilled_14x15); + } else { + canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15); + } + + if(model->rx_active) { + canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180); + } else { + canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180); + } +} + +static bool seader_uart_view_input_callback(InputEvent* event, void* context) { + furi_assert(context); + SeaderUartView* seader_uart_view = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + consumed = true; + furi_assert(seader_uart_view->callback); + // seader_uart_view->callback(SeaderUartViewEventConfig, seader_uart_view->context); + } + } + + return consumed; +} + +SeaderUartView* seader_uart_view_alloc() { + SeaderUartView* seader_uart_view = malloc(sizeof(SeaderUartView)); + + seader_uart_view->view = view_alloc(); + view_allocate_model(seader_uart_view->view, ViewModelTypeLocking, sizeof(SeaderUartViewModel)); + view_set_context(seader_uart_view->view, seader_uart_view); + view_set_draw_callback(seader_uart_view->view, seader_uart_view_draw_callback); + view_set_input_callback(seader_uart_view->view, seader_uart_view_input_callback); + + return seader_uart_view; +} + +void seader_uart_view_free(SeaderUartView* seader_uart_view) { + furi_assert(seader_uart_view); + view_free(seader_uart_view->view); + free(seader_uart_view); +} + +View* seader_uart_view_get_view(SeaderUartView* seader_uart_view) { + furi_assert(seader_uart_view); + return seader_uart_view->view; +} + +void seader_uart_view_set_callback( + SeaderUartView* seader_uart_view, + SeaderUartViewCallback callback, + void* context) { + furi_assert(seader_uart_view); + furi_assert(callback); + + with_view_model( + seader_uart_view->view, + SeaderUartViewModel * model, + { + UNUSED(model); + seader_uart_view->callback = callback; + seader_uart_view->context = context; + }, + false); +} + +void seader_uart_view_update_state( + SeaderUartView* instance, + SeaderUartConfig* cfg, + SeaderUartState* st) { + furi_assert(instance); + furi_assert(cfg); + furi_assert(st); + + with_view_model( + instance->view, + SeaderUartViewModel * model, + { + model->tx_active = (model->tx_cnt != st->tx_cnt); + model->rx_active = (model->rx_cnt != st->rx_cnt); + model->tx_cnt = st->tx_cnt; + model->rx_cnt = st->rx_cnt; + model->protocol = st->protocol; + }, + true); +} diff --git a/views/seader_uart_view.h b/views/seader_uart_view.h new file mode 100644 index 0000000..f6f87ad --- /dev/null +++ b/views/seader_uart_view.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "../seader_custom_event.h" +#include "../seader_bridge.h" + +typedef struct SeaderUartView SeaderUartView; +typedef void (*SeaderUartViewCallback)(SeaderCustomEvent event, void* context); + +SeaderUartView* seader_uart_view_alloc(); + +void seader_uart_view_free(SeaderUartView* seader_uart_view); + +View* seader_uart_view_get_view(SeaderUartView* seader_uart_view); + +void seader_uart_view_set_callback( + SeaderUartView* seader_uart_view, + SeaderUartViewCallback callback, + void* context); + +void seader_uart_view_update_state( + SeaderUartView* instance, + SeaderUartConfig* cfg, + SeaderUartState* st);