Nixie Clock With Vacuum-tube Power Supply – Part 2

In Part 1 I covered the genesis of this project and my first stab at a vacuum tube power supply.

One of the things I wanted to do was provide all of the power for the clock from the transformer. My regular clock circuit only needs 5V, so I figured I could get this from the 6V3 filament winding using a bridge rectifier and a 5V voltage regulator. This is only really possible because the 6CA4 rectifier tube uses an indirectly heated cathode. That is to say, the heater is not connected to the output pins, so it can be at any potential with respect to the output voltage. In particular, it means we can tie one side of the heater to the common ‘ground’ or 0V rail. The HV ‘ground’ is the center tap of the secondary:

Circuit, showing heater

Some of you may have figured that that is cutting it a bit too fine. A bridge rectifier using regular diodes could drop around 1.4V to 2V depending on the current draw. The voltage regulator would drop another 2V, so it needs a steady 7V in. So if the smoothing were perfectly efficient, we would go from 8.9V (6.3*1.414) to possibly less than 7V. Simulation with LTSpice shows this is optimistic.

Another alternative would be to use Schottky diodes for the bridge, and simulation show this would probably work. However, I decided to use a simple voltage multiplier on the 6V3 AC which gives me better smoothing for fewer components. So the whole circuit now looks like this:

Circuit with 5V section

You’ll note that this diagram is slightly different. I have cut the direct connection between pin 4 of T1 and the 0V rail of the HV section. Instead I have explicitly shown each section (the voltage doubler, HV and 5V sections) with a separate connection to ground. This is because, as it is, I am only using one half of the voltage doubler. This is just one diode drop, rather than two (as with the a full-wave bridge), so the voltage is enough for the 7805 regulator. If I wanted to use the full doubled voltage, I could just move the voltage-doubler ground to the point labelled A instead.

This is all very well, but it turns out to have a couple of draw-backs, as will become apparent later.

Current Limiter

The HV power supply is unregulated – which is to say, as you draw more current, the voltage starts to sag. This is because the forward voltage drop of a rectifier tube increases as the current increases. To get around this I decided I would use a small constant-current circuit to drive my Nixie tubes, in place of the usual current-limiting resistor. This basically works by connecting the source of a PMOS transistor to HV via a resistor (Rset) and then biasing the gate a fixed voltage below HV (Vgs) like so:

This is the equation for the resistor, given the desired current (Id):

Rset=VgsVgs(on)Id

Where Vgs(on) is the voltage drop between the source and the gate, which you can get from the transfer characteristics chart of the data sheet. A rough estimate would the typical gate threshold voltage.

Of course, the trick is to keep Vgs constant, even though HV is varying. To this end, I used a small isolated DC-DC converter to boost 5V to 12V and then attach it to the circuit as shown above. The gate takes virtually no current, so the boost converter can be very small. Using a voltage divider let’s us get to 10V and using a small potentiometer in one leg of the voltage divider allows us to adjust the voltage to get precisely the current we want.

Following some advice I received on the Neonixie google group, the actual circuit is a little more complicated, to provide some protection to the MOSFET. I designed some boards to act as sockets for the nixie tubes that included all this circuitry:

At this point I had decided I would ultimately want to use all of this to build a clock that would feature the large NL7037 tubes I had been collecting. I had been meaning to build a clock with an industrial feel to it to match the look of these tubes, and I felt that this would be the one. Finally here is a shot of a test I made using one of the tubes and adapters:

ESP32: A solution looking for a problem?

The ESP32 was released a few years ago, and was immediately touted as an ‘ESP8266 killer’. What happened?

I’ve been using the ESP8285 in my clocks for a while now. It is an ESP8266 with 1M flash built-in. This not only means you have everything you need on one chip, but it also frees up a couple of GPIOs. I’ve been very happy with it and with the Arduino core that is implemented on top of it. It has done everything I have asked of it. I find it odd that it is virtually impossible to find any commercial boards that use it.

So the ESP32, with dual cores, higher speed and more GPIOs should be better right? Well the Espressif code, which is layered on top of FreeRTOS, is still fairly unstable. The Arduino core, just got to 1.0.

However, I figured it was time to seriously start playing with it and I chose the ESP32 Pico dev kit, because, like the ESP8285, it has flash on-chip.

The first thing I did was try and play sounds on it – I wanted to use the I2S interface, which seems like a big distinguishing feature of this chip. After a while I managed to find a good library that works on the Arduino core, and includes support for the ESP32 – ESP8266Audio (by the way, that guy is prolific, check out his other projects). It worked very well.

Next I wanted to add a WiFi Manager to manage setting up a captive portal. It turns out that the one I use on the ESP8285 (AsyncWiFiManager) is also just about the only one that really supports the ESP32.

When I tried to connect my iPhone to the AP, the ESP32 kept crashing. It turns out that this was a problem in esp-idf, which was recently fixed, but the fix wasn’t included in the Arduino core 1.0.1. A few days after I submitted a bug report, it was included in master, so I switched to using master.

Playing sounds is great, but of course, it all starts to fall apart as I try to do more with the platform. I wanted to move beyond just playing sounds so I dove right in and created several FreeRTOS tasks to do things asynchronously.

A quick aside: The arduino core is implemented on top of FreeRTOS tasks. So for example, loop() is in its own task.

Naturally the audio playback instantly started glitching, so I moved everything back into loop() and played around a little. I have a sound effect playing and I have AsyncWifiManager also checking for AP access. As soon as I access the AP, the sound starts to glitch. Basically, the library I am using needs to take over full time to feed I2S.

OK, so we have two cores. By default the arduino core runs on core 1 and the RF stack runs on core 0. So I switch AsyncWifiManager to core 0 and leave the sound player on core 1. But it still glitches whenever I access the AP. Why? Who knows? Probably there are lower-level components that are making assumptions about which core they should be running on.

Now I am sure that I could write a sound library, that has input and output queues, that cause a task to yield if they are full and that are drained under interrupts. Maybe there are even some hardware modules I could use that have built-in queues so I don’t have to worry about timing on the ESP32. But at that point, why bother? I’m not doing this to improve sounds streaming on the ESP32. I just want to play some sounds on my clocks.

Is the ESP32 a case of a solution looking for a problem? Did they just throw everything they thought might be fun at it? Capacitive touch? You got it. I2S? You got it. Dual core? You got it. Why does the software support seem like so much of an afterthought? Kudos to me-no-dev and the Arduino core implemented on top if esp-idf, but Espressif really need to be more on top of this.

Divergence Meter Battery Mod

The divergence meter I built can be powered by an internal 9V battery. However, a regular 9V battery doesn’t last very long (between 10 and 45 minutes). LiPo 9V batteries last much longer – over 3 hours, though they aren’t really 9V as they are actually two regular LiPo batteries in series – so the actual voltage is at most 8V, dropping to 6V just before the power runs out.

However, that still means that you have to open the clock up to change the battery when it is out of power. A better alternative would be to include a power-sharing charger inside the case, so that the LiPo will charge up when the clock is plugged back into the power. A quick(ish) search of the internet turned up this 9V LiPo charger module, which is small enough to fit inside the case along with the battery. So that gave me the charger, but I needed to add the load-sharing part. A load-sharing charger will use some of the input power (input from the wall adapter) to charge up the battery, while the clock runs off the rest. It also needs to disconnect the battery from the clock as long as the clock is plugged in to the wall adapter, but instantly connect the battery to the clock as soon as it is unplugged from the wall adapter. This is actually pretty easy, and my circuit is basically the one described here.

Here is the basic circuit:

Load Sharing Charger

The load-sharing part is inside the dashed box. Here is what my implementation of this looks like. I used a surface-mount MOSFET, because I happened to have one already:

Load Sharer

This works as follows:

  1. As long as there is power on Vin, the P-channel MOSFET (Q1) will be turned off, preventing any power from being discharged from the battery and also preventing any power from Vin being directly applied to the positive terminal of the battery. Power from Vin will flow to V+ through the Schottky diode (D1).
  2. While there is power on Vin, the TP5100 module will also be able to charge the battery, if necessary.
  3. As soon as power is removed from Vin, the 100K resistor (R1) will pull Vin to ground, which will turn on the MOSFET allowing current to flow from the battery to V+.
  4. This is why the Schottky diode (D1) is needed. It stops the gate of the MOSFET being pulled high again by the battery, which would turn it off, etc. The only reason that D1 is a Schottky diode, is to minimize the voltage drop from Vin to V+.
  5. The LED (D2) shows when the battery is being charged.

I checked it all out before stuffing it into the case:

Load Sharing Charger

To integrate this with the clock, you have to cut the track from the Vin pin on the barrel connector (power in), then wire that to the Vin connector on the battery charger. Then wire V+ from the load-sharing board to the other side of the track that you cut – just find an easy place on the board to do that.

To re-use the existing switch, remove the optional diode (if you installed it) and just wire the switch up as shown in the circuit diagram above.

