Arduino source code: SK6812 control for Glass Tile backlights
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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
More details on my blog at https://wp.me/poZKh-98r