Compile error: '(functionName)' was not declared in this scope

I am VERY familiar with this error. Either a mistyped name or capitalization problem, or a nesting issue from a missing or extra brace, bracket, or parenthesis.
But this time, neither of those seems to be the issue. I have spent waaayyy to much time trying to track this down already and am facing a deadline. And I've torn out what little hair I have left. I HATE having to ask for help, much prefer to research it and solve things myself, and I usually learn some extra stuff that way. But I'm exasperated and it's time to call in the pros.
So first of all, I have double-triple-quadruple checked the names and spellings and capitalization. That ain't it. And I have meticulously looked for block-nesting problems and a missing/extra brace, bracket, or parenthesis. Have not only folded and unfolded everything many times in the Arduino IDE, but also opened it in Visual Studio with Visual Micro, and Visual Studio Code with PlatformIO and used those IDE functions to check all my block nesting. I'm convinced at this point that that ain't it either (although I might not be seeing the forest because of all the trees in my way).
Finally, digging thru these forums, plenty of other people have made posts, and responses regarding this error. One that caught my eye though was this one where J-M-L Jackson noted on Feb 1 about the ordering of the procedures:

When it comes to functions (a void does not mean anything) the IDE will try to help you and pre-declare them for you in case you define them in the wrong way.

for example in theory this is not correct in C++, function A tries to call functionB which is unknown

so the right way to do this is to define functionB first.

but the IDE will help you and actually generate prototypes for you so the code that gets compiled looks like this

so you can write them in any order.

I have a hunch this may not be working correctly, or at least not how I (likely incorrectly) expect it to work. After much rearranging and shuffling of code I can change which function is "not declared" and create more and new errors, but still not get it to compile.
P.S. It's a nifty little sketch for controlling a string of pixels where you can pre-define a list of animations and colors. A little like WLED but with absolutely no user interface or configuration of any kind for people that are extremely technophobic, like my aunt. It takes a geek to set up and define the list of patterns and effects, but once compiled and flashed, you just plug it in and it just runs, super reliable. Feel free to reuse any of it.
XMasTree_Janine.zip (13.8 KB)

I (and many other members) will not download and unzip a zip file from an untrusted source. It is just too dangerous and a pain in the tutu. There is a way to properly post code. Please read the forum guidelines to see how to properly post code and some information on making a good post.

Use the IDE autoformat tool (ctrl-t or Tools, Auto format) before posting code in code tags.

Please include the entire error message. It is easy to do. There is a button (lower right of the IDE window) called "copy error message". Copy the error and paste into a post in code tags. Paraphrasing the error message leaves out important information.

If you mistake me for an expert, you are really in trouble.

But as @groundFungus says, POST the code. This will greatly increase the pool of people willing to help.

My apologies gentlemen, I thought it would be better to post it zipped since it's 2100 lines and I can't isolate the error to just one section (plus, as mentioned, rearranging the code changes but does not eliminate the 'undeclared' function)

A summary of the error would be:

W:\Documents\SourceCode\uControllers\Arduino\Pixels\Janine\XMasTree_Janine\XMasTree_Janine.ino: In function 'void setup()':
XMasTree_Janine:798:5: error: 'setDefaults' was not declared in this scope
     setDefaults();
     ^~~~~~~~~~~
W:\Documents\SourceCode\uControllers\Arduino\Pixels\Janine\XMasTree_Janine\XMasTree_Janine.ino:798:5: note: suggested alternative: 'setTestDefaults'
     setDefaults();
     ^~~~~~~~~~~
     setTestDefaults

And the full error is:

D:\arduino\arduino-builder -dump-prefs -logger=machine -hardware D:\arduino\hardware -hardware C:\Users\Wizard\AppData\Local\Arduino15\packages -hardware W:\Documents\SourceCode\uControllers\hardware -tools D:\arduino\tools-builder -tools D:\arduino\hardware\tools\avr -tools C:\Users\Wizard\AppData\Local\Arduino15\packages -built-in-libraries D:\arduino\libraries -libraries W:\Documents\SourceCode\uControllers\libraries -fqbn=SparkFun:avr:promicro:cpu=16MHzatmega32U4 -ide-version=10819 -build-path C:\Users\Wizard\AppData\Local\Temp\arduino_build_585799 -warnings=default -build-cache C:\Users\Wizard\AppData\Local\Temp\arduino_cache_947569 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.avrdude.path=C:\Users\Wizard\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17 -prefs=runtime.tools.avrdude-6.3.0-arduino17.path=C:\Users\Wizard\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17 -prefs=runtime.tools.avr-gcc.path=C:\Users\Wizard\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7 -prefs=runtime.tools.avr-gcc-7.3.0-atmel3.6.1-arduino7.path=C:\Users\Wizard\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7 -prefs=runtime.tools.arduinoOTA.path=C:\Users\Wizard\AppData\Local\Arduino15\packages\arduino\tools\arduinoOTA\1.3.0 -prefs=runtime.tools.arduinoOTA-1.3.0.path=C:\Users\Wizard\AppData\Local\Arduino15\packages\arduino\tools\arduinoOTA\1.3.0 -verbose W:\Documents\SourceCode\uControllers\Arduino\Pixels\Janine\XMasTree_Janine\XMasTree_Janine.ino
D:\arduino\arduino-builder -compile -logger=machine -hardware D:\arduino\hardware -hardware C:\Users\Wizard\AppData\Local\Arduino15\packages -hardware W:\Documents\SourceCode\uControllers\hardware -tools D:\arduino\tools-builder -tools D:\arduino\hardware\tools\avr -tools C:\Users\Wizard\AppData\Local\Arduino15\packages -built-in-libraries D:\arduino\libraries -libraries W:\Documents\SourceCode\uControllers\libraries -fqbn=SparkFun:avr:promicro:cpu=16MHzatmega32U4 -ide-version=10819 -build-path C:\Users\Wizard\AppData\Local\Temp\arduino_build_585799 -warnings=default -build-cache C:\Users\Wizard\AppData\Local\Temp\arduino_cache_947569 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.avrdude.path=C:\Users\Wizard\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17 -prefs=runtime.tools.avrdude-6.3.0-arduino17.path=C:\Users\Wizard\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17 -prefs=runtime.tools.avr-gcc.path=C:\Users\Wizard\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7 -prefs=runtime.tools.avr-gcc-7.3.0-atmel3.6.1-arduino7.path=C:\Users\Wizard\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7 -prefs=runtime.tools.arduinoOTA.path=C:\Users\Wizard\AppData\Local\Arduino15\packages\arduino\tools\arduinoOTA\1.3.0 -prefs=runtime.tools.arduinoOTA-1.3.0.path=C:\Users\Wizard\AppData\Local\Arduino15\packages\arduino\tools\arduinoOTA\1.3.0 -verbose W:\Documents\SourceCode\uControllers\Arduino\Pixels\Janine\XMasTree_Janine\XMasTree_Janine.ino
Using board 'promicro' from platform in folder: C:\Users\Wizard\AppData\Local\arduino15\packages\SparkFun\hardware\avr\1.1.13
Using core 'arduino' from platform in folder: C:\Users\Wizard\AppData\Local\arduino15\packages\arduino\hardware\avr\1.8.5
Warning: Board arduino:avr://leonardo doesn't define a 'build.board' preference. Auto-set to: AVR_//LEONARDO
Detecting libraries used...
"C:\\Users\\Wizard\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega32u4 -DF_CPU=16000000L -DARDUINO=10819 -DARDUINO_AVR_PROMICRO -DARDUINO_ARCH_AVR -DUSB_VID=0x1b4f -DUSB_PID=0x9206 "-DUSB_MANUFACTURER=\"Unknown\"" "-DUSB_PRODUCT=\"SparkFun Pro Micro\"" "-IC:\\Users\\Wizard\\AppData\\Local\\arduino15\\packages\\arduino\\hardware\\avr\\1.8.5\\cores\\arduino" "-IC:\\Users\\Wizard\\AppData\\Local\\arduino15\\packages\\SparkFun\\hardware\\avr\\1.1.13\\variants\\promicro" "C:\\Users\\Wizard\\AppData\\Local\\Temp\\arduino_build_585799\\sketch\\XMasTree_Janine.ino.cpp" -o nul
Alternatives for EEPROMex.h: [EEPROMex@1.0.0]
ResolveLibrary(EEPROMex.h)
  -> candidates: [EEPROMex@1.0.0]
"C:\\Users\\Wizard\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega32u4 -DF_CPU=16000000L -DARDUINO=10819 -DARDUINO_AVR_PROMICRO -DARDUINO_ARCH_AVR -DUSB_VID=0x1b4f -DUSB_PID=0x9206 "-DUSB_MANUFACTURER=\"Unknown\"" "-DUSB_PRODUCT=\"SparkFun Pro Micro\"" "-IC:\\Users\\Wizard\\AppData\\Local\\arduino15\\packages\\arduino\\hardware\\avr\\1.8.5\\cores\\arduino" "-IC:\\Users\\Wizard\\AppData\\Local\\arduino15\\packages\\SparkFun\\hardware\\avr\\1.1.13\\variants\\promicro" "-IW:\\Documents\\SourceCode\\uControllers\\libraries\\EEPROMex" "C:\\Users\\Wizard\\AppData\\Local\\Temp\\arduino_build_585799\\sketch\\XMasTree_Janine.ino.cpp" -o nul
Alternatives for FastLED.h: [FastLED@3.5.0]
ResolveLibrary(FastLED.h)
  -> candidates: [FastLED@3.5.0]
