/*
   Select between several LED sequences
   https://forum.arduino.cc/t/selectable-led-sequence-with-push-button-to-run-program-only-one/1126244/5
   2023-05-13 by noiasca
   sketch in forum
*/

const uint8_t switchPin = 2;                      // switch is connected to pin 2
const uint8_t pin[] {6, 7, 8, 9, 10, 11, 12, 13}; // the used output pins
const bool runOnce = true;                        // run only once by pushing
const bool on = HIGH;                             // set to LOW if output(relay) is LOW active
const bool off = LOW;                             // set to HIGH if output(relay) is LOW active
int buttonState;                                  // variable to hold the button state
int mode = 0;                                     // What mode is the light in?

struct Step {                // each step consists of:
  uint16_t out;              //   a bitmask
  uint32_t interval;         //   how long to be displayed
};

// now we create an array of steps, several steps are one sequence
// just define for each step, which output schould be on/which one should be off and the interval
Step sequenceA[] {
  {0b00000001, 1000},
  {0b00000101, 1000},
  {0b00010101, 1000},
  {0b01010101, 1000},
  {0b01010100, 500},
  {0b01010110, 500},
  {0b01010010, 500},
  {0b01011010, 500},
  {0b01001010, 500},
  {0b01101010, 500},
  {0b00101010, 500},
  {0b10101010, 500},
  {0b00000000, 500},
};

Step sequenceC[] {
  {0b00000001, 500},
  {0b00000011, 500},
  {0b00000111, 500},
  {0b00001111, 500},
  {0b00011111, 500},
  {0b00111111, 500},
  {0b01111111, 500},
  {0b11111111, 500},
  {0b11111110, 500},
  {0b11111100, 500},
  {0b11111000, 500},
  {0b11110000, 500},
  {0b11100000, 500},
  {0b11000000, 500},
  {0b10000000, 500},
  {0b00000000, 500},
};

byte actual = 0;  // step within a sequence
// this takes care of the time management of the sequence and runs the steps
// it needs a pointer to a sequence (=an array of steps), and the number of steps
void runSequence(Step * step, size_t noOfSteps) {
  static uint32_t previousMillis = 0;
  uint32_t currentMillis = millis();
  if (currentMillis - previousMillis > step[actual].interval) {
    previousMillis = currentMillis;
    if (actual >= noOfSteps && runOnce) return;
    Serial.print(F(" mode=")); Serial.print(mode);       // just for debug printing ...
    Serial.print(F(" actual=")); Serial.print(actual);
    Serial.print(F("\t0b"));
    for (int i = sizeof(pin) - 1; i >= 0 ; --i) {
      if (step[actual].out & (1 << i)) {
        digitalWrite(pin[i], on);
        Serial.print('1');
      }
      else {
        digitalWrite(pin[i], off);
        Serial.print('0');
      }
    }
    Serial.println();
    actual++;  // for the next run
    if (actual >= noOfSteps && !runOnce) actual = 0;  // rollover if sequence should  loop infinite
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(switchPin, INPUT);              // Set the switch pin as input
  for (auto &i : pin) pinMode(i, OUTPUT); // set all led pins to outputs
  buttonState = digitalRead(switchPin);   // read the initial state
}

void allPins(int val) {
  for (auto &i : pin) digitalWrite(i, val); // switch all pins
}

void loop() {
  int val = digitalRead(switchPin);
  if (buttonState != val)  {
    buttonState = val;
    delay (20);     // debounce
    if (val == LOW) {
      if (3 < ++mode) {
        mode = 0;
        allPins(off);
      }
      actual = 0;
      Serial.println(mode);
      if (mode == 2) allPins(on);
    }
  }
  // in some modes we must call runSequence
  if (mode == 1) runSequence(sequenceA,  sizeof(sequenceA) / sizeof(sequenceA[0]));
  else if (mode == 3) runSequence(sequenceC,  sizeof(sequenceC) / sizeof(sequenceC[0]));
}