Arduino Source Code: Vacuum Tube LEDs with Morse Code message
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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
More details on my blog at http://wp.me/poZKh-6dD