Cycling Between Voids W/ a PushButton & Issues W/ Charliecube not Cycling

Hello all, and happy thanksgiving if you celebrate it!

I recently built a Charliecube based on Asher Glick's tutorial. The issue I'm having with it is that it doesn't cycle through the animations as it should. It just gets stuck on the "planarSpin" animation. If I comment out the animations one by one, I've found that the "shiftSquares" animation does cycle over to "tunnel", but that's the only one that works properly.

#include "cubeplex.h"

int color = red;
int count = 0;

void setup() {
  initCube();
  
  animationMax = 10;  // how many secconds until the animation is told to progress
}
void loop() {
  planarSpin();
  fountian();
  trifade();
  shiftSquares();
  tunnel();
  chaseTheDot();
  planarFlop3D();

  //SeqTheDot();      //for testing one LED at a time, and finding wiring errors
}

void planarSpin() {
.
.
.

void shiftSquares() {
  int animationSpeed = 100;
  
  int blx = 2; // blue x
  int bly = 0; // blue y
  int blz = 0; // blue z
  
  int rdx = 0; // red x
  int rdy = 2; // red y
  int rdz = 0; // red z
  
  int gnx = 0; // green x
  int gny = 0; // green y
  int gnz = 2; // green z
  
  int * mover = &blx;
  continuePattern = true;
  
  while(continuePattern) {
    switch (random(0,9)) {
      case 0: mover = &blx; break;
      case 1: mover = &bly; break;
      case 2: mover = &blz; break;
      case 3: mover = &rdx; break;
      case 4: mover = &rdy; break;
      case 5: mover = &rdz; break;
      case 6: mover = &gnx; break;
      case 7: mover = &gny; break;
      case 8: mover = &gnz; break;
    }
    *mover = (((*mover)+2)%4)-1;
    drawBox(blue ,abs(blx),abs(bly),abs(blz),abs(blx)+1,abs(bly)+1,abs(blz)+1);
    drawBox(red  ,abs(gnx),abs(gny),abs(gnz),abs(gnx)+1,abs(gny)+1,abs(gnz)+1);
    drawBox(green,abs(rdx),abs(rdy),abs(rdz),abs(rdx)+1,abs(rdy)+1,abs(rdz)+1);
    flushBuffer();
    clearBuffer();
    delay(animationSpeed);
    *mover = (((*mover)+2)%4)-1;
    drawBox(blue ,abs(blx),abs(bly),abs(blz),abs(blx)+1,abs(bly)+1,abs(blz)+1);
    drawBox(red  ,abs(gnx),abs(gny),abs(gnz),abs(gnx)+1,abs(gny)+1,abs(gnz)+1);
    drawBox(green,abs(rdx),abs(rdy),abs(rdz),abs(rdx)+1,abs(rdy)+1,abs(rdz)+1);
    flushBuffer();
    clearBuffer();
    delay(animationSpeed*2);
  }
}

void tunnel() {
  continuePattern = true;
  int animationSpeed =100;
  
  int color1[]  = {R,R,R,R,B,B,B,B};
  int bright1[] = {2,4,6,8,2,4,6,8};
  int color2[]  = {B,B,B,B,R,R,R,R};
//int bright2[] = {6,4,2,0,6,4,2,0};
  int bright2[] = {8,6,4,2,8,6,4,2};
  
  int index[]   = {0,1,2,3,4,5,6,7};
  
  while (continuePattern) {
    drawBoxWalls(color1[index[0]],bright1[index[0]],1,1,0,2,2,0);
    drawBoxWalls(color2[index[0]],bright2[index[0]],1,1,0,2,2,0);
    drawBoxWalls(color1[index[1]],bright1[index[1]],1,1,1,2,2,1);
    drawBoxWalls(color2[index[1]],bright2[index[1]],1,1,1,2,2,1);
    drawBoxWalls(color1[index[2]],bright1[index[2]],1,1,2,2,2,2);
    drawBoxWalls(color2[index[2]],bright2[index[2]],1,1,2,2,2,2);
    drawBoxWalls(color1[index[3]],bright1[index[3]],1,1,3,2,2,3);
    drawBoxWalls(color2[index[3]],bright2[index[3]],1,1,3,2,2,3);
    
    drawBoxWalls(color1[index[4]],bright1[index[4]],0,0,3,3,3,3);
    drawBoxWalls(color2[index[4]],bright2[index[4]],0,0,3,3,3,3);
    drawBoxWalls(color1[index[5]],bright1[index[5]],0,0,2,3,3,2);
    drawBoxWalls(color2[index[5]],bright2[index[5]],0,0,2,3,3,2);
    drawBoxWalls(color1[index[6]],bright1[index[6]],0,0,1,3,3,1);
    drawBoxWalls(color2[index[6]],bright2[index[6]],0,0,1,3,3,1);
    drawBoxWalls(color1[index[7]],bright1[index[7]],0,0,0,3,3,0);
    drawBoxWalls(color2[index[7]],bright2[index[7]],0,0,0,3,3,0);
    
    
    flushBuffer();
    clearBuffer();
    for (int i = 0; i < 8; i++){
      //index[i] = index[i]==7?0:index[i]+1;
      index[i] = (index[i]+1)%8;
    }
    delay(animationSpeed);
    
  }
}
.
.
.

Also, I tried to add a pushbutton to manually cycle the modes, but that isn't working either. I think I figured out the issue with this, correct me if I'm wrong. I think the issue is that as soon as the "void loop" starts, it jumps out to the "void planarSpin", so it never comes back to the loop to read the pushbutton. I believe the solution to this would be to free up one of the hardware interrupt pins, and use that for the pushbutton instead? (I already moved the output that was on pin 13 to A5, to get rid of the ghosting from the onboard led, which is why I used pin 13 as my input.)

#include "cubeplex.h"

int color = red;
int count = 0;
int currentStatus = LOW;
int previousStatus = LOW;

void setup() {
  initCube();
  
  pinMode(13, INPUT);

  Serial.begin(9600);
  
  animationMax = 30;  // how many secconds until the animation is told to progress
}
void loop() {
  currentStatus = digitalRead(13);
  
  if (currentStatus == HIGH && previousStatus == LOW){
    count = count + 1;
  };
  
  previousStatus = currentStatus;
  
  if(count == 0) {planarSpin(); }
  if(count == 1) {fountian(); }
  if(count == 2) {trifade(); }
  if(count == 3) {shiftSquares(); }
  if(count == 4) {tunnel(); }
  if(count == 5) {chaseTheDot(); }
  if(count == 6) {planarFlop3D(); }
  if(count == 7) {count =0; }

  Serial.print("Count = ");
  Serial.println(count);
  
  //SeqTheDot();      //for testing one LED at a time, and finding wiring errors
}

void planarSpin() {
.
.
.

void shiftSquares() {
  int animationSpeed = 100;
  
  int blx = 2; // blue x
  int bly = 0; // blue y
  int blz = 0; // blue z
  
  int rdx = 0; // red x
  int rdy = 2; // red y
  int rdz = 0; // red z
  
  int gnx = 0; // green x
  int gny = 0; // green y
  int gnz = 2; // green z
  
  int * mover = &blx;
  continuePattern = true;
  
  while(continuePattern) {
    switch (random(0,9)) {
      case 0: mover = &blx; break;
      case 1: mover = &bly; break;
      case 2: mover = &blz; break;
      case 3: mover = &rdx; break;
      case 4: mover = &rdy; break;
      case 5: mover = &rdz; break;
      case 6: mover = &gnx; break;
      case 7: mover = &gny; break;
      case 8: mover = &gnz; break;
    }
    *mover = (((*mover)+2)%4)-1;
    drawBox(blue ,abs(blx),abs(bly),abs(blz),abs(blx)+1,abs(bly)+1,abs(blz)+1);
    drawBox(red  ,abs(gnx),abs(gny),abs(gnz),abs(gnx)+1,abs(gny)+1,abs(gnz)+1);
    drawBox(green,abs(rdx),abs(rdy),abs(rdz),abs(rdx)+1,abs(rdy)+1,abs(rdz)+1);
    flushBuffer();
    clearBuffer();
    delay(animationSpeed);
    *mover = (((*mover)+2)%4)-1;
    drawBox(blue ,abs(blx),abs(bly),abs(blz),abs(blx)+1,abs(bly)+1,abs(blz)+1);
    drawBox(red  ,abs(gnx),abs(gny),abs(gnz),abs(gnx)+1,abs(gny)+1,abs(gnz)+1);
    drawBox(green,abs(rdx),abs(rdy),abs(rdz),abs(rdx)+1,abs(rdy)+1,abs(rdz)+1);
    flushBuffer();
    clearBuffer();
    delay(animationSpeed*2);
  }
}

void tunnel() {
  continuePattern = true;
  int animationSpeed =100;
  
  int color1[]  = {R,R,R,R,B,B,B,B};
  int bright1[] = {2,4,6,8,2,4,6,8};
  int color2[]  = {B,B,B,B,R,R,R,R};
//int bright2[] = {6,4,2,0,6,4,2,0};
  int bright2[] = {8,6,4,2,8,6,4,2};
  
  int index[]   = {0,1,2,3,4,5,6,7};
  
  while (continuePattern) {
    drawBoxWalls(color1[index[0]],bright1[index[0]],1,1,0,2,2,0);
    drawBoxWalls(color2[index[0]],bright2[index[0]],1,1,0,2,2,0);
    drawBoxWalls(color1[index[1]],bright1[index[1]],1,1,1,2,2,1);
    drawBoxWalls(color2[index[1]],bright2[index[1]],1,1,1,2,2,1);
    drawBoxWalls(color1[index[2]],bright1[index[2]],1,1,2,2,2,2);
    drawBoxWalls(color2[index[2]],bright2[index[2]],1,1,2,2,2,2);
    drawBoxWalls(color1[index[3]],bright1[index[3]],1,1,3,2,2,3);
    drawBoxWalls(color2[index[3]],bright2[index[3]],1,1,3,2,2,3);
    
    drawBoxWalls(color1[index[4]],bright1[index[4]],0,0,3,3,3,3);
    drawBoxWalls(color2[index[4]],bright2[index[4]],0,0,3,3,3,3);
    drawBoxWalls(color1[index[5]],bright1[index[5]],0,0,2,3,3,2);
    drawBoxWalls(color2[index[5]],bright2[index[5]],0,0,2,3,3,2);
    drawBoxWalls(color1[index[6]],bright1[index[6]],0,0,1,3,3,1);
    drawBoxWalls(color2[index[6]],bright2[index[6]],0,0,1,3,3,1);
    drawBoxWalls(color1[index[7]],bright1[index[7]],0,0,0,3,3,0);
    drawBoxWalls(color2[index[7]],bright2[index[7]],0,0,0,3,3,0);
    
    
    flushBuffer();
    clearBuffer();
    for (int i = 0; i < 8; i++){
      //index[i] = index[i]==7?0:index[i]+1;
      index[i] = (index[i]+1)%8;
    }
    delay(animationSpeed);
    
  }
}
.
.
.

(Some code has been ellipsed to meet the 9000 character post limit)

Jeremy1998:
I think the issue is that as soon as the "void loop" starts, it jumps out to the "void planarSpin", so it never comes back to the loop to read the pushbutton. I believe the solution to this would be to free up one of the hardware interrupt pins, and use that for the pushbutton instead?

It's rather unfortunate that you refer to planarSpin() but have not included its code :slight_smile:

However I presume it would be similar to shiftSquares() and your assumption is probably correct. As I see it each of those functions is intended to run continuously based on the line

 while(continuePattern)

I suspect it will all work as you want if you remove that line completely and its corresponding } AND change the variable definitions within the function to static - like this

void shiftSquares() {
  static int animationSpeed = 100;
 
  static int blx = 2; // blue x

This is certainly not a problem that requires an interrupt as a solution.

...R

Robin2:
As I see it each of those functions is intended to run continuously based on the line

 while(continuePattern)

I suspect it will all work as you want if you remove that line completely and its corresponding } AND change the variable definitions within the function to static - like this

void shiftSquares() {

static int animationSpeed = 100;

static int blx = 2; // blue x




This is certainly not a problem that requires an interrupt as a solution.

...R

Alright, I modified the code as you suggested, and it's closer to my desired result. Now the button will cycle it to the next mode, but only if I hold it down until the code returns to the void loop. Is there a way to fix this without an interrupt pin? Or even with an interrupt?

#include "cubeplex.h"

int color = red;

int count = 0;
int currentStatus = LOW;
int previousStatus = LOW;
int button = 13;

void setup() {
  initCube();
  
  pinMode(button, INPUT);
  
  animationMax = 30;  // how many secconds until the animation is told to progress

  Serial.begin(9600);
}

void loop() { 
  currentStatus = digitalRead(button);
  
  if (currentStatus == HIGH && previousStatus == LOW){
    count = count + 1;
  }
  
  previousStatus = currentStatus;
  
  if(count == 0) {planarSpin(); }
  if(count == 1) {fountian(); }
  if(count == 2) {trifade(); }
  if(count == 3) {shiftSquares(); }
  if(count == 4) {tunnel(); }
  if(count == 5) {chaseTheDot(); }
  if(count == 6) {planarFlop3D(); }
  if(count == 7) {count =0; }

  Serial.print("Count = ");
  Serial.println(count);
  //SeqTheDot();      //for testing one LED at a time, and finding wiring errors
}

void planarSpin() {
  static int animationSpeed = 50;
  static int spinsPerColor = 5; // a spin is actually half a revolution

    int x = 0;
    int y = 0;
    for (int i = 0; i < spinsPerColor; i++) {
      for (int x = 0; x < 3; x++) {
        drawLine(color,x,0,0,3-x,3,0);
        drawLine(color,x,0,1,3-x,3,1);
        drawLine(color,x,0,2,3-x,3,2);
        drawLine(color,x,0,3,3-x,3,3);
        flushBuffer();
        clearBuffer();
        delay(animationSpeed);
      }
      for (int y = 0; y < 3; y++) {
        drawLine(color,3,y,0,0,3-y,0);
        drawLine(color,3,y,1,0,3-y,1);
        drawLine(color,3,y,2,0,3-y,2);
        drawLine(color,3,y,3,0,3-y,3);
        flushBuffer();
        clearBuffer();
        delay(animationSpeed);
      }
    }
    color = nextColor(color);
  
}

void fountian() {
  static int animationSpeed = 100;

    for (int z = 0; z <= 3; z++) {
      drawBoxWalls(color,1,1,z,2,2,z);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    for (int z = 3; z >= 0; z--) {
      drawBoxWalls(color,0,0,z,3,3,z);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    color=nextColor(color);
  
}

void trifade() {
  static int animationSpeed = 100;
  
    // blue fade out, red fade in
    for (int i = 1; i <= 8; i++) {
      drawBox(blue,9-i,0,0,0,3,3,3);
      drawBox(red,i,0,0,0,3,3,3);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    // red fade out, green fade in
    for (int i = 1; i <= 8; i++) {
      drawBox(red,9-i,0,0,0,3,3,3);
      drawBox(green,i,0,0,0,3,3,3);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    // green fade out, blue fade in
    for (int i = 1; i <= 8; i++) {
      drawBox(green,9-i,0,0,0,3,3,3);
      drawBox(blue,i,0,0,0,3,3,3);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
}

void shiftSquares() {
  static int animationSpeed = 100;
  
  static int blx = 2; // blue x
  static int bly = 0; // blue y
  static int blz = 0; // blue z
  
  static int rdx = 0; // red x
  static int rdy = 2; // red y
  static int rdz = 0; // red z
  
  static int gnx = 0; // green x
  static int gny = 0; // green y
  static int gnz = 2; // green z
  
  int * mover = &blx;
  
    switch (random(0,9)) {
      case 0: mover = &blx; break;
      case 1: mover = &bly; break;
      case 2: mover = &blz; break;
      case 3: mover = &rdx; break;
      case 4: mover = &rdy; break;
      case 5: mover = &rdz; break;
      case 6: mover = &gnx; break;
      case 7: mover = &gny; break;
      case 8: mover = &gnz; break;
    }
    *mover = (((*mover)+2)%4)-1;
    drawBox(blue ,abs(blx),abs(bly),abs(blz),abs(blx)+1,abs(bly)+1,abs(blz)+1);
    drawBox(red  ,abs(gnx),abs(gny),abs(gnz),abs(gnx)+1,abs(gny)+1,abs(gnz)+1);
    drawBox(green,abs(rdx),abs(rdy),abs(rdz),abs(rdx)+1,abs(rdy)+1,abs(rdz)+1);
    flushBuffer();
    clearBuffer();
    delay(animationSpeed);
    *mover = (((*mover)+2)%4)-1;
    drawBox(blue ,abs(blx),abs(bly),abs(blz),abs(blx)+1,abs(bly)+1,abs(blz)+1);
    drawBox(red  ,abs(gnx),abs(gny),abs(gnz),abs(gnx)+1,abs(gny)+1,abs(gnz)+1);
    drawBox(green,abs(rdx),abs(rdy),abs(rdz),abs(rdx)+1,abs(rdy)+1,abs(rdz)+1);
    flushBuffer();
    clearBuffer();
    delay(animationSpeed*2);
  
}

Code too long, continued in next post.

Code part two:

void tunnel() {
  static int animationSpeed =100;
  
  static int color1[]  = {R,R,R,R,B,B,B,B};
  static int bright1[] = {2,4,6,8,2,4,6,8};
  static int color2[]  = {B,B,B,B,R,R,R,R};
//static int bright2[] = {6,4,2,0,6,4,2,0};
  static int bright2[] = {8,6,4,2,8,6,4,2};
  
  static int index[]   = {0,1,2,3,4,5,6,7};
  
    drawBoxWalls(color1[index[0]],bright1[index[0]],1,1,0,2,2,0);
    drawBoxWalls(color2[index[0]],bright2[index[0]],1,1,0,2,2,0);
    drawBoxWalls(color1[index[1]],bright1[index[1]],1,1,1,2,2,1);
    drawBoxWalls(color2[index[1]],bright2[index[1]],1,1,1,2,2,1);
    drawBoxWalls(color1[index[2]],bright1[index[2]],1,1,2,2,2,2);
    drawBoxWalls(color2[index[2]],bright2[index[2]],1,1,2,2,2,2);
    drawBoxWalls(color1[index[3]],bright1[index[3]],1,1,3,2,2,3);
    drawBoxWalls(color2[index[3]],bright2[index[3]],1,1,3,2,2,3);
    
    drawBoxWalls(color1[index[4]],bright1[index[4]],0,0,3,3,3,3);
    drawBoxWalls(color2[index[4]],bright2[index[4]],0,0,3,3,3,3);
    drawBoxWalls(color1[index[5]],bright1[index[5]],0,0,2,3,3,2);
    drawBoxWalls(color2[index[5]],bright2[index[5]],0,0,2,3,3,2);
    drawBoxWalls(color1[index[6]],bright1[index[6]],0,0,1,3,3,1);
    drawBoxWalls(color2[index[6]],bright2[index[6]],0,0,1,3,3,1);
    drawBoxWalls(color1[index[7]],bright1[index[7]],0,0,0,3,3,0);
    drawBoxWalls(color2[index[7]],bright2[index[7]],0,0,0,3,3,0);
    
    
    flushBuffer();
    clearBuffer();
    for (int i = 0; i < 8; i++){
      //index[i] = index[i]==7?0:index[i]+1;
      index[i] = (index[i]+1)%8;
    }
    delay(animationSpeed);
    
  
}


void chaseTheDot() {
  static int animationSpeed = 100;
  
  static int xpos = 0;
  static int ypos = 0;
  static int zpos = 0;
  
    switch(random(0,6)) {
      case 0:
        if (xpos > 0) {xpos--;break;}
        else color=nextColor(color);
      case 1:
        if (xpos < 3) {xpos++;break;}
        else color=nextColor(color);
        xpos--; break;
        
      case 2:
        if (ypos > 0) {ypos--;break;}
        else color=nextColor(color);
      case 3:
        if (ypos < 3) {ypos++;break;}
        else color=nextColor(color);
        ypos--; break;
      
      case 4:
        if (zpos > 0) {zpos--;break;}
        else color=nextColor(color);
      case 5:
        if (zpos < 3) {zpos++;break;}
        else color=nextColor(color);
        zpos--; break;
    }
    drawLed(color,xpos,ypos,zpos);
    flushBuffer();
    clearBuffer();
    delay(animationSpeed);
  
}

void planarFlop3D() {
  static int animationSpeed = 65;
    
    for (int y = 3; y>0; y--){
      for (int z = 0; z < 4; z++) drawLine(color,0,3,z,3,y,z);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    for (int x = 3; x > 0; x--) {
      for (int z = 0; z < 4; z++) drawLine(color,0,3,z,x,0,z);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    
    
    for (int x = 0; x < 3; x++) {
      for (int y = 0; y < 4; y++) drawLine(color,0,y,0,x,y,3);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    for (int z = 3; z > 0; z--) {
      for (int y = 0; y < 4; y++) drawLine(color,0,y,0,3,y,z);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    
    for (int z = 0; z < 3; z++) {
      for (int x = 0; x < 4; x++) drawLine(color,x,0,0,x,3,z);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    for (int y = 3; y > 0; y--) {
      for (int x = 0; x < 4; x++) drawLine(color,x,0,0,x,y,3);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    
    for (int y = 0; y < 3; y++) {
      for (int z = 0; z < 4; z++) drawLine(color,3,0,z,0,y,z);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    for (int x = 0; x < 3; x++) {
      for (int z = 0; z < 4; z++) drawLine(color,3,0,z,x,3,z);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    
    for (int x = 3; x > 0; x--) {
      for (int y = 0; y < 4; y++) drawLine(color,3,y,3,x,y,0);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    for (int z = 0; z < 3; z++) {
      for (int y = 0; y < 4; y++) drawLine(color,3,y,3,0,y,z);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    
    for (int z = 3; z > 0; z--) {
      for (int x = 0; x < 4; x++) drawLine(color,x,3,3,x,0,z);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    } 
    for (int y = 0; y < 3; y++) {
      for (int x = 0; x < 4; x++) drawLine(color,x,3,3,x,y,0);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    color = nextColor(color);
  
}

void SeqTheDot() {

static int animationSpeed = 500; // change this to speed up or down.

// color= green; // change this to set a specific color or comment it out to cycle.

int xpos = 0;
int ypos = 0;
int zpos = 0;
for (xpos; xpos < 4; xpos++) {
  for (ypos; ypos < 4; ypos++) {
    for (zpos; zpos < 4; zpos++) {

      // drawLed(color,1,1,3); // use this one to light up a specific LED

      drawLed(color,xpos,ypos,zpos);
      flushBuffer();
      clearBuffer();
      delay(animationSpeed);
    }
    zpos=0;
  }
  ypos=0;
}
xpos=0;
color=nextColor(color); 
}

Is there a way to fix this without an interrupt pin? Or even with an interrupt?

Yes. Throw away those for loops and delay()s and use millis() for timing.

See Using millis() for timing. A beginners guide, Several things at the same time and look at the BlinkWithoutDelay example in the IDE.

You can then read the input each time through loop() as the code will not be blocked/locked in a for loop.

UKHeliBob:
Yes. Throw away those for loops and delay()s and use millis() for timing.

See Using millis() for timing. A beginners guide, Several things at the same time and look at the BlinkWithoutDelay example in the IDE.

You can then read the input each time through loop() as the code will not be blocked/locked in a for loop.

Wow, this project is testing my mental strength! So I think I can figure out how to get rid of the delays, but I don't know how to get rid of the for loops.

I don't know how to get rid of the for loops.

One way would be to use what the loop() function does best which is, of course, to loop

The advantage is that you get a chance to read inputs, for instance, each time through loop() no matter what other code is executing as long as that code is not blocking.

The best way to do this is probably to define each of the states that the system could be in and use switch/case to activate the code associated with that state. In the code you use millis() for timing and when the required period between actions has elapsed take the next actions and start timing again.

You can replicate what the for loops do by having a couple of counter variables which is, after all, what a for loop does. Increment the "inner" counter each time the timing period is up and when the "inner" variable rolls over and you reset it to its initial value increment the "outer" variable

UKHeliBob:
One way would be to use what the loop() function does best which is, of course, to loop

The best way to do this is probably to define each of the states that the system could be in and use switch/case to activate the code associated with that state.

So if I understand you correctly, you're saying move the code out of the "void planarSpin()", "void fountian()", etc. and into a switch case in the main "void loop"?

UKHeliBob:
You can replicate what the for loops do by having a couple of counter variables which is, after all, what a for loop does. Increment the "inner" counter each time the timing period is up and when the "inner" variable rolls over and you reset it to its initial value increment the "outer" variable

Yeah, that's what I was assuming, but I wanted to make sure before I go through all that work modifying the code.

So if I understand you correctly, you're saying move the code out of the "void planarSpin()", "void fountian()", etc. and into a switch case in the main "void loop"?

The switch/case for each state is probably best left in loop() but each case could call an appropriate function to test whether it is time to do anything and if so, do it. The key thing is that each state should also test whether an input has been made that should cause a change of state and if so should make that change and set up the entry conditions for the new state such as start time and start values for variables. It may also need to turn off all LEDs so that the new state can start cleanly

I wanted to make sure before I go through all that work modifying the code.

Here is an example of the technique.

unsigned long periodStartTime;
unsigned long currentTime;
const unsigned long period = 1000;
byte xPos = 0;
byte yPos = 0;
byte xMax = 5;
byte yMax = 5;
boolean xChanged = false;
boolean yChanged = false;
const byte inputPin = A1;

void setup()
{
  Serial.begin(115200);
  pinMode(inputPin, INPUT_PULLUP);
  showPos();
}

void loop()
{
  if (digitalRead(inputPin) == LOW)
  {
    Serial.println("Input is LOW");
  }
  currentTime = millis();
  if (currentTime - periodStartTime >= period)
  {
    xPos++;
    xChanged = true;
    if (xPos == xMax)
    {
      xPos = 0;
      yPos++;
      yChanged = true;
      if (yPos == yMax)
      {
        yPos = 0;
      }
    }
    periodStartTime = currentTime;
  }
  if (xChanged || yChanged)
  {
    showPos();
    xChanged = false;
    yChanged = false;
  }
}

void showPos()
{
  Serial.print("x : ");
  Serial.print(xPos);
  Serial.print("\t");
  Serial.print("y : ");
  Serial.println(yPos);
}

Holding down the input button and watching the Serial monitor certainly give a good idea of how frequently the loop() function runs. Note that in practice you would write it to detect that the button had become pressed rather than was pressed when read. Note also that the example only prints the x and y values when they change whereas if driving LEDs it may not be necessary to restrict the output like this,