Skip to content

Instantly share code, notes, and snippets.

@ednisley
Created June 9, 2020 19:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ednisley/8e72ddc66ce82591080a9cc5379869c7 to your computer and use it in GitHub Desktop.
Save ednisley/8e72ddc66ce82591080a9cc5379869c7 to your computer and use it in GitHub Desktop.
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);
}
}
@ednisley
Copy link
Author

ednisley commented Jun 9, 2020

More details on my blog at https://wp.me/poZKh-98r

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment