Forum Index > Projects > Clock Kits
 Calendar done
 |  Printable Version
By: Anonymous: Robert () on Sunday, March 18 2012 @ 04:58 AM PDT (Read 647 times)  
Anonymous: Robert

** CALENDAR DONE OLL RAIGTH **

  • Snooze button shows date
  • Time set allows adjustment of date



PHP Formatted Code


/*
alphaclock_18.pde
 
 
 Software for the Five Letter Word Clock desiged by
 Evil Mad Scientist Laboratories http://www.evilmadscientist.com
 
 Target: ATmega644A, clock at 16 MHz.
 
 Designed to work with Arduino 23; untested with other versions.
 Also (1) requires the "old" DateTime library:  http://www.arduino.cc/playground/Code/DateTime
 Also (2) requires Sanguino extensions to Arduino, rev 0023, available from:
 http://code.google.com/p/sanguino/downloads/list
 
 * May require "boards.txt" file to be edited as well, for atmega644 (not -P).
 * May require "pins_arduino.c" and "pins_arduino.h" to be edited as well, for 644.
 - Arduino does not directly support the '644A, so the outgoing serial port may not be usable from
 within the Arduino IDE.
 * Bootloader should be set to give device signature as ATmega644P.
 
 Untested with newer versions of Arduino.
 
 
 Version 1.0 - 12/17/2011
 Copyright (c) 2011 Windell H. Oskay.  All right reserved.
 http://www.evilmadscientist.com/
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this library.  If not, see <http://www.gnu.org/licenses/>.
         
 */


#include <EEPROM.h>            // For saving settings
#include <Wire.h>              // For optional RTC module
#include <DateTime.h>          // For optional Serial Sync
#include <DateTimeStrings.h>   // For optional Serial Sync


#define AllLEDsOff();  PORTA |= 127;  

// The buttons are located at PB0, PB1, PB2, PB3
#define buttonmask 15

// Default "idle" time to wait before saving settings in EEPROM, usually 5 seconds (5000U)
#define IdleDelay  5000U


byte PINBLast;

byte TimeError;

byte ShowDateNow;

byte HalfSecNow;
byte SpinnerNow;

byte MinNow;
byte MinNowTens;
byte MinNowOnes;

byte HrNow;
byte HrNowTens;
byte HrNowOnes;

byte DateNow;
byte DateNowTens;
byte DateNowOnes;

byte MonthNow;

byte YearNow;
byte YearNowTens;
byte YearNowOnes;

byte WeekdayNow;

byte ZeroHourBlankNow;
byte IdleTimeEnded;


char AMPM24HdisplayNow;   // Units character

byte ZeroHourBlankAlrm;
char AMPM24HdisplayAlrm;
byte MinAlrmOnes;
byte MinAlrmTens;
byte HrAlrmOnes;
byte HrAlrmTens;

byte CharacterBuffer[10];  // What position in char array

byte bufl1[10];  // First byte  (low power)
byte bufh1[10];  // second byte (high power)
byte bufh2[10];  // third byte  (high power)

char stringTemp[5];

/* Start at ASCII 32 (decimal), character ' ' (space).
 Byte format: A, B, C, order sent to shift register.
 A: First byte,  "low power"
 B: Second byte, "high power," MSB
 C: Third byte,  "high power," LSB
 
 */

byte AlphaArray[] = {
  0,0,0,    // [space]  ASCII 32
  0,0,3,    // ! (Yes, lame, but it's the "standard." Better Suggestions welcome.)
  0,0,40,  // "
  63,3,255,    //#
  63,1,42,    //$
  57,3,106,    //%
  14,3,98,    //&
  0,0,64,      // '
  0,0,192,      // (
  0,2,16,      // )
  48,3,240,    // *
  48,1,32,      //+
  0,2,0,      //  ,
  48,0,0,      // -
  64,0,0,      // . (lower DP)
  0,2,64,    //  /

  15,0,15,  // 0
            // for slashed zero use 15,2,79,
  0,0,67,   // 1
  47,2,1,   // 2
  47,0,66,  // 3
  48,0,11,  // 4
  63,0,10,  // 5
  63,0,14,  // 6
  51,2,64,  // crossed 7
  63,0,15,  // 8
  63,0,11,  // 9

  0,1,32,      //:
  0,2,32,      //;
  0,0,192,      //<
  60,0,0,      //=
  0,2,16,      //>
  34,1,1,      //?
  47,0,13,      //@
  51,0,15,      //A
  47,1,35,      //B
  15,0,12,      //C
  15,1,35,     //D
  31,0,12,      //E
  19,0,12,    //F
  47,0,14,      //G
  48,0,15,      //H
  15,1,32,        //I
  12,0,7,        //J
  16,0,204,      //K
  12,0,12,       //L
  0,0,95,        //M
  0,0,159,      //N
  15,0,15,      //O
  51,0,13,      //P
  15,0,143,    //Q
  51,0,141,    //R
  63,0,10,      //S
  3,1,32,      //T
  12,0,15,      //U
  0,2,76,      //V
  0,2,143,      //W
  0,2,208,      //X
  0,1,80,      //Y
  15,2,64,      //Z
  10,1,32,      // [
  0,0,144,      // backslash
  5,1,32,        // ]
  0,2,128,      // ^
  12,0,0,      // _
  0,0,16,        // `
 
  0,0,32,      // spinner position 0
  0,0,64,
  32,0,0,
  0,0,128,
  0,1,0,
  0,2,0,
  16,0,0,
  0,0,16,      // spinner position 7

  10,1,47,  // single-cell figure 10 (month number, etc.)
  0,0,15,   // single-cell figure 11
  42,1,13   // single-cell figure 12  
 
};

// Starting offset of our ASCII array:
#define asciiOffset 32  

// Starting offset for number zero
#define numberOffset 16

// Starting offset for spinner
#define spinnerOffset 65

#define singleCellTen 73


//Faster latch macro!
#define Latch();    PORTC |= 4;PORTC &= 251;    


void AlphaWrite (byte l1, byte h1, byte h2)
{  // Transmit data to to LED drivers through SPI
  PORTA |= 127; // Blank all character "rows" (A0-A5) and LED driver
  SPDR = l1;
  while (!(SPSR & _BV(SPIF))) {
  }  //Wait for transmission complete
  SPDR = h1;
  while (!(SPSR & _BV(SPIF))) {
  }  //Wait for transmission complete
  SPDR = h2;
  while (!(SPSR & _BV(SPIF))) {
  }  //Wait for transmission complete
}



byte OptionMode;    // Are we in the settings menu?   If so, what position?
// Number of option modes: (e.g., 2, if there are only two option modes)
#define OptionsMax 4
byte OptionNameSequence;  // State variable for multi-word option labels
byte SoundSequence;

unsigned long WordStopTime;
byte DisplayWordMode;
byte SerialDisplayMode;

byte LEDTestMode;
byte HoldLEDTest;
byte LEDTestStage;

void LoadCharBuffer (char WordIn[])
{

  CharacterBuffer[0] = (WordIn[4] - asciiOffset);
  CharacterBuffer[1] = (WordIn[3] - asciiOffset);
  CharacterBuffer[2] = (WordIn[2] - asciiOffset);
  CharacterBuffer[3] = (WordIn[1] - asciiOffset);
  CharacterBuffer[4] = (WordIn[0] - asciiOffset);
}

void DisplayWordSequence (char WordIn[], unsigned int durationMillis)
{
  WordStopTime = millis() + durationMillis;

  CharacterBuffer[0] = (WordIn[4] - asciiOffset);
  CharacterBuffer[1] = (WordIn[3] - asciiOffset);
  CharacterBuffer[2] = (WordIn[2] - asciiOffset);
  CharacterBuffer[3] = (WordIn[1] - asciiOffset);
  CharacterBuffer[4] = (WordIn[0] - asciiOffset);

  LoadShiftRegBuffers();
  DisplayWordMode = 1;
  OptionNameSequence++;
}



#define EELength 8
byte EEvalues[EELength];

byte MainBright;
byte FadeMode;  // Not used at present.
byte HourMode24;
byte AlarmEnabled; // If the "ALARM" function is currently turned on or off.
byte AlarmTimeHr;
byte AlarmTimeMin;
byte NightLightType;   //0
byte AlarmTone;


