-
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

- 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.
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
.
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?
i had meant this one
so where does return A go that is different from return B?
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.
![]()
- 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.

- 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.








