Infinity Table Code Breakdown - Arduino

11 Apr 2014

This article showcases one of the more interesting pieces of code written for this Infinity Table.

Here’s the goal:

Random bgColor
Black bgColor

Here’s a diagram showing the layout of our addressable LEDs on the tab

​I established the LEDs that I wanted to start and end at. I also divided the table into quarters, and assigned a counter for each quarter.

You can access all of the following code on my Github.

int northEastCounter, southEastCounter, northWestCounter, southWestCounter;
int northPixel = 150; // pixel at the top of the table
int southPixel = 63;
int eastPixel = 108; // right side halfway point
int westPixel = 18;

So far, this is pretty simple. For each tick, we update all 174 LEDs. If their value falls between the starting address (northPixel and southPixel) and the counter’s endpoint, then flip those LEDs on with an RGB code.

  for (i = 0; i < strip.numPixels(); i++) {    // led update loop, this updates all 174 leds (if applicable)
    if (i == southPixel || i == northPixel) {  // always leave these on, since they're our marker for north/south
      strip.setPixelColor(i, 255, 0, 0);       // red
    }
    else if (i >= southPixel + southWestCounter && i <= southPixel && i >= westPixel) {
      strip.setPixelColor(i, 255, 0, 0);  //red
    }
    else if (i <= southPixel + southEastCounter && i >= southPixel && i <= eastPixel) {
      strip.setPixelColor(i, 255, 0, 0);
    }
    else {
      strip.setPixelColor(i, 0, 0, 0); //black
    }
  }

  strip.show();
  delay(wait);

  northWestCounter++;
  northEastCounter--;
  southWestCounter--;
  southEastCounter++;
}

The problem was jumping the awkward gap between LED 174 (the end of our strip) and LED 0 (the beginning). In order for our light to extend smoothly outwards, the loop logic would have to jump nicely from 174 to 0 and extend the counter.

Here’s how I solved that. I wrapped this all in another loop, and I created a variable named remainder there. remainder is the sum of northPixel + northWestCounter, which we then subtract the number of pixels on the strip. Look at the diagram above if you need help visualizing this.

If our remainder is greater than zero, it means that the northWestCounter has extended past 174, and we can use this knowledge to create a logical check - if remainder has a value greater than or equal to zero, it meant the counter had “jumped” back to 0. We could use this remainder to guide our updates along the rest of the strip, up until the desired stopping point (westPixel).

for (tick = 0; tick < 43; tick++) { // one full lap

    remainder = (northPixel + northWestCounter) - strip.numPixels();

    // If our remainder is positive, that means we've crossed over from LED 174 to LED 0.
    // So, we add our counter and starting position (northPixel), and subtract the number of LEDs.
    // The remainder is what needs to be added to 0 to keep the led update pushing from 0-westPixel

    for (i = 0; i < strip.numPixels(); i++) { // led update loop
      if (i == southPixel || i == northPixel) {  // always leave these on, since they're our marker for north/south
        strip.setPixelColor(i, 255, 0, 0);
      }
      else if (i <= northPixel + northWestCounter && i > northPixel ) {  // start sending a red wave out from the northPixel, +1 every tick
        strip.setPixelColor(i, 255, 0, 0);
      }
      else if (remainder >= 0 && i >= 0 && i <= remainder && i <= westPixel) {  // if the remainder is greater than 0 (i.e. we have overrun the number of LEDs in the strip)
        strip.setPixelColor(i, 255, 0, 0); // fill up to the remainder (which will be between 0-18)
      }    // this is just to handle going from LED 174 -> 0 -> 1 -> etc

      else if (i >= northPixel + northEastCounter && i <= northPixel && i >= eastPixel) {
        strip.setPixelColor(i, 255, 0, 0);
      }
    // I redacted the south side setup for brevity
    }

    strip.show();
    delay(wait);

    northWestCounter++;
    northEastCounter--;
    southWestCounter--;
    southEastCounter++;
  }
}

Handling the reversing of the effect was done by switching the counter directions: i.e northWestCounter++ became northWestCounter--.

We also made two variations of the code, one with a black background, and one with a random background color.

A little note on the background color - the “tracer” colors are generated every time a full loop is completed. These tracer color values are stored before the start of the next loop, and the value is passed to the bgColor variable. This gives our colors a smooth continuity, as it looks like a random color bursts over our previously painted work.

I’ve pasted the full code below. This will work on your Arduino Uno with the Adafruit Neopixel Library and exactly 174 LEDs on your strip, arranged in exactly the manner we have. That’s my way of saying you’re going to need to adjust this code. Use it as inspiration, not a plug and play gist.

#include <adafruit_neopixel.h>
#define PINdroite 1
#define STRIPSIZE 174
Adafruit_NeoPixel strip = Adafruit_NeoPixel(STRIPSIZE, PINdroite, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();
  strip.setBrightness(55);  // Lower brightness and save eyeballs
  strip.show(); // Initialize all pixels to 'off'
}

void loop() {
  connectingPixels(20);
}

int returnNumber(int number) {
  return number;
}

void connectingPixels( uint8_t wait) {
  int i, looper, tick, northEastCounter, southEastCounter, northWestCounter, southWestCounter, northPixel, southPixel, eastPixel, westPixel, remainder;
  int traceR, traceG, traceB, bgR, bgG, bgB;

  northPixel = 150; // pixel at the top of the table
  southPixel = 63;  //pixel at the bottom of the table
  eastPixel = 108;  // right side halfway point
  westPixel = 18;   // left side

  traceR = rand() % 255; // re-roll the random dice every time a loop is completed
  traceG = rand() % 255; // we need a value between 0 and 255 (to pass in as RGB values)
  traceB = rand() % 255;

  bgR = 0; //background colors
  bgG = 0; //initialize these values to whatever you want
  bgB = 0; //they are only used on the first lap. think of it as "booting up"

  // this loop controls the number of times the full sequence will run.
  // a full sequence begins with two pixels enabled in the middle of the north and south ends // the table
  // tracers are then deployed towards east and west ends of the table.
  // a sequence ends when the tracers return to their starting position and a new RGB value is generated
  // set cycle to 50 to see this effect 50 times, for example

  for (int cycle = 0; cycle < 30; cycle++) {
    for (tick = 0; tick < 43; tick++) {   // one loop

      remainder = (northPixel + northWestCounter) - strip.numPixels();

      // If our remainder is positive, that means we've crossed over from LED 174 to LED 0.
      // So, we add our counter and starting position (northPixel), and subtract the number of LEDs.
      // The remainder is what needs to be added to 0 to keep the led update pushing from 0-18

      for (i = 0; i < strip.numPixels(); i++) {   // led update loop
        if (i == southPixel || i == northPixel) {  // always leave these on, since they're our marker for north/south
          strip.setPixelColor(i, traceR, traceG, traceB);
        }
        else if (i <= northPixel + northWestCounter && i > northPixel ) {  // start sending a red wave out from the northPixel, +1 every tick
          strip.setPixelColor(i, traceR, traceG, traceB);
        }
        else if (remainder >= 0 && i >= 0 && i <= remainder && i <= westPixel) {  // if the remainder is greater than 0 (i.e. we have overrun the number of LEDs in the strip)
          strip.setPixelColor(i, traceR, traceG, traceB);                         // fill up to the remainder (which will be between 0-18)
        }                                                                         // this is just to handle going from LED 174 -> 0 -> 1 -> etc

        else if (i >= northPixel + northEastCounter && i <= northPixel && i >= eastPixel) {
          strip.setPixelColor(i, traceR, traceG, traceB);
        }
        else if (i >= southPixel + southWestCounter && i <= southPixel && i >= westPixel) {
          strip.setPixelColor(i, traceR, traceG, traceB);
        }
        else if (i <= southPixel + southEastCounter && i >= southPixel && i <= eastPixel) {
          strip.setPixelColor(i, traceR, traceG, traceB);
        }
      }

      strip.show();
      delay(wait);

      northWestCounter++;
      northEastCounter--;
      southWestCounter--;
      southEastCounter++;
    }

    for (tick = 0; tick < 43; tick++) { // this loop reverses the tracer, filling the background color behind it

      remainder = (northPixel + northWestCounter) - strip.numPixels();

      // If our remainder is positive, that means we've crossed over from LED 174 to LED 0.
      //  So, we add our counter and starting position (northPixel), and subtract the number of LEDs.
      // The remainder is what needs to be added to 0 to keep the led update pushing from 0-18

      for (i = 0; i < strip.numPixels(); i++) {  // led update loop
        if (i == southPixel || i == northPixel) { //always leave these on, since they're our marker for north/south
          strip.setPixelColor(i, traceR, traceG, traceB);
        }
        else if (i <= northPixel + northWestCounter && i > northPixel ) {         // start sending a red wave out from the northPixel, +1 every tick
          strip.setPixelColor(i, traceR, traceG, traceB);
        }
        else if (remainder >= 0 && i >= 0 && i <= remainder && i <= westPixel) {  // if the remainder is greater than 0 (i.e. we have overrun the number of LEDs in the strip)
          strip.setPixelColor(i, traceR, traceG, traceB);                         // fill up to the remainder (which will be between 0-18)
        }                                                                         // this is just to handle going from LED 174 -> 0 -> 1 -> etc

        else if (i >= northPixel + northEastCounter && i <= northPixel && i >= eastPixel) {
          strip.setPixelColor(i, traceR, traceG, traceB);
        }
        else if (i >= southPixel + southWestCounter && i <= southPixel && i >= westPixel) {
          strip.setPixelColor(i, traceR, traceG, traceB);
        }
        else if (i <= southPixel + southEastCounter && i >= southPixel && i <= eastPixel) {
          strip.setPixelColor(i, traceR, traceG, traceB);
        }
        else {
          strip.setPixelColor(i, bgR, bgG, bgB);
        }
      }

      strip.show();
      delay(wait);

      northWestCounter--;
      northEastCounter++;
      southWestCounter++;
      southEastCounter--;
    }

    northEastCounter = 0;
    northWestCounter = 0;
    southEastCounter = 0;
    southWestCounter = 0;

    bgR = returnNumber(traceR);
    bgG = returnNumber(traceG);
    bgB = returnNumber(traceB);

    traceR = rand() % 255;  // re-roll the random dice every time a loop is completed
    traceG = rand() % 255;
    traceB = rand() % 255;
  }
}

“That’s it!” :)

Here are some more pictures of the finished product.