Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 2 of 2

Thread: "Smallest Code" optimization breaks EEPROM writes

  1. #1
    Senior Member
    Join Date
    Apr 2020
    Location
    DFW area in Texas
    Posts
    193

    "Smallest Code" optimization breaks EEPROM writes

    I am working on the next version of my TeensyMIDIPolySynth . . . this one is controlled via TFT sliders & buttons instead of hardware pots, pushbuttons, & LEDs as was used in the original project. I was satisfied with what I had in the initial implementation of the project, but is was impossible to add any more capabilities as it had no place to put any additional pots and/or pushbuttons. So now, by using the TFT, this advanced version of the project is infinitely more versatile & much easier to add/modify/move capabilities. For more capability, I just add more menus (within reason) !!

    I have now completed the implementation of all of the previous functionality using the new TFT control interface (using the very nice ILI9341_t3n library: thanks @KurtE !!). Everything is working great. Now that all of the controls are electronic instead of physical, the time has come to implement configuration storage & retrieval to/from EEPROM. This is something that was sorely lacking in the original project (how do you force a physical pot to a particular value upon startup ??). In this new version, everything (EEPROM-related) works great until "Smallest Code" is selected for the compiler optimization, in which case EEPROM writes seem to hang and/or kill SPI.

    Symptoms: I can no longer make any control changes on the TFT & the on-board LED (PIN 13 is used for SPI SCLK) extinguishes. Likewise, after a restart, the values in the EEPROM are not correct/complete.

    Environment: Windows 10 Pro/64-bit, Arduino 1.8.13 (fresh install) & TD 1.54b6 (fresh install), Teensy4.0, PJRC ILI9341 touchscreen (CS=14, DC=9) using the ILI9341_t3n w/ framebuffer active/used.

    Code: included below is a scaled-down version of my latest TeensyMIDIPolySynth which includes just enough code to see the problem in action

    Arduino Tools settings: Teensy4.0, USB Type: Serial, CPU Speed: 600MHz, Optimize: (see steps below),

    Steps to reproduce the problem:
    0) perform a 15-second Blink restore on the Teensy4.0 (to make sure it is factory fresh)
    1) set Arduino compiler optimization to "Fastest"
    2) build "Teensy-TFT-EEPROM-conflict.ino"
    3) install (using PROGRAM button) on a fresh Teensy4.0 & run
    4) in Serial Monitor, note that invalid EEPROM contents were detected (as you would expect, since this sketch has not yet saved any settings), so new settings are successfully written to EEPROM

    Code:
    Attempting to read saved settings from EEPROM
    (READ 000: 255)
    (READ 001: 255)
    (READ 002: 255)
    (READ 003: 255)
    (READ 004: 255)
    (READ 005: 255)
    (READ 006: 255)
    (READ 007: 255)
    (READ 008: 255)
    (READ 009: 255)
    (READ 010: 255)
    (READ 011: 255)
    (READ 012: 255)
    (READ 013: 255)
    (READ 014: 255)
    Invalid settings found in EEPROM
    
    (READ 013) xor_value          = 255
    (CALC) xor_result             = 178
    (READ 014) inv_xor_value      = 255
    (CALC) inv_xor_result         = 77
    
    Saving settings to EEPROM
    
    (WRITE 000: 084) Header[0]                              = T
    (WRITE 001: 049) Header[1]                              = 1
    (WRITE 002: 050) Header[2]                              = 2
    (WRITE 003: 080) Header[3]                              = P
    (WRITE 004: 083) Header[4]                              = S
    (WRITE 005: 255) Main Volume Slider                     = 1.00
    (WRITE 006: 191) Main Level A Slider                    = 0.75
    (WRITE 007: 191) Main Level B Slider                    = 0.75
    (WRITE 008: 191) Main Level C Slider                    = 0.75
    (WRITE 009: 127) Main Tuning Slider                     = 0.00
    (WRITE 010: 127) Main Tuning A Slider                   = 0.00
    (WRITE 011: 127) Main Tuning B Slider                   = 0.00
    (WRITE 012: 127) Main Tuning C Slider                   = 0.00
    (WRITE 013: 089) Calculated CHECKSUM                    = 089
    (WRITE 014: 166) Calculated INVERSE CHECKSUM            = 166
    5) perform a 15-second Blink restore on the Teensy4.0 (to make sure it is factory fresh)
    6) set Arduino compiler optimization to "Smallest Code"
    7) build "Teensy-TFT-EEPROM-conflict.ino"
    8) install (using PROGRAM button) on a fresh Teensy4.0 & run
    9) in Serial Monitor, little to nothing shows up (& on-board LED, which is SPI SCLK, is extinguished)
    10) Teensy4.0 appears to be hung

    Under the "Smallest Code" compiler optimization, once the code has been loaded, each time the Teensy4.0 is unplugged/plugged, it will attempt to write fresh setting values to the EEPROM. After 14 unplug/plug cycles, the serial monitor will show that the settings were successfully written to EEPROM (as if one EEPROM location were successfully written with each of the power-on cycles - first location on the first run right after programming, & the remaining 14 w/ each of the unplug/plug cycles). Likewise, after it successfully continues to run, if you change only one slider, it will take 2 unplug/plug cycles to remain running - one EEPROM write seems to succeed when the slider change was detected, & the CHECKSUM & INVERSE CHECKSUM EEPROM writes seem to succeed in the additional 2 unplug/plug cycles).

    Also, under any compiler optimization other than "Smallest Code", changes can be made to the sliders on the TFT touchscreen & 5-seconds after the last change, new settings values will be written to the EEPROM successfully. Unfortunately, because of the amount of code & variables in my latest TeensyMIDIPolySynth, I have had to use the "Smallest Code" optimization as that is the only way my sketch variables fit in available RAM.

    Please let me know if you are able to reproduce this failure to write to EEPROM under the "Smallest Code" compiler optimization, or if you see something silly either that I've done, or that I've forgotten to do !!

    Thanks,

    Mark J Culross
    KD5RXT

    CODE:

    Code:
    //
    //  Scaled down version of Teensy MIDI (12-note / 3-voice) TFT-Controlled Polyphonic Synthesizer - version 1.7 dated 20210205-1835
    //
    //    - written by Mark J Culross (KD5RXT)
    //
    //    - can read MIDI data & controls/commands from any of three sources:
    //         1) traditional (serial) MIDI thru the MIDI DIN plugs/cables
    //         1) USB MIDI (play MIDI files from PC thru this MIDI device)
    //         3) connect a USB MIDI device (keyboard) the the USB Host of this device
    //
    //    - plays the indicated note(s) in three voices, with the specified audio characteristics
    //
    //    - appears as MIDI device "Teensy-MIDI-12-PolySynth"
    //
    //  See documentation on MIDI callbacks here:
    //     https://github.com/FortySevenEffects/arduino_midi_library/wiki/Using-Callbacks
    //
    //
    //  Arduino IDE Configuration:
    //     Tools/Board:           "Teensy 4.0"
    //     Tools/USB Type:        "Serial"
    //     Tools/CPU Speed:       "600MHz"
    //     Tools/Optimize:        "Smallest Code"
    //     Tools/Keyboard Layout: "US English"
    //     Tools/Port:            "COMx Serial (Teensy 4.0)"
    //
    
    String VERSION1 = "Teensy-MIDI-TFT-PolySynth";
    String VERSION2 = "version 1.7 dated 02/05/2021 @1835";
    String VERSION3 = "designed & written by Mark J Culross (KD5RXT)";
    
    #define DEBUG_EEPROM_READ               // uncomment to show specifics of EEPROM reads
    #define DEBUG_EEPROM_WRITE              // uncomment to show specifics of EEPROM writes
    //#define DISABLE_EEPROM_READ_SETTINGS    // uncomment to prevent reading settings from EEPROM
    //#define DISABLE_EEPROM_WRITE_SETTINGS   // uncomment to prevent writing settings to EEPROM
    
    #include <ILI9341_t3n.h>
    #include <XPT2046_Touchscreen.h>
    #include <EEPROM.h>
    
    
    // This is calibration data for mapping the raw touch data to the screen coordinates (for my particular PJRC ILI9341 TFT display)
    const int TS_MINX = 200;
    const int TS_MINY = 250;
    const int TS_MAXX = 3750;
    const int TS_MAXY = 3900;
    
    // when used w/ Audio Adapter, must use an alternate CS pin for the display
    const int TFT_CHIP_SELECT = 14;
    const int TFT_DATA_COMMAND = 9;
    ILI9341_t3n tft = ILI9341_t3n(TFT_CHIP_SELECT, TFT_DATA_COMMAND);
    
    // create TFT framebuffer
    DMAMEM uint16_t framebuf[320 * 240];
    
    // when used w/ Audio Adapter, must use an alternate CS pin for the touchscreen
    #define TS_CS_PIN  5
    
    XPT2046_Touchscreen ts(TS_CS_PIN, 255);
    
    //
    // The following pins are used in this project:
    //
    // PIN  D1  = (not used)
    // PIN  D2  = (not used)
    // PIN  D3  = (not used)
    // PIN  D4  = (not used)
    // PIN  D5  = TouchScreen chip select (alternate when used w/ audio adapter)
    // PIN  D6  = Audio adapter memory chip select
    // PIN  D7  = Audio adapter data in
    // PIN  D8  = Audio adapter data out
    // PIN  D9  = TFT/TS data/command select
    // PIN D10  = Audio adapter SD card chip select
    // PIN D11  = SPI MOSI (data in)
    // PIN D12  = SPI MISO (data out)
    // PIN D13  = SPI serial clock + on-board LED
    // PIN  A0  = (D14) TFT chip select (alternate when used w/ audio adapter)
    // PIN  A1  = Volume pot on audio adapter
    // PIN  A2  = (not used)
    // PIN  A3  = (not used)
    // PIN  A4  = (D18) Audio adapter SDA (I2C control data)
    // PIN  A5  = (D19) Audio adapter SCL (I2C control clock)
    
    // onboard LED on pin 13
    #define LED_PIN 13
    
    // keep track of how often to repaint the display
    #define SCREEN_UPDATE_MILLIS 125
    unsigned long screen_update_time = millis();
    
    // keep track of when the screen needs to be updated
    bool screen_update_required = false;
    
    
    // manage accumulating variable changes to reduce EEPROM writes
    #define SAVE_DELAY_MILLIS 5000
    unsigned long save_delay_millis = 0;
    bool save_needed = false;
    
    
    // custom ILI9341 TFT colors (5-bit RED, 6-bit GREEN, 5-bit BLUE)
    #define ILI9341_SHADOWGREY  0x8414      // SHADOWGREY: 0x8414 = 10000 100000 10100       LIGHTGREY: 0xC618 = 11000 110000 11000  
    
    typedef enum
    {
       CONFIG_MODE_MAIN=0,
    }  CONFIG_MODE;
    
    CONFIG_MODE config_mode = CONFIG_MODE_MAIN;
    
    boolean previously_touched = false;
    boolean touch_triggered = false;
    
    // global touchscreen coordinates (in pixels) where touched
    int BtnX, BtnY;
    int previousBtnX = -1, previousBtnY = -1;
    
    struct BUTTON_TYPE
    {
       unsigned int   xCenterLoc;
       unsigned int   yCenterLoc;
       unsigned int   xSize;
       unsigned int   ySize;
       String*        textPtr;
       uint16_t       textColor;
       uint16_t       buttonColor;
       uint16_t       borderColor;
       bool           activated;
    };
    
    
    // MAIN BUTTONS
    String mainVolumeButtonText        = "VOL";
    BUTTON_TYPE mainVolumeButton       = { 16,  88, 28, 15,      &mainVolumeButtonText, ILI9341_GREEN, ILI9341_BLACK, ILI9341_RED,  true };
    
    String mainLevelAButtonText        = "LvlA";
    BUTTON_TYPE mainLevelAButton       = { 53,  88, 28, 15,      &mainLevelAButtonText, ILI9341_GREEN, ILI9341_BLACK, ILI9341_RED,  true };
    
    String mainLevelBButtonText        = "LvlB";
    BUTTON_TYPE mainLevelBButton       = { 90,  88, 28, 15,      &mainLevelBButtonText, ILI9341_GREEN, ILI9341_BLACK, ILI9341_RED,  true };
    
    String mainLevelCButtonText        = "LvlC";
    BUTTON_TYPE mainLevelCButton       = {127,  88, 28, 15,      &mainLevelCButtonText, ILI9341_GREEN, ILI9341_BLACK, ILI9341_RED,  true };
    
    String mainTuningButtonText        = "TUNE";
    BUTTON_TYPE mainTuningButton       = {189,  88, 28, 15,      &mainTuningButtonText, ILI9341_GREEN, ILI9341_BLACK, ILI9341_RED,  true };
    
    String mainTuningAButtonText       = "TunA";
    BUTTON_TYPE mainTuningAButton      = {226,  88, 28, 15,     &mainTuningAButtonText, ILI9341_GREEN, ILI9341_BLACK, ILI9341_RED,  true };
    
    String mainTuningBButtonText       = "TunB";
    BUTTON_TYPE mainTuningBButton      = {263,  88, 28, 15,     &mainTuningBButtonText, ILI9341_GREEN, ILI9341_BLACK, ILI9341_RED,  true };
    
    String mainTuningCButtonText       = "TunC";
    BUTTON_TYPE mainTuningCButton      = {300,  88, 28, 15,     &mainTuningCButtonText, ILI9341_GREEN, ILI9341_BLACK, ILI9341_RED,  true };
    
    
    typedef enum
    {
       SLIDER_MODE_HORIZONTAL=0, SLIDER_MODE_VERTICAL
    }  SLIDER_MODE;
    
    struct SLIDER_TYPE
    {
       unsigned int   xCenterLoc;
       unsigned int   yCenterLoc;
       unsigned int   xSize;
       unsigned int   ySize;
       float          value;
       unsigned int   placesBeforeTheDecimal;
       unsigned int   placesAfterTheDecimal;
       bool           showPlusMinusSign;
       float          minValue;  // for HORIZONTAL = all the way to the left, for VERTICAL, all the way up
       float          maxValue;  // for HORIZONTAL = all the way to the right, for VERTICAL, all the way down
       unsigned int   xValueCenterLoc;
       unsigned int   yValueCenterLoc;
       uint16_t       valueColor;
       uint16_t       backgroundColor;
       uint16_t       borderColor;
       uint16_t       scaleColor;
       uint16_t       handleColor;
       uint16_t       handleBorderColor;
       uint16_t       backgroundColorDisabled;
       uint16_t       borderColorDisabled;
       uint16_t       scaleColorDisabled;
       uint16_t       handleColorDisabled;
       uint16_t       handleBorderColorDisabled;
       bool           activated;
       SLIDER_MODE    orientation;
    };
    
       SLIDER_TYPE  mainVolumeSlider      = {  16,  172,  15, 116,   1.00, 1, 1,  true,    0.0,      1.0,  16, 101, ILI9341_WHITE, ILI9341_SHADOWGREY, ILI9341_GREEN, ILI9341_BLACK,   ILI9341_GREEN, ILI9341_BLACK,   ILI9341_BLACK, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_BLACK, true, SLIDER_MODE_VERTICAL };
       SLIDER_TYPE  mainLevelASlider      = {  53,  172,  15, 116,   0.75, 1, 1,  true,    0.0,      1.0,  53, 101, ILI9341_WHITE, ILI9341_SHADOWGREY, ILI9341_GREEN, ILI9341_BLACK,   ILI9341_GREEN, ILI9341_BLACK,   ILI9341_BLACK, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_BLACK, true, SLIDER_MODE_VERTICAL };
       SLIDER_TYPE  mainLevelBSlider      = {  90,  172,  15, 116,   0.75, 1, 1,  true,    0.0,      1.0,  90, 101, ILI9341_WHITE, ILI9341_SHADOWGREY, ILI9341_GREEN, ILI9341_BLACK,   ILI9341_GREEN, ILI9341_BLACK,   ILI9341_BLACK, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_BLACK, true, SLIDER_MODE_VERTICAL };
       SLIDER_TYPE  mainLevelCSlider      = { 127,  172,  15, 116,   0.75, 1, 1,  true,    0.0,      1.0, 127, 101, ILI9341_WHITE, ILI9341_SHADOWGREY, ILI9341_GREEN, ILI9341_BLACK,   ILI9341_GREEN, ILI9341_BLACK,   ILI9341_BLACK, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_BLACK, true, SLIDER_MODE_VERTICAL };
       SLIDER_TYPE  mainTuningSlider      = { 189,  172,  15, 116,   0.00, 1, 1,  true,   -0.5,      0.5, 189, 101, ILI9341_WHITE, ILI9341_SHADOWGREY, ILI9341_GREEN, ILI9341_BLACK,   ILI9341_GREEN, ILI9341_BLACK,   ILI9341_BLACK, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_BLACK, true, SLIDER_MODE_VERTICAL };
       SLIDER_TYPE  mainTuningASlider     = { 226,  172,  15, 116,   0.00, 1, 1,  true,   -0.5,      0.5, 226, 101, ILI9341_WHITE, ILI9341_SHADOWGREY, ILI9341_GREEN, ILI9341_BLACK,   ILI9341_GREEN, ILI9341_BLACK,   ILI9341_BLACK, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_BLACK, true, SLIDER_MODE_VERTICAL };
       SLIDER_TYPE  mainTuningBSlider     = { 263,  172,  15, 116,   0.00, 1, 1,  true,   -0.5,      0.5, 263, 101, ILI9341_WHITE, ILI9341_SHADOWGREY, ILI9341_GREEN, ILI9341_BLACK,   ILI9341_GREEN, ILI9341_BLACK,   ILI9341_BLACK, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_BLACK, true, SLIDER_MODE_VERTICAL };
       SLIDER_TYPE  mainTuningCSlider     = { 300,  172,  15, 116,   0.00, 1, 1,  true,   -1.0,      1.0, 300, 101, ILI9341_WHITE, ILI9341_SHADOWGREY, ILI9341_GREEN, ILI9341_BLACK,   ILI9341_GREEN, ILI9341_BLACK,   ILI9341_BLACK, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_SHADOWGREY, ILI9341_BLACK, true, SLIDER_MODE_VERTICAL };
    
    
    const char EEPROM_HEADER[] = "T12PS";
    
    typedef enum
    {
       EEPROM_INDEX_HEADER_0=0, EEPROM_INDEX_HEADER_1, EEPROM_INDEX_HEADER_2, EEPROM_INDEX_HEADER_3, EEPROM_INDEX_HEADER_4,
       EEPROM_INDEX_MAIN_VOLUME, EEPROM_INDEX_MAIN_LEVEL_A, EEPROM_INDEX_MAIN_LEVEL_B, EEPROM_INDEX_MAIN_LEVEL_C,
       EEPROM_INDEX_MAIN_TUNING, EEPROM_INDEX_MAIN_TUNING_A, EEPROM_INDEX_MAIN_TUNING_B, EEPROM_INDEX_MAIN_TUNING_C,
       EEPROM_INDEX_CHECKSUM, EEPROM_INDEX_INV_CHECKSUM
    }  EEPROM_INDEX;
    
    
    // function headers
    void centerDrawText(String text, unsigned int xCenterLoc, unsigned int yCenterLoc, uint16_t textColor, uint16_t textBackground);
    bool checkButton(BUTTON_TYPE thisButton);
    bool checkSlider(SLIDER_TYPE* thisSlider);
    void drawButton(BUTTON_TYPE thisButton);
    void drawScreen(void);
    void drawSlider(SLIDER_TYPE thisSlider);
    void loop();
    void readSettings(void);
    void saveSettings(void);
    void setup();
    void showIndex(int index);
    
    
    
    
    // draw text, centered around xLoc & yLoc
    void centerDrawText(String text, unsigned int xCenterLoc, unsigned int yCenterLoc, uint16_t textColor, uint16_t textBackground)
    {
       unsigned int xOffset = (text.length() / 2) * 6;
       unsigned int yOffset = 3;
    
       if (text.length() % 2)
       {
          xOffset += 3;
       }
    
       tft.setTextColor(textColor, textBackground);
       tft.setTextSize(1);
    
       tft.setCursor(xCenterLoc - xOffset, yCenterLoc - yOffset);
       tft.print(text);
    }  // centerDrawText
    
    
    // check if a button was pressed
    bool checkButton(BUTTON_TYPE thisButton)
    {
       bool retVal = false;
    
       // if touched most recently in thisButton
       if ((BtnX >= (int)(thisButton.xCenterLoc - (thisButton.xSize / 2))) &&
           (BtnX <= (int)(thisButton.xCenterLoc + (thisButton.xSize / 2))) &&
           (BtnY >= (int)(thisButton.yCenterLoc - (thisButton.ySize / 2))) &&
           (BtnY <= (int)(thisButton.yCenterLoc + (thisButton.ySize / 2))))
       {
          retVal = true;
       }
    
       return(retVal);
    }  // checkButton()
    
    
    // check if a slider has changed
    bool checkSlider(SLIDER_TYPE* thisSlider)
    {
       bool retVal = false;
       float newValue = 0.0;
    
       // if thisSlider is active & touched most recently in thisSlider
       if ((thisSlider->activated) &&
           (BtnX >= (int)(thisSlider->xCenterLoc - (thisSlider->xSize / 2))) &&
           (BtnX <= (int)(thisSlider->xCenterLoc + (thisSlider->xSize / 2))) &&
           (BtnY >= (int)(thisSlider->yCenterLoc - (thisSlider->ySize / 2))) &&
           (BtnY <= (int)(thisSlider->yCenterLoc + (thisSlider->ySize / 2))))
       {
          if (thisSlider->orientation == SLIDER_MODE_HORIZONTAL)
          {
             newValue = (float)map((float)BtnX, (float)(thisSlider->xCenterLoc - (thisSlider->xSize / 2)), (float)(thisSlider->xCenterLoc + (thisSlider->xSize / 2)), thisSlider->maxValue, thisSlider->minValue);
          } else {
             newValue = (float)map((float)BtnY, (float)(thisSlider->yCenterLoc - (thisSlider->ySize / 2)), (float)(thisSlider->yCenterLoc + (thisSlider->ySize / 2)), thisSlider->maxValue, thisSlider->minValue);
          }
    
          if (newValue != thisSlider->value)
          {
             thisSlider->value = newValue;
    
             drawSlider(*thisSlider);
    
             retVal = true;
          }
       }
    
       return(retVal);
    }  // checkSlider()
    
    
    // draw a button
    void drawButton(BUTTON_TYPE thisButton)
    {
       if (thisButton.activated)
       {
          tft.fillRect(thisButton.xCenterLoc - (thisButton.xSize / 2) + 1, thisButton.yCenterLoc - (thisButton.ySize / 2) + 1, thisButton.xSize - 2, thisButton.ySize - 2, thisButton.buttonColor);
          tft.drawRect(thisButton.xCenterLoc - (thisButton.xSize / 2), thisButton.yCenterLoc - (thisButton.ySize / 2), thisButton.xSize, thisButton.ySize, thisButton.borderColor);
    
          centerDrawText(*(thisButton.textPtr), thisButton.xCenterLoc, thisButton.yCenterLoc, thisButton.textColor, thisButton.buttonColor);
       } else {
          tft.fillRect(thisButton.xCenterLoc - (thisButton.xSize / 2) + 1, thisButton.yCenterLoc - (thisButton.ySize / 2) + 1, thisButton.xSize - 2, thisButton.ySize - 2, thisButton.borderColor);
          tft.drawRect(thisButton.xCenterLoc - (thisButton.xSize / 2), thisButton.yCenterLoc - (thisButton.ySize / 2), thisButton.xSize, thisButton.ySize, thisButton.buttonColor);
    
          centerDrawText(*(thisButton.textPtr), thisButton.xCenterLoc, thisButton.yCenterLoc, thisButton.textColor, thisButton.borderColor);
       }
    }  // drawButton()
    
    
    // update the screen based upon the current mode
    void drawScreen(void)
    {
       // clear the unique portion of the display screen
       tft.fillRect(0, 80, 320, 160, ILI9341_BLACK);
    
       // update screen using the current config mode
       switch(config_mode)
       {
          case CONFIG_MODE_MAIN:
          {
             tft.setTextSize(1);
             tft.setTextColor(ILI9341_WHITE);
    
             drawSlider(mainVolumeSlider);
             drawButton(mainVolumeButton);
    
             drawSlider(mainTuningSlider);
             drawButton(mainTuningButton);
    
             drawSlider(mainLevelASlider);
             drawButton(mainLevelAButton);
    
             drawSlider(mainTuningASlider);
             drawButton(mainTuningAButton);
    
             drawSlider(mainLevelBSlider);
             drawButton(mainLevelBButton);
    
             drawSlider(mainTuningBSlider);
             drawButton(mainTuningBButton);
    
             drawSlider(mainLevelCSlider);
             drawButton(mainLevelCButton);
    
             drawSlider(mainTuningCSlider);
             drawButton(mainTuningCButton);
          }
          break;
       }
    }  // drawScreen()
    
    
    // draw a slider
    void drawSlider(SLIDER_TYPE thisSlider)
    {
       int characterCount = 0;
       String outString = "";
    
       if (thisSlider.orientation == SLIDER_MODE_HORIZONTAL)
       {
          if (thisSlider.activated)
          {
             // clear the entire slider
             tft.fillRect(thisSlider.xCenterLoc - (thisSlider.xSize / 2) - 7, thisSlider.yCenterLoc - (thisSlider.ySize / 2) - 2, thisSlider.xSize + 15, thisSlider.ySize + 4, thisSlider.backgroundColor);
    
             // draw the slider outline
             tft.drawRect(thisSlider.xCenterLoc - (thisSlider.xSize / 2) - 7, thisSlider.yCenterLoc - (thisSlider.ySize / 2) - 2, thisSlider.xSize + 15, thisSlider.ySize + 4, thisSlider.borderColor);
    
             // draw the slider handle
             tft.fillCircle((int)map(thisSlider.value, thisSlider.minValue, thisSlider.maxValue, thisSlider.xCenterLoc - (thisSlider.xSize / 2), thisSlider.xCenterLoc + (thisSlider.xSize / 2)), (int)thisSlider.yCenterLoc, 6, thisSlider.handleColor);
             tft.drawCircle((int)map(thisSlider.value, thisSlider.minValue, thisSlider.maxValue, thisSlider.xCenterLoc - (thisSlider.xSize / 2), thisSlider.xCenterLoc + (thisSlider.xSize / 2)), (int)thisSlider.yCenterLoc, 6, thisSlider.handleBorderColor);
             tft.drawCircle((int)map(thisSlider.value, thisSlider.minValue, thisSlider.maxValue, thisSlider.xCenterLoc - (thisSlider.xSize / 2), thisSlider.xCenterLoc + (thisSlider.xSize / 2)), (int)thisSlider.yCenterLoc, 7, thisSlider.handleBorderColor);
    
             // draw the slider lines
             tft.drawLine(thisSlider.xCenterLoc - (thisSlider.xSize / 2), thisSlider.yCenterLoc, thisSlider.xCenterLoc + (thisSlider.xSize / 2), thisSlider.yCenterLoc, thisSlider.scaleColor);
             tft.drawLine(thisSlider.xCenterLoc - (thisSlider.xSize / 2), thisSlider.yCenterLoc - 3, thisSlider.xCenterLoc - (thisSlider.xSize / 2), thisSlider.yCenterLoc + 3, thisSlider.scaleColor);  // left end
             tft.drawLine(thisSlider.xCenterLoc + (thisSlider.xSize / 2), thisSlider.yCenterLoc - 3, thisSlider.xCenterLoc + (thisSlider.xSize / 2), thisSlider.yCenterLoc + 3, thisSlider.scaleColor);  // right end
             tft.drawLine(thisSlider.xCenterLoc, thisSlider.yCenterLoc - 2, thisSlider.xCenterLoc, thisSlider.yCenterLoc + 2, thisSlider.scaleColor);  // center
          } else {
             // clear the entire slider
             tft.fillRect(thisSlider.xCenterLoc - (thisSlider.xSize / 2) - 7, thisSlider.yCenterLoc - (thisSlider.ySize / 2) - 2, thisSlider.xSize + 15, thisSlider.ySize + 4, thisSlider.backgroundColorDisabled);
    
             // draw the slider outline
             tft.drawRect(thisSlider.xCenterLoc - (thisSlider.xSize / 2) - 7, thisSlider.yCenterLoc - (thisSlider.ySize / 2) - 2, thisSlider.xSize + 15, thisSlider.ySize + 4, thisSlider.borderColorDisabled);
    
             // draw the slider handle
             tft.fillCircle((int)map(thisSlider.value, thisSlider.minValue, thisSlider.maxValue, thisSlider.xCenterLoc - (thisSlider.xSize / 2), thisSlider.xCenterLoc + (thisSlider.xSize / 2)), (int)thisSlider.yCenterLoc, 6, thisSlider.handleColorDisabled);
             tft.drawCircle((int)map(thisSlider.value, thisSlider.minValue, thisSlider.maxValue, thisSlider.xCenterLoc - (thisSlider.xSize / 2), thisSlider.xCenterLoc + (thisSlider.xSize / 2)), (int)thisSlider.yCenterLoc, 6, thisSlider.handleBorderColorDisabled);
             tft.drawCircle((int)map(thisSlider.value, thisSlider.minValue, thisSlider.maxValue, thisSlider.xCenterLoc - (thisSlider.xSize / 2), thisSlider.xCenterLoc + (thisSlider.xSize / 2)), (int)thisSlider.yCenterLoc, 7, thisSlider.handleBorderColorDisabled);
    
             // draw the slider lines
             tft.drawLine(thisSlider.xCenterLoc - (thisSlider.xSize / 2), thisSlider.yCenterLoc, thisSlider.xCenterLoc + (thisSlider.xSize / 2), thisSlider.yCenterLoc, thisSlider.scaleColorDisabled);
             tft.drawLine(thisSlider.xCenterLoc - (thisSlider.xSize / 2), thisSlider.yCenterLoc - 3, thisSlider.xCenterLoc - (thisSlider.xSize / 2), thisSlider.yCenterLoc + 3, thisSlider.scaleColorDisabled);  // left end
             tft.drawLine(thisSlider.xCenterLoc + (thisSlider.xSize / 2), thisSlider.yCenterLoc - 3, thisSlider.xCenterLoc + (thisSlider.xSize / 2), thisSlider.yCenterLoc + 3, thisSlider.scaleColorDisabled);  // right end
             tft.drawLine(thisSlider.xCenterLoc, thisSlider.yCenterLoc - 2, thisSlider.xCenterLoc, thisSlider.yCenterLoc + 2, thisSlider.scaleColorDisabled);  // center
          }
       } else {
          if (thisSlider.activated)
          {
             // clear the entire slider
             tft.fillRect(thisSlider.xCenterLoc - (thisSlider.xSize / 2) - 3, thisSlider.yCenterLoc - (thisSlider.ySize / 2) - 7, thisSlider.xSize + 6, thisSlider.ySize + 15, thisSlider.backgroundColor);
    
             // draw the slider outline
             tft.drawRect(thisSlider.xCenterLoc - (thisSlider.xSize / 2) - 3, thisSlider.yCenterLoc - (thisSlider.ySize / 2) - 7, thisSlider.xSize + 6, thisSlider.ySize + 15, thisSlider.borderColor);
    
             // draw the slider handle
             tft.fillCircle(thisSlider.xCenterLoc, (int)map(thisSlider.value, thisSlider.maxValue, thisSlider.minValue, thisSlider.yCenterLoc - (thisSlider.ySize / 2), thisSlider.yCenterLoc + (thisSlider.ySize / 2)), 6, thisSlider.handleColor);
             tft.drawCircle(thisSlider.xCenterLoc, (int)map(thisSlider.value, thisSlider.maxValue, thisSlider.minValue, thisSlider.yCenterLoc - (thisSlider.ySize / 2), thisSlider.yCenterLoc + (thisSlider.ySize / 2)), 6, thisSlider.handleBorderColor);
             tft.drawCircle(thisSlider.xCenterLoc, (int)map(thisSlider.value, thisSlider.maxValue, thisSlider.minValue, thisSlider.yCenterLoc - (thisSlider.ySize / 2), thisSlider.yCenterLoc + (thisSlider.ySize / 2)), 7, thisSlider.handleBorderColor);
    
             // draw the slider lines
             tft.drawLine(thisSlider.xCenterLoc, thisSlider.yCenterLoc - (thisSlider.ySize / 2), thisSlider.xCenterLoc, thisSlider.yCenterLoc + (thisSlider.ySize / 2), thisSlider.scaleColor);
             tft.drawLine(thisSlider.xCenterLoc - 3, thisSlider.yCenterLoc - (thisSlider.ySize / 2), thisSlider.xCenterLoc + 3, thisSlider.yCenterLoc - (thisSlider.ySize / 2), thisSlider.scaleColor);  // top end
             tft.drawLine(thisSlider.xCenterLoc - 3, thisSlider.yCenterLoc + (thisSlider.ySize / 2), thisSlider.xCenterLoc + 3, thisSlider.yCenterLoc + (thisSlider.ySize / 2), thisSlider.scaleColor);  // bottom end
             tft.drawLine(thisSlider.xCenterLoc - 2, thisSlider.yCenterLoc, thisSlider.xCenterLoc + 2, thisSlider.yCenterLoc, thisSlider.scaleColor);  // center
          } else {
             // clear the entire slider
             tft.fillRect(thisSlider.xCenterLoc - (thisSlider.xSize / 2) - 3, thisSlider.yCenterLoc - (thisSlider.ySize / 2) - 7, thisSlider.xSize + 6, thisSlider.ySize + 15, thisSlider.backgroundColorDisabled);
    
             // draw the slider outline
             tft.drawRect(thisSlider.xCenterLoc - (thisSlider.xSize / 2) - 3, thisSlider.yCenterLoc - (thisSlider.ySize / 2) - 7, thisSlider.xSize + 6, thisSlider.ySize + 15, thisSlider.borderColorDisabled);
    
             // draw the slider handle
             tft.fillCircle(thisSlider.xCenterLoc, (int)map(thisSlider.value, thisSlider.maxValue, thisSlider.minValue, thisSlider.yCenterLoc - (thisSlider.ySize / 2), thisSlider.yCenterLoc + (thisSlider.ySize / 2)), 6, thisSlider.handleColorDisabled);
             tft.drawCircle(thisSlider.xCenterLoc, (int)map(thisSlider.value, thisSlider.maxValue, thisSlider.minValue, thisSlider.yCenterLoc - (thisSlider.ySize / 2), thisSlider.yCenterLoc + (thisSlider.ySize / 2)), 6, thisSlider.handleBorderColorDisabled);
             tft.drawCircle(thisSlider.xCenterLoc, (int)map(thisSlider.value, thisSlider.maxValue, thisSlider.minValue, thisSlider.yCenterLoc - (thisSlider.ySize / 2), thisSlider.yCenterLoc + (thisSlider.ySize / 2)), 7, thisSlider.handleBorderColorDisabled);
    
             // draw the slider lines
             tft.drawLine(thisSlider.xCenterLoc, thisSlider.yCenterLoc - (thisSlider.ySize / 2), thisSlider.xCenterLoc, thisSlider.yCenterLoc + (thisSlider.ySize / 2), thisSlider.scaleColorDisabled);
             tft.drawLine(thisSlider.xCenterLoc - 3, thisSlider.yCenterLoc - (thisSlider.ySize / 2), thisSlider.xCenterLoc + 3, thisSlider.yCenterLoc - (thisSlider.ySize / 2), thisSlider.scaleColorDisabled);  // top end
             tft.drawLine(thisSlider.xCenterLoc - 3, thisSlider.yCenterLoc + (thisSlider.ySize / 2), thisSlider.xCenterLoc + 3, thisSlider.yCenterLoc + (thisSlider.ySize / 2), thisSlider.scaleColorDisabled);  // bottom end
             tft.drawLine(thisSlider.xCenterLoc - 2, thisSlider.yCenterLoc, thisSlider.xCenterLoc + 2, thisSlider.yCenterLoc, thisSlider.scaleColorDisabled);  // center
          }
       }
    
       characterCount = thisSlider.placesBeforeTheDecimal + thisSlider.placesAfterTheDecimal;
       if (thisSlider.placesAfterTheDecimal != 0)
       {
          // add one character for the decimal point
          characterCount++;
       }
       if (thisSlider.showPlusMinusSign)
       {
          // add one for the +/- sign
          characterCount++;
    
          if (thisSlider.value >= 0.0)
          {
             outString = outString + "+";
          } else {
             outString = outString + "-";
          }
       }
    
       switch (thisSlider.placesBeforeTheDecimal)
       {
          case 5:
          {
             if (thisSlider.value < 10000.0)
             {
                characterCount--;
             } else {
                outString = outString + (char)(((int)(abs(thisSlider.value)) / 10000) + 0x30);
             }
          }
          // no break, so fall-thru
    
          case 4:
          {
             if (thisSlider.value < 1000)
             {
                characterCount--;
             } else {
                outString = outString + (char)((((int)(abs(thisSlider.value)) % 10000) / 1000) + 0x30);
             }
          }
          // no break, so fall-thru
    
          case 3:
          {
             if (thisSlider.value < 100)
             {
                characterCount--;
             } else {
                outString = outString + (char)((((int)(abs(thisSlider.value)) % 1000) / 100) + 0x30);
             }
          }
          // no break, so fall-thru
    
          case 2:
          {
             if (thisSlider.value < 10)
             {
                characterCount--;
             } else {
                outString = outString + (char)((((int)(abs(thisSlider.value)) % 100) / 10) + 0x30);
             }
          }
          // no break, so fall-thru
       }
    
       outString = outString + (char)(((int)(abs(thisSlider.value)) % 10) + 0x30);
    
       if (thisSlider.placesAfterTheDecimal != 0)
       {
          outString = outString + ".";
       }
    
       switch (thisSlider.placesAfterTheDecimal)
       {
          case 1:
          {
             outString = outString + (char)(((int)((abs(thisSlider.value) * 10.0)) % 10) + 0x30);
          }
          break;
    
          case 2:
          {
             outString = outString + (char)(((int)((abs(thisSlider.value) * 10.0)) % 10) + 0x30);
             outString = outString + (char)(((int)((abs(thisSlider.value) * 100.0)) % 10) + 0x30);
          }
          break;
       }
    
       tft.fillRect(thisSlider.xValueCenterLoc - ((thisSlider.placesBeforeTheDecimal + thisSlider.placesAfterTheDecimal + 2) * 3), thisSlider.yValueCenterLoc - 4, (thisSlider.placesBeforeTheDecimal + thisSlider.placesAfterTheDecimal + 2) * 6, 8, ILI9341_BLACK);
    
       centerDrawText(outString, thisSlider.xValueCenterLoc + 1, thisSlider.yValueCenterLoc, thisSlider.valueColor, ILI9341_BLACK);
    }  // drawSlider()
    
    
    // main loop
    void loop()
    {
       bool screen_change = false;
    
       // if the touchscreen is being touched anywhere
       if (processTouchscreen())
       {
          switch(config_mode)
          {
             case CONFIG_MODE_MAIN:
             {
                if (checkSlider(&mainVolumeSlider))
                {
                   screen_update_required = true;
                   save_needed = true;
                }
    
                if (checkSlider(&mainTuningSlider))
                {
                   screen_update_required = true;
                   save_needed = true;
                }
    
                if (checkSlider(&mainLevelASlider))
                {
                   screen_update_required = true;
                   save_needed = true;
                }
    
                if (checkSlider(&mainTuningASlider))
                {
                   screen_update_required = true;
                   save_needed = true;
                }
    
                if (checkSlider(&mainLevelBSlider))
                {
                   screen_update_required = true;
                   save_needed = true;
                }
    
                if (checkSlider(&mainTuningBSlider))
                {
                   screen_update_required = true;
                   save_needed = true;
                }
    
                if (checkSlider(&mainLevelCSlider))
                {
                   screen_update_required = true;
                   save_needed = true;
                }
    
                if (checkSlider(&mainTuningCSlider))
                {
                   screen_update_required = true;
                   save_needed = true;
                }
             }
             break;
          }
       } else {
          // if the touchscreen was touched, but is no longer being touched
          if (touch_triggered)
          {
             touch_triggered = false;
    
             switch(config_mode)
             {
                case CONFIG_MODE_MAIN:
                {
                   if (checkButton(mainTuningButton))
                   {
                      mainTuningSlider.value = 0.0;
    
                      drawSlider(mainTuningSlider);
    
                      screen_update_required = true;
    
                      save_needed = true;
                   }
    
                   if (checkButton(mainTuningAButton))
                   {
                      mainTuningASlider.value = 0.0;
    
                      drawSlider(mainTuningASlider);
    
                      screen_update_required = true;
    
                      save_needed = true;
                   }
    
                   if (checkButton(mainTuningBButton))
                   {
                      mainTuningBSlider.value = 0.0;
    
                      drawSlider(mainTuningBSlider);
    
                      screen_update_required = true;
    
                      save_needed = true;
                   }
    
                   if (checkButton(mainTuningCButton))
                   {
                      mainTuningCSlider.value = 0.0;
    
                      drawSlider(mainTuningCSlider);
    
                      screen_update_required = true;
    
                      save_needed = true;
                   }
                }
                break;
             }
    
             if (screen_change)
             {
                drawScreen();
    
                screen_change = false;
    
                screen_update_required = true;
             }
    
             BtnX = BtnY = -1;
          }
       }
    
       // see if a save has been requested
       if (save_needed == true)
       {
          save_needed = false;
    
          // initialize the save accumulator delay timer
          save_delay_millis = millis();
       } else {
          // if a delay is in progress & the dealy timer period is exceeded
          if ((save_delay_millis > 0) && ((millis() - save_delay_millis) > SAVE_DELAY_MILLIS))
          {
             // reset the save accumulator delay timer
             save_delay_millis = 0;
    
             // actually save everything to EEPROM
             saveSettings();
          }
       }
    
       if ((screen_update_required) && (millis() > screen_update_time))
       {
          screen_update_time = millis() + SCREEN_UPDATE_MILLIS;
          screen_update_required = false;
          tft.updateScreen();
       }
    }  // loop()
    
    
    // process any touchscreen activity
    bool processTouchscreen(void)
    {
       int i = 5;
    
       TS_Point p = ts.getPoint();
    
       if ((!ts.touched()) && (previously_touched))
       {
          touch_triggered = true;
       }
    
       if (ts.touched())
       {
          // Scale from raw to tft values to expected width & height using the calibration #'s
          BtnX = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
          BtnY = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());
    
          // see if we can't settle the touchpoint a little
          while ((BtnX != previousBtnX) && (BtnY != previousBtnY) && (--i > 0))
          {
             previousBtnX = BtnX;
             previousBtnY = BtnY;
    
             // Scale from raw to tft values to expected width & height using the calibration #'s
             BtnX = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
             BtnY = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());
          }
    
          previously_touched = true;
       } else {
          previously_touched = false;
       }
    
       return (ts.touched());  // whether the touchscreen is being touched or not
    }  // processTouchscreen()
    
    
    // read the current settings from EEPROM
    void readSettings(void)
    {
       byte eeprom_value, xor_value, inv_xor_value;
       byte xor_result = 0x4D;  // start with a non-zero value
       bool header_is_good = true;
    
    #ifdef DEBUG_EEPROM_READ
       Serial.println("");
       Serial.println("Attempting to read saved settings from EEPROM");
    #endif
    
       for (int eeprom_index = (int)(EEPROM_INDEX_HEADER_0); eeprom_index <= (int)(EEPROM_INDEX_INV_CHECKSUM); eeprom_index++)
       {
          EEPROM.get(eeprom_index, eeprom_value);
    
          if (eeprom_index < EEPROM_INDEX_CHECKSUM)
          {
             xor_result = xor_result ^ eeprom_value;
          }
    
    #ifdef DEBUG_EEPROM_READ
          Serial.print("(READ ");
          showIndex(eeprom_index);
          Serial.print(": ");
          showIndex(eeprom_value);
          Serial.println(")");
    #endif
    
          if (eeprom_index <= EEPROM_INDEX_HEADER_4)
          {
             if (eeprom_value != EEPROM_HEADER[eeprom_index])
             {
                header_is_good = false;
             }
          }
       }
    
       // read the checksum & inverse checksum
       EEPROM.get(EEPROM_INDEX_CHECKSUM, xor_value);
       EEPROM.get(EEPROM_INDEX_INV_CHECKSUM, inv_xor_value);
    
       // if the checksums match & the header values match, then we seem to have valid settings in EEPROM, so read all of the settings
       if ((xor_value == xor_result) &&
           (inv_xor_value == (byte)~xor_result) &&
           (header_is_good))
       {
    #ifdef DEBUG_EEPROM_READ
          Serial.println("Valid settings found in EEPROM");
          Serial.println("");
    #endif
    
    #ifndef DISABLE_EEPROM_READ_SETTINGS
          for (int eeprom_index = (int)(EEPROM_INDEX_HEADER_0); eeprom_index <= (int)(EEPROM_INDEX_INV_CHECKSUM); eeprom_index++)
          {
             EEPROM.get(eeprom_index, eeprom_value);
    
    #ifdef DEBUG_EEPROM_READ
             Serial.print("(READ ");
             showIndex(eeprom_index);
             Serial.print(": ");
             showIndex(eeprom_value);
    #endif
    
             switch(eeprom_index)
             {
                case EEPROM_INDEX_HEADER_0:
                case EEPROM_INDEX_HEADER_1:
                case EEPROM_INDEX_HEADER_2:
                case EEPROM_INDEX_HEADER_3:
                case EEPROM_INDEX_HEADER_4:
                {
    #ifdef DEBUG_EEPROM_READ
                   Serial.print(") Header[");
                   Serial.print(eeprom_index);
                   Serial.print("]                       = ");
                   Serial.println((char)eeprom_value);
    #endif
                }
                break;
    
                case EEPROM_INDEX_MAIN_VOLUME:
                {
                   mainVolumeSlider.value = map((float)eeprom_value, 0.0, 255.0, mainVolumeSlider.minValue, mainVolumeSlider.maxValue);
    
    #ifdef DEBUG_EEPROM_READ
                   Serial.print(") Main Volume Slider              = ");
                   Serial.println(mainVolumeSlider.value);
    #endif
                }
                break;
    
                case EEPROM_INDEX_MAIN_LEVEL_A:
                {
                   mainLevelASlider.value = map((float)eeprom_value, 0.0, 255.0, mainLevelASlider.minValue, mainLevelASlider.maxValue);
    
    #ifdef DEBUG_EEPROM_READ
                   Serial.print(") Main Level A Slider             = ");
                   Serial.println(mainLevelASlider.value);
    #endif
                }
                break;
    
                case EEPROM_INDEX_MAIN_LEVEL_B:
                {
                   mainLevelBSlider.value = map((float)eeprom_value, 0.0, 255.0, mainLevelBSlider.minValue, mainLevelBSlider.maxValue);
    
    #ifdef DEBUG_EEPROM_READ
                   Serial.print(") Main Level B Slider             = ");
                   Serial.println(mainLevelBSlider.value);
    #endif
                }
                break;
    
                case EEPROM_INDEX_MAIN_LEVEL_C:
                {
                   mainLevelCSlider.value = map((float)eeprom_value, 0.0, 255.0, mainLevelCSlider.minValue, mainLevelCSlider.maxValue);
    
    #ifdef DEBUG_EEPROM_READ
                   Serial.print(") Main Level C Slider             = ");
                   Serial.println(mainLevelCSlider.value);
    #endif
                }
                break;
    
                case EEPROM_INDEX_MAIN_TUNING:
                {
                   mainTuningSlider.value = map((float)eeprom_value, 0.0, 255.0, mainTuningSlider.minValue, mainTuningSlider.maxValue);
    
    #ifdef DEBUG_EEPROM_READ
                   Serial.print(") Main Tuning Slider              = ");
                   Serial.println(mainTuningSlider.value);
    #endif
                }
                break;
    
                case EEPROM_INDEX_MAIN_TUNING_A:
                {
                   mainTuningASlider.value = map((float)eeprom_value, 0.0, 255.0, mainTuningASlider.minValue, mainTuningASlider.maxValue);
    
    #ifdef DEBUG_EEPROM_READ
                   Serial.print(") Main Tuning A Slider            = ");
                   Serial.println(mainTuningASlider.value);
    #endif
                }
                break;
    
                case EEPROM_INDEX_MAIN_TUNING_B:
                {
                   mainTuningBSlider.value = map((float)eeprom_value, 0.0, 255.0, mainTuningBSlider.minValue, mainTuningBSlider.maxValue);
    
    #ifdef DEBUG_EEPROM_READ
                   Serial.print(") Main Tuning B Slider            = ");
                   Serial.println(mainTuningBSlider.value);
    #endif
                }
                break;
    
                case EEPROM_INDEX_MAIN_TUNING_C:
                {
                   mainTuningCSlider.value = map((float)eeprom_value, 0.0, 255.0, mainTuningCSlider.minValue, mainTuningCSlider.maxValue);
    
    #ifdef DEBUG_EEPROM_READ
                   Serial.print(") Main Tuning C Slider            = ");
                   Serial.println(mainTuningCSlider.value);
    #endif
                }
                break;
    
                case EEPROM_INDEX_CHECKSUM:
                {
                   eeprom_value = (char)(xor_result);
    
    #ifdef DEBUG_EEPROM_WRITE
                   Serial.print(") Calculated CHECKSUM             = ");
                   showIndex(xor_result);
                   Serial.println("");
    #endif
                }
                break;
    
                case EEPROM_INDEX_INV_CHECKSUM:
                {
                   eeprom_value = (char)(~xor_result);
    
    #ifdef DEBUG_EEPROM_WRITE
                   Serial.print(") Calculated INVERSE CHECKSUM     = ");
                   showIndex((byte)~xor_result);
                   Serial.println("");
    #endif
                }
                break;
    
                default:
                {
    #ifdef DEBUG_EEPROM_READ
                   Serial.println(")");
    #endif
                }
                break;
             }
          }
    #endif
       } else {
    #ifdef DEBUG_EEPROM_READ
          Serial.println("Invalid settings found in EEPROM");
          Serial.println("");
    
          Serial.print("(READ ");
          showIndex(EEPROM_INDEX_CHECKSUM);
          Serial.print  (") xor_value          = ");
          Serial.println(xor_value);
          Serial.print("(CALC) xor_result             = ");
          Serial.println(xor_result);
          Serial.print("(READ ");
          showIndex(EEPROM_INDEX_INV_CHECKSUM);
          Serial.print  (") inv_xor_value      = ");
          Serial.println(inv_xor_value);
          Serial.print("(CALC) inv_xor_result         = ");
          Serial.println((byte)~xor_result);
    #endif
    
          saveSettings();
       }
    }  // readSettings()
    
    
    // save the current settings to EEPROM
    void saveSettings(void)
    {
       byte xor_result = 0x4D;  // start with a non-zero value
       char eeprom_value;
    
    #ifdef DEBUG_EEPROM_WRITE
       Serial.println("");
       Serial.println("Saving settings to EEPROM");
       Serial.println("");
    #endif
    
       for (byte eeprom_index = (byte)(EEPROM_INDEX_HEADER_0); eeprom_index <= (byte)(EEPROM_INDEX_INV_CHECKSUM); eeprom_index++)
       {
    #ifdef DEBUG_EEPROM_WRITE
          Serial.print("(WRITE ");
          showIndex(eeprom_index);
          Serial.print(": ");
    #endif
          switch(eeprom_index)
          {
             case EEPROM_INDEX_HEADER_0:
             case EEPROM_INDEX_HEADER_1:
             case EEPROM_INDEX_HEADER_2:
             case EEPROM_INDEX_HEADER_3:
             case EEPROM_INDEX_HEADER_4:
             {
                eeprom_value = EEPROM_HEADER[eeprom_index];
    
    #ifdef DEBUG_EEPROM_WRITE
                showIndex(eeprom_value);
                Serial.print(") Header[");
                Serial.print(eeprom_index);
                Serial.print("]                              = ");
                Serial.println(eeprom_value);
    #endif
             }
             break;
    
             case EEPROM_INDEX_MAIN_VOLUME:
             {
                eeprom_value = (char)map(mainVolumeSlider.value, mainVolumeSlider.minValue, mainVolumeSlider.maxValue, 0, 255);
    
    #ifdef DEBUG_EEPROM_WRITE
                showIndex(eeprom_value);
                Serial.print(") Main Volume Slider                     = ");
                Serial.println(mainVolumeSlider.value);
    #endif
             }
             break;
    
             case EEPROM_INDEX_MAIN_LEVEL_A:
             {
                eeprom_value = (char)map(mainLevelASlider.value, mainLevelASlider.minValue, mainLevelASlider.maxValue, 0, 255);
    
    #ifdef DEBUG_EEPROM_WRITE
                showIndex(eeprom_value);
                Serial.print(") Main Level A Slider                    = ");
                Serial.println(mainLevelASlider.value);
    #endif
             }
             break;
    
             case EEPROM_INDEX_MAIN_LEVEL_B:
             {
                eeprom_value = (char)map(mainLevelBSlider.value, mainLevelBSlider.minValue, mainLevelBSlider.maxValue, 0, 255);
    
    #ifdef DEBUG_EEPROM_WRITE
                showIndex(eeprom_value);
                Serial.print(") Main Level B Slider                    = ");
                Serial.println(mainLevelBSlider.value);
    #endif
             }
             break;
    
             case EEPROM_INDEX_MAIN_LEVEL_C:
             {
                eeprom_value = (char)map(mainLevelCSlider.value, mainLevelCSlider.minValue, mainLevelCSlider.maxValue, 0, 255);
    
    #ifdef DEBUG_EEPROM_WRITE
                showIndex(eeprom_value);
                Serial.print(") Main Level C Slider                    = ");
                Serial.println(mainLevelCSlider.value);
    #endif
             }
             break;
    
             case EEPROM_INDEX_MAIN_TUNING:
             {
                eeprom_value = (char)map(mainTuningSlider.value, mainTuningSlider.minValue, mainTuningSlider.maxValue, 0, 255);
    
    #ifdef DEBUG_EEPROM_WRITE
                showIndex(eeprom_value);
                Serial.print(") Main Tuning Slider                     = ");
                Serial.println(mainTuningSlider.value);
    #endif
             }
             break;
    
             case EEPROM_INDEX_MAIN_TUNING_A:
             {
                eeprom_value = (char)map(mainTuningASlider.value, mainTuningASlider.minValue, mainTuningASlider.maxValue, 0, 255);
    
    #ifdef DEBUG_EEPROM_WRITE
                showIndex(eeprom_value);
                Serial.print(") Main Tuning A Slider                   = ");
                Serial.println(mainTuningASlider.value);
    #endif
             }
             break;
    
             case EEPROM_INDEX_MAIN_TUNING_B:
             {
                eeprom_value = (char)map(mainTuningBSlider.value, mainTuningBSlider.minValue, mainTuningBSlider.maxValue, 0, 255);
    
    #ifdef DEBUG_EEPROM_WRITE
                showIndex(eeprom_value);
                Serial.print(") Main Tuning B Slider                   = ");
                Serial.println(mainTuningBSlider.value);
    #endif
             }
             break;
    
             case EEPROM_INDEX_MAIN_TUNING_C:
             {
                eeprom_value = (char)map(mainTuningCSlider.value, mainTuningCSlider.minValue, mainTuningCSlider.maxValue, 0, 255);
    
    #ifdef DEBUG_EEPROM_WRITE
                showIndex(eeprom_value);
                Serial.print(") Main Tuning C Slider                   = ");
                Serial.println(mainTuningCSlider.value);
    #endif
             }
             break;
    
             case EEPROM_INDEX_CHECKSUM:
             {
                eeprom_value = (char)(xor_result);
    
    #ifdef DEBUG_EEPROM_WRITE
                showIndex(eeprom_value);
                Serial.print(") Calculated CHECKSUM                    = ");
                showIndex(xor_result);
                Serial.println("");
    #endif
             }
             break;
    
             case EEPROM_INDEX_INV_CHECKSUM:
             {
                eeprom_value = (char)(~xor_result);
    
    #ifdef DEBUG_EEPROM_WRITE
                showIndex(eeprom_value);
                Serial.print(") Calculated INVERSE CHECKSUM            = ");
                showIndex((byte)~xor_result);
                Serial.println("");
    #endif
             }
             break;
    
             default:
             {
                eeprom_value = eeprom_index;
    
    #ifdef DEBUG_EEPROM_WRITE
                showIndex(eeprom_value);
                Serial.println(")");
    #endif
             }
             break;
          }
    
    #ifndef DISABLE_EEPROM_WRITE_SETTINGS
          EEPROM.update(eeprom_index, (byte)(eeprom_value));
    #endif
          if (eeprom_index < EEPROM_INDEX_CHECKSUM)
          {
             xor_result = (byte)(xor_result ^ eeprom_value);
          }
       }
    }  // saveSettings()
    
    
    // one-time setup
    void setup()
    {
       bool foundBegin = false;
       bool foundEnd = false;
       int indexCount = 0;
    
       Serial.begin(57600);
       while (!Serial && (millis() <= 1000));
    
       Serial.println("=============================================");
       Serial.print("         ");
       Serial.println(VERSION1);
       Serial.print("     ");
       Serial.println(VERSION2);
       Serial.println(VERSION3);
       Serial.println("=============================================");
       Serial.println("");
       Serial.println("");
    
       delay(500);
    
       tft.begin();
       tft.setRotation(1);
       tft.setFrameBuffer(framebuf);
       tft.useFrameBuffer(true);
    
       delay(100);
    
       ts.begin();
       ts.setRotation(3);
    
       tft.fillScreen(ILI9341_BLACK);
    
       centerDrawText(VERSION1, 160, 100, ILI9341_GREEN, ILI9341_BLACK);
       centerDrawText(VERSION2, 160, 120, ILI9341_YELLOW, ILI9341_BLACK);
       centerDrawText(VERSION3, 160, 140, ILI9341_RED, ILI9341_BLACK);
       tft.updateScreen();
    
       delay(3000);
    
       tft.fillScreen(ILI9341_BLACK);
    
       centerDrawText('v', 280, 4, ILI9341_YELLOW, ILI9341_BLACK);
       for(unsigned int i = 0; i < sizeof(VERSION2); i++)
       {
          if (!foundBegin)
          {
             if (VERSION2[i] == ' ')
             {
                foundBegin = true;
             }
          } else {
             if (!foundEnd)
             {
                if (VERSION2[i] == ' ')
                {
                   foundEnd = true;
                } else {
                   centerDrawText((char)VERSION2[i], 280 + (6 * ++indexCount), 4, ILI9341_YELLOW, ILI9341_BLACK);
                }
             }
          }
       }
    
       centerDrawText(VERSION1, 160, 4, ILI9341_GREEN, ILI9341_BLACK);
    
       drawScreen();
    
       tft.updateScreen();
    
       // try to read the settings from EEPROM
       readSettings();
    
       drawScreen();
    
       tft.updateScreen();
    }  // setup()
    
    
    // show index of EEPROM reads/writes
    void showIndex(int index)
    {
       if (index < 100)
       {
          Serial.print("0");
       } else {
          Serial.print((char)((index / 100) + 0x30));
       }
    
       if (index < 10)
       {
          Serial.print("0");
       } else {
          Serial.print((char)(((index / 10) % 10) + 0x30));
       }
    
       Serial.print((char)((index % 10) + 0x30));
    }  // showIndex()
    
    
    // EOF placeholder
    Last edited by kd5rxt-mark; 02-06-2021 at 07:24 PM.

  2. #2

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •