Need Help With a Basic Arduino Nixie Clock.

I'm trying to design a basic nixie clock with an arduino.

Some pictures of the setup.



The hardware consists of an 74HC595 shift register and two k155id1 for each of the nixies. The power supply for the nixies is based on the 555 timer and gives the required 170v.

The code I'm using is very basic which could be causing the problem I'm having.

int clockPin = 12;
int latchPin = 8;
int dataPin = 11;
int y = 0;
int x = 0;
int Seconds = 0;
int Minutes = 0;
int Hours = 0;

void setup() {

  //Serial.begin(9600);
  pinMode(dataPin, OUTPUT);       
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  
}
void loop() {

//Serial.println (Seconds);
//Serial.println (Minutes);
//Serial.println (Hours);


Seconds = x;
  
 y = ++x; // counts from 0 to 59.

if (y > 59) // when seconds reach 59, Seconds go back to 0, 1 is added to minutes.
x = 1, Minutes ++;

if (Minutes > 59) // when minutes reach 59, 1 is added to hours, minutes go back to 0.
Minutes = 0, Hours ++;

if (Hours > 23) // when hours reach 23, hours rollover to 0.
Hours = 0;



digitalWrite(latchPin, LOW);
shiftOut(dataPin,clockPin,MSBFIRST, Seconds); // shifting out Seconds only.
digitalWrite(latchPin, HIGH);

delay(1000); // one second delay to make it work like a clock.
}

The program kinda works , But when the nixies try to rollover from 9 to 0 there is a 5 second delay before the next part of the counting cycle continues . The counting only reaches 39 before rolling over to 0 again. This problem does not happen when digits are viewed though the serial panel so I think it might be something to do with the shift register.

I also have a video, might give you a better idea of what I'm taking about.

TRY THIS OUT

void loop()
 {
Seconds =Seconds +1;
if(Seconds >=59)
{
Minutes =Minutes +1;Seconds =0;
} 
if( Minutes>=59)
{
 Minutes =0;Hours =Hours +1;
}
if (Hours > 23)
{
Hours =0;
}

}

Thank you for your suggestion, but their is still a 5 second delay and it only counts to 39 still.

harry89:
The hardware consists of an 74HC595 shift register and two k155id1 for each of the nixies. The power supply for the nixies is based on the 555 timer and gives the required 170v.

  1. You should only need 1 k155id1 for each nixie. They have 10 outputs, and a nixie has 10 pins to define a number.
  2. The k155id1 is a BCD to Decimal decoder, and you are sending it binary. You need to convert the value from binary to BCD
  3. You are sending an int, but only a byte is being shifted out. Declare them as byte.

To convert binary to BCD:

  if ((Seconds & 0x0f) == 10) {
    Seconds += 6;    //  0000 1010 + 6 = 0001 0000, which is BCD 10
//    if (Seconds == 0xA0) {  // if  Seconds > BCD 90 (you won't need this if because you won't go above BCD 59
//      Seconds = 0;
//    }
  }

Follow AMPS-N's example, but correct it to:

void loop()
{
  Seconds = Seconds +1;
  if(Seconds > 59)
  {
    Minutes = Minutes + 1;
    Seconds = 0;
  } 
  if( Minutes > 59)
  {
    Minutes = 0;
    Hours = Hours + 1;
  }
  if (Hours > 23)
  {
    Hours =0;
  }

  // convert anything you want to shift out to BCD
  // shift it out
}

Thank you, That works!!. no more delays!!

Sorry error on my part the nixies have one BCD Converter each.
I thought just sending it binary would be fine, to my mind it was just a series of high's and low's.

Does this explain why it was only counting to 40 before going back to 0?
So you just add 6 onto the binary figure to convert to BCD?

I had to change seconds to 90 so it would count to 59 without going back to 0.

Thanks again!!

harry89:
Does this explain why it was only counting to 40 before going back to 0?

That was because of your if statement, rolling over from 59 to 0, and assigning 1 to x., I think. You were all out of whack between what the time should have been, and what it actually sent out to the shift register.

So you just add 6 onto the binary figure to convert to BCD?

Well, yes and no. You add 6 if the value in the low-order 4 bits seconds is 10. You are converting from binary to BCD. BCD stores the 10s position of a decimal number in the high-order 4 bits (high nybble). and the units position in the low nybble. So a value of 12 in binary is stored as 0x0c, but in BCD, it is stored as 0x12. Looking at the bits, it's:

  Binary  0000 1100
  BCD     0001 0010

I had to change seconds to 90 so it would count to 59 without going back to 0.

