/*
Forum: https://forum.arduino.cc/t/variable-types-used-in-state-machine/1405843
Wokwi: https://wokwi.com/projects/441879724354787329
*/
constexpr uint8_t analogPins[] = {A3, A4, A5}; // analog pin names for the LEDs
constexpr uint8_t ledNumber = sizeof(analogPins) / sizeof(analogPins[0]);
constexpr uint8_t buttonPin = {A0};
constexpr unsigned long base_time_increment = 150; // increment of time between LEDs
constexpr unsigned long base_time_offset = 200; // time added to all of the timing data
constexpr unsigned long overall_time = (base_time_offset + base_time_increment*ledNumber) * 2; // Complete cycle time
byte direction; // Holds the state of the switch to control the "led off" timing
unsigned long previousMillis = 0; // Holds the time when one cycle starts
unsigned long timing[ledNumber]; // Time interval after that a certain led has to be switched off
uint8_t ledState[ledNumber]; // Actual state of a certain led
void setup() {
Serial.begin(115200);
pinMode(buttonPin, INPUT_PULLUP);
direction = digitalRead(buttonPin); // Get the actual button state at start/restart
Serial.print("Overall time:\t");
Serial.println(overall_time);
// Calculate the timings for direction HIGH,
// print the timings for both directions (HIGH and LOW)
// and set pinMode for all pins declared in analogPins[]
for (int i = 0; i < ledNumber; i++) {
timing[i] = base_time_offset + base_time_increment * (i + 1);
pinMode(analogPins[i], OUTPUT);
Serial.print("Led "); Serial.print(i + 1); Serial.print("\t");
Serial.print(timing[i]); Serial.print("\t"); Serial.println(overall_time - timing[i]);
}
allLedsOn();
}
void loop() {
handleDirection(); // Checks the switch and changes "direction" if required
handleLeds(); // Checks the led states and switches off and on depending on the timing
printLedStatesEveryMS(10); // Uncomment this line and start the Serial Plotter View to see a graphical "state" display
}
// Routine to set a pin to a given state and store the actual state in ledState[]
void setLed(uint8_t no, byte state) {
digitalWrite(analogPins[no], state);
ledState[no] = state;
}
// Function to switch all leds on and set previousMillis as the common sync basis
void allLedsOn() {
for (int i = 0; i < ledNumber; i++) {
setLed(i, HIGH);
}
previousMillis = millis();
}
// Function that checks the state of the switch
// If the state changes we wait for 50 ms (a very simple way to debounce the mechanical switch)
// then "direction" is set to the actual switch state and all leds are turned on
// As allLedsOn() sets previousMillis to the recent time a new cycle begins
void handleDirection() {
uint8_t actState = digitalRead(buttonPin);
if (actState != direction) {
delay(50); // Very Simple Debouncing ...
direction = actState;
allLedsOn();
}
}
// For each led where the ledState[] is HIGH this function determines the appropriate interval to switch it off
// To determine the correct timing based on the switch/button state the "ternary operator" is used:
// If "direction" is HIGH (equals true) the interval equals timing[i] else overall_time - timing[i]
// If the on-time of a led is over it is set to LOW.
// Finally - if all leds are off - we check if the overall_time has expired.
// If yes the function allLedsOn() sets all leds to HIGH and starts the new cycle
void handleLeds() {
unsigned long currentMillis = millis();
boolean allLedsAreOff = true;
for (int i = 0; i < ledNumber; i++) {
if (ledState[i]) {
unsigned long interval = direction ? timing[i] : overall_time - timing[i];
if (currentMillis - previousMillis > interval) {
setLed(i, LOW);
}
// if at least one single led is On the value of allLedsAreOff will be set to false
if (ledState[i]) {
allLedsAreOff = false;
}
}
}
if (allLedsAreOff) {
if (currentMillis - previousMillis > overall_time) {
allLedsOn();
}
}
}
// This function prints the led states so that they can be tracked with the Serial Plotter
// Each led's state is multiplied by the led no so that they can be easily distinguished
// on the screen by their height
void printLedStatesEveryMS(unsigned long interval) {
static unsigned long lastPrint = 0;
if (millis() - lastPrint < interval) {
return;
};
lastPrint = millis();
for (int i = 0; i < ledNumber; i++) {
Serial.print(ledState[i] * (i + 1));
Serial.print("\t");
}
Serial.println();
}