Arduino locking up while controlling LEDs... memory leak?

I'm working on an interactive/arts project for an upcoming variety show, which will allow an audience member to control effects being displayed on an LED array made from the addressable RGB LED strips available from Sparkfun (http://www.sparkfun.com/products/10312).

The problem is, my sketch will run for 10-20 seconds, then the whole LED array will turn red and then the Arduino either locks up and becomes unresponsive or it resets itself. I've ruled out problems with the SPI library I'm using, as the sample effects that came with it work fine and I hit the same problem with alternate SPI libraries/methods. So I'm starting to think I might be maxing out the available memory or that there's a leak somewhere, but I can't see where if that's what's happening.

Here's a simplified version of the sketch that doesn't use any external library but exhibits the behavior I've described:

int SDI = 2;
int CKI = 3;
int ledPin = 13; //On board LED

#define STRIP_LENGTH 64
long strip_colors[STRIP_LENGTH];

long cellColors[8][8];
int tempo = 500;

void setup() {
  pinMode(SDI, OUTPUT);
  pinMode(CKI, OUTPUT);
  pinMode(ledPin, OUTPUT);
  
  //Clear out the arrays
  for(int x = 0 ; x < STRIP_LENGTH ; x++)
    strip_colors[x] = 0;
  
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      cellColors[i][j] = 0;
    }
  }
    
  randomSeed(analogRead(0));
}

void setCell(int x, int y, long color) {
  cellColors[x][y] = color;
  strip_colors[(x*8)+(7-y)] = color;
}

// from Sparkfun example code
long randomColor() {
  long new_color = 0;
  for(int x = 0; x < 3; x++){
    new_color <<= 8;
    new_color |= random(0xFF); //Give me a number from 0 to 0xFF
  }
  
  return new_color;
}

void shiftRows() {
  long newColor = randomColor();
  
  for (int y = 0; y < 7; y++) {
    for (int x = 0; x < 8; x++) {
      setCell(x, y, cellColors[x][y+1]);
    }
  }
  for (int x = 0; x < 8; x++) {
    setCell(x, 7, newColor);
  }
  post_frame();
}

void loop() {
  volatile int state = LOW;
  
  while(1){
    state = !state;
    digitalWrite(ledPin, state);
    
    shiftRows();
    delay(tempo);
  }
}

//Takes the current strip color array and pushes it out (from Sparkfun example code)
void post_frame (void) {
  //Each LED requires 24 bits of data
  //MSB: R7, R6, R5..., G7, G6..., B7, B6... B0 
  //Once the 24 bits have been delivered, the IC immediately relays these bits to its neighbor
  //Pulling the clock low for 500us or more causes the IC to post the data.
  long this_led_color;
  long mask;

  for(int LED_number = 0 ; LED_number < STRIP_LENGTH ; LED_number++) {
    this_led_color = strip_colors[LED_number]; //24 bits of color data

    for(byte color_bit = 23 ; color_bit != 255 ; color_bit--) {
      //Feed color bit 23 first (red data MSB)
      
      digitalWrite(CKI, LOW); //Only change data when clock is low
      
      mask = 1L << color_bit;
      //The 1'L' forces the 1 to start as a 32 bit number, otherwise it defaults to 16-bit.
      
      if(this_led_color & mask) 
        digitalWrite(SDI, HIGH);
      else
        digitalWrite(SDI, LOW);
  
      digitalWrite(CKI, HIGH); //Data is latched when clock goes high
    }
  }

  //Pull clock low to put strip into reset/post mode
  digitalWrite(CKI, LOW);
  delayMicroseconds(500); //Wait for 500us to go into reset
}

The reason for the abstraction of the grid is to allow various effects to be implemented at a higher level (with simple x,y coordinates in a single orientation), which can then be transformed (e.g. rotated) at a lower level (controlled by user-operated buttons or a joystick). I've excluded that functionality from this simplification, since it still locks up without that.

Could the arrays be maxing out the memory on my Arduino Uno? 4 bytes * 64 * 2 = 512 bytes, which seems well within range of the 2kb I read about somewhere.

Is there any memory leak here that I'm missing? Given that the program seems to run fine initially, shifting rows one by one until it locks up (as though it hit some sort of threshold), I'm wondering if I'm not cleaning something up properly.

I would appreciate any insights! Thanks!

So you've got

long strip_colors[STRIP_LENGTH];
to create an array with 4 bytes at each location
So 256 bytes with STRIP_LENGTH = 64

Then the same here
long cellColors[8][8];
so 4 bytes each at 8x8 locations for another 256

then another couple dozen for the various int and long variables.
Seems like it'd be okay for variable use as you indicated.
Don't know what the compiler is assigning.
Have you tried Freemem() or whatever that command is to see how much the IDE says is available?

Running your code on my Uno (however without anything connected to it) it does not appear to lock up. Also a debugging display in setup is only shown once.

I suspect an electrical problem, you are possibly trying to drive too much, overheating the processor, and it resets.

Technical Parameter
Current
1.8A

Yeah, could be overheating the regulator, going into thermal shutdown.

This is cute:

void loop() {
  volatile int state = LOW;
  
  while(1){
    state = !state;
    digitalWrite(ledPin, state);
    
    shiftRows();
    delay(tempo);
  }
}

An infinite loop inside an infinite loop.

I should have caught the current requirements. I guess I assumed that since other effects were working fine across all 64 LEDs that must mean it's a software problem. But those effects didn't have all 64 LEDs constantly lit either, and they did exhibit some strange behavior when I switched from USB power to a separate (9V, 3A) power supply. So that's probably it.

My power supply can be switched to 5V, 4A, but the Arduino won't run on that will it? If I run the Arduino off a USB or 9V supply, then run the SPI pins through an optoisolator and power the LEDs with the 5V supply, that should work, right?

Re: the infinite loops, hey, I've just been trying to get the damn thing to work. :wink:

My power supply can be switched to 5V, 4A

If you connect to the 5V pin, yes that will work. However, you have to question how much current you want running through the Arduino board itself. Personally, if my project needs more than 500mA I separate out the supplies.

Well, I've got what I think is the right circuit for isolating the Arduino from the LEDs, but now I'm getting to the edge of my electronics experience. I've got my CKI and SDI pins connected to separate optoisolator channels (I'm using a PS2501 from Optoisolator - 4 Channel - COM-00784 - SparkFun Electronics) with in series resistors. I think that part is right, but I'm not sure how to wire the other side. The phototransistors in the optoisolator can't handle much current, so I'm guessing I'll need a couple regular transistors in the mix. Am I on the right track? If so how do I wire those in?

Attached is what I've tried so far with no luck. The transistors are 2N2222. The WS2801 represents the entire 64 LED strip.

Even if that's how to go about the wiring, are the SPI signals going to translate through the optoisolator appropriately? Will it be able to keep up?

If not, is there another way to separate power supplies?

Thanks for your help!

ArcadeLights.fzz (25.7 KB)

I don't see why you have to opto-isolate, and in any case the PS2501 is far too slow to switch (depending on your data rate I suppose). According to the datasheet it will switch in about 8 uS (3 rise + 5 fall).

Looking at this screenshot from my logic analyzer:

It won't be fast enough IMHO. Maybe the more experienced circuit guys will disagree.

I don't see why you can't just throw the optoisolator out the window, and simply power your WS2801 from your high-current source. And then just connect the grounds together, and CKI/SDI from the Arduino (no isolators).

Subject to what the electronics experts say, but it seems to me that should work.

Warning: programming is my stronger suit, not electronics. Better get someone else to confirm what I am saying.

Is there any particular reason to isolate the circuit? That seems like a lot of work to drive a chip.

That opto is very slow, even bit-banging as you are you could be too fast. One way to find out is to add a few delayMicros() in the shift routine.

Why are you bit banging anyway, you can use SPI or shifhOut().

You're driving Q1 and Q2 with no base resistors, that's not very kosher. Why do you need them?


Rob

Thanks guys, I think it's going to work without the optoisolator like you suggested. I'm a software guy as well, so this is a brave new world for me.

Re: the bit banging, that was just example code to illustrate what I thought was a memory leak. I have a couple SPI libraries I'm working with, at least one of which seems to be working now with my revised and simplified circuit. No more lockups!