Finally, if you choose to use the charging indicator LED, you will have to drill a hole in the case somewhere to install it.

Here it is in the case:

Charger in the Case

View Showing LED Placement

I took a time-lapse video of the clock running off battery. As you can see, it lasts a little over three hours:

Display the Time on an Old Frequency Counter

A while back I bought an old frequency counter that has a Nixie tube display. It is a Japanese SF-87A made by the Sansei Electronics company. I bought it for the tubes – eight CD66 – however, when it arrived, it was in very good condition and worked just fine, so I decided not to harvest the tubes from it. I subsequently used it to display the frequency of my Nixie power supply, but that was just looking for an excuse, I didn’t actually need to use it for that:

Displaying the frequency of my Nixie power supply.

This left me in a bit of a quandry, but not too long ago Alic Loeliger suggested on the Nixie Clocks Fan Page on Facebook that I could get it to display the time by just sending the right number of pulses to it in a given time. This was such a simple idea that I had to try it.

The maximum gate time of the counter is 1 sec, and the largest number I would need to display would be 125959 (aka 12:59:59) for a 12-hour clock, or 235959 (aka 23:59:59) for a 24-hour clock. In other words, I would need to generate (at most) either a 126KHz signal, or a 236KHz signal. As I was doing this in software, because I am a software engineer, I went for the lower of the two numbers – I wanted to make sure that one of the processors I had available would be able to do it. I had a choice of an Arduino Uno, or an ESP8266. I ended up going with the ESP8266, which I programmed using the Arduino ESP8266 toolset.

At first, I split the number of pulses evenly over the gate time. i.e. if I needed to display 10:00:00, I would send send one pulse every 0.00001s. Then I realized that the counter just counted the number of pulses that occurred within the gate time, then divided that count by the gate time to get the frequency.  In other words, all I had to do was generate the right number of pulses in less than one second. However, when I tried this, it didn’t work – I assume the pulses were too close together for the counter to detect them. So, back to sending them spread over the gate period.

The result is kind of, sort of OK. The higher the frequency gets, the less able it is to accurately display the seconds – there is some ’rounding’ involved. Here is a video of it displaying 03:07:21 – note I have no control over the placement of the thousand separators:

Here is the source code:

#include "Arduino.h"
#include 

// Use pin 0
#define PIN 0

volatile unsigned long scaledPeriod = 0;
unsigned long oldMicros = 0;
unsigned long counter = 0;

/*
 * generate the pulse train in a timer interrupt. The function uses
 * the period, rather than the frequency. This is in 1/100,000,000s
 * units
 */
const int counterPeriod = ESP.getCpuFreqMHz() * 10;

void ICACHE_RAM_ATTR isr() {
    uint32_t ccount;
    __asm__ __volatile__("esync; rsr %0,ccount":"=a" (ccount));
	timer0_write(ccount + counterPeriod);

	unsigned long newMicros = micros();
	// micros is millionths of a second. Our period is in 
	// 100,000,000 of a second, so we multiply the difference
	// by 100
	unsigned long diff = (newMicros - oldMicros) * 100;
	if (scaledPeriod > 0 && (diff > scaledPeriod)) {
		digitalWrite(PIN, HIGH);
		oldMicros = micros();
	} else {
		digitalWrite(PIN, LOW);
	}
}

void setup()
{
	// Set to some arbitrary time
	setTime(3, 7, 20, 1, 2, 2018);
	pinMode(PIN, OUTPUT);
	timer0_isr_init();
	timer0_attachInterrupt(isr);

	timer0_write(ESP.getCycleCount() + counterPeriod);
}

unsigned long  oldTime = 0;

void loop()
{
	unsigned long newTime = hour() * 10000 + minute() * 100 + second();

	if (newTime != oldTime) {
		oldTime = newTime;
		if (newTime != 0) {
			// Need a numerator that is >> newTime could be
			scaledPeriod = 100000000/newTime;
		} else {
			scaledPeriod = 0;
		}
	}
}

This code has some problems! As newTime gets larger, scaledPeriod loses more and more precision. Furthermore, because the interrupt routine is called at a fixed periodicity, it is essentially sampling the waveform. Both of these things mean that the displayed time gets less accurate as the absolute size of the number being displayed is increased.

In retrospect, it would be better to adjust the periodicity of the interrupt routine to match that of the waveform we are trying to generate. That would be an improvement, but we would still have problems caused by the rather coarse granularity of the timer we have available.

Still, the result isn’t too bad.