"C:\\Users\\Wizard\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega32u4 -DF_CPU=16000000L -DARDUINO=10819 -DARDUINO_AVR_PROMICRO -DARDUINO_ARCH_AVR -DUSB_VID=0x1b4f -DUSB_PID=0x9206 "-DUSB_MANUFACTURER=\"Unknown\"" "-DUSB_PRODUCT=\"SparkFun Pro Micro\"" "-IC:\\Users\\Wizard\\AppData\\Local\\arduino15\\packages\\arduino\\hardware\\avr\\1.8.5\\cores\\arduino" "-IC:\\Users\\Wizard\\AppData\\Local\\arduino15\\packages\\SparkFun\\hardware\\avr\\1.1.13\\variants\\promicro" "-IW:\\Documents\\SourceCode\\uControllers\\libraries\\EEPROMex" "-IW:\\Documents\\SourceCode\\uControllers\\libraries\\FastLED\\src" "C:\\Users\\Wizard\\AppData\\Local\\Temp\\arduino_build_585799\\sketch\\XMasTree_Janine.ino.cpp" -o nul
Using cached library dependencies for file: W:\Documents\SourceCode\uControllers\libraries\EEPROMex\EEPROMex.cpp
Using cached library dependencies for file: W:\Documents\SourceCode\uControllers\libraries\FastLED\src\FastLED.cpp
Using cached library dependencies for file: W:\Documents\SourceCode\uControllers\libraries\FastLED\src\bitswap.cpp
Using cached library dependencies for file: W:\Documents\SourceCode\uControllers\libraries\FastLED\src\colorpalettes.cpp
Using cached library dependencies for file: W:\Documents\SourceCode\uControllers\libraries\FastLED\src\colorutils.cpp
Using cached library dependencies for file: W:\Documents\SourceCode\uControllers\libraries\FastLED\src\hsv2rgb.cpp
Using cached library dependencies for file: W:\Documents\SourceCode\uControllers\libraries\FastLED\src\lib8tion.cpp
Using cached library dependencies for file: W:\Documents\SourceCode\uControllers\libraries\FastLED\src\noise.cpp
Using cached library dependencies for file: W:\Documents\SourceCode\uControllers\libraries\FastLED\src\platforms\esp\32\clockless_rmt_esp32.cpp
Using cached library dependencies for file: W:\Documents\SourceCode\uControllers\libraries\FastLED\src\platforms.cpp
Using cached library dependencies for file: W:\Documents\SourceCode\uControllers\libraries\FastLED\src\power_mgt.cpp
Using cached library dependencies for file: W:\Documents\SourceCode\uControllers\libraries\FastLED\src\wiring.cpp
Generating function prototypes...
"C:\\Users\\Wizard\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega32u4 -DF_CPU=16000000L -DARDUINO=10819 -DARDUINO_AVR_PROMICRO -DARDUINO_ARCH_AVR -DUSB_VID=0x1b4f -DUSB_PID=0x9206 "-DUSB_MANUFACTURER=\"Unknown\"" "-DUSB_PRODUCT=\"SparkFun Pro Micro\"" "-IC:\\Users\\Wizard\\AppData\\Local\\arduino15\\packages\\arduino\\hardware\\avr\\1.8.5\\cores\\arduino" "-IC:\\Users\\Wizard\\AppData\\Local\\arduino15\\packages\\SparkFun\\hardware\\avr\\1.1.13\\variants\\promicro" "-IW:\\Documents\\SourceCode\\uControllers\\libraries\\EEPROMex" "-IW:\\Documents\\SourceCode\\uControllers\\libraries\\FastLED\\src" "C:\\Users\\Wizard\\AppData\\Local\\Temp\\arduino_build_585799\\sketch\\XMasTree_Janine.ino.cpp" -o "C:\\Users\\Wizard\\AppData\\Local\\Temp\\arduino_build_585799\\preproc\\ctags_target_for_gcc_minus_e.cpp"
"D:\\arduino\\tools-builder\\ctags\\5.8-arduino11/ctags" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives "C:\\Users\\Wizard\\AppData\\Local\\Temp\\arduino_build_585799\\preproc\\ctags_target_for_gcc_minus_e.cpp"
Compiling sketch...
"C:\\Users\\Wizard\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-g++" -c -g -Os -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -mmcu=atmega32u4 -DF_CPU=16000000L -DARDUINO=10819 -DARDUINO_AVR_PROMICRO -DARDUINO_ARCH_AVR -DUSB_VID=0x1b4f -DUSB_PID=0x9206 "-DUSB_MANUFACTURER=\"Unknown\"" "-DUSB_PRODUCT=\"SparkFun Pro Micro\"" "-IC:\\Users\\Wizard\\AppData\\Local\\arduino15\\packages\\arduino\\hardware\\avr\\1.8.5\\cores\\arduino" "-IC:\\Users\\Wizard\\AppData\\Local\\arduino15\\packages\\SparkFun\\hardware\\avr\\1.1.13\\variants\\promicro" "-IW:\\Documents\\SourceCode\\uControllers\\libraries\\EEPROMex" "-IW:\\Documents\\SourceCode\\uControllers\\libraries\\FastLED\\src" "C:\\Users\\Wizard\\AppData\\Local\\Temp\\arduino_build_585799\\sketch\\XMasTree_Janine.ino.cpp" -o "C:\\Users\\Wizard\\AppData\\Local\\Temp\\arduino_build_585799\\sketch\\XMasTree_Janine.ino.cpp.o"
W:\Documents\SourceCode\uControllers\Arduino\Pixels\Janine\XMasTree_Janine\XMasTree_Janine.ino: In function 'void setup()':
XMasTree_Janine:798:5: error: 'setDefaults' was not declared in this scope
     setDefaults();
     ^~~~~~~~~~~
W:\Documents\SourceCode\uControllers\Arduino\Pixels\Janine\XMasTree_Janine\XMasTree_Janine.ino:798:5: note: suggested alternative: 'setTestDefaults'
     setDefaults();
     ^~~~~~~~~~~
     setTestDefaults
W:\Documents\SourceCode\uControllers\Arduino\Pixels\Janine\XMasTree_Janine\XMasTree_Janine.ino: In function 'long int color2hex(CRGB)':
W:\Documents\SourceCode\uControllers\Arduino\Pixels\Janine\XMasTree_Janine\XMasTree_Janine.ino:925:28: warning: left shift count >= width of type [-Wshift-count-overflow]
   long ret = theColor.r << 16;
                            ^~
Using library EEPROMex at version 1.0.0 in folder: W:\Documents\SourceCode\uControllers\libraries\EEPROMex 
Using library FastLED at version 3.5.0 in folder: W:\Documents\SourceCode\uControllers\libraries\FastLED 
exit status 1
'setDefaults' was not declared in this scope

The code for XMasTree_Janine.ino is:

/**** XmasWS2811 ***
  Controls a string of 2801/2811/2812 etc. "Pixels"** for a Christmas Tree
  Patterns, Colors, etc.
** THIS sketch uses the FastLED 3.x.x Library

  UnCopyright (u) 2022+ Dr. Wizard K. Pendleton and Wizlights Software
  Released under General Public License.
  Open Source Rules Apply: No warranty of any kind!  It works for me, but I don't guarantee
  that it won't crash spectacularly for you!
  Send comments, complaints, bug fixes, updates, enhancements, and good dirty jokes to:
   dev.arduino@drwiz.net

  This is the version for Princess Janine D'Berry's Christmas Tree
  and includes a custom effect list just for her.
*/

#include <EEPROMex.h>
#include <EEPROMVar.h>
#include <FastLED.h>
//#include <Wire.h>
#include "DebugUtils.h"

// ****************
// **  WS2811    **
// **  (Pixels)  **
// **  L E D s   **
// ****************
#define DATA_PIN  21 // aka A3, using that one cuz its right beside VCC, and close to GND
//    Obviously, a different pin could be used if necessary.
#define MAX_LEDS 512
int num_leds = 250;
//struct CRGB
//{
//	byte r;
//	byte g;
//	byte b;
//};

// HERE'S THE BIG MEMORY HOG!
// THIS TAKES 5K OF THE ARDUINO MEGA 2560'S 8K OF RAM, RIGHT HERE
// 5 Bytes for each of the 1024 LEDS
// 3 Bytes for the color of each LED,
// 2 Bytes for a list of Pixel #'s in some particular order for certain effects
// such as Sparkle and Dissolve

// Define the array of leds
CRGB leds[MAX_LEDS];
int theList[MAX_LEDS];  // For storing a list of which pixel something was done to on the last pass
// Also used for random dissolve effect

// ****************
// **  Standard  **
// **  Colors    **
// ****************
#define COLOR_BLACK_OFF  0
#define   RGB_BLACK_OFF  0x000000L
#define COLOR_CUSTOM1    1
#define COLOR_CUSTOM2    2
#define COLOR_CUSTOM3    3
#define COLOR_CUSTOM4    4
#define COLOR_CUSTOM5    5
#define COLOR_CUSTOM6    6
#define COLOR_CUSTOM7    7
#define COLOR_CUSTOM8    8
#define COLOR_RED        9
#define   RGB_RED        0xff0000L
#define COLOR_GREEN      10
#define   RGB_GREEN      0x00ff00L
#define COLOR_BLUE       11
#define   RGB_BLUE       0x0000ffL
#define COLOR_YELLOW     12
#define   RGB_YELLOW     0x808000L
#define COLOR_CYAN       13
#define   RGB_CYAN       0x008080L
#define COLOR_MAGENTA    14
#define   RGB_MAGENTA    0x800080L
#define COLOR_WHITE      15
#define   RGB_WHITE      0X555555L
#define COLOR_DK_RED     16
#define   RGB_DK_RED     0x400000L
#define COLOR_DK_GREEN   17
#define   RGB_DK_GREEN   0x004000L
#define COLOR_DK_BLUE    18
#define   RGB_DK_BLUE    0x000040L
#define COLOR_DK_YELLOW  19
#define   RGB_DK_YELLOW  0x202000L
#define COLOR_DK_CYAN    20
#define   RGB_DK_CYAN    0x002020L
#define COLOR_DK_MAGENTA 21
#define   RGB_DK_MAGENTA 0x200020L
#define COLOR_DIM_WHITE  22
#define   RGB_DIM_WHITE  0x202020L
#define COLOR_LT_RED     23
#define   RGB_LT_RED     0xff2020L
#define COLOR_LT_GREEN   24
#define   RGB_LT_GREEN   0x20ff20L
#define COLOR_LT_BLUE    25
#define   RGB_LT_BLUE    0x2020ffL
#define COLOR_LT_YELLOW  26
#define   RGB_LT_YELLOW  0x808020L
#define COLOR_LT_CYAN    27
#define   RGB_LT_CYAN    0x208080L
#define COLOR_LT_MAGENTA 28
#define   RGB_LT_MAGENTA 0x802080L
#define COLOR_BRT_WHITE  29
#define   RGB_BRT_WHITE  0xf8f8f8L
#define COLOR_PINK       30
#define   RGB_PINK       0xff2020L
#define COLOR_ORANGE     31
#define   RGB_ORANGE     0xffa500
#define COLOR_PURPLE     32
#define   RGB_PURPLE     0x500050
#define REGULAR_COLOR_COUNT COLOR_PURPLE + 1
#define COLOR_RANDOM     33  // One of the colors above, at random
#define COLOR_MEDIUM     34  // Random R,G,B values but balanced for medium brightness
#define COLOR_ANY        35  // Any R,G,B values at random (including dim and bright colors)
#define COLOR_COUNT      COLOR_ANY + 1

// To avoid memory fragmentation, all color names (including custom colors) are fixed-length character arrays at a uniform 10 chars each
#define COLOR_NAME_SIZE 10  // 10 chars including the trailing zero, so really just 9 chars
//                                    "123456789"; Can't be longer than this!
const char NAME_BLACK_OFF [] PROGMEM = "Black-Off";
const char NAME_RED       [] PROGMEM = "Red";
const char NAME_GREEN     [] PROGMEM = "Green";
const char NAME_BLUE      [] PROGMEM = "Blue";
const char NAME_YELLOW    [] PROGMEM = "Yellow";
const char NAME_CYAN      [] PROGMEM = "Cyan";
const char NAME_MAGENTA   [] PROGMEM = "Magenta";
const char NAME_WHITE     [] PROGMEM = "White";
const char NAME_DK_RED    [] PROGMEM = "Dk Red";
const char NAME_DK_GREEN  [] PROGMEM = "Dk Green";
const char NAME_DK_BLUE   [] PROGMEM = "Dk Blue";
const char NAME_DK_YELLOW [] PROGMEM = "Mustard";
const char NAME_DK_CYAN   [] PROGMEM = "Teal";
const char NAME_DK_MAGENTA[] PROGMEM = "Fushia";
const char NAME_DIM_WHITE [] PROGMEM = "Dim White";
const char NAME_LT_RED    [] PROGMEM = "Lt Red";
const char NAME_LT_GREEN  [] PROGMEM = "Lt Green";
const char NAME_LT_BLUE   [] PROGMEM = "Lt Blue";
const char NAME_LT_YELLOW [] PROGMEM = "Lt Yellow";
const char NAME_LT_CYAN   [] PROGMEM = "Sea";
const char NAME_LT_MAGENTA[] PROGMEM = "Lavender";
const char NAME_BRT_WHITE [] PROGMEM = "Brt White";
const char NAME_PINK      [] PROGMEM = "Pink";
const char NAME_ORANGE    [] PROGMEM = "Orange";
const char NAME_PURPLE    [] PROGMEM = "Purple";
const char NAME_RANDOM    [] PROGMEM = "Random";
const char NAME_MEDIUM    [] PROGMEM = "Medium";
const char NAME_ANY       [] PROGMEM = "Any";
// Factory Default Names for Custom Colors 0-7, may end up being changed by user, stored in EEPROM
const char NAME_CORAL     [] PROGMEM = "Coral";
const char NAME_OLIVE     [] PROGMEM = "Olive";
const char NAME_WARM_WITE [] PROGMEM = "Warm Wite";
const char NAME_TURQUOISE [] PROGMEM = "Turquoise";
const char NAME_LIME      [] PROGMEM = "Lime";
const char NAME_GOLD      [] PROGMEM = "Gold";
const char NAME_INDIGO    [] PROGMEM = "Indigo";
const char NAME_CRIMSON   [] PROGMEM = "Crimson";

