Skip to content

Instantly share code, notes, and snippets.

@ednisley
Created October 1, 2016 19:36
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/1aa17e93491fa3b20b27e426edcdca0a to your computer and use it in GitHub Desktop.
Save ednisley/1aa17e93491fa3b20b27e426edcdca0a to your computer and use it in GitHub Desktop.
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);
}
}
@ednisley
Copy link
Author

ednisley commented May 6, 2017

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

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