#include <Servo.h>
/**************************************************************************************************/
/* Lesson 4b - Adding Complexity While Simplifying */
/* - modularizing code/removing redundancy */
/**************************************************************************************************/
/* A note on data naming conventions */
// Constants and structure definitions are denoted by ALL CAPS; global variables have first letter capitalized;
// all other variables and function names use what is called camelCase, like:
// thisIsAFunctionCall() butThisIsAVariable
// variables created local to a single function/subfunction may be terse; e.g. an index in a for loop could
// be simply "i"; however, this habit should be minimized, as it leads to dense but incomprehensible code
//deviations from this should be justifiable, usually due to common acronyms, like PWMSpeed or CPU_MHz
const bool HOME = 0; //alleviates having to think about ones and zeros when evaluating logic
const bool THROWN = 1; //
struct TURNOUT { //a structure definition like this is often considered to be akin to a constant
int servoPin; //where we send the pulse to to position the servo
int buttonPin; //where we get the setting for the servo from
int homePosition; //the angle needed for 'homing' the turnout
int thrownPosition; //the angle needed for 'throwing' the turnout
bool state; //where we store the present state of the servo
Servo ourServo; //a holder for the instance of the servo
};
//Let's manage two turnouts now, by making "turnout" into an array of turnouts; this array is the heart of how we don't have to duplicate code
TURNOUT Turnouts[] = { //I have expanded to two turnouts, but you can imagine that this could hold 10, or 20, or 30...
{2, 3, 45, 135}, //a servo on pin 2, state determined by pin 3; note that we do not HAVE to initialize state, it gets read anyway, nor ourServo, as it is managed by 'attach'
{4, 5, 35, 145} //a servo on pin 4, state determined by pin 5, with different angles
};
//I have incorporated only two items right now, because there are complexities coming down the road that will change our data structure, but while we talk through what I've done so far, KISS
//and we'll create a constant that tells us automagically how many turnouts are in the array, so we don't have to use fingers and toes
//In order to manage the array now, we'll need to use a for loop; a loop needs to know how often to iterate, so we need a number that tells us how many items are to be managed:
int NumTurnouts = sizeof(Turnouts) / sizeof(TURNOUT); //all this says is "tell me the size of that turnout array(in bytes), and divide that by the size of the TURNOUT structure, to arrive at the number of turnouts represented in the array
//let's make that loop counter int in both setup and loop a global
int PresTurnout = 0; //counter used to indicate which turnout is presently active
void setup() { //of course now, we need to handle both turnouts, so we'll add a for loop to work on each one:
for (int i = 0; i < NumTurnouts;) { //so, for each turnout in the array, do the following actions
i = updateTurnout(i); //setup and loop both contain a kernel of identical code. Let's make that a function, esp. since it's going to get bigger later
if (i == 0) break;
}//end of for loop
}//end of setup()
void loop() {//note how, since loop repeats endlessly, we don't need a for loop in loop.
PresTurnout = updateTurnout(PresTurnout);//updateTurnout will return the same turnout number if it's not done with the update, else it returns the next turnout to be managed
}
bool updateTurnout(int t) {// this function will get much more complex, but this is it's essence
Turnouts[t].state = digitalRead(Turnouts[t].buttonPin); //find out what position the switch is in
if (Turnouts[t].state == HOME)
Turnouts[t].ourServo.write(Turnouts[t].homePosition); //set position
else
Turnouts[t].ourServo.write(Turnouts[t].thrownPosition); //set position
Turnouts[t].ourServo.attach(Turnouts[t].servoPin); //tells ourServo what pin to send signal on
delay(1000); //must presume servo misaligned, wait for it to settle
Turnouts[t].ourServo.detach(); //must leave servo detached for power conservation
return (t + 1) % NumTurnouts; //select next turnout, automatically wrap around if at end of array
} //end of updateTurnout
//At this point, the program will seem extremely slow to respond, as we have an unconditional 1s update delay. That will change.