PGM_P const COLOR_NAMES[] PROGMEM =
{
  NAME_BLACK_OFF,
  NAME_RED,
  NAME_GREEN,
  NAME_BLUE,
  NAME_YELLOW,
  NAME_CYAN,
  NAME_MAGENTA,
  NAME_WHITE,
  NAME_DK_RED,
  NAME_DK_GREEN,
  NAME_DK_BLUE,
  NAME_DK_YELLOW,
  NAME_DK_CYAN,
  NAME_DK_MAGENTA,
  NAME_DIM_WHITE,
  NAME_LT_RED,
  NAME_LT_GREEN,
  NAME_LT_BLUE,
  NAME_LT_YELLOW,
  NAME_LT_CYAN,
  NAME_LT_MAGENTA,
  NAME_BRT_WHITE,
  NAME_PINK,
  NAME_ORANGE,
  NAME_PURPLE,
  NAME_RANDOM,
  NAME_MEDIUM,
  NAME_ANY
};

/*
  char customColorName1[COLOR_NAME_SIZE] = {'/0         '};
  char customColorName2[COLOR_NAME_SIZE] = {'/0         '};
  char customColorName3[COLOR_NAME_SIZE] = {'/0         '};
  char customColorName4[COLOR_NAME_SIZE] = {'/0         '};
  char customColorName5[COLOR_NAME_SIZE] = {'/0         '};
  char customColorName6[COLOR_NAME_SIZE] = {'/0         '};
  char customColorName7[COLOR_NAME_SIZE] = {'/0         '};
  char customColorName8[COLOR_NAME_SIZE] = {'/0         '};
*/
// To avoid memory fragmentation, all color names (including custom colors) are fixed-length character arrays at a uniform 10 chars each.
// 10 chars includes the trailing zero, so really just 9 chars.
//                                        "123456789"; Can't be longer than this!
char customColorName1[COLOR_NAME_SIZE] = {"         "};
char customColorName2[COLOR_NAME_SIZE] = {"         "};
char customColorName3[COLOR_NAME_SIZE] = {"         "};
char customColorName4[COLOR_NAME_SIZE] = {"         "};
char customColorName5[COLOR_NAME_SIZE] = {"         "};
char customColorName6[COLOR_NAME_SIZE] = {"         "};
char customColorName7[COLOR_NAME_SIZE] = {"         "};
char customColorName8[COLOR_NAME_SIZE] = {"         "};

#define NUM_CUSTOM_COLORS 8
char *customColorNames[NUM_CUSTOM_COLORS] =
{
  customColorName1,
  customColorName2,
  customColorName3,
  customColorName4,
  customColorName5,
  customColorName6,
  customColorName7,
  customColorName8
};

// ****************
// **  Patterns  **
// ****************
// 0-31 are reserved for the Effects List in menus
#define PATTERN_BASE  40 // Starting number for patterns, Equal to PAT_NONE
#define PAT_NONE      40
#define PAT_DOTS      41
#define PAT_GRADIENT  42
#define PAT_FADE      43
#define PAT_FWDCHASE  44
#define PAT_REVCHASE  45
#define PAT_SPARKLE   46
#define PAT_DISSOLVE  47
#define PAT_PAUSE     48
#define PATTERN_LAST  48 // Highest numbered pattern, should also be equal to PATTERN_BASE + PATTERN_COUNT
#define PATTERN_COUNT 8

#define PATTERN_NAME_SIZE 10  // 10 chars including the trailing zero, so really just 9 chars
//                                    "123456789"; Can't be longer than this!
const char NAME_NONE     [] PROGMEM = "None";
const char NAME_DOTS     [] PROGMEM = "Dots";
const char NAME_GRADIENT [] PROGMEM = "Gradient";
const char NAME_FADE     [] PROGMEM = "Fade";
const char NAME_FWDCHASE [] PROGMEM = "Fwd Chase";
const char NAME_REVCHASE [] PROGMEM = "Rev Chase";
const char NAME_SPARKLE  [] PROGMEM = "Sparkle";
const char NAME_DISSOLVE [] PROGMEM = "Dissolve";
const char NAME_PAUSE    [] PROGMEM = "Pause";
const char NAME_BLANK    [] PROGMEM = "Blank";
//const char NAME_ANY      [] PROGMEM = "Any";

//PROGMEM const char *PATTERN_NAMES[] =
PGM_P const PATTERN_NAMES[] PROGMEM =
{
  NAME_NONE,
  NAME_DOTS,
  NAME_GRADIENT,
  NAME_FADE,
  NAME_FWDCHASE,
  NAME_REVCHASE,
  NAME_SPARKLE,
  NAME_DISSOLVE,
  NAME_PAUSE,
};

#define EFFECT_SIZE  16  // Number of bytes used

// ******************
// ******************
// **  Effects     **
// **  Class       **
// **  Definition  **
// ******************
// ******************
class effect
{
  public:
    effect();  // Initial Constructor
    effect(byte thePattern); // Normal Constructor, pattern required
    byte pattern();
    void pattern(byte thePattern);
    void fetchPatternName();
    byte time();
    void time(byte theTime);
    byte speed();
    void speed(byte theSpeed);
    byte size();
    void size(byte theSize);
    byte count();
    void count(byte theCount);
    byte color(byte whichColor);
    CRGB colorRGB  (byte whichColor);
    CRGB fetchColor (byte whichColor);
    void fetchColorName (byte whichColor);
    void color(byte whichColor, byte colorIndex);
    byte backColor();
    CRGB backColorRGB  ();
    void fetchBackColorName ();
    void backColor(byte colorIndex);
    byte minTime();
    byte maxTime();
    byte minSpeed();
    byte maxSpeed();
    byte minSize();
    byte maxSize();
    byte minCount();
    byte maxCount();
    void save(byte effectIndex);
    void read(byte effectIndex);
    void printEffectDetails();
    void setDefaults();
  private:
    byte _pattern;
    byte _time;
    byte _speed;
    byte _size;
    byte _count;
    byte _color[6];
}; // End effect class

#define FADE_STEPS    32

// defined here, because they are used by the Effect class which follows
void fetchPatternName(byte patternIndex);
void fetchColorName(byte colorIndex);

#define NUM_EFFECTS 32  // Maximum Number

byte effectCount = 0;
byte curEffect = 0;
class effect theEffect;

//CRGB   customColors[0];
#define CUSTOM_OFFSET 1    // Color 0 is a regular color (black)
//                         // Colors 1-8 are Custom, but array holds elements 0 thru 7
//                         // *THUS* we need this offset!
CRGB colorsRGB[REGULAR_COLOR_COUNT];    // The last "normal" color before Random/Medium/Any
#define COLOR_RGB_SIZE 4

//#define COLOR_NAME_SIZE 10  // 10 chars including the trailing zero, so really just 9 chars
//                                        "123456789"; Can't be longer than this!

// *****************
// **  HeartBeat  **
// *****************
//#define       heartLED        13    // On-Board LED on Digital Pin 13
// Pro-Minis do not have an On-Board LED
// The TX Led on Pin 1 could be used instead, but might intefere with an upload
//   so use at your own risk.
#define       heartLED    20 // aka A2
//  Chose this one due to its proximity to Gnd, VCC, and 21/A3 used for pixel data.
//    Can be changed if desired.
#define       heartRate       60    // Beats-Per-Minute
#define       heartTime       ((60000 / heartRate) / 8)
unsigned long heartNextBeat = millis() + heartTime;
bool          heartState    = false;
byte          heartTic      = 0;

// ***********************************
// **  EEPROM Configuration Data    **
// ***********************************
byte curConfig =            0;
#define MY_LUCKY_NUMBER     77
// Written into EEPROM location zero, indicates a [probably] valid ws2811 xmas config
// A zero means this board has never been initialized for this program
// Any other value indicates likely corruption
#define CONFIG_VERSION      2
// Start Address of Configuration data in EEPROM
// Byte #1 is the config version.  If significant changes are made to config code, the stored config may need to be updated
// A brand new Arduino should have a zero there, meaning we need to set up factory defaults

// Addresses (offsets) in EEPROM where this data is stored
#define ADDR_LUCKY_NUMBER   0
#define ADDR_CONFIG_VERSION 1
#define ADDR_NUM_LEDS       2
#define ADDR_EFFECTS        4  // Therefore, effects are stored starting at location 4
#define ADDR_COLORS        (ADDR_EFFECTS + (NUM_EFFECTS * EFFECT_SIZE)) // Should = 516: 32 = future possible max effects, 16 = size of an effect structure
#define ADDR_NAMES         (ADDR_COLORS + (NUM_CUSTOM_COLORS * COLOR_RGB_SIZE))  // Should = 548: 16 = future possible custom colors, 4 = size of a long, to hold the color

// *****************
// **  M I S C    **
// *****************
char strBuffer[17]; //General purpose buffer for getting strings out of Progmem.  17 char 'cause should never need to do longer than 1 lcd line
unsigned long curMillis = millis();
bool running = true;

// ***********************
// ***********************
// ****  EFFECT CLASS ****
// ****  PROCEDURES   ****
// ****  BEGIN        ****
// ****  HERE         ****
// ***********************
// ***********************

// Plain constructor, no parameters
effect::effect()
{
  _pattern = PAT_NONE;
  //_time = 1;
  //_speed = 1;
  //_size = 1;
  //_count = 0;
  //_color[0] = COLOR_RED;
  //_color[1] = COLOR_YELLOW;
  //_color[2] = COLOR_GREEN;
  //_color[3] = COLOR_CYAN;
  //_color[4] = COLOR_BLUE;
  //_color[5] = COLOR_MAGENTA;
  setDefaults();
}
// Constructor specifying a pattern
effect::effect(byte thePattern) // Constructor, pattern required
{
  pattern(thePattern);
}

