"Smallest Code" optimization breaks EEPROM writes

kd5rxt-mark

Well-known member
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:
Back
Top