Pārlūkot izejas kodu

[FL-2864] NFC update detect reader (#1820)

* nfc: update detect reader view
* nfc: make detect reader more interractive
* nfc: update icons
* nfc: fix detect reader gui
* nfc: fix gui, fix worker events
* nfc: fix notifications
* nfc: add nfc_worker NULL assert

Co-authored-by: あく <alleteam@gmail.com>
gornekich 3 gadi atpakaļ
vecāks
revīzija
5de2c32c81

+ 41 - 4
applications/main/nfc/scenes/nfc_scene_detect_reader.c

@@ -1,6 +1,15 @@
 #include "../nfc_i.h"
 #include <dolphin/dolphin.h>
 
+#define NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX (10U)
+
+static const NotificationSequence sequence_detect_reader = {
+    &message_green_255,
+    &message_blue_255,
+    &message_do_not_reset,
+    NULL,
+};
+
 bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) {
     UNUSED(event);
     furi_assert(context);
@@ -20,21 +29,26 @@ void nfc_scene_detect_reader_on_enter(void* context) {
     DOLPHIN_DEED(DolphinDeedNfcEmulate);
 
     detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc);
+    detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX);
+
+    // Store number of collected nonces in scene state
+    scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDetectReader, 0);
+    notification_message(nfc->notifications, &sequence_detect_reader);
+
     nfc_worker_start(
         nfc->worker,
         NfcWorkerStateAnalyzeReader,
         &nfc->dev->dev_data,
         nfc_detect_reader_worker_callback,
         nfc);
-
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDetectReader);
-
-    nfc_blink_read_start(nfc);
 }
 
 bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = context;
     bool consumed = false;
+    uint32_t nonces_collected =
+        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDetectReader);
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == NfcCustomEventViewExit) {
@@ -42,8 +56,29 @@ bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyNoncesInfo);
             consumed = true;
         } else if(event.event == NfcWorkerEventDetectReaderMfkeyCollected) {
-            detect_reader_inc_nonce_cnt(nfc->detect_reader);
+            nonces_collected += 2;
+            scene_manager_set_scene_state(
+                nfc->scene_manager, NfcSceneDetectReader, nonces_collected);
+            detect_reader_set_nonces_collected(nfc->detect_reader, nonces_collected);
+            if(nonces_collected >= NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX) {
+                detect_reader_set_state(nfc->detect_reader, DetectReaderStateDone);
+                nfc_blink_stop(nfc);
+                notification_message(nfc->notifications, &sequence_single_vibro);
+                notification_message(nfc->notifications, &sequence_set_green_255);
+                nfc_worker_stop(nfc->worker);
+            }
             consumed = true;
+        } else if(event.event == NfcWorkerEventDetectReaderDetected) {
+            if(nonces_collected < NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX) {
+                notification_message(nfc->notifications, &sequence_blink_start_cyan);
+                detect_reader_set_state(nfc->detect_reader, DetectReaderStateReaderDetected);
+            }
+        } else if(event.event == NfcWorkerEventDetectReaderLost) {
+            if(nonces_collected < NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX) {
+                nfc_blink_stop(nfc);
+                notification_message(nfc->notifications, &sequence_detect_reader);
+                detect_reader_set_state(nfc->detect_reader, DetectReaderStateReaderLost);
+            }
         }
     }
 
@@ -59,5 +94,7 @@ void nfc_scene_detect_reader_on_exit(void* context) {
     // Clear view
     detect_reader_reset(nfc->detect_reader);
 
+    // Stop notifications
     nfc_blink_stop(nfc);
+    notification_message(nfc->notifications, &sequence_reset_green);
 }

+ 3 - 3
applications/main/nfc/scenes/nfc_scene_mfkey_nonces_info.c

