/* * Program for a 6-digit, SN74141-based clock. * * Time count, multiplex and crossfade display by ArduiNix : http://arduinix.com/Main/Code/ANX-6Tube-Clock-Crossfade.txt * Sleep, Date and Alarm modes, inputs, digit cycle added by Chris Gerekos. */ // SN74141 : Truth Table //D C B A # //L,L,L,L 0 //L,L,L,H 1 //L,L,H,L 2 //L,L,H,H 3 //L,H,L,L 4 //L,H,L,H 5 //L,H,H,L 6 //L,H,H,H 7 //H,L,L,L 8 //H,L,L,H 9 // SN74141 (1) int ledPin_0_a = 2; int ledPin_0_b = 3; int ledPin_0_c = 4; int ledPin_0_d = 5; // SN74141 (2) int ledPin_1_a = 6; int ledPin_1_b = 7; int ledPin_1_c = 8; int ledPin_1_d = 9; // anode pins int ledPin_a_1 = 10; int ledPin_a_2 = 11; int ledPin_a_3 = 12; void setup() { Serial.begin(9600); pinMode(ledPin_0_a, OUTPUT); pinMode(ledPin_0_b, OUTPUT); pinMode(ledPin_0_c, OUTPUT); pinMode(ledPin_0_d, OUTPUT); pinMode(ledPin_1_a, OUTPUT); pinMode(ledPin_1_b, OUTPUT); pinMode(ledPin_1_c, OUTPUT); pinMode(ledPin_1_d, OUTPUT); pinMode(ledPin_a_1, OUTPUT); pinMode(ledPin_a_2, OUTPUT); pinMode(ledPin_a_3, OUTPUT); // Commands : pinMode(A0, INPUT_PULLUP); //Mode (with pullup resistor) pinMode(A1, OUTPUT); //Alarm LED indicator pinMode(A2, INPUT_PULLUP); //Setting selection pinMode(A3, INPUT_PULLUP); //Setting selection pinMode(A4, INPUT_PULLUP); //Increase scroll pinMode(A5, INPUT_PULLUP); //Decrease scroll pinMode(1, INPUT); //Sleep lightning command will arrive here pinMode(13, OUTPUT); //Alarm command will be sent here } void SetSN74141Chips( int num2, int num1 ) { int a,b,c,d; // set defaults. a=0;b=0;c=0;d=0; // will display a zero. // Load the a,b,c,d.. to send to the SN74141 IC (1) switch( num1 ) { case 0: a=0;b=0;c=0;d=0;break; case 1: a=1;b=0;c=0;d=0;break; case 2: a=0;b=1;c=0;d=0;break; case 3: a=1;b=1;c=0;d=0;break; case 4: a=0;b=0;c=1;d=0;break; case 5: a=1;b=0;c=1;d=0;break; case 6: a=0;b=1;c=1;d=0;break; case 7: a=1;b=1;c=1;d=0;break; case 8: a=0;b=0;c=0;d=1;break; case 9: a=1;b=0;c=0;d=1;break; default: a=1;b=1;c=1;d=1; break; } // Write to output pins. digitalWrite(ledPin_0_d, d); digitalWrite(ledPin_0_c, c); digitalWrite(ledPin_0_b, b); digitalWrite(ledPin_0_a, a); // Load the a,b,c,d.. to send to the SN74141 IC (2) switch( num2 ) { case 0: a=0;b=0;c=0;d=0;break; case 1: a=1;b=0;c=0;d=0;break; case 2: a=0;b=1;c=0;d=0;break; case 3: a=1;b=1;c=0;d=0;break; case 4: a=0;b=0;c=1;d=0;break; case 5: a=1;b=0;c=1;d=0;break; case 6: a=0;b=1;c=1;d=0;break; case 7: a=1;b=1;c=1;d=0;break; case 8: a=0;b=0;c=0;d=1;break; case 9: a=1;b=0;c=0;d=1;break; default: a=1;b=1;c=1;d=1; break; } // Write to output pins digitalWrite(ledPin_1_d, d); digitalWrite(ledPin_1_c, c); digitalWrite(ledPin_1_b, b); digitalWrite(ledPin_1_a, a); } float fadeMax = 6.0f; float fadeStep = 0.3f; int NumberArray[6]={0,0,0,0,0,0}; int currNumberArray[6]={0,0,0,0,0,0}; float NumberArrayFadeInValue[6]={0.0f,0.0f,0.0f,0.0f,0.0f,0.0f}; float NumberArrayFadeOutValue[6]={8.0f,8.0f,8.0f,8.0f,8.0f,8.0f}; void DisplayFadeNumberString() { // Anode channel 1 - numerals 0,3 SetSN74141Chips(currNumberArray[0],currNumberArray[3]); digitalWrite(ledPin_a_1, HIGH); delay(NumberArrayFadeOutValue[2]); SetSN74141Chips(NumberArray[0],NumberArray[3]); delay(NumberArrayFadeInValue[2]); digitalWrite(ledPin_a_1, LOW); // Anode channel 2 - numerals 1,4 SetSN74141Chips(currNumberArray[1],currNumberArray[4]); digitalWrite(ledPin_a_2, HIGH); delay(NumberArrayFadeOutValue[2]); SetSN74141Chips(NumberArray[1],NumberArray[4]); delay(NumberArrayFadeInValue[2]); digitalWrite(ledPin_a_2, LOW); // Anode channel 3 - numerals 2,5 SetSN74141Chips(currNumberArray[2],currNumberArray[5]); digitalWrite(ledPin_a_3, HIGH); delay(NumberArrayFadeOutValue[2]); SetSN74141Chips(NumberArray[2],NumberArray[5]); delay(NumberArrayFadeInValue[2]); digitalWrite(ledPin_a_3, LOW); // Loop thru and update all the arrays, and fades. for( int i = 0 ; i < 6 ; i ++ ) { if( NumberArray[i] != currNumberArray[i] ) { NumberArrayFadeInValue[i] += fadeStep; NumberArrayFadeOutValue[i] -= fadeStep; if( NumberArrayFadeInValue[i] >= fadeMax ) { NumberArrayFadeInValue[i] = 0.0f; NumberArrayFadeOutValue[i] = fadeMax; currNumberArray[i] = NumberArray[i]; } } } } // Defines unsigned long time = 0; // Time from when we started. // default time sets. Time will start at 12:24:50. Date will start at 01/01/13. Default Alarm is 08:00. long ClockHourSet = 12; long ClockMinSet = 24; long ClockSecSet = 45; long ClockDaySet = 01; long ClockMonthSet= 01; long ClockYearSet = 13; int AlmHours = 8; //Those are set directly, no need for time count. int AlmMins = 0; //Defining commands byte Mode = 1; //0=Sleep, 1=Time, 2=Date, 3=Alarm. int ModeTimer = 0; long AlmMillis = 0; byte Set = 0; //0=Left, 1=Middle, 2=Right. byte ModeButtonPressed = false; byte IncreaseButtonPressed = false; byte DecreaseButtonPressed = false; byte AlmButtonPressed = false; byte LightCmdPin = false; byte ALARM = false; byte AlarmPlaying = false; void loop() { digitalWrite(13,HIGH); //default HIGH. Will be used for Alarm. // Get milliseconds. unsigned long runTime = millis(); // MODE SELECTION /////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// //When button is pressed, the pin is grounded and reads 0, a timer counts how much time the button is pressed. int ModeInput = digitalRead(A0); if(ModeInput==0) ModeButtonPressed = true; while(ModeInput==0){ ModeTimer++; delay(100); ModeInput = digitalRead(A0); } //If the button is pressed shortly, these actions will be taken just after the button is released. if (Mode==1 && ModeButtonPressed==true && ModeInput==1 && ModeTimer<5) { Mode = 2; //Switching from Time to Date mode ModeButtonPressed = false; ModeTimer=0; } if (Mode==2 && ModeButtonPressed==true && ModeInput==1 && ModeTimer<5) { Mode = 3; //Switching from Date to Alarm mode. ModeButtonPressed = false; ModeTimer=0; } if (Mode==3 && ModeButtonPressed==true && ModeInput==1 && ModeTimer<5) { Mode = 1; //Switching from Alarm to Time mode ModeButtonPressed = false; ModeTimer=0; } //If the button is pressed longly, if((Mode==1||Mode==2||Mode==3) && ModeButtonPressed==true && ModeInput==1 && ModeTimer>=5){ Mode = 0; //Switching from Time or Date to Sleep ModeButtonPressed = false; ModeTimer=0; } if(Mode==0 && ModeButtonPressed==true && ModeInput==1 && ModeTimer>=5){ Mode = 1; //Switching from Sleep to Time ModeButtonPressed = false; ModeTimer=0; } // SETTING SELECTION //////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// int LeftInput = digitalRead(A2); int RightInput = digitalRead(A3); if(LeftInput==LOW && RightInput==HIGH) Set = 0; //Left couple of tubes will be available for setting. if(RightInput==LOW && LeftInput==HIGH) Set = 2; //Right pair of tubes will be available for setting. if(RightInput==HIGH && LeftInput==HIGH) Set = 1; //Middle pair of tubes will be available for setting. if(Mode==3){ if(LeftInput==LOW) AlmButtonPressed = true; if(LeftInput==HIGH && AlmButtonPressed==true && ALARM==false){ //Kick on the "Set" switch upwards to set alarm on. ALARM = true; AlmButtonPressed = false; } if(LeftInput==HIGH && AlmButtonPressed==true && ALARM==true){ //turn alarm off. ALARM = false; AlmButtonPressed = false; } } // SETTING THE SELECTED PAIR OF TUBES //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// int DecreaseInput = digitalRead(A4); int IncreaseInput = digitalRead(A5); if(DecreaseInput==0) DecreaseButtonPressed = true; if(IncreaseInput==0) IncreaseButtonPressed = true; if(DecreaseButtonPressed==true && DecreaseInput==1) { if(Set==0){ if(Mode==1) ClockHourSet--; if(Mode==2) ClockDaySet--; } if(Set==1){ if(Mode==1) ClockMinSet--; if(Mode==2) ClockMonthSet--; if(Mode==3) AlmHours--; } if(Set==2){ if(Mode==1) ClockSecSet--; if(Mode==2) ClockYearSet--; if(Mode==3) AlmMins--; } DecreaseButtonPressed = false; } if(IncreaseButtonPressed==true && IncreaseInput==1) { if(Set==0){ if(Mode==1) ClockHourSet++; if(Mode==2) ClockDaySet++; } if(Set==1){ if(Mode==1) ClockMinSet++; if(Mode==2) ClockMonthSet++; if(Mode==3) AlmHours++; } if(Set==2){ if(Mode==1) ClockSecSet++; if(Mode==2) ClockYearSet++; if(Mode==3) AlmMins++; } IncreaseButtonPressed = false; } // TIME CALCULATION ///////////////////////////////// ///////////////////////////////////////////////////// // Get time in seconds. long time = runTime / 1000; time += ClockSecSet + 60*ClockMinSet + 3600*ClockHourSet + 86400*ClockDaySet; //time in seconds, based on offset // Convert time to days,hours,mins,seconds long days = time / 86400; time -= days * 86400; long hours = time / 3600; time -= hours * 3600; long minutes = time / 60; time -= minutes * 60; long seconds = time; // Get the high and low order values for hours,min,seconds. byte lowerHours = hours % 10; byte upperHours = hours - lowerHours; byte lowerMins = minutes % 10; byte upperMins = minutes - lowerMins; byte lowerSeconds = seconds % 10; byte upperSeconds = seconds - lowerSeconds; if( upperSeconds >= 10 ) upperSeconds = upperSeconds / 10; if( upperMins >= 10 ) upperMins = upperMins / 10; if( upperHours >= 10 ) upperHours = upperHours / 10; // DATE CALCULATION ///////////////////////////////// ///////////////////////////////////////////////////// int Jour = days; int Mois= ClockMonthSet; int Annee = ClockYearSet; //Defining years incrementation. if(Mois>=13){ Annee++; Mois=Mois-12; } if(Mois<=0){ Annee--; Mois=Mois+12; } //Number of days in a month: byte February; if(Year%4==0) February=29; else February=28; byte MonthArray[13] = {0,31,February,31,30,31,30,31,31,30,31,30,31}; //We'll skip the first entry, so we can use the array's index directly. //Defining months incrementation. for(byte i=1;i<13;i++){ if(Mois==i){ if(Jour>MonthArray[i]){ //If "Day" exceeds the number of days in the present month, it is resetted to 1 and we step into the next month. Jour -= MonthArray[i]; Mois++; } if(Jour<1){ //Similarly if "Day" is to become 0. if(i-1==0) i=13; //Before January comes December. Jour += MonthArray[i-1]; Mois--; } } } // Splitting. byte lowerYears = Annee % 10; byte upperYears = Annee - lowerYears; byte lowerMonths = Mois % 10; byte upperMonths = Mois - lowerMonths; byte lowerDays = Jour % 10; byte upperDays = Jour - lowerDays; if( upperDays >= 10 ) upperDays = upperDays / 10; if( upperMonths >= 10 ) upperMonths = upperMonths / 10; if( upperYears >= 10 ) upperYears = upperYears / 10; // ALM RULES ///////////////////////////////// ////////////////////////////////////////////// if(AlmHours>23) AlmHours=AlmHours-24; //After 23:00 comes 00:00. if(AlmHours<0) AlmHours=AlmHours+23; //vice-versa. if(AlmMins>59) AlmMins=AlmMins-60; //same thing. if(AlmMins<0) AlmMins=AlmMins+59; // Splitting. byte lowerAlmHours = AlmHours % 10; byte upperAlmHours = AlmHours - lowerAlmHours; byte lowerAlmMins = AlmMins % 10; byte upperAlmMins = AlmMins - lowerAlmMins; if( upperAlmMins >= 10 ) upperAlmMins = upperAlmMins / 10; if( upperAlmHours >= 10 ) upperAlmHours = upperAlmHours / 10; //Alm actions. if(ALARM==true){ digitalWrite(A1,HIGH) //Led is on. if(upperAlmHours==upperHours && lowerAlmHours==lowerHours && upperAlmMins==upperMins && lowerAlmMins==lowerMins && upperSeconds==0 && lowerSeconds==0){ digitalWrite(13,LOW); //Send a signal to the other Arduino. delay(2000); AlarmPlaying=true; AlmMillis=runTime; Mode=1; //Light the digits on. } } if(ALARM==false) digitalWrite(A1,LOW); //Led is off. //How to shut down the alarm without "desarming" it for the next day. if(AlarmPlaying==true){ if(ModeButtonPressed == true && ModeInput == 1 && ModeTimer<5){ //Manually : digitalWrite(13,LOW); //Send a signal to the other Arduino. After this, the next pulse will play the alarm again. delay(2000); AlarmPlaying=false; } if(runTime-AlmMillis>900000){ //Or automatically after 15' digitalWrite(13,LOW); //Send a signal to the other Arduino. After this, the next pulse will play the alarm again. delay(2000); AlarmPlaying=false; Mode=0; //Shut the digits down. } } // SHOWTIME ///////////////////////////////////////// ///////////////////////////////////////////////////// // Fill in the Number array used to display on the tubes following the Mode. if(Mode==0){ NumberArray[3] = upperHours; NumberArray[1] = lowerHours; NumberArray[5] = upperMins; NumberArray[0] = lowerMins; NumberArray[4] = upperSeconds; NumberArray[2] = lowerSeconds; if(digitalRead(1)==HIGH){ DisplayFadeNumberString(); //Light the digits only when told (see second Arduino code). LightCmdPin = true; } if(digitalRead(0)==LOW && LightCmdPin==true){ //After lightning time is over, "clean" the nixies with a digit cycle. for(int j=-1; j<11; j++){ //Alternating to make it more beautiful. The -1 and 11 value will turn the digits off to mark a short pause. NumberArray[3] = j; NumberArray[1] = 9-j; NumberArray[5] = j; NumberArray[0] = 9-j; NumberArray[4] = j; NumberArray[2] = 9-j; int k = 0; while(k<500){ DisplayFadeNumberString(); // Display numbers for a while. k++; delay(1); } } LightCmdPin=false; } } if(Mode==1){ NumberArray[3] = upperHours; NumberArray[1] = lowerHours; NumberArray[5] = upperMins; NumberArray[0] = lowerMins; NumberArray[4] = upperSeconds; NumberArray[2] = lowerSeconds; DisplayFadeNumberString(); // Display time. if((lowerMins==0||lowerMins==5)&&upperSeconds==0&&lowerSeconds==1) //lighting all the numbers every 5min to prevent cathode poisining. { for(int j=-1; j<11; j++){ //Alternating to make it more beautiful. The -1 and 11 value will turn the digits off to mark a short pause. NumberArray[3] = j; NumberArray[1] = 9-j; NumberArray[5] = j; NumberArray[0] = 9-j; NumberArray[4] = j; NumberArray[2] = 9-j; byte k = 0; while(k<20){ DisplayFadeNumberString(); // Display numbers for a little while. k++; delay(1); } } } } if(Mode==2){ NumberArray[3] = upperDays; NumberArray[1] = lowerDays; NumberArray[5] = upperMonths; NumberArray[0] = lowerMonths; NumberArray[4] = upperYears; NumberArray[2] = lowerYears; DisplayFadeNumberString(); //Display date. } if(Mode==3){ NumberArray[3] = 11; //Eleven will turn off the tube. NumberArray[1] = 11; NumberArray[5] = upperAlmHours; NumberArray[0] = lowerAlmHours; NumberArray[4] = upperAlmMins; NumberArray[2] = lowerAlmMins; DisplayFadeNumberString(); //Display Alarm. } }