Arduino Microcontrollers

Language
English
Simpson College
Computer Science

Chapter 5: Steam Punk Clock

5.1 Code

Steam Punk Clock

Nixie tube clock

This is an Arduino UNO computer powered clock mounted in an old radio enclosure. The display uses a Nixie tube, which is a gas-filled tube filled with several glowing cathode elements in the shape of numbers. They were invented in 1955 and in use up through the early 1980's. DFRobot has been selling this Nixie module that make them super-easy to integrate with an Arduino.

Looking inside the enclosure, one can see the Arduino UNO with an LCD shield from Adafruit on top. Attached to the separate breadboard is a Chronodot. The Chronodot keeps a very accurate time, and includes a battery back-up if the clock is unplugged. The Chronodot also includes a thermometer which helps with the accuracy of the time, and provides a nice way to get indoor temperature.

The project pulls about 0.38 amps at 12.1 volts for about 4.5 watts. The Nixie tubes themselves have the voltage stepped up to 170 volts, so caution is required when working with the electronics.

The LCD screen in back allows a person to set the time, an alarm, and the color of the LCD screen. The color of the LEDs that illuminate the outside of the Nixie tubes changes depending on the time of day. When the alarm time hits, a small speaker beeps, an the lights on the nixie tubes flash.

Arduino control of nixie tube clock

Other related posts:

#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>
#include <Chronodot.h>
#include <NixieTube.h>

// Backlight Colors
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#define WHITE 0x7

// Define how many Nixie modules in serial
#define NIXIE_COUNT 4 

// Pin the speaker is on
#define SPEAKER_PIN 9

// Enumerations
enum Mode { NORMAL, SET_LCD_COLOR, SET_ALARM_1 };
enum TimeSetMode { MINUTE, HOUR };
enum ButtonState { UP, DOWN };

// Globals
Mode current_mode = NORMAL;
int currentLCDBacklightColor = WHITE;
int alarm1hour = 6;
int alarm1minute = 0;
int alarm1state = 0;
int alarmSetPos = 0;
int alarmSequence = 0;

int priorMinute = -1;

Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
Chronodot RTC;
NixieTube tube(11, 12, 13, 10, NIXIE_COUNT); // pin_ds, pin_st. pin_sh, pin_oe(pwm pin is preferred), NIXIE_COUNT

ButtonState priorSelectState = UP;
ButtonState priorUpState = UP;
ButtonState priorDownState = UP;
ButtonState priorLeftState = UP;
ButtonState priorRightState = UP;

void setup() {
  // Set up the LCD 
  lcd.begin(16, 2); // 16x2 LCD
  lcd.setBacklight(currentLCDBacklightColor);

  // Set up the clock
  RTC.begin();
  
  if (! RTC.isrunning()) {
   Serial.println("RTC is NOT running!");
   // following line sets the RTC to the date & time this sketch was compiled
   RTC.adjust(DateTime(__DATE__, __TIME__));
   }  
   

  // Set up the Nixie Tube
  tube.setBrightness(0xff);	// brightness control, 0x00(off)-0xff
  tube.display();  
}

int chimeList[10][3] = {{1,8,0},
			 {2,8,0},
			 {3,8,0},
			 {4,8,0},
			 {5,8,0},
			 {1,14,10},
			 {2,14,10},
			 {3,14,10},
			 {4,14,10},
			 {5,14,10}
			 };

void chime(DateTime now) {
	// See if the minute has recently changed
	if( priorMinute == now.minute() )
		return;
	priorMinute = now.minute();
	
	for( int i=0; i < 10; i++ ) {
		int * chime = chimeList[i];
		if( now.dayOfWeek() >= chime[0] && now.hour() == chime[1] && now.minute() == chime[2] ) {
			tone(SPEAKER_PIN, 500, 50);
		}
	}
}
/** Display time on the tubes **/
void tubeDisplayTime(DateTime now) {
  int hour = now.hour();
  
  if ( hour <= 5 || hour >= 20 ) {
    tube.setBackgroundColor(Black);
    tube.setBrightness(0x00);	// brightness control, 0x00(off)-0xff
  } else {
    tube.setBrightness(0xFF);	// brightness control, 0x00(off)-0xff
  }
  
  if( hour == 6 || hour == 7 || hour == 18 || hour == 19 )
    tube.setBackgroundColor(Red);
  else if (hour == 9 || hour == 16 )
    tube.setBackgroundColor(Cyan);
  else if (hour == 10 || hour == 15 )
    tube.setBackgroundColor(Green);
  else if (hour == 11 || hour == 14 )
    tube.setBackgroundColor(Blue);
  else if (hour == 12 || hour == 13 )
    tube.setBackgroundColor(Magenta);
  else
    tube.setBackgroundColor(Black);
  if( hour < 10 || (hour > 12 && hour < 22) )
    tube.setBackgroundColor(0,Black);
  
  if (hour == 0 ) {
    tube.printf("%2d:%02d", 12, now.minute() );
  } else if (hour < 13) {
    tube.printf("%2d:%02d", hour, now.minute() );
  } 
  else {
    tube.printf("%2d:%02d", hour-12, now.minute() );
  }
  
  
  if( now.second() % 4 < 2 )
    tube.setColon(1,None);
  else
    tube.setColon(1,Both);
	
  // Trigger Alarm!!!
  if( alarm1state && alarm1hour == now.hour() && alarm1minute == now.minute() ) {
    int color = (alarmSequence / 4) % 7 + 1;
    int colorPos = alarmSequence % 4;
    if( alarmSequence < 24 )
      tube.setBackgroundColor(colorPos, (Color) color);
    else {
      if( alarmSequence % 2 == 0 ) {
        tube.setBackgroundColor(White);
        tone(SPEAKER_PIN, 500, 25);
      } else
        tube.setBackgroundColor(Black);	
    }
    alarmSequence++;
    if( alarmSequence > 34 )
      alarmSequence = 0;
  }
  tube.display();
}

/** Display time on the LCD **/
void lcdDisplayTime(DateTime now) {
    // --- Write the time to the LCD
    char buffer[50];
    if (now.hour() < 12) {
      sprintf(buffer,"Time %2d:%02d:%02d AM",now.hour(),now.minute(),now.second() );
    } else if (now.hour() < 13) {
      sprintf(buffer,"Time %2d:%02d:%02d PM",now.hour(),now.minute(),now.second() );
    } else {
      sprintf(buffer,"Time %2d:%02d:%02d PM",now.hour()-12,now.minute(),now.second() );
    }
    lcd.setCursor(0, 0);
    lcd.print(buffer); 
  
    sprintf(buffer,"Temp %d F",now.tempF() );
  
    lcd.setCursor(0, 1);
    lcd.print(buffer); 

    lcd.noBlink();
}

void lcdDisplayAlarmSet() {
    lcd.setCursor(0, 0);
    lcd.print("Alarm 1 Set");
    
    char * state;
    if( alarm1state )
      state="ON  ";
    else
      state="OFF ";
      
    char buffer [50];
    if( alarm1hour < 13 )
      sprintf(buffer,"%s%2d:%02d AM ",state,alarm1hour,alarm1minute);
    else
      sprintf(buffer,"%s%2d:%02d PM ",state,alarm1hour-12,alarm1minute);
          
    lcd.setCursor(0, 1);
    lcd.print(buffer); 

    if( alarmSetPos == 0 )
      lcd.setCursor(8, 1);
    else if ( alarmSetPos == 1 )
      lcd.setCursor(7, 1);
    else if ( alarmSetPos == 2 )
      lcd.setCursor(5, 1);
    else
      lcd.setCursor(0, 1);
    lcd.blink();  
}

/** Process buttons in alarm mode **/
void processAlarm1Buttons(uint8_t buttons) {
  if ( buttons & BUTTON_UP) {
    if( priorUpState == UP ) {
      priorUpState = DOWN;
      if( alarmSetPos == 0 ) {
        alarm1minute++;
        if (alarm1minute > 59)
          alarm1minute=0;
      } else if( alarmSetPos == 1 ) {
        alarm1minute += 10;
        if (alarm1minute > 59)
          alarm1minute -= 60;
      } else if( alarmSetPos == 2 ) {
        alarm1hour++;
        if (alarm1hour > 24)
          alarm1hour = 1;
      } else if( alarmSetPos == 3 ) {
        alarm1state = !alarm1state;
      }
    }
  } else {
    priorUpState = UP;
  }
  
  if ( buttons & BUTTON_DOWN) {
    if( priorDownState == UP ) {
      priorDownState = DOWN;
      if( alarmSetPos == 0 ) {
        alarm1minute--;
        if (alarm1minute < 0 )
          alarm1minute=59;
      } else if( alarmSetPos == 1 ) {
        alarm1minute -= 10;
        if (alarm1minute < 0 )
          alarm1minute += 60;
      } else if( alarmSetPos == 2 ) {
        alarm1hour--;
        if (alarm1hour < 1)
          alarm1hour = 24;
      } else if( alarmSetPos == 3 ) {
        alarm1state = !alarm1state;
      }
    }
  } else {
    priorDownState = UP;
  }  
  
  if ( buttons & BUTTON_LEFT) {
    if( priorLeftState == UP ) {
      priorLeftState = DOWN;
      alarmSetPos++;
      if (alarmSetPos > 3 )
        alarmSetPos=3;
    }
  } else {
    priorLeftState = UP;
  }    
  
  if ( buttons & BUTTON_RIGHT) {
    if( priorRightState == UP ) {
      priorRightState = DOWN;
      alarmSetPos--;
      if (alarmSetPos < 0 )
        alarmSetPos=0;
    }
  } else {
    priorRightState = UP;
  }    
}

/** Process buttons in LCD color mode **/
void processLCDButtons(uint8_t buttons) {
  if ( buttons & BUTTON_UP) {
    if( priorUpState == UP ) {
      priorUpState = DOWN;
      currentLCDBacklightColor++;
      if (currentLCDBacklightColor > 7)
        currentLCDBacklightColor=1;
      lcd.setBacklight(currentLCDBacklightColor);
    }
  } else {
    priorUpState = UP;
  }
  
  if ( buttons & BUTTON_DOWN) {
    if( priorDownState == UP ) {
      priorDownState = DOWN;
      currentLCDBacklightColor--;
      if (currentLCDBacklightColor < 1)
        currentLCDBacklightColor=7;
      lcd.setBacklight(currentLCDBacklightColor);
    }
  } else {
    priorDownState = UP;
  }  
}

void processButtons() {
  // Figure out mode
  uint8_t buttons = lcd.readButtons();
  if ( buttons & BUTTON_SELECT ) {
    if( priorSelectState == UP) {
      priorSelectState = DOWN;
      lcd.clear();
      if( current_mode == NORMAL )
        current_mode = SET_ALARM_1;
      else if( current_mode == SET_ALARM_1 )
        current_mode = SET_LCD_COLOR;
      else
        current_mode = NORMAL;
    }
  } else {
    priorSelectState = UP;
  }
  
  if( current_mode == SET_LCD_COLOR )
    processLCDButtons(buttons);
  if( current_mode == SET_ALARM_1 )
    processAlarm1Buttons(buttons);
}
/** Main Loop **/
void loop() {
  processButtons();
  
  DateTime now = RTC.now();
  tubeDisplayTime(now);
  
  if( current_mode == NORMAL ) {
    lcdDisplayTime(now);
  }
  if( current_mode == SET_LCD_COLOR ) {
    lcd.setCursor(0, 0);
    lcd.print("Set LCD Color");
    lcd.noBlink();
  }
  if( current_mode == SET_ALARM_1 ) {
    lcdDisplayAlarmSet();
  }

  delay(25);
}