5

I have built a custom installation in my home with 896 WS2812 LED driven by an ArduinoMega 2560 and powered by several rails just fine. However, the installation is arranged in a checkerboard-like pattern. There are 14 panels with 8 strips of 8 LED each (64 LED per panel), The strips are soldered in zigzag pattern within a panel, and the tiles are connected via leads in a "column progressive" fashion.

Hooking this up and running it works well but I'm trying to deal with the unusual pattern I've created, and having a hard time scaling with my current solution. I am worried that my solution is not very graceful and will use up a lot of unnecessary RAM. I am currently using Adafruit's NeoMatrix library.

My solution is to "fake" like I have many more pixels and use the setRemapFunction() to throw these fake grid points away. But as I continue to add fake pixels, I assume the overhead RAM is set aside for them and I will max out before getting done. I have 896 real pixels total, but at this rate I'll be declaring that I have 1,792. This is my attempt to deal with the offset, and is a work in progress. This is utilizing about half of my total grid so far:

const int16_t grid[] = {
0,  15,  16,  31,  32,  47,  48,  63,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 256, 271, 272, 287, 288, 303, 304, 319,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
1,  14,  17,  30,  33,  46,  49,  62,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 257, 270, 273, 286, 289, 302, 305, 318,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
2,  13,  18,  29,  34,  45,  50,  61,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 258, 269, 274, 285, 290, 301, 306, 317,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
3,  12,  19,  28,  35,  44,  51,  60,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 259, 268, 275, 284, 291, 300, 307, 316,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
4,  11,  20,  27,  36,  43,  52,  59,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 260, 267, 276, 283, 292, 299, 308, 315,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
5,  10,  21,  26,  37,  42,  53,  58,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 261, 266, 277, 282, 293, 298, 309, 314,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
6,   9,  22,  25,  38,  41,  54,  57,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 262, 265, 278, 281, 294, 297, 310, 313,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
7,   8,  23,  24,  39,  40,  55,  56,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 263, 264, 279, 280, 295, 296, 311, 312,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,

-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 128, 143, 144, 159, 160, 175, 176, 191,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 384, 399, 400, 415, 416, 431, 432, 447,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 129, 142, 145, 158, 161, 174, 177, 190,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 385, 398, 401, 414, 417, 430, 433, 446,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 130, 141, 146, 157, 162, 173, 178, 189,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 386, 397, 402, 413, 418, 429, 434, 445,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 131, 140, 147, 156, 163, 172, 179, 188,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 387, 396, 403, 412, 419, 428, 435, 444,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 132, 139, 148, 155, 164, 171, 180, 187,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 388, 395, 404, 411, 420, 427, 436, 443,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 133, 138, 149, 154, 165, 170, 181, 186,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 389, 394, 405, 410, 421, 426, 437, 442,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 134, 137, 150, 153, 166, 169, 182, 185,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 390, 393, 406, 409, 422, 425, 438, 441,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 135, 136, 151, 152, 167, 168, 183, 184,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 391, 392, 407, 408, 423, 424, 439, 440,

64,  79,  80,  95,  96, 111, 112, 127,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 320, 335, 336, 351, 352, 367, 368, 383,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
65,  78,  81,  94,  97, 110, 113, 126,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 321, 334, 337, 350, 353, 366, 369, 382,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
66,  77,  82,  93,  98, 109, 114, 125,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 322, 333, 338, 349, 354, 365, 370, 381,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
67,  76,  83,  92,  99, 108, 115, 124,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 323, 332, 339, 348, 355, 364, 371, 380,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
68,  75,  84,  91, 100, 107, 116, 123,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 324, 331, 340, 347, 356, 363, 372, 379,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
69,  74,  85,  90, 101, 106, 117, 122,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 325, 330, 341, 346, 357, 362, 373, 378,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
70,  73,  86,  89, 102, 105, 118, 121,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 326, 329, 342, 345, 358, 361, 374, 377,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
71,  72,  87,  88, 103, 104, 119, 120,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 327, 328, 343, 344, 359, 360, 375, 376,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,

-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 192, 207, 208, 223, 224, 239, 240, 255,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 448, 463, 464, 479, 480, 495, 496, 511,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 193, 206, 209, 222, 225, 238, 241, 254,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 449, 462, 465, 478, 481, 494, 497, 510,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 194, 205, 210, 221, 226, 237, 242, 253,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 450, 461, 466, 477, 482, 493, 498, 509,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 195, 204, 211, 220, 227, 236, 243, 252,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 451, 460, 467, 476, 483, 492, 499, 508,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 196, 203, 212, 219, 228, 235, 244, 251,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 452, 459, 468, 475, 484, 491, 500, 507,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 197, 202, 213, 218, 229, 234, 245, 250,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 453, 458, 469, 474, 485, 490, 501, 506,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 198, 201, 214, 217, 230, 233, 246, 249,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 454, 457, 470, 473, 486, 489, 502, 505,
-1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 199, 200, 215, 216, 231, 232, 247, 248,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 455, 456, 471, 472, 487, 488, 503, 504,

};

I am using this like so:

#define PIN 6

#define WIDTH 32 // So far. Will be 56 when I finish creating array.
#define HEIGHT 32

