Smart Nixie Tube Display

I built a nixie tube display that can display anything from the web.

Published on: Wednesday, March 13th 2024, 11:19 pm

The finished product with a counting program

Introduction

If there's any display technology that instantly screams "retro", it's the nixie tube. It has a 'steampunk' feel or maybe some kind of 'retro-modernism'. It'll probably surprise you then, that they were invented in the 1950's.

Fun fact: nixie tubes are shown in Oppenheimer even though it took place more than 10 years before they were invented!

This is definitely an unpopular opinion, but I actually like the aesthetic of breadboard projects. They have a 'prototype-y' style. My primary inspiration was the divergence meter from Stein's Gate.

He uses a protoboard here which is certainly cleaner, but I find the solder connections on the bottom are harder to keep neat.

Build log

Parts list

Circuit

I referenced GreatScott's guide. Unfortunately, the schematic has a few wiring inconsistencies between the nixie tubes and driver. I'll upload a corrected one when I get a chance. Make sure to consult the datasheets for the right connections.

Step-by-step

I started with putting the breadboards together and planning the circuit. The Pico takes up the half-size breadboard and the tubes/drivers will be on the full-size breadboard. Each tube takes 7 rows and each driver takes 8 rows. For 4 tubes and 4 drivers, that's 60 pins in total - just enough to fit!

2

Next, I wired up the first tube to its driver. I used solid core wire and cut each connection depending on the length needed. It's a time taking process, but well-worth it if you want a neat breadboard.

So, why not use jumpers? Jumpers are awesome for prototyping, but for something more permanent, solid core is reliable and keeps the whole circuit flat and neat.

The input from the wall adapter (12V) goes into the top rail of the half-size. The high-voltage power supply will take 12V and convert it into 170V in the top rail of the full-size. The voltage regulator sends 5V in the bottom rail of both breadboards. 5V is used for the Pico and drivers.

3

Let's plug in 12V power and the 170V power supply. I connected a 10k resistor from the 170V rail to the tube (it's hidden behind). Since the driver needs a valid input to allow the current through, I connected pin D on the driver to ground to test the circuit.

5

Looking good! I then repeated the same wiring for the next 3 tubes.

6

Now, wire up the input pins on the drivers to the Pico. Make sure to connect them to GPIO pins. You can find a pinout diagram in the documentation.

7

Let's program the Pico and we're done!

Programming

I recommend MicroPython for the Pico W. There is a C/C++ SDK for the Pico, but it is not easy to make HTTP requests. Generally, MicroPython is simpler if speed isn't a big deal for you. You'll have to flash the Pico with the MicroPython image and from there, you can use Thonny to upload programs easily.

Set up your pin list. Use the GPIO numbers, not the physical pin numbers.

PIN_LIST = [
    # A   B   C   D  (GPIO pin numbers)
    [28, 26, 22, 27],   # 1's
    [20, 18, 17, 19],   # 10's
    [15, 13, 12, 14],   # 100's
    [10,  8,  7,  9],   # 1000's
]

Use a helper function to turn this into actual pins.

def pin_list_to_pins(pin_list):
    for i in range(len(pin_list)):
        for j in range(len(pin_list[i])):
            pin_list[i][j] = Pin(pin_list[i][j], Pin.OUT)

Now, we can create some functions to set each digit individually, and as a whole.

# Sets the given pins to the 1's digit of value.
# pins - [A, B, C, D]
# value - 0 to 9
def set_digit(pins, value):
    pins[0].value(value % 2)
    value //= 2
    pins[1].value(value % 2)
    value //= 2
    pins[2].value(value % 2)
    value //= 2
    pins[3].value(value % 2)


# Sets the given pins to len(pins_list) digits of value.
# pins - [ [A, B, C, D], ... ]
# value - integer
def set_number(pins_list, value):
    for digit in pins_list:
        set_digit(digit, value % 10)
        value //= 10

Now for a simple counting program, I added a loop that counts up until 9999 and then goes back to 0. Here's the full test program.

from machine import Pin, Timer

PIN_LIST = [
    # A   B   C   D  (GPIO pin numbers)
    [28, 26, 22, 27],   # 1's
    [20, 18, 17, 19],   # 10's
    [15, 13, 12, 14],   # 100's
    [10,  8,  7,  9],   # 1000's
]


def pin_list_to_pins(pin_list):
    for i in range(len(pin_list)):
        for j in range(len(pin_list[i])):
            pin_list[i][j] = Pin(pin_list[i][j], Pin.OUT)


# Sets the given pins to the 1's digit of value.
# pins - [A, B, C, D]
# value - 0 to 9
def set_digit(pins, value):
    pins[0].value(value % 2)
    value //= 2
    pins[1].value(value % 2)
    value //= 2
    pins[2].value(value % 2)
    value //= 2
    pins[3].value(value % 2)


# Sets the given pins to len(pins_list) digits of value.
# pins - [ [A, B, C, D], ... ]
# value - integer
def set_number(pins_list, value):
    for digit in pins_list:
        set_digit(digit, value % 10)
        value //= 10


if __name__ == "__main__":
    
    pin_list_to_pins(PIN_LIST)

    for digit in PIN_LIST:
        for pin in digit:
            pin.value(0)

    timer = Timer()

    count = 0

    def countup_callback(t):
        global count
        set_number(PIN_LIST, count)
        if count >= 9999:
            count = 0
        else:
            count += 1

    timer.init(freq=5, mode=Timer.PERIODIC, callback=countup_callback)

Conclusion

This looks so cool. I'll probably set it to show the minutes that have passed since my last League game haha. I highly recommend this project if you aren't scared of higher voltages. Thanks for reading!