byte effect::pattern() {
  return _pattern;
}
void effect::pattern(byte thePattern)
{
  if ((thePattern >= PATTERN_BASE) && (thePattern <= PATTERN_LAST))
  {
    _pattern = thePattern;
    if (_time < minTime())  {
      _time = minTime();
    }
    if (_time > maxTime())  {
      _time = maxTime();
    }
    if (_time < minSpeed()) {
      _time = minSpeed();
    }
    if (_time > maxSpeed()) {
      _time = maxSpeed();
    }
    if (_time < minSize())  {
      _time = minSize();
    }
    if (_time > maxSize())  {
      _time = maxSize();
    }
    if (_time < minCount()) {
      _time = minCount();
    }
    if (_time > maxCount()) {
      _time = maxCount();
    }

    // Anytime the pattern # is set (even if reset to the same #) set parameters to default
    setDefaults();
  }
}
void effect::fetchPatternName()
{
  ::fetchPatternName(_pattern);
}
byte effect::time() {
  return _time;
}
void effect::time(byte theTime)
{
  if (theTime < minTime())  {
    theTime = minTime();
  }
  if (theTime > maxTime())  {
    theTime = maxTime();
  }
  _time = theTime;
}
byte effect::speed() {
  return _speed;
}
void effect::speed(byte theSpeed)
{
  if (theSpeed < minSpeed())  {
    theSpeed = minSpeed();
  }
  if (theSpeed > maxSpeed())  {
    theSpeed = maxSpeed();
  }
  _speed = theSpeed;
}
byte effect::size() {
  return _size;
}
void effect::size(byte theSize)
{
  if (theSize < minSize())  {
    theSize = minSize();
  }
  if (theSize > maxSize())  {
    theSize = maxSize();
  }
  _size = theSize;
}
byte effect::count() {
  return _count;
}
void effect::count(byte theCount)
{
  if (theCount < minCount())  {
    theCount = minCount();
  }
  if (theCount > maxCount())  {
    theCount = maxCount();
  }
  _count = theCount;
}
// Note to self:
//   'colorIndex' refers to the color number, 0 to 35 (BLACK to ANY)
//   'whichColor' refers to which of the 6 color slots (0-5) in an effect
byte effect::color(byte whichColor) {
  return _color[whichColor];
}
CRGB effect::colorRGB(byte whichColor) {
  return fetchColor(_color[whichColor]);
}
void effect::fetchColorName(byte whichColor)
{
  ::fetchColorName(_color[whichColor]);
}
void effect::color(byte whichColor, byte color)
{
  if (whichColor < 6)
  {
    if (color < COLOR_COUNT)
    {
      _color[whichColor] = color;
    }
  }
}
byte effect::backColor() {
  return _color[5];
}
CRGB effect::backColorRGB() {
  return fetchColor(_color[5]);
}
void effect::fetchBackColorName()
{
  fetchColorName(_color[5]);
}
void effect::backColor(byte colorIndex)
{
  if (colorIndex < COLOR_COUNT)
  {
    _color[5] = colorIndex;
  }
}
byte effect::minTime()
{
  byte ret = 0;
  if (_pattern == PAT_PAUSE) {
    ret = 1;
  }
  if (_pattern == PAT_GRADIENT) {
    ret = 3;
  }
  return ret;
}
byte effect::maxTime()
{
  byte ret = 240;

  return ret;
}
byte effect::minSpeed()
{
  byte ret = 1;
  return ret;
}
byte effect::maxSpeed()
{
  byte ret = 10;
  if (_pattern == PAT_PAUSE) {
    ret = 1;
  }
  return ret;
}
byte effect::minSize()
{
  byte ret = 1;
  return ret;
}
byte effect::maxSize()
{
  byte ret = 255;
  if (ret > num_leds) {
    ret = num_leds;
  }
  if (_pattern == PAT_PAUSE) {
    ret = 1;
  }
  return ret;
}
byte effect::minCount()
{
  byte ret = 1;
  if (_pattern == PAT_PAUSE) {
    ret = 0;
  }
  return ret;
}
byte effect::maxCount()
{
  byte ret = 6;
  if (_pattern == PAT_PAUSE) {
    ret = 0;
  }
  return ret;
}
void effect::save(byte effectIndex)
{
  int addr = ADDR_EFFECTS + (int)effectIndex * EFFECT_SIZE;

  DEBUG_Print_Info(F("Saving Effect Pattern to slot "));
  DEBUG_Print_Info(effectIndex);;
  DEBUG_Print_Info(F(" address "));
  DEBUG_Print_Info(addr);
  DEBUG_Print_Info(F(" is "));
  DEBUG_Print_Info(_pattern);
  DEBUG_Print_Info(F(" - "));
  ::fetchPatternName(_pattern);
  DEBUG_Println_Info(strBuffer);

  EEPROM.writeByte(addr + 0, _pattern);
  EEPROM.writeByte(addr + 1, _time);
  EEPROM.writeByte(addr + 2, _speed);
  EEPROM.writeByte(addr + 3, _size);
  EEPROM.writeByte(addr + 4, _count);
  EEPROM.writeByte(addr + 5, _color[0]);
  EEPROM.writeByte(addr + 6, _color[1]);
  EEPROM.writeByte(addr + 7, _color[2]);
  EEPROM.writeByte(addr + 8, _color[3]);
  EEPROM.writeByte(addr + 9, _color[4]);
  EEPROM.writeByte(addr + 10, _color[5]);
  delay(250);
}
void effect::read(byte effectIndex)
{
  int addr = ADDR_EFFECTS + (int)effectIndex * EFFECT_SIZE;
  pattern (EEPROM.readByte (addr + 0));
  time    (EEPROM.readByte (addr + 1));
  speed   (EEPROM.readByte (addr + 2));
  size    (EEPROM.readByte (addr + 3));
  count   (EEPROM.readByte (addr + 4));
  color(0, EEPROM.readByte (addr + 5));
  color(1, EEPROM.readByte (addr + 6));
  color(2, EEPROM.readByte (addr + 7));
  color(3, EEPROM.readByte (addr + 8));
  color(4, EEPROM.readByte (addr + 9));
  color(5, EEPROM.readByte (addr + 10));
}
void effect::printEffectDetails()
{
  byte z;

  fetchPatternName();
  DEBUG_Print_Info  (F(" Effect is "));
  DEBUG_Print_Info  (_pattern);
  DEBUG_Print_Info  (F(" - "));
  DEBUG_Println_Info(strBuffer);
  DEBUG_Print_Info  (F("      Time:"));
  DEBUG_Println_Info(_time);
  DEBUG_Print_Info  (F("      Size:"));
  DEBUG_Println_Info(_size);
  DEBUG_Print_Info  (F("     Speed:"));
  DEBUG_Println_Info(_speed);
  DEBUG_Print_Info  (F("     Count:"));
  DEBUG_Println_Info(_count);

  z = 0;
  DEBUG_Print_Info  (F("Color 1 is "));
  DEBUG_Print_Info  (_color[z]);
  DEBUG_Print_Info  (F(" - "));
  fetchColorName(z);
  DEBUG_Println_Info (strBuffer);

  z = 1;
  DEBUG_Print_Info  (F("Color 2 is "));
  DEBUG_Print_Info  (_color[z]);
  DEBUG_Print_Info  (F(" - "));
  fetchColorName(z);
  DEBUG_Println_Info (strBuffer);

  z = 2;
  DEBUG_Print_Info  (F("Color 3 is "));
  DEBUG_Print_Info  (_color[z]);
  DEBUG_Print_Info  (F(" - "));
  fetchColorName(z);
  DEBUG_Println_Info (strBuffer);

  z = 3;
  DEBUG_Print_Info  (F("Color 4 is "));
  DEBUG_Print_Info  (_color[z]);
  DEBUG_Print_Info  (F(" - "));
  fetchColorName(z);
  DEBUG_Println_Info (strBuffer);

  z = 4;
  DEBUG_Print_Info  (F("Color 5 is "));
  DEBUG_Print_Info  (_color[z]);
  DEBUG_Print_Info  (F(" - "));
  fetchColorName(z);
  DEBUG_Println_Info (strBuffer);

  z = 5;
  DEBUG_Print_Info  (F("Color 6 is "));
  DEBUG_Print_Info  (_color[z]);
  DEBUG_Print_Info  (F(" - "));
  fetchColorName(z);
  DEBUG_Println_Info (strBuffer);

} // end printEffectDetails
void effect::setDefaults()
{
  // Standard defaults, may possibly get overriden below
  _time  = 30; // Seconds
  _speed = 5; // Medium
  _size  = 6;
  _count = 6;
  _color[0] = COLOR_RED;
  _color[1] = COLOR_YELLOW;
  _color[2] = COLOR_GREEN;
  _color[3] = COLOR_CYAN;
  _color[4] = COLOR_BLUE;
  _color[5] = COLOR_MAGENTA;

  switch (theEffect.pattern())
  {
    case PAT_NONE:
      //DEBUG_Print_Trace(F("Shouldn't get here!  Why does new effect have pattern of none?"));
      // Standard defaults are fine
      break;
    case PAT_DOTS:
      // set time to zero
      _time = 0;
      break;
    case PAT_GRADIENT:
      // Increase Size, based on num_leds, set time to zero
      _time = 0;
      _size = num_leds / 6;
      break;
    case PAT_FWDCHASE:
      // Standard defaults are fine
      break;
    case PAT_REVCHASE:
      // Standard defaults are fine
      break;
    case PAT_FADE:
      // Standard defaults are fine
      break;
    case PAT_SPARKLE:
      // Better colors for this effect
      _color[5] = COLOR_DK_MAGENTA; // Background
      _color[0] = COLOR_BRT_WHITE;
      _color[1] = COLOR_WHITE;
      _color[2] = COLOR_DIM_WHITE;
      _color[3] = COLOR_BRT_WHITE;
      _color[4] = COLOR_WHITE;
      break;
    case PAT_DISSOLVE:
      // Standard defaults are fine
      break;
    case PAT_PAUSE:
      // Standard defaults are fine
      // time is only parameter that even matters anyway
      break;
    default:
      DEBUG_Print_Trace(F("Shouldn't get here!  No Defaults for new pattern!"));
  } // end switch
} // end new effect set defaults
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// END OF EFFECT CLASS PROPERTIES AND METHODS
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


// ***********************
// ***********************
// ****  REGULAR      ****
// ****  PROCEDURES   ****
// ****  BEGIN        ****
// ****  HERE         ****
// ***********************
// ***********************

// *********************************
// *********************************
// ***                           ***
// ***   F E T C H   C O L O R   ***
// ***                           ***
// *********************************
// *********************************
CRGB fetchColor(byte colorIndex)
{
  CRGB ret;
  if (colorIndex <= REGULAR_COLOR_COUNT)
  {
    ret = colorsRGB[colorIndex];
  }
  else
  {
    if (colorIndex == COLOR_RANDOM)
    {
      byte rndclr = random(COLOR_COUNT - 2);
      ret = colorsRGB[rndclr];
    }
    if (colorIndex == COLOR_MEDIUM)  // Any R,G,B values at random, but balanced for medium brightness
    {
      ret.r = random(255);
      ret.g = random(255 - ret.r);
      ret.b = 255 - ret.r - ret.g;
    }
    if (colorIndex == COLOR_ANY)  // Any R,G,B values at random (will include dim and bright colors)
    {
      ret.r = random(255);
      ret.g = random(255);
      ret.b = random(255);
    }
  } // END custom color

  return ret;
} // END CRGB fetchColor(byte colorIndex)


// ***********************
// ***********************
// ****               ****
// ****   S E T U P   ****
// ****               ****
// ***********************
// ***********************
void setup() {
  Serial.begin(9600);
  DEBUG_Println_Trace(F("setup()"));
  EEPROM.setMemPool(0, 4096);
  EEPROM.setMaxAllowedWrites(8192);

  //FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, num_leds);
  FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, num_leds);
  // Buffer is 'empty' and contains all zeros at this point
  // Therefore, this will set all LEDs to black/off
  FastLED.show();
  //DEBUG_Println_Trace(F("Setup Here 1"));

  checkConfig();
  if (curConfig != CONFIG_VERSION)
  {
    // At some future point, may need to check if it has a config, but is an older version that needs updating
    // But don't need to worry about that - yet.

    DEBUG_Println_Trace(F("WTF?  Why doesn't config version match?"));
    //infiniteLoop();

    setDefaults();
    //writeConfig();
    readConfig();
    dumpConfig();
  }
  else // has config and is correct version
  {
    //setTestDefaults();      // TEMPORARY!  FOR DEBUGGING PATTERNS AND EFFECTS

    readConfig();
    DEBUG_Println_Trace(F("Configuration read from EEPROM appears valid!"));
    //dumpConfig();
  }

  initColors(ADDR_COLORS);

  //DEBUG_Println_Trace(F("Setup Here 2"));
  theEffect.read(0);
  DEBUG_Println_Trace(F("End setup()"));
} // End void setup()

// *******************
// *******************
// ***             ***
// ***   L O O P   ***
// ***             ***
// *******************
// *******************
void loop() {
  //DEBUG_Println_Trace(F("loop()"));
  curMillis = millis();
  //delay(100); // FOR DEBUGGING, ALLOW TIME FOR SERIAL.PRINT STATUS BEFORE CONTINUING

  playEffect(theEffect);

  curEffect ++;
  if (curEffect >= effectCount)
  {
    curEffect = 0;
  }

  theEffect.read(curEffect);

} // End void loop()

// ********************
// ***   I D L E    ***
// ********************
void idleUntil(unsigned long untilThen)
{
  curMillis = millis();
  while (curMillis < untilThen)
  {
    curMillis = millis();
    if (curMillis > heartNextBeat)
    {
      heartTic++;
      heartTic &= 7;
      heartState = ((heartTic == 1) || (heartTic == 3));
      digitalWrite(heartLED, heartState);
      heartNextBeat = curMillis + heartTime;
    }

  }
} // End void idleUntil(unsigned long untilThen)

void initColors(int address)
{
  //int addr = ADDR_COLORS;
  CRGB clr;

  // Color # 0
  colorsRGB[COLOR_BLACK_OFF]  = hex2color(RGB_BLACK_OFF);

  // Colors # 1-9 ** The custom colors
  // Read from EEPROM
  for (int i = 0; i < NUM_CUSTOM_COLORS; i++)
  {
    // ignore/skip addr + 0  (Value stored as a long, byte 0 = 0, byte 1 = 4, byte 2 = g, byte 3 = b)
    clr.r = EEPROM.readByte(address + 1);
    clr.g = EEPROM.readByte(address + 2);
    clr.b = EEPROM.readByte(address + 3);
    colorsRGB[i + 1] = clr;
    address += COLOR_RGB_SIZE;
  }

  // Colors # 9 thru whatever
  colorsRGB[COLOR_RED]        = hex2color(RGB_RED);
  colorsRGB[COLOR_GREEN]      = hex2color(RGB_GREEN);
  colorsRGB[COLOR_BLUE]       = hex2color(RGB_BLUE);
  colorsRGB[COLOR_YELLOW]     = hex2color(RGB_YELLOW);
  colorsRGB[COLOR_CYAN]       = hex2color(RGB_CYAN);
  colorsRGB[COLOR_MAGENTA]    = hex2color(RGB_MAGENTA);
  colorsRGB[COLOR_WHITE]      = hex2color(RGB_WHITE);
  colorsRGB[COLOR_DK_RED]     = hex2color(RGB_DK_RED);
  colorsRGB[COLOR_DK_GREEN]   = hex2color(RGB_DK_GREEN);
  colorsRGB[COLOR_DK_BLUE]    = hex2color(RGB_DK_BLUE);
  colorsRGB[COLOR_DK_YELLOW]  = hex2color(RGB_DK_YELLOW);
  colorsRGB[COLOR_DK_CYAN]    = hex2color(RGB_DK_CYAN);
  colorsRGB[COLOR_DK_MAGENTA] = hex2color(RGB_DK_MAGENTA);
  colorsRGB[COLOR_DIM_WHITE]  = hex2color(RGB_DIM_WHITE);
  colorsRGB[COLOR_LT_RED]     = hex2color(RGB_LT_RED);
  colorsRGB[COLOR_LT_GREEN]   = hex2color(RGB_LT_GREEN);
  colorsRGB[COLOR_LT_BLUE]    = hex2color(RGB_LT_BLUE);
  colorsRGB[COLOR_LT_YELLOW]  = hex2color(RGB_LT_YELLOW);
  colorsRGB[COLOR_LT_CYAN]    = hex2color(RGB_LT_CYAN);
  colorsRGB[COLOR_LT_MAGENTA] = hex2color(RGB_LT_MAGENTA);
  colorsRGB[COLOR_BRT_WHITE]  = hex2color(RGB_BRT_WHITE);
  colorsRGB[COLOR_PINK]       = hex2color(RGB_PINK);
  colorsRGB[COLOR_ORANGE]     = hex2color(RGB_ORANGE);
  colorsRGB[COLOR_PURPLE]     = hex2color(RGB_PURPLE);
} // End void InitColors()

CRGB hex2color(long hexVal)
{
  CRGB ret;
  long l;
  ret.b = hexVal & 0x0000ff;
  l = (hexVal & 0x00ff00) >> 8;
  ret.g = l;
  l = (hexVal & 0xff0000) >> 16;
  ret.r = l;
  return ret;
} // end hex2color

long color2hex(CRGB theColor)
{
  long ret = theColor.r << 16;
  ret += theColor.g << 8;
  ret += theColor.b;
  return ret;
} // end color2hex

byte getEffectPattern(byte effectIndex)
{
  byte ret;
  int addr = ADDR_EFFECTS + effectIndex * EFFECT_SIZE;
  ret = EEPROM.readByte(addr);
  return ret;
}

void printCustomColorDetails(byte colorIndex)
{
  DEBUG_Print_Info  (F("Custom Color "));
  DEBUG_Print_Info  (colorIndex + CUSTOM_OFFSET);
  DEBUG_Print_Info  (F(" '"));
  fetchColorName(colorIndex + CUSTOM_OFFSET);
  DEBUG_Print_Info  (strBuffer);
  DEBUG_Print_Info  (F("' = 0x"));
  DEBUG_HexPrint_Info  (colorsRGB[colorIndex + CUSTOM_OFFSET].r);
  //Serial.print (", ");
  DEBUG_HexPrint_Info  (colorsRGB[colorIndex + CUSTOM_OFFSET].g);
  //Serial.print (", ");
  DEBUG_HexPrint_Info  (colorsRGB[colorIndex + CUSTOM_OFFSET].b);
  DEBUG_Println_Info  ();
} // end printCustomColorDetails


void checkConfig()
{
  byte cc = 0;
  int n = EEPROM.readByte(ADDR_LUCKY_NUMBER);

  if (n == MY_LUCKY_NUMBER)    // Byte 0 should = My Favorite Magic Number!  Means EEPROM was written with a WS8211 Xmas program
  {
    n = EEPROM.readByte(ADDR_NUM_LEDS);
    if ((n > 0) && (n <= MAX_LEDS))    // Byte 2 is number of LEDs
    {
      cc = EEPROM.readByte(ADDR_CONFIG_VERSION);
      if (cc == CONFIG_VERSION)
      {
        n = EEPROM.readByte(ADDR_EFFECTS);
        // Check the first byte of the effects list,
        //  first byte will be pattern of first effect
        //   if not PATTERN_NONE
        if ((n > PAT_NONE) && (n <= PATTERN_LAST))
        {
          //n = EEPROM.readByte(ADDR_NAMES);
          n = EEPROM.readByte(ADDR_NAMES);
          // Check the first byte of the custom color names,
          //  first byte will first letter of first color name
          //   if not a zero
          if (n)
          {
            curConfig = cc;
          } // first custom color has a name of at least one character
        } // fifth byte contains a valid pattern number
      } // second byte matches config version
    } // third and fourth byte is zero (for now)
  } // first byte is my favorite magic number (77)
} // end void checkConfig

// WRITE CONFIG
/*
  void writeConfig()
  {
  // Entire Config should all be written only in the case of a factory defaults reset

  setLCDbacklight(LCD_RED);
  lcd.setCursor(0,0);
  strcpy_P(strBuffer, (char*)FACTORY_DEFAULTS);
  lcd.print (strBuffer);

  lcd.setCursor(0,1);
  lcd.print("Writing:       ");

  EEPROM.writeByte(ADDR_LUCKY_NUMBER,   MY_LUCKY_NUMBER);
  EEPROM.writeByte(ADDR_CONFIG_VERSION, CONFIG_VERSION);    // See Notes in CheckConfig()
  EEPROM.writeInt (ADDR_NUM_LEDS,       num_leds);

  for (byte i=0; i < NUM_EFFECTS; i++)
  {
    writeEffect(i);
    delay(100);
  }


  for (byte i = 0; i < NUM_CUSTOM_COLORS; i++)
  {
    writeCustomColorRGB(i);
    delay(100);
  }
  for (byte i = 0; i < NUM_CUSTOM_COLORS; i++)
  {
    writeCustomColorName(i);
    delay(100);
  }

  setLCDbacklight(LCD_GREEN);
  lcd.setCursor(0,0);
  lcd.print ("  DEFAULTS SET  ");
  lcd.setCursor(0,1);
  lcd.print ("                ");


  } // End writeConfig
*/

void dumpConfig()
{
  for (byte i = 0; i < NUM_EFFECTS; i++)
  {
    DEBUG_Print_Info(F("Effect # "));
    DEBUG_Println_Info(i);
    theEffect.read(i);
    theEffect.printEffectDetails();
  }
  for (byte i = 0; i < NUM_CUSTOM_COLORS; i++)
  {
    printCustomColorDetails(i);
  }
}

void readConfig()
{
  int addr;
  byte ep;
  if (EEPROM.readByte(ADDR_LUCKY_NUMBER) == MY_LUCKY_NUMBER)
  {
    if (EEPROM.readByte(ADDR_CONFIG_VERSION) == CONFIG_VERSION)
    {
      num_leds = EEPROM.readInt(ADDR_NUM_LEDS);
      if ((num_leds > 0) && (num_leds <= MAX_LEDS))
      {
        if (EEPROM.readByte(ADDR_EFFECTS) != PAT_NONE)
        {
          DEBUG_Println_Info(F("Configuration read from EEPROM appears valid"));
          DEBUG_Print_Info  (F("LED Count = "));
          DEBUG_Println_Info(num_leds);
          for (byte i = 0; i < NUM_EFFECTS; i++)
          {
            //readEffect(i);
            //delay(50);
            addr = ADDR_EFFECTS + (int)i * EFFECT_SIZE;
            ep = getEffectPattern(i);
            DEBUG_Print_Info(F("Effect Pattern in slot "));
            DEBUG_Print_Info(i);;
            DEBUG_Print_Info(F(" address "));
            DEBUG_Print_Info(addr);
            DEBUG_Print_Info(F(" is "));
            DEBUG_Print_Info(ep);
            DEBUG_Print_Info(F(" - "));
            fetchPatternName(ep);
            DEBUG_Println_Info(strBuffer);

            if ((ep == PAT_NONE) && (effectCount == 0))
            {

              effectCount = i;
            }
          }
          for (byte i = 0; i < NUM_CUSTOM_COLORS; i++)
          {
            readCustomColorRGB(i);
            //delay(20);
          }
        }
        else // first effect.pattern != none
        {
          effectCount = 0;
        } // end check first effect.pattern is not none
        DEBUG_Print_Info(F("Effect Count = "));
        DEBUG_Println_Info(effectCount);
      }
      else // number of leds is invalid (=0 or >1024)
      {
        DEBUG_Println_Trace(F("Houston, we have a problem in readConfig() Number of LEDs is invalid"));
      } // end num_leds check
    }
    else // config version does NOT match
    {
      DEBUG_Println_Trace(F("Houston, we have a problem in readConfig() The config version doesn't match"));
    } // end config version check
  }
  else // first byte is not luck number, probably not a valid ws2811 xmas config
  {
    DEBUG_Println_Trace(F("Houston, we have a problem in readConfig() No Lucky Number"));
  } // end lucky number check
} // end readConfig

// WRITE EFFECT
/*
  void writeEffect(byte effectIndex)
  {
  int addr = ADDR_EFFECTS + effectIndex * EFFECT_SIZE;
  lcd.setCursor(8,1);
  lcd.print(addr);
  effectList[effectIndex].save(addr);
  } // end writeEffect

  void readEffect(byte effectIndex)
  {
  int addr = ADDR_EFFECTS + effectIndex * EFFECT_SIZE;
  lcd.setCursor(8,1);
  lcd.print(addr);
  effectList[effectIndex].read(addr);
  } // end readEffect
*/

void writeCustomColorRGB(byte colorIndex)
{
  int addr = ADDR_COLORS + colorIndex * COLOR_RGB_SIZE;
  EEPROM.writeByte(addr + 1, colorsRGB[colorIndex + 1].r); // i + 1 because color 0 = black
  EEPROM.writeByte(addr + 2, colorsRGB[colorIndex + 1].g);
  EEPROM.writeByte(addr + 3, colorsRGB[colorIndex + 1].b);
} // end writeCustomColorRGB

