WS2812 Grid effects, flashing red

I’m attempting to run some row/column lighting effects with the WS2812 libraries and the core keeps flashing red after wifi connect. Looks to be an S-O-S pattern, followed by a few steady flashes. I see that there might be some RAM issues, so I’m wondering if anybody could confirm that this is what I’m running into!

Each pixel is assigned to an array position depending on its x/y coordinates… so I suspect that the lightgrid[50][15] variable might be a little big, though I am totally unsure.

Code is as follows:

// This #include statement was automatically added by the Spark IDE.
#include "Spark_NeoPixel.h"

/* ======================= Includes ================================= */

#include <application.h>
#include "Spark_NeoPixel.h"

/* ======================= Prototype Defs =========================== */

void colorAll(uint32_t c, uint8_t wait);
void colorWipe(uint32_t c, uint8_t wait);
void rainbowCycle(uint8_t wait);
uint32_t Wheel(byte WheelPos);

int tinkerDigitalRead(String pin);
int tinkerDigitalWrite(String command);
int tinkerAnalogRead(String pin);
int tinkerAnalogWrite(String command);

/* ======================= Spark_StrandTest.cpp ===================== */

#define PIN D0
Adafruit_NeoPixel strip = Adafruit_NeoPixel(56, PIN, WS2812);

int cRed=0;
int cGrn=0;
int cBlu=0;
int timOut = 10;
int timCycle = 20;
int countdown;
bool colorUpdated = 0;
bool rainbowMode = 0;
bool clearStrip = 0;

int lightgrid[50][15];

void loadLightGrid(){
    
    // ..grid[xx][y] = LED_sequence_num;
    lightgrid[42][1]  = 33;
    lightgrid[42][3]  = 34;
    lightgrid[13][6]  = 12;
    lightgrid[22][6]  = 19;  // O in
    lightgrid[24][6]  = 26;  // O out
    lightgrid[29][6]  = 27;  // R in
    lightgrid[31][6]  = 28;
    lightgrid[33][6]  = 29;
    lightgrid[38][6]  = 44;
    lightgrid[40][6]  = 45;  // D out
    lightgrid[42][6]  = 35;
    lightgrid[45][6]  = 54;
    lightgrid[47][6]  = 53;
    lightgrid[6][7]   = 7;
    lightgrid[8][7]   = 8;
    lightgrid[10][7]  = 9;
    lightgrid[12][7]  = 11;
    lightgrid[36][7]  = 43;
    lightgrid[42][7]  = 36;
    lightgrid[48][7]  = 52;
    lightgrid[5][8]   = 6;
    lightgrid[14][8]  = 18;
    lightgrid[16][8]  = 14;
    lightgrid[20][8]  = 20;
    lightgrid[26][8]  = 25;
    lightgrid[29][8]  = 30;
    lightgrid[5][9]   = 5;
    lightgrid[42][9]  = 37;
    lightgrid[45][9]  = 47;
    lightgrid[47][9]  = 46;    // A in
    lightgrid[6][10]  = 4;
    lightgrid[8][10]  = 2;
    lightgrid[10][10] = 1;    // CA in
    lightgrid[12][10] = 10;
    lightgrid[20][10] = 21;
    lightgrid[26][10] = 24;
    lightgrid[29][10] = 31;
    lightgrid[36][10] = 42;
    lightgrid[48][10] = 51;
    lightgrid[7][11]  = 3;
    lightgrid[41][11] = 39;
    lightgrid[13][12] = 17;
    lightgrid[15][12] = 16;
    lightgrid[16][12] = 15;
    lightgrid[22][12] = 22;
    lightgrid[24][12] = 23;
    lightgrid[29][12] = 32;   // R out
    lightgrid[38][12] = 41;
    lightgrid[40][12] = 40;
    lightgrid[42][12] = 38;
    lightgrid[45][12] = 48;
    lightgrid[47][12] = 49;
    lightgrid[48][12] = 50;


} // loadLightGrid

void showColor() {
  colorUpdated = 1;
  countdown = 1000;
}
void setup() {
    
    Spark.function("digitalwrite", chageLEDPattern);
    Spark.function("analogwrite", updateRGBLEDColor);
    //Spark.variable('lightgrid_1_1', &lightgrid[1][1], STRING);

    strip.begin();
    strip.show(); // Initialize all pixels to 'off'
    
    loadLightGrid();
}

void loop() {
 
    if( colorUpdated == 1){
        colorAll( strip.Color(cRed, cGrn, cBlu), timOut);
        colorUpdated = 0;
    }
    
    if( rainbowMode == 1){
        rainbowCycle(timCycle);
    }
    
    if( clearStrip == 1){
        colorAll( strip.Color(0,0,0), 0);
        clearStrip = 0;
    }

}
/*******************************************************************************
 * Function Name  : updateRGBLEDColor
 * Description    : Overrides analogWrite for display purposes
 * Input          : Pin and Value (0 to 255)
 * Output         : None.
 * Return         : 1 on success and a negative number on failure
 *******************************************************************************/
