|
// 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); |
|
} |
|
|
|
} |