Skip to content

Instantly share code, notes, and snippets.

@ednisley
Created April 21, 2020 19:06
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/dee4136ed2ed345d3484ffb264c29a04 to your computer and use it in GitHub Desktop.
Save ednisley/dee4136ed2ed345d3484ffb264c29a04 to your computer and use it in GitHub Desktop.
Arduino source code: SK2812 RGB LED driver for "mood lighting" an upcycled Nissan fog lamp
// Neopixel Algorithmic Art
// W2812 RGB Neopixel version
// Ed Nisley - KE4ZNU
#include <Adafruit_NeoPixel.h>
#include <Entropy.h>
//----------
// Pin assignments
const byte PIN_NEO = A3; // DO - data out to first Neopixel
const byte PIN_HEARTBEAT = 13; // DO - Arduino LED
#define PIN_MORSE 12
//----------
// Constants
// number of pixels
#define PIXELS 4
// lag between adjacent pixels in degrees of slowest period
#define PIXELPHASE 1
// update LEDs only this many ms apart (minus loop() overhead)
#define UPDATEINTERVAL 50ul
#define UPDATEMS (UPDATEINTERVAL - 0ul)
// number of steps per cycle, before applying prime factors
#define RESOLUTION 500
//----------
// Globals
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXELS, PIN_NEO, NEO_GRB + NEO_KHZ800);
uint32_t FullWhite = strip.Color(255,255,255);
uint32_t FullOff = strip.Color(0,0,0);
uint32_t MorseColor;
struct pixcolor_t {
unsigned int Prime;
unsigned int NumSteps;
unsigned int Step;
float StepSize;
float Phase;
byte MaxPWM;
};
unsigned long int TotalSteps;
unsigned long int SuperCycleSteps;
byte PrimeList[] = {3,5,7,11,13,17,19,29}; // small primes = faster changes
// colors in each LED and their count
enum pixcolors {RED, GREEN, BLUE, PIXELSIZE};
struct pixcolor_t Pixel[PIXELSIZE]; // all the data for each pixel color intensity
uint32_t UniColor;
unsigned long int MillisNow;
unsigned long int MillisThen;
//-- Select three unique primes for the color generator function
// Then compute all the step parameters based on those values
void SetColorGenerators(void) {
Pixel[RED].Prime = PrimeList[random(sizeof(PrimeList))];
do {
Pixel[GREEN].Prime = PrimeList[random(sizeof(PrimeList))];
} while (Pixel[RED].Prime == Pixel[GREEN].Prime);
do {
Pixel[BLUE].Prime = PrimeList[random(sizeof(PrimeList))];
} while (Pixel[BLUE].Prime == Pixel[RED].Prime ||
Pixel[BLUE].Prime == Pixel[GREEN].Prime);
if (false) {
Pixel[RED].Prime = 1;
Pixel[GREEN].Prime = 3;
Pixel[BLUE].Prime = 5;
}
printf("Primes: %d %d %d\r\n",Pixel[RED].Prime,Pixel[GREEN].Prime,Pixel[BLUE].Prime);
TotalSteps = 0;
SuperCycleSteps = RESOLUTION;
for (byte c = 0; c < PIXELSIZE; c++) {
SuperCycleSteps *= Pixel[c].Prime;
}
printf(" Super cycle length: %lu steps\r\n",SuperCycleSteps);
Pixel[RED].MaxPWM = 255;
Pixel[GREEN].MaxPWM = 255;
Pixel[BLUE].MaxPWM = 255;
unsigned int PhaseSteps = (unsigned int) ((PIXELPHASE / 360.0) *
RESOLUTION * (unsigned int) max(max(Pixel[RED].Prime,Pixel[GREEN].Prime),Pixel[BLUE].Prime));
printf("Inter-pixel phase: %d deg = %d steps\r\n",(int)PIXELPHASE,PhaseSteps);
for (byte c = 0; c < PIXELSIZE; c++) {
Pixel[c].NumSteps = RESOLUTION * Pixel[c].Prime; // steps per cycle
Pixel[c].StepSize = TWO_PI / Pixel[c].NumSteps; // radians per step
Pixel[c].Step = random(Pixel[c].NumSteps); // current step
Pixel[c].Phase = PhaseSteps * Pixel[c].StepSize; // phase in radians for this color
printf(" c: %d Steps: %5d Init: %5d Phase: %3d deg",c,Pixel[c].NumSteps,Pixel[c].Step,(int)(Pixel[c].Phase * 360.0 / TWO_PI));
printf(" PWM: %d\r\n",Pixel[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
Serial.begin(57600);
fdevopen(&s_putc,0); // set up serial output for printf()
printf("Algorithmic Art\r\n RGB WS2812\r\nEd Nisley - KE4ZNU - April 2020\r\n");
Entropy.initialize(); // start up entropy collector
// set up pixels
strip.begin();
strip.show();
// lamp test: a brilliant white flash
printf("Lamp test: flash full-on colors\r\n");
uint32_t FullRGB = strip.Color(255,255,255);
uint32_t FullR = strip.Color(255,0,0);
uint32_t FullG = strip.Color(0,255,0);
uint32_t FullB = strip.Color(0,0,255);
uint32_t FullOff = strip.Color(0,0,0);
uint32_t TestColors[] = {FullR,FullG,FullB,FullRGB,FullOff};
for (byte i = 0; i < sizeof(TestColors)/sizeof(uint32_t) ; i++) {
printf(" color: %08lx\r\n",TestColors[i]);
for (int p=0; p < strip.numPixels(); p++) {
strip.setPixelColor(p,TestColors[i]);
}
strip.show();
delay(1000);
}
// 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();
MillisNow = MillisThen = millis();
}
//------------------
// Run the mood
void loop() {
MillisNow = millis();
if ((MillisNow - MillisThen) >= UPDATEMS) { // time for another step?
digitalWrite(PIN_HEARTBEAT,HIGH);
TotalSteps++;
strip.show(); // send out precomputed colors
for (byte c = 0; c < PIXELSIZE; c++) { // compute next increment for each color
if (++Pixel[c].Step >= Pixel[c].NumSteps) {
Pixel[c].Step = 0;
printf("Color %-5d steps %-5d at %-8ld ms %-8ld TS %-8lu\r\n",
c,Pixel[c].NumSteps,MillisNow,(MillisNow - MillisThen),TotalSteps);
}
}
// If all cycles have completed, reset the color generators
if (TotalSteps >= SuperCycleSteps) {
printf("Supercycle end, setting new color values\r\n");
SetColorGenerators();
}
for (int p = 0; p < strip.numPixels(); p++) { // for each pixel
byte Value[PIXELSIZE];
for (byte c=0; c < PIXELSIZE; c++) { // compute new colors
Value[c] = (Pixel[c].MaxPWM / 2.0) * (1.0 + sin(Pixel[c].Step * Pixel[c].StepSize - p*Pixel[c].Phase));
}
UniColor = strip.Color(Value[RED],Value[GREEN],Value[BLUE]);
strip.setPixelColor(p,UniColor);
}
MillisThen = MillisNow;
digitalWrite(PIN_HEARTBEAT,LOW);
}
}
@ednisley
Copy link
Author

More details on my blog at https://wp.me/poZKh-8Zr

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