int updateRGBLEDColor(String pinAndValue) {
  int pinNumber = pinAndValue.charAt(1) - '0';
  int value = pinAndValue.substring(3).toInt();
  
  if( value > 255)
    value = 255;
 
  if (5 == pinNumber)
    cRed = value;
  else if (6 == pinNumber)
    cGrn = value;
  else if (7 == pinNumber)
    cBlu = value;
  else if(1 == pinNumber)
    timCycle = value;
  else if(0 == pinNumber)
    timOut = value;
 
  showColor();
 
  return 0;
}

/*******************************************************************************
 * Function Name  : changeLEDPattern
 * Description    : Override digitalWrite call
 * Input          : Pin and value
 * Output         : None.
 * Return         : 1 on success and a negative number on failure
 *******************************************************************************/
int chageLEDPattern(String command){
    
    int pinNumber = command.charAt(1) - '0';
	//Sanity check to see if the pin numbers are within limits
	if (pinNumber< 0 || pinNumber >7) return -1;
	
    /* Deal with this later...
	bool value = 0;
	//convert ascii to integer
	if(command.substring(3,7) == "HIGH") rainbowMode = 1;
	else if(command.substring(3,6) == "LOW") rainbowMode = 0;
	else return -2;

	if(command.startsWith("D"))
	{
		//pinMode(pinNumber, OUTPUT);
		//digitalWrite(pinNumber, value);
		
		return 1;
	}
	else if(command.startsWith("A"))
	{
		//pinMode(pinNumber+10, OUTPUT);
		//digitalWrite(pinNumber+10, value);
		return 1;
	}
	*/
	
	if( (pinNumber == 7) && (command.substring(3,7) == "HIGH") ){
	    rainbowMode = 1;
	} else if( (pinNumber == 7) && (command.substring(3,6) == "LOW") ){
	    rainbowMode = 0;
	    clearStrip = 1;
	} else if( (pinNumber == 0) && (command.substring(3,6) == "HIGH") ){
	   rowWipe( strip.Color(cRed, cGrn, cBlu), 500);   
    } else if( (pinNumber == 1) && (command.substring(3,6) == "HIGH") ){
	   colWipe( strip.Color(cRed, cGrn, cBlu), 500);   
	}
	else return -3;
	
	
}

/*******************************************************************************

 * Custom NEOPIXEL functions
 
 *******************************************************************************/
// Set all pixels in the strip to a solid color, then wait (ms)
void rowWipe(uint32_t c, uint8_t wait) {
  uint16_t i;
  
//  for(i=0; i<strip.numPixels(); i++) {
//    strip.setPixelColor(i, c);
//  }
//  strip.show();
  delay(wait);
}

// Set all pixels in the strip to a solid color, then wait (ms)
void colWipe(uint32_t c, uint8_t wait) {
    uint16_t i;
    uint16_t j;
  
    for(i=1; i<48; i++) {
        for(j=1; j<12; j++) {
            if( lightgrid[i][j] > 0 ){
                strip.setPixelColor( lightgrid[i][j], c);    
            }
        }
        
        strip.show();
        delay(wait);
  
    }
  
}

/*******************************************************************************

 * NEOPIXEL FUNCTIONS
 
 *******************************************************************************/

// Set all pixels in the strip to a solid color, then wait (ms)
void colorAll(uint32_t c, uint8_t wait) {
  uint16_t i;
  
  for(i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
  }
  strip.show();
  delay(wait);
}

// Fill the dots one after the other with a color, wait (ms) after each one
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout, then wait (ms)
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) { // 1 cycle of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}

Any and all insight welcome! Thanks for your time guys :smile:

This keeps coming up… I wish I had a better answer for exactly how much RAM is available, and how large we can globally define arrays in RAM. Part of the problem is the available RAM keeps changing as new features get added to the Core firmware. I suspect most answers that follow will say things like, you’ll be fine if you keep it under around so and so bytes… or I can run so and so fine, but this and that yields errors. Anyone at Spark have a more definitive answer? Obviously we are way off from having 20KB of RAM available to the user.

I have to agree. I ran into this problem, managed to reduce my ram consuption but this is really ridiculous…

Incidentally, I also hit this on some long JSON strings today during a totally different project. Breaking the strings into multiple sprintf calls was the key, but I was sad that even 128 characters was too much. Are we expecting too much out of the core?

In the beginning I was expecting 20KB of RAM, but then as we learned how the software worked it was something more like 10KB. But now users have issues declaring global buffers larger than 1024 so I honestly don’t know what the expectation is anymore. I still want the expectation to be high though :smile:

Is the RAM onboard one of the chips, and could it be expanded?

Yo @BDub - does this lightgrid[] variable have to be defined as a global, or would it work better if I moved it into the main loop?

If it's a spark variable I "think" it needs to be global. However now that I look at this you are globally defining it as an int array but then trying to use it as a STRING with Spark.variable?

Good call on the int/string, likely a slip between posting code versions here and re-writing a bunch of stuff as I formulate my 1-hour forum post :smile:

Also good to know that Spark variables need to be globals.