@@ -16,14 +16,14 @@ void nfc_scene_mfkey_nonces_info_on_enter(void* context) {
 
     uint16_t nonces_saved = mfkey32_get_auth_sectors(temp_str);
     widget_add_text_scroll_element(nfc->widget, 0, 22, 128, 42, furi_string_get_cstr(temp_str));
-    furi_string_printf(temp_str, "Nonces saved %d", nonces_saved);
+    furi_string_printf(temp_str, "Nonce pairs saved: %d", nonces_saved);
     widget_add_string_element(
         nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(temp_str));
     widget_add_string_element(
         nfc->widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Authenticated sectors:");
 
     widget_add_button_element(
-        nfc->widget, GuiButtonTypeRight, "Next", nfc_scene_mfkey_nonces_info_callback, nfc);
+        nfc->widget, GuiButtonTypeCenter, "OK", nfc_scene_mfkey_nonces_info_callback, nfc);
 
     furi_string_free(temp_str);
 
@@ -35,7 +35,7 @@ bool nfc_scene_mfkey_nonces_info_on_event(void* context, SceneManagerEvent event
     bool consumed = false;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == GuiButtonTypeRight) {
+        if(event.event == GuiButtonTypeCenter) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyComplete);
             consumed = true;
         }

+ 58 - 15
applications/main/nfc/views/detect_reader.c

@@ -10,29 +10,50 @@ struct DetectReader {
 
 typedef struct {
     uint16_t nonces;
+    uint16_t nonces_max;
+    DetectReaderState state;
 } DetectReaderViewModel;
 
 static void detect_reader_draw_callback(Canvas* canvas, void* model) {
     DetectReaderViewModel* m = model;
     char text[32] = {};
 
-    snprintf(text, sizeof(text), "Tap the reader several times");
-    canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, "Tap the reader several times");
+    // Draw header and icon
+    canvas_draw_icon(canvas, 0, 16, &I_Modern_reader_18x34);
+    if(m->state == DetectReaderStateStart) {
+        snprintf(text, sizeof(text), "Touch the reader");
+        canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39);
+    } else if(m->state == DetectReaderStateReaderDetected) {
+        snprintf(text, sizeof(text), "Move the Flipper away");
+        canvas_draw_icon(canvas, 24, 25, &I_Release_arrow_18x15);
+    } else if(m->state == DetectReaderStateReaderLost) {
+        snprintf(text, sizeof(text), "Touch the reader again");
+        canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39);
+    }
+
+    canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, text);
 
-    if(m->nonces == 0) {
+    // Draw collected nonces
+    if(m->state == DetectReaderStateStart) {
         canvas_set_font(canvas, FontPrimary);
-        canvas_draw_str_aligned(canvas, 52, 22, AlignLeft, AlignTop, "Emulating...");
+        canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Emulating...");
         canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str_aligned(canvas, 52, 35, AlignLeft, AlignTop, "MIFARE Classic");
-        canvas_draw_icon(canvas, 0, 13, &I_Tap_reader_36x38);
+        canvas_draw_str_aligned(canvas, 51, 35, AlignLeft, AlignTop, "MIFARE MFkey32");
     } else {
-        canvas_set_font(canvas, FontPrimary);
-        canvas_draw_str_aligned(canvas, 54, 22, AlignLeft, AlignTop, "Collecting...");
+        if(m->state == DetectReaderStateDone) {
+            canvas_set_font(canvas, FontPrimary);
+            canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Completed!");
+        } else {
+            canvas_set_font(canvas, FontPrimary);
+            canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Collecting...");
+        }
         canvas_set_font(canvas, FontSecondary);
-        snprintf(text, sizeof(text), "Nonces: %d", m->nonces);
-        canvas_draw_str_aligned(canvas, 54, 35, AlignLeft, AlignTop, text);
-        elements_button_right(canvas, "Next");
-        canvas_draw_icon(canvas, 6, 15, &I_ArrowC_1_36x36);
+        snprintf(text, sizeof(text), "Nonce pairs: %d/%d", m->nonces, m->nonces_max);
+        canvas_draw_str_aligned(canvas, 51, 35, AlignLeft, AlignTop, text);
+    }
+    // Draw button
+    if(m->nonces > 0) {
+        elements_button_center(canvas, "Done");
     }
 }
 
