|
// SK6812 RGBW LED array exerciser |
|
// Ed Nisley - KE4ANU - February 2017 |
|
// 2020-01-25 add row-by-row color modifications |
|
|
|
#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 |
|
|
|
const byte PIN_SELECT = 2; // DI - pattern select input |
|
|
|
//---------- |
|
// 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 |
|
|
|
pinMode(PIN_SELECT,INPUT_PULLUP); |
|
|
|
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); |
|
} |
|
|
|
if (digitalRead(PIN_SELECT)) { |
|
for (int col=0; col < NUMCOLS ; col++) { // for each column |
|
byte Value[PIXELSIZE]; // figure first row colors |
|
for (byte p=0; p < PIXELSIZE; p++) { // ... for each color in pixel |
|
Value[p] = StepColor(p,-col*Pixels[p].TubePhase); |
|
} |
|
// just RGB |
|
int row = 0; |
|
uint32_t UniColor = strip.Color(Value[RED],Value[GREEN],Value[BLUE],0); |
|
strip.setPixelColor(col + NUMCOLS*row++,UniColor); |
|
|
|
byte MinWhite = min(min(Value[RED],Value[GREEN]),Value[BLUE]); |
|
|
|
// only common white |
|
UniColor = strip.Color(0,0,0,MinWhite); |
|
strip.setPixelColor(col + NUMCOLS*row++,UniColor); |
|
|
|
// RGB minus common white + white |
|
UniColor = strip.Color(Value[RED]-MinWhite,Value[GREEN]-MinWhite,Value[BLUE]-MinWhite,MinWhite); |
|
strip.setPixelColor(col + NUMCOLS*row++,UniColor); |
|
|
|
// RGB minus common white |
|
UniColor = strip.Color(Value[RED]-MinWhite,Value[GREEN]-MinWhite,Value[BLUE]-MinWhite,0); |
|
strip.setPixelColor(col + NUMCOLS*row++,UniColor); |
|
|
|
// inverse RGB |
|
UniColor = strip.Color(255 - Value[RED],255 - Value[GREEN],255 - Value[BLUE],0); |
|
strip.setPixelColor(col + NUMCOLS*row++,UniColor); |
|
} |
|
} |
|
else { |
|
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); |
|
} |
|
|
|
} |