Vacuum Tube LEDS: Neopixel Plate Cap

[Edit: Welcome Hackaday! You might prefer real vacuum tubes; searching for “vacuum tube leds” will turn up more posts about this long-running project. And, yes, I lit up a tube, just for old time’s sake, and have some plans for that huge triode.]

A single (knockoff) Neopixel hovers over a defunct halogen bulb:

Vacuum Tube LEDs - plate lead - overview
Vacuum Tube LEDs – plate lead – overview

The Arduino code comes from stripping down the Hard Drive Platter Mood Light to suit just one Neopixel, with the maximum PWM values favoring the red-blue-purple end of the color wheel:

	Pixels[RED].Prime = 3;
	Pixels[GREEN].Prime = 5;
	Pixels[BLUE].Prime = 7;
	printf("Primes: (%d,%d,%d)\r\n",Pixels[RED].Prime,Pixels[GREEN].Prime,Pixels[BLUE].Prime);

	Pixels[RED].MaxPWM = 255;
	Pixels[GREEN].MaxPWM = 64;
	Pixels[BLUE].MaxPWM = 255;

Unlike the Mood Light’s dozen Neopixels jammed into the platter’s hub ring, running one Neopixel at full throttle atop the tube doesn’t overheat the poor controller. In a 22 °C room, PWM 255 white raises the cap’s interior temperature to 35 °C, which looks like a horrific 40 °C/W thermal coefficient if you figure the dissipation at 300 mW = 5 V x 60 mA.

Feeding those parameters into the raised sine wave equation causes the cap to tick along at 27 °C for an average dissipation of 120 mW, which sounds about right:

113 mW = 5 V x (20 + 20 + 5 mA) / 2

The effect is striking in a dark room, but it’s hard to photograph; the halogen capsule inside the bulb resembles a Steampunk glass jellyfish:

Vacuum Tube LEDs - plate lead - detail
Vacuum Tube LEDs – plate lead – detail

That ceramic light socket should stand on a round base with room for the Arduino controller. I think powering it from a wall wart through a USB cable makes sense, with a USB-to-serial converter epoxied inside the box for reprogramming.

It looks pretty good, methinks, should you like that sort of thing.

The Arduino source code as a GitHub gist:

// Neopixel mood lighting for vacuum tubes
// Ed Nisley - KE4ANU - January 2016
#include <Adafruit_NeoPixel.h>
// Pin assignments
const byte PIN_NEO = 6; // DO - data out to first Neopixel
const byte PIN_HEARTBEAT = 13; // DO - Arduino LED
// Constants
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 100
// want to randomize the startup a little?
#define RANDOMIZE true
// Globals
// instantiate the Neopixel buffer array
Adafruit_NeoPixel strip = Adafruit_NeoPixel(1, PIN_NEO, NEO_GRB + NEO_KHZ800);
uint32_t FullWhite = strip.Color(255,255,255);
uint32_t FullOff = strip.Color(0,0,0);
struct pixcolor_t {
byte Prime;
unsigned int NumSteps;
unsigned int Step;
float StepSize;
byte MaxPWM;
unsigned int PlatterSteps;
// colors in each LED
enum pixcolors {RED, GREEN, BLUE, PIXELSIZE};
struct pixcolor_t Pixels[PIXELSIZE]; // all the data for each pixel color intensity
unsigned long MillisNow;
unsigned long MillisThen;
//-- 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) {
// Set the mood
void setup() {
digitalWrite(PIN_HEARTBEAT,LOW); // show we arrived
fdevopen(&s_putc,0); // set up serial output for printf()
printf("Vacuum Tube Mood Light with Neopixels\r\nEd Nisley - KE4ZNU - January 2016\r\n");
/// set up Neopixels
// lamp test: a brilliant white dot
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
for (int j=0; j < strip.numPixels(); j++) { // fill LEDs with black
// set up the color generators
MillisNow = MillisThen = millis();
randomSeed(MillisNow + analogRead(7));
printf("Start not randomized\r\n");
printf("First random number: %ld\r\n",random(10));
Pixels[RED].Prime = 3;
Pixels[GREEN].Prime = 5;
Pixels[BLUE].Prime = 7;
printf("Primes: (%d,%d,%d)\r\n",Pixels[RED].Prime,Pixels[GREEN].Prime,Pixels[BLUE].Prime);
Pixels[RED].MaxPWM = 255;
Pixels[GREEN].MaxPWM = 64;
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);
// Run the mood
void loop() {
MillisNow = millis();
if ((MillisNow - MillisThen) > UpdateMS) {
for (byte c=0; c < PIXELSIZE; c++) { // step to next increment in 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
// Value[c] = (c == RED && Value[c] == 0) ? Pixels[c].MaxPWM : Value[c]; // flash highlight for tracking
uint32_t UniColor = strip.Color(Value[RED],Value[GREEN],Value[BLUE]);
for (int j=0; j < strip.numPixels(); j++) { // fill LEDs with color
MillisThen = MillisNow;
view raw TubeMood.ino hosted with ❤ by GitHub

9 thoughts on “Vacuum Tube LEDS: Neopixel Plate Cap

  1. I’d probably go with a PJRC Teensy LC as a combination microcontroller and USB interface. Even though the Teensy 2.0 has a simpler 32U4 chip, it’s $16 compared to the Teensy LC with a powerful ARM controller for $12. PJRC offers plug-ins to the Arduino IDE so either can be programmed from a familiar environment. The name is apt too, they’re quite small and should fit nicely under that ceramic lamp socket.

    1. The economics seem really odd:

      • Knockoff Neopixel: $0.20
      • Knockoff Arduino Pro Mini: $2.80
      • Knockoff FTDI USB-to-serial: $2.90

      Makes it hard to justify anything fancier, it does, even if the Teensy would eliminate a dozen hand-soldered wires under the hood. I’ll show pix of the base in a few days.

      That “antique” ceramic fixture may be worth more than everything else combined! [heavy sigh]

      1. Accordiong to Home Depot’s web site, if you don’t need a switch, you can get a gen-u-wine ceramic socket for $1.49. If you want the switch, it’s $4.29. My previous house, circa 1935, had sockets not much different from the current ones. (Somebody offers a plastic version, but haven’t seen one at Depot. IIRC, the farm store had them.)

        1. The socket had a switch, so I removed the bead chain, bent the actuator a bit, and stuffed the cable through the eyelet for that polished look. That level of fit-and-finish will be costly when I go into mass production…

  2. Arduino Pro Micro ATmega32U4 based $3.99 or less (on, if you really want built in USB. There are oddities in the 32U4, it can do things the 328p can’t but it’s different (no pin 13 LED, needs waiting for serial to start up). However you’re paying too much for your Pro Mini’s ($1.42).

    1. Looks like the “Leonardo” Pro Micro boards start around $4 here, too, but the offerings quickly ramp upward toward $8, suggesting the half-price ones lack a certain, mmm, commitment to detail. I should pick up a few just to see how awful they can be: the cheapest 328 boards have been amusing.

      The $2.90 came from my last bulk order of 328 Pro Minis, so we’re in a deflationary market. Oddly, the low-end Chinese listings today show Canadian dollar amounts: C$2.38 = US$1.69. What financial reasoning is in play?

      1. They seem to list the same things over and over, varying the price and currency. One week, I’ll see things in UK pounds, the next week in US dollars, then Canadian dollars. Once in a while, I’ll see other currencies, but usually those three (as long as I’m shopping on the eBay US site). It may be an attempt to ride the exchange rates (eBay will sort by price, converted to local currency, and they’re obviously trying to sell on price), or it may be inscrutable Chinese marketing practices. I’m buffaloed.

Comments are closed.