Programming an Adafruit LED matrix

The plan

 

For Halloween, I am making costumes that are like discos and for the costume I wanted to create a light setup that would flash colored lights off of the costume. I wanted to support various modes like a beating heart:

a strobe:

and  various geometric animations:

 

I decided to build a system using an Arduino and an Adafruit Neopixel matrix. I wrote some software and built a simple button-based system /circuit that lets me control the lights and puts on a fun show. The following video shows me playing with the prototypes:

I’ll run through my overall process of hacking the system together, hope it’s helpful if you’re building your own.

Getting to where I can code

I’m not a hardware guy and can’t solder to save my life, I started with software. I did the bare minimum and wired jumper cables for prototyping. Because I’m lazy, clumsy, and wanted to see if the hardware even worked, I took an Arduino Uno and powered my lights directly from the 5V power pins and connected a 1A power supply. I started up the Adafruit basic project from GitHub, (License) and forked it for building out my sample grid app.

I upgraded Arduino Studio and compiled the software. After connecting the data pin and power pins and running example apps, I realized that I would need auxiliary power: the LEDs would dim when I powered them up. This indicated that I didn’t have enough power going to the Neopixel – most likely because I had been lazy and directly connected my microcontroller to the power lines on the Neopixel.

I took some scissors and cut apart a small 5v power supply I was no longer using. With the wires separated, I soldered on a JST connector. On the Neopixel matrix, I soldered the paired JST to power. Now powered independently, I assumed that had a working testbed for coding. This was confirmed after powering up the power supply and re-running my code from earlier.

Programming the software

Now that I could reliably control the LEDs, I was ready to start coding. I started with a simple method that added a mask function to the existing color wipe function for the Adafruit library:

void maskedColorWipe(uint8_t wait, uint32_t color, uint8_t brightness, uint8_t shift, byte mask[][ROW_SIZE]) {
  uint16_t i, j;

  for(i=0; i<strip.numPixels(); i++) {
    if (drawGivenMask(i / ROW_SIZE, i % ROW_SIZE, shift, mask)){
      strip.setBrightness(brightness);
      strip.setPixelColor(i, color);
    }else{
      strip.setPixelColor(i, 0);
    }
  }
  
  strip.show();
  delay(wait);
}

This function was the start of being able to properly render data to the matrix. The plan is to allow on bits of the mask would render and the off (0) bits would be rendered as blank. The following examples show matrices specified in the program for various designs:

byte doubleheart[][ROW_SIZE] = {
  {0,0,0,0,0,0,0,0},
  {0,1,1,0,1,1,0,0},
  {1,1,1,1,1,1,1,0},
  {1,1,1,1,1,1,1,0},
  {0,1,1,1,1,1,0,0},
  {0,0,1,1,1,0,0,0},
  {0,0,0,1,0,0,0,0},
  {0,0,0,0,0,0,0,0}
};


byte doubleArrow[][ROW_SIZE] = {
  {0,1,0,0,0,0,0,0},
  {0,0,1,0,0,0,0,0},
  {0,0,0,1,0,0,0,0},
  {0,0,0,0,1,0,0,0},
  {0,0,0,0,1,0,0,0},
  {0,0,0,1,0,0,0,0},
  {0,0,1,0,0,0,0,0},
  {0,1,0,0,0,0,0,0}
};

byte flatline[][ROW_SIZE] = {
  {0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0},
  {0,0,0,1,0,0,0,0},
  {0,0,1,0,1,0,0,0},
  {1,1,0,0,0,1,1,},
  {0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0}
};

If it’s not apparent from the function names, there is a heart, an arrow, and a little spike pattern in each of those grids. I chose the design of bit masks based on how it would be easy to visually produce the masks in this early prototype stage.

The function needed a helper, drawGivenMask, that would determine whether to render given a bit of the matrix:

boolean drawGivenMask(int row, int col, int shift, byte mask[][ROW_SIZE]){
  #ifdef FLIPMODE
  if (row & 1) {
    col = ROW_SIZE - (col + 1);
  }  
  #endif
  col = (col + shift) % ROW_SIZE;
  
  if (mask[row][col] & 1){
    return true;
  }
  return false;
}

Between these two functions, I could take a mask and color and then draw pictures to the matrix. Next step was to be able to rotate the matrix around because still light images are way too light-bright.

uint8_t shiftty = 1;
int scrollDir = 1;
//  speed - Speed to draw (lower is faster).
//  scrollLimit - # times to show before reversing pattern scrolling.
//  pattern - The displayed pattern, defined by 0's (off) and 1's (on).
void patternRotate(int speed, int scrollLimit, byte pattern[][ROW_SIZE]) {
  uint32_t color = strip.Color(255,0,137);
  if (true) {
    maskedColorWipe(speed, Wheel(shiftty*5), 50, shiftty, pattern);  
  } else if (true) {
    maskedRainbowCycle(speed, 50, shiftty, pattern);
  } else {
  }
  shiftty += scrollDir;
  
  if(shiftty > scrollLimit){
    scrollDir = -1;
  }
  if (shiftty <= 0){
    scrollDir = 1;
  }
  return;
}

Note that I use the HSV hack from one of the other example apps to render a nice rainbow while the colors rotate. At this point I had spinning designs like the following gratuitous gif:

Next, I added code for switching patterns when a button is pressed:

void checkButton () {
  if (val == HIGH) {
    if (bounceCount < bounceLimit){
      bounceCount++;
    }else{
      pattern++;
      bounceCount = 0;
    }
    if (pattern > patternLimit){
      pattern = 0;
    }
  }
}