void readCustomColorRGB(byte colorIndex)
{
  int addr = ADDR_COLORS + colorIndex * COLOR_RGB_SIZE;
  colorsRGB[colorIndex + 1].r = EEPROM.readByte(addr + 1); // i + 1 because color 0 = black
  colorsRGB[colorIndex + 1].g = EEPROM.readByte(addr + 2);
  colorsRGB[colorIndex + 1].b = EEPROM.readByte(addr + 3);
} // end readCustomColorRGB

void writeCustomColorName(byte customIndex)
{
  // Note this is the custom color index, which is zero based and goes from 0 to 7
  // (As opposed to the full color list, in which the custom colors are # 1 to 8)
  int addr = ADDR_NAMES + customIndex * COLOR_NAME_SIZE;
  for (byte i = 0; i < COLOR_NAME_SIZE; i++)
  {
    EEPROM.writeByte(addr + i, strBuffer[i]);
  }
} // end WriteCustomColorName

// fetchHexColorString(struct CRGB &theColor)
/*
  fetchHexColorString(struct CRGB &theColor)
  {
  String ret = "0x00";
  ret += String(theColor.r, HEX);
  ret += String(theColor.g, HEX);
  ret += String(theColor.b, HEX);
  return ret;
  } // end fetchHexColorString
*/

void fetchPatternName(byte patternIndex)
{
  // NOTE: patternIndex is no longer zero-based!  It now starts at PATTERN_BASE and goes to PATTERN_LAST, with the # of elements = to PATTERN_COUNT
  strcpy_P(strBuffer, (char*)pgm_read_word(&(PATTERN_NAMES[patternIndex - PATTERN_BASE]))); // Necessary casts and dereferencing, just copy.
}

void fetchColorName(byte colorIndex)
{
  // Note to self:
  //   'colorIndex' refers to the color number, 0 to 35 (BLACK to ANY)
  //   'whichColor' refers to which of the 6 color slots (0-5) in an effect
  if (colorIndex == COLOR_BLACK_OFF)
  {
    strcpy_P(strBuffer, (char*)pgm_read_word(&(COLOR_NAMES[colorIndex]))); // Necessary casts and dereferencing, just copy.
  }
  else
  {
    if (colorIndex < COLOR_RED)
    {
      strcpy_P(strBuffer, (char*)pgm_read_word(&(customColorNames[colorIndex]))); // Necessary casts and dereferencing, just copy.
    }
    else
    {
      strcpy_P(strBuffer, (char*)pgm_read_word(&(COLOR_NAMES[colorIndex]))); // Necessary casts and dereferencing, just copy.
    }
  }
}

// ************************
// ***  Play Effect   ***
// ************************
void playEffect(class effect &theEffect)
{

  DEBUG_Println_Trace(F("playEffect()"));
  //delay(100); // FOR DEBUGGING, ALLOW TIME FOR SERIAL.PRINT STATUS BEFORE CONTINUING

  theEffect.fetchPatternName();
  DEBUG_Print_Info(F("Play Effect #"));
  DEBUG_Print_Info(curEffect);
  DEBUG_Print_Info(F(" - Pattern #"));
  DEBUG_Print_Info(theEffect.pattern());
  DEBUG_Print_Info(F(" - "));
  DEBUG_Println_Info(strBuffer);

  strcpy_P(strBuffer, (char*)NAME_BLANK);
  theEffect.fetchPatternName();

  curMillis = millis();
  switch (theEffect.pattern())
  {
    case PAT_DOTS:
      playDotsPattern(theEffect);
      break;
    case PAT_GRADIENT:
      playGradientPattern(theEffect);
      break;
    case PAT_FADE:
      playFadePattern(theEffect);
      break;
    case PAT_FWDCHASE:
      playFwdChasePattern(theEffect);
      break;
    case PAT_REVCHASE:
      playRevChasePattern(theEffect);
      break;
    case PAT_SPARKLE:
      playSparklePattern(theEffect);
      break;
    case PAT_DISSOLVE:
      playDissolvePattern(theEffect);
      break;
    case PAT_PAUSE:
      playPausePattern(theEffect);
      break;
    default:
      DEBUG_Println_Trace(F("WTF?  Pattern Not Found "));

  } // end switch

} // end playEffect

// ************************
// ***   Dots Pattern   ***
// ************************
void playDotsPattern(class effect &theEffect)
{
  curMillis = millis();
  unsigned long endTime = curMillis + (unsigned long)theEffect.time() * 1000;
  int batch = theEffect.size() * theEffect.count();
  for (int p = 0; p < num_leds; p += batch)
  {
    for (byte c = 0; c < theEffect.count(); c++)
    {
      for (byte s = 0; s < theEffect.size(); s++)
      {
        int n = p + (c * theEffect.size()) + s;
        if (n < num_leds)
        {
          //DEBUG_Print_Info("Should increment ->" + String(n));
          leds[n] = theEffect.colorRGB(c);
          //DEBUG_Print_Info("LED #" + String(n) + " to Color#" + String(theEffect.color[c]) + " = ");
          //DEBUG_Print_Info(hexColorString(leds[n]) + " is ");
          //getColorName(theEffect.color[c], strBuffer);
          //DEBUG_Println_Info (strBuffer);
        }
      }
    }
  }
  FastLED.show();
  endTime = curMillis + theEffect.time() * 1000;  // time in seconds
  idleUntil(endTime);  // 100 because .time is in 1/10 second and millis is in 1/1000 second
} // end playDots

// ************************
// ***   Gradient       ***
// ************************
void playGradientPattern(class effect &theEffect)
{
  curMillis = millis();
  unsigned long endTime = curMillis + (unsigned long)theEffect.time() * 1000;
  int n = 0;
  CRGB newColor;
  byte c2;

  while (n < num_leds)
  {
    for (byte c = 0; c < theEffect.count(); c++)
    {
      c2 = (c + 1) % theEffect.count();
      for (byte f = 0; f < theEffect.size(); f++)
      {

        if (n < num_leds)
        {

          DEBUG_Print_Info (F("Color:"));
          DEBUG_Print_Info (c);
          DEBUG_Print_Info (F(" of "));
          DEBUG_Print_Info (theEffect.count());
          DEBUG_Print_Info (F("Step:"));
          DEBUG_Print_Info (f);
          DEBUG_Print_Info (F(" of "));
          DEBUG_Print_Info (theEffect.size());
          DEBUG_Print_Info (F(" LED:"));
          DEBUG_Println_Info (n);


          newColor = calcFade(theEffect.colorRGB(c), theEffect.colorRGB(c2), f, theEffect.size());
          leds[n] = newColor;
          n++;
        } // still < num_leds
      } // End for size loop
    } // END for color
  } // End while n < num_leds

  FastLED.show();

  idleUntil(endTime);

} // end playGradient

// ************************
// ***   Fade Pattern   ***
// ************************
void playFadePattern(class effect &theEffect)
{
  CRGB lastColor;
  CRGB newColor;
  curMillis = millis();
  unsigned long endTime = curMillis + (unsigned long)theEffect.time() * 1000;
  int delayAmt = (10 - theEffect.speed());

  while (curMillis < endTime)
  {
    for (byte c = 0; c < theEffect.count(); c++)
    {
      if (curMillis < endTime)
      {
        for (byte f = 0; f < FADE_STEPS; f++)
        {
          // Betweeen steps, set the 'last' color to a very unlikely color to avoid it being cached from the last step
          lastColor.r = 7; lastColor.g = 7; lastColor.b = 7;
          if (curMillis < endTime)
          {
            //byte q = FADE_STEPS - f;
            for (int p = 0; p < num_leds; p++)
            {
              // Caching: Don't do a bunch of floating point math if this pixel matches the last one
              if (leds[p] == lastColor)  // Is this not working?
                //if ((leds[p].r == lastColor.r) && (leds[p].g == lastColor.g) && (leds[p].b == lastColor.b))
              {
                leds[p] = newColor;
                //DEBUG_Print_Info(F("^"));
              }
              else
              {

                //DEBUG_Print_Info (F("STEP:"));
                //DEBUG_Print_Info (f);
                //DEBUG_Print_Info (F(" of "));
                //DEBUG_Print_Info (FADE_STEPS);
                //DEBUG_Print_Info (F(" LED:"));
                //DEBUG_Println_Info (p);

                lastColor = leds[p];
                newColor = calcFade(leds[p], theEffect.colorRGB(c), f, FADE_STEPS);
                leds[p] = newColor;
              }
            } // END for 0 to num_leds
            //DEBUG_Println_Info();
            FastLED.show();
            idleUntil(curMillis + delayAmt);
          }
          else
          {
            break;
          } // End if millis < endTime
        } // End for fade steps
      } // End if millis < endTime
    } // END for color
  } // End while < endtime
} // end playFade

// ************************
// ***   Fwd Chase      ***
// ************************
void playFwdChasePattern(class effect &theEffect)
{
  curMillis = millis();
  unsigned long endTime = curMillis + (unsigned long)theEffect.time() * 1000;  // 100 because .time is in 1/10 second and millis is in 1/1000 second
  int delayAmt = (15 - theEffect.speed()) * 15;
  while (curMillis < endTime)
  {
    CRGB tempColor = leds[0];
    for (int p = 1; p < num_leds; p++)
    {
      leds[p - 1] = leds[p];
    }
    leds[num_leds - 1] = tempColor;
    FastLED.show();
    idleUntil(curMillis + delayAmt);
  }
} // end playFwdChase

// ************************
// ***   Rev Chase      ***
// ************************
void playRevChasePattern(class effect &theEffect)
{
  curMillis = millis();
  unsigned long endTime = curMillis + (unsigned long)theEffect.time() * 1000;
  int delayAmt = (15 - theEffect.speed()) * 15;
  while (curMillis < endTime)
  {
    CRGB tempColor = leds[num_leds - 1];
    for (int p = num_leds - 1; p > 0; p--)
    {
      leds[p] = leds[p - 1];
    }
    leds[0] = tempColor;
    FastLED.show();
    idleUntil(curMillis + delayAmt);
  }
} // end playRevChase

// ************************
// ***   Sparkle        ***
// ************************
void playSparklePattern(class effect &theEffect)
{
  curMillis = millis();
  unsigned long endTime = curMillis + (unsigned long)theEffect.time() * 1000;
  int delayAmt = (10 - theEffect.speed());
  //int batch = num_leds / ((theEffect.count()) * (theEffect.size() + 3));
  int batch = num_leds / (theEffect.count() * 3);
  DEBUG_Print_Info(F("Sparkle 'Batch' size is "));
  DEBUG_Println_Info(batch);
  int listIndex = 0;

  while (curMillis < endTime)
  {

    for (int p = 0; p < listIndex; p++)
    {
      CRGB backColor;

      // Sixth/Last/[5] color is the background/default color
      //DEBUG_Print_Info(F(" are we crashing here "));
      //delay(50);
      backColor = theEffect.backColorRGB();

      //DEBUG_Print_Info("LED " + String(theList[p]) + " reset to ");
      //theEffect.backColorName(strBuffer);
      //DEBUG_Println_Info(strBuffer);

      leds[theList[p]] = backColor;
      //DEBUG_Println_Info(F(" nope. "));
    }


    listIndex = 0;
    //DEBUG_Println_Trace(F("Begin Main Sparkle Loop"));
    delay(100);
    // up to first five colors [0]-[4] are the sparkle colors


    for (byte c = 0; c < theEffect.count(); c++)
    {
      for (byte b = 0; b < batch; b++)
      {
        int r = random(num_leds);
        //DEBUG_Print_Info("LED " + String(r) + " set to ");
        //theEffect.colorName(c, strBuffer);
        //DEBUG_Println_Info(strBuffer);
        //delay(100);

        //DEBUG_Print_Info(F(" get it "));
        //delay(100);
        leds[r] = theEffect.colorRGB(c);
        //DEBUG_Println_Info(F(" set it "));

        theList[listIndex] = r;
        listIndex++;
        if (listIndex > (num_leds / 3))
        {
          DEBUG_Println_Trace(F("Uh Oh, You screwed up on the batch count!"));
        }
      } // END for batch
    } // END for color


    FastLED.show();
    delayAmt = theEffect.speed() * 10;
    idleUntil(curMillis + delayAmt);
  } // END while millis < endTime
} // end playSparkle