@@ -49,7 +70,7 @@ static bool detect_reader_input_callback(InputEvent* event, void* context) {
         });
 
     if(event->type == InputTypeShort) {
-        if(event->key == InputKeyRight) {
+        if(event->key == InputKeyOk) {
             if(nonces > 0) {
                 detect_reader->callback(detect_reader->context);
                 consumed = true;
@@ -84,6 +105,8 @@ void detect_reader_reset(DetectReader* detect_reader) {
     with_view_model(
         detect_reader->view, (DetectReaderViewModel * model) {
             model->nonces = 0;
+            model->nonces_max = 0;
+            model->state = DetectReaderStateStart;
             return false;
         });
 }
@@ -105,11 +128,31 @@ void detect_reader_set_callback(
     detect_reader->context = context;
 }
 
-void detect_reader_inc_nonce_cnt(DetectReader* detect_reader) {
+void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_max) {
+    furi_assert(detect_reader);
+
+    with_view_model(
+        detect_reader->view, (DetectReaderViewModel * model) {
+            model->nonces_max = nonces_max;
+            return false;
+        });
+}
+
+void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected) {
     furi_assert(detect_reader);
+
     with_view_model(
         detect_reader->view, (DetectReaderViewModel * model) {
-            model->nonces++;
+            model->nonces = nonces_collected;
             return false;
         });
 }
+
+void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state) {
+    furi_assert(detect_reader);
+    with_view_model(
+        detect_reader->view, (DetectReaderViewModel * model) {
+            model->state = state;
+            return true;
+        });
+}

+ 12 - 1
applications/main/nfc/views/detect_reader.h

@@ -5,6 +5,13 @@
 
 typedef struct DetectReader DetectReader;
 
+typedef enum {
+    DetectReaderStateStart,
+    DetectReaderStateReaderDetected,
+    DetectReaderStateReaderLost,
+    DetectReaderStateDone,
+} DetectReaderState;
+
 typedef void (*DetectReaderDoneCallback)(void* context);
 
 DetectReader* detect_reader_alloc();
@@ -20,4 +27,8 @@ void detect_reader_set_callback(
     DetectReaderDoneCallback callback,
     void* context);
 
-void detect_reader_inc_nonce_cnt(DetectReader* detect_reader);
+void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_max);
+
+void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected);
+
+void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state);

BIN
assets/icons/NFC/Modern_reader_18x34.png


BIN
assets/icons/NFC/Move_flipper_26x39.png


BIN
assets/icons/NFC/Release_arrow_18x15.png


+ 3 - 0
firmware/targets/f7/api_symbols.csv

@@ -2693,6 +2693,8 @@ Variable,+,I_Lock_7x8,const Icon,
 Variable,+,I_Lock_8x8,const Icon,
 Variable,+,I_MHz_25x11,const Icon,
 Variable,+,I_Medium_chip_22x21,const Icon,
+Variable,+,I_Modern_reader_18x34,const Icon,
+Variable,+,I_Move_flipper_26x39,const Icon,
 Variable,+,I_Mute_25x27,const Icon,
 Variable,+,I_Mute_hvr_25x27,const Icon,
 Variable,+,I_NFC_manual_60x50,const Icon,
@@ -2720,6 +2722,7 @@ Variable,+,I_RFIDDolphinReceive_97x61,const Icon,
 Variable,+,I_RFIDDolphinSend_97x61,const Icon,
 Variable,+,I_RFIDDolphinSuccess_108x57,const Icon,
 Variable,+,I_Reader_detect_43x40,const Icon,
