Close
0%
0%

Just Another Nixie Clock

An arduino based multiplexed, cross-fading IN-14 Nixie tube clock.

Similar projects worth following
An IN-14 Nixie clock, multiplexed in groups of 3 (hours, minutes, seconds), which functions by switching on and off the anode with high side switch transistors.

The code is written on a stand alone Arduino, an ATMEGA328P chip in a 28 pin DIP configuration. Using timer interrupts, and direct port writes, the clock is capable of smooth cross-fading of the digits. There is a custom library for debouncing buttons. Current software todo: create menu to set the time, date, and other settings such as cross-fade speed, sleep/wake up functions, etc.

Power is supplied through a 12 VDC barrel jack, and it steps up the voltage to 170 volts using the kit from Threeneuron's Pile o'Poo, and it steps it down to 5V for the MCU. Since the clock is designed to be off most of the time, time is kept with a DS1307 RTC, with 56 bytes of NVRAM for config settings.

The code/schematics will be available on github via a GPL3 license.

Crossfade demo:

  • Button code working - buttons not working

    Kevin Mossey03/05/2023 at 05:19 0 comments

    Finally got all the button code working (at least initially) and put it all together and... the cheap keyswitches I found in my ex's box o'whatever components don't work well at all.  I took one apart and found the keyswitch I was using is a terrible design and just can't be relied on.  I bought some Cherry MX browns and plan to redesign the button mount to work with the slightly different form factor switch and 3d print that soon.

    One of the keyswitches worked though, so currently I'm using that as the Settings button.  This functions to wake up the clock. I can configure the wake time anywhere from 1-600 minutes, I've got it set to 60 minutes to save tube life. The brightness is also configurable - 8 different levels of it.

    I'm storing the wake time and the brightness in the RAM of the DS1307 so that even when you power off the clock it will reload the settings.

    Button functions:

    • Press settings button to wake up clock.
    • Pressing the settings button while awake enables you to set (in order):
      • Hours
      • Minutes
      • Seconds
      • Year (2000-2099)
      • Month
      • Day
      • Brightness (8 levels)
      • Wake period (How long the clock stays on until it auto-shuts off)
    • Moving to the next setting saves the value.  Not pressing any buttons for 10 seconds cancels the most recent changes and exits settings mode.
    • Press and hold the settings button for 3 seconds to put it back to sleep early.
    • Use the advance (12:05) and decrease (11:55) to move forward and backwards through values.

  • State Machine & Timing updates

    Kevin Mossey12/08/2022 at 04:09 0 comments

    Creating a state machine turned out to be a harder challenge than I originally thought. Between my new job and moving, starting to date again, moving again, then moving again, I didn't think I'd get it done!


    The idea behind building a state machine was both a learning opportunity for me and to optimize readability of the code. The interesting thing about it was it exposed some timing issues which meant the crossfade wouldn't work as smoothly as it should. 


    As the code is written now, it will wait until the proper point in a cycle to start the crossfade so that the full process executes properly.  It was incredibly minor, but during the debugging process I discovered that instead of starting on the first phase of the cycle, it might actually start the crossfade on the third or seventh step of the cycle causing a very jagged crossfade.

  • Buttons done! And other overdue updates

    Kevin Mossey12/19/2021 at 04:43 0 comments

    So life did what life does and got in the way of this project.  I started a new job in a new state and ended a 10 year relationship with my ex, but this project is still not dead!

    Right before I left the state I was living in I managed to mill out the Australian Red Gum burl I'm using as a body.  This is a HARD wood and I definitely needed to cut it on a slower speed with the saw - it was VERY smokey.  I made another grievous mistake and didn't true the sides before making all the holes and milling out the center and so it slipped between holes.  They're slightly offset but it's not noticeable.  Of course the test piece I used was already trued so I didn't have any problems in the prototype.

    I used linseed oil on the wood and I'm really really happy with how it looks.IAnd the buttons!  I haven't mounted them in the clock, but I bought a piece of brass pipe from a big box home improvement store and turned it on my lathe before I moved.  I FINALLY got around to trimming the watch hands I got from my former FIL down, filling the turned brass with FIMO (polymer clay) and placing the hands, watch gear, and pressing the key switch into the back and baking them.  Voila!  30 minutes later, custom buttons.  Again, really happy with how this worked out.  The gear is the "settings" button, the 11:55 is the decrease button, and the 12:05 is the increase button.

    I really like the analog buttons on the digital clock.  Don't think I've seen it before!

  • Body prototype complete

    Kevin Mossey07/27/2021 at 03:24 0 comments

    I tend to make mistakes on my first pass of something - case in point, the PCB I designed had the resistors for the real time clock DS1307 chip acting as pull down resistors, and they should have been pull up resistors.  (In plain English I should have connected them to the 5v line instead of ground). It's easily enough fixed and I have the clock working on the board, even if I have a bit of an ugly corner of the board...

    And I have the clock assembled and working:

    Finally, I milled out the test block (seen sitting on top of what will be the clock body for real) to ensure that my CAD design for the body worked:

    Overall I'm pretty happy with the design - I need to figure out I want to mount it inside though as my original idea of having mounting brackets won't work. I tried neodymium magnets but those interfered with the inductor on the power supply. Smaller ones seem to work ok though, so I may go with that. Still much to think about.

    There are definitely tweaks to be made before I cut up the expensive piece of wood though.  I'm glad I did the test piece first!

  • PCB ordered!!!

    Kevin Mossey06/08/2021 at 22:15 0 comments

    I'm skipping over the post I was going to do on timer interrupts to just fawn over the fact that I finally ordered my first ever custom PCBs, which are for this project.  I had to bite the bullet and order the board because I'm designing the body in parallel, and I'm going to need the guts of the clock done and fully assembled before I can finalize the design of the body.  Sent this to oshpark.com, I should have it in less than a month.  While their preview images are purple, I went with the After Dark motif.

    I spent a lot of time working on making it a piece of art - I can see why Steve Wozniak redid the Apple motherboard just to get it slightly better.  This is all through-hole tech, no attempting surface mount soldering for board number 1!  The 8 jumpers at the bottom correspond to the 8 pins on the Arduino I have not yet used.  I know I'll be using 4 of them for signals from the buttons, but which ones exactly I'm not sure of yet, so this gives me the flexibility to get the board made now and figure out the buttons later.

  • Digit fade-in, fade out

    Kevin Mossey05/26/2021 at 14:19 0 comments

    In the last log I talked about having gotten the anode switching working, but the Arduino's built in pw modulation doesn't run at a frequency that is fast enough to actually do a proper crossfade.

    Nevertheless, when not multiplexing (aka direct drive of each digit) the built in pw modulation is enough to fade digits out, switch to the next one, and fade it in.

    The code I used to do this was something like this: (again, recreated from memory after the fact and I haven't debugged it so it might not work).  This would be the replacement of the for loop from the last log.

      for (int x=0; x<9; x++) {
        for (int pw=250; pw>0; pw=pw-25) {
          analogWrite(anode, pw);
          delayMicroseconds(10);
        }
        analogWrite(anode, 0);      // turn off anode before changing digit
        update_tube(x);
        for (int pw=0; pw<250; pw=pw+25) {
          analogWrite(anode, pw);
          delayMicroseconds(10);
        }
        analogWrite(anode, 255);
        while (current_micros - step_start_time < 750)
        {
          current_micros = micros();
        }
      }

    Even though I'm not using this effect in my clock, I think it's beautiful.  Maybe in the next one!  Next up: replacing all the delayMicroseconds() and weird while{} loops with timer interrupts.

  • Crossfade attempt #1 - learning from failure

    Kevin Mossey05/21/2021 at 04:03 0 comments

    Once I got the digits displaying, I wanted to crossfade them.  This was something I had seen people talk about doing, and I saw videos of people successfully doing it, but I couldn't find examples of it being done.  I figured the Arduino had pulse width modulation on some pins, so I thought I could use one of those with the anode and increase the brightness on the new digit and decrease the brightness on the old digit.

    I wanted the bulb to transition over about a quarter second.  The following is just representative of what I originally wrote, which has been long since lost because I didn't have any version control set up yet as I was just trying different things, and not really trying to write a formal clock yet.  

    #define anode 3
    #define A 8
    #define B 9
    #define C 10
    #define D 11
    
    void setup() {
      pinMode(anode, OUTPUT);
      pinMode(A, OUTPUT);
      pinMode(B, OUTPUT);
      pinMode(C, OUTPUT);
      pinMode(D, OUTPUT);
    }
    
    void loop() {
    static int previous=9;
      for (int i=0; i<10; i++) {
        transition_tube(i, previous);
        delay(750);
      }
    }
    
    void transition_tube(int new_digit, int old_digit) {
      unsigned long step_start_time = micros();
      unsigned long current_micros = micros();
      int delay_array[] = {100,200,300,400,500,600,700,800,900};  
      int pw_array[] = {30,60,90,120,150,180,210,240,255};
      for (int x=0; x<9; x++) {
        analogWrite(anode, 0);      // turn off anode before changing digit
        update_tube(old_digit);
        analogWrite(anode, 255 - pw_array[x]);
        step_start_time = micros();
        while (current_micros - step_start_time < 1000 - delay_arr[x])
        {
          current_micros = micros();
        }
        analogWrite(anode, 0);      // turn off anode before changing digit
        update_tube(new_digit);
        analogWrite(anode, pw_array[x]);
        step_start_time = micros();
        while (current_micros - step_start_time < delay_arr[x])
        {
          current_micros = micros();
        }
    }
    
    void updateTube(int val)
    {
      digitalWrite(A, val & 1 ? HIGH : LOW);
      digitalWrite(B, val & 2 ? HIGH : LOW);
      digitalWrite(C, val & 4 ? HIGH : LOW);
      digitalWrite(D, val & 8 ? HIGH : LOW);
    }
    

    There are a few problems with what I originally did here.  What I was trying to do was turn the tube on with the new digit for 0.1ms at a pulse width of about 12%, then turn the tube on with the old digit for 0.9ms at roughly 88%.  But all I got was a hot mess as you can see with the video below, taken in slow motion on my phone.

    I learned a lot in troubleshooting the issue. I had assumed (yes, Mom, I know what happens when I assume something!) that it was working at some magical percentage of the 16MHz clock that was really fast, like in the microsecond range. When I started I didn't know that the Arduino's pulse width modulation through analogWrite() only works between about 490Hz to 980Hz. At it's fastest, though, it can only work to a resolution of 1.0ms, which means that my attempts to drive it at 0.1ms intervals was useless.  It just can't work for what I'm trying to do.  It also explains why the bulb appears to have no fade in or out - it just flickers between the old and new until it "pops" to the new digit. 

    While troubleshooting the awful flickering, I discovered that the digitalWrite() function is slow.  Like really slow.  About 20x times slower than direct port writes.  https://roboticsbackend.com/ has a fantastic explanation and benchmark comparison of doing direct port writes vs using the Arduino functions.  I love the fact that Arduino is easy to get started with, but I really love the fact that when you're ready to get more advanced, you can.

    I think another big problem with my approach was I was trying to replace delayMicroseconds() with my own loop so that I might be able to execute other code instead of freezing my program, but the loop once through took longer to execute than I intended the loop to be, so I couldn't get accurate timings.  

    I benchmarked the mostly-empty while loop (which only updates the current_micros), looping...

    Read more »

  • Driving the tubes, part 2

    Kevin Mossey05/19/2021 at 15:46 3 comments

    IN-14 nixie tube pin layout
    IN-14 nixie tube pin layout

    In the last post I mentioned you shouldn't change the digits of the nixie tube by changing the cathode.  So let's take a look at the anatomy of a tube and one possible method of turning them on/off.  A nixie tube has one anode - this is the positive terminal where the 170V comes from.  It also has around 10 cathodes, depending on the tubes.  By making a complete circuit through the anode and one cathode, you light up that particular digit.

    The IN-14 tube has two extra cathodes beyond the digits 0-9, one for the left decimal point and one for the right.  Other tubes, like the IN-12a or IN-8, only have the digits cathodes.

    There are a number of ways to turn the anode on and off.  A common one you will find online (and also the original kit this project started from) is using an NPN transistor and a PNP transistor together.  The baldengineer.com site has a really good write up on this, which helped me to understand what's going on here.  The takeaway is that this is NOT a Darlington pair - which is what people think two transistors connected like this are.  This is using an NPN transistor as a low side switch (meaning the switch, in this case the transistor, is on the low side of the voltage, after the load has used some of the power) to turn on a PNP transistor, which acts as a high side switch, (meaning the switch is on the high side of the voltage, before the load).

    The big advantage to using this method is that you can electrically isolate the 5V from the Arduino logic from the 170V the tubes need to light up.  Yes, this could be done with MOSFETs, optocouplers, or any of a number of other methods, but the high voltage MPSA42 and MPSA92 transistors I used have a 50MHz frequency, which is >3x faster than the Arduino (16MHz), and the tubes themselves can't really be driven much faster than 10-20kHz or else you get bleed through from previous digits. (Though if you don't care about ghosting, they can be driven REALLY FAST!) 

    NPN transistor driving PNP transistor driving load.


    Since I'm turning on each pair of tubes at the same time, there are two anodes being driven by one switch.  You could easily have one of these circuits for each tube but I was trying to save output pins.  "Logic" is the Arduino digital output, "Low_Anode" is the one's places tube and "High_Anode" is the ten's places tube.  Each tube normally uses only 2.5mA, so there is a current limiting resistor before the tube, though with the multiplexing it's a little higher at around 7-13mA.

    I looked and looked for some simple formulas to calculate the appropriate values of the resistors, but could not find anything. If you know of a good resource for this please leave a comment below.  Ultimately though, I ended up copying the values from the existing circuit because I knew that worked.

    Full disclosure: I'm not an electrical engineer.  I studied some basic circuits in college but it was only one class and then never touched it again until this project.  I copied the circuits I used from what's available online and tried to understand what's happening as best I could.  When I first built this circuit, it didn't work, and I spent days troubleshooting it.  Finally I realized that the transistor datasheet was garbage and it made it look like the order of the pins was backwards when facing the flat side, at least to my newbie eyes.  Once I flipped the transistors around it worked perfectly!  I can now turn off the anode before changing digits!

    Next up: Cross-fade attempt #1 which resulted in failure.

  • Driving the tubes, flipping digits, part 1

    Kevin Mossey05/17/2021 at 13:31 5 comments

    The Russian K155ID1 chip (which is equivalent to the SN74141) is a BCD (binary coded decimal) decoders with a weird pinout.  There are 16 pins, of which 2 are used for power/ground, leaving 14.  This gives you the outputs for digits 0-9 via just 4 inputs, which acting as a binary number gives you 16 possible inputs.  The truth table is easy enough - an input of 0 turns on the "0" output, an input of 1 turns on the "1" output, etc, through to an input of 9 (binary 1001) turns on the "9" output pin.  Values 10-15 as inputs have no output.

    Two warnings about invalid values: First, this blanks the Nixie tube on the cathode side, not the anode side.  If you are going to blank a Nixie tube you should do it by turning off the anode. Second, there is a pair of chips similar to the K155ID1/SN74141 that have similar specs, but the digital inputs 10-15 turn on pins 0-5.  My code is taking advantage of the invalid output to turn off a bulb at the cathode by sending invalid codes - even though I just said you shouldn't do this!  This is to save output pins on the 'duino and it's only for some of the setup pages - so it won't be an issue for very long.  If you come across the 5441A/7441A chips, the setup pages will act funky and you'll need to add more anode high side switches.

    Back to the pinout! Pins 3, 4, 6, and 7 are the inputs, but they don't correspond to binary places the way you might expect in order.  Instead, they are out of order:

    Chip Driver Pin
    3
    4
    6
    7
    Binary position you'd expect:
    2^0
    2^1
    2^2
    2^3
    Or maybe you'd expect this:
    2^3
    2^2
    2^1
    2^0
    ??? But this is how it actually is: ???
    2^0
    2^3
    2^1
    2^2

    My first go I just set up the chip on a breadboard, with jumpers going from digital out pins on the Arduino to the driver chip, and I criss-crossed the jumpers and just turned on pins 8-11 with digitalWrite() and a direct drive to the anode from the 170V power supply (through a 10K resistor!!!) and switched the value by changing the cathode.  Not ideal, but I can be impatient and wanted to see the pretty tubes light up.

    I wrote a simple program which was really something like this:

    #define A 8
    #define B 9
    #define C 10
    #define D 11
    
    void setup() {
      pinMode(A, OUTPUT);
      pinMode(B, OUTPUT);
      pinMode(C, OUTPUT);
      pinMode(D, OUTPUT);
    }
    
    void loop() {
    
      for (int i=0; i<10; i++) {
        digitalWrite(A, i & 1 ? HIGH : LOW);
        digitalWrite(B, i & 2 ? HIGH : LOW);
        digitalWrite(C, i & 4 ? HIGH : LOW);
        digitalWrite(D, i & 8 ? HIGH : LOW);
        delay(1000);
      }
    }

    And here's the video:


  • Powering the tubes

    Kevin Mossey05/16/2021 at 16:27 0 comments

    So the first thing you need with Nixie tubes is a power source capable of creating approximately 170V.  Working with high voltage and electrocuting myself doesn't seem like a good idea, so for this part of my project I'm going to let someone else do the hard work.  

    The HV kit from ThreeNeurons is right up the alley.  It's affordable, configurable to a range of voltages, and ships from the USA (which is where I am).  There is a ton of supporting documentation in case you have issues.  And it comes with pins so I can just put a header on my final board and run it from there!

    To test the power output, I started with a multimeter, but then I wanted to see a bulb light up - so I got one of my IN-8's and built a housing with an old floppy drive ribbon cable with sockets soldered on and drove it through a breadboard directly.  While it's 170V - the current for each bulb is only 2.5mA, which means the power draw is small and the ribbon cable can handle it no problem.  NOTE: Use a 10K resistor in between the 170V source and the anode pin. IN-8's are the same size as the IN-14 but with pins instead of wires and normal 5's instead of upside-down 2's.  They will be used in my next nixie project, where I focus more on the style than the electronics.

View all 11 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates