Model Railway Turntable - in a spin so much Im dizzy

  • So we are now executing the code in the EEPROM State Machine.

  • Explain what is happening here ?

So the eeprom machine firstly starts up if not already done so, in which case it skips this step, or carries this out and then returns to the left side column. skipping it this time as its already done.
The machine then awaits an order/instruction.
If not instruction is received it loops back around.
if it receives an instruction it moves to the eepromTiming to determine what to do next. i.e. has a switch been opened.
no it flashingleds carried out.
if switch is opened we move through actioning the eeprom - turn of leds, save the last state, etc.
then we move to finished.

  • We check the EEPROM State Machine every 5ms
  • Once we have executed the code in the EEPROM State Machine (pink), we then check the MoveTo State Machine etc.



  • The question might be how do we physically step through the EEPROM State Machine :thinking:
  • We start stepping through this State Machine when we want to store an updated position to EEPROM.
  • 1st we close and hold the SetPosSw switch, 2nd close and hold the EncoderSw switch, in code we now process the EEPROM State Machine.
  • We normally sit in eepromWait1 and do nothing.
  • When both switches are closed, we move to eepromTiming (yellow).
    There execute the code in eepromTiming.

Good to go ?

Ok that makes sense.

  • Let's say we are now in the eepromTIMING state.

  • Explain what is happening now that we are executing eepromTIMING code ?

so firstly it awaits to see if either switch is released early/too soon.
if it is the leds are turned off and the eeprom returns to wait.

once the timer then expires it checks whether there is a change in position and if so saves the position.
to confirm this is done it enters the led flash protocol.
If there is no change in position it simply skips this process and loops back around.

  • In state eepromTiming we make sure EncoderSw and SetPosSw remain closed.
    If a switch opens, we set the state back to eepromWait1, again doing nothing.
    i.e. 5 seconds did not elapse while holding the 2 switches.

  • When the switches remain closed for a full 5 seconds,
    we save the new position to EEPROM,
    then enable and set the eepromCommonTIMER to 200ms,
    and set the next state to eepromFlashingLEDs.

  • The next time the EEPROM State Machine is checked, we will be sitting in eepromFlashingLEDs (green).

  • So, we went into yellow, stored the new position to EEPROM and we are now sitting in green :nerd_face:.

Good to go ?

so should we have an additional arrow out the bottom of the yellow square going to where the n arrow is on the bottom of the eepromTiming diamond?

  • THis image ? :thinking:

image

  • Or this one ?

i had meant this one

  • That yellow square is contains all of this.

Does that make sense ?

so where does return A go that is different from return B?

Okay ?

Right Ok that makes sense/clarifies it for me. Thank you.




  • The next time the EEPROM State Machine is checked, we will be sitting in eepromFlashingLEDs (green).

  • Explain what is happening above ?

    //========================          e e p r o m F l a s h i n g L E D s
    case eepromFlashingLEDs:
      {
        if (eepromLedControlTIMER.checkTIMER() == EXPIRED)
        {
          //toggle LEDs
          digitalWrite(cwLED, digitalRead(cwLED) == HIGH ? LOW : HIGH);
          digitalWrite(ccwLED, digitalRead(ccwLED) == HIGH ? LOW : HIGH);
        }

        if (digitalRead(SetPosSw.pin) == OPENED)
        {
          digitalWrite(cwLED, LEDoff);
          digitalWrite(ccwLED, LEDoff);

          //next state in the "EEPROM State Machine"
          eepromMachine = eepromFinished;
        }
      }
      break;

so this is the process of if high make them low, if low make them high. ie. flashing.

once the set position switch is opened the leds are switched off, whatever state they sit in.

the eepromfinishes and we reach return B location on the diagram.

:partying_face:

  • Below is a PDF of all the images we just went through.

ModelTrainFlowChart.pdf (448.9 KB)

  • At some point, suggest you review what we have just went though.



  • Changing topics for a second, it strikes me you may be the type of person to whom this might appeal.
    Put this on the wife’s To Do Reading List.



Software Debugging Hint

1.

  • For each opening curly bracket we must have a closing curly bracket.

  • There are times when we are debugging, we need to check this.

  • First save your sketch in the event something goes wrong.

  • Use the Search Tool <crtl><F> to count the number of these curly brackets.
    Put { in both the Find and Replace with: text boxes.
    Click Replace all.

  • Depending on the Arduino IDE version, you will be told the number of replacements.

  • Do the same with }, these two (2) numbers must be the same.

Do the same for ( and ).

  • I have IDE version 1.89, this feature does not work.

When I need to do this, I use NotePad++.

2.

  • If you see the silcrow character beside a Tab’s name, this reminds you the code has modified, needs to be saved.

After saving the sketch, this character will disappear.

2024-06-04_11-38-20




  • Attached is Version 2.6 of the sketch, some bugs have been fixed.
  • Please check it's functionality.
//================================================^================================================
//                                    T o p   O f   S k e t c h
//
//
//  https://forum.arduino.cc/t/model-railway-turntable-in-a-spin-so-much-im-dizzy/1263988
//
//  HLD
//
//  Version    YY/MM/DD    Comments
//  =======    ========    ========================================================================
//  1.00       24/05/25    Started writing sketch
//  1.10       24/05/25    Added code to read the Rotary Encoder
//  1.20       24/05/25    Added Stepping Motor code
//  1.30       24/05/27    Added the State Machine named "resetMachine"
//  1.40       24/05/27    Added Homing after zeroing the motor
//  1.50       24/05/27    Added LED effects when Zeroing and Homing
//  1.60       24/05/27    When the motor stops stepping, turn off motor coil currents
//  1.70       24/05/27    Added LED action when calibrating the Motor position
//  1.80       24/05/28    Added added debug macros, added some sketch comments
//  1.90       24/05/29    Added stepping limits of 0-4096 for the motor stepping range
//  2.00       24/05/30    Added added rotary encoder stepping, with limits, to motor control
//  2.10       24/05/30    CW and CCW switches now make motor go to the next/previous stored position
//  2.20       24/05/31    Calibration mode added, hold encoder switch at power up time
//  2.30       24/05/31    5 Track turntable needs 10 positions so you can turn the train engine 180 degrees
//  2.40       24/05/31    Added EEPROM stuff
//  2.40       24/06/01    Added turntable 180° movements each track position
//  2.50       24/06/03    Hold CS_Sw and CCW_Sw at powerup time makes EEPROM go to default values
//  2.60       24/06/03    
//

//========================
//this prints to the Serial Monitor at powerup time               <-------<<<<<<<  V E R S I O N  #
char versionNumber[]       = {"\n\nVersion = 2.60"};
//========================


//#include <Wire.h>

#include <EEPROM.h>

//                                           M A C R O S
//================================================^================================================
#define LEDon              HIGH   //PIN---[220R]---A[LED]K---GND
#define LEDoff             LOW

#define PRESSED            LOW    //+5V---[Internal 50k]---PIN---[Switch]---GND
#define RELEASED           HIGH

#define CLOSED             LOW    //+5V---[Internal 50k]---PIN---[Switch]---GND
#define OPENED             HIGH

#define ENABLED            true
#define DISABLED           false

#define OPTOclosed         HIGH   //use with "HIGH when closed" opto devices
#define OPTOopened         LOW    //use with "LOW when open" opto devices 

//========================================================================  <-------<<<<<<<
//these macros are used for debug purposes
//
//comment next line to stop printing to the Serial Monitor,
#define DEBUG
//debug Macros
//========================
#ifdef DEBUG
#define SERIALBEGIN(...)    Serial.begin(__VA_ARGS__)

#define DPRINT(...)         Serial.print(__VA_ARGS__)
#define DPRINTLN(...)       Serial.println(__VA_ARGS__)

#define DPRINTF(...)        Serial.print(F(__VA_ARGS__))   //for text only, using the F macro
#define DPRINTLNF(...)      Serial.println(F(__VA_ARGS__)) //for text only, using the F macro plus new line

#else
#define SERIALBEGIN(...)

#define DPRINT(...)
#define DPRINTLN(...)

#define DPRINTF(...)
#define DPRINTLNF(...)
#endif

