/* * Program for a 6-digit, SN74141-based alarm clock; * coding by Robin Birtles and Chris Gerekos ( based on http://arduinix.com/Main/Code/ANX-6Tube-Clock-Crossfade.txt ); * for RLB Designs ( http://www.rlb-designs.com/ ). */ #include #include "RTClib.h" RTC_DS1307 RTC; // 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; int ledPin_a_4 = 13; void setup() { Serial.begin(57600); Wire.begin(); 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__)); } 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); pinMode(ledPin_a_4, OUTPUT); // NOTE: All analog pins as digital inputs with pull-up resistor activated. pinMode(A0, INPUT_PULLUP); // Set pinMode(A1, INPUT_PULLUP); // Mode swicth pinMode(A2, INPUT_PULLUP ); // Alarm arming pinMode(A3, INPUT_PULLUP ); // Increase switch pinMode(A6, INPUT_PULLUP ); // Decrease switch pinMode(A7, OUTPUT); // Alarm tone will be sent there } 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 = 5.0f; float fadeStep = 1.0f; 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_2, HIGH); delay(NumberArrayFadeOutValue[0]); SetSN74141Chips(NumberArray[0],NumberArray[3]); delay(NumberArrayFadeInValue[0]); digitalWrite(ledPin_a_2, LOW); // Anode channel 2 - numerals 1,4 SetSN74141Chips(currNumberArray[1],currNumberArray[4]); digitalWrite(ledPin_a_3, HIGH); delay(NumberArrayFadeOutValue[1]); SetSN74141Chips(NumberArray[1],NumberArray[4]); delay(NumberArrayFadeInValue[1]); digitalWrite(ledPin_a_3, LOW); // Anode channel 3 - numerals 2,5 SetSN74141Chips(currNumberArray[2],currNumberArray[5]); digitalWrite(ledPin_a_4, HIGH); delay(NumberArrayFadeOutValue[2]); SetSN74141Chips(NumberArray[2],NumberArray[5]); delay(NumberArrayFadeInValue[2]); digitalWrite(ledPin_a_4, 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: long runTime = 0; // Used for blinking timers int AlmHours = 12; // Those are set directly, no need for time count. int AlmMins = 00; byte Mode = 1; //1: time, 2: date, 3: alarm byte ModeButtonPressed = false; byte Set = 1; //1: left, 2: middle, 3: right long SetTimer = 0; byte SETMODE = false; byte SetButtonPressed = false; byte IncreaseButtonPressed = false; byte DecreaseButtonPressed = false; byte AlmButtonPressed = false; int nixState = HIGH; int toneState = LOW; long previousTimeBlink = 0; long previousTimeTone= 0; byte AlmArmed=false; byte AlmPlaying=false; void loop(){ // Get milliseconds. runTime = millis(); // MODE SELECTION /////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// byte ModeInput = digitalRead(A1); if(ModeInput==0) ModeButtonPressed = true; if (Mode==1 && ModeButtonPressed==true && ModeInput==1) { Mode = 2; //Switching from Time to Date mode ModeButtonPressed = false; SETMODE=false; } if (Mode==2 && ModeButtonPressed==true && ModeInput==1) { Mode = 3; //Switching from Date to Alarm mode ModeButtonPressed = false; SETMODE=false; } if (Mode==3 && ModeButtonPressed==true && ModeInput==1) { Mode = 1; //Switching from Alarm to Time mode ModeButtonPressed = false; } // SETTING ////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// byte SetInput = digitalRead(A0); if(SetInput==0) SetButtonPressed = true; while(SetInput==0){ SetTimer++; delay(10); SetInput = digitalRead(A0); } if(SetTimer>=100){ if(SETMODE==false) SETMODE=true; //A long press will either enter or exit the setting mode option. else SETMODE=false; } SetTimer=0; //Reinitializing. if(SETMODE==true){ if (Set==1 && SetButtonPressed==true && SetInput==1) { Set = 2; //Switching from left to middle pair available for setting. SetButtonPressed = false; } if (Set==2 && SetButtonPressed==true && SetInput==1) { Set = 1; //Switching from middle to left pair available for setting. SetButtonPressed = false; } //Now we set the selected the pair of tube, let's modify its value: int DecreaseInput = digitalRead(A3); int IncreaseInput = digitalRead(A6); if(DecreaseInput==0) DecreaseButtonPressed = true; if(IncreaseInput==0) IncreaseButtonPressed = true; if(Mode==3){ //Only mode that needs setting if(DecreaseButtonPressed==true && DecreaseInput==1) { if(Set==1) AlmHours--; if(Set==2) AlmMins--; DecreaseButtonPressed = false; } if(IncreaseButtonPressed==true && IncreaseInput==1) { if(Set==1) AlmHours++; if(Set==2) AlmMins++; IncreaseButtonPressed = false; } } } // ALARM ARMING ///////////////////////////////////// ///////////////////////////////////////////////////// if(Mode==3 && AlmPlaying==false){ //The second condition ensures that the alarm will stay armed when the user presses A2 just to turn it off. int AlmInput = digitalRead(A2); if(AlmInput==0) AlmButtonPressed = true; if(AlmButtonPressed==true && AlmInput==1){ if(AlmArmed==true) AlmArmed=false; else AlmArmed=true; AlmButtonPressed = false; } } // TIME AND DATE RETREIVING ///////////////////////// ///////////////////////////////////////////////////// DateTime now = RTC.now(); // Extracting all the useful values from the RTC. int year = now.year()%100; int month = now.month(); int day = now.day(); int hour = now.hour(); int minute = now.minute(); int second = now.second(); // Splitting time into actual tube values. byte lowerHours = hour % 10; byte upperHours = hour - lowerHours; byte lowerMins = minute % 10; byte upperMins = minute - lowerMins; byte lowerSeconds = second % 10; byte upperSeconds = second - lowerSeconds; if( upperSeconds >= 10 ) upperSeconds = upperSeconds / 10; if( upperMins >= 10 ) upperMins = upperMins / 10; if( upperHours >= 10 ) upperHours = upperHours / 10; // Splitting date. byte lowerYears = year % 10; byte upperYears = year - lowerYears; byte lowerMonths = month % 10; byte upperMonths = month - lowerMonths; byte lowerDays = day % 10; byte upperDays = day - 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(AlmPlaying==true){ if(runTime-previousTimeTone > 500){ //Play an intermittent (1s period) tune. previousTimeTone = runTime; if(toneState == HIGH){ tone(1, 200); //Tune is 200Hz. toneState=LOW; } else{ noTone(1); toneState=HIGH; } } if(digitalRead(A0)==LOW || digitalRead(A1)==LOW || digitalRead(A2)==LOW || digitalRead(A3)==LOW || digitalRead(A6)==LOW) AlmPlaying=false; //Press anything to stop it. } else noTone(1); // SHOWTIME ///////////////////////////////////////// ///////////////////////////////////////////////////// // Fill in the Number array used to display on the tubes following the Mode. if(Mode==1){ NumberArray[5] = upperHours; NumberArray[1] = lowerHours; NumberArray[3] = upperMins; NumberArray[2] = lowerMins; NumberArray[4] = upperSeconds; NumberArray[0] = lowerSeconds; DisplayFadeNumberString();// Display time. if((lowerMins==5)&&upperSeconds==0&&lowerSeconds==1)//lighting all the numbers every 10min 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[5] = j; NumberArray[1] = 9-j; NumberArray[3] = j; NumberArray[2] = 9-j; NumberArray[4] = j; NumberArray[0] = 9-j; int k = 0; while(k<10){ DisplayFadeNumberString();// Clean NIXIE's Display for 2 seconds. k++; delay(1); } } } if(lowerMins==3 && upperSeconds==0 && lowerSeconds==1){ //Display date only on every (03,13,23,33) Mins NumberArray[5] = upperDays; NumberArray[1] = lowerDays; NumberArray[3] = upperMonths; NumberArray[2] = lowerMonths; NumberArray[4] = upperYears; NumberArray[0] = lowerYears; int k = 0; while(k<550){ // Display date for 10 seconds (50 = 1sec). DisplayFadeNumberString(); // Display date. k++; delay(1); } } } if(Mode==2){ NumberArray[5] = upperDays; NumberArray[1] = lowerDays; NumberArray[3] = upperMonths; NumberArray[2] = lowerMonths; NumberArray[4] = upperYears; NumberArray[0] = lowerYears; DisplayFadeNumberString(); // Display date. } if(Mode==3){ if(SETMODE==true){ if(Set==1){ if(runTime-previousTimeBlink > 500){ //Blinking these digits while in setmode. previousTimeBlink = runTime; if(nixState == LOW){ NumberArray[5] = 11; //Turning this pair off. NumberArray[1] = 11; nixState = HIGH; } else{ NumberArray[5] = upperAlmHours; NumberArray[1] = lowerAlmHours; nixState = LOW; } } NumberArray[3] = upperAlmMins; NumberArray[2] = lowerAlmMins; NumberArray[0] = AlmArmed; //1: armed, 0: not armed } if(Set==2){ if(runTime-previousTimeBlink > 500){ //Blinking these digits while in setmode. previousTimeBlink = runTime; if(nixState == LOW){ NumberArray[3] = 11; NumberArray[2] = 11; nixState = HIGH; } else{ NumberArray[3] = upperAlmMins; NumberArray[2] = lowerAlmMins; nixState = LOW; } } NumberArray[5] = upperAlmHours; NumberArray[1] = lowerAlmHours; NumberArray[0] = AlmArmed; } } else{ //Fill the digits normally. NumberArray[5] = upperAlmHours; NumberArray[1] = lowerAlmHours; NumberArray[3] = upperAlmMins; NumberArray[2] = lowerAlmMins; NumberArray[0] = AlmArmed; } NumberArray[4] = 11; //Eleven will turn off the tube DisplayFadeNumberString(); // Display alarm. } }