Uno/Nano DIO Sequencer

Version 1 - Passing two-dimension, character array to a single function. (scaling failed - skip to V3)

Text based DIO sequencer for Uno/Nano R3. No millis(); required. Useful for simulated lightning, fireworks, dancing sprinklers, player bells, relay music, et c. Extend sequence lines to limit of memory.

// DIO sequencer
// Edit the sequence elements.  "*" and "." only, or edit the switch/case.

const int seqlen = 100; // sequence length MUST BE DEFINED (multiply times "speed" for loop time)
const char sequence[][seqlen] = { // array of sequenced elements
  //12345....1.........2.........3.........4.........5.........6.........7.........8.........9.........1    // elements
  {"********...........*.......*..........*...*...*.........*.....*......*.....*...*.................*.."}, // LED[0]... sequence[0][0..n]
  {"***********.......*.........*.........*...*...*........*......*.........*........*.............*...."}, // LED[1]... sequence[1][0..n]
  {"**************...*...........*........*...*...*.......*.......*......*......*......*.........*......"}, // LED[2]... sequence[2][0..n]
  {".......................................*.*...........*........*.........*............*.....*........"},
  {"...*................................*.....*.........*.........*......*......*..........*.*.........."},
  {"......*..........................*.........*.......*..........*.........*..............*.*.........."}, // EXTENSIBLE --->
  {".........*....................*.............*.....*...........*......*......*........*.....*........"},
  {"............*..............*.................*...*............*.........*..........*.........*......"},
  {"...............*........*.....................*.*.............*......*......*....*.............*...."},
  {"..................*..*.........................*..............*.........*......*.................*.."}, // LED[9]... sequence[9][0..n]
};

byte ledPin[] = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; // LED pins
int arraySize = sizeof(ledPin) / sizeof(ledPin[0]);
int speed = 50; // milliseconds per element - speed control (multiply time "seqlen" for loop time)

void setup() {
  Serial.begin(115200);
  for (int i = 0; i < arraySize; i++)
    pinMode(ledPin[i], OUTPUT); // start on DIO pin 4
  loopTime();
}

void loop() {
  for (int seq = 0; seq < seqlen; seq++) { // count through the sequence 0..n
    for (int arr = 0; arr < arraySize; arr++) { // read "seq" value from each array
      switch (sequence[arr][seq]) {
        case '*': digitalWrite(ledPin[arr], HIGH); break; // turn LED ON
        case '.': digitalWrite(ledPin[arr],  LOW); break; // turn LED OFF
        default: break;
      }
    }
    delay(speed); // intra-sequence delay
  }
}


void loopTime() {
  Serial.print("Loop Time (");
  Serial.print(speed * seqlen);
  Serial.print("ms)");
  Serial.print(" = speed (");
  Serial.print(speed);
  Serial.print("ms) * sequence length (");
  Serial.print(seqlen);
  Serial.println(" elements)");
}
diagram.json for wokwi.com
{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-nano", "id": "nano", "top": 62.4, "left": 9.1, "attrs": {} },
    {
      "type": "wokwi-resistor",
      "id": "r1",
      "top": 32.2,
      "left": 37.55,
      "rotate": 270,
      "attrs": { "value": "333" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r2",
      "top": 32.2,
      "left": 47.15,
      "rotate": 270,
      "attrs": { "value": "333" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r3",
      "top": 32.2,
      "left": 75.95,
      "rotate": 270,
      "attrs": { "value": "333" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r4",
      "top": 32.2,
      "left": 56.75,
      "rotate": 270,
      "attrs": { "value": "333" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r5",
      "top": 32.2,
      "left": 66.35,
      "rotate": 270,
      "attrs": { "value": "333" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r6",
      "top": 32.2,
      "left": 27.95,
      "rotate": 270,
      "attrs": { "value": "333" }
    },
    {
      "type": "wokwi-led-bar-graph",
      "id": "bargraph1",
      "top": -52.6,
      "left": 43.4,
      "rotate": 270,
      "attrs": { "color": "white" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r7",
      "top": 32.2,
      "left": -0.85,
      "rotate": 270,
      "attrs": { "value": "333" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r8",
      "top": 32.2,
      "left": 8.75,
      "rotate": 270,
      "attrs": { "value": "333" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r9",
      "top": 32.2,
      "left": 18.35,
      "rotate": 270,
      "attrs": { "value": "333" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r10",
      "top": 32.2,
      "left": -10.45,
      "rotate": 270,
      "attrs": { "value": "333" }
    }
  ],
  "connections": [
    [ "r3:2", "bargraph1:A10", "green", [ "h0" ] ],
    [ "r5:2", "bargraph1:A9", "green", [ "h0" ] ],
    [ "r4:2", "bargraph1:A8", "green", [ "h0" ] ],
    [ "r2:2", "bargraph1:A7", "green", [ "h0" ] ],
    [ "r1:2", "bargraph1:A6", "green", [ "h0" ] ],
    [ "r6:2", "bargraph1:A5", "green", [ "h0" ] ],
    [ "r9:2", "bargraph1:A4", "green", [ "h0" ] ],
    [ "r8:2", "bargraph1:A3", "green", [ "h0" ] ],
    [ "r7:2", "bargraph1:A2", "green", [ "h0" ] ],
    [ "r10:2", "bargraph1:A1", "green", [ "h0" ] ],
    [ "bargraph1:C10", "bargraph1:C9", "gray", [ "v0" ] ],
    [ "bargraph1:C9", "bargraph1:C8", "gray", [ "v0" ] ],
    [ "bargraph1:C8", "bargraph1:C7", "gray", [ "v0" ] ],
    [ "bargraph1:C7", "bargraph1:C6", "gray", [ "v0" ] ],
    [ "bargraph1:C6", "bargraph1:C5", "gray", [ "v0" ] ],
    [ "bargraph1:C5", "bargraph1:C4", "gray", [ "v0" ] ],
    [ "bargraph1:C4", "bargraph1:C3", "gray", [ "v0" ] ],
    [ "bargraph1:C3", "bargraph1:C2", "gray", [ "v0" ] ],
    [ "bargraph1:C2", "bargraph1:C1", "gray", [ "v0" ] ],
    [ "nano:13", "r10:1", "white", [ "v-48", "h-9.6" ] ],
    [ "r7:1", "nano:12", "white", [ "h0" ] ],
    [ "r8:1", "nano:11", "white", [ "h0" ] ],
    [ "r9:1", "nano:10", "white", [ "h0" ] ],
    [ "r6:1", "nano:9", "white", [ "h0" ] ],
    [ "r1:1", "nano:8", "white", [ "h0" ] ],
    [ "r2:1", "nano:7", "white", [ "h0" ] ],
    [ "r4:1", "nano:6", "white", [ "h0" ] ],
    [ "r5:1", "nano:5", "white", [ "h0" ] ],
    [ "r3:1", "nano:4", "white", [ "h0" ] ],
    [ "nano:GND.2", "bargraph1:C10", "black", [ "v0" ] ]
  ],
  "dependencies": {}
}

3 Likes

Version 3 - Passing multiple single-dimension arrays of various sizes to a single function.

(v2 was my failed attempt at passing multiple 2D character arrays to one display function).

// DIO sequencer version 3

// - Edit sequenced elements using "*" and "." only, or edit the switch/case for different "*/.".
// https://forum.arduino.cc/t/uno-nano-dio-sequencer/1364441

byte ledPin[] = {4, 5, 6}; // 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 "length" for total time to complete one loop
int columnSpacing = 90; // milliseconds per element - speed control

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

char blinkyList[] = {
  //2345....1.........2.........3.........4.........5
  "*...........................*....................."
  ".......*.............*.............*.............."
  "..............*...........................*......."
};

char flashyList[] = {
  //2345....1.........2.........3
  "......*......*......*......*.."
  "......*......*......*......*.."
  "......*......*......*......*.."
};

void setup() {
  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() {

  // call sequencer() function with array name, length, and loops

  loops = 1; // number of times the sequence is run
  sequencer(sequence, sizeof(sequence) / ledCount, loops); // arguments must be defined

  delay(0); // 0ms pause (no delay) before next sequence

  loops = 3;
  sequencer(blinkyList, sizeof(blinkyList) / ledCount, loops);

  delay(1000); // 1000 milliseconds pause before next sequence (1000ms = 1s)

  loops = 4;
  sequencer(flashyList, sizeof(flashyList) / ledCount, loops);

  delay(2000);
}

void sequencer(char list[], int length, int loops) {
  for (int lop = 0; lop < loops; lop++) { // count through loops
    loopTime(columnSpacing, length, loops); // display Loop Time
    for (int col = 0; col < length; col++) { // count through row length
      for (int row = 0; row < ledCount; row++) { // count through number of LEDs
        switch (list[row * length + col]) { // find character using 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(int spacing, int length, int loops) { // display Loop Time on Serial Monitor
  if (firstTime) { // only if "firstTime" flag is set...
    Serial.print("Loop Time (");
    Serial.print(loops * spacing * length); // multiply all the values
    Serial.print("ms)");
    Serial.print(" = loops (");
    Serial.print(loops);
    Serial.print(") * spacing (");
    Serial.print(spacing);
    Serial.print("ms) * length (");
    Serial.print(length);
    Serial.println(" elements)");
    firstTime = 0; // clear "firstTime" flag
  }
}

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
  }
}