// ************************
// ***   Dissolve       ***
// ************************
void playDissolvePattern(class effect &theEffect)
{
  curMillis = millis();
  unsigned long endTime = curMillis + (unsigned long)theEffect.time() * 1000;
  unsigned long colorTime = theEffect.size() * 1000;
  int delayAmt = (10 - theEffect.speed());
  while (curMillis < endTime)
  {
    DEBUG_Print_Info(F("L1 curMills:"));
    DEBUG_Print_Info(curMillis);
    DEBUG_Print_Info(F("  endTime:"));
    DEBUG_Println_Info(endTime);

    for (byte c = 0; c < theEffect.count(); c++)
    {
      if (curMillis < endTime)
      {
        DEBUG_Print_Info(F("L2 curMills:"));
        DEBUG_Print_Info(curMillis);
        DEBUG_Print_Info(F("  endTime:"));
        DEBUG_Println_Info(endTime);

        randomizeTheList();
        for (int n = 0; n < num_leds; n++)
        {
          //DEBUG_Print_Info(F("Dissolving LED "));
          //DEBUG_Println_Info(theList[n]);

          leds[theList[n]] = theEffect.colorRGB(c);
          FastLED.show();
          idleUntil(curMillis + delayAmt);
        } // end loop thru 'theList' (of leds in random order
        if (curMillis < endTime)
        {
          idleUntil(curMillis + colorTime);
        } // < endTime
      } // end if curMillis < endTime;
    } // end Color Loop
  } // End while < endtime

  //idleUntil(endTime);
} // end playDissolve

// ************************
// ***     Pause        ***
// ************************
void playPausePattern(class effect &theEffect)
{
  unsigned long endTime = curMillis + (unsigned long)theEffect.time() * 1000;
  idleUntil(endTime);
} // end playPause

// ************************
// ***   Play Color     ***
// ************************
void playColor(CRGB theColor)
{
  for (int i = 0; i < num_leds; i++)
  {
    leds[i] = theColor;
  }
  FastLED.show();
}

void infiniteLoop()
// ONLY FOR DEBUGGING!
// Puts Arduino in infinite loop, with just heartbeat LED blinking
{
  while (running)
  {
    curMillis = millis();
    if (curMillis > heartNextBeat)
    {
      heartState = ((heartTic == 1) || (heartTic == 4));
      digitalWrite(heartLED, heartState);
      DEBUG_Print_Info("*");
      heartTic++;
      heartTic &= 7;
      heartNextBeat = curMillis + heartTime;
    }
  }
}

CRGB calcFade(CRGB startColor, CRGB endColor, byte stepNum, byte ofSteps)
{
  CRGB ret = startColor;

  /*
    DEBUG_Print_Info (F("Fade from "));
    DEBUG_Print_Info (startColor.r);
    DEBUG_Print_Info (F(", "));
    DEBUG_Print_Info (startColor.g);
    DEBUG_Print_Info (F(", "));
    DEBUG_Println_Info (startColor.b);
    DEBUG_Print_Info (F("      to "));
    DEBUG_Print_Info (endColor.r);
    DEBUG_Print_Info (F(", "));
    DEBUG_Print_Info (endColor.g);
    DEBUG_Print_Info (F(", "));
    DEBUG_Println_Info (endColor.b);
  */

  //DEBUG_Print_Info (F("  Step # "));
  //DEBUG_Print_Info (stepNum);
  //DEBUG_Print_Info (F(" of "));
  //DEBUG_Println_Info (ofSteps);

  if ((stepNum > 0) && (stepNum < ofSteps))
  {
    float frac = ofSteps - stepNum + 1;
    //DEBUG_Print_Info(F("Ratio should be 1/"));
    //DEBUG_Print_Info(frac);
    //DEBUG_Println_Info(F("th"));

    float ratio = 1 / frac;  // Example: step 1 of 5 would be .2, step 2 of 5 would be .25, step 3 of 5 would be .333, step 4 of 5 would be .5
    //DEBUG_Print_Info (F("   Ratio "));
    //DEBUG_Println_Info (ratio);

    int diff = endColor.r - startColor.r;
    //DEBUG_Print_Info (F("    R-Diff "));
    //DEBUG_Print_Info (diff);

    float change = diff * ratio;
    change = round(change);

    //DEBUG_Print_Info (F("  R-Change "));
    //DEBUG_Print_Info (change);

    ret.r += (byte)(change);

    diff = endColor.g - startColor.g;
    //DEBUG_Print_Info (F("    G-Diff "));
    //DEBUG_Print_Info (diff);

    change = diff * ratio;
    change = round(change);

    //DEBUG_Print_Info (F("  G-Change "));
    //DEBUG_Print_Info (change);

    ret.g += (byte)(change);

    diff = endColor.b - startColor.b;
    //DEBUG_Print_Info (F("    B-Diff "));
    //DEBUG_Print_Info (diff);

    change = diff * ratio;
    change = round(change);

    //DEBUG_Print_Info (F("  B-Change "));
    //DEBUG_Print_Info (change);

    ret.b += (byte)(change);

  } // end Step # > 0

  if (stepNum == ofSteps)
  {
    ret = endColor;
  }

  /*
    DEBUG_Print_Info (F("  Output "));
    DEBUG_Print_Info (ret.r);
    DEBUG_Print_Info (F(", "));
    DEBUG_Print_Info (ret.g);
    DEBUG_Print_Info (F(", "));
    DEBUG_Println_Info (ret.b);
  */

  return ret;
}

void randomizeTheList()
{
  int m;
  int r;
  // First pass, fill theList with numbers in order
  for (int n = 0; n < num_leds; n++)
  {
    theList[n] = n;
  }
  // Second pass, swap random pairs
  for (int n = 0; n < num_leds; n++)
  {
    r = random(num_leds);
    m = theList[n];
    theList[n] = theList[r];
    theList[r] = m;
  }
}

void setTestDefaults()
{
  // These are the TESTING defaults!
  // A simple basic set of effects
  // to check whatever I am trying to debug.
  // The REAL factory defaults procedure is below,

  EEPROM.writeByte(ADDR_LUCKY_NUMBER, MY_LUCKY_NUMBER);
  EEPROM.writeByte(ADDR_CONFIG_VERSION, CONFIG_VERSION);
  num_leds = 50;
  EEPROM.writeInt(ADDR_NUM_LEDS, num_leds);

  byte k = 0;

  // Write some defaults into EVERY location
  // effectively erasing the entire prior
  // config and all its effects.
  theEffect.pattern(PAT_NONE);
  theEffect.time (10);
  theEffect.speed(5);
  theEffect.size (1);
  theEffect.count(6);
  theEffect.color(0, COLOR_RED);
  theEffect.color(1, COLOR_YELLOW);
  theEffect.color(2, COLOR_GREEN);
  theEffect.color(3, COLOR_CYAN);
  theEffect.color(4, COLOR_BLUE);
  theEffect.color(5, COLOR_MAGENTA);
  for (byte i = 0; i < NUM_EFFECTS; i++)
  {
    theEffect.save(i);
  }

  // Now start refilling with test effects
  // Effect #0
  theEffect.pattern(PAT_DISSOLVE);
  theEffect.time(40);
  theEffect.speed(5); // dissolve speed for each color
  theEffect.count(4);
  theEffect.size(5);  // Seconds to wait between colors
  theEffect.color(0, COLOR_WHITE);
  theEffect.color(1, COLOR_RED);
  theEffect.color(2, COLOR_MEDIUM);
  theEffect.color(3, COLOR_GREEN);
  theEffect.save(k);
  k++;

  // Effect #1
  theEffect.pattern(PAT_GRADIENT);
  theEffect.time (10);
  theEffect.speed(5);  // NOT Used
  theEffect.size (10); // Spacing between colors
  theEffect.count(5);
  theEffect.color(0, COLOR_BLUE);
  theEffect.color(1, COLOR_MAGENTA);
  theEffect.color(2, COLOR_RED);
  theEffect.color(3, COLOR_YELLOW);
  theEffect.color(4, COLOR_GREEN);
  theEffect.save(k);
  k++;

  // Effect #2
  theEffect.pattern(PAT_FWDCHASE);
  theEffect.time (15);
  theEffect.speed(10);
  theEffect.save(k);
  k++;

  // #3
  theEffect.pattern(PAT_DISSOLVE);
  theEffect.time(60);
  theEffect.speed(5);
  theEffect.count(4);
  theEffect.color(0, COLOR_BLACK_OFF);
  theEffect.color(1, COLOR_RED);
  theEffect.color(2, COLOR_MEDIUM);
  theEffect.color(3, COLOR_GREEN);
  theEffect.save(k);
  k++;

  // #4
  theEffect.pattern(PAT_SPARKLE);
  theEffect.time (30);
  theEffect.size (5);
  theEffect.count(5);
  theEffect.speed(10);
  theEffect.backColor(COLOR_DK_MAGENTA);
  theEffect.color(0, COLOR_BRT_WHITE);
  theEffect.color(1, COLOR_LT_RED);
  theEffect.color(2, COLOR_LT_MAGENTA);
  theEffect.color(3, COLOR_LT_BLUE);
  theEffect.color(4, COLOR_BRT_WHITE);
  theEffect.save(k);
  k++;

  // write a NONE effect to end the list
  // #5
  theEffect.pattern(PAT_NONE);
  theEffect.time (0);
  theEffect.save(k);

  effectCount = k;

  // Write the custom color names
  strcpy(strBuffer, NAME_CORAL);
  writeCustomColorName(0);
  colorsRGB[1] = hex2color(0xf08080);     // RGB value in index +1 because color 0 = black

  strcpy(strBuffer, NAME_OLIVE);
  writeCustomColorName(1);
  colorsRGB[2] = hex2color(0x6b8e23);

  strcpy(strBuffer, NAME_WARM_WITE);
  writeCustomColorName(2);
  colorsRGB[3] = hex2color(0xffc0c0);

  strcpy(strBuffer, NAME_TURQUOISE);
  writeCustomColorName(3);
  colorsRGB[4] = hex2color(0x40e0d0);

  strcpy(strBuffer, NAME_LIME);
  writeCustomColorName(4);
  colorsRGB[5] = hex2color(0x40ff40);

  strcpy(strBuffer, NAME_GOLD);
  writeCustomColorName(5);
  colorsRGB[6] = hex2color(0xb0ff20);

  strcpy(strBuffer, NAME_INDIGO);
  writeCustomColorName(6);
  colorsRGB[7] = hex2color(0x4b0082);

  strcpy(strBuffer, NAME_CRIMSON);
  writeCustomColorName(7);
  colorsRGB[8] = hex2color(0xdc143c);

} // End setTestDefaults()

