Friday, March 25, 2016

Daisy Chained NeoPixel Ring Flower Garden

I've had this recessed frame kicking around for a long time and finally came across a good use for it. Inspired by the story of a student's flower garden Logo program in Teaching With Logo by Molly and Daniel Watt, I picked up some NeoPixel rings at my local go to hobby store, Tinkersphere, and set about learning how to daisy chain them into one circuit.
Lynn's flower garden

I set about by downloading the Adafruit NeoPixel library and connecting up one ring to an Arduino Duemilanove (my first Arduino!) and lighting it up with the "simple" example included in the library. Some of the NeoPixel rings I got have through holes to connect up the wires and others have solder pads. I prepared the rings by soldering on like-colored wires for the 5V, GND, Data In, and Data Out connections.

NeoPixel wiring connections
Once one ring is lit up, it's a simple matter to add more. The Data Out wire on the first connects directly to the Data In of the second. To add the second to the code, add the total NeoPixels up and put them in the NUMPIXELS definition:
#define NUMPIXELS     80 
The for loop that lights them up inside loop() runs for the numbers of pixels you define, so all the pixels along the chain should light up:

  for(int i=0;i<NUMPIXELS;i++){
    pixels.setPixelColor(i, pixels.Color(0,150,0));
    pixels.show();
    delay(delayval);
  }
I tested the chain lighting up all the same color using a breadboard to make sure the circuit worked.
I connected each ring's 5V wire to the + rail and it's GND wire to the - rail. The Arduino 5V pin goes to the + rail and the GND pin goes to the - rail. And the data pin (I used 13) connects to the Data In wire on the first ring. I added a 330 ohm resistor on this wire as Adafruit recommends. The very last ring in the series gets its Data In from the second-to-last ring's Data Out, but its Data Out does not need to connect to anything.

Then I tested the circuit on different power sources to make sure a battery power source would be enough in the final setup. There are lots of forum posts about people having problems getting all of their NeoPixels to light up properly once they move a circuit from testing to installation. Surprisingly, a 6V (4 AA) battery pack powering the Arduino, and that in turn supplying 5V to the parallel power circuit of 7 rings, 88 NeoPixels, was enough to make them all nice and bright. You'll see I set the maximum brightness at 30, which is plenty nice.
Transferring the circuit to the frame backing just required a lot of soldering, hot glueing the rings down, and adding wire segments. The GND and 5V rails go around the outside. Then the battery pack is glued on the side. It includes a handy switch to power off.

Here's a final video of the fade effect:
And the final source code:
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

#define PIN        13
int RING1 = 8, RING2 = 24, RING3 = 8, RING4 = 12, RING5 = 16, RING6 = 8, RING7 = 12;
int rings[] = {RING1, RING2, RING3, RING4, RING5, RING6, RING7};
int NUMPIXELS = RING1 + RING2 + RING3 + RING4 + RING5 + RING6 + RING7;
int limits[sizeof(rings)/sizeof(int)];
int STEP = 2;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int delayval = 5;
int brightVal = STEP, changeBright = STEP;
int r1, g1, b1,r2, g2, b2,r3, g3, b3,r4, g4, b4,r5, g5, b5,r6, g6, b6,r7, g7, b7;
int colors[] = {r1, g1, b1,r2, g2, b2,r3, g3, b3,r4, g4, b4,r5, g5, b5,r6, g6, b6,r7, g7, b7};

void setup() {
  pixels.begin();
  pixels.setBrightness(brightVal);
  pixels.show();
  for (int i = 0; i < 7; i++) {              //create delimiters to for loop each ring separately
    if (i > 0) {
      limits[i] = limits[i - 1] + rings[i];
    } else {
      limits[i] = rings[i];
    }
    for(int i = 0; i<(sizeof(colors)/sizeof(int)); i++) { //set random rgb colors for each ring
      colors[i] = random(255);
    }
  }
}

void loop() {
  if (brightVal > 30 || brightVal < STEP) {  //brightness increase and decrease
    changeBright = -changeBright;
  }
  if (brightVal < STEP) {
    for(int i = 0; i<(sizeof(colors)/sizeof(int)); i++) { //change all colors at min brightness
      colors[i] = random(255);
    }
  }
  brightVal += changeBright;                      //change brightness
  pixels.setBrightness(brightVal);                // set brightness
  for (int i = 0; i < limits[0]; i++) {            //each for loop is a ring
    pixels.setPixelColor(i, pixels.Color(colors[0], colors[1], colors[2]));
    pixels.show();
  }
  for (int i = limits[0]; i < limits[1]; i++) {
    pixels.setPixelColor(i, pixels.Color(colors[3], colors[4], colors[5]));
    pixels.show();
  }
  for (int i = limits[1]; i < limits[2]; i++) {
    pixels.setPixelColor(i, pixels.Color(colors[6], colors[7], colors[8]));
    pixels.show();
  }
  for (int i = limits[2]; i < limits[3]; i++) {
    pixels.setPixelColor(i, pixels.Color(colors[9], colors[10], colors[11]));
    pixels.show();
  }
  for (int i = limits[3]; i < limits[4]; i++) {
    pixels.setPixelColor(i, pixels.Color(colors[12], colors[13], colors[14]));
    pixels.show();
  }
  for (int i = limits[4]; i < limits[5]; i++) {
    pixels.setPixelColor(i, pixels.Color(colors[15], colors[16], colors[17]));
    pixels.show();
  }
  for (int i = limits[5]; i < limits[6]; i++) {
    pixels.setPixelColor(i, pixels.Color(colors[18], colors[19], colors[20]));
    pixels.show();
  }
}