How to store color values in a dynamic 2d array

Hi, I’m trying to do a chase pattern where a new tail gets added to the pattern in response to a cloud event occurring. And, depending on the cloud event, I want each tail to be a different color. So, I need a way to store the order of colors–trying to use a dynamic 2d array to do this.

It seems I need to declare the array with a set number of dimensions. But, I want the dimensions to increase as more cloud events occur. Is there a way to add more and more rows to an array dynamically in real time like this?

Here is my code:

#include "application.h"
#include "neopixel/neopixel.h"

// IMPORTANT: Set pixel COUNT, PIN and TYPE
#define PIXEL_COUNT 864
#define PIXEL_PIN D1
#define PIXEL_TYPE SK6812RGBW

Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);

int ideaCount = 0;
int colCount = 3;
int tailCount = 0;

uint8_t** ary = new uint8_t*[50];



void setup() {
    delay(2000);
    strip.begin();
    strip.show();

    // We are also going to declare a Particle.function so that we can turn the LED on and off from the cloud.
    // This is saying that when we ask the cloud for the function "led", it will employ the function ledToggle() from this app.
    Particle.variable("ideaCount", ideaCount);
    Particle.function("led",ledToggle);
    for(int i = 0; i < 50; i++) {
        ary[i] = new uint8_t[3];
}
    ary[0][0] = 0;
    ary[0][1] = 100;
    ary[0][2] = 0;
}
    

                    


void loop() {
    chase();
}


// We're going to have a super cool function now that gets called when a matching API request is sent
// This is the ledToggle function we registered to the "led" Particle.function earlier.
int ledToggle(String command) {
    /* Particle.functions always take a string as an argument and return an integer.
    Since we can pass a string, it means that we can give the program commands on how the function should be used.
    In this case, telling the function "on" will turn the LED on and telling it "off" will turn the LED off.
    Then, the function returns a value to us to let us know what happened.
    In this case, it will return 1 for the LEDs turning on, 0 for the LEDs turning off,
    and -1 if we received a totally bogus command that didn't do anything to the LEDs.
    */

    if (command=="idea") {
        // Do not run more than 15 seconds of these, or the b/g tasks
        // will be blocked.
        //--------------------------------------------------------------
        ary[ideaCount][0] = 200;
        ary[ideaCount][1] = 200;
        ary[ideaCount][2] = 0;
        ideaCount++;
        return 0;
      
    }
    else if (command=="comment") {
        ary[ideaCount][0] = 0;
        ary[ideaCount][1] = 0;
        ary[ideaCount][2] = 200;
        ideaCount++;
        return 1;
    }
    else {
        return -1;
    }
}



void chase() {
    
    for(uint16_t i=0; i<strip.numPixels()+(ideaCount*15); i++) {
        //tailCount = 0;
        for(uint16_t j=0; j<ideaCount*15; j=j+15) {
            strip.setPixelColor(i-j, strip.Color(ary[(j/15)][0], ary[(j/15)][1], ary[(j/15)][2], 200)); // Draw new pixel
            strip.setPixelColor(i-1-j, strip.Color(ary[(j/15)][0], ary[(j/15)][1], ary[(j/15)][2], 100)); // Draw new pixel
            strip.setPixelColor(i-2-j, strip.Color(ary[(j/15)][0], ary[(j/15)][1], ary[(j/15)][2], 75)); // Draw new pixel
            strip.setPixelColor(i-3-j, strip.Color(ary[(j/15)][0], ary[(j/15)][1], ary[(j/15)][2], 50)); // Draw new pixel
            strip.setPixelColor(i-4-j, strip.Color(ary[(j/15)][0], ary[(j/15)][1], ary[(j/15)][2], 25)); // Draw new pixel
            strip.setPixelColor(i-5-j, strip.Color(ary[(j/15)][0], ary[(j/15)][1], ary[(j/15)][2], 25)); // Draw new pixel
            strip.setPixelColor(i-6-j, strip.Color(ary[(j/15)][0], ary[(j/15)][1], ary[(j/15)][2], 10)); // Draw new pixel
            strip.setPixelColor(i-7-j, strip.Color(ary[(j/15)][0], ary[(j/15)][1], ary[(j/15)][2], 0)); // Draw new pixel
            strip.setPixelColor(i-8-j, strip.Color(ary[(j/15)][0], ary[(j/15)][1], ary[(j/15)][2], 0)); // Draw new pixel
            strip.setPixelColor(i-9-j, strip.Color(ary[(j/15)][0], ary[(j/15)][1], ary[(j/15)][2], 0)); // Draw new pixel
            strip.setPixelColor(i-10-j, 0);// Erase pixel a few steps back
            //tailCount ++;
        
        }
        
        strip.show();
        delay(25);
    };
};

@kkwak, any reason you dynamically allocated arrays instead of static allocation?

You could use dynamic C++ containers
http://www.cplusplus.com/reference/stl/

But since you have an static upper limit provided by your PIXEL_COUNT you could well go for a static array (as @peekay123 suggested) to avoid the troubles of garbage collection (which isn’t done by the system) and heap fragmentation.

@peekay123 @ScruffR
I’m using a dynamic array because the # of rows I want to allocate to it aren’t yet determined. The # of rows will increase as more cloud events occur.

For example, if Type1 cloud event occurs, I want to add a ‘red’ chase light to the strip. Then, Type 2 cloud event occurs, so I want to add a ‘blue’ chase light running behind the red one. Then Type1 happens again so I add a ‘red’ chase light behind the second one. The pattern will keep increasing as more events occur. And I’m using the dynamic array to keep track of the pattern of colors, based on the order in which the respective cloud events occur.

Is this possible to do without setting a limit to the # of rows in the 2d array?

@kkwak: yup, there is a way. There are multiple ways. If you process your events in order, then you can leverage/use the Arduino library QueueList. It is a library built up of what @ScruffR mentioned – C++ containers (templates). This particular queue is first in/first out (FIFO). I cobbled a code example which can be downloaded in raw form. You will need the library which is a plain header file: QueueList.h in the same directory as your project’s ino file.

The benefit of a defined structure is your ability to quickly add/remove/modify the structure in one part of your code… of course adjusting other parts that utilize the structure. The structure cannot change mid-stream though. It could, but that would mean passing a pointer to create a queue of pointers.

This queue will still cause heap fragmentation as the library itself is doing all the allocation and destruction of memory for the structures being pushed and popped from the queue. The queue will let you create an infinite number of elements. It has a memory check to see if the new node is created. I don’t know if you will just happily exceed heap :wink:

So, looks like you are using RGB information. You can also store other information along with the structure as desired.

If you run the example program, you do not need the SYSTEM_THREAD(ENABLED);. I needed that so setup() and loop() would run while the Photon doesn’t have access to the internet. The output of the program should look like this:

0000010043 [app] TRACE: creating eventColor queue
0000010043 [app] TRACE: adding some sample events:RG
0000010044 [app] TRACE: adding some sample events:R
0000010044 [app] TRACE: Event count? 2
0000010044 [app] TRACE: adding some sample events:B
0000010044 [app] INFO: Event seen R(200) G(200) B(0)
0000010044 [app] INFO: Event seen R(200) G(0) B(0)
0000010045 [app] INFO: Event seen R(0) G(0) B(200)

I added two events in setup() and one in the loop() for demonstration. Adding events could be done via a Particle.function() callback and then captured in the loop() for later processing.

2 Likes