Explorar el Código

Merge pull request #10 from zacharyweiss/settings

GPIO Settings
Zachary Weiss hace 1 año
padre
commit
6f05f6914b

+ 60 - 51
helpers/mag_helpers.c

@@ -2,12 +2,6 @@
 
 #define TAG "MagHelpers"
 
-// Haviv Board - pins gpio_ext_pa7 & gpio_ext_pa6 was swapped.
-#define GPIO_PIN_A &gpio_ext_pa7
-#define GPIO_PIN_B &gpio_ext_pa6
-#define GPIO_PIN_ENABLE &gpio_ext_pa4
-#define RFID_PIN_OUT &gpio_rfid_carrier_out
-
 #define ZERO_PREFIX 25 // n zeros prefix
 #define ZERO_BETWEEN 53 // n zeros between tracks
 #define ZERO_SUFFIX 25 // n zeros suffix
@@ -19,18 +13,18 @@ const int sublen[] = {32, 48, 48};
 
 uint8_t last_value = 2;
 
-void play_halfbit(bool value, MagSetting* setting) {
-    switch(setting->tx) {
+void play_halfbit(bool value, MagState* state) {
+    switch(state->tx) {
     case MagTxStateRFID:
-        furi_hal_gpio_write(RFID_PIN_OUT, value);
+        furi_hal_gpio_write(&gpio_rfid_carrier_out, value);
         /*furi_hal_gpio_write(RFID_PIN_OUT, !value);
         furi_hal_gpio_write(RFID_PIN_OUT, value);
         furi_hal_gpio_write(RFID_PIN_OUT, !value);
         furi_hal_gpio_write(RFID_PIN_OUT, value);*/
         break;
     case MagTxStateGPIO:
-        furi_hal_gpio_write(GPIO_PIN_A, value);
-        furi_hal_gpio_write(GPIO_PIN_B, !value);
+        furi_hal_gpio_write(mag_state_enum_to_pin(state->pin_input), value);
+        furi_hal_gpio_write(mag_state_enum_to_pin(state->pin_output), !value);
         break;
     case MagTxStatePiezo:
         furi_hal_gpio_write(&gpio_speaker, value);
@@ -41,7 +35,7 @@ void play_halfbit(bool value, MagSetting* setting) {
 
         break;
     case MagTxStateLF_P:
-        furi_hal_gpio_write(RFID_PIN_OUT, value);
+        furi_hal_gpio_write(&gpio_rfid_carrier_out, value);
         furi_hal_gpio_write(&gpio_speaker, value);
 
         /* // Weaker but cleaner signal
@@ -88,7 +82,7 @@ void play_halfbit(bool value, MagSetting* setting) {
     last_value = value;
 }
 
-void play_track(uint8_t* bits_manchester, uint16_t n_bits, MagSetting* setting, bool reverse) {
+void play_track(uint8_t* bits_manchester, uint16_t n_bits, MagState* state, bool reverse) {
     for(uint16_t i = 0; i < n_bits; i++) {
         uint16_t j = (reverse) ? (n_bits - i - 1) : i;
         uint8_t byte = j / 8;
@@ -117,9 +111,9 @@ void play_track(uint8_t* bits_manchester, uint16_t n_bits, MagSetting* setting,
         // for DWT->CYCCNT. Note timer is aliased to 64us as per
         // #define FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND (SystemCoreClock / 1000000) | furi_hal_cortex.c
 
-        play_halfbit(bit, setting);
-        furi_delay_us(setting->us_clock);
-        // if (i % 2 == 1) furi_delay_us(setting->us_interpacket);
+        play_halfbit(bit, state);
+        furi_delay_us(state->us_clock);
+        // if (i % 2 == 1) furi_delay_us(state->us_interpacket);
     }
 }
 
@@ -131,7 +125,7 @@ void tx_init_rfid() {
     // furi_hal_ibutton_start_drive();
     furi_hal_ibutton_pin_write(false);
 
-    // Initializing at GpioSpeedLow seems sufficient for our needs; no improvements seen by increasing speed setting
+    // Initializing at GpioSpeedLow seems sufficient for our needs; no improvements seen by increasing speed state
 
     // this doesn't seem to make a difference, leaving it in
     furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
@@ -143,14 +137,14 @@ void tx_init_rfid() {
     furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
     furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false);
 
-    furi_hal_gpio_init(RFID_PIN_OUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
+    furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
 
     furi_delay_ms(300);
 }
 
 void tx_deinit_rfid() {
     // reset RFID system
-    furi_hal_gpio_write(RFID_PIN_OUT, 0);
+    furi_hal_gpio_write(&gpio_rfid_carrier_out, 0);
 
     furi_hal_rfid_pins_reset();
 }
@@ -179,19 +173,31 @@ void tx_deinit_piezo() {
     furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 }
 
-bool tx_init(MagSetting* setting) {
+bool tx_init(MagState* state) {
     // Initialize configured TX method
-    switch(setting->tx) {
+    switch(state->tx) {
     case MagTxStateRFID:
         tx_init_rfid();
         break;
     case MagTxStateGPIO:
         // gpio_item_configure_all_pins(GpioModeOutputPushPull);
-        furi_hal_gpio_init(GPIO_PIN_A, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
-        furi_hal_gpio_init(GPIO_PIN_B, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
-        furi_hal_gpio_init(GPIO_PIN_ENABLE, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
-
-        furi_hal_gpio_write(GPIO_PIN_ENABLE, 1);
+        furi_hal_gpio_init(
+            mag_state_enum_to_pin(state->pin_input),
+            GpioModeOutputPushPull,
+            GpioPullNo,
+            GpioSpeedLow);
+        furi_hal_gpio_init(
+            mag_state_enum_to_pin(state->pin_output),
+            GpioModeOutputPushPull,
+            GpioPullNo,
+            GpioSpeedLow);
+        furi_hal_gpio_init(
+            mag_state_enum_to_pin(state->pin_enable),
+            GpioModeOutputPushPull,
+            GpioPullNo,
+            GpioSpeedLow);
+
+        furi_hal_gpio_write(mag_state_enum_to_pin(state->pin_enable), 1);
 
         // had some issues with ~300; bumped higher temporarily
         furi_delay_ms(500);
@@ -219,21 +225,24 @@ bool tx_init(MagSetting* setting) {
     return true;
 }
 
-bool tx_deinit(MagSetting* setting) {
+bool tx_deinit(MagState* state) {
     // Reset configured TX method
-    switch(setting->tx) {
+    switch(state->tx) {
     case MagTxStateRFID:
         tx_deinit_rfid();
         break;
     case MagTxStateGPIO:
-        furi_hal_gpio_write(GPIO_PIN_A, 0);
-        furi_hal_gpio_write(GPIO_PIN_B, 0);
-        furi_hal_gpio_write(GPIO_PIN_ENABLE, 0);
+        furi_hal_gpio_write(mag_state_enum_to_pin(state->pin_input), 0);
+        furi_hal_gpio_write(mag_state_enum_to_pin(state->pin_output), 0);
+        furi_hal_gpio_write(mag_state_enum_to_pin(state->pin_enable), 0);
 
         // set back to analog output mode? - YES
-        furi_hal_gpio_init(GPIO_PIN_A, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-        furi_hal_gpio_init(GPIO_PIN_B, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-        furi_hal_gpio_init(GPIO_PIN_ENABLE, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+        furi_hal_gpio_init(
+            mag_state_enum_to_pin(state->pin_input), GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+        furi_hal_gpio_init(
+            mag_state_enum_to_pin(state->pin_output), GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+        furi_hal_gpio_init(
+            mag_state_enum_to_pin(state->pin_enable), GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 
         //gpio_item_configure_all_pins(GpioModeAnalog);
         break;
@@ -262,7 +271,7 @@ bool tx_deinit(MagSetting* setting) {
 }
 
 void mag_spoof(Mag* mag) {
-    MagSetting* setting = mag->setting;
+    MagState* state = &mag->state;
 
     // TODO: cleanup this section. Possibly move precompute + tx_init to emulate_on_enter?
     FuriString* ft1 = mag->mag_dev->dev_data.track[0].str;
@@ -320,47 +329,47 @@ void mag_spoof(Mag* mag) {
     last_value = 2;
     bool bit = false;
 
-    if(!tx_init(setting)) return;
+    if(!tx_init(state)) return;
 
     FURI_CRITICAL_ENTER();
     for(uint16_t i = 0; i < (ZERO_PREFIX * 2); i++) {
         // is this right?
         if(!!(i % 2)) bit ^= 1;
-        play_halfbit(bit, setting);
-        furi_delay_us(setting->us_clock);
+        play_halfbit(bit, state);
+        furi_delay_us(state->us_clock);
     }
 
-    if((setting->track == MagTrackStateOneAndTwo) || (setting->track == MagTrackStateOne))
-        play_track((uint8_t*)bits_t1_manchester, bits_t1_count, setting, false);
+    if((state->track == MagTrackStateOneAndTwo) || (state->track == MagTrackStateOne))
+        play_track((uint8_t*)bits_t1_manchester, bits_t1_count, state, false);
 
-    if((setting->track == MagTrackStateOneAndTwo))
+    if((state->track == MagTrackStateOneAndTwo))
         for(uint16_t i = 0; i < (ZERO_BETWEEN * 2); i++) {
             if(!!(i % 2)) bit ^= 1;
-            play_halfbit(bit, setting);
-            furi_delay_us(setting->us_clock);
+            play_halfbit(bit, state);
+            furi_delay_us(state->us_clock);
         }
 
-    if((setting->track == MagTrackStateOneAndTwo) || (setting->track == MagTrackStateTwo))
+    if((state->track == MagTrackStateOneAndTwo) || (state->track == MagTrackStateTwo))
         play_track(
             (uint8_t*)bits_t2_manchester,
             bits_t2_count,
-            setting,
-            (setting->reverse == MagReverseStateOn));
+            state,
+            (state->reverse == MagReverseStateOn));
 
-    if((setting->track == MagTrackStateThree))
-        play_track((uint8_t*)bits_t3_manchester, bits_t3_count, setting, false);
+    if((state->track == MagTrackStateThree))
+        play_track((uint8_t*)bits_t3_manchester, bits_t3_count, state, false);
 
     for(uint16_t i = 0; i < (ZERO_SUFFIX * 2); i++) {
         if(!!(i % 2)) bit ^= 1;
-        play_halfbit(bit, setting);
-        furi_delay_us(setting->us_clock);
+        play_halfbit(bit, state);
+        furi_delay_us(state->us_clock);
     }
     FURI_CRITICAL_EXIT();
 
     free(data1);
     free(data2);
     free(data3);
-    tx_deinit(setting);
+    tx_deinit(state);
 }
 
 uint16_t add_bit(bool value, uint8_t* out, uint16_t count) {

+ 4 - 4
helpers/mag_helpers.h

@@ -2,16 +2,16 @@
 #include <stdio.h>
 #include <string.h>
 
-void play_halfbit(bool value, MagSetting* setting);
-void play_track(uint8_t* bits_manchester, uint16_t n_bits, MagSetting* setting, bool reverse);
+void play_halfbit(bool value, MagState* state);
+void play_track(uint8_t* bits_manchester, uint16_t n_bits, MagState* state, bool reverse);
 
 void tx_init_rf(int hz);
 void tx_init_rfid();
 void tx_init_piezo();
-bool tx_init(MagSetting* setting);
+bool tx_init(MagState* state);
 void tx_deinit_piezo();
 void tx_deinit_rfid();
-bool tx_deinit(MagSetting* setting);
+bool tx_deinit(MagState* state);
 
 uint16_t add_bit(bool value, uint8_t* out, uint16_t count);
 uint16_t add_bit_manchester(bool value, uint8_t* out, uint16_t count);

+ 3 - 30
mag.c

@@ -2,12 +2,6 @@
 
 #define TAG "Mag"
 
-#define SETTING_DEFAULT_REVERSE MagReverseStateOff
-#define SETTING_DEFAULT_TRACK MagTrackStateOneAndTwo
-#define SETTING_DEFAULT_TX MagTxStateGPIO
-#define SETTING_DEFAULT_US_CLOCK 240
-#define SETTING_DEFAULT_US_INTERPACKET 10
-
 static bool mag_debug_custom_event_callback(void* context, uint32_t event) {
     furi_assert(context);
     Mag* mag = context;
@@ -20,19 +14,6 @@ static bool mag_debug_back_event_callback(void* context) {
     return scene_manager_handle_back_event(mag->scene_manager);
 }
 
-static MagSetting* mag_setting_alloc() {
-    // temp hardcoded defaults
-    MagSetting* setting = malloc(sizeof(MagSetting));
-    setting->reverse = SETTING_DEFAULT_REVERSE;
-    setting->track = SETTING_DEFAULT_TRACK;
-    setting->tx = SETTING_DEFAULT_TX;
-    setting->us_clock = SETTING_DEFAULT_US_CLOCK;
-    setting->us_interpacket = SETTING_DEFAULT_US_INTERPACKET;
-    setting->is_debug = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug);
-
-    return setting;
-}
-
 static Mag* mag_alloc() {
     Mag* mag = malloc(sizeof(Mag));
 
@@ -53,7 +34,7 @@ static Mag* mag_alloc() {
         mag->view_dispatcher, mag_debug_back_event_callback);
 
     mag->mag_dev = mag_device_alloc();
-    mag->setting = mag_setting_alloc();
+    mag_state_load(&mag->state);
 
     // Open GUI record
     mag->gui = furi_record_open(RECORD_GUI);
@@ -98,13 +79,9 @@ static Mag* mag_alloc() {
     mag->expansion = furi_record_open(RECORD_EXPANSION);
     expansion_disable(mag->expansion);
 
-    return mag;
-}
-
-static void mag_setting_free(MagSetting* setting) {
-    furi_assert(setting);
+    // Move UART here? conditional upon setting?
 
-    free(setting);
+    return mag;
 }
 
 static void mag_free(Mag* mag) {
@@ -118,10 +95,6 @@ static void mag_free(Mag* mag) {
     mag_device_free(mag->mag_dev);
     mag->mag_dev = NULL;
 
-    // Mag setting
-    mag_setting_free(mag->setting);
-    mag->setting = NULL;
-
     // Submenu
     view_dispatcher_remove_view(mag->view_dispatcher, MagViewSubmenu);
     submenu_free(mag->submenu);

+ 2 - 10
mag_i.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "mag_device.h"
+#include "mag_state.h"
 //#include "helpers/mag_helpers.h"
 #include "helpers/mag_types.h"
 
@@ -53,15 +54,6 @@ enum MagCustomEvent {
     MagEventPopupClosed,
 };
 
-typedef struct {
-    MagTxState tx;
-    MagTrackState track;
-    MagReverseState reverse;
-    uint32_t us_clock;
-    uint32_t us_interpacket;
-    bool is_debug;
-} MagSetting;
-
 typedef struct {
     ViewDispatcher* view_dispatcher;
     Gui* gui;
@@ -76,7 +68,7 @@ typedef struct {
     FuriString* file_name;
     FuriString* args;
 
-    MagSetting* setting;
+    MagState state;
 
     // Common views
     Submenu* submenu;

+ 109 - 0
mag_state.c

@@ -0,0 +1,109 @@
+#include "mag_state.h"
+
+#define TAG "MagState"
+
+const GpioPin* mag_state_enum_to_pin(MagPin pin) {
+    switch(pin) {
+    case MagPinA7:
+        return &gpio_ext_pa7;
+    case MagPinA6:
+        return &gpio_ext_pa6;
+    case MagPinA4:
+        return &gpio_ext_pa4;
+    case MagPinB3:
+        return &gpio_ext_pb3;
+    case MagPinB2:
+        return &gpio_ext_pb2;
+    case MagPinC3:
+        return &gpio_ext_pc3;
+    case MagPinC1:
+        return &gpio_ext_pc1;
+    case MagPinC0:
+        return &gpio_ext_pc0;
+    default:
+        return NULL;
+    }
+}
+
+bool mag_state_gpio_is_valid(MagState* state) {
+    return (state->pin_input != state->pin_output) && (state->pin_input != state->pin_enable) &&
+           (state->pin_enable != state->pin_output);
+}
+
+void mag_state_gpio_reset(MagState* state) {
+    state->pin_input = MAG_STATE_DEFAULT_PIN_INPUT;
+    state->pin_output = MAG_STATE_DEFAULT_PIN_OUTPUT;
+    state->pin_enable = MAG_STATE_DEFAULT_PIN_ENABLE;
+}
+
+bool mag_state_load(MagState* out_state) {
+    MagState state;
+
+    // Try to load from file
+    bool loaded_from_file = false;
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    if(storage_file_exists(storage, MAG_STATE_PATH)) {
+        FlipperFormat* file = flipper_format_file_alloc(storage);
+        do {
+            uint32_t tmp;
+            FuriString* str = furi_string_alloc();
+            if(!flipper_format_file_open_existing(file, MAG_STATE_PATH)) break;
+            if(!flipper_format_read_header(file, str, &tmp)) break;
+            if(furi_string_cmp_str(str, MAG_STATE_HEADER)) break;
+            if(tmp != MAG_STATE_VER) break;
+
+            if(!flipper_format_read_uint32(file, "pin_input", &tmp, 1)) break;
+            state.pin_input = tmp;
+            if(!flipper_format_read_uint32(file, "pin_output", &tmp, 1)) break;
+            state.pin_output = tmp;
+            if(!flipper_format_read_uint32(file, "pin_enable", &tmp, 1)) break;
+            state.pin_enable = tmp;
+
+            loaded_from_file = true;
+        } while(0);
+        flipper_format_free(file);
+    }
+    furi_record_close(RECORD_STORAGE);
+
+    // If could not be read from file
+    // Or file GPIO config is invalid (pins overlap)
+    // Set defaults
+    if(!loaded_from_file || !mag_state_gpio_is_valid(&state)) {
+        mag_state_gpio_reset(&state);
+    }
+
+    // set defaults we don't save
+    state.tx = MAG_STATE_DEFAULT_TX;
+    state.track = MAG_STATE_DEFAULT_TRACK;
+    state.reverse = MAG_STATE_DEFAULT_REVERSE;
+    state.us_clock = MAG_STATE_DEFAULT_US_CLOCK;
+    state.us_interpacket = MAG_STATE_DEFAULT_US_INTERPACKET;
+    state.is_debug = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug);
+
+    // Copy to caller state before popping stack
+    memcpy(out_state, &state, sizeof(state));
+
+    return loaded_from_file;
+}
+
+void mag_state_save(MagState* state) {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    storage_simply_mkdir(storage, MAG_STATE_DIR);
+    FlipperFormat* file = flipper_format_file_alloc(storage);
+
+    do {
+        uint32_t tmp;
+        if(!flipper_format_file_open_always(file, MAG_STATE_PATH)) break;
+        if(!flipper_format_write_header_cstr(file, MAG_STATE_HEADER, MAG_STATE_VER)) break;
+
+        tmp = state->pin_input;
+        if(!flipper_format_write_uint32(file, "pin_input", &tmp, 1)) break;
+        tmp = state->pin_output;
+        if(!flipper_format_write_uint32(file, "pin_output", &tmp, 1)) break;
+        tmp = state->pin_enable;
+        if(!flipper_format_write_uint32(file, "pin_enable", &tmp, 1)) break;
+
+    } while(0);
+    flipper_format_free(file);
+    furi_record_close(RECORD_STORAGE);
+}

+ 61 - 0
mag_state.h

@@ -0,0 +1,61 @@
+#pragma once
+
+#include <string.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <furi_hal_resources.h>
+#include <furi_hal_rtc.h>
+
+#include <storage/storage.h>
+#include <flipper_format/flipper_format.h>
+
+#include "helpers/mag_types.h"
+
+#define MAG_STATE_HEADER "Mag State"
+#define MAG_STATE_VER 1
+#define MAG_STATE_DIR STORAGE_APP_DATA_PATH_PREFIX
+#define MAG_STATE_PATH MAG_STATE_DIR "/mag_state.txt"
+
+typedef enum {
+    MagPinA7,
+    MagPinA6,
+    MagPinA4,
+    MagPinB3,
+    MagPinB2,
+    MagPinC3,
+    MagPinC1,
+    MagPinC0,
+} MagPin;
+
+#define MAG_STATE_DEFAULT_REVERSE MagReverseStateOff
+#define MAG_STATE_DEFAULT_TRACK MagTrackStateOneAndTwo
+#define MAG_STATE_DEFAULT_TX MagTxStateGPIO
+#define MAG_STATE_DEFAULT_US_CLOCK 240
+#define MAG_STATE_DEFAULT_US_INTERPACKET 10
+#define MAG_STATE_DEFAULT_PIN_INPUT MagPinA7
+#define MAG_STATE_DEFAULT_PIN_OUTPUT MagPinA6
+#define MAG_STATE_DEFAULT_PIN_ENABLE MagPinA4
+
+typedef struct {
+    MagTxState tx;
+    MagTrackState track;
+    MagReverseState reverse;
+    uint32_t us_clock;
+    uint32_t us_interpacket;
+    MagPin pin_input;
+    MagPin pin_output;
+    MagPin pin_enable;
+    bool is_debug;
+} MagState;
+
+const GpioPin* mag_state_enum_to_pin(MagPin pin);
+
+bool mag_state_gpio_is_valid(MagState* state);
+
+void mag_state_gpio_reset(MagState* state);
+
+bool mag_state_load(MagState* out_state);
+
+void mag_state_save(MagState* state);

+ 3 - 2
scenes/mag_scene_config.h

@@ -1,5 +1,7 @@
 ADD_SCENE(mag, start, Start)
 ADD_SCENE(mag, about, About)
+ADD_SCENE(mag, read, Read)
+ADD_SCENE(mag, settings, Settings)
 ADD_SCENE(mag, emulate, Emulate)
 ADD_SCENE(mag, emulate_config, EmulateConfig)
 ADD_SCENE(mag, file_select, FileSelect)
@@ -11,5 +13,4 @@ ADD_SCENE(mag, save_success, SaveSuccess)
 ADD_SCENE(mag, delete_success, DeleteSuccess)
 ADD_SCENE(mag, delete_confirm, DeleteConfirm)
 ADD_SCENE(mag, exit_confirm, ExitConfirm)
-ADD_SCENE(mag, under_construction, UnderConstruction)
-ADD_SCENE(mag, read, Read)
+ADD_SCENE(mag, under_construction, UnderConstruction)

+ 2 - 2
scenes/mag_scene_emulate.c

@@ -30,7 +30,7 @@ void mag_scene_emulate_on_enter(void* context) {
         widget, 13, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp_str));
     furi_string_reset(tmp_str);
 
-    FURI_LOG_D(TAG, "%d", mag->setting->reverse);
+    FURI_LOG_D(TAG, "%d", mag->state.reverse);
 
     // print relevant data
     uint8_t cat_count = 0;
@@ -39,7 +39,7 @@ void mag_scene_emulate_on_enter(void* context) {
 
         // still messy / dumb way to do this, but slightly cleaner than before.
         // will clean up more later
-        switch(mag->setting->track) {
+        switch(mag->state.track) {
         case MagTrackStateOne:
             if(i == 0) cat_trackstr(tmp_str, cat_count++, i, trackstr);
             break;

+ 20 - 20
scenes/mag_scene_emulate_config.c

@@ -2,12 +2,12 @@
 
 #define TAG "MagSceneEmulateConfig"
 
-enum MagSettingIndex {
-    MagSettingIndexTx,
-    MagSettingIndexTrack,
-    MagSettingIndexReverse,
-    MagSettingIndexClock,
-    MagSettingIndexInterpacket,
+enum MagEmulateConfigIndex {
+    MagEmulateConfigIndexTx,
+    MagEmulateConfigIndexTrack,
+    MagEmulateConfigIndexReverse,
+    MagEmulateConfigIndexClock,
+    MagEmulateConfigIndexInterpacket,
 };
 
 #define TX_COUNT 7
@@ -128,17 +128,17 @@ static void mag_scene_emulate_config_set_tx(VariableItem* item) {
 
     variable_item_set_current_value_text(item, tx_text[index]);
 
-    mag->setting->tx = tx_value[index];
+    mag->state.tx = tx_value[index];
 };
 
 static void mag_scene_emulate_config_set_track(VariableItem* item) {
     Mag* mag = variable_item_get_context(item);
     uint8_t index = variable_item_get_current_value_index(item);
 
-    if(mag->setting->reverse == MagReverseStateOff) {
+    if(mag->state.reverse == MagReverseStateOff) {
         variable_item_set_current_value_text(item, track_text[index]);
-        mag->setting->track = track_value[index];
-    } else if(mag->setting->reverse == MagReverseStateOn) {
+        mag->state.track = track_value[index];
+    } else if(mag->state.reverse == MagReverseStateOn) {
         variable_item_set_current_value_index(
             item, value_index_uint32(MagTrackStateOneAndTwo, track_value, TRACK_COUNT));
     }
@@ -151,10 +151,10 @@ static void mag_scene_emulate_config_set_reverse(VariableItem* item) {
     Mag* mag = variable_item_get_context(item);
     uint8_t index = variable_item_get_current_value_index(item);
 
-    if(mag->setting->track == MagTrackStateOneAndTwo) {
+    if(mag->state.track == MagTrackStateOneAndTwo) {
         // only allow reverse track to be set when playing both 1 and 2
         variable_item_set_current_value_text(item, reverse_text[index]);
-        mag->setting->reverse = reverse_value[index];
+        mag->state.reverse = reverse_value[index];
         //FURI_LOG_D(TAG, "%s", reverse_text[index]);
         //FURI_LOG_D(TAG, "%d", mag->setting->reverse);
     } else {
@@ -169,7 +169,7 @@ static void mag_scene_emulate_config_set_clock(VariableItem* item) {
 
     variable_item_set_current_value_text(item, clock_text[index]);
 
-    mag->setting->us_clock = clock_value[index];
+    mag->state.us_clock = clock_value[index];
 };
 
 static void mag_scene_emulate_config_set_interpacket(VariableItem* item) {
@@ -178,7 +178,7 @@ static void mag_scene_emulate_config_set_interpacket(VariableItem* item) {
 
     variable_item_set_current_value_text(item, interpacket_text[index]);
 
-    mag->setting->us_interpacket = interpacket_value[index];
+    mag->state.us_interpacket = interpacket_value[index];
 };
 
 void mag_scene_emulate_config_on_enter(void* context) {
@@ -191,7 +191,7 @@ void mag_scene_emulate_config_on_enter(void* context) {
     // Clock
     item = variable_item_list_add(
         mag->variable_item_list, "Clock:", CLOCK_COUNT, mag_scene_emulate_config_set_clock, mag);
-    value_index = value_index_uint32(mag->setting->us_clock, clock_value, CLOCK_COUNT);
+    value_index = value_index_uint32(mag->state.us_clock, clock_value, CLOCK_COUNT);
     scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
     variable_item_set_current_value_index(item, value_index);
     variable_item_set_current_value_text(item, clock_text[value_index]);
@@ -199,7 +199,7 @@ void mag_scene_emulate_config_on_enter(void* context) {
     // Track
     item = variable_item_list_add(
         mag->variable_item_list, "Track:", TRACK_COUNT, mag_scene_emulate_config_set_track, mag);
-    value_index = value_index_uint32(mag->setting->track, track_value, TRACK_COUNT);
+    value_index = value_index_uint32(mag->state.track, track_value, TRACK_COUNT);
     scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
     variable_item_set_current_value_index(item, value_index);
     variable_item_set_current_value_text(item, track_text[value_index]);
@@ -212,25 +212,25 @@ void mag_scene_emulate_config_on_enter(void* context) {
         REVERSE_COUNT,
         mag_scene_emulate_config_set_reverse,
         mag);
-    value_index = value_index_uint32(mag->setting->reverse, reverse_value, REVERSE_COUNT);
+    value_index = value_index_uint32(mag->state.reverse, reverse_value, REVERSE_COUNT);
     scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
     variable_item_set_current_value_index(item, value_index);
     variable_item_set_current_value_text(item, reverse_text[value_index]);
 
     // TX
 #ifdef FW_ORIGIN_Official
-    if(mag->setting->is_debug) {
+    if(mag->state.is_debug) {
 #endif
         item = variable_item_list_add(
             mag->variable_item_list, "TX via:", TX_COUNT, mag_scene_emulate_config_set_tx, mag);
-        value_index = value_index_uint32(mag->setting->tx, tx_value, TX_COUNT);
+        value_index = value_index_uint32(mag->state.tx, tx_value, TX_COUNT);
         scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
         variable_item_set_current_value_index(item, value_index);
         variable_item_set_current_value_text(item, tx_text[value_index]);
 #ifdef FW_ORIGIN_Official
     }
 #else
-    variable_item_set_locked(item, !mag->setting->is_debug, "Enable Debug!");
+    variable_item_set_locked(item, !mag->state.is_debug, "Enable Debug!");
 #endif
     // Interpacket
     /*

+ 4 - 4
scenes/mag_scene_saved_menu.c

@@ -24,13 +24,13 @@ void mag_scene_saved_menu_on_enter(void* context) {
     bool is_empty_t3 = furi_string_empty(mag->mag_dev->dev_data.track[2].str);
 
     if(!is_empty_t1 && !is_empty_t2) {
-        mag->setting->track = MagTrackStateOneAndTwo;
+        mag->state.track = MagTrackStateOneAndTwo;
     } else if(!is_empty_t1) {
-        mag->setting->track = MagTrackStateOne;
+        mag->state.track = MagTrackStateOne;
     } else if(!is_empty_t2) {
-        mag->setting->track = MagTrackStateTwo;
+        mag->state.track = MagTrackStateTwo;
     } else if(!is_empty_t3) {
-        mag->setting->track = MagTrackStateThree;
+        mag->state.track = MagTrackStateThree;
     } // TODO: what happens if no track data present?
 
     submenu_add_item(

+ 113 - 0
scenes/mag_scene_settings.c

@@ -0,0 +1,113 @@
+#include "../mag_i.h"
+#include "../mag_state.h"
+#include "../helpers/mag_helpers.h"
+
+#define TAG "MagSceneEmulateConfig"
+
+static const char* gpio[] = {
+    [MagPinA7] = "2 (A7)",
+    [MagPinA6] = "3 (A6)",
+    [MagPinA4] = "4 (A4)",
+    [MagPinB3] = "5 (B3)",
+    [MagPinB2] = "6 (B2)",
+    [MagPinC3] = "7 (C3)",
+    [MagPinC1] = "15 (C1)",
+    [MagPinC0] = "16 (C0)",
+};
+const uint8_t GPIO_COUNT = COUNT_OF(gpio);
+
+static void mag_scene_settings_set_gpio(VariableItem* item, MagPin* pin_out) {
+    MagPin pin = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, gpio[pin]);
+    *pin_out = pin;
+}
+
+static void mag_scene_settings_set_gpio_input(VariableItem* item) {
+    Mag* mag = variable_item_get_context(item);
+    mag_scene_settings_set_gpio(item, &mag->state.pin_input);
+};
+
+static void mag_scene_settings_set_gpio_output(VariableItem* item) {
+    Mag* mag = variable_item_get_context(item);
+    mag_scene_settings_set_gpio(item, &mag->state.pin_output);
+};
+
+static void mag_scene_settings_set_gpio_enable(VariableItem* item) {
+    Mag* mag = variable_item_get_context(item);
+    mag_scene_settings_set_gpio(item, &mag->state.pin_enable);
+};
+
+static void mag_pin_variable_item_list_add(
+    Mag* mag,
+    const char* label,
+    MagPin pin,
+    VariableItemChangeCallback change_callback) {
+    VariableItem* item =
+        variable_item_list_add(mag->variable_item_list, label, GPIO_COUNT, change_callback, mag);
+    variable_item_set_current_value_index(item, pin);
+    variable_item_set_current_value_text(item, gpio[pin]);
+}
+// static const char* uart_pins[] = {[DapUartTypeUSART1] = "13,14", [DapUartTypeLPUART1] = "15,16"};
+// static const char* uart_swap[] = {[DapUartTXRXNormal] = "No", [DapUartTXRXSwap] = "Yes"};
+
+void mag_scene_settings_on_enter(void* context) {
+    Mag* mag = context;
+
+    mag_pin_variable_item_list_add(
+        mag, "Input pin:", mag->state.pin_input, mag_scene_settings_set_gpio_input);
+    mag_pin_variable_item_list_add(
+        mag, "Output pin:", mag->state.pin_output, mag_scene_settings_set_gpio_output);
+    mag_pin_variable_item_list_add(
+        mag, "Enable pin:", mag->state.pin_enable, mag_scene_settings_set_gpio_enable);
+
+    view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewVariableItemList);
+}
+
+void mag_scene_invalid_dialog(Mag* mag) {
+    SceneManager* scene_manager = mag->scene_manager;
+
+    DialogMessage* message = dialog_message_alloc();
+
+    dialog_message_set_header(message, "Invalid Pin Configuration!", 64, 0, AlignCenter, AlignTop);
+    dialog_message_set_buttons(message, "Modify", NULL, "Reset");
+    dialog_message_set_text(
+        message,
+        "Pins cannot overlap\nChange, or reset to defaults.",
+        64,
+        32,
+        AlignCenter,
+        AlignCenter);
+    DialogMessageButton res = dialog_message_show(furi_record_open(RECORD_DIALOGS), message);
+    dialog_message_free(message);
+    furi_record_close(RECORD_DIALOGS);
+    if(res == DialogMessageButtonRight) {
+        mag_state_gpio_reset(&mag->state);
+        scene_manager_previous_scene(scene_manager);
+    }
+}
+
+bool mag_scene_settings_on_event(void* context, SceneManagerEvent event) {
+    Mag* mag = context;
+    SceneManager* scene_manager = mag->scene_manager;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeBack) {
+        consumed = true;
+
+        if(!mag_state_gpio_is_valid(&mag->state)) {
+            mag_scene_invalid_dialog(mag);
+        } else {
+            scene_manager_previous_scene(scene_manager);
+        }
+    }
+
+    return consumed;
+}
+
+void mag_scene_settings_on_exit(void* context) {
+    Mag* mag = context;
+
+    variable_item_list_reset(mag->variable_item_list);
+
+    mag_state_save(&mag->state);
+}

+ 9 - 1
scenes/mag_scene_start.c

@@ -4,6 +4,7 @@ typedef enum {
     SubmenuIndexSaved,
     SubmenuIndexRead,
     //SubmenuIndexAddManually,
+    SubmenuIndexSettings,
     SubmenuIndexAbout,
 } SubmenuIndex;
 
@@ -24,10 +25,13 @@ void mag_scene_start_on_enter(void* context) {
         SubmenuIndexRead,
         mag_scene_start_submenu_callback,
         mag,
-        !mag->setting->is_debug,
+        !mag->state.is_debug,
         "Enable Debug!");
     //submenu_add_item(
     //    submenu, "Add Manually", SubmenuIndexAddManually, mag_scene_start_submenu_callback, mag);
+    submenu_add_item(
+        submenu, "Settings", SubmenuIndexSettings, mag_scene_start_submenu_callback, mag);
+
     submenu_add_item(submenu, "About", SubmenuIndexAbout, mag_scene_start_submenu_callback, mag);
 
     submenu_set_selected_item(
@@ -59,6 +63,10 @@ bool mag_scene_start_on_event(void* context, SceneManagerEvent event) {
         //    scene_manager_next_scene(mag->scene_manager, MagSceneInputValue);
         //    consumed = true;
         //    break;
+        case SubmenuIndexSettings:
+            scene_manager_next_scene(mag->scene_manager, MagSceneSettings);
+            consumed = true;
+            break;
         case SubmenuIndexAbout:
             scene_manager_next_scene(mag->scene_manager, MagSceneAbout);
             consumed = true;