Fun with Arduino 23 Neopixel Addressable LEDs, WS2812 IC, struct{…}

WS2812‘Neopixel’ is the brand name of color LEDs (RGB or RGBW) that have a chip built in that makes them addressable, while they are connected to each other via just one data line. They can be found on the internet via several searches: Neopixel, addressable LED, WS2812 (the name of the built in chip). Prices are very reasonable at about €0,10 – €0,20 per LED. Click the images to enlarge.

Neo_StripThey are available in different form factors.

LED strip, which can be cut to the desired length.

Neo_84, 8, 16 LEDs on a PCB strip, already electrically interconnected.

Neo_PanelArrays of diferent sizes, already electrically interconnected.

Neo_CircleCircles of different sizes, already electrically interconnected.

Neo_BreakIndividual LEDs that you break off and that you have to wire yourself (3 wires: GND, 5V, Data) with the lengths that you need in between.

These breakout LEDs are ideal to light village houses on a model railway layout. Say you have a village with 10 or 20 houses … place one or two LEDs in every house, connect them with 3 wires and the big plus now is that all houses can be individually lit, with independent timing and with different colors and brightness. One house can have cool white light, another more warm, or yellowish, what ever you like … the village really comes to life with all these different timing, colors and brightness’s, just like real. And … all this is controlled with just one (yes, one!) Arduino output, connected to the Data line.

Read on below the video …

 

For the software we can use the step sequencer we made in the previous video, where we add the Adafruit Neopixel library and then add an RGB color per LED. For a police car flash light the interval time will be around  50 ms, but when we light houses we can make the interval time multiple seconds. With say 96 steps, we then have a multiple minutes day / night cycle.

LibraryThe Adafruit Neopixel library is not standard available, it needs to be downloaded and installed. The easiest way to do this is via the menu: Sketch > Include Library > Manage Libraries. Once there, do a search for neopixel and from the list of results select Adafruit Neopixel and click ‘Install’. It will now be downloaded and installed fully automatic.

After the library is installed, we can include the library in our code and then we create an instance of Adafruit_NeoPixel, which I chose to give the name neopixel:

#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(NUMLEDS, LEDS_PIN, NEO_GRB + NEO_KHZ800);

Now, to turn on LED nr 3 in the neopixel string with color white on full brightness (R=255, G=255, B=255) and LED nr 5 with half bright red (R=128), we use these instructions:

neopixel.setPixelColor(3, 255, 255 , 255); // LED 3 full white
neopixel.setPixelColor(5, 128, 0   , 0  ); // LED 5 half red
neopixel.show();

We can first prepare all the colors and brightnesses and then give one final neopixel.show(); instruction to write all the prepared RGB values to the LEDs. Note that he first LED in a Neopixel string has address 0.

We can specify an RGB color for every LED. To do that with our sequencer, we create a struct (structure) that we give the name ‘RGB_steps’ (it’s just a name … you can give it any name you like) and that contains the R, G, B values as well as the step sequencer 1’s and 0’s. This struct can now be used as a new (array) variable type. We can define an array with it, which we name seq (seq is short for sequencer):

struct RGB_steps {
  byte R, G, B; 
  steps[NUMSTEPS];
};
RGB_steps seq[NUMLEDS]; // seq is short for sequencer

We now have our array available, where for every LED we can store the R, G, B color values plus the steps for the sequencer. After we have loaded the array with the values we want, in loop() we can now light the LEDs in the Neopixel string like this, where p is the step pointer:

if (blink_enabled) {
  if (millis() > time_for_action) {
    time_for_action = millis() + (unsigned long)INTERVAL;
    for (led=0; led<NUMLEDS; led++) {
      if (seq[led].steps[p] == 1)
        neopixel.setPixelColor(led, seq[led].R, seq[led].G, seq[led].B);
      else
        neopixel.setPixelColor(led, 0, 0, 0);        
    }
    neopixel.show();
    p = (p+1) % NUMSTEPS;
  }
}

The complete Neopixel step sequencer code :

#include <Adafruit_NeoPixel.h>

#define INTERVAL     40 // [ms] time between sequence steps
#define NUMLEDS       3 // number of LEDs in the Neopixel string
#define NUMSTEPS     48 // number of steps in the sequence
#define LEDS_PIN      6 // output pin to neopixel data line
#define TRIGGER_PIN   8 // make low to start blink for BLINK_TIME seconds
#define BLINK_TIME    6 // [s] blink time after trigger, then stop blinking 

// This is the structure that is going to store RGB values and steps[]
struct RGB_steps {
  byte R, G, B;
  byte steps[NUMSTEPS];
};

// Create as many lines here as you have LEDs (NUMLEDS)
// and specify RGB color and on/off steps per LED (NUMSTEPS)
RGB_steps seq[NUMLEDS] = {
//        0                   1                   2                   3                   4   
// R G B  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
   0,0,9, 1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,
   0,0,9, 0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,
   9,0,0, 0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0
};

byte led, p; // led nr, current step pointer
byte blink_enabled;
byte state = 1, transition;
unsigned long time_for_action;
unsigned long time_to_switch_off;

Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(NUMLEDS, LEDS_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  pinMode(TRIGGER_PIN, INPUT_PULLUP);
  neopixel.begin();
  for(led=0; led<NUMLEDS; led++)
    neopixel.setPixelColor(led, 0, 0, 0);
  neopixel.show();
  Serial.begin(9600);
  Serial.println("Ready, waiting for trigger");
}

void loop() {
  switch(state) {
    case 1: // idle
      if(digitalRead(TRIGGER_PIN) == LOW) transition = 12;
    break;

    case 2: // blinking for ON_TIME seconds
      if(millis() > time_to_switch_off)   transition = 21;
    break;
  }
  
  switch(transition) {
    case 12:
      blink_enabled = 1;
      time_to_switch_off = millis() + (unsigned long)BLINK_TIME * 1000;
      Serial.print("Triggered,  blinking for ");
      Serial.print(BLINK_TIME);
      Serial.println(" seconds");
      transition = 0;
      state = 2;
    break;

    case 21:
      blink_enabled = 0;
      time_for_action = 0;
      for(led=0; led<NUMLEDS; led++)
        neopixel.setPixelColor(led, 0, 0, 0); 
      neopixel.show();
      p = 0;
      Serial.println("Time is up, blinking stopped");
      Serial.println("Waiting for trigger");
      transition = 0;
      state = 1;
    break;
  }
  
  if (blink_enabled) {
    if (millis() > time_for_action) {
      time_for_action = millis() + (unsigned long)INTERVAL;
      for (led=0; led<NUMLEDS; led++) {
        if (seq[led].steps[p] == 1)
          neopixel.setPixelColor(led, seq[led].R, seq[led].G, seq[led].B);
        else
          neopixel.setPixelColor(led, 0, 0, 0);        
      }
      neopixel.show();
      p = (p+1) % NUMSTEPS;
    }
  }
}

In the next video we’ll have a look at another Neopixel step sequencer … one where we make a list with actions like: LEDnr, R, G, B, time. That way we can specify which LED switches to which RGB color and then we have an interval time until the next action. We’ll have full control over our lighting, with very little code!

— 0 —

 

One thought on “Fun with Arduino 23 Neopixel Addressable LEDs, WS2812 IC, struct{…}

Leave a comment