#include <EEPROM.h>
#include <Servo.h>
const bool HOME = 0; //alleviates having to think about ones and zeros when evaluating logic
const bool THROWN = !HOME; //
struct TURNOUTS { //values that are static and must be stored for use after next reset/power up; serial function writeline(Turnout) must be updated if this structure changes
uint8_t servoPin; //where we send the pulse to position the servo
uint8_t buttonPin; //where we get the setting for the servo from(i.e. the Touch Toggle connection)
int16_t homePosition; //the angle needed for 'homing' the turnout
int16_t thrownPosition; //the angle needed for 'throwing' the turnout
int8_t servospeed; //initially zero, causes ASAP movement; positive values are degrees/update, negative are updates/degree, allowing a broad range
};
struct TURNOUTV { //values that are volatile, initialized after every reset/power up
bool state; //commanded state of the servo (Touch Toggle)
Servo ourServo; //a holder for the instance of the servo
};
struct TURNOUT {
TURNOUTS S; //static elements
TURNOUTV V; //volatile elements
};
TURNOUT Turnouts[] = { //items shown are for 8 servos on a classic Nano; clearly, the list would change for different Arduinos; serial function writeline(Turnout) must be updated if this structure changes
{{2, 3, 30, 140, 0}, {}}, //a servo on pin 2, button on pin 3; moves from 45 to 135; default speed ASAP; note the volatile elements don't get initialized
{{4, 5, 31, 141, 0}, {}}, //a servo on pin 4, button on pin 5
{{6, 7, 32, 142, 0}, {}},
{{8, 9, 33, 143, 0}, {}},
{{10, 11, 34, 144, 0}, {}},
{{14, 15, 35, 145, 0}, {}},
{{16, 17, 36, 146, 0}, {}},
{{18, 19, 37, 147, 0}, {}},
};
const uint8_t NumTurnouts = sizeof(Turnouts) / sizeof(TURNOUT); //automatically determines the number of turnouts represented in the Turnouts array - user just adds/deletes rows as desired
//uint8_t PresTurnout = 0; //counter used to indicate which turnout is presently active in update
void setup() {
Serial.begin(115200); //configures the connection to Serial Monitor, the terminal window for the IDE
Serial.flush(); //swallows any startup trash
// dumpEEPROM(0, 0x60);
// writeEEPROM(); //writes new values to EEPROM
// dumpEEPROM(0, 0x60);
readEEPROM(); //reads settings from EEPROM; function also will write EEPROM after a dialog, if the EEPROM did not appear to have been configured
// dumpEEPROM(0, 0x60); //compare against what we know EEPROM should have based on data (signature, turnout count, turnout size, turnouts 0-7)
// swapAllPos(); //modify turnouts by swapping home and thrown positions
// writeEEPROM(); //writes new values to EEPROM
// dumpEEPROM(0, 0x60); //verify all swapped positions were written
// swapPos(1); //modify 2nd turnout's positions again
// writeTurnout(1);
// reportTurnout(1);
// readTurnout(1); //restore 2nd turnouts positions
// reportTurnout(1);
// reportTurnout(2);
// swapPos(2); //change 3rd turnout's positions
// reportTurnout(2);
// writeTurnout(2); //store changed turnout
// dumpEEPROM(0, 0x60); //verify only 3rd Turnout changed
}//end of setup ******************************************************************************************************
void swapAllPos() {
for (uint8_t n = 0; n < NumTurnouts; n++)
swapPos(n);
}//end swapAllPos
void swapPos(uint8_t nn) { //swap home and thrown positions for indicated turnout
uint16_t temp = Turnouts[nn].S.homePosition;
Turnouts[nn].S.homePosition = Turnouts[nn].S.thrownPosition;
Turnouts[nn].S.thrownPosition = temp;
}
void reportTurnout(uint8_t tt) {
Serial.print(F(" Turnout ")); Serial.print(tt); //remind the user which turnout this config is for
Serial.print(F(" Speed: ")); Serial.print(Turnouts[tt].S.servospeed); //Serial.println(); //print the speed setting
Serial.print(F(" Servo Pin: ")); Serial.print(Turnouts[tt].S.servoPin); //print the servo pin
Serial.print(F(" Buttn Pin: ")); Serial.print(Turnouts[tt].S.buttonPin); //Serial.println(); //print the button pin
Serial.print(F(" Home A: ")); Serial.print(Turnouts[tt].S.homePosition); //print the home angle
Serial.print(F(" Thrown A: ")); Serial.print(Turnouts[tt].S.thrownPosition); //Serial.println(); //print the thrown angle
Serial.print(F(" State: ")); Turnouts[tt].V.state == HOME ? Serial.print(F(" HOME ")) : Serial.print(F("THROWN ")); //print the state
Serial.print(F(" Position: ")); Serial.println(Turnouts[tt].V.ourServo.read()); //print the state
}//end reportTurnout **********************************************************************************************
void loop() {
}//end of loop *******************************************************************************************************************
//SEC EEPROM
/* Notes about the stored content of the EEPROM:
Byte addresses 0 and 1 must contain 0xAA, 0x55 respectively, or the EEPROM is presumed to be empty. This is the "signature" referred to.
byte address 2 tells us how many Turnout entries are in the table
byte address 3 tells us how many bytes make up each Turnout entry in the table
Values for each turnout variable that must be stored are then stored sequentially.
EEPROM layout:
uint16_t signature
uint8_t sizeTable
uint8_t sizeTurnout
2d array of bytes - [tableSize}{turnoutSize]
public EEPROM functions to date:
readEEPROM() - read entire EEPROM.
readTurnout(TO) - read current Turnout from EEPROM,
writeEEPROM() - write entire EEPROM, and
writeTurnout(TO) - write current Turnout to EEPROM,
dumpEEPROM(add, len) - print, in hex, in 32 byte lines, EEPROM content starting at add, running for length bytes.
All other functions should be considered private to the EEPROM managment code.
It behooves the user to write regularly, to avoid loss of settings should a read be necessary!
*/
const uint16_t SIGNATURE = 0x55aa;
const uint16_t EEPBASE = 0; //EEPROM starts at address zero in Nano, Uno, and Mega processors
const uint16_t SIGLOC = EEPBASE;
const uint16_t TABSIZLOC = SIGLOC + sizeof(SIGNATURE);
const uint16_t ITMSIZLOC = TABSIZLOC + sizeof(NumTurnouts);
const uint16_t TABLOC = ITMSIZLOC + sizeof(NumTurnouts);
void readEEPROM() {//reads settings from EEPROM; function also will write EEPROM after a dialog, if the EEPROM did not appear to have been configured
if (checkEEPROMSignature() && getTurnoutNum() && getTurnoutSize()) {
Serial.print("Reading Turnout Array\n");
readTurnoutArray();//read in the array
}
else {
Serial.print("Writing EEPROM\n");
writeEEPROM();
}
}//end readEEPROM *********************************************************************************************************
bool checkEEPROMSignature() { //verifies that two byte signature is present in locations 0,1 of EEPROM
uint16_t EEPSig;
EEPROM.get(SIGLOC, EEPSig);
if (SIGNATURE == EEPSig) return true;
else {
Serial.print("Signature error\n");
return false;
}
}//end checkEEPROMSignature *********************************************************************************************************
bool getTurnoutNum() { //reads stored number from EEPROM; must match NumTurnouts
uint8_t sizeTable;
EEPROM.get(TABSIZLOC, sizeTable);
if (sizeTable == NumTurnouts) return true;
else {
Serial.print("TurnoutNum incorrect\n");
return false;
}
}//end getTurnoutNum *********************************************************************************************************
bool getTurnoutSize() { //reads stored number from EEPROM; must match sizeof(Turnout)
uint8_t sizeTurnout;
EEPROM.get(ITMSIZLOC, sizeTurnout);
if (sizeTurnout == sizeof(TURNOUT)) return true;
else {
Serial.print("TURNOUT size error\n");
return false;
}
}//end getTurnoutSize *********************************************************************************************************
void readTurnoutArray() { //reads data into Turnouts[] based on two sizes read
for (uint8_t to = 0; to < NumTurnouts; to++) {
readTurnout(to);
}
}//end readTurnoutArray *********************************************************************************************************
void readTurnout(uint8_t to) { //called numTurnout times by readTurnoutArray()
EEPROM.put(TABLOC + to * sizeof(TURNOUT), Turnouts[to]); //perform read
}//end readTurnout *********************************************************************************************************
void writeEEPROM() { //write signature, table size, element size, and table contents to EEPROM
EEPROM.put(SIGLOC, SIGNATURE); //write sig
EEPROM.put(TABSIZLOC, NumTurnouts); //write number of elements
EEPROM.put(ITMSIZLOC, sizeof(TURNOUT)); //write number of bytes in an element
for (uint8_t to = 0; to < NumTurnouts; to++) { //write Turnouts
writeTurnout(to);
}
}//end writeEEPROM *********************************************************************************************************
void writeTurnout(uint8_t to) { //writes specified Turnout to EEPROM if loop detects a change; also called repetitively by writeEEPROM()
EEPROM.put(TABLOC + to * sizeof(TURNOUT), Turnouts[to]); //perform write
} //end writeTurnout *********************************************************************************************************
void dumpEEPROM(uint16_t start, uint16_t length) { //utility to display EEPROM content on request. Call from SERIALS
uint8_t dat;
for (uint16_t add = start; add < start + length; add++) {
if (add % 32 == 0) Serial.print("\n ");
dat = EEPROM.read(add);
if (dat < 16) Serial.print('0');
Serial.print(dat, HEX);
if(add % 32 != 31) Serial.print(' ');
}
Serial.println();
}//end dumpEEPROM *************************************************************************************************************