Uno/Nano DIO Sequencer

Version 4

  • added PROGMEM to preserve RAM
  • previous limit was 250 elements of 10 rows
// DIO sequencer version 4 - using PROGMEM

// - char arrays are flat (one long string, no comma separators)
// - edit array elements using (star) "*" for "ON" and (dot) "." for "OFF"
// - each row in an array must be the same length
// https://forum.arduino.cc/t/uno-nano-dio-sequencer/1364441

#include <avr/pgmspace.h> // to offload arrays from RAM to PROGMEM 
byte ledPin[] = {4, 5, 6, 7, 8, 9, 10, 11, 12}; // DIO pins for LEDs. ** Leave pin 2 and 3 for hardware interrupts
int ledCount = sizeof(ledPin) / sizeof(ledPin[0]); // number of LEDs
int loops; // for repeating a sequence
bool firstTime = 1; // flag for printing Loop Time

// Multiply "spacing" times "columns" for total time to complete one loop
unsigned long columnSpacing = 50; // milliseconds per element - speed control

const char sequence[] PROGMEM = { // single-dimension array of sequenced elements
  //12345...1.........2.........3.........4.........5.........6.........7.........8.........9.........1  // elements or columns
  "...******...........*.......*..........*...*...*.........*..*.......*...*...*...............*......." // LED[0]... sequence[0][0..n]
  "...*********.......*.........*.........*...*...*........*....*.....*....*.....*..........*.....*...." // LED[1]... sequence[1][0..n]
  "...************...*...........*........*...*...*.......*......*...*.....*.......*.....*...........*." // LED[2]... sequence[2][0..n]
  "...******...........*.......*..........*...*...*.........*..*.......*...*...*...............*......." // LED[3]... sequence[3][0..n]
  "...*********.......*.........*.........*...*...*........*....*.....*....*.....*..........*.....*...." // LED[4]... sequence[4][0..n]
  "...************...*...........*........*...*...*.......*......*...*.....*.......*.....*...........*." // LED[5]... sequence[5][0..n]
  "...************...*...........*........*...*...*.......*......*...*.....*.......*...........*......." // LED[6]... sequence[6][0..n]
  "...******...........*.......*..........*...*...*.........*..*.......*...*...*............*.....*...." // LED[7]... sequence[7][0..n]
  "...*********.......*.........*.........*...*...*........*....*.....*....*.....*.......*...........*." // LED[8]... sequence[8][0..n]
};

const char scan[] PROGMEM = {
  //2345....1.........2
  "*..............."
  ".*.............*"
  "..*...........*."
  "...*.........*.."
  "....*.......*..."
  ".....*.....*...."
  "......*...*....."
  ".......*.*......"
  "........*......."
};

const char flash[] PROGMEM = {
  //2345....1.........2
  ".....*.....*.....*"
  ".....*.....*.....*"
  ".....*.....*.....*"
  ".....*.....*.....*"
  ".....*.....*.....*"
  ".....*.....*.....*"
  ".....*.....*.....*"
  ".....*.....*.....*"
  ".....*.....*.....*"
};

const char wowow[] PROGMEM = {
  //2345....1.........2.........3
  "*........**........**........*"
  ".*......*..*......*..*......*."
  "..*....*....*....*....*....*.."
  "...*..*......*..*......*..*..."
  "....**........**........**...."
  "...*..*......*..*......*..*..."
  "..*....*....*....*....*....*.."
  ".*......*..*......*..*......*."
  "*........**........**........*"
};

const char clear[] PROGMEM = {
  "."
  "."
  "."
  "."
  "."
  "."
  "."
  "."
  "."
};


void setup() {
  randomSeed(analogRead(A0)); // increase pseudo-randomness
  Serial.begin(115200); // start serial communications
  for (int i = 0; i < ledCount; i++) // count through LED pins
    pinMode(ledPin[i], OUTPUT); // configure pins for OUTPUT
}

void loop() {
  columnSpacing = random(50, 100); // random spacing for one sequence
  delay(random(1000, 3000)); // random delay time between sequences
  loops = random (1, 5); // random sequence number of loops

  switch (random (4)) { // call a random sequence
    case 0: {
        sequencer(sequence, sizeof(sequence) / ledCount, loops);
        break;
      }

    case 1: {
        sequencer(scan, sizeof(scan) / ledCount, loops);
        break;
      }

    case 2: {
        sequencer(flash, sizeof(flash) / ledCount, loops);
        break;
      }

    case 3: {
        sequencer(wowow, sizeof(wowow) / ledCount, loops);
        break;
      }

    default: break;
  }
  sequencer(clear, sizeof(clear) / ledCount, 1); // clear LEDs
}

void sequencer(char list[], int columns, int loops) {
  for (int lop = 0; lop < loops; lop++) { // count through loops
    loopTime(columnSpacing, columns, loops); // display Loop Time
    for (int col = 0; col < columns; col++) { // count through row length
      for (int row = 0; row < ledCount; row++) { // count through number of LEDs
        switch (pgm_read_byte(&list[row * columns + col])) { // locate element row and column location
          case '*': digitalWrite(ledPin[row], HIGH); break; // turn LED ON
          case '.': digitalWrite(ledPin[row],  LOW); break; // turn LED OFF
          default: break;
        }
      }
      delay(columnSpacing); // regulate the speed of the sequence
    }
  }
  firstTime = 1; // set first time flag for LoopTime display
}

void loopTime(unsigned long spacing, unsigned long columns, unsigned long loops) { // display Loop Time on Serial Monitor
  if (firstTime) { // only if "firstTime" flag is set...
    Serial.print("Loop Time (");
    Serial.print(loops * spacing * columns); // multiply all the values
    Serial.print("ms)");
    Serial.print(" = loops (");
    Serial.print(loops);
    Serial.print(") * spacing (");
    Serial.print(spacing);
    Serial.print("ms) * columns (");
    Serial.print(columns);
    Serial.println(" elements)");
    firstTime = 0; // clear "firstTime" flag
  }
}