/**
 * Setup the lightwall matrix.
 *
 * Each panel is 8x8 pixels. There are 7 columns and 2 rows,
 * but they are arranged in a checkerboard pattern.
 */
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(WIDTH, HEIGHT,PIN,
  NEO_MATRIX_TOP     + NEO_MATRIX_LEFT +
  NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
  NEO_GRBW           + NEO_KHZ800
);

uint16_t remapXY(uint16_t x, uint16_t y) {
  y = y * WIDTH + x;
  return grid[y];
}

void setup() {
  matrix.setRemapFunction(remapXY);
  matrix.begin();
}

So the remapXY() returns the correct key of the array, thus the reason for the crazy array pasted above. And that array is only halfway complete (tediously typing it out!)

But so far it is working. I have a pacman animation (not pictured) scrolling across the screen and being "off screen" for a believable amount of time. That is the whole point of this madness. But am I going about it all wrong? Attached is also a picture of the full visible matrix for visual aid.

lightwall

Starting from the upper left corner is panel 1, then lower left panel 2. Next column top is panel 3, bottom panel 4. And so on.

So will I need more RAM in order for this to be performant? Or is there a better way to code my way out of this problem? I have a lot of animations and ambient effects planned, not all of which will strictly require this offset.

Is there another way to "mask" or delay some pixels off screen temporarily based on where they should be? Only to put them back where they belong afterward?

  • that is an absolutely cool prroject – jsotola Apr 21 '18 at 17:46
  • 2
    you can use a spreadsheet program, like excel, to generate arrays like the one that you have. ... then cut and past from the range of cells to the program editor – jsotola Apr 21 '18 at 17:52
  • Your room looks more high-tech than mine, and that's saying something! – Nick Gammon Apr 22 '18 at 7:56
4

break it down into blocks to determine the upper left corner of any 8x8 grid

const int16_t grid[8][8] = {
       {0,  15,  16,  31,  32,  47,  48,  63},
       {1,  14,  17,  30,  33,  46,  49,  62},
       {2,  13,  18,  29,  34,  45,  50,  61},
       {3,  12,  19,  28,  35,  44,  51,  60},
       {4,  11,  20,  27,  36,  43,  52,  59},
       {5,  10,  21,  26,  37,  42,  53,  58},
       {6,   9,  22,  25,  38,  41,  54,  57},
       {7,   8,  23,  24,  39,  40,  55,  56}
};

const int16_t block[7][4] = {
       { 0,  -1,   4,  -1,   8,  -1,  12},
       {-1,   2,  -1,   6,  -1,  10,  -1},
       { 1,  -1,   5,  -1,   9,  -1,  13},
       {-1,   3,  -1,   7,  -1,  11,  -1}
};



blockNumber = block[x/8][y/8];

// some checking for the -1 value

pixelNumber = blockNumber * 64 + grid[x%8][y%8]   

//
  • This looks really promising - I think I understand. I'm going to try this out and get back to you on how it goes. Thank you! – GhostToast Apr 21 '18 at 19:21
  • thanks again! this is working. i had to fix a couple minor nuances but this was immensely helpful! – GhostToast Apr 21 '18 at 19:40
  • If I add more, I run out of RAM. Each pixel needs 3bytes of RAM. If I make it 10 "boxes" wide, that becomes a grid of 80x32, or 2560 pixels. That's 7680 bytes of RAM, which is just too much for this weak little board. – GhostToast Apr 21 '18 at 20:32
  • i would have a serious look at @edgarbonet's answer and accept that one instead of this one if it allows for larger panels – jsotola Apr 21 '18 at 21:04
  • make the grid and block arrays of type uint8_t instead of int16_t for starters .... you could subdivide the grid array into 4 sections horizontally to save a few more bytes ..... you could add more unused blocks to follow the actual layout more closely ..... top row of block array would be { 0, -1, -1, -1, 4, -1, -1, -1, 8, -1, -1, -1, 12}, – jsotola Apr 21 '18 at 22:26
3

This indeed looks awesome!

I suggest getting rid of the big grid array, and instead generate the mapping programmatically. Given that it is very regular, the mapping can be computed instead of stored:

uint16_t remapXY(uint16_t x, uint16_t y) {

    // Compute panel position and offset of pixel within that panel.
    uint8_t pannel_x = x / 8;
    uint8_t offset_x = x % 8;
    uint8_t pannel_y = y / 8;
    uint8_t offset_y = y % 8;

    // Is this a missing panel?
    if ((pannel_x + pannel_y) & 1)
        return -1;

    // Compute panel number.
    uint8_t pannel = pannel_y / 2 + pannel_x * 2;

    // LED index within the panel.
    uint8_t led = 8 * offset_x;

    if (offset_x & 1)  // numbered bottom to top
        led += 7 - offset_y;
    else               // numbered top to bottom
        led += offset_y;

    return pannel * 64 + led;
}

I hope this will save enough memory to make the whole thing work.

  • This is also a very elegant solution and also works! It doesn't allow me to add even more fake panels as @jsotola hoped, but definitely feels like less overhead (though I wonder if doing the computations repeatedly is savings vs having the constants defined? Suppose it's CPU vs RAM question). The Mega just doesn't have enough RAM to pretend beyond what I've got it doing. – GhostToast Apr 21 '18 at 21:08
  • 1
    It occurs to me I could store the lookup tables in PROGMEM too and save some SRAM that way. Reading this: arduino.cc/en/Tutorial/Memory – GhostToast Apr 21 '18 at 21:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.