[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:

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:

Wiring up a 5×5 panel went as before:

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:

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

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 | |
| #define UPDATEINTERVAL 20ul | |
| 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 NUMPIXELS (NUMCOLS * NUMROWS) | |
| #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) { | |
| Serial.write(c); | |
| } | |
| //—————— | |
| // Set the mood | |
| void setup() { | |
| pinMode(PIN_HEARTBEAT,OUTPUT); | |
| digitalWrite(PIN_HEARTBEAT,LOW); // show we arrived | |
| Serial.begin(57600); | |
| 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 | |
| strip.begin(); | |
| strip.show(); | |
| // lamp test: run a brilliant white dot along the length of the strip | |
| printf("Lamp test: walking white\r\n"); | |
| strip.setPixelColor(0,FullWhite); | |
| strip.show(); | |
| delay(250); | |
| for (int i=1; i<NUMPIXELS; i++) { | |
| digitalWrite(PIN_HEARTBEAT,HIGH); | |
| strip.setPixelColor(i-1,FullOff); | |
| strip.setPixelColor(i,FullWhite); | |
| strip.show(); | |
| digitalWrite(PIN_HEARTBEAT,LOW); | |
| delay(250); | |
| } | |
| strip.setPixelColor(NUMPIXELS – 1,FullOff); | |
| strip.show(); | |
| delay(250); | |
| // fill the array, row by row | |
| printf(" … fill\r\n"); | |
| for (int i=NUMROWS-1; i>=0; i–) { // for each row | |
| digitalWrite(PIN_HEARTBEAT,HIGH); | |
| for (int j=NUMCOLS-1; j>=0 ; j–) { | |
| strip.setPixelColor(PINDEX(i,j),FullWhite); | |
| strip.show(); | |
| delay(100); | |
| } | |
| digitalWrite(PIN_HEARTBEAT,LOW); | |
| } | |
| // clear to black, column by column | |
| printf(" … clear\r\n"); | |
| for (int j=NUMCOLS-1; j>=0; j–) { // for each column | |
| digitalWrite(PIN_HEARTBEAT,HIGH); | |
| for (int i=NUMROWS-1; i>=0; i–) { | |
| strip.setPixelColor(PINDEX(i,j),FullOff); | |
| strip.show(); | |
| delay(100); | |
| } | |
| digitalWrite(PIN_HEARTBEAT,LOW); | |
| } | |
| delay(1000); | |
| // 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", | |
| Pixels[RED].Prime,Pixels[GREEN].Prime,Pixels[BLUE].Prime,Pixels[WHITE].Prime); | |
| 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) { | |
| digitalWrite(PIN_HEARTBEAT,HIGH); | |
| 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]); | |
| strip.setPixelColor(k,UniColor); | |
| } | |
| strip.show(); | |
| MillisThen = MillisNow; | |
| digitalWrite(PIN_HEARTBEAT,LOW); | |
| } | |
| } |
Comments
3 responses to “SK2812 RGBW LED: Test Fixture”
When and how did you start using printf() with arduino?
I have no idea where or when I learned This One Simple Trick, but …
Add this:
int s_putc(char c, FILE *t) {Serial.write(c);
}
Then do this:
Serial.begin(57600);
fdevopen(&s_putc,0);
And then
printf()Just Works![…] two knockoff Neopixel test fixtures went dark while their USB charger accompanied me on a trip and spent a few days at ambient basement […]