//Arduino UNO, produce a 62ns pulse on D13
#define PULSE62Heartbeat   cli(); PINB = bit(PINB5); PINB = bit(PINB5); sei()

//                                            G P I O s
//================================================^================================================
//
//a structure to define input objects, switches or sensors
struct makeInput
{
  const byte pin;                  //the digital input pin number
  unsigned long switchTime;        //the time the switch was closed
  byte lastState;                  //the state the input was last in
  byte counter;                    //a counter used to validate a switch change in state
}; //END of   struct makeInput

const byte inputPins[]           = {4, 14, 15, 16, 17};

//Digital Inputs
//define the input connected to a PB switch

//========================
makeInput EncoderSw =
{
  4, 0, OPENED, 0                  //pin, switchTime, lastState, counter
};

//========================
makeInput DeleteSw =
{
  14, 0, OPENED, 0                 //pin, switchTime, lastState, counter
};

//========================
makeInput SetPosSw =
{
  15, 0, OPENED, 0                 //pin, switchTime, lastState, counter
};

//========================
makeInput CCW_Sw =
{
  16, 0, OPENED, 0                 //pin, switchTime, lastState, counter
};

//========================
makeInput CW_Sw =
{
  17, 0, OPENED, 0                 //pin, switchTime, lastState, counter
};

//========================
makeInput OptoSensor =
{
  18, 0, OPTOopened, 0            //pin, switchTime, lastState, counter
};


byte filter                      = 10;
//TIMER "switches" runs every 5ms.
//5ms * 10 = 50ms is needed to validate a switch change in state.
//A switch change in state is valid "only after" 10 identical changes are detected.
//This is used to filter out EMI noise in the system


const byte EncoderOutA           = 3;  //there is a pull-up resistor inside the rotary encoder
const byte EncoderOutB           = 2;  //there is a pull-up resistor inside the rotary encoder


//OUTPUTS
//===================================
const byte outputPins[]          = {13, 12, 11, 10, 9, 8, 7, 6, 5};

const byte heartbeatLED          = 13;
const byte cwLED                 = 12;
const byte ccwLED                = 11;
const byte deleteLED             = 10;
const byte setPosLED             =  9;

//stepping motor
const byte IN4                   = 8;
const byte IN3                   = 7;
const byte IN2                   = 6;
const byte IN1                   = 5;


//                                        V A R I A B L E S
//================================================^================================================
//
//                                         "Stepping Motor"                         2 8 B Y J - 4 8
//a 5-wire unipolar stepper motor that runs on 5V
//1/2 steps per revolution ~4096
//Stepping Motor stepping pattern                       8 half (1/2) steps then repeats
//index                                0       1       2       3       4       5       6       7       8
int lookup[]                     = {B01000, B01100, B00100, B00110, B00010, B00011, B00001, B01001, B00000};
//index 8 in the array is B00000
//we use this step pattern to turn off the motor current when the motor is stopped

//Stepper Motor inputs
//                  IN1, Bit 0 _____
//                  IN2, Bit 1 ____ |
//                  IN3, Bit 2 ___ ||
//                  IN4, Bit 3 __ |||
//                               ||||
//ex. step pattern             B01000
//IN1 is bit 0
//IN2 is bit 1
//IN3 is bit 2
//IN4 is bit 3

int stepIndex                        = 0;                //where we are in the Look-Up table
int savedStepIndex;                                      //a copy of stepIndex when powering off the motor

//================================================
//miscellaneous

const unsigned long shortPushTime   = 1000ul;

//for turning motor 180°
const unsigned long longPush        = 3000ul;            //CW/CCW switch hold time before table reverses

bool cwLED_Flag                     = DISABLED;           //if enabled, we will turn on the LED when long push is valid
bool ccwLED_Flag                    = DISABLED;           //if enabled, we will turn on the LED when long push is valid

//================================================
//if enabled, the CW and CCW switches can move the motor so you
//can check the track exact locations
bool calibrationFlag                = DISABLED;

byte homeFlashCounter;                                   //used in flashing the LEDs at the HOME position

int homingCounter                   = 0;                 //number of steps away from the motor zero position

const unsigned long CW_CCW_Interval = 5000ul;            //5 seconds to verify if CW and CCW still being held

//================================================
const byte maxTrackPositions        = 10;                //this includes the 180 degree calculated values
int positionIndex                   = -1;                //the storedPostions array index, -1 at reset
//array of stored positions
//index                                   0  1  2  3  4  5  6  7  8  9
long storedPostions[maxTrackPositions] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

long destinationPosition            = 0;                 //motor position we want to go to
long currentMotorPosition           = 0;                 //number of steps away from the HOME position

const int homeSteps                 = 100;               //number of CW steps from the motor zero position

const int stepsPerRev               = 4096;              //the number of 1/2 steps per RPM
const int stepsIn180Degrees         = stepsPerRev / 2;
const unsigned long motorSpeed      = 3000ul;            //microseconds larger the number, slower the the motor movement

//================================================
//EEPROM stuff
const byte eeValidFlag                = B10101010;  //0xAA, pattern will signify the EEPROM data is valid

const byte eeStartAddress             = 0;          //the 1st address where our DATA begins
int eeAddress                         = 0;          //the current EEPROM address
byte eeReadData                       = 0;          //receives DATA we read
const unsigned long encoderSwHoldTime = 5000ul;     //hold encoder switch for this time to store the current position



//                          millis() / micros()   B a s e d   T I M E R S
//================================================^================================================
//TIMER objects are non-blocking
struct makeTIMER
{
#define MILLIS             1
#define MICROS             1000    //we can use this value to divide into a variable to get milliseconds

#define ENABLED            true
#define DISABLED           false

#define YES                true
#define NO                 false

#define STILLtiming        0
#define EXPIRED            1
#define TIMERdisabled      2

  //these are the bare minimum "members" needed when defining a TIMER
  int                      TimerType;      //what kind of TIMER is this? MILLIS/MICROS
  unsigned long            Time;           //when the TIMER started
  unsigned long            Interval;       //delay time which we are looking for
  bool                     TimerFlag;      //is the TIMER enabled ? ENABLED/DISABLED
  bool                     Restart;        //do we restart this TIMER   ? YES/NO

  //================================================
  //condition returned: STILLtiming (0), EXPIRED (1) or TIMERdisabled (2)
  //function to check the state of our TIMER  ex: if(myTimer.checkTIMER() == EXPIRED);
  byte checkTIMER()
  {
    //========================
    //is this TIMER enabled ?
    if (TimerFlag == ENABLED)
    {
      //========================
      //has this TIMER expired ?
      if (getTime() - Time >= Interval)
      {
        //========================
        //should this TIMER restart again?
        if (Restart == YES)
        {
          //restart this TIMER
          Time = getTime();
        }

        //this TIMER has expired
        return EXPIRED;
      }

      //========================
      else
      {
        //this TIMER has not expired
        return STILLtiming;
      }

    } //END of   if (TimerFlag == ENABLED)

    //========================
    else
    {
      //this TIMER is disabled
      return TIMERdisabled;
    }

  } //END of   checkTime()

  //================================================
  //function to enable and restart this TIMER  ex: myTimer.enableRestartTIMER();
  void enableRestartTIMER()
  {
    TimerFlag = ENABLED;

    //restart this TIMER
    Time = getTime();

  } //END of   enableRestartTIMER()

  //================================================
  //function to disable this TIMER  ex: myTimer.disableTIMER();
  void disableTIMER()
  {
    TimerFlag = DISABLED;

  } //END of    disableTIMER()

  //================================================
  //function to restart this TIMER  ex: myTimer.restartTIMER();
  void restartTIMER()
  {
    Time = getTime();

  } //END of    restartTIMER()

  //================================================
  //function to force this TIMER to expire ex: myTimer.expireTimer();
  void expireTimer()
  {
    //force this TIMER to expire
    Time = getTime() - Interval;

  } //END of   expireTimer()

  //================================================
  //function to set the Interval for this TIMER ex: myTimer.setInterval(100);
  void setInterval(unsigned long value)
  {
    //set the Interval
    Interval = value;

  } //END of   setInterval()