Nonono! Leave it rolling over when it's > 59, and only convert to BCD when it's about to be shifted out, but use another variable to hold the value to be shifted, so it doesn't destroy your time variables. This should be all you need for counting and displaying the seconds. Duplicate the BCD conversion and the shiftout for the minutes and hours, and you're done.

int clockPin = 12;
int latchPin = 8;
int dataPin = 11;

byte Seconds = 0;
byte BCDSeconds
byte Minutes = 0;
byte Hours = 0;

void setup() {
//  Serial.begin(115200);
  pinMode(dataPin, OUTPUT);       
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
}

void loop() {
  delay(1000);
  
  Seconds = Seconds + 1;
  if(Seconds > 59)
  {
    Minutes = Minutes + 1;
    Seconds = 0;
  } 
  if( Minutes > 59)
  {
    Minutes = 0;
    Hours = Hours + 1;
  }
  if (Hours > 23)
  {
    Hours =0;
  }

  if ((Seconds & 0x0f) == 10) {
    Seconds += 6;    //  0000 1010 + 6 = 0001 0000, which is BCD 10
  }
  
  if ((Seconds & 0x0f) == 10) {
    BCDSeconds = Seconds + 6;    //  0000 1010 + 6 = 0001 0000, which is BCD 10
  }

  digitalWrite(latchPin, LOW);
  shiftOut(dataPin,clockPin,MSBFIRST, BCDSeconds); // shifting out Seconds only.
  digitalWrite(latchPin, HIGH);
}

That second part of the code does not seem to work., the bytes will not shift-out.

