[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); | |
} | |
} |
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!