  //================================================
  //function to return the current time
  unsigned long getTime()
  {
    //return the time             i.e. millis() or micros()
    //========================
    if (TimerType == MILLIS)
    {
      return millis();
    }

    //========================
    else
    {
      return micros();
    }

  } //END of   getTime()

}; //END of   struct makeTIMER


//                             D e f i n e   a l l   t h e   T I M E R S
//================================================^================================================
/*example
  //========================
  makeTIMER toggleLED =
  {
     MILLIS/MICROS, 0, 500ul, ENABLED/DISABLED, YES/NO  //.TimerType, .Time, .Interval, .TimerFlag, .Restart
  };

  Functions we can access:
  toggleLED.checkTIMER();
  toggleLED.enableRestartTIMER();
  toggleLED.disableTIMER();
  toggleLED.expireTimer();
  toggleLED.setInterval(100ul);
*/

//========================
makeTIMER heartbeatTIMER =
{
  MILLIS, 0, 500ul, ENABLED, YES        //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};

//========================
makeTIMER switchesTIMER =
{
  MILLIS, 0, 5ul, ENABLED, YES          //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};

//========================
makeTIMER encoderTIMER =
{
  MILLIS, 0, 5ul, ENABLED, YES          //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};

//========================
makeTIMER machineTIMER =
{
  MILLIS, 0, 5ul, ENABLED, YES          //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};

//========================
makeTIMER eepromCommonTIMER =
{
  MILLIS, 0, 1000ul, DISABLED, YES      //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};

//========================
makeTIMER reverseCommonTIMER =
{
  MILLIS, 0, 5000ul, DISABLED, YES      //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};

//========================
makeTIMER eepromLedControlTIMER =
{
  MILLIS, 0, 1000ul, DISABLED, YES      //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};

//========================
makeTIMER resetCommonTIMER =
{
  MILLIS, 0, 1000ul, DISABLED, YES      //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};

//========================
makeTIMER moveToCommonTIMER =
{
  MILLIS, 0, 1000ul, DISABLED, YES      //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};

//========================
makeTIMER stepperTIMER =
{
  MICROS, 0, motorSpeed, DISABLED, YES  //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};


//                                   S t a t e   M a c h i n e s
//================================================^================================================
//the States in our Machines (use names that mean something to you)

//================================================     resetMachine State Machine
enum STATES : byte   //save RAM, use bytes
{
  ResetStartup,      //do power up stuff
  Check_CW_CCW_Sw,   //checking to see if CW and CCW are closed
  CW_CCW_Timing,     //wait to see if the switches have been closed long enough
  ResetMotor,        //wait here while motor is going CCW and looking for Opto Sensor
  MovingHome,        //set up things to home the motor
  HomingWait,        //wait here while the motor is homing, waiting for the Opto Sensor to close
  ResetFinished      //flash LEDs, reset the "Reset State Machine"
};

STATES resetMachine = ResetStartup;


//================================================     moveToMachine State Machine
enum POSITIONS : byte   //save RAM, use bytes
{
  MoveToStartup,        //do power up stuff
  MoveToWait1,          //1st place to wait
  GoCW,                 //moving motor CW  to next stored position
  GoCCW,                //moving motor CCW to next stored position
  MoveToFinished        //
};

POSITIONS moveToMachine = MoveToStartup;


//================================================     eepromMachine State Machine
enum EEPROMtype : byte  //save RAM, use bytes
{
  eepromStartup,        //do power up stuff
  eepromWait1,          //1st place to wait
  eepromTiming,         //wait to see if the switch have been closed long enough
  eepromFlashingLEDs,   //we flash LEDs here
  eepromFinished        //
};

EEPROMtype eepromMachine = eepromStartup;


//================================================     reverseTurntable State Machine
enum TABLEtype : byte    //save RAM, use bytes
{
  reverseStartup,        //do power up stuff
  reverseWait1,          //1st place to wait
  reverseTableToGoCW,    //we will turn the table 180° CW for this track position tracks 0-4 ex. 0 to 5
  reverseTableToGoCCW,   //we will turn the table 180° CCW for this track position tracks 5-9 ex. 5 to 0
  reverseWait_CW,        //say here while the motor is going 180° CW
  reverseWait_CCW,       //say here while the motor is going 180° CW
  reverseFinished        //
};

TABLEtype reverseTurntable = reverseStartup;




//                                           s e t u p ( )
//================================================^================================================         s e t u p ( )
void setup()
{
  //this delay prevents setup() from running twice when the serial monitor printing.
  delay(1000);

  SERIALBEGIN(115200);

  //Inputs
  //======================
  //use INPUT_PULLUP so the pin does not float, floating pins can cause faulty readings
  //configure the input pins
  for (byte x = 0; x < sizeof(inputPins); x++)
  {
    pinMode(inputPins[x], INPUT_PULLUP);
  }

  //Outputs
  //======================
  //configure the output pins
  for (byte x = 0; x < sizeof(outputPins); x++)
  {
    digitalWrite(outputPins[x], LOW);
    pinMode(outputPins[x], OUTPUT);
  }

  //back to defaults ?
  //======================
  //is the user requesting to go back to default values ?
  //i.e. CWsw and CCWsw closed at powerup time ?
  if (digitalRead(CW_Sw.pin) == CLOSED && digitalRead(CCW_Sw.pin) == CLOSED)
  {
    DPRINTF("\nGoing back to defaults\n");

    //clear the EEPOM eeValidFlag address (i.e. change B10101010 to B11111111)
    EEPROM.put(eeStartAddress, B11111111);

    //all LEDs on to indicate a default reset has happened
    digitalWrite(cwLED , LEDon);
    digitalWrite(ccwLED , LEDon);
    digitalWrite(deleteLED , LEDon);
    digitalWrite(setPosLED , LEDon);

    //wait here for switches to OPEN
    while (digitalRead(CW_Sw.pin) == CLOSED && digitalRead(CCW_Sw.pin) == CLOSED)
    {
      //waiting . . . . . .
    }

    digitalWrite(cwLED , LEDoff);
    digitalWrite(ccwLED , LEDoff);
    digitalWrite(deleteLED , LEDoff);
    digitalWrite(setPosLED , LEDoff);
  }

  //is the DATA in EEPROM good DATA ?
  //======================
  //get the data at the staring EEPROM address
  EEPROM.get(eeStartAddress, eeReadData);

  //is the EEPROM data valid ?
  if (eeReadData == eeValidFlag)
  {
    //the data is valid, retrieve it
    //first DATA address after eeStartAddress
    eeAddress = eeStartAddress + 1;

    //get the track positions from EEPROM
    for (byte x = 0; x < maxTrackPositions / 2; x++)
    {
      //get the data for this array element
      EEPROM.get(eeAddress, storedPostions[x]);

      DPRINT(storedPostions[x]);
      DPRINTF(" ");

      //ready for the next address
      eeAddress = eeAddress + sizeof(storedPostions[x]);
    }

    DPRINTLNF("\nEEPROM data RETRIEVED\n");
  }

  //there was no valid data in the EEPROM, use the default values
  else
  {
    //use the calibration mode to determine each track value (make sure you do a zero reset first)
    storedPostions[0] = 750;                                   //1st track
    storedPostions[1] = 1100;                                  //2nd track
    storedPostions[2] = 1900;                                  //3rd track
    storedPostions[3] = 2250;                                  //4th track
    storedPostions[4] = 2600;                                  //5th track

    //first DATA address after eeStartAddress
    eeAddress = eeStartAddress + 1;

    //update the EEPROM DATA
    for (byte x = 0; x < maxTrackPositions / 2; x++)
    {
      EEPROM.put(eeAddress, storedPostions[x]);

      DPRINT(storedPostions[x]);
      DPRINTF(" ");

      //ready for the next address
      eeAddress = eeAddress + sizeof(storedPostions[x]);
    }

    //DATA is now valid in our EEPROM
    EEPROM.update(eeStartAddress, eeValidFlag);

    DPRINTLN("\nEEPROM data UPDATED with default values\n");
  }

  //======================
  //fill in the 180° array  values
  storedPostions[5] = storedPostions[0] + stepsIn180Degrees;   //1st track + 180 degrees
  storedPostions[6] = storedPostions[1] + stepsIn180Degrees;   //2nd track + 180 degrees
  storedPostions[7] = storedPostions[2] + stepsIn180Degrees;   //3rd track + 180 degrees
  storedPostions[8] = storedPostions[3] + stepsIn180Degrees;   //4th track + 180 degrees
  storedPostions[9] = storedPostions[4] + stepsIn180Degrees;   //5th track + 180 degrees

  DPRINTLNF("Values for all storedPostions[x] positions:");

  //print positions from storedPostions[x]
  for (byte x = 0; x < maxTrackPositions; x++)
  {
    DPRINT(storedPostions[x]);
    DPRINTF(" ");
  }

  DPRINTF("\n");

  //======================
  //do we want to enter calibration mode ?
  if (digitalRead(EncoderSw.pin) == CLOSED)
  {
    //allow the motor to be controlled by the CW and CCW switches
    calibrationFlag = ENABLED;

    DPRINTLNF("Entering Calibration Mode. \n");
  }

  else
  {
    DPRINTLN(versionNumber);

    DPRINTLNF("Controller now is running.");
  }

} //END of   setup()


//                                            l o o p ( )
//================================================^================================================
void loop()
{
  //creates a 62ns pulse on the heartbeatLED each time loop( ) starts up
  //PULSE62Heartbeat;

  //========================================================================  T I M E R  heartbeat
  //condition returned: STILLtiming, EXPIRED or TIMERdisabled
  //if the TIMER is enabled, is it time to toggle the heartbeat LED ?
  if (heartbeatTIMER.checkTIMER() == EXPIRED)
  {
    //when this TIMER expires, toggle the heartbeat LED
    //this LED is a rudimentary way of displaying if the sketch code is blocking or stuttering

    //toggle the heartbeat LED
    digitalWrite(heartbeatLED, digitalRead(heartbeatLED) == HIGH ? LOW : HIGH);

  } //END of this   TIMER

  //========================================================================  T I M E R  switches
  //condition returned: STILLtiming, EXPIRED or TIMERdisabled
  //if the TIMER is enabled, is it time to check our switches ?
  if (switchesTIMER.checkTIMER() == EXPIRED)
  {
    //when this TIMER expires, check if any switch/sensor has changed state

    checkSwitches();

  } //END of this   TIMER

  //========================================================================  T I M E R  encoder
  //condition returned: STILLtiming, EXPIRED or TIMERdisabled
  //if the TIMER is enabled, is it time to read the Rotary Encoder ?
  if (SetPosSw.lastState == OPENED && encoderTIMER.checkTIMER() == EXPIRED)
  {
    //when this TIMER expires, check the rotary encode to see if it has been turned

    readRotaryEncoder();

  } //END of this   TIMER

  //========================================================================  T I M E R  machine
  //condition returned: STILLtiming, EXPIRED or TIMERdisabled
  //if the TIMER is enabled, is it time to service our State Machines ?
  if (machineTIMER.checkTIMER() == EXPIRED)
  {
    //when this TIMER expires, service any/all the State Machines

    //check all the "State Machines"
    checkMachines();

  } //END of this   TIMER

  //========================================================================  T I M E R  stepper
  //condition returned: STILLtiming, EXPIRED or TIMERdisabled
  //if the TIMER is enabled, is it time to move the Stepping Motor ?
  if (stepperTIMER.checkTIMER() == EXPIRED)
  {
    //when this TIMER expires, step the motor
    //exclusionary events (top/down priority) that move the stepper motor
    //i.e. only one event can be operated on at a time
    // 1. HomingWait:        Motor going HOME
    // 2. ResetMotor:        Motor going to ZERO
    // 3. GoCW               Motor going to the next CW  saved position
    // 4. GoCCW              Motor going to the next CCW saved position
    // 5. reverseWait_CW     Motor is going 180° CW
    // 6. reverseWait_CW     Motor is going 180° CCW
    //
    // Calibration Mode
    // CW switch closed:  Make motor go CW  (max 4096)
    // CCW switch closed: Make motor go CCW (min 0)

    //========================                       H o m i n g W a i t
    //are we waiting for the motor to home ?
    if (resetMachine == HomingWait)
    {
      oneStepCW();

      //we have gone 1 more CW step
      homingCounter++;
      //DPRINTLN(homingCounter);

      //========================
      //are we finished homing ?
      if (homingCounter >= homeSteps)
      {
        //motor is now sitting at HOME
        currentMotorPosition = 0;
        Serial.println(currentMotorPosition);

        //setting up toggling of the LEDs every 500ms
        homeFlashCounter = 0;
        //LED toggle duration is 500ms
        resetCommonTIMER.setInterval(500ul);
        //restart the common TIMER
        resetCommonTIMER.enableRestartTIMER();

        //all LEDs OFF
        digitalWrite(cwLED, LEDoff);
        digitalWrite(ccwLED, LEDoff);
        digitalWrite(deleteLED, LEDoff);
        digitalWrite(setPosLED, LEDoff);

        //we are at HOME, reset the "Reset State Machine"
        resetMachine = ResetFinished;

        savePower();

        //we are done, disable this TIMER
        stepperTIMER.disableTIMER();
      }
    }

    //========================                       R e s e t M o t o r
    //are we resetting the motor position to zero ?
    else if (resetMachine == ResetMotor)
    {
      oneStepCCW();

      //========================
      //has the OptoSensor gone CLOSED ?
      if (OptoSensor.lastState == OPTOclosed)
      {
        //we are at position ZERO
        currentMotorPosition = 0;

        //it is now time to HOME the motor
        //next state in the "Reset State Machine"
        resetMachine = MovingHome;

        //we are done, disable this TIMER
        stepperTIMER.disableTIMER();
      }
    }

    //========================                       G o C W
    //are we moving to the next CW stored position ?
    else if (moveToMachine == GoCW)
    {
      oneStepCW();

      //advance the Motor Position
      currentMotorPosition++;
      DPRINT(currentMotorPosition);
      DPRINTF("\t");
      DPRINTLN(destinationPosition);

      //have we arrived at the stored position
      if (currentMotorPosition == destinationPosition)
      {
        savePower();

        //next state in the "MoveTo State Machine"
        moveToMachine = MoveToFinished;

        //we are done, disable this TIMER
        stepperTIMER.disableTIMER();
      }
    }

    //========================                       G o C C W
    //are we moving to the next CCW stored position ?
    else if (moveToMachine == GoCCW)
    {
      oneStepCCW();

      //advance the Motor Position
      currentMotorPosition--;
      DPRINT(currentMotorPosition);
      DPRINTF("\t");
      DPRINTLN(destinationPosition);

      //have we arrived at the stored position
      if (currentMotorPosition == destinationPosition)
      {
        savePower();

        //next state in the "MoveTo State Machine"
        moveToMachine = MoveToFinished;

        //we are done, disable this TIMER
        stepperTIMER.disableTIMER();
      }
    }

    //========================                       R e v e r s e   T u r n t a b l e   C W
    //turntable to go CW 180° ?
    else if (reverseTurntable == reverseWait_CW)
    {
      oneStepCW();

      //advance the Motor Position
      currentMotorPosition++;

      DPRINT(currentMotorPosition);
      DPRINTF("\t");
      DPRINTLN(destinationPosition);

      //have we arrived at the 180° CW position ?
      if (currentMotorPosition == destinationPosition)
      {
        savePower();

        DPRINTF("We are at postionIndex ");
        DPRINTLN(positionIndex);

        //next state in the "reverse State Machine"
        reverseTurntable = reverseFinished;

        //we are done, disable this TIMER
        stepperTIMER.disableTIMER();
      }
    }

    //========================                       R e v e r s e   T u r n t a b l e  C C W
    //are we resetting the motor position to zero ?
    else if (reverseTurntable == reverseWait_CCW)
    {
      oneStepCCW();

      //reduce the Motor Position
      currentMotorPosition--;

      DPRINT(currentMotorPosition);
      DPRINTF("\t");
      DPRINTLN(destinationPosition);

      //have we arrived at the 180° CCW position
      if (currentMotorPosition == destinationPosition)
      {
        savePower();

        //next state in the "reverse State Machine"
        reverseTurntable = reverseFinished;

        //we are done, disable this TIMER
        stepperTIMER.disableTIMER();
      }
    }

    //========================                       C W   s w i t c h   c l o s e d
    //when the CW switch is closed AND the CCW switch is OPENED
    else if (calibrationFlag == ENABLED && currentMotorPosition < stepsPerRev && CW_Sw.lastState == CLOSED &&
             CCW_Sw.lastState == OPENED)
    {
      oneStepCW();

      //advance the Motor Position
      currentMotorPosition++;
      DPRINTLN(currentMotorPosition);
    }

    //========================                       C C W   s w i t c h   c l o s e d
    //when the CCW switch is closed AND the CW switch is OPENED
    else if (calibrationFlag == ENABLED && currentMotorPosition > 0 && CCW_Sw.lastState == CLOSED &&
             CW_Sw.lastState == OPENED)
    {
      oneStepCCW();

      //reduce the Motor Position
      currentMotorPosition--;
      DPRINTLN(currentMotorPosition);
    }

  } //END of this   TIMER


  //================================================
  //other non blocking code goes here
  //================================================

} //END of   loop()


//                                   c h e c k M a c h i n e s ( )
//================================================^================================================
void checkMachines()
{
  //service all the State Machines

  //========================================================================  "reverseTurntable State Machine"
  switch (reverseTurntable)
  {
    //========================          r e v e r s e  S t a r t u p
    case reverseStartup:
      {
        //do startup stuff here

        //next state in the "reverseTurntable State Machine"
        reverseTurntable = reverseWait1;
      }
      break;

    //========================          r e v e r s e  W a i t 1
    case reverseWait1:
      {
        //do nothing here
      }
      break;

    //========================          r e v e r s e T a b l e T o G o C W
    case reverseTableToGoCW:
      {
        //moving 180° CW
        //we are going to the 180° matching position from where we are now

        //the new motor position we want to go
        destinationPosition = storedPostions[positionIndex + maxTrackPositions / 2];

        //we will end up here
        positionIndex = positionIndex + maxTrackPositions / 2;

        DPRINTF("We are going to position #");
        DPRINT(positionIndex);
        DPRINTF("\t180° Track #");
        DPRINTLN((positionIndex - maxTrackPositions / 2) + 1 );

        restoreMotorPower();

        //next state in the moveToMachine State Machine
        reverseTurntable = reverseWait_CW;

        //movement is allowed, the Motor will move CW
        stepperTIMER.enableRestartTIMER();
      }
      break;

    //========================          r e v e r s e T a b l e T o G o C C W
    case reverseTableToGoCCW:
      {
        //moving 180° CCW
        //we are going to the -180° matching position from where we are now

        //the new motor position we want to go
        destinationPosition = storedPostions[positionIndex - maxTrackPositions / 2];

        //we will end up here
        positionIndex = positionIndex - maxTrackPositions / 2;

        DPRINTF("We are going to position #");
        DPRINT(positionIndex);
        DPRINTF("\tTrack #");
        DPRINTLN(positionIndex + 1 );

        restoreMotorPower();

        //next state in the moveToMachine State Machine
        reverseTurntable = reverseWait_CCW;

        //movement is now allowed, the Motor will move CW
        stepperTIMER.enableRestartTIMER();
      }
      break;

    //========================          r e v e r s e W a i t _ C W
    case reverseWait_CW:
      {
        //do nothing here
      }
      break;

    //========================          r e v e r s e W a i t _ C C W
    case reverseWait_CCW:
      {
        //do nothing here
      }
      break;

    //========================          r e v e r s e F i n i s h e d
    case reverseFinished:
      {
        DPRINTF("We have arrived at positionIndex #");
        DPRINT(positionIndex);

        //are we in the calculated positionIndexes ?
        if (positionIndex >= maxTrackPositions / 2)
        {
          byte temp = (positionIndex - maxTrackPositions / 2) + 1;
          DPRINTF("\t Track #");
          DPRINT(temp);
          DPRINTLNF(" at 180° degrees.");
        }

        else
        {
          DPRINTF("\t Track #");
          DPRINTLN(positionIndex + 1);
        }     
        
        //we have turned the turntable 180°
        digitalWrite(cwLED, LEDoff);
        digitalWrite(ccwLED, LEDoff);

        //next state in the "reverseTurntable State Machine"
        reverseTurntable = reverseStartup;
      }
      break;

  } //END of  "reverseTurntable State Machine"

  //========================================================================  "EEPROM State Machine"
  switch (eepromMachine)
  {
    //========================          e e p r o m S t a r t u p
    case eepromStartup:
      {
        //do startup stuff here

        //next state in the "EEPROM State Machine"
        eepromMachine = eepromWait1;
      }
      break;

    //========================          e e p r o m W a i t 1
    case eepromWait1:
      {
        //sit here and wait
      }
      break;

    //========================          e e p r o m T i m i n g
    case eepromTiming:
      {
        //a switch has been OPENED ?
        if (digitalRead(EncoderSw.pin) == OPENED || SetPosSw.lastState == OPENED)
        {
          digitalWrite(cwLED, LEDoff);
          digitalWrite(ccwLED, LEDoff);

          //a switch was let go too soon
          eepromMachine = eepromWait1;

          DPRINTLNF("Encoder switch or SetPosSw was released too soon");

          break;
        }

        //condition returned: STILLtiming, EXPIRED or TIMERdisabled
        //has the switch been closed long enough ?
        if (eepromCommonTIMER.checkTIMER() == EXPIRED)
        {
          //nothing to save at "reset / powerup time" ?
          if (positionIndex > -1)
          {
            //are we in our DATA address range (0 thru 4) ?
            if (positionIndex < maxTrackPositions / 2)
            {
              //update the positionIndex location
              storedPostions[positionIndex] = currentMotorPosition;

              //DPRINTF("Updating storedPosition ");
              //DPRINT(positionIndex);
              //DPRINTF(" with ");
              //DPRINTLN(storedPostions[positionIndex]);

              //update the positionIndex + the 180 degree location
              storedPostions[positionIndex + maxTrackPositions / 2] = storedPostions[positionIndex] + stepsIn180Degrees;

              //DPRINTF("Updating storedPosition ");
              //DPRINT(positionIndex + maxTrackPositions / 2);
              //DPRINTF(" with ");
              //DPRINTLN(storedPostions[positionIndex + maxTrackPositions / 2]);

              //calculate the EEPROM address for this position ?
              int address = (eeStartAddress + 1) + positionIndex * sizeof(long);

              //we only have 100,000 EEPROM write cycles per EEPROM location
              //when the EEPROM already has the same data, there is no updating, hence saving write cycles
              EEPROM.put(address, currentMotorPosition);

              //confirm the DATA was written
              DPRINTF("EEPROM address = ");
              DPRINT(address);
              DPRINTF("\tDATA read = ");
              long DATA;
              EEPROM.get(address, DATA);
              DPRINTLN(DATA);
            }

            //get ready to flash LEDs
            eepromLedControlTIMER.enableRestartTIMER();
            eepromLedControlTIMER.setInterval(200ul);

            //next state in the "EEPROM State Machine"
            eepromMachine = eepromFlashingLEDs;
          }
        }
      }
      break;

    //========================          e e p r o m F l a s h i n g L E D s
    case eepromFlashingLEDs:
      {
        if (eepromLedControlTIMER.checkTIMER() == EXPIRED)
        {
          //toggle LEDs
          digitalWrite(cwLED, digitalRead(cwLED) == HIGH ? LOW : HIGH);
          digitalWrite(ccwLED, digitalRead(ccwLED) == HIGH ? LOW : HIGH);
        }

        if (digitalRead(SetPosSw.pin) == OPENED)
        {
          digitalWrite(cwLED, LEDoff);
          digitalWrite(ccwLED, LEDoff);

          //next state in the "EEPROM State Machine"
          eepromMachine = eepromFinished;
        }
      }
      break;

    //========================          e e p r o m F i n i s h e d
    case eepromFinished:
      {
        //next state in the "EEPROM State Machine"
        eepromMachine = eepromStartup;
      }
      break;

  } //END of  "EEPROM State Machine"

  //========================================================================  "MoveTo State Machine"
  switch (moveToMachine)
  {
    //========================          M o v e T o S t a r t u p
    case MoveToStartup:
      {
        //do startup stuff here

        //next state in the "MoveTo State Machine"
        moveToMachine = MoveToWait1;
      }
      break;

    //========================          M o v e T o W a i t 1
    case MoveToWait1:
      {
        //wait here
      }
      break;

    //========================          G o C W
    case GoCW:
      {

      }
      break;

    //========================          G o C C W
    case GoCCW:
      {

      }
      break;

    //========================          M o v e T o F i n i s h e d
    case MoveToFinished:
      {
        DPRINTF("We have arrived at positionIndex #");
        DPRINT(positionIndex);

        //are we in the calculated positionIndexes ?
        if (positionIndex >= maxTrackPositions / 2)
        {
          byte temp = (positionIndex - maxTrackPositions / 2) + 1;
          DPRINTF("\t Track #");
          DPRINT(temp);
          DPRINTLNF(" at 180° degrees.");
        }

        else
        {
          DPRINTF("\t Track #");
          DPRINTLN(positionIndex + 1);
        }

        digitalWrite(cwLED, LEDoff);
        digitalWrite(ccwLED, LEDoff);

        //next state in the "MoveTo State Machine"
        moveToMachine = MoveToStartup;
      }
      break;

  } //END of  "MoveTo State Machine"

  //========================================================================  "Reset State Machine"
  switch (resetMachine)
  {
    //========================          R e s e t S t a r t u p
    case ResetStartup:
      {
        //do startup stuff here

        //next state in the "Reset State Machine"
        resetMachine = Check_CW_CCW_Sw;
      }
      break;

    //================================================ <-------<<<<<<<
    //When the motor is stopped,
    //this State Machine will sit
    //in this state
    //========================          C h e c k _ C W _ C C W _ S w
    case Check_CW_CCW_Sw:
      {
        //are both CW and CCW switches CLOSED ?
        if (digitalRead(CW_Sw.pin) == CLOSED && digitalRead(CCW_Sw.pin) == CLOSED)
        {
          //cancel what is happening in the MoveTo State Machine
          moveToMachine = MoveToStartup;
          //back to rest position
          positionIndex = -1;

          DPRINTLNF("Both switches are closed");

          //disable any motor movement
          stepperTIMER.disableTIMER();

          resetCommonTIMER.setInterval(CW_CCW_Interval);
          resetCommonTIMER.enableRestartTIMER();

          //next state in the "Reset State Machine"
          resetMachine = CW_CCW_Timing;
        }
      }
      break;

    //========================          C W _ C C W _ T i m i n g
    case CW_CCW_Timing:
      {
        //has a switch been released ?
        if (digitalRead(CW_Sw.pin) == OPENED || digitalRead(CCW_Sw.pin) == OPENED)
        {
          digitalWrite(cwLED, LEDoff);
          digitalWrite(ccwLED, LEDoff);

          //back to checking for the 2 switches CLOSED
          //next state in the "Reset State Machine"
          resetMachine = Check_CW_CCW_Sw;

          break;
        }

        //has the TIMER expired ?
        if (resetCommonTIMER.checkTIMER() == EXPIRED)
        {
          //during motor zeroing, turn ON the LEDs
          digitalWrite(cwLED, LEDon);
          digitalWrite(ccwLED, LEDon);
          digitalWrite(deleteLED, LEDon);
          digitalWrite(setPosLED, LEDon);

          restoreMotorPower();

          //allow the motor to move
          stepperTIMER.enableRestartTIMER();

          //next state in the "Reset State Machine"
          resetMachine = ResetMotor;
        }
      }
      break;

    //========================          R e s e t M o t o r
    case ResetMotor:
      {
        //wait here until the motor is at the zero position
      }
      break;

    //========================          M o v i n g H o m e
    case MovingHome:
      {
        DPRINTLNF("Motor is at Zero");
        DPRINTLNF("Motor is now Homing");

        //start out at the first element on our Lookup table
        stepIndex = 0;

        //initialize the number of steps away from the motor zero position
        homingCounter = 0;

        //allow the motor to move
        stepperTIMER.enableRestartTIMER();

        //next state in the "Reset State Machine"
        resetMachine = HomingWait;
      }
      break;


    //========================          H o m i n g W a i t
    case HomingWait:
      {
        //wait here while homing the motor
      }
      break;


    //========================          R e s e t F i n i s h e d
    case ResetFinished:
      {
        //we are at the Motor Home position, toggle the LEDs
        if (homeFlashCounter < 4)
        {
          //has the common TIMER expired ?
          if (resetCommonTIMER.checkTIMER() == EXPIRED)
          {
            //toggle the LEDs
            digitalWrite(cwLED, digitalRead(cwLED) == HIGH ? LOW : HIGH);
            digitalWrite(ccwLED, digitalRead(ccwLED) == HIGH ? LOW : HIGH);
            digitalWrite(deleteLED, digitalRead(deleteLED) == HIGH ? LOW : HIGH);
            digitalWrite(setPosLED, digitalRead(setPosLED) == HIGH ? LOW : HIGH);

            //ready next flash
            homeFlashCounter++;
          }

          break;
        }

        //all LEDs OFF
        digitalWrite(cwLED, LEDoff);
        digitalWrite(ccwLED, LEDoff);
        digitalWrite(deleteLED, LEDoff);
        digitalWrite(setPosLED, LEDoff);

        //we are now finished with motor calibration
        DPRINTLNF("Motor is Home");
        DPRINTLNF("System has Reset");

        //we are done, reset this State Machine
        //next state in the "Reset State Machine"
        resetMachine = ResetStartup;
      }
      break;

  } //END of  "Reset State Machine"

} //END of   checkMachines()


//                                   c h e c k S w i t c h e s ( )
//================================================^================================================
void checkSwitches()
{
  byte currentState;

  //================================================  Calibration Mode ?
  //this switch is ignored in calibration mode
  if (calibrationFlag != ENABLED)
  {
    //========================================================================  EncoderSw.pin
    //only proceed if the "EEPROM State Machine" is in the "eepromWait1" state
    //AND we are not at a calculated positionIndex
    if (eepromMachine == eepromWait1 && positionIndex < maxTrackPositions / 2)
    {
      currentState = digitalRead(EncoderSw.pin);

      //===================================
      //has this switch changed state ?
      if (EncoderSw.lastState != currentState)
      {
        EncoderSw.counter++;

        //is this change in state stable ?
        if (EncoderSw.counter >= filter)
        {
          //get ready for the next sequence
          EncoderSw.counter = 0;

          //update to this new state
          EncoderSw.lastState = currentState;

          //================================================  Encoder CLOSED ?
          //did this switch close ?
          if (currentState == CLOSED)
          {
            DPRINTLNF("Encoder switch closed");

            //we need SetPosSw held before we can update the EEPROM position
            if (SetPosSw.lastState == CLOSED)
            {
              digitalWrite(cwLED, LEDon);
              digitalWrite(ccwLED, LEDon);

              //next "EEPROM State Machine" state
              eepromMachine = eepromTiming;

              //time we need to hold this switch
              eepromCommonTIMER.setInterval(encoderSwHoldTime);

              //enable the common TIMER
              eepromCommonTIMER.enableRestartTIMER();
            }
          }

          //========================
          //did this switch open ?
          else if (currentState == OPENED)
          {
            DPRINTLNF("Encoder switch opened");
          }
        }
      }

      //===================================
      //a valid switch change has not been confirmed
      else
      {
        EncoderSw.counter = 0;
      }
    }
  } //END of   if (calibrationFlag != ENABLED)

  //END of EncoderSw.pin

  //================================================  Calibration Mode ?
  //CW_Sw and CCW_Sw will not be monitored if we are in calibration mode
  if (calibrationFlag == DISABLED)
  {
    //proceed as normal

    //========================================================================  CW_Sw.pin
    //only proceed if the "Reset State Machine" not busy AND
    //we are not currently moving to the next position
    if (resetMachine == Check_CW_CCW_Sw && moveToMachine == MoveToWait1)
    {
      //has CW_Sw been held the long push time ?
      if (cwLED_Flag == ENABLED)
      {
        if (millis() - CW_Sw.switchTime >= longPush)
        {
          digitalWrite(cwLED, LEDon);
        }
      }

      currentState = digitalRead(CW_Sw.pin);

      //===================================
      //has this switch changed state ?
      if (CW_Sw.lastState != currentState)
      {
        CW_Sw.counter++;

        //is this change in state stable ?
        if (CW_Sw.counter >= filter)
        {
          //get ready for the next sequence
          CW_Sw.counter = 0;

          //update to this new state
          CW_Sw.lastState = currentState;

          //================================================  CW_Sw CLOSED ?
          //did this switch close ?
          if (currentState == CLOSED)
          {
            //capture the current time
            CW_Sw.switchTime = millis();

            //the cwLED will come on at the long press time
            cwLED_Flag = ENABLED;
          }

          //========================
          //this switch went open
          else
          {
            //no longer need now
            cwLED_Flag = DISABLED;

            //================================================ S h o r t   P r e s s  ?
            //was this a short push
            if (millis() - CW_Sw.switchTime <= shortPushTime)
            {
              //get ready for the next position
              positionIndex++;

              //do not go past the last stored count
              if (positionIndex >= maxTrackPositions)
              {
                //stay where we are
                positionIndex = positionIndex - 1;

                DPRINTLNF("Upper limit reached");
              }

              //are we already at the position ?
              if (currentMotorPosition != storedPostions[positionIndex])
              {
                //the new motor position we want to go
                destinationPosition = storedPostions[positionIndex];

                restoreMotorPower();

                //next state in the moveToMachine State Machine
                moveToMachine = GoCW;

                //movement is now allowed, the Motor will move CW
                stepperTIMER.enableRestartTIMER();

                digitalWrite(cwLED, LEDon);
              }
            }

            //================================================ L o n g   P r e s s  ?
            //CCW_Sw must be open AND was this a long push ?
            //(indicating the user wants to turn the table 180° CW)
            else if (CCW_Sw.lastState == OPENED && millis() - CW_Sw.switchTime >= longPush)
            {
              digitalWrite(cwLED, LEDoff);

              //we must be between positions 0 and 4 inclusive to be able to turn CW 180°
              if (positionIndex < maxTrackPositions / 2)
              {
                //next state in the "reverseTurntable State Machine"
                //go CW 180°
                reverseTurntable = reverseTableToGoCW;
              }
            }
          } //END of   else    "this switch went open"
        }
      }

      //===================================
      //a valid switch change has not been confirmed
      else
      {
        CW_Sw.counter = 0;
      }

      //END of CW_Sw.pin
    }


    //========================================================================  CCW_Sw.pin
    //only proceed if the "Reset State Machine" not busy AND
    //we are not currently moving to the next position
    if (resetMachine == Check_CW_CCW_Sw && moveToMachine == MoveToWait1)
    {
      //has CCW_Sw been held the long push time ?
      if (ccwLED_Flag == ENABLED)
      {
        if (millis() - CCW_Sw.switchTime >= longPush)
        {
          digitalWrite(ccwLED, LEDon);
        }
      }

      currentState = digitalRead(CCW_Sw.pin);

      //===================================
      //has this switch changed state ?
      if (CCW_Sw.lastState != currentState)
      {
        CCW_Sw.counter++;

        //is this change in state stable ?
        if (CCW_Sw.counter >= filter)
        {
          //get ready for the next sequence
          CCW_Sw.counter = 0;

          //update to this new state
          CCW_Sw.lastState = currentState;

          //================================================  CCW_Sw CLOSED ?
          //did this switch close ?
          if (currentState == CLOSED)
          {
            //capture the current time
            CCW_Sw.switchTime = millis();

            //the cwLED will come on at the long press time
            ccwLED_Flag = ENABLED;
          }

          //========================
          //this switch went open
          else
          {
            //no longer need now
            ccwLED_Flag = DISABLED;

            //================================================ S h o r t   P r e s s  ?
            //was this a short push
            if (millis() - CCW_Sw.switchTime < shortPushTime)
            {
              //are we at power up time "OR" at position 0 ?
              if (currentMotorPosition == 0 ||  currentMotorPosition <= storedPostions[0])
              {
                DPRINTLNF("Lower limit reached");
              }

              else
              {
                //get ready for the next position
                positionIndex--;

                //do not go below index 0 ?
                if (positionIndex < 0)
                {
                  //stay where we are
                  positionIndex = 0;
                }

                //the new motor position we want to go
                destinationPosition = storedPostions[positionIndex];

                restoreMotorPower();

                //next state in the moveToMachine State Machine
                moveToMachine = GoCCW;

                //movement is now allowed, the Motor will move CW
                stepperTIMER.enableRestartTIMER();

                digitalWrite(ccwLED, LEDon);
              }
            }

            //================================================ L o n g   P r e s s  ?
            //CW_Sw must be open AND
            //was this a long push ? (indicating the user wants to turn the table CCW 180°)
            else if (CW_Sw.lastState == OPENED && millis() - CCW_Sw.switchTime >= longPush)
            {
              digitalWrite(ccwLED, LEDoff);

              //we must be between positions 5 and 9 inclusive to be able to turn CCW 180°
              if (positionIndex < maxTrackPositions && positionIndex >= maxTrackPositions / 2)
              {
                //next state in the "reverseTurntable State Machine"
                //to go CCW 180°
                reverseTurntable = reverseTableToGoCCW;
              }
            }
          } //END of   else  "this switch went open"
        }
      }

      //===================================
      //a valid switch change has not been confirmed
      else
      {
        CCW_Sw.counter = 0;
      }

      //END of CCW_Sw.pin

    }

  } //END of   we were not in calibration mode


  //================================================  We are in Calibration Mode
  //we are in calibration mode
  else
  {
    //========================================================================  CW_Sw.pin  Calibration Mode
    //only proceed if the "Reset State Machine" is sitting in the Check_CW_CCW_Sw state
    if (resetMachine == Check_CW_CCW_Sw)
    {
      currentState = digitalRead(CW_Sw.pin);

      //===================================
      //has this switch changed state ?
      if (CW_Sw.lastState != currentState)
      {
        CW_Sw.counter++;

        //is this change in state stable ?
        if (CW_Sw.counter >= filter)
        {
          //get ready for the next sequence
          CW_Sw.counter = 0;

          //update to this new state
          CW_Sw.lastState = currentState;

          //========================
          //did this switch close ?
          if (currentState == CLOSED)
          {
            DPRINTLNF("Motor going Clockwise");

            restoreMotorPower();

            //movement is now allowed and the Motor will move CW
            stepperTIMER.enableRestartTIMER();

            digitalWrite(cwLED, LEDon);
          }

          //========================
          //did this switch open ?
          else if (currentState == OPENED)
          {
            DPRINTLNF("Motor Stopped");

            savePower();

            //the TIMER is now disabled and the Motor will stop
            stepperTIMER.disableTIMER();

            digitalWrite(cwLED, LEDoff);
          }
        }
      }

      //===================================
      //a valid switch change has not been confirmed
      else
      {
        CW_Sw.counter = 0;
      }
      //END of CW_Sw.pin

    }


    //========================================================================  CCW_Sw.pin  Calibration Mode
    //only proceed if the "Reset State Machine" is sitting in the Check_CW_CCW_Sw state
    if (resetMachine == Check_CW_CCW_Sw)
    {
      currentState = digitalRead(CCW_Sw.pin);

      //===================================
      //has this switch changed state ?
      if (CCW_Sw.lastState != currentState)
      {
        CCW_Sw.counter++;

        //is this change in state stable ?
        if (CCW_Sw.counter >= filter)
        {
          //get ready for the next sequence
          CCW_Sw.counter = 0;

          //update to this new state
          CCW_Sw.lastState = currentState;

          //========================
          //did this switch close ?
          if (currentState == CLOSED)
          {
            DPRINTLNF("Motor going Counter Clockwise");

            restoreMotorPower();

            //movement is now allowed and the Motor will move CCW
            stepperTIMER.enableRestartTIMER();

            digitalWrite(ccwLED, LEDon);
          }

          //========================
          //did this switch open ?
          else if (currentState == OPENED)
          {
            DPRINTLNF("Motor Stopped");

            savePower();

            //the TIMER is now disabled and the Motor will stop
            stepperTIMER.disableTIMER();

            digitalWrite(ccwLED, LEDoff);
          }
        }
      }

      //===================================
      //a valid switch change has not been confirmed
      else
      {
        CCW_Sw.counter = 0;
      }

      //END of CCW_Sw.pin
    }

  } //END of else (we are in calibration mode)

  //========================================================================  DeleteSw.pin
  currentState = digitalRead(DeleteSw.pin);

  //===================================
  //has this switch changed state ?
  if (DeleteSw.lastState != currentState)
  {
    DeleteSw.counter++;

    //is this change in state stable ?
    if (DeleteSw.counter >= filter)
    {
      //get ready for the next sequence
      DeleteSw.counter = 0;

      //update to this new state
      DeleteSw.lastState = currentState;

      //================================================  DeleteSw CLOSED ?
      //did this switch close ?
      if (currentState == CLOSED)
      {
        DPRINTLNF("Delete switch closed");

        digitalWrite(deleteLED, LEDon);
      }

      //========================
      //did this switch open ?
      else if (currentState == OPENED)
      {
        DPRINTLNF("Delete switch opened");

        digitalWrite(deleteLED, LEDoff);
      }
    }
  }

  //===================================
  //a valid switch change has not been confirmed
  else
  {
    DeleteSw.counter = 0;
  }

  //END of DeleteSw.pin

  //========================================================================  SetPosSw.pin
  currentState = digitalRead(SetPosSw.pin);

  //===================================
  //has this switch changed state ?
  if (SetPosSw.lastState != currentState)
  {
    SetPosSw.counter++;

    //is this change in state stable ?
    if (SetPosSw.counter >= filter)
    {
      //get ready for the next sequence
      SetPosSw.counter = 0;

      //update to this new state
      SetPosSw.lastState = currentState;

      //================================================  SetPosSw CLOSED ?
      //did this switch close ?
      if (currentState == CLOSED)
      {
        DPRINTLNF("Set switch closed");

        //toggle LED
        //digitalWrite(setPosLED, digitalRead(setPosLED) == HIGH ? LOW : HIGH);

        digitalWrite(setPosLED, LEDon);
      }

      //========================
      //did this switch open ?
      else if (currentState == OPENED)
      {
        DPRINTLNF("Set switch opened");

        digitalWrite(setPosLED, LEDoff);
      }
    }
  }

  //===================================
  //a valid switch change has not been confirmed
  else
  {
    SetPosSw.counter = 0;
  }

  //END of SetPosSw.pin

  //========================================================================  OptoSensor.pin
  currentState = digitalRead(OptoSensor.pin);

  //===================================
  //has this switch changed state ?
  if (OptoSensor.lastState != currentState)
  {
    OptoSensor.counter++;

    //is this change in state stable ?
    if (OptoSensor.counter >= filter)
    {
      //get ready for the next sequence
      OptoSensor.counter = 0;

      //update to this new state
      OptoSensor.lastState = currentState;

      //================================================  OptoSensor CLOSED ?
      //did this switch close ?
      //if (currentState == CLOSED)
      if (currentState == OPTOclosed)
      {
        DPRINTLNF("Opto Sensor has Closed");
      }

      //========================
      //did this switch open ?
      //else if (currentState == OPENED)
      else if (currentState == OPTOopened)
      {
        DPRINTLNF("Opto Sensor has Opened");
      }
    }
  }

  //===================================
  //a valid switch change has not been confirmed
  else
  {
    OptoSensor.counter = 0;
  }

  //END of OptoSensor.pin

} //END of   checkSwitches()


//                               r e a d R o t a r y E n c o d e r ( )
//================================================^================================================
void readRotaryEncoder()
{
  static byte lastStateA = digitalRead(EncoderOutA);
  byte currentStateA;

  currentStateA = digitalRead(EncoderOutA);

  //=========================================
  //has there been a state change ?
  if (lastStateA != currentStateA)
  {
    //update to the new state
    lastStateA = currentStateA;

    //========================
    //when the outputB state is different from the outputA state
    //we are rotating clockwise CW
    if (digitalRead(EncoderOutB) != currentStateA)
    {
      //CW direction
      //============
      //don't let the motor go more than stepsPerRrev
      if ((currentMotorPosition + 10) <= (storedPostions[4] + stepsPerRev))
      {
        //make the motor go CW
        restoreMotorPower();

        for (byte x = 0; x < 10; x++)
        {
          oneStepCW();

          delayMicroseconds(2000ul);
        }

        savePower();

        //increment the Motor Position
        currentMotorPosition = currentMotorPosition + 10;
        DPRINT(currentMotorPosition);
        DPRINTLNF("\t Motor not calibrated");
      }
    }

    //========================
    //we are rotating counter clock wise CCW
    else
    {
      //CCW direction
      //============
      //don't let the motor go less than 0
      if ((currentMotorPosition - 10) >= 0)
      {
        //make the motor go CCW
        restoreMotorPower();

        for (byte x = 0; x < 10; x++)
        {
          oneStepCCW();

          delayMicroseconds(2000ul);
        }

        savePower();

        //decrement the Motor Position
        currentMotorPosition = currentMotorPosition - 10;
        DPRINT(currentMotorPosition);
        DPRINTLNF("\t Motor not calibrated");
      }
    }
  }

} //END of  readRotaryEncoder()


//                                       s t e p M o t o r ( )
//================================================^================================================
void stepMotor(int output)
{
  digitalWrite(IN1, bitRead(lookup[output], 0));
  digitalWrite(IN2, bitRead(lookup[output], 1));
  digitalWrite(IN3, bitRead(lookup[output], 2));
  digitalWrite(IN4, bitRead(lookup[output], 3));

} //END of  stepMotor()


//                                       o n e S t e p C W ( )
//================================================^================================================
void oneStepCW()
{
  //get ready for the next step
  stepIndex++;

  //should we go back to the beginning index ?
  if (stepIndex > 7)
  {
    stepIndex = 0;
  }

  //move the motor
  stepMotor(stepIndex);

} //END of  oneStepCW()


//                                      o n e S t e p C C W ( )
//================================================^================================================
void oneStepCCW()
{
  //get ready for the next step
  stepIndex--;

  //should we go back to the ending index ?
  if (stepIndex < 0)
  {
    stepIndex = 7;
  }

  //move the motor
  stepMotor(stepIndex);

} //END of  oneStepCCW()


//                                       s a v e P o w e r ( )
//================================================^================================================
void savePower()
{
  //to save power, turn off stepper coils
  //save the current stepIndex
  savedStepIndex = stepIndex;
  stepIndex = 8;
  stepMotor(stepIndex);

} //END of   savePower()


//                                    r e s t o r e P o w e r ( )
//================================================^================================================
void restoreMotorPower()
{
  //power up, turn on stepper coils
  //restore the saved Motor stepIndex
  stepIndex = savedStepIndex;
  stepMotor(stepIndex);

} //END of   restoreMotorPower()


//                                      d e b u g P r i n t ( )
//================================================^================================================
void debugPrint()
{
  //use this line to halt the sketch and print certain variables
  //
  //debugPrint();  //                                       D E B U G   H A L T

  //print these variables to the serial monitor and HALT
  //
  //  DPRINTLNF("\nCurrent debug variables selected");
  //  DPRINTF("currentMotorPosition = ");
  //  DPRINTLN(currentMotorPosition);
  //  DPRINTF("Index 0 = ");
  //  DPRINTLN(storedPostions[0]);
  //  DPRINTF("Index 5 = ");
  //  DPRINTLN(storedPostions[5]);
  //  DPRINTF("180 degrees = ");
  //  DPRINTLN(stepsIn180Degrees);
  //  DPRINTLN(eeReadData,HEX);

  //stop code execution
  while (1);

} //END of   debugPrint()


//
//================================================^================================================
  • Think you have enough to do for now (I have to go and plant the garden).

  • Ask questions . . . . .

thank you. its all saved and i can go back over it all with our comments and annotate accordingly.

LOL yes this would appear to be right up my street at first glance. love a good tip lol.
Not sure the wife would appreciate me using her 'utensils' to model etc with though.
I will have a good read of it all over the next day or so between jobs. could be some useful stuff in there.