Arduino source code: SK6812 control for Glass Tile backlights
// Neopixel lighting for glass tiles | |
// Ed Nisley - KE4ANU - May 2020 | |
#include <Adafruit_NeoPixel.h> | |
#include <Entropy.h> | |
//---------- | |
// Pin assignments | |
const byte PIN_NEO = A3; // DO - data to first Neopixel | |
const byte PIN_MODE = 2; // DI - select mode | |
const byte PIN_SPEED = 3; // DI - select speed | |
const byte PIN_SELECT = 4; // DO - drive adjacent pins low | |
const byte PIN_HEARTBEAT = 13; // DO - Arduino LED | |
//---------- | |
// Constants | |
// number of pixels | |
#define PIXELS 9 | |
// lag between adjacent pixels in degrees of slowest period | |
#define PIXELPHASE 45 | |
// update LEDs only this many ms apart (minus loop() overhead) | |
#define UPDATEINTERVAL 50ul | |
// number of steps per cycle, before applying prime factors | |
#define RESOLUTION 500 | |
//---------- | |
// Globals | |
// instantiate the Neopixel buffer array | |
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXELS, PIN_NEO, NEO_GRBW + NEO_KHZ800); | |
struct pixcolor_t { | |
unsigned int Prime; | |
unsigned int NumSteps; | |
unsigned int Step; | |
float StepSize; | |
float Phase; | |
byte MaxPWM; | |
}; | |
unsigned int PlatterSteps; | |
byte PrimeList[] = {3,5,7,13,19,29}; | |
unsigned int MaxTileTime; | |
// colors in each LED | |
enum pixcolors {RED, GREEN, BLUE, WHITE, PIXELSIZE}; | |
struct pixcolor_t Pixels[PIXELSIZE]; // all the data for each pixel color intensity | |
uint32_t UniColor; | |
enum dispmode {GLOW, FLASH}; // based on input pin | |
unsigned long UpdateMS; | |
unsigned long MillisNow; | |
unsigned long MillisThen; | |
//-- Select three unique primes for the color generator function | |
// Then compute all the step parameters based on those values | |
void SetColorGenerators(void) { | |
Pixels[RED].Prime = PrimeList[random(sizeof(PrimeList))]; | |
do { | |
Pixels[GREEN].Prime = PrimeList[random(sizeof(PrimeList))]; | |
} while (Pixels[RED].Prime == Pixels[GREEN].Prime); | |
do { | |
Pixels[BLUE].Prime = PrimeList[random(sizeof(PrimeList))]; | |
} while (Pixels[BLUE].Prime == Pixels[RED].Prime || | |
Pixels[BLUE].Prime == Pixels[GREEN].Prime); | |
do { | |
Pixels[WHITE].Prime = PrimeList[random(sizeof(PrimeList))]; | |
} while (Pixels[WHITE].Prime == Pixels[RED].Prime || | |
Pixels[WHITE].Prime == Pixels[GREEN].Prime || | |
Pixels[WHITE].Prime == Pixels[BLUE].Prime); | |
if (!digitalRead(PIN_SPEED)) { // force fast for debugging | |
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); | |
Pixels[RED].MaxPWM = 255; | |
Pixels[GREEN].MaxPWM = 255; | |
Pixels[BLUE].MaxPWM = 255; | |
Pixels[WHITE].MaxPWM = 255; | |
unsigned int PhaseSteps = (unsigned int) ((PIXELPHASE / 360.0) * | |
RESOLUTION * (unsigned int) max(max(Pixels[RED].Prime,Pixels[GREEN].Prime),Pixels[BLUE].Prime)); | |
printf("Pixel phase offset: %d deg = %d steps\r\n",(int)PIXELPHASE,PhaseSteps); | |
for (byte c=0; c < PIXELSIZE; c++) { | |
Pixels[c].NumSteps = RESOLUTION * Pixels[c].Prime; // steps per cycle | |
Pixels[c].StepSize = TWO_PI / Pixels[c].NumSteps; // radians per step | |
Pixels[c].Step = random(Pixels[c].NumSteps); // current step | |
Pixels[c].Phase = PhaseSteps * Pixels[c].StepSize;; // phase in radians for this color | |
printf(" c: %d Steps: %5d Init: %5d Phase: %3d deg",c,Pixels[c].NumSteps,Pixels[c].Step,(int)(Pixels[c].Phase * 360.0 / TWO_PI)); | |
printf(" PWM: %d\r\n",Pixels[c].MaxPWM); | |
} | |
} | |
//-- 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_MODE,INPUT_PULLUP); | |
pinMode(PIN_SPEED,INPUT_PULLUP); | |
pinMode(PIN_SELECT,OUTPUT); | |
digitalWrite(PIN_SELECT,LOW); // drive adjacent pins | |
Serial.begin(57600); | |
fdevopen(&s_putc,0); // set up serial output for printf() | |
printf("\r\nAlgorithmic Art Light - Glass Tiles\r\nEd Nisley - KE4ZNU - May 2020\r\n"); | |
printf("Display mode: %s\r\n",digitalRead(PIN_MODE) == GLOW ? "Glow" : "Flash"); | |
printf("Speed: %s\r\n",digitalRead(PIN_SPEED) ? "Normal" : "Override"); | |
Entropy.initialize(); // start up entropy collector | |
// set up pixels | |
strip.begin(); | |
strip.show(); | |
// lamp test | |
printf("Lamp test: flash full-on colors\r\n"); | |
uint32_t FullRGBW = strip.Color(255,255,255,255); | |
uint32_t FullRGB = strip.Color(255,255,255,0); | |
uint32_t FullR = strip.Color(255,0,0,0); | |
uint32_t FullG = strip.Color(0,255,0,0); | |
uint32_t FullB = strip.Color(0,0,255,0); | |
uint32_t FullW = strip.Color(0,0,0,255); | |
uint32_t FullOff = strip.Color(0,0,0,0); | |
uint32_t TestColors[] = {FullR,FullG,FullB,FullRGB,FullW,FullRGBW,FullOff}; | |
for (byte i=0; i < sizeof(TestColors)/sizeof(uint32_t) ; i++) { | |
printf(" color: %08lx\r\n",TestColors[i]); | |
for (int j=0; j < strip.numPixels(); j++) { | |
strip.setPixelColor(j,TestColors[i]); | |
} | |
strip.show(); | |
delay(1000); | |
} | |
// while (1) {continue;}; // all LEDs constant for burn-in testing | |
// get an actual random number | |
uint32_t rn = Entropy.random(); | |
printf("Random seed: %08lx\r\n",rn); | |
randomSeed(rn); | |
// set up the color generators | |
SetColorGenerators(); | |
MaxTileTime = (digitalRead(PIN_SPEED) ? 6 : 1) * (1000.0 / UPDATEINTERVAL); | |
UpdateMS = UPDATEINTERVAL; | |
MillisNow = MillisThen = millis(); | |
} | |
//------------------ | |
// Run the mood | |
void loop() { | |
MillisNow = millis(); | |
if ((MillisNow - MillisThen) >= UpdateMS) { // time for color change? | |
digitalWrite(PIN_HEARTBEAT,HIGH); | |
if (digitalRead(PIN_MODE) == GLOW) { | |
boolean CycleRun = false; // check to see if all cycles have ended | |
for (byte c=0; c < PIXELSIZE; c++) { // compute next increment for each color | |
if (++Pixels[c].Step >= Pixels[c].NumSteps) { | |
Pixels[c].Step = 0; | |
printf("Cycle %5d steps %5d at %8ld delta %8ld ms\r\n",c,Pixels[c].NumSteps,MillisNow,(MillisNow - MillisThen)); | |
} | |
else { | |
CycleRun = true; // this color is still cycling | |
} | |
} | |
if (!CycleRun) { | |
printf("All cycles ended: setting new color generator values\r\n"); | |
SetColorGenerators(); | |
} | |
for (int i=0; i < strip.numPixels(); i++) { // for each pixel | |
byte Value[PIXELSIZE]; | |
for (byte c=0; c < PIXELSIZE; c++) { // ... for each color | |
Value[c] = (Pixels[c].MaxPWM / 2.0) * (1.0 + sin(Pixels[c].Step * Pixels[c].StepSize - i*Pixels[c].Phase)); | |
} | |
byte WhiteBias = min(min(Value[RED],Value[GREEN]),Value[BLUE]); // hack to reduce power | |
UniColor = strip.Color((Value[RED] - WhiteBias) * Pixels[RED].MaxPWM/255, | |
(Value[GREEN] - WhiteBias) * Pixels[GREEN].MaxPWM/255, | |
(Value[BLUE] - WhiteBias) * Pixels[BLUE].MaxPWM/255, | |
WhiteBias * Pixels[WHITE].MaxPWM/255); | |
strip.setPixelColor(i,UniColor); | |
} | |
} | |
else { | |
byte c = random(1,8); // exclude 0 = all off, to avoid darkness | |
printf("Color %d ",c); | |
byte r = c & 0x04 ? 0xff : 0; | |
byte g = c & 0x02 ? 0xff : 0; | |
byte b = c & 0x01 ? 0xff : 0; | |
byte w = 0; | |
if (c == 7) { // use white LED instead of R+G+B | |
r = g = b = 0; | |
w = 0xff; | |
} | |
UniColor = strip.Color(r, g, b, w); | |
byte i = random(strip.numPixels()); | |
printf("at %d ",i); | |
if (UniColor == strip.getPixelColor(i)) { // flip color | |
printf("^ "); | |
if (w) { // white becomes dim | |
w = 0x7f; | |
UniColor = strip.Color(r, g, b, w); | |
} | |
else | |
UniColor ^= 0xffffff00l; // other colors flip | |
} | |
else { | |
printf(" "); | |
} | |
strip.setPixelColor(i,UniColor); | |
UpdateMS = random(10,MaxTileTime) * UPDATEINTERVAL; // pick time for next update | |
printf("delay: %6ld ms\r\n",UpdateMS); | |
} | |
strip.show(); // send out precomputed colors | |
MillisThen = MillisNow; | |
digitalWrite(PIN_HEARTBEAT,LOW); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
More details on my blog at https://wp.me/poZKh-98r