diff --git a/Helper.ino b/Helper.ino new file mode 100644 index 0000000..b21e03b --- /dev/null +++ b/Helper.ino @@ -0,0 +1,255 @@ +#include +#include + +/// @brief Loads the last saved colors and delays into the current arrays. +void loadLast() +{ + uint16_t buf = 0; + for (uint8_t k = 0; k < FRAME_COUNT; k++) + { + for (uint8_t l = 0; l < LED_COUNT; l++) + { + buf = colorsLast[k][l]; + colorsLast[k][l] = colors[k][l]; + colors[k][l] = buf; + } + buf = delayLast[k]; + delayLast[k] = cycleDelay[k]; + cycleDelay[k] = buf; + } +} + +void printNumPadded(uint16_t in) { + if(in == 255) { + Serial.print("---"); //Special case in matrix printing + } else if (in < 10) { + Serial.print(" "); + Serial.print(in); + Serial.print(" "); + } else if (in < 100) { + Serial.print(" "); + Serial.print(in); + } else { + Serial.print(in); + } +} + + + +/// @brief Saves the current colors and delays into the "last" arrays for potential later retrieval. Call this before making changes to the current arrays if you want to be able to easily revert back to the previous state. +void saveLast() +{ + for (uint8_t k = 0; k < FRAME_COUNT; k++) + { + for (uint8_t l = 0; l < LED_COUNT; l++) + { + colorsLast[k][l] = colors[k][l]; + } + delayLast[k] = cycleDelay[k]; + } +} + +/// @brief Clears all colors and delays from the current arrays and the strip. +void clear() +{ + for (uint8_t k = 0; k < FRAME_COUNT; k++) + { + for (uint8_t l = 0; l < LED_COUNT; l++) + { + colors[k][l] = 0; + } + cycleDelay[k] = 0; + strip.clear(); + strip.show(); + } +} + +/// @brief Clears the colors for a specific frame. +void clear(uint8_t k) +{ + for (uint8_t l = 0; l < LED_COUNT; l++) + { + colors[k][l] = 0; + } + strip.clear(); + strip.show(); +} + +///@brief Generates a color. +uint32_t c(uint8_t r, uint8_t g, uint8_t b) +{ + return strip.Color(r, g, b); +} + +///@brief Generates black color. +uint32_t c() +{ + return strip.Color(0, 0, 0); +} + +uint32_t c(uint32_t in, bool silent = true) +{ + + uint8_t r = floor(in / 100); + uint8_t g = floor((in - (100 * r)) / 10); + uint8_t b = floor(in - (100 * r) - (10 * g)); + + r = constrain(r, 0, 9); + g = constrain(g, 0, 9); + b = constrain(b, 0, 9); + + uint8_t newr = 3 * r * r; + uint8_t newg = 3 * g * g; + uint8_t newb = 3 * b * b; + + if (r == 1) + { + newr = 6; + } + + if (g == 1) + { + newg = 6; + } + + if (b == 1) + { + newb = 6; + } + + if (!silent) + { + Serial.print("> Color code `"); + Serial.print(in); + Serial.print("`: rgb("); + Serial.print(newr); + Serial.print(", "); + Serial.print(newg); + Serial.print(", "); + Serial.print(newb); + Serial.print(")"); + Serial.println(); + } + + return strip.Color(newr, newg, newb); +} + +/// @brief Saves the current colors and delays to EEPROM. +void saveEEPROM() +{ + EEPROM.write(0, 1); // Set a flag to indicate that data is saved + EEPROM.put(1, colors); + EEPROM.put(sizeof(colors) + 1, cycleDelay); +} + +/// @brief Loads the colors and delays from EEPROM into the current arrays. +/// @return true if data was successfully loaded, false if no valid data was found. +bool loadEEPROM() +{ + if (EEPROM.read(0) != 1) + { + return false; + } + EEPROM.get(1, colors); + EEPROM.get(sizeof(colors) + 1, cycleDelay); + return true; +} + +/// @brief Clears all data from EEPROM. +void clearEEPROM() +{ + for (int i = 0; i < EEPROM.length(); i++) + { + EEPROM.write(i, 0); + } +} + +void toggleBuiltin(){ + if (LEDBuiltinOn) { + digitalWrite(LED_BUILTIN, LOW); + } else { + digitalWrite(LED_BUILTIN, HIGH); + } + LEDBuiltinOn = !LEDBuiltinOn; +} + +/// @brief Transforms an x and y value into an LED index based on the MATRIX array. +uint8_t xyToIndex(int x, int y) +{ + if (x < 0 || x >= MATRIX_WIDTH || y < 0 || y >= MATRIX_HEIGHT) + { + Serial.println("> Warning: xyToIndex: 'cell (" + String(x) + "," + String(y) + ") is out of bounds'"); + return 255; + } + + uint8_t index = pgm_read_byte(&(MATRIX[y][x])); + if (index == 255) + { + Serial.println("> Warning: xyToIndex: 'cell (" + String(x) + "," + String(y) + ") is empty'"); + return 255; + } + return index; +} + +uint8_t indToX(uint8_t ind){ + for(uint8_t y = 0; y < MATRIX_HEIGHT; y++){ + for(uint8_t x = 0; x < MATRIX_WIDTH; x++){ + if(pgm_read_byte(&(MATRIX[y][x])) == ind){ + return x; + } + } + } + return 255; +} + +uint8_t indToY(uint8_t ind){ + for(uint8_t y = 0; y < MATRIX_HEIGHT; y++){ + for(uint8_t x = 0; x < MATRIX_WIDTH; x++){ + if(pgm_read_byte(&(MATRIX[y][x])) == ind){ + return y; + } + } + } + return 255; +} + +String extractCmd(String in){ + while (in.indexOf('\n') != -1) { + in = in.substring(0, in.indexOf('\n')); + } + uint8_t idxStr = in.indexOf(' '); + if(idxStr == -1){ + return in; + } else { + return in.substring(0, idxStr); + } +} + +uint8_t parseIntCharsToBurn(String s) { + uint8_t parseIntIdx = 0; + bool shouldFinish = false; + while (!shouldFinish) { + if (isDigit(s.charAt(parseIntIdx))) { + parseIntIdx++; + } else { + shouldFinish = true; + } + } + return parseIntIdx; +} + +int parseInt(String s) { + uint8_t parseIntIdx = 0; + int out = 0; + bool shouldFinish = false; + while (!shouldFinish) { + if (isDigit(s.charAt(parseIntIdx))) { + out *= 10; + out += s.substring(parseIntIdx, parseIntIdx + 1).toInt(); + parseIntIdx++; + } else { + shouldFinish = true; + } + } + return out; +} \ No newline at end of file diff --git a/LightShowCLI-Embedded.ino b/LightShowCLI-Embedded.ino new file mode 100644 index 0000000..e2a915a --- /dev/null +++ b/LightShowCLI-Embedded.ino @@ -0,0 +1,86 @@ +#include +#include +#include "Matrix.h" + +#define PARITY_EVEN 2 +#define PARITY_ODD 1 +#define PARITY_ALL 0 + +bool LEDBuiltinOn = false; + +uint16_t cycleDelay[FRAME_COUNT]; +uint16_t delayLast[FRAME_COUNT]; +uint16_t colors[FRAME_COUNT][LED_COUNT]; +uint16_t colorsLast[FRAME_COUNT][LED_COUNT]; + +Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_RGB + NEO_KHZ800); + +void setup() +{ + toggleBuiltin(); + pinMode(13, OUTPUT); + strip.begin(); + strip.show(); + Serial.begin(9600); + Serial.println(F("> ####################> SERIAL LIGHT SHOW <####################")); + toggleBuiltin(); + Serial.println(F("> Version 0.0.0 [beta]")); + Serial.println(F("> **Warning:** Set line ending to `New Line` for proper parsing.")); + Serial.println(F("> Type `help` for help")); + Serial.println("> Currently configured with " + String(LED_COUNT) + " LEDs and " + String(FRAME_COUNT) + " frames. Feature flags: cacheCmds: NO; textDisp: NO; WiFi: NO"); + toggleBuiltin(); + if (loadEEPROM()) + { + Serial.println(F("> Saved data found in EEPROM and loaded successfully.")); + } + else + { + Serial.println(F("> No saved data found in EEPROM.")); + } + Serial.println(F("> Ready for command \n")); + toggleBuiltin(); +} + +uint16_t bm = 0; + +void loop() +{ + serialHandle(); + for (uint8_t i = 0; i < FRAME_COUNT; i++) + { + if(cycleDelay[i] == 254) { //Text flag + for (uint8_t j = 1; j < LED_COUNT; j++) { + if (colors[i][j] == 0) { + break; + } else { + //textLoop(i, j); + serialHandle(); + } + } + } else { + if(cycleDelay[i] != 0){ + colorLoop(i); + toggleBuiltin(); + } + bm = cycleDelay[i]; + if (bm > 0){ + while(bm > 3000){ + toggleBuiltin(); + delay(50); + toggleBuiltin(); + delay(50); + toggleBuiltin(); + delay(50); + toggleBuiltin(); + delay(50); + toggleBuiltin(); + delay(50); + bm -= 500; + serialHandle(); + } + delay(bm); + bm = 0; + } + } + } +} \ No newline at end of file diff --git a/LoopFuncs.ino b/LoopFuncs.ino new file mode 100644 index 0000000..7fb5a87 --- /dev/null +++ b/LoopFuncs.ino @@ -0,0 +1,449 @@ +/// @brief Runs the inner loop for a given frame index, displaying the colors and applying the delay. +/// @param i The frame index to run. +void colorLoop(uint8_t i) +{ + for (uint8_t k = 0; k < LED_COUNT; k++) + { + strip.setPixelColor(k + OFFSET, c(colors[i][k])); + } + strip.show(); +} + +void serialHandle(){ + if(Serial.available() == -1) return; + delay(200); + String cmd = ""; + String cmdNext = ""; + uint8_t parseIdx = 0; + bool mustReParse = false; + while(Serial.available() > 0 || mustReParse){ + char in = '\n'; + if (!mustReParse) { + in = (char)Serial.read(); + cmd += in; + } + if(in == '\n' || mustReParse){ + String cmdM = cmd; //Modifiable + int indNextCmd = cmd.indexOf(" && "); + if(indNextCmd > 0) { + mustReParse = true; + cmdNext = cmd.substring(indNextCmd + 4); + cmdM = cmd.substring(0, indNextCmd); + } else { + mustReParse = false; + cmdNext = ""; + } + Serial.print("< " + cmdM); + //Crawl command! + if (cmdM.startsWith("help")) { + Serial.println(F(R"( +> Help: -------------------------- +> * - All available options in a range +> set <[o/e]frame[-frame2]> ([o/e], [o/e]) - Set a specific LED or series in a specific frame to a color. Use o/e before a series to only apply to the odd or even indexed LEDs. Use - to specify a range of frames or LEDs. +> clr <[o/e]frame[-frame2]> ([o/e], [o/e]) - Clear a specific LED or series in a specific frame. Use o/e before a series to only apply to the odd or even indexed LEDs. Use - to specify a range of frames or LEDs. +> clr <[o/e]frame[-frame2]> - Clear all LEDs in a specific frame +> dly <[o/e]frame[-frame2]> - Set the delay for a specific frame or series of frames. Enter in ms. Use o/e before a series to only apply to the odd or even indexed frames. Use - to specify a range of frames. +> txt - Display scrolling text on the display in place of a specific frame. +> print - Prints all previous commands since the last `clr` command. +> mtx [-n] - Display an ASCII representation of the LED matrix (-n gives LED IDs) +> export [—program] - Exports a C array of the current colors and delays for all frames, which can be copied into the program for static displays. (optionally as a program) +> help - Displays this help message. +> idx n - (x, y) of LED n +> idx (x, y) - number of LED at (x, y) +> undo - revert changes +> clr - Clear all frames and delays. +> clr -e - Clear EEPROM +> && - Used to chain multiple commands together. Example: `dly 0-4 500 && txt 0 Hello 555` sets frames 0-4 to a delay of 500ms and sets frame 0 to display "Hello" in color code 555. +> wrt - Writes the current frames and delays to EEPROM for retrieval on the next startup. +> rgb - Replies with the RGB values for a specific color code. +)")); + } else if (cmdM.startsWith("clr")) { + if (cmdM.length() < 6) { + saveLast(); + clear(); + Serial.println("> All light data cleared; `undo` to revert."); + } else if (cmdM.substring(4, 6).startsWith("-e")) { + clearEEPROM(); + } else { + saveLast(); + uint8_t from = 0; + uint8_t to = 0; + uint8_t parity = 2; + uint8_t fromX = 0; + uint8_t toX = MATRIX_WIDTH - 1; + uint8_t parityX = 2; + uint8_t fromY = 0; + uint8_t toY = MATRIX_HEIGHT - 1; + uint8_t parityY = 2; + cmdM = cmdM.substring(4); + if(cmdM.startsWith("o")) { + parity = 1; + cmdM = cmdM.substring(1); + } else if (cmdM.startsWith("e")) { + parity = 0; + cmdM = cmdM.substring(1); + } + if (cmdM.startsWith("*")){ + from = 0; + to = FRAME_COUNT; + cmdM = cmdM.substring(1); + } else { + from = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (cmdM.startsWith("-")) { + cmdM = cmdM.substring(1); + to = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (to < from) { + to = from; + } + } else { + to = from; + } + } + + cmdM = cmdM.substring(1); //burn space + if (cmdM.startsWith("(")) { + cmdM = cmdM.substring(1); + if(cmdM.startsWith("o")) { + parityX = 1; + cmdM = cmdM.substring(1); + } else if (cmdM.startsWith("e")) { + parityX = 0; + cmdM = cmdM.substring(1); + } + if (cmdM.startsWith("*")){ + cmdM = cmdM.substring(1); + } else { + fromX = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (cmdM.startsWith("-")) { + cmdM = cmdM.substring(1); + toX = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (toX < fromX) { + toX = fromX; + } + } else { + toX = fromX; + } + } + + cmdM = cmdM.substring(2); //burn comma and space + if(cmdM.startsWith("o")) { + parityY = 1; + cmdM = cmdM.substring(1); + } else if (cmdM.startsWith("e")) { + parityY = 0; + cmdM = cmdM.substring(1); + } + if (cmdM.startsWith("*")){ + cmdM = cmdM.substring(1); + } else { + fromY = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (cmdM.startsWith("-")) { + cmdM = cmdM.substring(1); + toY = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (toY < fromY) { + toY = fromY; + } + } else { + toY = fromY; + } + } + } + + Serial.print("> Clearing"); + for (uint8_t id = from; id <= to; id++) { + if((parity == 2) || (parity == id % 2)){ + for (uint8_t idX = fromX; idX <= toX; idX++) { + if((parityX == 2) || (parityX == idX % 2)){ + for (uint8_t idY = fromY; idY <= toY; idY++) { + if((parityY == 2) || (parityY == idY % 2)){ + colors[id][xyToIndex(idX, idY)] = 0; + Serial.print("."); + } + delay(20); + } + } + delay(50); + } + } + delay(100); + } + Serial.println("\n> Cleared requested LEDs."); + } + } else if (cmdM.startsWith("dly")) { + uint8_t from = 0; + uint8_t to = 0; + uint8_t parity = 2; + cmdM = cmdM.substring(4); + if(cmdM.startsWith("o")) { + parity = 1; + cmdM = cmdM.substring(1); + } else if (cmdM.startsWith("e")) { + parity = 0; + cmdM = cmdM.substring(1); + } + if (cmdM.startsWith("*")){ + from = 0; + to = FRAME_COUNT; + cmdM = cmdM.substring(1); + } else { + from = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (cmdM.startsWith("-")) { + cmdM = cmdM.substring(1); + to = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (to < from) { + to = from; + } + } else { + to = from; + } + } + saveLast(); + cmdM = cmdM.substring(1); //burn space + uint16_t del = parseInt(cmdM); + for (uint8_t id = from; id <= to; id++) { + if((parity == 2) || (parity == id % 2)){ + cycleDelay[id] = del; + Serial.print("> Delay for cycle "); + Serial.print(id); + Serial.print(" set to "); + Serial.print(del); + Serial.println("."); + } + delay(100); + } + } else if (cmdM.startsWith("wrt")) { + saveEEPROM(); + } else if (cmdM.startsWith("rgb")) { + c(parseInt(cmdM.substring(4)), false); + } else if (cmdM.startsWith("set")) { + saveLast(); + uint8_t from = 0; + uint8_t to = 0; + uint8_t parity = 2; + uint8_t fromX = 0; + uint8_t toX = MATRIX_WIDTH - 1; + uint8_t parityX = 2; + uint8_t fromY = 0; + uint8_t toY = MATRIX_HEIGHT - 1; + uint8_t parityY = 2; + cmdM = cmdM.substring(4); + if(cmdM.startsWith("o")) { + parity = 1; + cmdM = cmdM.substring(1); + } else if (cmdM.startsWith("e")) { + parity = 0; + cmdM = cmdM.substring(1); + } + if (cmdM.startsWith("*")){ + from = 0; + to = FRAME_COUNT; + cmdM = cmdM.substring(1); + } else { + from = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (cmdM.startsWith("-")) { + cmdM = cmdM.substring(1); + to = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (to < from) { + to = from; + } + } else { + to = from; + } + } + + cmdM = cmdM.substring(1); //burn space + if (cmdM.startsWith("(")) { + cmdM = cmdM.substring(1); + if(cmdM.startsWith("o")) { + parityX = 1; + cmdM = cmdM.substring(1); + } else if (cmdM.startsWith("e")) { + parityX = 0; + cmdM = cmdM.substring(1); + } + if (cmdM.startsWith("*")){ + cmdM = cmdM.substring(1); + } else { + fromX = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (cmdM.startsWith("-")) { + cmdM = cmdM.substring(1); + toX = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (toX < fromX) { + toX = fromX; + } + } else { + toX = fromX; + } + } + + cmdM = cmdM.substring(2); //burn comma and space + if(cmdM.startsWith("o")) { + parityY = 1; + cmdM = cmdM.substring(1); + } else if (cmdM.startsWith("e")) { + parityY = 0; + cmdM = cmdM.substring(1); + } + if (cmdM.startsWith("*")){ + cmdM = cmdM.substring(1); + } else { + fromY = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (cmdM.startsWith("-")) { + cmdM = cmdM.substring(1); + toY = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM)); + if (toY < fromY) { + toY = fromY; + } + } else { + toY = fromY; + } + } + cmdM = cmdM.substring(2); + } + + uint16_t col = parseInt(cmdM); + + Serial.print("> Setting"); + for (uint8_t id = from; id <= to; id++) { + if((parity == 2) || (parity == id % 2)){ + for (uint8_t idX = fromX; idX <= toX; idX++) { + if((parityX == 2) || (parityX == idX % 2)){ + for (uint8_t idY = fromY; idY <= toY; idY++) { + if((parityY == 2) || (parityY == idY % 2)){ + colors[id][xyToIndex(idX, idY)] = col; + Serial.print("."); + } + delay(20); + } + } + delay(50); + } + } + delay(100); + } + Serial.print("\n> Set requested LEDs to color "); + Serial.print(col); + Serial.println("."); + } else if (cmdM.startsWith("export")) { + Serial.println("> Export: "); + if(cmdM.substring(7).startsWith("--program")) { + Serial.print("#define LED_COUNT "); + Serial.println(LED_COUNT); + Serial.print("#define FRAME_COUNT "); + Serial.println(FRAME_COUNT); + Serial.println(F("#include \n#define LED_PIN 2\nAdafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_RGB + NEO_KHZ800")); + } + Serial.print("uint16_t cycleDelay[FRAME_COUNT] = {"); + for (uint16_t d = 0; d < FRAME_COUNT; d++) { + Serial.print(cycleDelay[d]); + if (d != FRAME_COUNT - 1) { + Serial.print(", "); + } + } + Serial.print("};\nuint16_t colors[FRAME_COUNT] = {\n"); + for (uint16_t d = 0; d < FRAME_COUNT; d++) { + Serial.print("{"); + for (uint16_t e = 0; e < LED_COUNT; e++) { + Serial.print(colors[d][e]); + if (e != LED_COUNT - 1) { + Serial.print(", "); + } + } + Serial.print(d != FRAME_COUNT - 1 ? "},\n" : "}\n"); + } + Serial.println("};"); + if(cmdM.substring(7).startsWith("--program")) { + Serial.print(F(R"( +void setup() +{ + strip.begin(); + strip.show(); + strip.setBrightness(50); +} +void loop() +{ + for (uint8_t i = 0; i < FRAME_COUNT) { + if (cycleDelay[i] != 0) + { + for (uint8_t k = 0; k < LED_COUNT; k++) + { + strip.setPixelColor(k + OFFSET, c(colors[i][k])); + } + strip.show(); + delay(cycleDelay[i]); + } + } + } +} + )")); + } + } else if (cmdM.startsWith("undo") || cmdM.startsWith("redo")) { + loadLast(); + Serial.println("> Last changes undone."); + } else if (cmdM.startsWith("idx")) { + cmdM = cmdM.substring(4); + uint8_t x, y, ind = 0; + if(cmdM.startsWith("(")) { + x = parseInt(cmdM); + cmdM = cmdM.substring(parseIntCharsToBurn(cmdM) + 2); + y = parseInt(cmdM); + ind = xyToIndex(x, y); + } else { + ind = parseInt(cmdM); + x = indToX(ind); + y = indToY(ind); + } + Serial.print("> LED with index " + ind); + Serial.print(" is at (" + x); + Serial.print(", " + y); + Serial.println(")."); + } else if (cmdM.startsWith("mtx")) { + bool printNums = false; + bool printCols = false; + uint8_t printFrame = 0; + if(cmdM[4] == '-' && cmdM[5] == 'n'){ + printNums = true; + } + if(cmdM[4] == '-' && cmdM[5] == 'c'){ + printCols = true; + cmdM = cmdM.substring(7); + printFrame = parseInt(cmdM); + } + Serial.println("> Matrix:"); + for (uint8_t l = 0; l < MATRIX_HEIGHT; l++){ + Serial.print("> "); + for (uint8_t m = 0; m < MATRIX_WIDTH; m++) { + if(printNums) { + printNumPadded(pgm_read_byte(&(MATRIX[l][m]))); + Serial.print(" "); + } else if (printCols) { + printNumPadded(colors[printFrame][xyToIndex(m, l)]); + Serial.print(" "); + } else { + Serial.print(pgm_read_byte(&(MATRIX[l][m])) == 255 ? " " : "o"); + } + } + Serial.println(); + } + } else { + Serial.println("> Command `" + extractCmd(cmd) + "` not recognized."); + } + cmd = cmdNext; + Serial.println(mustReParse ? "&&" : ""); + } + } +} \ No newline at end of file diff --git a/Matrix.h b/Matrix.h new file mode 100644 index 0000000..5573466 --- /dev/null +++ b/Matrix.h @@ -0,0 +1,35 @@ +// User-definable setup definitions +#define MATRIX_WIDTH 13 +#define MATRIX_HEIGHT 6 +#define OFFSET 4 //LEDs to customize before the main grid +#define LED_COUNT (MATRIX_WIDTH * MATRIX_HEIGHT) + OFFSET +#define LED_PIN 25 +#define FRAME_COUNT 14 + +const uint8_t MATRIX[MATRIX_HEIGHT][MATRIX_WIDTH] PROGMEM = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + {25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13}, + {26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, + {51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39}, + {52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64}, + {77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65} +}; // 6 rows, 12 columns +// #define TXT_ENABLED //Define this and add characters for display size +/* +#define MATRIX_WIDTH 7 +#define MATRIX_HEIGHT 7 +#define LED_COUNT 32 + +const uint8_t MATRIX[MATRIX_HEIGHT][MATRIX_WIDTH] PROGMEM = { + {255, 255, 255, 18, 19, 255, 255}, + {255, 5, 6, 17, 20, 31, 255}, + {255, 4, 7, 16, 21, 30, 255}, + {255, 3, 8, 15, 22, 29, 255}, + {255, 2, 9, 14, 23, 28, 255}, + {0, 1, 10, 13, 24, 26, 27}, + {255, 255, 11, 12, 25, 255, 255} + //7x7 Christmas tree pattern. 255 indicates no LED in that cell. +} +*/ + +/// DO NOT USE 255 as a value in the MATRIX array. It is used to indicate an empty cell and will be ignored by the program. \ No newline at end of file