SK2812 RGBW LED: Test Fixture

[Edit: The SK2812 in the title and elsewhere should be SK6812. If I change the title, then all the other links break. So it goes.]

An envelope of RGBW LEDs, allegedly with SK6812 controllers, arrived from halfway around the planet:

SK2812RGBW LEDs - as received
SK2812RGBW LEDs – as received

The yellow phosphor sauce poured atop the blue LED on the left that makes it glow white leaves the upper loop of two wire bonds sticking out, but I can’t fault ’em for that. The overall build quality looks better than the ill-fated WS2812 LEDs, although it’s hard to tell by looking.

I conjured a test stand from the vasty digital deep by tweaking the WS2812 mount:

SK6812 LED Array Test Fixture - Slic3r preview
SK6812 LED Array Test Fixture – Slic3r preview

Wiring up a 5×5 panel went as before:

SK2812RGBW test fixture - rear
SK2812RGBW test fixture – rear

The array test code adds another pixel channel and runs another raised sine wave with another random period, accomplished without much hackage.

With the warm-white LED at full throttle (MaxPWM = 255), the panel tends toward the pallid end of HSV space:

SK2812RGBW test fixture - front - W PWM255
SK2812RGBW test fixture – front – W PWM255

Dialing the white MaxPWM back to 32 crisps things a bit:

SK2812RGBW test fixture - front - W PWM32
SK2812RGBW test fixture – front – W PWM32

Of course, the RGBW data stream isn’t compatible with the RGB data stream, so vacuum tubes with SK6812 chips require a slightly different driver and I can’t mix the two chips on a single tube.

The Arduino source code as a GitHub Gist:

// SK6812 RGBW LED array exerciser
// Ed Nisley - KE4ANU - February 2017
#include <Adafruit_NeoPixel.h>
// Pin assignments
const byte PIN_NEO = A3; // DO - data out to first Neopixel
const byte PIN_HEARTBEAT = 13; // DO - Arduino LED
// Constants
const unsigned long UpdateMS = UPDATEINTERVAL - 1ul; // update LEDs only this many ms apart minus loop() overhead
// number of steps per cycle, before applying prime factors
#define RESOLUTION 100
// phase difference between LEDs for slowest color
#define BASEPHASE (PI/16.0)
// LEDs in each row
#define NUMCOLS 5
// number of rows
#define NUMROWS 5
#define PINDEX(row,col) (row*NUMCOLS + col)
// Globals
// instantiate the Neopixel buffer array
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN_NEO, NEO_GRBW + NEO_KHZ800);
uint32_t FullWhite = strip.Color(255,255,255,255);
uint32_t FullOff = strip.Color(0,0,0,0);
struct pixcolor_t {
byte Prime;
unsigned int NumSteps;
unsigned int Step;
float StepSize;
float TubePhase;
byte MaxPWM;
// colors in each LED
enum pixcolors {RED, GREEN, BLUE, WHITE, PIXELSIZE};
struct pixcolor_t Pixels[PIXELSIZE]; // all the data for each pixel color intensity
unsigned long MillisNow;
unsigned long MillisThen;
//-- Figure PWM based on current state
byte StepColor(byte Color, float Phi) {
byte Value;
Value = (Pixels[Color].MaxPWM / 2.0) * (1.0 + sin(Pixels[Color].Step * Pixels[Color].StepSize + Phi));
// Value = (Value) ? Value : Pixels[Color].MaxPWM; // flash at dimmest points
// printf("C: %d Phi: %d Value: %d\r\n",Color,(int)(Phi*180.0/PI),Value);
return Value;
//-- Helper routine for printf()
int s_putc(char c, FILE *t) {
// Set the mood
void setup() {
digitalWrite(PIN_HEARTBEAT,LOW); // show we arrived
fdevopen(&s_putc,0); // set up serial output for printf()
printf("WS2812 / SK6812 array exerciser\r\nEd Nisley - KE4ZNU - February 2017\r\n");
/// set up Neopixels
// lamp test: run a brilliant white dot along the length of the strip
printf("Lamp test: walking white\r\n");
for (int i=1; i<NUMPIXELS; i++) {
strip.setPixelColor(NUMPIXELS - 1,FullOff);;
// fill the array, row by row
printf(" ... fill\r\n");
for (int i=NUMROWS-1; i>=0; i--) { // for each row
for (int j=NUMCOLS-1; j>=0 ; j--) {
// clear to black, column by column
printf(" ... clear\r\n");
for (int j=NUMCOLS-1; j>=0; j--) { // for each column
for (int i=NUMROWS-1; i>=0; i--) {
// set up the color generators
MillisNow = MillisThen = millis();
printf("First random number: %ld\r\n",random(10));
Pixels[RED].Prime = 3;
Pixels[GREEN].Prime = 5;
Pixels[BLUE].Prime = 7;
Pixels[WHITE].Prime = 11;
printf("Primes: (%d,%d,%d,%d)\r\n",
unsigned int PixelSteps = (unsigned int) ((BASEPHASE / TWO_PI) *
RESOLUTION * (unsigned int) max(max(max(Pixels[RED].Prime,Pixels[GREEN].Prime),Pixels[BLUE].Prime),Pixels[WHITE].Prime));
printf("Pixel phase offset: %d deg = %d steps\r\n",(int)(BASEPHASE*(360.0/TWO_PI)),PixelSteps);
Pixels[RED].MaxPWM = 255;
Pixels[GREEN].MaxPWM = 255;
Pixels[BLUE].MaxPWM = 255;
Pixels[WHITE].MaxPWM = 32;
for (byte c=0; c < PIXELSIZE; c++) {
Pixels[c].NumSteps = RESOLUTION * (unsigned int) Pixels[c].Prime;
Pixels[c].Step = (3*Pixels[c].NumSteps)/4;
Pixels[c].StepSize = TWO_PI / Pixels[c].NumSteps; // in radians per step
Pixels[c].TubePhase = PixelSteps * Pixels[c].StepSize; // radians per tube
printf("c: %d Steps: %5d Init: %5d",c,Pixels[c].NumSteps,Pixels[c].Step);
printf(" PWM: %3d Phi %3d deg\r\n",Pixels[c].MaxPWM,(int)(Pixels[c].TubePhase*(360.0/TWO_PI)));
// Run the mood
void loop() {
MillisNow = millis();
if ((MillisNow - MillisThen) > UpdateMS) {
unsigned int AllSteps = 0;
for (byte c=0; c < PIXELSIZE; c++) { // step to next increment in each color
if (++Pixels[c].Step >= Pixels[c].NumSteps) {
Pixels[c].Step = 0;
printf("Color %d steps %5d at %8ld delta %ld ms\r\n",c,Pixels[c].NumSteps,MillisNow,(MillisNow - MillisThen));
AllSteps += Pixels[c].Step; // will be zero only when all wrap at once
if (0 == AllSteps) {
printf("Grand cycle at: %ld\r\n",MillisNow);
for (int k=0; k < NUMPIXELS; k++) { // for each pixel
byte Value[PIXELSIZE];
for (byte c=0; c < PIXELSIZE; c++) { // ... for each color
Value[c] = StepColor(c,-k*Pixels[c].TubePhase); // figure new PWM value
// Value[c] = (c == RED && Value[c] == 0) ? Pixels[c].MaxPWM : Value[c]; // flash highlight for tracking
uint32_t UniColor = strip.Color(Value[RED],Value[GREEN],Value[BLUE],Value[WHITE]);
MillisThen = MillisNow;
view raw ArrayTest-RGBW.ino hosted with ❤ by GitHub

3 thoughts on “SK2812 RGBW LED: Test Fixture

    1. I have no idea where or when I learned This One Simple Trick, but …

      Add this:
      int s_putc(char c, FILE *t) {

      Then do this:

      And then printf() Just Works!

Comments are closed.