void setDefaults()
{
  // These are the normal "default" effects and patterns
  // but not really "default" as this set is custom designed
  // specifically for Princess Janine D'Berry's Chrismas Tree
  EEPROM.writeByte(ADDR_LUCKY_NUMBER, MY_LUCKY_NUMBER);
  EEPROM.writeByte(ADDR_CONFIG_VERSION, CONFIG_VERSION);
  num_leds = 250;
  EEPROM.writeInt(ADDR_NUM_LEDS, num_leds);
  byte k = 0;

  // Write some defaults into EVERY location
  // effectively erasing the entire prior
  // config and all its effects.
  theEffect.pattern(PAT_NONE);
  theEffect.time (10);
  theEffect.speed(5);
  theEffect.size (1);
  theEffect.count(6);
  theEffect.color(0, COLOR_RED);
  theEffect.color(1, COLOR_YELLOW);
  theEffect.color(2, COLOR_GREEN);
  theEffect.color(3, COLOR_CYAN);
  theEffect.color(4, COLOR_BLUE);
  theEffect.color(5, COLOR_MAGENTA);
  for (byte i = 0; i < NUM_EFFECTS; i++)
  {
    theEffect.save(i);
  }

  // Now start filling in with some actual effects
  // Effect #1
  theEffect.pattern(PAT_DOTS);  // White on Purple, for Richard
  theEffect.time (2);
  theEffect.speed(3);
  theEffect.count(5);
  theEffect.size(10);
  theEffect.color(0, COLOR_RED);
  theEffect.color(1, COLOR_YELLOW);
  theEffect.color(2, COLOR_GREEN);
  theEffect.color(3, COLOR_BLUE);
  theEffect.color(4, COLOR_MAGENTA);
  theEffect.save(k);
  k++;

  //2
  theEffect.pattern(PAT_FWDCHASE);
  theEffect.time (15);
  theEffect.speed(10);
  theEffect.save(k);
  k++;

  //3
  theEffect.pattern(PAT_REVCHASE);
  theEffect.time (15);
  theEffect.speed(10);
  theEffect.save(k);
  k++;

  //4
  theEffect.pattern(PAT_DOTS);  // Setup for Purple Sparkle
  theEffect.time (1);
  theEffect.size (50);
  theEffect.count(1);
  theEffect.color(0, COLOR_DK_MAGENTA);
  theEffect.save(k);
  k++;

  //5
  theEffect.pattern(PAT_SPARKLE);  // White on Purple, for Richard
  theEffect.time (20);
  theEffect.speed(3);
  theEffect.count(6);
  theEffect.backColor(COLOR_DK_MAGENTA);
  theEffect.color(0, COLOR_WHITE);
  theEffect.color(1, COLOR_BRT_WHITE);
  theEffect.color(2, COLOR_WHITE);
  theEffect.color(3, COLOR_DIM_WHITE);
  theEffect.color(4, COLOR_BRT_WHITE);
  theEffect.save(k);
  k++;

  //6
  theEffect.pattern(PAT_FADE); // Rainbow
  theEffect.time (30);
  theEffect.speed(5);
  theEffect.count(6);
  theEffect.color(0, COLOR_BLUE);
  theEffect.color(1, COLOR_CYAN);
  theEffect.color(2, COLOR_GREEN);
  theEffect.color(3, COLOR_YELLOW);
  theEffect.color(4, COLOR_RED);
  theEffect.color(5, COLOR_BLACK_OFF);
  theEffect.save(k);
  k++;

  //7
  theEffect.pattern(PAT_GRADIENT);
  theEffect.time (5);
  theEffect.speed(5);  // NOT Used
  theEffect.size (50); // Spacing between colors
  theEffect.count(5);
  theEffect.color(0, COLOR_BLUE);
  theEffect.color(1, COLOR_MAGENTA);
  theEffect.color(2, COLOR_RED);
  theEffect.color(3, COLOR_YELLOW);
  theEffect.color(4, COLOR_GREEN);
  theEffect.save(k);
  k++;

  //8
  theEffect.pattern(PAT_FWDCHASE);
  theEffect.time (15);
  theEffect.speed(10);
  theEffect.save(k);
  k++;

  //9
  theEffect.pattern(PAT_DISSOLVE);
  theEffect.time(4);
  theEffect.speed(10);
  theEffect.count(3);
  theEffect.color(0, COLOR_RED);
  theEffect.color(1, COLOR_GREEN);
  theEffect.color(2, COLOR_WHITE);
  theEffect.save(k);
  k++;

  //10
  theEffect.pattern(PAT_SPARKLE); // Purple on Black, for Richard
  theEffect.time (20);
  theEffect.speed(3);
  theEffect.count(6);
  theEffect.backColor(COLOR_BLACK_OFF);
  theEffect.color(0, COLOR_PURPLE);
  theEffect.color(1, COLOR_MAGENTA);
  theEffect.color(2, COLOR_DK_MAGENTA);
  theEffect.color(3, COLOR_LT_MAGENTA);
  theEffect.color(4, COLOR_MAGENTA);
  theEffect.save(k);
  k++;

  //11
  theEffect.pattern(PAT_DOTS);  // Setup for Purple Sparkle
  theEffect.time (1);
  theEffect.size (50);
  theEffect.count(1);
  theEffect.color(0, COLOR_MEDIUM);
  theEffect.save(k);
  k++;

  //12
  theEffect.pattern(PAT_SPARKLE);
  theEffect.time (20);
  theEffect.speed(5);
  theEffect.count(6);
  theEffect.color(0, COLOR_DK_BLUE);
  theEffect.color(1, COLOR_BRT_WHITE);
  theEffect.color(2, COLOR_WHITE);
  theEffect.color(3, COLOR_DK_BLUE);
  theEffect.color(4, COLOR_BLACK_OFF);
  theEffect.color(5, COLOR_DK_BLUE);
  theEffect.save(k);
  k++;

  //13
  theEffect.pattern(PAT_SPARKLE);
  theEffect.time (20);
  theEffect.speed(5);
  theEffect.count(6);
  theEffect.color(0, COLOR_DK_GREEN);
  theEffect.color(1, COLOR_RED);
  theEffect.color(2, COLOR_DK_RED);
  theEffect.color(3, COLOR_LT_RED);
  theEffect.color(4, COLOR_DK_GREEN);
  theEffect.color(5, COLOR_DK_GREEN);
  theEffect.save(k);
  k++;

  //14
  theEffect.pattern(PAT_GRADIENT);
  theEffect.time (5);
  theEffect.speed(5);  // NOT Used
  theEffect.size (75); // Spacing between colors
  theEffect.count(2);
  theEffect.color(0, COLOR_GREEN);
  theEffect.color(1, COLOR_RED);
  theEffect.save(k);
  k++;

  //15
  theEffect.pattern(PAT_REVCHASE);
  theEffect.time (15);
  theEffect.speed(10);
  theEffect.save(k);
  k++;

  //16
  theEffect.pattern(PAT_DOTS); // Rainbow
  theEffect.time (3);
  theEffect.size (1);
  theEffect.count(3);
  theEffect.color(0, COLOR_RANDOM);
  theEffect.color(1, COLOR_ANY);
  theEffect.color(2, COLOR_MEDIUM);
  theEffect.save(k);
  k++;

  //17
  theEffect.pattern(PAT_DISSOLVE);
  theEffect.time(30);
  theEffect.speed(1);
  theEffect.count(4);
  theEffect.color(0, COLOR_BRT_WHITE);
  theEffect.color(1, COLOR_WHITE);
  theEffect.color(2, COLOR_DIM_WHITE);
  theEffect.color(2, COLOR_BLACK_OFF);
  theEffect.save(k);
  k++;

  // Write a NONE effect to end the list
  // (18)
  theEffect.pattern(PAT_NONE);
  theEffect.time (0);
  theEffect.save(k);

  effectCount = k;

  // Write the custom color names
  strcpy(strBuffer, NAME_CORAL);
  writeCustomColorName(0);
  colorsRGB[1] = hex2color(0xf08080);     // RGB value in index +1 because color 0 = black

  strcpy(strBuffer, NAME_OLIVE);
  writeCustomColorName(1);
  colorsRGB[2] = hex2color(0x6b8e23);

  strcpy(strBuffer, NAME_WARM_WITE);
  writeCustomColorName(2);
  colorsRGB[3] = hex2color(0xffc0c0);

  strcpy(strBuffer, NAME_TURQUOISE);
  writeCustomColorName(3);
  colorsRGB[4] = hex2color(0x40e0d0);

  strcpy(strBuffer, NAME_LIME);
  writeCustomColorName(4);
  colorsRGB[5] = hex2color(0x40ff40);

  strcpy(strBuffer, NAME_GOLD);
  writeCustomColorName(5);
  colorsRGB[6] = hex2color(0xb0ff20);

  strcpy(strBuffer, NAME_INDIGO);
  writeCustomColorName(6);
  colorsRGB[7] = hex2color(0x4b0082);

  strcpy(strBuffer, NAME_CRIMSON);
  writeCustomColorName(7);
  colorsRGB[8] = hex2color(0xdc143c);

} // End void setDefaults()

And finally, the code for DebugUtils.h is:

//*******************************************************************
// DebugUtils.h - Simple debugging utilities.
// (u) UnCopyright 2022+ Dr. Wizard K. Pendleton, but I take no credit for it.
// Modelled after version by Fabio Varesano <fabio at varesano dot net>
//
// Ideas taken from:
// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1271517197
//
// Open source rules apply under the terms of the version 3 GNU General Public
// License as published by the Free Software Foundation.
// No warranty, use at your own risk, etc.
//********************************************************************

#ifndef DEBUGUTILS_H
#define DEBUGUTILS_H

// Turn various types of debug messages on and off

// FOR DEBUGGING, UN-REMARK ONE OR BOTH DEFINES BELOW
// FOR RELEASE, REMARK BOTH LINES BELOW
#define DEBUG_Mode_Trace
#define DEBUG_Mode_Info
// End Debug/Release defines

#ifdef DEBUG_Mode_Trace
#define DEBUG_Print_Trace(str) \
  Serial.print(str); \
  Serial.print(F(" - in ")); \
  Serial.print(__PRETTY_FUNCTION__); \
  Serial.print(F(" in ")); \
  Serial.print(__FILE__); \
  Serial.print(F(" #")); \
  Serial.print(__LINE__); \
  Serial.print(F(" @")); \
  Serial.print(millis());
#define DEBUG_Println_Trace(str) \
  Serial.print(str); \
  Serial.print(F(" - in ")); \
  Serial.print(__PRETTY_FUNCTION__); \
  Serial.print(F(" in ")); \
  Serial.print(__FILE__); \
  Serial.print(F(" #")); \
  Serial.print(__LINE__); \
  Serial.print(F(" @")); \
  Serial.println(millis());
#else
#define DEBUG_Print_Trace(str)
#define DEBUG_Println_Trace(str)
#endif


#ifdef DEBUG_Mode_Info
#define DEBUG_Print_Info(str)         Serial.print  (str)
#define DEBUG_Println_Info(str)       Serial.println(str)
#define DEBUG_HexPrint_Info(str)    Serial.print  (str, HEX)   // Doesn't work, dangit
//#define DEBUG_Println_Info(str, mod)  Serial.println(str, mod)
#else
#define DEBUG_Print_Info(str)
#define DEBUG_Println_Info(str)
#define DEBUG_HexPrint_Info(str)
//#define DEBUG_Println_Info(str, mod)
#endif


#endif //ifndef DEBUGUTILS_H

Try putting a prototype in for setDefaults - sometimes the Arduino IDE's auto generation of them is faulty. It wouldn't surprise me if it's confused by the fact that you have two functions with the same name.

Add a function prototype for setDefaults() near the top of your code; e.g.

#include <EEPROMex.h>
#include <EEPROMVar.h>
#include <FastLED.h>
//#include <Wire.h>
#include "DebugUtils.h"

// function prototype
void setDefaults();

After that you only have linker errors for fetchColor().

Note:
EEPROMEx functionalities have been implemented in the standard EEPROM library for a number of years. You can use EEPROM.put() and EEPROM.get().

Woo-Hoo! Thanks wildbill and sterretje!
Princess Janine will now get her blinky Christmas tree on time! Thank goodness, you do not want to displease the princess!
So I take it my hunch was correct about the prototype not being automatically/correctly generated by the compiler? Perhaps I should give myself a (small) pat on the back?
Thanks also for the tip about EEPROMEx!