+Variable,+,I_Release_arrow_18x15,const Icon,
 Variable,+,I_Restoring_38x32,const Icon,
 Variable,+,I_Right_mouse_icon_9x9,const Icon,
 Variable,+,I_SDQuestion_35x43,const Icon,

+ 1 - 1
lib/nfc/helpers/mfkey32.c

@@ -92,7 +92,7 @@ void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback,
 
 static bool mfkey32_write_params(Mfkey32* instance, Mfkey32Params* params) {
     FuriString* str = furi_string_alloc_printf(
-        "Sector %d key %c cuid %08x nt0 %08x nr0 %08x ar0 %08x nt1 %08x nr1 %08x ar1 %08x\n",
+        "Sec %d key %c cuid %08x nt0 %08x nr0 %08x ar0 %08x nt1 %08x nr1 %08x ar1 %08x\n",
         params->sector,
         params->key == MfClassicKeyA ? 'A' : 'B',
         params->cuid,

+ 20 - 1
lib/nfc/nfc_worker.c

@@ -647,7 +647,8 @@ static void nfc_worker_reader_analyzer_callback(ReaderAnalyzerEvent event, void*
     furi_assert(context);
     NfcWorker* nfc_worker = context;
 
-    if(event == ReaderAnalyzerEventMfkeyCollected) {
+    if((nfc_worker->state == NfcWorkerStateAnalyzeReader) &&
+       (event == ReaderAnalyzerEventMfkeyCollected)) {
         if(nfc_worker->callback) {
             nfc_worker->callback(NfcWorkerEventDetectReaderMfkeyCollected, nfc_worker->context);
         }
@@ -655,6 +656,9 @@ static void nfc_worker_reader_analyzer_callback(ReaderAnalyzerEvent event, void*
 }
 
 void nfc_worker_analyze_reader(NfcWorker* nfc_worker) {
+    furi_assert(nfc_worker);
+    furi_assert(nfc_worker->callback);
+
     FuriHalNfcTxRxContext tx_rx = {};
 
     ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer;
@@ -673,17 +677,32 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) {
     rfal_platform_spi_acquire();
 
     FURI_LOG_D(TAG, "Start reader analyzer");
+
+    uint8_t reader_no_data_received_cnt = 0;
+    bool reader_no_data_notified = true;
+
     while(nfc_worker->state == NfcWorkerStateAnalyzeReader) {
         furi_hal_nfc_stop_cmd();
         furi_delay_ms(5);
         furi_hal_nfc_listen_start(nfc_data);
         if(furi_hal_nfc_listen_rx(&tx_rx, 300)) {
+            if(reader_no_data_notified) {
+                nfc_worker->callback(NfcWorkerEventDetectReaderDetected, nfc_worker->context);
+            }
+            reader_no_data_received_cnt = 0;
+            reader_no_data_notified = false;
             NfcProtocol protocol =
                 reader_analyzer_guess_protocol(reader_analyzer, tx_rx.rx_data, tx_rx.rx_bits / 8);
             if(protocol == NfcDeviceProtocolMifareClassic) {
                 mf_classic_emulator(&emulator, &tx_rx);
             }
         } else {
+            reader_no_data_received_cnt++;
+            if(!reader_no_data_notified && (reader_no_data_received_cnt > 5)) {
+                nfc_worker->callback(NfcWorkerEventDetectReaderLost, nfc_worker->context);
+                reader_no_data_received_cnt = 0;
+                reader_no_data_notified = true;
+            }
             FURI_LOG_D(TAG, "No data from reader");
             continue;
         }

+ 2 - 0
lib/nfc/nfc_worker.h

@@ -56,6 +56,8 @@ typedef enum {
     NfcWorkerEventFoundKeyB,
 
     // Detect Reader events
+    NfcWorkerEventDetectReaderDetected,
+    NfcWorkerEventDetectReaderLost,
     NfcWorkerEventDetectReaderMfkeyCollected,
 
     // Mifare Ultralight events