// "Factory" default configuration can be configured here:
#define MainBrightDefault 5
#define FadeModeDefault 1
#define HourMode24Default 0
#define AlarmEnabledDefault 0
#define AlarmTimeHrDefault 7
#define AlarmTimeMinDefault 30
#define NightLightTypeDefault 0
#define AlarmToneDefault 1

unsigned long LastTime;

//byte TimeSinceButton;
//byte LastAlarmCheckMin;



byte ExtRTC;


void updateNightLight()
{

  if (NightLightType == 0)
    analogWrite(14, 0);  
  if (NightLightType == 1)
    analogWrite(14, 60);  
  if (NightLightType == 2)
    analogWrite(14, 255);  

}


#define TIME_MSG_LEN  13   // time sync to PC is HEADER followed by unix time_t as ten ascii digits  (Was 11)
#define TIME_HEADER  255   // Header tag for serial time sync message


boolean getPCtime() {
  char charTemp,  charTemp2;
  int i;

  // if time sync available from serial port, update time and return true
  while(Serial.available() >=  TIME_MSG_LEN ){  // time message consists of a header and ten ascii digits


    /*if (Serial.read() != TIME_HEADER)
     {
     while((Serial.peek() != TIME_HEADER) &&  (Serial.peek() >= 0))  // Flush buffer up until next 0xFF.    
     Serial.read();
     
     DisplayWordSequence("FLUSH",500);     //TODO: Remove this debug message
     }
     */

    if( Serial.read() == TIME_HEADER) {

      //   DisplayWordSequence("RECV ",100);  //TODO: Remove this debug message
      //    Read command, next two bytes:
      charTemp = Serial.read();
      charTemp2 = Serial.read();

      if( charTemp == 'S' ){

        // DisplayWordSequence("RECVS",100);  //TODO: Remove this debug message
        if( charTemp2 == 'T' ){
          // Time setting mode:
          //DisplayWordSequence("RECVT",100);  //TODO: Remove this debug message

          time_t pctime = 0;
          for( i=0; i < 10; i++){  
            char c= Serial.read();          
            if( c >= '0' && c <= '9'){  
              pctime = (10 * pctime) + (c - '0') ; // convert digits to a number    
            }
          }  
          DateTime.sync(pctime);   // Sync Arduino clock to the time received on the serial port
          return true;   // return true if time message received on the serial port
        }

      }

      else if( charTemp == 'A' ){  
        if( charTemp2 == '0' )  {

          //  DisplayWordSequence("RECA0",500);  //TODO: Remove this debug message
          // ASCII display mode, first 5 chars will be displayed.

          for( i=0; i < 10; i++){  
            charTemp = Serial.read();          

            if (i < 5)
            {
              CharacterBuffer[4 - i] = charTemp - asciiOffset;
            }
            else
            {
              CharacterBuffer[i] = charTemp;
            }

          }  

          LoadShiftRegBuffers();
          DisplayWordMode = 1;  


          for( i=5; i < 10; i++){  

            if (CharacterBuffer[i] == 'L')
              bufl1[9 - i] |= 64;  // Add lower DP
            if (CharacterBuffer[i] == 'U')
              bufl1[9 - i] |= 128;  // Add upper DP
            if (CharacterBuffer[i] == 'B')
              bufl1[9 - i] |= 192;  // Add both DPs

          }
          SerialDisplayMode  = 1;
          // Serial.println("Writing Text!");
        }
      }
      else if( charTemp == 'M' ){
        if( charTemp2 == 'T' )
        {
          // Clock display mode
          SerialDisplayMode  = 0;
          // Serial.println("Resuming clock display!");
          for( i=0; i < 10; i++){     // Read dummy input....
            charTemp = Serial.read();          
          }
        }

      }
    }
    else
    {
      DisplayWordSequence("ERROR",200);  //Display error!
    }
  }
  return false;  //if no message return false

}


void printDigits(byte digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits,DEC);
}

void digitalClockDisplay(){
  // digital clock display of current date and time
  Serial.print(DateTime.Hour,DEC);
  printDigits(DateTime.Minute);
  printDigits(DateTime.Second);
  Serial.print(" ");
  Serial.print(DateTimeStrings.dayStr(DateTime.DayofWeek));
  Serial.print(" ");
  Serial.print(DateTimeStrings.monthStr(DateTime.Month));
  Serial.print(" ");
  Serial.println(DateTime.Day,DEC);
}


// Modes:

byte VCRmode;  // In VCR mode, the clock blinks at you because the time hasn't been set yet.
//byte FactoryResetDisable;  // To make sure that we don't accidentally reset the settings...

byte SettingTime;
byte SettingAlarm;
byte AlarmNow;    // Alarm is actually going off, right now.

byte HoldTimeSet;
byte HoldOption;
byte HoldAlarmSet;
byte HoldLoopCount;

byte     MomentaryOverridePlus;
byte     MomentaryOverrideMinus;

unsigned long prevtime;
unsigned long millisCopy;
unsigned long NextAdvance;
unsigned long NextAdvanceSound;
unsigned long endIdleTime;

unsigned long NumAdvances;

void ApplyDefaults (void) {

  // VARIABLES THAT HAVE EEPROM STORAGE AND DEFAULTS...
  // FadeMode = FadeModeDefault;

  MainBright = MainBrightDefault;
  HourMode24 = HourMode24Default;
  AlarmEnabled = AlarmEnabledDefault;
  AlarmTimeHr = AlarmTimeHrDefault;
  AlarmTimeMin = AlarmTimeMinDefault;
  AlarmTone = AlarmToneDefault;
  NightLightType = NightLightTypeDefault;  
}

void EEReadSettings (void) {

  byte detectBad = 0;
  byte value = 255;

  value = EEPROM.read(0);      

  if ((value >= 1) && (value <= 13))
    MainBright = value;  // MainBright has maximum possible value of 13.    
  else  {
    MainBright = MainBrightDefault;  // Turn back on when power goes back on-- don't leave it dark.
    EESaveSettings();
}

  value = EEPROM.read(1);
  if (value > 1)
    detectBad = 1;
  else  
    HourMode24 = value;


  value = EEPROM.read(2);
  if (value > 1)
    detectBad = 1;
  else  
    AlarmEnabled = value;

  value = EEPROM.read(3);
  if (value > 23)
    detectBad = 1;
  else  
    AlarmTimeHr = value;  

  value = EEPROM.read(4);
  if (value > 59)
    detectBad = 1;
  else  
    AlarmTimeMin = value;  

  value = EEPROM.read(5);
  if (value > 4)
    detectBad = 1;
  else  
    AlarmTone = value;      

  value = EEPROM.read(6);
  if (value > 4)
    detectBad = 1;
  else  
    NightLightType = value;      
/*
if (detectBad){
    ApplyDefaults();
    EESaveSettings();
}
*/

}

void EESaveSettings (void){
  //EEPROM.write(Addr, Value);

  byte detectBad = 0;
  byte EEPROMwritten = 0;
  byte value = 255;

  // Careful if you use  this function: EEPROM has a limited number of write
  // cycles in its life.  Good for human-operated buttons, bad for automation.

  if (MainBright > 13)
    detectBad = 1;
  else  
  {
    value = EEPROM.read(0);  
    if (MainBright != value){
      EEPROM.write(0, MainBright);
      EEPROMwritten = 1;
    }
  }

  if (HourMode24 > 1)
    detectBad = 1;
  else  
  {
    value = EEPROM.read(1);  
    if (HourMode24 != value){
      EEPROM.write(1, HourMode24);
      EEPROMwritten = 1;
    }
  }

  if (AlarmEnabled > 1)
    detectBad = 1;
  else  
  {
    value = EEPROM.read(2);  
    if (AlarmEnabled != value){
      EEPROM.write(2, AlarmEnabled);
      EEPROMwritten = 1;
    }
  }

  if (AlarmTimeHr > 23)
    detectBad = 1;
  else  
  {
    value = EEPROM.read(3);  
    if (AlarmTimeHr != value){
      EEPROM.write(3, AlarmTimeHr);
      EEPROMwritten = 1;
    }
  }

  if (AlarmTimeMin > 59)
    detectBad = 1;
  else  
  {
    value = EEPROM.read(4);  
    if (AlarmTimeMin != value){
      EEPROM.write(4, AlarmTimeMin);
      EEPROMwritten = 1;
    }
  }


  if (AlarmTone > 4)
    detectBad = 1;
  else  
  {
    value = EEPROM.read(5);  
    if (AlarmTone != value){
      EEPROM.write(5, AlarmTone);
      EEPROMwritten = 1;
    }
  }


  if (NightLightType > 4)
    detectBad = 1;
  else  
  {
    value = EEPROM.read(6);  
    if (NightLightType != value){
      EEPROM.write(6, NightLightType);
      EEPROMwritten = 1;
    }
  }



  // Optional: Blink LEDs off to indicate when we're writing to the EEPROM


  if (EEPROMwritten)
  {  
    AllLEDsOff();
    delay(100);
  }


}




void RTCsetTime(byte yearIn, byte monthIn, byte dateIn, byte weekdayIn, byte hourIn, byte minuteIn, byte secondIn)
{
  Wire.beginTransmission(104); // 104 is DS3231 device address
  Wire.send(0); // start at register 0

  Wire.send(secondIn + 6*(secondIn/10)); //Send seconds as BCD
  Wire.send(minuteIn + 6*(minuteIn/10)); //Send minutes as BCD
  Wire.send(hourIn + 6*(hourIn/10)); //Send hours as BCD
  Wire.send(weekdayIn); //Send weekday
  Wire.send(dateIn + 6*(dateIn/10)); //Send date as BCD
  Wire.send(monthIn + 6*(monthIn/10)); //Send month as BCD
  Wire.send(yearIn + 6*(yearIn/10)); //Send year as BCD

  Wire.endTransmission();  

}

byte RTCgetTime()
{ // Read out time from RTC module, if present
  // send request to receive data starting at register 0

  byte status = 0;
  Wire.beginTransmission(104); // 104 is DS3231 device address
  Wire.send(0); // start at register 0
  Wire.endTransmission();
  Wire.requestFrom(104, 7); // request seven bytes (time and date)

  byte seconds, minutes, hours, weekday, date, month, year;
  byte errorfound = 0;
  byte rWeekday;
  // unsigned int temptime1, temptime2;
  byte updatetime = 0;

  while(Wire.available())
  {
    status = 1;
    seconds = Wire.receive(); // get seconds
    minutes = Wire.receive(); // get minutes
    hours = (Wire.receive()) & 0x3F;   // get hours
    rWeekday = Wire.receive();
    date = Wire.receive();
    month = (Wire.receive()) & 0x1F;
    year = Wire.receive();
  }

  // IF time is off by MORE than two seconds, then correct the displayed time.
  // Otherwise, DO NOT update the time, it may be a sampling error rather than an
  // actual offset.
  // Skip checking if minutes == 0. -- the 12:00:00 rollover is distracting,
  // UNLESS this is the first time running after reset.

  // if (ExtRTC) is equivalent to saying,  "if this has run before"

  if (status){
    seconds -= (6*(seconds/16)); // convert BCD to decimal
    minutes -= (6*(minutes/16)); // convert BCD to decimal
    hours -= (6*(hours/16)); // convert BCD to decimal (assume 24 hour mode)
    // we handle the weekday later
    date -= (6*(date/16)); // BCD to decimal
    month -= (6*(month/16)); // BCD to decimal
    year -= (6*(year/16)); // BCD to decimal
   
    // calculate right day of the week
    weekday = year%28;
    weekday += weekday/4;
    switch (month) {
      case 1: weekday+=1; if (year%4==0) weekday--; break;
      case 2: weekday+=4; if (year%4==0) weekday--; break;
      case 3: weekday+=4; break;
      case 4: weekday+=0; break;
      case 5: weekday+=2; break;
      case 6: weekday+=5; break;
      case 7: weekday+=0; break;
      case 8: weekday+=3; break;
      case 9: weekday+=6; break;
      case 10: weekday+=1; break;
      case 11: weekday+=4; break;
      case 12: weekday+=6; break;
    }
    weekday+=date;
    weekday+=5;
    weekday%=7;
    if (weekday==0) weekday=7;
    // weekday calculation is complete
    // 1 is Monday, 2 is Tuesday, ... 7 is Sunday

    // check for bogus date and/or time
    if ((year<0) || (year>99)) errorfound=1;
    if ((month<1) || (month>12)) errorfound=1;
    if ((date<1) || (date>31)) errorfound=1;
    if ((date>30) && ((month==4) || (month==11) || (month==9) || (month==6))) errorfound=1;
    if ((date>29) && (month==2)) errorfound=1;
    if ((date>28) && (month==2) && ((year%4)>0)) errorfound=1;
    if (weekday!=rWeekday) errorfound=1;

    //Optional: report time::
    // Serial.print(hours); Serial.print(":"); Serial.print(minutes); Serial.print(":"); Serial.println(seconds);

    updatetime = 1;
   
    if (errorfound) {
      TimeError = 1;
      updatetime = 0;
    }

    if (updatetime)
    {
      HalfSecNow = seconds*2;
      MinNow = minutes;
      HrNow = hours;
      WeekdayNow = weekday;
      DateNow = date;
      MonthNow = month;
      YearNow = year;
    }
  }

  return status;
}




byte AlarmTimeSnoozeMin;
byte AlarmTimeSnoozeHr;
byte snoozed;




void CalculateNewTime (void)
{  // Update current display representation of the time

  if (HalfSecNow > 119){
    HalfSecNow -= 120;
    MinNow++;


    if (IdleTimeEnded)   { // Skip this step if there has been a recent button press.
      if ((SettingTime == 0) && ExtRTC) // Check value at RTC ONCE PER MINUTE, if enabled.
        RTCgetTime();              // Do not check RTC time, if we are in time-setting mode.
    }
  }
 
  if (SettingTime) TimeError=0;

  if (MinNow > 59){
    MinNow -= 60;
    HrNow++;
  }
  if  (HrNow > 23) {
    HrNow -= 24;
    WeekdayNow++;
    DateNow++;
  }
  if (WeekdayNow>=249) WeekdayNow+=7;
  if (WeekdayNow>7) WeekdayNow-=7;
  if (WeekdayNow==0) WeekdayNow=7;

  if (DateNow==0 || DateNow>=226) {
    MonthNow--;
    if (MonthNow==0) {
      MonthNow=12;
      if (YearNow==0) {
        // wrap around to year 2099
        YearNow=99;
        WeekdayNow--; // our calendar cycle is 36525 days, which is one day short of a whole number of weeks
        if (WeekdayNow==0) WeekdayNow=7;
      } else {
        YearNow--;
      }
    }
    switch (MonthNow) {
      case 4:
      case 11:
      case 9:
      case 6:
        DateNow+=30;
        break;
      case 2:
        DateNow+=28;
        if ((YearNow&3)==0) DateNow++;
        break;
        default:
      DateNow+=31;
    }
  }
 
  if (DateNow>31 && !(MonthNow==4 || MonthNow==11 || MonthNow==9 || MonthNow==6 || MonthNow==2)) {
    DateNow -= 31;
    MonthNow++;
    if (MonthNow>12) {
      MonthNow = 1;
      YearNow++;
      if (YearNow>99) {
        YearNow = 0;
        WeekdayNow++; // our calendar cycle is 36525 days, which is one day short of a whole number of weeks
        if (WeekdayNow>7) WeekdayNow-=7;
      }
    }
  } else if (DateNow>30 && (MonthNow==4 || MonthNow==11 || MonthNow==9 || MonthNow==6)) {
    DateNow-=30;
    MonthNow++;
  } else if (MonthNow==2 && DateNow>((YearNow&3)==0?29:28)){
   // Serial.println(YearNow&3,DEC);
    DateNow-=28;
    if ((YearNow&3)==0) DateNow--;
    MonthNow++;
  }
 
  /*
  this is debug code for the calendar
  if (DateNow==8 || DateNow==22) {
    Serial.print(YearNow,DEC); Serial.print("."); Serial.print(MonthNow,DEC); Serial.print("."); Serial.print(DateNow,DEC); Serial.print(" ");
    switch (WeekdayNow) {
      case 1: Serial.println("Mon"); break;
      case 2: Serial.println("Tue"); break;
      case 3: Serial.println("Wed"); break;
      case 4: Serial.println("Thu"); break;
      case 5: Serial.println("Fri"); break;
      case 6: Serial.println("Sat"); break;
      case 7: Serial.println("Sun"); break;
      default: Serial.println("ERR");
    }
  }
  */

  YearNowTens = 0;
  YearNowOnes = YearNow;
  if (YearNowOnes >= 60) {YearNowOnes -= 60; YearNowTens += 6;}
  if (YearNowOnes >= 30) {YearNowOnes -= 30; YearNowTens += 3;}
  if (YearNowOnes >= 20) {YearNowOnes -= 20; YearNowTens += 2;}
  if (YearNowOnes >= 10) {YearNowOnes -= 10; YearNowTens++;}
 
  DateNowTens = 0;
  DateNowOnes = DateNow;
  if (DateNowOnes >= 20) {DateNowOnes -= 20; DateNowTens += 2;}
  if (DateNowOnes >= 10) {DateNowOnes -= 10; DateNowTens++;}
 
  MinNowTens = 0;
  MinNowOnes = MinNow;
  if (MinNowOnes >= 30) {MinNowOnes -= 30; MinNowTens += 3;}
  if (MinNowOnes >= 20) {MinNowOnes -= 20; MinNowTens += 2;}
  if (MinNowOnes >= 10) {MinNowOnes -= 10; MinNowTens++;}

  ZeroHourBlankNow = 0;

  if (HourMode24 ){
 
    HrNowTens = 0;
    HrNowOnes = HrNow;
    if (HrNowOnes >= 20) {HrNowOnes -= 20; HrNowTens += 2;}
    if (HrNowOnes >= 10) {HrNowOnes -= 10; HrNowTens++;}

    AMPM24HdisplayNow = ' ';

  }
  else
  {
    byte HrNowTemp = HrNow;
    if (HrNow >= 12)
    {
      HrNowTemp -= 12;
      AMPM24HdisplayNow = 'P';
    }
    else
      AMPM24HdisplayNow = 'A';

    if (HrNowTemp == 0 )
    {
      HrNowTens = 1;
      HrNowOnes = 2;
    }
    else {  
      HrNowTens = (HrNowTemp >= 10 ? 1 : 0);
      HrNowOnes = HrNowTemp - 10 * HrNowTens;

      if (HrNowTens == 0)
        ZeroHourBlankNow = 1;
    }
  }
 
  byte HalfSecNowTemp = HalfSecNow;
  SpinnerNow = 0;
  if (HalfSecNowTemp >= 60) {HalfSecNowTemp -= 60; SpinnerNow += 4;}
  if (HalfSecNowTemp >= 30) {HalfSecNowTemp -= 30; SpinnerNow += 2;}
  if (HalfSecNowTemp >= 15) {HalfSecNowTemp -= 15; SpinnerNow++;}

  if ((SettingTime == 0) && (HalfSecNow==0))
  {  // Only check once per minute
    if (AlarmEnabled)  {

      if ((AlarmTimeHr == HrNow ) && (AlarmTimeMin == MinNow ))
      {
        AlarmNow = 1;
        snoozed = 0;
        NextAdvanceSound = 0;
        SoundSequence = 0;
      }

      if (snoozed)
        if  ((AlarmTimeSnoozeHr == HrNow ) && (AlarmTimeSnoozeMin == MinNow ))
        {
          AlarmNow = 1;
          snoozed = 0;
          NextAdvanceSound = 0;
          SoundSequence = 0;
        }
    }

    // LastAlarmCheckMin = MinNow;

  }

}

void CalculateNewAlarm (void)
{  // Update current display representation of the Alarm time

  if (AlarmTimeMin > 59){
    AlarmTimeMin -= 60;
    AlarmTimeHr++;
  }
  if  (AlarmTimeHr > 23){
    AlarmTimeHr -= 24;
  }

  MinAlrmTens = AlarmTimeMin / 10;
  MinAlrmOnes = AlarmTimeMin - 10 * MinAlrmTens;

  ZeroHourBlankAlrm = 0;

  if (HourMode24 ){
    HrAlrmTens = AlarmTimeHr / 10;
    HrAlrmOnes = AlarmTimeHr - 10 * HrAlrmTens;

    AMPM24HdisplayAlrm = 'H';
  }
  else
  {
    byte AlarmTimeHrTemp = AlarmTimeHr;
    if (AlarmTimeHr >= 12)
    {
      AlarmTimeHrTemp -= 12;
      AMPM24HdisplayAlrm = 'P';
    }
    else
      AMPM24HdisplayAlrm = 'A';

    if (AlarmTimeHrTemp == 0 )
    {
      HrAlrmTens = 1;
      HrAlrmOnes = 2;
    }
    else{  
      HrAlrmTens = AlarmTimeHrTemp / 10;
      HrAlrmOnes = AlarmTimeHrTemp - 10 * HrAlrmTens;

      if (HrAlrmTens == 0)
        ZeroHourBlankAlrm = 1;
    }
  }

  snoozed = 0;  //  Recalculating alarm time *turns snoose off.*

  if (AlarmEnabled)  
    if ((AlarmTimeHr == HrNow ) && (AlarmTimeMin == MinNow ))
    {
      AlarmNow = 1;
      NextAdvanceSound = 0;
      SoundSequence = 0;
    }

}




void LoadShiftRegBuffers( void)
{
  // Map the 5-character cotents of the ASCII Character Buffer into the 15 SPI output bytes
  //  (three bytes per LED character) needed to draw those characters.
  byte alphaPosTemp;
  byte j = 0;
  while (j < 5)
  {
    alphaPosTemp = 3 * CharacterBuffer[j];
    bufl1[j] = AlphaArray[alphaPosTemp++];
    bufh1[j] = AlphaArray[alphaPosTemp++];
    bufh2[j] = AlphaArray[alphaPosTemp];    
    j++;
  }
}



void refreshDisplay (void)
{

  byte k1, k2; // temporary register values
  byte i,j; // dummy variables
  unsigned int onPeriod;
  unsigned int offPeriod;

  //MainBright can be 0 to 13; 14 levels of brightness. 0 is OFF.
  //MainBright = 1;  // for test only


  byte tempbright = MainBright;

  if (VCRmode){
    if (HalfSecNow & 2)
      tempbright = 0;
  }



  if (tempbright == 0)
  {
    PORTA |= 64;    // Blank LED driver

  }
  else if (tempbright < 5)
  {

    offPeriod = 240;
    if (tempbright == 2)
      offPeriod = 60;
    if (tempbright == 3)
      offPeriod = 15;
    if (tempbright == 4)
      offPeriod = 3;

    i = 0;
    while (i < 32)  // Number of loops through the five digits (for low-power modes) // Normally 32
    {
      j = 0;
      while (j < 5)
      {

        AlphaWrite(bufl1[j],bufh1[j],bufh2[j]);      
        Latch();
        PORTA &= ~(1 << j);  // Enable character
        //PORTA &= 191;  // Enable LED Driver

        k1 = PORTA & 191;   // Enable LED Driver
        k2 = PORTA | 64;    // Blank LED driver    //  Was  PORTA |= _BV(6);


        // For low brightness levels, we use a very low duty cycle PWM.
        // That's normally fine, but the Arduino interrupts in the background
        // (for example, the one that keeps our millisecond timer accurate!)
        // create tiny timing varations, and so these short "on" bursts
        // can have *very* poor consistency, leading to a jittery, flickery
        // display.  To get around this, we temporarily turn off interrupts,
        // for just long enough to turn on the LEDs for a few clock cycles
        // (under 1 us).   Once interrupts are back on, pending interrupt
        // requests will be serviced, so we should not lose any time in the
        // process.  *However* take great care if you extend this
        // "interrupt free" section of the code to any longer duration.


        byte SREGtemp = SREG;
        cli(); // Disable interrupts
        PORTA = k1;

        asm volatile("nop\n\t"
          "nop\n\t"
          "nop\n\t"
          "nop\n\t"
          "nop\n\t"
          ::);

        PORTA = k2;
        SREG = SREGtemp; // reenable interrupts.

        delayMicroseconds(offPeriod);

        j++;
      }
      i++;
    }
  }
  else
  {    // Higher duty-cycle modes
    if (tempbright > 12)
    {
      tempbright = 13;
    }
    //   onPeriod =  15 + ((tempbright - 3) * (tempbright - 3) ) * 4;  // Nonlinear brightness scale!
    //   offPeriod = 339 - onPeriod;

    onPeriod =  15 + ((tempbright - 4) * (tempbright - 4) ) * 2;  // Nonlinear brightness scale!
    offPeriod = 178 - onPeriod;




    i = 0;
    while (i < 16)  // Number of loops through the five digits (for high-power modes) // Normally 16
    {

      j = 0;
      while (j < 5)

      {

        AlphaWrite(bufl1[j],bufh1[j],bufh2[j]);      
        Latch();
        PORTA &= ~(1 << j);  // Enable character
        //PORTA &= 191;  // Enable LED Driver

        k1 = PORTA & 191;   // Enable LED Driver
        k2 = PORTA | 64;    // Blank LED driver    //  Was  PORTA |= _BV(6);

        PORTA = k1;
        delayMicroseconds(onPeriod);  
        PORTA = k2;

        if (offPeriod != 0)
          delayMicroseconds(offPeriod);

        j++;
      }
      i++;
    }
  }

}


void TurnAlarmOff (void)
{      
  AlarmNow = 0;
  noTone(13);
  PORTD |= 32; // Turn off speaker

}

void setup()                    // run once, when the sketch starts
{

  PORTA = 127;
  PORTB = buttonmask;  // Pull-up resistors for buttons
  PORTC = 3;  // PULL UPS for I2C
  PORTD = 0;

  DDRD = _BV(6) | _BV(7);  // LED on PD6, PD7
  DDRC = _BV(2);  // Latch pin, PC2    
  DDRB = _BV(4) | _BV(5) | _BV(7);  // SS, SCK, MOSI are outputs
  DDRA = 127;  // Outputs: PA0-PA5 ("rows") and Blank pin, PA6

  //ENABLE SPI, MASTER, CLOCK RATE fck/4:      
  SPCR = _BV(SPE) | _BV(MSTR);  // Initialize SPI, fast!
  SPSR |= 1;  // enable double-speed mode!     

  AlphaWrite(0,0,0);
  Latch();
 
  TimeError = 0; // will be zero until we find something wrong

  HalfSecNow = 0;
  SpinnerNow = 0;
  MinNow = 0;
  MinNowTens = 0;
  MinNowOnes = 0;

  HrNow = 0;
  HrNowTens = 0;
  HrNowOnes = 0;
 
  WeekdayNow = 6; // Saturday
 
  DateNow = 1;
  DateNowTens = 0;
  DateNowOnes = 1;
 
  MonthNow = 1; // January
  YearNow = 0; // AD 2000
  YearNowTens = 0;
  YearNowOnes = 0;

  LEDTestMode = 0;
  HoldLEDTest = 0;
  SerialDisplayMode = 0;

  ZeroHourBlankNow = 0;
  IdleTimeEnded = 1;
  ZeroHourBlankAlrm = 0;
  AMPM24HdisplayAlrm = ' ';
  MinAlrmOnes = 0;
  MinAlrmTens = 0;
  HrAlrmOnes = 0;
  HrAlrmTens = 0;
  VCRmode = 0;  // Time is NOT yet set, but this should be zero until after "hello world."
  OptionMode = 0;

  // LastAlarmCheckMin = 0;
  HoldLoopCount = 0;

  EEReadSettings();   // Read stored settings from EEPROM.

  updateNightLight();


  VCRmode = 1;  // Time is NOT yet set.
  //FactoryResetDisable = 0;    

  PINBLast =  PINB & buttonmask;

  HoldTimeSet = 0;
  HoldAlarmSet = 0;
  HoldOption = 0;

  MomentaryOverridePlus = 0;
  MomentaryOverrideMinus = 0;


  SettingTime = 0;  // Normally 0, while not setting time.  

  SettingAlarm = 0;  // Same deal.

  PORTA |= _BV(6); // Blank LED driver

  DisplayWordSequence("HELLO",1000);

  while (  millis() < WordStopTime){    
    refreshDisplay ();
    DisplayWordMode = 0;
  }

  delay (10);

  DisplayWordSequence("WORLD",2000);

  while (  millis() < WordStopTime){    
    refreshDisplay ();
    DisplayWordMode = 0;
  }

  delay (250);


  VCRmode = 1;  // Time is NOT yet set.

  Serial.begin(19200);
  DateTime.sync(0);

  Wire.begin();


  ExtRTC = 0;
  // Check if RTC is avaiiable, and use it to set the time if so.
  ExtRTC = RTCgetTime();
  // If no RTC is found, no attempt will be made to use it thereafter.

  if (ExtRTC)          // If time is already set from the RTC...
    VCRmode = 0;

  CalculateNewTime();
  CalculateNewAlarm();

  TurnAlarmOff();
  snoozed = 0;
  noTone(13);  
  PORTD |= 32; // Turn off speaker
}  // End Setup



void loop()
{
  //byte HighLine, LowLine;
  byte PINBcopy;
  //byte alphaPosTemp;
  byte i,j;  // Dummy indices


  millisCopy = millis();

  PINBcopy = PINB & buttonmask;

  if (PINBcopy != PINBLast)  // Button change detected
  {

    endIdleTime = millisCopy + IdleDelay; //  Idle time for EEPROM purposes is 5 seconds.
    IdleTimeEnded = 0;

    VCRmode = 0;  // End once any buttons have been pressed...
    //TimeSinceButton = 0;

    if ((PINBcopy & 1) && ((PINBLast & 1) == 0))
    { //"Snooze" / Set Alarm Time Button was pressed and just released!



      if (OptionMode) {

        if ( OptionMode > 1)
          OptionMode--;
        else
          OptionMode = OptionsMax;
        OptionNameSequence = 0;
        TurnAlarmOff();
      }
      else if (SettingAlarm) {
        SettingAlarm = 0;  // End alarm time display/setting mode, when the "Snooze" button is released.
      }  
      else if (AlarmNow)
      {        
        snoozed = 1;
        TurnAlarmOff();        
        DisplayWordSequence("SNOOZ",2500);

        AlarmTimeSnoozeHr = HrNow;
        AlarmTimeSnoozeMin  = MinNow + 9;    // Nine minutes, from time *snooze button pressed*

        if  ( AlarmTimeSnoozeMin > 59){
          AlarmTimeSnoozeMin -= 60;
          AlarmTimeSnoozeHr += 1;
        }
        if (AlarmTimeSnoozeHr > 23)
          AlarmTimeSnoozeHr -= 24;
      }
      else
      {
        // show today's date for a few seconds
        WordStopTime = millis() + 4500;
        switch (WeekdayNow) {
          case 1: LoadCharBuffer("MO   "); break;
          case 2: LoadCharBuffer("TU   "); break;
          case 3: LoadCharBuffer("WE   "); break;
          case 4: LoadCharBuffer("TH   "); break;
          case 5: LoadCharBuffer("FR   "); break;
          case 6: LoadCharBuffer("SA   "); break;
          case 7: LoadCharBuffer("SU   "); break;
        }
        CharacterBuffer[0] = (DateNowOnes + numberOffset);
        CharacterBuffer[1] = (DateNowTens + numberOffset);
        if (MonthNow < 10) {
          CharacterBuffer[2] = (MonthNow + numberOffset);
        } else {
          CharacterBuffer[2] = ((MonthNow - 10) + singleCellTen);
        }

        LoadShiftRegBuffers();
        bufl1[2] |= 64; // punctuation
        bufl1[3] |= 64;
        DisplayWordMode = 1;
      }
    }

    if ((PINBcopy & 2) && ((PINBLast & 2) == 0))
    { //"Time" / Alarm On/off Button was pressed and just released!


      if (OptionMode) {

        if ( OptionMode < OptionsMax)
          OptionMode++;
        else
          OptionMode = 1;

        OptionNameSequence = 0;
        TurnAlarmOff();
      }
      else if ((AlarmNow) || (snoozed)){  // Just Turn Off Alarm

        TurnAlarmOff();  
        snoozed = 0;

        delay (100);
        DisplayWordSequence("ALARM",1000);

        while (  millis() < WordStopTime){    
          refreshDisplay ();
          DisplayWordMode = 0;
        }

        delay (100);

        DisplayWordSequence(" OFF ",1000);

        while (  millis() < WordStopTime){    
          refreshDisplay ();
          DisplayWordMode = 0;
        }

        delay (100);

      }
      else if (SettingTime) {
        // We *have* been setting the time, but have just released the button.


        SettingTime = 0; // Turn off time-setting mode.


        if  (ExtRTC) // Write the new time to the RTC, RIGHT NOW.
        {
          RTCsetTime(YearNow,MonthNow,DateNow,WeekdayNow,HrNow,MinNow,HalfSecNow/2);      
        }


      }
      else {
        // Normal adjustment mode.
        if (AlarmEnabled)
          AlarmEnabled = 0;
        else  
          AlarmEnabled = 1;    


      }  
    }

    if ((PINBcopy & 4) && ((PINBLast & 4) == 0))  //"+" Button was just released!
    {
      if ( MomentaryOverridePlus)
      {  
        MomentaryOverridePlus = 0;
        // Ignore this transition if it was part of a hold sequence.
      }
      else
      {
        if (OptionMode) {

          if (OptionMode == 1)  {
            // 24-HR - AM/PM mode switch

            if (HourMode24)
              HourMode24 = 0;
            else
              HourMode24 = 1;

            CalculateNewTime();    // Make sure time is ready to display, in the correct format!
            CalculateNewAlarm();   // Make sure alarm is ready to display!, in the correct format!

          }
          else if (OptionMode == 2) {
            if (NightLightType < 2)
              NightLightType++;
            else
              NightLightType = 0;

            updateNightLight();
          }
          else if (OptionMode == 3) {
            if (AlarmTone < 3)
              AlarmTone++;
            else
              AlarmTone = 0;

          }
          else if (OptionMode == 4) {
            // ACTIVATE Alarm test mode!   Momentarily turn the alarm on.
            SoundSequence = 0;
            AlarmNow = 1;
          }

        }
        else if ((PINBcopy & 2) == 0)   {  // Time-setting button is currently depressed

          SettingTime = 1;  // Flag that we are now changing the time.
          MinNow++;       // Advance the time!
          HalfSecNow=0;       // set seconds to zero if individual setp
          CalculateNewTime();

        }
        else if ((PINBcopy & 1) == 0)   {  // Alarm-setting button is currently depressed

          SettingAlarm = 1;  // Individual step mode
          AlarmTimeMin++;       // Advance the Alarm time!  

          CalculateNewAlarm();

        }
        else {  
          // Brightness control mode
          if (MainBright < 13)
            MainBright++;
        }
      }
    }


    if ((PINBcopy & 8) && ((PINBLast & 8) == 0))
    { //"-" Button was pressed and just released!

      if ( MomentaryOverrideMinus)
      {  
        MomentaryOverrideMinus = 0;
        // Ignore this transition if it was part of a hold sequence.
      }
      else
      {

        if (OptionMode) {

          if (OptionMode == 1)  {
            // 24-HR - AM/PM mode switch

            if (HourMode24)
              HourMode24 = 0;
            else
              HourMode24 = 1;

            CalculateNewTime();    // Make sure time is ready to display, in the correct format!
            CalculateNewAlarm();   // Make sure alarm is ready to display!, in the correct format!

          }
          else if (OptionMode == 2) {
            if (NightLightType > 0)
              NightLightType--;
            else
              NightLightType = 2;
            updateNightLight();
          }
          else if (OptionMode == 3) {
            if (AlarmTone > 0)
              AlarmTone--;
            else
              AlarmTone = 3;
          }
          else if (OptionMode == 4) {
            // Alarm test mode!
            TurnAlarmOff();
          }
        }
        else if ((PINBcopy & 2) == 0)   {  // Time-setting button is currently depressed

          SettingTime = 1;  // Declare that we are in individual step mode
         
          HalfSecNow=0; // if in individual step mode, zero the seconds
          HrNow+=23;
          MinNow+=59;
          DateNow--;
          WeekdayNow--;

          CalculateNewTime();

        }
        else if ((PINBcopy & 1) == 0)   {  // Alarm-setting button is currently depressed

          SettingAlarm = 2;  // Individual step mode  

          AlarmTimeHr+=23;
          AlarmTimeMin+=59;

          CalculateNewAlarm();

        }
        else {  //Normal brightness adjustment mode
          if (MainBright > 0)
            MainBright--;
        }  
      }
    }
  }


  PINBLast = PINBcopy;

  // The next if statement detects and deals with the millis() rollover.
  // This introduces an error of up to  1 s, about every 50 days.  
  //
  // (If you have the standard quartz timebase, this will not dominate the inaccuracy.
  // If you have the optional RTC, this error will be corrected next time we read the
  // time from the RTC.)

  if (millisCopy < LastTime) {
    LastTime = 0;
    NextAdvance = 0;      // Other variables that could cause issues if millis rolls over!
    WordStopTime = 0;    // Other variables that could cause issues if millis rolls over!
  }

  if ((millisCopy - LastTime) >= 500)
  {
    LastTime += 500;    


    // Check to see if any buttons are being held down:

    if (( PINB & buttonmask) == buttonmask)
    {  // No buttons are pressed.
      // Reset the variables that check to see if buttons are being held down.

      HoldTimeSet = 0;
      HoldOption = 0;
      HoldAlarmSet = 0;
      HoldLEDTest = 0;
      NumAdvances = 0;

      // FactoryResetDisable = 1;

      // Save EEPROM if updated.
      if (millisCopy  > endIdleTime)
      {
        if (IdleTimeEnded == 0){
          EESaveSettings();
          IdleTimeEnded = 1;
        }
      }


    }
    else
    {   // At least one button is down!

      // Note which buttons are being held down

      if ((( PINB & buttonmask) == 10) ||(( PINB & buttonmask) == 6))   // Alarm-time set is down
      { // Alarm button is down, and so is EITHER + or -.

        HoldAlarmSet++;  
        HoldOption = 0;
        HoldTimeSet = 0;
        HoldLEDTest = 0;
      }

      if ((( PINB & buttonmask) == 9) ||(( PINB & buttonmask) == 5)) //Time-set is pressed down.
      { // Time button is down, and so is EITHER + or -.

        HoldTimeSet++;      
        HoldOption = 0;
        HoldAlarmSet = 0;
        HoldLEDTest = 0;
      }

      if (( PINB & buttonmask) == 3)  // "+" and "-" are down.
      {
        HoldOption++;   // We are holding for option setting mode.
        HoldTimeSet = 0;
        HoldAlarmSet = 0;
        HoldLEDTest = 0;
      }    

      if (( PINB & buttonmask) == 12)  // "time" and "alarm" are down.
      {
        if (HoldLEDTest < 10)
          HoldLEDTest++;   // We are holding for option setting mode.
        HoldTimeSet = 0;
        HoldAlarmSet = 0;
        HoldOption = 0;


      }    

    }

    if (HoldAlarmSet > 2)
    {
      MomentaryOverridePlus = 1;       // Override momentary-action of switches
      MomentaryOverrideMinus = 1;      // since we've detected a hold-down condition.
      //MomentaryOverrideSetAlarm = 1;

      OptionMode = 0;



      if (HoldAlarmSet > 250)
        HoldAlarmSet = 250;


      /*  // TODO: Add factory restore option
       // Hold + and - for 3 s AT POWER ON to restore factory settings.
       if ( FactoryResetDisable == 0){
       ApplyDefaults();
       EESaveSettings();
       AllLEDsOff();        // Blink LEDs off to indicate restoring data
       delay(100);
       }
       else
       {
       if (AlignMode) {
       AlignMode = 0;
       }
       else {
       }
       }
       */
    }

    if (HoldLEDTest == 8)  //"time" and "alarm" Held down, together, for 4 seconds
    {  
      if (LEDTestMode)
        LEDTestMode = 0;
      else{
        LEDTestMode = 1;
        DisplayWordSequence("VER10",2000);   // Display software version number, 1.0
        bufl1[1] |= 64;  // Add lower DP for proper "1.0" !
      }
    }


    if (HoldOption == 4)  // + and - Held down, together, for 2 seconds
    {  

      MomentaryOverridePlus = 1;
      MomentaryOverrideMinus = 1;      // since we've detected a hold-down condition.

      if (OptionMode) {
        OptionMode = 0;  
      }
      else {
        OptionMode = 1;  // On *and* set position in menu.
      }
    }

    if (HoldTimeSet > 2)
    {
      MomentaryOverridePlus = 1;       // Override momentary-action of switches
      MomentaryOverrideMinus = 1;      // since we've detected a hold-down condition.
      // MomentaryOverrideSetTime = 1;

      OptionMode = 0;
      SettingAlarm = 0;

      if (HoldTimeSet > 250)
        HoldTimeSet = 250;
    }

    // Note: this section could act funny if you hold the buttons for 256 or more seconds.  
    // So... um... don't do that.  :P

    HalfSecNow++;       // Advance the time!
    CalculateNewTime();
  }  


  if (( PINB & buttonmask) == buttonmask) {
    HoldTimeSet = 0;
    HoldOption = 0;
    HoldAlarmSet = 0;
    NumAdvances = 0;
    //FactoryResetDisable = 1;
  }
  else {   // Other "Immediate" actions if buttons are being held

    // Detect if + or - is released while scanning time (real or alarm) forwards or backwards.

    if (( PINB & 12) == 12){
      HoldAlarmSet = 0;
      HoldTimeSet = 0;
      NumAdvances = 0;
      ShowDateNow = 0;
    }

    if (millisCopy > NextAdvance)     //Holding buttons to advance time settings...
    {
      if (( PINB & buttonmask) == 10)    // Alarm +
      {
        if (HoldAlarmSet > 2)
        {      
          AlarmTimeMin+=15;       // Advance the Alarm time!  
          CalculateNewAlarm();
          NumAdvances++;
        }
        if (NumAdvances <= 8) {
          NextAdvance = millisCopy + 700;
        }
        else if (NumAdvances <= 24) {
          NextAdvance = millisCopy + 300;
        }
        else {
          NextAdvance = millisCopy + 150;
        }
      }
      if (( PINB & buttonmask) == 6)   // Alarm -
      {
        if (HoldAlarmSet > 2)
        {
          AlarmTimeHr+=23;
          AlarmTimeMin+=45;
          CalculateNewAlarm();
          NumAdvances++;
        }
        if (NumAdvances <= 8) {
          NextAdvance = millisCopy + 700;
        }
        else if (NumAdvances <= 24) {
          NextAdvance = millisCopy + 300;
        }
        else {
          NextAdvance = millisCopy + 150;
        }        
      }      
      if (( PINB & buttonmask) == 9)   // Time +
      {

        if (HoldTimeSet > 3)
        {
          if (NumAdvances < 128) {
            MinNow+=15;    // Advance the time!
          } else if (NumAdvances < 288){
            HrNow++;
          } else if (NumAdvances < 322){
            DateNow++;
            WeekdayNow++;
          } else {
            DateNow+=14;
          }
          SettingTime = 1;  // Flag that time is changing, so that we don't reset it by RTC.idl
          CalculateNewTime();
          NumAdvances++;
        }
        if (NumAdvances <= 8) {
          NextAdvance = millisCopy + 640;
        }
        else if (NumAdvances <= 32) {
          NextAdvance = millisCopy + 240;
        }
        else if (NumAdvances <= 128){  // 32 hours
          NextAdvance = millisCopy + 80;
        }
        else if (NumAdvances <= 288) {  // 8 days
          NextAdvance = millisCopy + 80;
        }
        else if (NumAdvances <= 322){  // 42 days
          NextAdvance = millisCopy + 240;
          ShowDateNow = 1;
        } else if (NumAdvances <= 346 ) { // 54 weeks
          NextAdvance = millisCopy + 320;
        } else {
          NextAdvance = millisCopy + 80;
        }
      }
      if (( PINB & buttonmask) == 5) // Time -
      {
        if (HoldTimeSet > 3)
        {
          SettingTime = 1;  // Flag that time is changing, so that we don't reset it by RTC.
         
          if (NumAdvances < 128) {
            HrNow+=23;
            MinNow+=45;
            DateNow--;
            WeekdayNow--;
          } else if (NumAdvances < 288) {
            HrNow+=23;
            DateNow--;
            WeekdayNow--;
          } else if (NumAdvances < 322){
            DateNow--;
            WeekdayNow--;
          } else {
            DateNow-=14;
          }
         
          CalculateNewTime();
          NumAdvances++;
        }
        if (NumAdvances <= 8) {
          NextAdvance = millisCopy + 640;
        }
        else if (NumAdvances <= 24) {
          NextAdvance = millisCopy + 240;
        }
        else if (NumAdvances <= 128){  // 32 hours
          NextAdvance = millisCopy + 80;
        }
        else if (NumAdvances <= 288) {  // 8 days
          NextAdvance = millisCopy + 80;
        }
        else if (NumAdvances <= 322){  // 42 days
          NextAdvance = millisCopy + 240;
          ShowDateNow = 1;
        } else if (NumAdvances <= 346 ) { // 54 weeks
          NextAdvance = millisCopy + 320;
        } else {
          NextAdvance = millisCopy + 80;
        }  
      }
    }
  }



  if (AlarmNow )        // Visual display and sounds durign ALARM sequences
  {
    if (OptionMode == 0)
    {    // If we are testing the alarm sound in option mode,
      // do not change the display.
      if (HalfSecNow & 2) {

        DisplayWordSequence("ALARM",100);
        //LoadShiftRegBuffers();
        //DisplayWordMode = 1;
      }
    }

    //SoundSequence
    if (millisCopy > NextAdvanceSound){

      if (AlarmTone == 3)   // Siren Tone
      {
        if (SoundSequence < 200)
        {
          tone(13, 20 + 5 * SoundSequence, 20);
          NextAdvanceSound = millisCopy + 10;
          SoundSequence++;
        }
        else if (SoundSequence == 200)
        {
          tone(13, 20 + 5 * SoundSequence, 2000);
          NextAdvanceSound = millisCopy + 1500;
          SoundSequence++;
        }
        else {
        NextAdvanceSound = millisCopy + 1000;
         SoundSequence = 0;
         noTone(13);
           PORTD |= 32; // Turn off speaker
        }

      }
      else   if (AlarmTone == 2)   // Low Tone
      {
        if (SoundSequence < 8)
        {
          if (SoundSequence & 1)
          {
            tone(13, 100, 300);
            NextAdvanceSound = millisCopy + 200;
            SoundSequence++;
          }
          else
          {
            NextAdvanceSound = millisCopy + 200;
            SoundSequence++;  
            noTone(13);  
            PORTD |= 32; // Turn off speaker  
            //
          }
        }
        else
        {
          NextAdvanceSound = millisCopy + 1000;
          SoundSequence = 0;
          noTone(13);  
          PORTD |= 32; // Turn off speaker  
        }

      }
      else  if (AlarmTone == 1) // Med Tone
      {
        if (SoundSequence < 6)
        {
          if (SoundSequence & 1)
          {
            tone(13, 1000, 300);
            NextAdvanceSound = millisCopy + 200;
            SoundSequence++;
          }
          else
          {
            NextAdvanceSound = millisCopy + 200;
            SoundSequence++;  
            noTone(13);  
            PORTD |= 32; // Turn off speaker    
          }

        }
        else
        {
          NextAdvanceSound = millisCopy + 1400;
          SoundSequence = 0;
          noTone(13);
          PORTD |= 32; // Turn off speaker
        }

      }
      else  if (AlarmTone == 0) // High Tone
      {
        if (SoundSequence < 6)
        {
          if (SoundSequence & 1)
          {
            tone(13, 2050, 400);
            NextAdvanceSound = millisCopy + 300;
            SoundSequence++;
          }
          else
          {
            NextAdvanceSound = millisCopy + 200;
            SoundSequence++;  
            noTone(13);  
            PORTD |= 32; // Turn off speaker    
          }

        }
        else
        {
          NextAdvanceSound = millisCopy + 1000;
          SoundSequence = 0;
          noTone(13);
          PORTD |= 32; // Turn off speaker
        }

      }
    }
  }




  if (LEDTestMode)
  {

    if (millisCopy > WordStopTime) {
      LEDTestStage++;
      byte atemp, btemp, ctemp;
      byte cpos = LEDTestStage / 18;  //(which char to use)

      if (cpos > 4){
        LEDTestStage = 0;
        cpos = 0;
      }

      byte dtemp = LEDTestStage - 18 * cpos;

      if (dtemp <= 7)
      {
        atemp = (1 << dtemp);
        btemp = 0;
        ctemp = 0;
      }
      else if (dtemp <= 9)
      {
        atemp = 0;
        btemp = (1 << (dtemp - 8));
        ctemp = 0;
      }
      else
      {
        atemp = 0;
        btemp = 0;
        ctemp = (1 << (dtemp - 10));
      }

      // Load buffers
      j = 0;
      while (j < 5)
      {
        if (j == cpos)
        {
          bufl1[j] = atemp;
          bufh1[j] = btemp;
          bufh2[j] = ctemp;  
        }
        else
        {
          bufl1[j] = 0;
          bufh1[j] = 0;
          bufh2[j] = 0;  
        }
        j++;
      }

      WordStopTime = millisCopy + 1000; // 100 ms per segment
      DisplayWordMode = 1;

    }
  }




  if (DisplayWordMode)
  {
    if (SerialDisplayMode == 0){
      if (millisCopy > WordStopTime)
        DisplayWordMode = 0;
    }

  }

  else if (OptionMode){

    if (OptionMode == 1) {  // AM-PM / 24 HR  

      if (HourMode24)
        LoadCharBuffer("24 HR");
      else
        LoadCharBuffer("AM/PM");

      LoadShiftRegBuffers();
    }
    else if (OptionMode == 2)
    {
      if (OptionNameSequence == 0)
      {
        DisplayWordSequence("NIGHT",700);      
      }
      else if (OptionNameSequence == 1)
        DisplayWordSequence("     ",100);
      else if (OptionNameSequence == 2)
      {
        DisplayWordSequence("LIGHT",700);
      }
      else if (OptionNameSequence == 3)
        DisplayWordSequence("     ",100);
      else {
        if(  NightLightType == 0)
        {
          LoadCharBuffer(" NONE");
          LoadShiftRegBuffers();
        }
        else if(  NightLightType == 1)
        {
          LoadCharBuffer("LED_L");
          LoadShiftRegBuffers();
        }
        else if(  NightLightType == 2)
        {
          LoadCharBuffer("LED_H");
          LoadShiftRegBuffers();
        }

      }
    }
    else if (OptionMode == 3)
    {
      if (OptionNameSequence == 0)
      {
        DisplayWordSequence("ALARM",700);
      }
      else if (OptionNameSequence == 1)
        DisplayWordSequence("     ",100);
      else if (OptionNameSequence == 2)
      {
        DisplayWordSequence("TONE ",700);
      }
      else if (OptionNameSequence == 3)
        DisplayWordSequence("     ",100);
      else {
        if(  AlarmTone == 0)
        {
          LoadCharBuffer(" HIGH");
          LoadShiftRegBuffers();
        }
        else if(  AlarmTone == 1)
        {
          LoadCharBuffer(" MED ");
          LoadShiftRegBuffers();
        }
        else if(  AlarmTone == 2)
        {
          LoadCharBuffer(" LOW ");
          LoadShiftRegBuffers();
        }
        else if(  AlarmTone == 3)
        {
          LoadCharBuffer("SIREN");
          LoadShiftRegBuffers();
        }

      }
    }
    else if (OptionMode == 4)
    {
      if (OptionNameSequence == 0)
      {
        DisplayWordSequence("TEST ",700);

      }
      else if (OptionNameSequence == 1)
        DisplayWordSequence("     ",100);
      else if (OptionNameSequence == 2)
      {
        DisplayWordSequence("SOUND",700);
      }
      else if (OptionNameSequence == 3)
        DisplayWordSequence("     ",100);
      else{
        LoadCharBuffer("USE+-");
        LoadShiftRegBuffers();
      }
    }
  }
  else {

    if (((PINBcopy & 1) == 0)){
      // Display Alarm time whenever "Alarm/Snooze" button is pressed.
      CharacterBuffer[0] = (AMPM24HdisplayAlrm - asciiOffset);
      CharacterBuffer[1] = (MinAlrmOnes + numberOffset);
      CharacterBuffer[2] = (MinAlrmTens + numberOffset);
      CharacterBuffer[3] = (HrAlrmOnes + numberOffset);
      CharacterBuffer[4] = (HrAlrmTens + numberOffset);
       
      // Leading-Zero blanking for 12-hour mode:
 
      if (ZeroHourBlankAlrm)
        CharacterBuffer[4] = (' ' - asciiOffset);
    }
    else {
      // "Normal" time display:
      if (ShowDateNow) {
        CharacterBuffer[0] = (DateNowOnes + numberOffset);
        CharacterBuffer[1] = (DateNowTens + numberOffset);
        if (MonthNow < 10) {
          CharacterBuffer[2] = (MonthNow + numberOffset);
        } else {
          CharacterBuffer[2] = ((MonthNow - 10) + singleCellTen);
        }
        CharacterBuffer[3] = (YearNowOnes + numberOffset);
        CharacterBuffer[4] = (YearNowTens + numberOffset);
      } else {
        if (TimeError) CharacterBuffer[0] = ('E' - asciiOffset);
        else if (AMPM24HdisplayNow == ' ') CharacterBuffer[0] = (SpinnerNow + spinnerOffset);
        else CharacterBuffer[0] = (AMPM24HdisplayNow - asciiOffset);
        CharacterBuffer[1] = (MinNowOnes + numberOffset);
        CharacterBuffer[2] = (MinNowTens + numberOffset);
        CharacterBuffer[3] = (HrNowOnes + numberOffset);
        CharacterBuffer[4] = (HrNowTens + numberOffset);
      }

      // Leading-Zero blanking for 12-hour mode:

      if (ZeroHourBlankNow)
        CharacterBuffer[4] = (' ' - asciiOffset);
    }

    LoadShiftRegBuffers();

    // Add time delimiter (colon) for time display, whether that's "real" time or the alarm.
    if (ShowDateNow) {
      bufl1[2] |= 64;
      bufl1[3] |= 64;
    } else {
      bufl1[2] |= 128;
      bufl1[3] |= 64;
    }    

    if (AlarmEnabled)
      bufl1[4] |= 128;    // Upper left dot
  }

  // Time (or word) to display is now computed.
  // Now is the place in the loop when we switch gears, and
  // actually light up the LEDs. :)

  refreshDisplay();


  // Can this sync be tried only once per second?
  if( getPCtime()) {  // try to get time sync from pc


    if(DateTime.available()) { // update clocks if time has been synced

      DisplayWordSequence("SYNC ",1000);
      // Set time to that given from PC.
      MinNow = DateTime.Minute;
      HalfSecNow = (DateTime.Second) * 2;
      HrNow = DateTime.Hour;
      WeekdayNow = DateTime.DayofWeek;
      if (WeekdayNow==0) WeekdayNow = 7;
      DateNow = DateTime.Day;
      MonthNow = (DateTime.Month) + 1;
      YearNow = (DateTime.Year) % 100;

      //    if ( HrNow > 11)   // Convert 24-hour mode to 12-hour mode
      //    HrNow -= 12;    

      // Print confirmation
      Serial.print("Alpha Clock Five: Clock sync at: ");
      Serial.println(DateTime.now(),DEC);

      if ( prevtime != DateTime.now() )
      {
        if (ExtRTC)
          RTCsetTime(YearNow,MonthNow,DateNow,WeekdayNow,HrNow,MinNow,HalfSecNow/2);

        DateTime.available(); //refresh the Date and time properties
        digitalClockDisplay( );   // update digital clock
        prevtime = DateTime.now();
      }
    }
  }

}


/*
Simple alternative loop , for testing brightness/refresh rates
 void loop()
 {   refreshDisplay();
 }
 */






 






       
   



 All times are PDT. The time is now 03:29 PM.
Normal Topic Normal Topic
Locked Topic Locked Topic
Sticky Topic Sticky Topic
New Post New Post
Sticky Topic W/ New Post Sticky Topic W/ New Post
Locked Topic W/ New Post Locked Topic W/ New Post
View Anonymous Posts 
Able to Post 
Filtered HTML Allowed 
Censored Content 

Evil Mad Scientist Forum Archives — Read only!

Please visit our new forums for new discussions.


DIY Hardware for Electronic Art


The Original Egg-Bot Kit


Octolively
Interactive LED kits


Meggy Jr RGB
LED matrix game
development kit.


Business-card sized
AVR target boards


Peggy 2
LED Pegboard kits

My Account






Lost your password?