[SOLVED] Program hangs when using random()

Program hangs when using too many random()

I built a Charliecube (4x4x4 LED cube) that cycles through a number of patterns. The original sketch runs through each pattern and colour in sequence for a fixed length of time. To make it more interesting I modified it to randomly vary the colours, the pattern sequence and the length of time each pattern runs.

The problem is, after a random length of time my version of the sketch will not advance to the next pattern. If I remove randomSeed or replace any one of the "random"s with an int (random pattern, random colour or random run time) it will advance after the time set in animationMax.

The changes between the original code and mine are in the sketch (ino). There is one change in cubeplex.h to randomise nextColor. All other header files are unchanged. See the attachment for the full sketch.

Why doesn’t it like being random?

Random nextColor from cubeplex.h

/**** Cycle through colours in sequence - removed by chopsuwe ****/
// int nextColor(int color)          { return  (color+1)%7; }
/**** Cycle through colours randomly - added by chopsuwe ****/
int nextColor(int color)          { return  (random(0,7)); }

Original Code

#include "cubeplex.h"

int color = red;

void setup() {
  initCube();
  
  // how many secconds until the animation is told to progress
  animationMax = 10; // this is declared in cubeplex.h
}

void loop() {
  planarSpin();
  fountian();
  trifade();
  shiftSquares();
  tunnel();
  chaseTheDot();
  planarFlop3D();
  
}

/********************************** FOUNTIAN **********************************\
| Light shoots up the middle of the cube then once it reaches the top fall     |
| back down on the outside of the cube. After it hits the bottom it changes    |
| color and starts again                                                       |
|                                                                              |
| Written By: Asher Glick                                                      |
\******************************************************************************/
void fountian() {
  continuePattern = true;
  int animationSpeed  = 200;
  while (continuePattern) {
    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);
  }
}

My Randomised code

#include "cubeplex.h"

int color = red;
int animationSpeedBase; // multiplied by a constant in each pattern to set the animationSpeed
void setup() {
  initCube();
  
  // gnerate a random starting number from analog pin noise.
  randomSeed(analogRead(7));
}

void loop() {
  // randomly change the speed the animation runs at. Default = 50
  animationSpeedBase = random(30,100);
  // how many seconds until the animation is told to progress (10-15 seconds)
  // moved here from setup() so it will be changed each time through
  animationMax = random(10,20);        // default = 10
  
  switch(random(0,7)){      // randomly pick a pattern
    case 0:
      planarSpin();
    break;
    case 1:
      fountian();
    break;
    case 2:
      trifade();
    break;
    case 3:
      shiftSquares();
    break;
    case 4:
      tunnel();
    break;
    case 5:
      chaseTheDot();
    break;
    case 6:
      planarFlop3D();
    break;
  }
}

/********************************** FOUNTIAN **********************************\
| Light shoots up the middle of the cube then once it reaches the top fall     |
| back down on the outside of the cube. After it hits the bottom it changes    |
| color and starts again                                                       |
|                                                                              |
| Written By: Asher Glick                                                      |
\******************************************************************************/
void fountian() {
  continuePattern = true;
  int animationSpeed = animationSpeedBase * 4;            //random animation speed
  while (continuePattern) {
    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);
  }
}

charlieCubeAll3.zip (27.5 KB)

Does your SWITCH code work if, instead of RANDOM, you just give it a value from a variable the increments with each iteration. For example

patternSelect = (patternSelect + 1) % 7;
switch (patternSelect) {

If this was my project I would save the random value to a variable before using it in SWITCH. That way I could print it out to check it. Something like

patternSelect = random(0,7);
Serial.println(patternSelect);
switch (patternSelect) {

And, maybe, to match %7 you should be using random(0,6) ?

...R

Robin2: And, maybe, to match %7 you should be using random(0,6) ?

Yes, however the problem still exists no matter how many patterns are included.

Does your SWITCH code work if, instead of RANDOM, you just give it a value from a variable the increments with each iteration.

I used an IF. Your method works too.

  patternSelect++;
  if (patternselect > 7){
    patternselect = 0
  }

If this was my project I would save the random value to a variable before using it in SWITCH. That way I could print it out to check it.

I tried that and added the print inside each CASE. When it hangs it doesn't come back to loop(). It stays inside the "while (continuePattern)" area of the pattern. I'll check to see it continuePattern never becomes false.

There are two ISR calls in cubeplex.h Is it possible the one that updates continuePattern is being locked out?

/******************************************************************************\
| Some helpfull info for overflowing timers with different prescaler values    |
|  16000000 / (   1*256) = 16000000 / 256    =  62500 Hz                       |
|  16000000 / (   8*256) = 16000000 / 2048   =  ~7812 Hz                       |
|  16000000 / (  32*256) = 16000000 / 8192   =  ~1953 Hz                       |
|  16000000 / (  64*256) = 16000000 / 16384  =   ~976 Hz                       |
|  16000000 / ( 128*256) = 16000000 / 32768  =   ~488 Hz                       | 
|  16000000 / ( 256*256) = 16000000 / 65536  =   ~244 Hz                       |
|  16000000 / (1024*256) = 16000000 / 262144 =    ~61 Hz                       |
\******************************************************************************/
int animationTimer = 0;
int animationMax = 0;

ISR(TIMER1_OVF_vect) {
  animationTimer++;
  if (animationTimer == animationMax) {
    continuePattern = false;
    animationTimer=0;
  }
}

chopsuwe: Yes, however the problem still exists no matter how many patterns are included. I used an IF. Your method works too.

I tried that and added the print inside each CASE. When it hangs it doesn't come back to loop(). It stays inside the "while (continuePattern)" area of the pattern. I'll check to see it continuePattern never becomes false.

I can't make sense of all of this.

Does your project work properly when you just give it different numbers but do not use RANDOM ?

If it does it is unlikely that the problem is within the cubePlex library

...R

PS - just a random thought ! ... is there a risk that your code is calling the cubePlex library too frequently and not giving it time to work ?

Got it! You're kind of right about cubeplex.h.

The problem is the ISR doesn't always trigger. There are two ISRs, one updates the LEDs and the other sets a flag to change the pattern. The LED update ISR is quite long and I think it locks the other out. I'm not sure but it doesn't matter because there is an easy fix.

The ISR fires each second and checks to see if it is time to change the pattern right now. ** if (animationTimer == animationMax)** But if the ISR was skipped that time it doesn't know to change because the the two values are no longer the same. All we have to do is see if it is time to change now, or if that time has passed. ** if (animationTimer >= animationMax)** It does mean the pattern might run for a second or two longer than it should but that doesn't matter.

Change the ISR in cubeplex.h

ISR(TIMER1_OVF_vect) {
  animationTimer++;
  if (animationTimer >= animationMax) {//in case ISR isn't called, change == to >= so the pattern will advance next time
    continuePattern = false;
    animationTimer=0;
  }
}
if (animationTimer == animationMax)

Seems to be sloppy programming.

Glad you have a solution.

...R