And a switch for changing the pattern:

  switch (pattern) {
    case 0:
      patternRotate(50, 256, flatline);  
      break;
    case 1:
      patternRotate(50, 32, doubleheart);
      break;
    case 2:
      patternRotate(50, 256, doubleArrow);  
      break;
    default:
      pattern = 0;
      break;
}

Next, I added the switch and started completing the hardware.

Building out the hardware system

With the software in place and working, it was time to get the actual decorations mobile!  I wasn’t sure it could cover the power needs but I found a 1A 5v USB battery used for charging phones that potentially could work for my project. Emphasis on USB – this is extremely convenient for powering Arduinos or other peripherals that have USB power-in. The plan here was to split off power from the battery to both charge the Arduino over micro usb as well as power the lights over the same power line. The circuit design looks something like this:

 

Of note, the Arduino digital line is connected to the Neopixel on pin 13, a button circuit is set to the analog pin, and the Neopixel power line is buffered by a small capacitor. It’s recommended that you also use a resistor on the data line to reduce noise.

After putting together the circuit, I was ready to go. Again, the key to keeping it simple was the battery. I used the USB phone charger and then split apart a micro usb cable. The cable was wired into the circuit on one part, powering the Neopixel, and then split off to resume power to the micro usb end that was used to power the Arduino micro. As opposed to my “prove it works” prototype, the power here was no longer going through the Arduino to the Neopixels.

Advanced patterns / animations

Now that I knew my costume piece was complete, it was time to add more complex patterns. The first draws a plaid-like pattern:

void grid(int delayMs, uint32_t color) {
  uint16_t i, j, displace;
  displace = 0;

  for (displace = 0; displace < ROW_SIZE; displace++){
    for (int i=0; i < ROW_SIZE; i++){
      for (int j=0; j < ROW_SIZE; j++){
        if (i == displace || j == displace || (i == (ROW_SIZE-1) - displace) || (j == (ROW_SIZE-1) - displace)){
          strip.setPixelColor(j + (i * ROW_SIZE), color);
        }else{
          strip.setPixelColor(j + (i * ROW_SIZE), 0);
        }
      }
    }
    strip.show();
    delay(delayMs);
  }
}

The next draws a shrinking box:

void box(int delayMs, uint32_t color) {
  uint16_t i, j, displace;
  displace = 0;

  for (displace = 0; displace < ROW_SIZE; displace++){
    for (int i=0; i < ROW_SIZE; i++){
      for (int j=0; j < ROW_SIZE; j++){
        if ( (i >= displace && i <= (ROW_SIZE-1)-displace) && (j >= displace && (j <= (ROW_SIZE-1) - displace)) ){
          strip.setPixelColor(j + (i * ROW_SIZE), color);
        }else{
          strip.setPixelColor(j + (i * ROW_SIZE), 0);
        }
      }
    }
    strip.show();
    delay(delayMs);
  }

  for (displace = ROW_SIZE-1; displace > 0; displace--){
    for (int i=0; i < ROW_SIZE; i++){
      for (int j=0; j < ROW_SIZE; j++){
        if ( (i >= displace && i <= (ROW_SIZE-1)-displace) && (j >= displace && (j <= (ROW_SIZE-1) - displace)) ){
          strip.setPixelColor(j + (i * ROW_SIZE), color);
        }else{
          strip.setPixelColor(j + (i * ROW_SIZE), 0);
        }
      }
    }
    strip.show();
    delay(delayMs);
  }
}

Another is the beating heart:

void heartPulse() {
  // New test patterns.
  int steps = 20;
  int speed = 40;
  uint32_t color = strip.Color(255,0,137);
  
  for (int i=0; i < steps; i++){  
    maskedColorWipe(speed, color, (100 / steps) * i, 0, doubleheart);
  }
  maskedColorWipe(250, color, 100, 0, doubleheart);
  checkButton();
  
  for (int i=steps; i > 0; i--){  
    maskedColorWipe(speed, color, (100 / steps) * i, 0, doubleheart);
    checkButton();
  }
  
  maskedColorWipe(500, color, 100, 0, maskEmpty);
  checkButton();
  
  // Prevents bouncing.
  if (pattern < patternLimit) pattern = 0;
  return;
}

I plan on adding another animation or two before finishing but am not sure what to add. Feel free to suggest animations.

Tweaks

The lights are super bright, so I added various diffusion layers to the LED matrices. For one, I used bubble packing that was used as packaging for the LEDs. For the other, I used diffusion paper ordered online. For both, the diffusion helps tremendously to prevent blindness (exaggeration, but my eyes have gotten hurt) and also blurs out the rendered patterns to make nice color bleed as you can see in the end of my demo video.

For some of the patterns, it was difficult to detect the button press at the correct time, so switching them to rotate a fixed period of time made sense. I would love to refactor the code to use an interrupt-driven button but didn’t have time to really get into doing it in the way it’s done on my Netduino blinky project.

Closing thoughts

First and foremost: the Neopixel is super bright. I could have gone with a less powerful setup than I had chosen and powered more lights, reduced power requirements, and lowered heat coming off of the thing. Despite the power and brightness issues, programming the Neopixel lights is a dream – I never had issues with rendering colors to pixels and using a pre-fab matrix saved loads of time. This was very, very much in part to the Adafruit Neopixel starter library and examples (License). The portable battery system worked surprisingly well too, I was from working in a test environment to mobile in no time.

In conclusion, Neopixels coupled with an Arduino are a great way to make blinky things, check them out if you’re into bright flashy objects. Hopefully my matrix code can help you jump-start if you are looking to test a quick and dirty matrix controller.