Skip to content

Instantly share code, notes, and snippets.

@ednisley
Created March 20, 2017 00:18
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/12bc36d23ae418da1e11de9fa897605f to your computer and use it in GitHub Desktop.
Save ednisley/12bc36d23ae418da1e11de9fa897605f to your computer and use it in GitHub Desktop.
Arduino source code: Vacuum tube lights using RGBW SK6812 LEDs
// Neopixel mood lighting for vacuum tubes
// Ed Nisley - KE4ANU - June 2016
// September 2016 - Add Morse library and blinkiness
// October 2016 - Set random colors at cycle end
// March 2017 - RGBW SK6812 LEDs
#include <Adafruit_NeoPixel.h>
#include <morse.h>
#include <Entropy.h>
//----------
// Pin assignments
const byte PIN_NEO = A5; // DO - data out to first Neopixel
const byte PIN_HEARTBEAT = 13; // DO - Arduino LED
#define PIN_MORSE 12
//----------
// Constants
// number of pixels
#define PIXELS 2
// index of the Morse output pixel and how fast it sends
boolean Send_Morse = false;
#define PIXEL_MORSE (PIXELS - 1)
#define MORSE_WPM 10
// lag between adjacent pixel, degrees of slowest period
#define PIXELPHASE 45
// update LEDs only this many ms apart (minus loop() overhead)
#define UPDATEINTERVAL 50ul
#define UPDATEMS (UPDATEINTERVAL - 1ul)
// 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);
uint32_t FullWhite = strip.Color(255,255,255,255);
uint32_t FullOff = strip.Color(0,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 int PlatterSteps;
byte PrimeList[] = {3,5,7,13,19,29};
// 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;
unsigned long MillisNow;
unsigned long MillisThen;
// Morse code
char * MorseText = " cq cq cq de ke4znu";
LEDMorseSender Morse(PIN_MORSE, (float)MORSE_WPM);
uint8_t PrevMorse, ThisMorse;
//-- Figure PWM based on current state
byte StepColor(byte Color, float Phi) {
byte Value;
Value = (Pixels[Color].MaxPWM / 2.0) * (1.0 + sin(Pixels[Color].Step * Pixels[Color].StepSize + Phi));
// Value = (Value) ? Value : Pixels[Color].MaxPWM; // flash at dimmest points for debug
return Value;
}
//-- 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);
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 = 32;
unsigned int PhaseSteps = (unsigned int) ((PIXELPHASE / 360.0) *
RESOLUTION * (unsigned int) max(max(max(Pixels[RED].Prime,Pixels[GREEN].Prime),Pixels[BLUE].Prime),Pixels[WHITE].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: %d Init: %d Phase: %d 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
Serial.begin(57600);
fdevopen(&s_putc,0); // set up serial output for printf()
printf("Vacuum Tube Mood Light - RGBW\r\nEd Nisley - KE4ZNU - March 2017\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 white\r\n");
for (byte i=0; i<5 ; i++) {
for (int j=0; j < strip.numPixels(); j++) { // fill LEDs with white
strip.setPixelColor(j,FullWhite);
}
strip.show();
delay(500);
for (int j=0; j < strip.numPixels(); j++) { // fill LEDs with black
strip.setPixelColor(j,FullOff);
}
strip.show();
delay(500);
}
// 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();
// set up Morse generator
Morse.setup();
Morse.setMessage(String(MorseText));
MorseColor = strip.Color(255,random(32,64),random(16),0);
PrevMorse = ThisMorse = digitalRead(PIN_MORSE);
printf("Morse enabled: %d at %d wpm color: %08lx\n [%s]\r\n",Send_Morse,MORSE_WPM,MorseColor,MorseText);
MillisNow = MillisThen = millis();
}
//------------------
// Run the mood
void loop() {
if (!Morse.continueSending()) {
printf("Restarting Morse message\r\n");
Morse.startSending();
}
ThisMorse = digitalRead(PIN_MORSE);
MillisNow = millis();
if (((MillisNow - MillisThen) >= UPDATEMS) || // time for color change?
(PrevMorse != ThisMorse)) { // Morse output bit changed?
digitalWrite(PIN_HEARTBEAT,HIGH);
if (Send_Morse && ThisMorse) { // if Morse output high, overlay flash
strip.setPixelColor(PIXEL_MORSE,MorseColor);
}
PrevMorse = ThisMorse;
strip.show(); // send out precomputed colors
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 %d steps %d at %8ld delta %ld ms\r\n",c,Pixels[c].NumSteps,MillisNow,(MillisNow - MillisThen));
}
else {
CycleRun = true; // this color is still cycling
}
}
// If all cycles have completed, reset the color generators
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));
}
UniColor = strip.Color(Value[RED],Value[GREEN],Value[BLUE],Value[WHITE]);
strip.setPixelColor(i,UniColor);
}
MillisThen = MillisNow;
digitalWrite(PIN_HEARTBEAT,LOW);
}
}
@ednisley
Copy link
Author

More details on my blog at http://wp.me/poZKh-6BP

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