For each LED, we can use a so-called struct that combines the information for a single led.
struct LEDINFO
{
int num;
byte red;
byte green;
byte blue;
};
And we can combine all those in a two-dimensional array. Below is based on the two patterns that you gave; I hope that I did not make copy/paste/edit mistakes. Note that the led numbers are one-based (starting from 1); reason is that leds that are not populated in the below array are automatically set to 0 (number, red, green and blue).
LEDINFO patterns[][110] =
{
// pattern 01
{
{38, 10, 0, 0},
{120, 10, 0, 0},
{255, 0, 0, 10},
{358, 10, 0, 0},
{420, 0, 0, 10},
{585, 10, 0, 0},
{633, 0, 0, 10},
{708, 10, 0, 0},
{838, 10, 0, 0},
{995, 0, 0, 10},
{1081, 10, 0, 0},
{1122, 10, 0, 0},
{1294, 0, 0, 10},
{1375, 10, 0, 0},
{1417, 10, 0, 0},
},
// pattern 02
{
{ 1, 10, 0, 0},
{ 11, 10, 0, 0},
{ 166, 0, 0, 10},
{ 299, 0, 0, 10},
{ 361, 10, 0, 0},
{ 515, 0, 0, 10},
{ 760, 10, 0, 0},
{ 1201, 10, 0, 0},
},
};
Below setup demonstrates how to use it. It loops through the patterns and for each pattern it loops through all leds. It skips leds with number 0 so the serial monitor is not flooded.
void setup()
{
Serial.begin(57600);
for (uint16_t patCnt = 0; patCnt < sizeof(patterns) / sizeof(patterns[0]); patCnt++)
{
Serial.print("Pattern "); Serial.println(patCnt + 1);
for (uint16_t ledCnt = 0; ledCnt < sizeof(patterns[0]) / sizeof(patterns[0][0]); ledCnt++)
{
if (patterns[patCnt][ledCnt].num != 0)
{
Serial.print("\tLed "); Serial.print(patterns[patCnt][ledCnt].num);
Serial.print("\tr = "); Serial.print(patterns[patCnt][ledCnt].red);
Serial.print("\tg = "); Serial.print(patterns[patCnt][ledCnt].green);
Serial.print("\tb = "); Serial.print(patterns[patCnt][ledCnt].blue);
Serial.println();
}
}
}
}
void loop()
{
}
The construction with sizeof is used to (at compile time) determine how many patterns there are and how many leds there are in a pattern. Usually one defines a macro for that that can take any type of array (int, float, struct) and will calculate the number of elements.
#define NUMELEMENTS(x) sizeof(x)/sizeof(x[0])
After compiling the above code
Sketch uses 3060 bytes (9%) of program storage space. Maximum is 32256 bytes.
Global variables use 1320 bytes (64%) of dynamic memory, leaving 728 bytes for local variables. Maximum is 2048 bytes.
This will take 1320 bytes of RAM; I'm basically at the limit of my Uno.
So let's move your patterns to PROGMEM
const LED patterns[][110] PROGMEM =
{
...
...
};
Sketch uses 3060 bytes (9%) of program storage space. Maximum is 32256 bytes.
Global variables use 220 bytes (10%) of dynamic memory, leaving 1828 bytes for local variables. Maximum is 2048 bytes.
OK, 1100 bytes saved. Do not run it as you will have garbage patterns.
You now need to read from PROGMEM. The below reads the information for the leds (one at a time) for each pattern
#define NUMELEMENTS(x) sizeof(x)/sizeof(x[0])
struct LEDINFO
{
int num;
byte red;
byte green;
byte blue;
};
const LEDINFO patterns[][110] PROGMEM =
{
// pattern 01
{
{38, 10, 0, 0},
{120, 10, 0, 0},
{255, 0, 0, 10},
{358, 10, 0, 0},
{420, 0, 0, 10},
{585, 10, 0, 0},
{633, 0, 0, 10},
{708, 10, 0, 0},
{838, 10, 0, 0},
{995, 0, 0, 10},
{1081, 10, 0, 0},
{1122, 10, 0, 0},
{1294, 0, 0, 10},
{1375, 10, 0, 0},
{1417, 10, 0, 0},
},
// pattern 02
{
{ 1, 10, 0, 0},
{ 11, 10, 0, 0},
{ 166, 0, 0, 10},
{ 299, 0, 0, 10},
{ 361, 10, 0, 0},
{ 515, 0, 0, 10},
{ 760, 10, 0, 0},
{ 1201, 10, 0, 0},
},
};
void setup()
{
Serial.begin(57600);
for (uint16_t patCnt = 0; patCnt < NUMELEMENTS(patterns); patCnt++)
{
Serial.print("Pattern "); Serial.println(patCnt + 1);
for (uint16_t ledCnt = 0; ledCnt < NUMELEMENTS(patterns[0]); ledCnt++)
{
// a work copy for a led
LEDINFO led;
// retrieve from PROGMEM
memcpy_P(&led, &patterns[patCnt][ledCnt], sizeof(LEDINFO));
// if the number is not zero, it's an 'active' led
if (led.num != 0)
{
Serial.print("\tLed "); Serial.print(led.num);
Serial.print("\tr = "); Serial.print(led.red);
Serial.print("\tg = "); Serial.print(led.green);
Serial.print("\tb = "); Serial.print(led.blue);
Serial.println();
}
}
}
}
void loop()
{
}
Instead of serial printing, you can use the led info to set the leds. First create two functions, one to clear the leds and one to set the leds.
/*
clear all leds of the strip
*/
void clearLeds()
{
// loop through all leds and clear them
for (uint16_t ledCnt = 0; ledCnt < NUM_LEDS; ledCnt++)
{
leds[ledCnt] = CRGB(0, 0, 0);
}
// show the result
FastLED.show();
}
/*
set the leds for a specified pattern
In:
zero based pattern number
*/
void setLeds(int patternNumber)
{
Serial.print("Pattern "); Serial.println(patternNumber + 1);
// loop through all leds for the given pattern
for (uint16_t ledCnt = 0; ledCnt < NUMELEMENTS(patterns[patternNumber]); ledCnt++)
{
// a work copy for a led
LEDINFO led;
// retrieve from PROGMEM
memcpy_P(&led, &patterns[patternNumber][ledCnt], sizeof(LEDINFO));
// if the number is not zero, it's an 'active' led
if (led.num != 0)
{
// led numbers are one-based, subtract 1 from num
leds[led.num - 1] = CRGB(led.red, led.green, led.blue);
}
}
// show the result
FastLED.show();
}
The full demo code that will display the two patterns for 5 seconds
#include <FastLED.h>
#define NUM_LEDS 1500
#define DATA_PIN 3
#define NUMELEMENTS(x) sizeof(x)/sizeof(x[0])
#define MAX_LEDS 110
CRGB leds[NUM_LEDS];
struct LEDINFO
{
int num;
byte red;
byte green;
byte blue;
};
const LEDINFO patterns[][MAX_LEDS] PROGMEM =
{
// pattern 01
{
{38, 10, 0, 0},
{120, 10, 0, 0},
{255, 0, 0, 10},
{358, 10, 0, 0},
{420, 0, 0, 10},
{585, 10, 0, 0},
{633, 0, 0, 10},
{708, 10, 0, 0},
{838, 10, 0, 0},
{995, 0, 0, 10},
{1081, 10, 0, 0},
{1122, 10, 0, 0},
{1294, 0, 0, 10},
{1375, 10, 0, 0},
{1417, 10, 0, 0},
},
// pattern 02
{
{ 1, 10, 0, 0},
{ 11, 10, 0, 0},
{ 166, 0, 0, 10},
{ 299, 0, 0, 10},
{ 361, 10, 0, 0},
{ 515, 0, 0, 10},
{ 760, 10, 0, 0},
{ 1201, 10, 0, 0},
},
};
void setup()
{
Serial.begin(57600);
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
clearLeds();
setLeds(0);
delay(5000);
clearLeds();
setLeds(1);
delay(5000);
clearLeds();
}
void loop()
{
}
/*
clear all leds of the strip
*/
void clearLeds()
{
// loop through all leds and clear them
for (uint16_t ledCnt = 0; ledCnt < NUM_LEDS; ledCnt++)
{
leds[ledCnt] = CRGB(0, 0, 0);
}
// show the result
FastLED.show();
}
/*
set the leds for a specified pattern
In:
zero based pattern number
*/
void setLeds(int patternNumber)
{
Serial.print("Pattern "); Serial.println(patternNumber + 1);
// loop through all leds for the given pattern
for (uint16_t ledCnt = 0; ledCnt < NUMELEMENTS(patterns[patternNumber]); ledCnt++)
{
// a work copy for a led
LEDINFO led;
// retrieve from PROGMEM
memcpy_P(&led, &patterns[patternNumber][ledCnt], sizeof(LEDINFO));
// if the number is not zero, it's an 'active' led
if (led.num != 0)
{
// led numbers are one-based, subtract 1 from num
leds[led.num - 1] = CRGB(led.red, led.green, led.blue);
}
}
// show the result
FastLED.show();
}
I did a quick compile for a Mega with 50 patterns
Sketch uses 32840 bytes (12%) of program storage space. Maximum is 253952 bytes.
Global variables use 4784 bytes (58%) of dynamic memory, leaving 3408 bytes for local variables. Maximum is 8192 bytes.
In a later post I might get back with a way to select a pattern using the serial monitor. You can try that out yourself.
Note
I have no way to test the last code as I don't have a Mega.