if ((Seconds & 0x0f) == 10) {
    BCDSeconds = Seconds + 6;

I've had a play around and the only real solution I can find in by changing the minutes and seconds to 90 and the hours to 35
in the if statement . I did not see any real problems when shifting out the seconds, minutes and hours. I just changed the delay to speed things up.

I don't see how using another variable with change anything. The result will be that 59 is still way over due to the BCD conversion adding multiple sixes before it changes back to 0. You can see this though the serial panel.

I've added a push button to change the minutes, that was easy enough.
here's how the code looks now. I plan to add buttons for setting seconds and hours too.

Another feature I would like to implement would be a "slot machine" effect to randomize the digits on all the nixies to prevent
something called "cathode poisoning" I may even use a more accurate method of keeping track of seconds. as the current its seen as not accurate.

int clockPin = 12;
int latchPin = 8;
int dataPin = 11;
byte Seconds = 0;
byte Minutes = 0;
byte Hours = 0;
const int SetTimeButton = 4;

void setup() {

  Serial.begin(9600);
  pinMode(dataPin, OUTPUT);       
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(SetTimeButton, INPUT);
  
}
void loop() {

//Serial.println (Seconds);
//Serial.println (Minutes);
//Serial.println (Hours);

int buttonState = digitalRead(SetTimeButton);

if (buttonState == HIGH) {  
Minutes =Minutes +1;
delay(200);
}
Seconds = Seconds +1;

if(Seconds >=90)
{
Minutes =Minutes +1;Seconds =0;
} 
if( Minutes>=90)
{
 Minutes =0;Hours =Hours +1;
}
if (Hours > 35)
{
Hours =0;
}
if ((Seconds & 0x0f) == 10) {
Seconds += 6; 
}
if ((Minutes & 0x0f) == 10) {
Minutes += 6; 
}
if ((Hours & 0x0f) == 10) {
Hours += 6; 
}
digitalWrite(latchPin, LOW);
shiftOut(dataPin,clockPin,MSBFIRST,Minutes); 
digitalWrite(latchPin, HIGH);

//Serial.println (Minutes);
//Serial.println (Hours);

if (buttonState == LOW) { 
delay(1000);

}
}

harry89:
That second part of the code does not seem to work., the bytes will not shift-out.

I apologize. I made a mistake in cutting/pasting code, and left something in there that will mess up the seconds

Here's the code I posted most recently, with the problem part removed. It will work. The problem was that I left in the code that modified Seconds in the conversion to BCD.

int clockPin = 12;
int latchPin = 8;
int dataPin = 11;

byte Seconds = 0;
byte BCDSeconds
byte Minutes = 0;
byte Hours = 0;

void setup() {
//  Serial.begin(115200);
  pinMode(dataPin, OUTPUT);       
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
}

void loop() {
  delay(1000);
  
  Seconds = Seconds + 1;
  if(Seconds > 59)
  {
    Minutes = Minutes + 1;
    Seconds = 0;
  } 
  if( Minutes > 59)
  {
    Minutes = 0;
    Hours = Hours + 1;
  }
  if (Hours > 23)
  {
    Hours =0;
  }

  if ((Seconds & 0x0f) == 10) {
    BCDSeconds = Seconds + 6;    //  0000 1010 + 6 = 0001 0000, which is BCD 10
  }

  digitalWrite(latchPin, LOW);
  shiftOut(dataPin,clockPin,MSBFIRST, BCDSeconds); // shifting out Seconds only.
  digitalWrite(latchPin, HIGH);
}

Messing with the rollover times is absolutely the wrong approach.

Another feature I would like to implement would be a "slot machine" effect to randomize the digits on all the nixies to prevent something called "cathode poisoning" I may even use a more accurate method of keeping track of seconds. as the current its seen as not accurate.

Randomizing the digits when the time changes is a good idea, as long as you don't do it too often. Every minute, ot half-minute, cycling them for a second or so, would be seen as a nifty effect, too often looks like a bug.

Your inaccuracy stems from two things.

  1. The Arduino is only as accurate as the accuracy of the crystal.
  2. you are doing things, then calling delay(1000). The things you do take some time, and the delay should be delay(1000 - (the time taken by everything else you're doing));

A real-time clock is really the best way to get a reasonably accurate clock. They are dirt-cheap (a lot less that 6 nixies!). You can get them as a module with a battery for backup, so even if you lose power, you can recover the time. when it comes back on.

I picked up three regular ones from China (via eBay) for $2.00 each, and one precision, temperature compensated one for about $4.50

No sorry,
that code still does not seem to work , well sort of but it makes the digits change very slowly about 5 seconds. The bottom tube does not get any digits, 0 to 9. just the top one 1 to 5, i f you understand me.

I copied and pasted your code directly into a new arduino window and uploaded it.
I have actually have a real time clock module with an eprom . I thought it would be more interesting to make clock a program from scratch.

Just though it would be something else to add into the design of the clock. Theirs already enough 5V logic in the full 6 digit static nixie clock without going to multiplexing like everyone seems to use.

I'm wanting to go the whole 9 yards with this clock etching circuit boards and embedding the ATmega chip off the Arduino into my design.

I would think the ATmega chip would be able to run off an 3.3v lithium cell , I would use that as a battery backup system.

harry89:
No sorry,
that code still does not seem to work , well sort of but it makes the digits change very slowly about 5 seconds. The bottom tube does not get any digits, 0 to 9. just the top one 1 to 5, i f you understand me.

That'll teach me not to post code without testing!

OK... this time for sure! Tested. Works like a charm.
The output to the Serial Monitor shows the Seconds value in the first column, and the Hexadecimal value of BCDSeconds.
I also moved the display part to the top, so that it will start at zero. The delay() is right after the display part, currently set to 300 ms.

int clockPin = 12;
int latchPin = 8;
int dataPin = 11;

byte Seconds = 0;
byte BCDSeconds;
byte Minutes = 0;
byte Hours = 0;

void setup() {
  Serial.begin(115200);
  pinMode(dataPin, OUTPUT);       
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
}

void loop() {
  // a better conversion, and it converts all, not just every ten
  BCDSeconds = (Seconds / 10 * 16) + (Seconds % 10);
  
  Serial.print(Seconds);
  Serial.print("    ");
  Serial.println(BCDSeconds,HEX);
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin,clockPin,MSBFIRST, BCDSeconds); // shifting out Seconds only.
  digitalWrite(latchPin, HIGH);

  delay(300);  // Changeto appropriate value
  
  Seconds = Seconds + 1;
  if(Seconds > 59)
  {
    Minutes = Minutes + 1;
    Seconds = 0;
  } 
  if( Minutes > 59)
  {
    Minutes = 0;
    Hours = Hours + 1;
  }
  if (Hours > 23)
  {
    Hours =0;
  }
}

Yes!! That works great thank you!!

I like it more then just using an if statement it looks more elegant.
Do you have any advice on adding buttons to change the time?

Is what I was doing before ok? just advancing minutes ++

harry89:
Do you have any advice on adding buttons to change the time?

Is what I was doing before ok? just advancing minutes ++

Well, you can add buttons, but I would suggest adding at least four buttons:
One to go into "Set" mode.
One to select the two digits (hours, Minutes, Seconds) in turn, cycling though in order
You might want to flash these two digits so you know which ones you are changing
One to increment the number
One to decrement the number (optional)

You could call a separate funtion when the Set button is pressed, and stay in it until it is not pressed. While in that function, increment or decrement the time in the two digits you are changing, placing the value (in binary) into the appropriate global variable (Hours, Minutes, Seconds), and when you let the Set button go, you return from the function and the time carries on from where you set it.

After you get that working, an optional nicety would be to allow holding the increment or decrement button down, and repeat the operation; fairly slowly at first, then increasing the speed to a comfortable maximum.

You might want to eventually have an alarm, and using a function as above would be handy for setting the alarm. All you need do is call the function with an indication that it's for the alarm.

For these operations you waill probably want to look at debouncing your switches (ALL switches bounce, some more than others). See Nick Gammon's excellent switch tutorial at Gammon Forum : Electronics : Microprocessors : Switches tutorial. You might also want to look at the Blink Without Delay sketch in the IDE's Examples for ways to do repeating switches.

Don't be afraid to try things. Back up your current sketch, and continue working on it. Any time you run into big problems that you can't seem to reverse, grab another copy of the backup and try again.

Another good technique is to make a new sketch to try something. Try to keep in mind the structure of your working sketch, and write the the code you're trying with a view to being able to just copy the working parts to paste into your working copy.

I could use some ideas on how to get the digit to randomize at a given time,
not posted anything for a while sorry.

I've made some changes to the code instead of using the delay() function for the clock timing I've used millis() instead.
it's just an modified blink without delay sketch.

For randomizing the digits are start-up I'm using this code.

while(var < 200){
  randomMinutes = random(0,99);
  randomHours = random(0,99);
  var++;
  
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin,clockPin,MSBFIRST,randomMinutes);
  shiftOut(dataPin,clockPin,MSBFIRST,randomHours);
  digitalWrite(latchPin, HIGH);
  
  delay(20);

To try and get it to randomize when the minutes reach 59 I tried using a simple if statement but the digit just freeze at 59.
I'm not sure if the second shift out selection is necessary but the clock does not do anything if copy it into the first shift out statement. I think the random Minutes & random Hours variable is necessary as it would mess up the current time.

Here's the entire code as well.

long previousMillis = 0;        
long interval = 1000;  

int clockPin = 7;
int latchPin = 8;
int dataPin = 11;

byte BCDSeconds;
byte BCDMinutes;
byte BCDHours;


byte Hours;
byte Minutes;
byte Seconds;

byte randomMinutes;
byte randomHours;
byte var = 0;

void setup() {
   
Serial.begin (9600);

  pinMode(dataPin, OUTPUT);       
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);

}

void loop(){
  
  unsigned long currentMillis = millis();
 
  if(currentMillis - previousMillis > interval) { 
  Seconds  = Seconds  + 1;
  previousMillis = currentMillis;   
  
  // a better conversion, and it converts all, not just every ten
  BCDSeconds = (Seconds / 10 * 16) + (Seconds % 10);
  BCDSeconds = (Seconds / 10 * 16) + (Seconds % 10);
  BCDMinutes = (Minutes / 10 * 16) + (Minutes % 10);
  BCDHours = (Hours / 10 * 16) + (Hours % 10);
  
  
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin,clockPin,MSBFIRST, BCDMinutes);
  shiftOut(dataPin,clockPin,MSBFIRST, BCDHours);
  digitalWrite(latchPin, HIGH);
  
}
  if(Seconds > 59){
  Minutes = Minutes + 1;
  Seconds = 0;
  } 
  if( Minutes > 59)
  {
  Minutes = 0;
  Hours = Hours + 1;
  }
  if (Hours > 23)
  {
    Hours = 0;
  }
  
  while(var < 200){
  randomMinutes = random(0,99);
  randomHours = random(0,99);
  var++;
  
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin,clockPin,MSBFIRST,randomMinutes);
  shiftOut(dataPin,clockPin,MSBFIRST,randomHours);
  digitalWrite(latchPin, HIGH);
  
  delay(20);
 
}
  
}
// convert decimal to BCD
uint8_t dec2bcd (uint8_t dec)
{
    return (dec / 10 * 16) + (dec % 10);
}

// convert BCD to decimal
uint8_t bcd2dec (uint8_t bcd)
{
    return (bcd / 16 * 10) + (bcd % 16);
}

I'm not sure what to do with the code you given me.
Can you be more specific? Thank you.

harry89:
I'm not sure what to do with the code you given me.
Can you be more specific? Thank you.

Well, you don't need BCD to Decimal, and the Decimal to BCD is already in the code I supplies.

Didn't think so was a little confused.

I figured out how to get the digits to randomize on the hours myself, just as well really learn more that way I guess.
The code needed was this.

if (Minutes > 58 & Seconds >= 55 && Seconds <= 59){
  BCDSeconds = random(0,99);
  BCDMinutes = random(0,99);
  BCDHours = random(0,99);
  delay(10);
  
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin,clockPin,MSBFIRST, BCDMinutes);
  shiftOut(dataPin,clockPin,MSBFIRST, BCDHours);
  digitalWrite(latchPin, HIGH);

Going to work on the buttons to control the time next. I think I'm going to look into hardware debouching rather then in software. Could always try both of course. I would like to use only one button to change the time by holding it down at specific intervals.