Arduino Source Code: Vacuum Tube LEDs with Morse Code message
// Neopixel mood lighting for vacuum tubes | |
// Ed Nisley - KE4ANU - June 2016 | |
// September 2016 - Add Morse library and blinkiness | |
#include <Adafruit_NeoPixel.h> | |
#include <morse.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 | |
#define PIXELS 2 | |
#define PIXEL_MORSE 1 | |
#define MORSE_WPM 10 | |
#define UPDATEINTERVAL 50ul | |
const unsigned long UpdateMS = UPDATEINTERVAL - 1ul; // update LEDs only this many ms apart (minus loop() overhead) | |
// number of steps per cycle, before applying prime factors | |
#define RESOLUTION 250 | |
// want to randomize the startup a little? | |
#define RANDOMIZE true | |
//---------- | |
// Globals | |
// instantiate the Neopixel buffer array | |
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 = strip.Color(255,191,0); | |
struct pixcolor_t { | |
byte Prime; | |
unsigned int NumSteps; | |
unsigned int Step; | |
float StepSize; | |
byte MaxPWM; | |
}; | |
unsigned int PlatterSteps; | |
byte PrimeList[] = {3,5,7,13,19,29}; | |
// colors in each LED | |
enum pixcolors {RED, GREEN, BLUE, 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 | |
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 | |
return Value; | |
} | |
//-- 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\r\nEd Nisley - KE4ZNU - September 2016\r\n"); | |
Entropy.initialize(); // start up entropy collector | |
// set up Neopixels | |
strip.begin(); | |
strip.show(); | |
// lamp test: a brilliant white flash | |
printf("Lamp test: flash white\r\n"); | |
for (byte i=0; i<3 ; 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); | |
} | |
// set up real random numbers | |
uint32_t rn = Entropy.random(); | |
if (RANDOMIZE) { | |
printf("Preloading LED array with seed: %08lx\r\n",rn); | |
randomSeed(rn); | |
} | |
else { | |
printf("Start not randomized\r\n"); | |
} | |
printf("First random number: %ld\r\n",random(10)); | |
// set up the color generators | |
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); | |
printf("Primes: (%d,%d,%d)\r\n",Pixels[RED].Prime,Pixels[GREEN].Prime,Pixels[BLUE].Prime); | |
Pixels[RED].MaxPWM = 255; | |
Pixels[GREEN].MaxPWM = 255; | |
Pixels[BLUE].MaxPWM = 255; | |
for (byte c=0; c < PIXELSIZE; c++) { | |
Pixels[c].NumSteps = RESOLUTION * (unsigned int) Pixels[c].Prime; | |
Pixels[c].Step = RANDOMIZE ? random(Pixels[c].NumSteps) : (3*Pixels[c].NumSteps)/4; | |
Pixels[c].StepSize = TWO_PI / Pixels[c].NumSteps; // in radians per step | |
printf("c: %d Steps: %d Init: %d",c,Pixels[c].NumSteps,Pixels[c].Step); | |
printf(" PWM: %d\r\n",Pixels[c].MaxPWM); | |
} | |
// set up Morse generator | |
printf("Morse %d wpm\n",MORSE_WPM); | |
Morse.setup(); | |
Morse.setMessage(String(" cq cq cq de ke4znu ")); | |
PrevMorse = ThisMorse = digitalRead(PIN_MORSE); | |
MillisNow = MillisThen = millis(); | |
} | |
//------------------ | |
// Run the mood | |
void loop() { | |
if (!Morse.continueSending()) { | |
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 (ThisMorse) { // if Morse output high, overlay | |
strip.setPixelColor(PIXEL_MORSE,MorseColor); | |
} | |
PrevMorse = ThisMorse; | |
strip.show(); // send out precomputed colors | |
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)); | |
} | |
} | |
byte Value[PIXELSIZE]; | |
for (byte c=0; c < PIXELSIZE; c++) { // ... for each color | |
Value[c] = StepColor(c,0.0); // figure new PWM value | |
} | |
UniColor = strip.Color(Value[RED],Value[GREEN],Value[BLUE]); | |
for (int j=0; j < strip.numPixels(); j++) { // fill all LEDs with color | |
strip.setPixelColor(j,UniColor); | |
} | |
MillisThen = MillisNow; | |
digitalWrite(PIN_HEARTBEAT,LOW); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
More details on my blog at http://wp.me/poZKh-6dD