// Ugi's Dawn-Light Alarm Clock Sketch. // Version 0.0.10 - for PCB where 8<->9 exchanged and encoder reversed. // plus different shift reg pin assignments byte sketchVersion[3]={0,0,10}; // It is expected that later versions will read EEPROM data from earlier versions. // Earlier versions will default if faced with EEPROM data from later versions. // MIT Licence // Ugi Dec 2012 // Written for Arduino 022 running on ATmega328. // We need the following libraries: // ShiftPWM - Available from: http://www.elcojacobs.com/shiftpwm/ // Wire, EEPROM, SPI - all included in Arduino 022 distribution. // Working functions: // Interface 1307 RTC // Display hours and minutes on 4-digit display. // Read rotary encoder by interrupt. // Read switches - fast and slow presses. // Save armed status to DS1307 battery-backed RAM // Set time and date from rotary encoder. // Set fade-up time, hold time, buzzer delay and brightness of display from encoder. // Set alarms from encoder. // Read EEPROM, check signature and version number, read system and alarm data. // Save Signature & Version number plus alarm data to EEPROM save system data to EEPROM. // PWM control for nightlight, Alarm recognition, PWM lamp control for alarm, fade out alarm. // ATX power suuply control - chip running on stand-by power fires up full power for light. // Buzzer with on/off option, delay after full brighness & escelating beep-pattern // Security light mode - random delay of 45 min ave, light on for up to around 90 mins // Save security light mode to RTC ram so we restart in Sec mode if interrupted. // Unsure/needs testing - // To Do - compress code to fit 168!? - Improve the Security Light Mode? // Arduino Pin connections: // A0 - Sw1 - normally pulled low through 10K // A1 - Sw2 - normally pulled low through 10K // A2 - free // A3 - free // A4 - 1307 (I2C data) // A5 - 1307 (I2C clock) // D0 - Serial Tx // D1 - Serial Rx // D2 - Rotary encoder LHS // D3 - Rotary encoder RHS // D4 - Encoder press Switch - normally pulled low throu' 10K // D5 - Power supply control // D6 - Buzzer // D7 - 7219 Clock // D8 - 7219 Data // D9 - 7219 Latch // D10- '595 latch // D11- '595 MISO (unused) // D12- '595 Data (MOSI) // D13- '595 clock // General clock settings: #include - // usees analogue pins A4 (SDA - pin 27) & A5 (SCL- pin 28) for 2-wire data. #include - // needed by Shift PWM library. #include const byte DS1307_Address = 0x68; int noOfAlarms = 7; // This is the max number of alarms that can be set. We can handle between 1 and 10 alarms. boolean alarmOn[10] = {1,0,0,0,0,0,0,0,0,0}; // One alarm on by default. boolean buzzerOn[10]={0,0,0,0,0,0,0,0,0,0}; // buzzer off as default. boolean alarmNow = false; // alarm actually displaying when this is true. boolean buzzerNow= false; // buzzer actually ringing? byte alarmNumber = 0; // which alarm is on? unsigned long alarmStartTime; // Used for ramping lamp unsigned long buzzerStartTime; // Used for alarm tone. unsigned long buzzerToneTime;// Used for beep pattern byte buzzerDelay=5; // Minutes between full lamp power and start buzzer unsigned long buzzerDelayMS= long(buzzerDelay)*60000; // Delay as above but in ms byte rampTime=5; // Default number of minutes for lamp ramp. Will get from EEPROM later if already set. unsigned long alarmStepPeriod =(long(rampTime) *1875);// (1875 = 60 *1000 / 32)- Mins of alarm ramp duration (5) * 60 * 1000 / Number of steps of ramp (32) byte delayTime=30; // Default period of full brightness after alarm comes on. unsigned long alarmDelayPeriod = (long(delayTime)*60000);//(30 * 60 * 1000) // 30 min duration of max light after ramping const unsigned long alarmFadePeriod = (2 * 31); // 1000/32 = 31.25 - Fade out alarm over 2 seconds. byte alarmState =0; // Where are we in our ramping? byte buzzerState = 0; // Where are we in beep pattern const byte buzzerTiming[4][4]={ // Each row is a pattern defined as buzz, wait, buzz, wait. In 100ths of a second. { 5, 250, 5, 25}, // pattern 1 { 5, 100, 5, 5}, // pattern 2 { 20, 80, 20, 10}, // pattern 3 { 8, 8, 8, 8}}; //pattern 4 byte alarmMin[10]; // up to 10 alarms alarms with fields of day, hour and month. byte alarmHour[10];// We'll allow the day code to control groups of days as well as individual days. byte alarmDay[10]; // So - 1-7 days of the week starting Mon. 0 - everyday. 8 - Mon-Fri, 9- Sat & Sun. boolean armed = true; // Is alarm set? Indicator LED for this on the 7219. byte seconds = 45, oldseconds=0, minutes=52, hours=23, date=8, month=12, day=4, year=11, protect = 0; // defaults just to start somewhere. byte NEWseconds = 45, NEWminutes=52, NEWhours=23, NEWdate=8, NEWmonth=12, NEWday=4, NEWyear=11 ; // These are for setting. Probably don't need to be global any more. // 4 - digit display control settings: // Our 4-digit display is setup as in MAX 7219 datasheet but only the DP for digit 3 is wired to anything - use other (external) LEDs as indicators const int fourDigitClock = 7; // 4-digit clock pin to control 7219 const int fourDigitData = 8; // 4-digit data pin to control 7219 const int fourDigitLatch = 9; // 4-digit latch pin to control 7219 boolean fourDigitBlink = false; // Redundant? **** int LEDintensity = 3; // Intensity of display set by 7219 - range 0 to 15 boolean LEDcontrol = false; // This may now be redundant... **** unsigned long blinkTime = 0; // Used to control blinking of the HH:MM colon separator. // Display Charater Definitions: //Segments numbered clockwise from top MSB first with middle bar last. This takes 7 bits - MSB will be for decimal / indicator. // These are the characters that I consider unambiguously legible on a 7-seg display. Others might be contrived. Of these, W and M are the most missed. const byte character[36] = { B1111110, //0 B0110000, //1 B1101101, //2 B1111001, //3 B0110011, //4 B1011011, //5 B1011111, //6 B1110000, //7 B1111111, //8 B1111011, //9 B1000000, // Top Dash - 10 B0000001, // Middle Dash - 11 B0001000, // Bottom Dash - 12 B0000000, // Blank - 13 B1110111, // A - 14 // B - 8 B0011111, // b - 15 B1001110, // C - 16 (was 15) B0001101, // c - 17 B0111101, // d - 18 (was 16) B1001111, // E - 19 (17) B1000111, // F - 20 (18) B1111011, // g - 21 B0110111, // H - 22 (21) B0010111, // h - 23 B0010000, // i - 24 B0001110, // L - 25 (23) // l - 1 B0010101, // n - 26 (24) // O - 0 B0011101, // o - 27 B1100111, // P - 28 (26) B1100111, // q - 29 B0000101, // r - 30 (28) // S - 5 B0001111, // t - 31 (29) B0111110, // U - 32 (30) B0011100, // u - 33 B0111011, // y - 34 (32) B1101101, // Z - 35 }; // Shift register control for ShiftPWM LEDS const int LEDmosi = 11; const int LEDmiso = 12; const int LEDsck = 13; // which shift reg pins control which colour? const int red1=7, green1=6, blue1=5, white1=4, red2=3, green2=2, blue2=1, white2=0; const int ShiftPWM_latchPin=10; const bool ShiftPWM_invertOutputs = 0; // if invertOutputs is 1, outputs will be active low. Usefull for common anode RGB led's. //int PWMtest = 500, PWMinc = 1; // Used for testing - no longer relevant #include // include ShiftPWM.h after setting the pins! unsigned char maxBrightness = 255; unsigned char pwmFrequency = 75; int numRegisters = 1; // only using one shift register. // Ramps give overall exponential power increase with exponent of 1.25 x at each step. // Assumes relative power of R:G:B:W 1:1:1:6. // Gradually ramps from pure red to orange/yellow to white. // Was defined when I thought we could use 10 bits with shiftPWM. Rather than re-draw we are just dividing by 4. const int redRamp[32] ={0,11, 14, 18, 22, 28, 35, 44, 54, 68, 85, 106, 133, 166, 207, 259, 324, 405, 456, 513, 577, 649, 730, 822, 924, 1023, 1023, 1023, 1023, 1023, 1023, 1023}; const int greenRamp[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 120, 214, 340, 506, 570, 641, 721, 811, 913, 1023, 1023, 1023, 1023}; const int blueRamp[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155, 367, 671, 755, 850, 956, 1023, 1023, 1023}; const int whiteRamp[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 165, 286, 471, 717, 1023}; int lampBrightness = 0; // Rotary encoder interface const int rotX = 2; const int rotY = 3; const int rotSw = 4; // have to start using some analogue pins for Switches 'cos short of digital pins boolean rotSwState = false; unsigned long rotSwTime =0; byte encoded[10]={0,0,0,0,0,0,0,0,0,0}; byte currentEncode = 2; // General Switch controls const int switch1 = 14; // This is A0 but using it as a digital pin we can refer to it as D14 boolean switch1State = false; const int switch2 = 15; // A1 = D15 - We could read as analogue easily enough but this avoids the need to change the code between pins... boolean switch2State = false; unsigned long switch1Time =0, switch2Time=0; const int power = 5; // Pin for controlling ATX main power (power on when pin low) const boolean powerControl=true; // set this to false to stop us switching the ATX power on an off. const int buzzer = 6; // Pin for buzzer - 5V 30ma driven directly. Any more current and we would need a transistor in there. // Security light mode variables & constants: // We could allow some of these to be set from the clock but not really necessary. boolean security=false; // whether we are in security light mode const byte delayMins=45; const byte onMins = 20; const byte earliestOnTime=18; void setup(){ Serial.begin(9600); Serial.println(" DS1307 RTC based clock and alarm - Ugi Jan 2012"); delay(2000); // Give RTC some time to settle. Wire.begin(); // Need for RTC shiftRegSetup(); fourDigitSetup(); encoderSetup(); encoded[2]=0; eepromSetup(); ShiftPWM.SetAll(0); // turn off lamps pinMode(buzzer, OUTPUT); pinMode(power, OUTPUT); //ShiftPWM.PrintInterruptLoad(); - to check if we're worried about processing time. for(byte scrap=0; scrap<8; scrap++){ ShiftPWM.SetOne(scrap,255); delay(1000); ShiftPWM.SetOne(scrap,0); } } void loop() { RTCread(); //RTCprintTime(); // sends time to serial. This function currently commented out. fourDigitShowTime(); // Displayt time on 4-digit display fourDigitWriteData(LEDintensity,10); // Set intensity. Probably don't need in main loop any more. // Switch monitoring: // Encoder Switch: boolean _newSwState = digitalRead(rotSw); // check for switch press & determine long or short: if( _newSwState && (_newSwState != rotSwState)){rotSwTime=millis();} // if new press then set timer. if( !_newSwState && (_newSwState != rotSwState)){ unsigned long _compMillis=millis()-rotSwTime; // by using subtraction, we should cope even if we get a time-rollover. if(_compMillis > 2000){ boolean _set = setFromFourDigit();} // long press - we want to set the time. else{ // short press - turn off alarm lamp & buzzer or display date. if(alarmNow){ // is the alarm on? if(buzzerNow){ // buzzer is ringing buzzerNow=false; // snooze buzzer - only allow snooze to the end of the "hold" time. digitalWrite(buzzer, LOW); if((millis()-alarmStartTime-alarmDelayPeriod+420000)<(91*60*1000)){ // will we wrap if we take 7 mins from remaining light-on time? buzzerDelayMS= alarmDelayPeriod-(millis()-alarmStartTime)-60000; // set buzzer to go 1 min before light goes off if less than 7 mins } else{ // not close to end of light-on time buzzerDelayMS=420000; // Std 7-minute snooze buzzerStartTime=millis(); // re-set buzzer start delay } byte _str[12]={5, 26, 27, 27, 27, 27, 35, 19, 13, 13, 13, 13};// SnooooZE fourDigitScroll(_str, 12, 250); } else{ // not buzzing alarmNow = false; buzzerNow = false; // just to be sure! digitalWrite(buzzer, LOW); byte _length = 0; if (date==25 && month==12){ byte _str[18]={22, 14, 28, 28, 34, 13, 34, 33, 25, 19, 31, 24, 18, 19, 13, 13, 13, 13}; // HAPPy yULEtidE _length = 18; fourDigitScroll(_str, _length, 250); } else{ if(hours>4 && hours<12){ byte _str[19]={22, 14, 32, 19, 13, 14, 13, 21, 27, 27, 18, 13, 18, 14, 34, 13,13,13,13};// HAVE A good dAy _length = 19; fourDigitScroll(_str, _length, 180); } if(hours>11 && hours<7){ byte _str[18]={21,27,27,18, 13, 14,20, 31, 19, 30, 26, 27, 27, 26, 13, 13, 13, 13}; // good AFtErnoon _length = 18; fourDigitScroll(_str, _length, 180); } } } } else{ // Short Press Event if nothing exciting happening... showDate(); //LEDcontrol=!LEDcontrol; } } } rotSwState=_newSwState; // First tactile switch - check for switch press & display or set alarms _newSwState = digitalRead(switch1); if( _newSwState && (_newSwState != switch1State)){ switch1Time=millis(); } if(_newSwState && switch2State){ security = true; // this will take us to security light mode but set after RCTread or we'll read back from saved data } if( !_newSwState && (_newSwState != switch1State)){ // just released see if long or short and behave accordingly. unsigned long _compMillis=millis()-switch1Time; if(_compMillis > 2000){ manageAlarms();// Event for long press of Switch1 goes here - alarm set routine //displayAlarms(); } else{ displayAlarms(); } // short press - show alarms. } switch1State=_newSwState; // Second tactile switch (on A0)- check for switch press & toggle armed flag. _newSwState = digitalRead(switch2); if( _newSwState && (_newSwState != switch2State)){switch2Time=millis();} // if new contact set timer. if( !_newSwState && (_newSwState != switch2State)){// Just released switch unsigned long _compMillis=millis()-switch2Time; if(_compMillis > 2000){ //armed=!armed; manageSettings(); // Event for long press of Switch2 goes here - More obscure parameter set routine for things like display brighness and ramp time. } else{ armed=!armed; // short press changes "armed" status and writes to DC1307 so that we keep it. Wire.beginTransmission(DS1307_Address); Wire.send(8); // set register to byte 8 Wire.send(armed); // save armed status to RTC Wire.endTransmission(); if ((hours>21 || hours<3) & armed){ byte _Str[15]={26, 24, 21, 23, 31, 13, 26, 24, 21, 23, 31, 13, 13,13,13}; fourDigitScroll(_Str, 15, 250); } } // short press change alarm armed state. } switch2State=_newSwState; // Alarm Control: if (alarmNow){ // alarm is on. Need to ramp up lamp, hold at high and ring alarm. showAlarm(); } else { checkAlarm(); } // Random delay to avoid writing to serial too fast when debugging. This can probably come out: // delay(50); // Night Light Control: lampBrightness = (encoded[2]/2); if(lampBrightness>=100){ lampBrightness = 0; encoded[2]=1;} if(lampBrightness>31){lampBrightness=31; encoded[2]=31*2+1;} // Set intensity by encoder. if(!alarmState){showLamp(lampBrightness);} // If our alarm state is greater than 0 then the alarm is on or fading out. // Lamp control test routine: //PWMtest+= PWMinc; //if (PWMtest>30 || PWMtest <2){PWMinc=-PWMinc;} //showLamp(PWMtest); //ShiftPWM.OneByOneSlow(); if (security){ securityLight(); switch1State=switch2State=_newSwState=0; switch1Time=switch2Time=millis(); security=false; } } // End main loop // Various funcions below. Generally RTC then 4-digit display then lamp then alarm then eeprom then buzzer. // Read time from RTC into time variables. void RTCread(){ byte data[11]={0,0,0,0,0,0,0,0,0,0,0}; Wire.beginTransmission(DS1307_Address); Wire.send(0); // set register to beginning Wire.endTransmission(); Wire.requestFrom(DS1307_Address, (byte)10); // request 7 bytes from RTC into buffer. for (byte _loop=0; _loop<10; _loop++){ data[_loop]=(Wire.receive()); } seconds = (((data[0] & B01110000)>>4)*10)+(data[0] & B00001111); minutes = (((data[1] & B01110000)>>4)*10)+(data[1] & B00001111); hours = (((data[2] & B00110000)>>4)*10)+(data[2] & B00001111); day = (data[3] & B00000111); date = (((data[4] & B00110000)>>4)*10)+(data[4] & B00001111); month = (((data[5] & B00010000)>>4)*10)+(data[5] & B00001111); year = (((data[6] & B11110000)>>4)*10)+(data[6] & B00001111); armed = data[8]; if (armed>1){armed=1;} // store armed status in RTC rather than EEPROM - likely to change frequently security = data[9]; // if the security light function is interrupted by power loss we read back here and go straight to that mode } // Write current time to RTC void RTCwrite(){ // writes all current data to RTC - if you only want to write one parameter then just read the clock first! byte data[7]; data[0] = (seconds % 10) + ((seconds/10)<<4); data[1] = (minutes % 10) + ((minutes/10)<<4); data[2] = (hours % 10) + ((hours/10)<<4); data[3] = day; data[4] = (date % 10) + ((date/10)<<4); data[5] = (month % 10)+ ((month/10)<<4); data[6] = (year % 10) + ((year/10)<<4); Wire.beginTransmission(DS1307_Address); Wire.send(0); // set register to beginning for (byte _loop=0; _loop<7; _loop++){ Wire.send(data[_loop]); } Wire.endTransmission(); oldseconds=seconds; } // End RTC Write function /* // Print current time to Serial... This can probably come out. void RTCprintTime(){ RTCread(); Serial.print("Time = "); Serial.print(hours, DEC); Serial.print(":");Serial.print(minutes, DEC);Serial.print(":"); Serial.println(seconds, DEC); Serial.print("Date = "); Serial.print(date, DEC); Serial.print("/");Serial.print(month, DEC);Serial.print("/20"); Serial.print(year, DEC); } byte RTCgetSerial(byte _val, String _Str){ Serial.println(" "); Serial.println(" *** "+_Str+" ***"); Serial.print("Current value = "); Serial.print(_val, DEC); Serial.println(" "+_Str); Serial.println("Enter new value (two digits e.g. '01'): "); Serial.flush(); byte NEW_val = -1; while (Serial.available() < 2){ delay(50);} NEW_val = ((Serial.read()-48)*10); NEW_val = NEW_val + (Serial.read()-48); Serial.flush(); Serial.print("New value = "); Serial.print(NEW_val, DEC); Serial.println(" "+_Str);Serial.println(" "); if (NEW_val >=0){return NEW_val;} else {return _val;} } boolean RTCconfirm(){ Serial.println(" "); Serial.println("Currently:"); RTCprintTime(); Serial.flush(); Serial.println(" "); Serial.println("Change to? (Y/N):"); Serial.print("time = "); Serial.print(NEWhours, DEC); Serial.print(":");Serial.print(NEWminutes, DEC);Serial.print(":"); Serial.println(NEWseconds, DEC); Serial.print("date = "); Serial.print(NEWdate, DEC); Serial.print("/");Serial.print(NEWmonth, DEC);Serial.print("/20");Serial.print(NEWyear, DEC); char _conf = -1; while (Serial.available()<1){ delay(50);} _conf = Serial.read(); Serial.flush(); Serial.print("confirm digit = "); Serial.println(_conf); if(_conf=='Y'){return true;} else{return false; } } boolean RTCsetFromSerial(){ // Get new time setting from serial port Serial.flush(); // avoid any random data in buffer. // See if we want to update char _input = 'X'; while (_input != 'R' && _input !='Q'){ while (Serial.available()<1){ RTCprintTime(); // We'll keep displaying current tiem and see whether it needs updating. Serial.println("Enter 'R' by serial input to re-set or Q to quit setting routine"); Serial.println(" "); delay(1000); } _input = Serial.read(); Serial.flush(); } // if(_input != 'R'){Serial.println(" "); Serial.println(" *** QUITTING ***"); Serial.println(" "); return false;} Serial.println(" "); Serial.println(" *** RESETTING RTC ***"); Serial.println(" "); RTCprintTime(); NEWyear = RTCgetSerial(year, "years"); RTCprintTime(); NEWday = RTCgetSerial(day, "days of the week"); RTCprintTime(); NEWmonth = RTCgetSerial(month, "months"); RTCprintTime(); NEWdate = RTCgetSerial(date, "days of the month"); RTCprintTime(); NEWhours = RTCgetSerial(hours, "Hours"); RTCprintTime(); NEWminutes = RTCgetSerial(minutes, "Minutes"); RTCprintTime(); NEWseconds = RTCgetSerial(seconds, "Seconds"); if (RTCconfirm()){ seconds = NEWseconds; minutes = NEWminutes; hours = NEWhours; date = NEWdate; month = NEWmonth; day = NEWday; year = NEWyear; RTCwrite(); Serial.println (" "); Serial.println (" *** RTC Updated ***"); return true; } else { Serial.println (" *** Update cancelled ***"); } return false; } */ void fourDigitSetup(){ pinMode(fourDigitLatch, OUTPUT); pinMode(fourDigitClock, OUTPUT); pinMode(fourDigitData, OUTPUT); fourDigitWriteData(1, 12); // turn off shutdown fourDigitWriteData(0, 15); // test mode off fourDigitWriteData(0,9); // Turn off decode mode fourDigitWriteData(3,11); // Set scan limit to our 4 digits. We can set it higher to reduce brighness if we wish. fourDigitWriteData(LEDintensity,10); // Set initial intensity byte _str[9]={22,19,25,25,0,13,13,13,13}; // say hello! fourDigitScroll(_str,9,250); } // End four-digit display setup // Write a digit to 4-digit display.. void fourDigitWrite(byte _char, byte _digit, boolean _dp){ byte _header = B00000000 | _digit; // redundant but we might need a header or other manipulation later. digitalWrite(fourDigitLatch, LOW); shiftOut(fourDigitData, fourDigitClock, MSBFIRST, _header);// Serial.print("Writing Header -");Serial.print(_header, HEX); byte _data = (character[_char]|(B10000000*_dp)); // Retrieve font definition and add decimal (colon separator or indicator LED) if appropriate. shiftOut(fourDigitData, fourDigitClock, MSBFIRST, _data );// Serial.print (" Writing Data -"); Serial.println (_data, BIN); digitalWrite(fourDigitLatch, HIGH); } // end character write // Need also to write control digits to appropriate registers. // Reg 10 - intensity // Reg 11 - scan limit // Reg 12 - shutdown control (1 to turn off shutdown) // Reg 15 - Test mode control (0 for test mode off) void fourDigitWriteData(byte _data, byte _register){ digitalWrite(fourDigitLatch, LOW); shiftOut(fourDigitData, fourDigitClock, MSBFIRST, _register); // Serial.print("register "); Serial.print(_register, HEX); shiftOut(fourDigitData, fourDigitClock, MSBFIRST, _data); // Serial.print(" data "); Serial.println(_data, BIN); digitalWrite(fourDigitLatch, HIGH); } // end data write // Display Time on four digit display: void fourDigitShowTime(){ byte _blink= true; if (seconds!=oldseconds){ // blink digit divider once a second. blinkTime = millis(); oldseconds=seconds; } _blink=true; if ((millis()-blinkTime)<250){_blink=false;} // blink for 250 ms every second. fourDigitWrite((hours/10),1, false); fourDigitWrite((hours%10),2, _blink); // colon separator on this digit fourDigitWrite((minutes/10),3, false); fourDigitWrite((minutes%10),4, armed); // armed indicator on this digit } void fourDigitDisplayBlink(){ // flashes whole display off for 50 ms fourDigitWriteData(0,12); delay(50); fourDigitWriteData(1,12); } void encoderSetup(){ // set up encoder and switch input pins. pinMode(rotX,INPUT); pinMode(rotY,INPUT); pinMode(rotSw, INPUT); attachInterrupt(0, encoder, CHANGE); pinMode(switch1, INPUT); pinMode(switch2, INPUT); } // Function for updating variables from interrupt using the rotary encoder. Keep it short! // I have not needed software debouncing but if you do then un-comment the approprate lines below. //unsigned long debounce; // we have a 2ms software debounce implemented by this variable. void encoder(){ // This is called by interrupt every time the status of D2 changes. Must be either up or down! //if ((millis()-debounce)>1){ // if not changed state in last 2ms byte _A = (PIND & B1100); // read pins 2 & 3 - use direct port read for speed. byte _B = ((_A & B1000) >> 3); // Pin 3 state into _B _A = ((_A & B100)>>2); // Pin 2 state into _A if (_A == _B ){ // If A==B then A is following. If A != B then A is leading. encoded[currentEncode]++; // if your encoder is wired the other way just reverse this } else{ encoded[currentEncode]--; // and this } //} // debounce=millis(); // reset each time we interrupt so we wait 2ms from last "bounce" } // Need to be able to set the clock functions from the encoder and 4-digit display: boolean setFromFourDigit(){ encoded[1]=(year*2); byte _wasEncoded = currentEncode; currentEncode=1; byte _scrap = 0; while(digitalRead (rotSw)){delay(50);} // wait for button press to end while(!digitalRead (rotSw)){ fourDigitWrite(2,1, false); fourDigitWrite(0,2,false); fourDigitWrite((year/10),3, false); fourDigitWrite((year%10),4, armed); // indicator on this digit delay(20); year=encoded[1]/2; if(year>99){year=99;} if(year<1){year=1;} // we don't need to go back to 2000 since it's 2011 now! _scrap++; if(_scrap>25){fourDigitDisplayBlink(); _scrap=0;} // blink every half-second - not working? } while(digitalRead (rotSw)){delay(50);} // wait for switch off encoded[1]=month*3; while(!digitalRead (rotSw)){ fourDigitWrite((date/10),1, false); fourDigitWrite((date%10),2,false); fourDigitWrite((month/10),3, false); fourDigitWrite((month%10),4, armed); // indicator on this digit delay(20); month=encoded[1]/3; if(month>12){month=12; encoded[1]=37;} if(month<1){month=1; encoded[1]=4;} // there is no month zero! _scrap++; if(_scrap>25){fourDigitWrite(13,3,0); fourDigitWrite(13,4,0); delay(50); _scrap=0;} // blink every half-second - not working? } while(digitalRead (rotSw)){delay(50);} // wait for switch off encoded[1]=date*3; while(!digitalRead (rotSw)){ fourDigitWrite((date/10),1, false); fourDigitWrite((date%10),2,false); fourDigitWrite((month/10),3, false); fourDigitWrite((month%10),4, armed); // indicator on this digit delay(20); date=encoded[1]/3; if(date>31){date=31; encoded[1]=94;} if(date<1){date=1; encoded[1]=4;} // there is no month zero! if(month==2 && (year%4)==0){if(date>29){date=29; encoded[1]=88;}} // Feb is a pain but at least we have already set the year! if(month==2 && (year%4)!=0){if(date>28){date=28; encoded[1]=85;}} if(month==4 || month==6 || month==9 || month==11){if(date>30){date=30; encoded[1]=91;}} _scrap++; if(_scrap>25){fourDigitWrite(13,1,0); fourDigitWrite(13,2,0); delay(50); _scrap=0;} // blink every half-second } while(digitalRead (rotSw)){delay(50);} // wait for switch off encoded[1]=(hours*3); while(!digitalRead (rotSw)){ fourDigitShowTime(); // indicator on this digit delay(20); hours=encoded[1]/3; if(hours>23){hours=0; encoded[1]=0;} if(hours<0){hours=23; encoded[1]=23*3+1;} _scrap++; if(_scrap>25){fourDigitWrite(13,1,0); fourDigitWrite(13,2,1); delay(50); _scrap=0;} // blink every half-second } while(digitalRead (rotSw)){delay(50);} // wait for switch off encoded[1]=(minutes*3); while(!digitalRead (rotSw)){ fourDigitShowTime(); // indicator on this digit delay(20); minutes=encoded[1]/3; if(minutes>59){minutes=0; encoded[1]=0;} if(minutes<0){minutes=59; encoded[1]=59*3+1;} _scrap++; if(_scrap>25){fourDigitWrite(13,3,1); fourDigitWrite(13,4,0); delay(50); _scrap=0;} // blink every half-second } while(digitalRead (rotSw)){delay(50);} // wait for switch off encoded[1]=(day*3); while(!digitalRead (rotSw)){ fourDigitWrite(13,1,0); // indicator on this digit fourDigitWrite(18,2,1); fourDigitWrite(day,3,0); fourDigitWrite(13,4,0); delay(20); day=encoded[1]/3; if(day>7){day=1; encoded[1]=0;} if(day<1){day=7; encoded[1]=7*3+1;} _scrap++; if(_scrap>25){fourDigitWrite(13,2,0); fourDigitWrite(13,3,1); delay(50); _scrap=0;} // blink every half-second } currentEncode=_wasEncoded; // set encoder back to controlling whatever it was doing before seconds=0; RTCwrite(); while(digitalRead (rotSw)){delay(50);} // wait for switch off } // End set time routine. void manageSettings(){ byte _wasEncoded=currentEncode; byte _str[11]={18, 1, 5, 28, 25, 14, 34,13,13,13,13}; // dISPLAy fourDigitScroll(_str, 11, 250); currentEncode=0; fourDigitWrite(18,1,0); fourDigitWrite(13,2,0); encoded[0]=LEDintensity*3; while(!digitalRead (rotSw)){ LEDintensity = (encoded[0]/3); if(LEDintensity> 63){ LEDintensity = 0; encoded[0]=0;} if(LEDintensity>15){LEDintensity=15; encoded[0]=46;} // Set intensity by encoder. fourDigitWrite((LEDintensity/10),3,0); fourDigitWrite((LEDintensity%10),4,0); fourDigitWriteData(LEDintensity,10); } while(digitalRead (rotSw)){delay(50);} currentEncode=1; encoded[1]=rampTime*3; byte _str2[11]={20, 14, 18, 19, 13, 32, 28,13,13,13,13}; // FAdE UP fourDigitScroll(_str2, 11, 250); fourDigitWrite(20,1,0); while(!digitalRead (rotSw)){ rampTime=(encoded[1]/3); if(rampTime<1){rampTime = 1; encoded[1]=4;} // can't go below 1 or we will get a division by zero! if(rampTime> 60){ rampTime = 60; encoded[1]=181;} fourDigitWrite((rampTime/10),3,0); fourDigitWrite((rampTime%10),4,0); } while(digitalRead (rotSw)){delay(50);} alarmStepPeriod =(long(rampTime) *1875);// (1875 = 60 *1000 / 32)- Mins of alarm ramp duration (5) * 60 * 1000 / Number of steps of ramp (32) encoded[1]=delayTime*2; byte _str3[8]={22, 27, 1, 18, 13,13,13,13}; // Hold fourDigitScroll(_str3, 8, 250); fourDigitWrite(22,1,0); while(!digitalRead (rotSw)){ delay(20); delayTime=(encoded[1]/2); if(delayTime<1){delayTime = 1; encoded[1]=3;} // can't go below 1 if(delayTime> 90){ delayTime = 90; encoded[1]=181;} fourDigitWrite((delayTime/10),3,0); fourDigitWrite((delayTime%10),4,0); } while(digitalRead (rotSw)){delay(50);} alarmDelayPeriod = (long(delayTime)*60000); encoded[1]=buzzerDelay*2; byte _str4[16]={15, 33, 35, 35, 19, 30, 13, 18, 19, 25, 14, 34, 13, 13, 13, 13}; // BUZZEr dElAy fourDigitScroll(_str4, 16, 250); fourDigitWrite(15,1,0); while(!digitalRead (rotSw)){ delay(20); buzzerDelay=(encoded[1]/2); if(buzzerDelay>120){buzzerDelay = 0; encoded[1]=1;} // can't go below 0 if(buzzerDelay>delayTime){ buzzerDelay=delayTime; encoded[1]=((delayTime*2)+1);} fourDigitWrite((buzzerDelay/10),3,0); fourDigitWrite((buzzerDelay%10),4,0); } while(digitalRead (rotSw)){delay(50);} buzzerDelayMS = (long(buzzerDelay)*60000); // Set buzzer delay here... for(byte _loop=1; _loop<5; _loop++){ for(byte _subloop=10; _subloop<14; _subloop++){ fourDigitWrite(_subloop, _loop, false); delay (50); // Just a unique sign so that we know we are writing to EEPROM } } eepromUpdate(9, LEDintensity); eepromUpdate(10, rampTime); eepromUpdate(11, delayTime); eepromUpdate(12, buzzerDelay); currentEncode=_wasEncoded; } void manageAlarms(){ // n = 26, y = 34 byte _wasEncoded = currentEncode; currentEncode = 1; byte _scrap =0; for(byte _loop=0; _loop1){alarmOn[_loop]=0; encoded[1]=0;} if(alarmOn[_loop]<0){alarmOn[_loop]=1; encoded[1]=4;} fourDigitWrite(14,1,0); // indicator on this digit fourDigitWrite(_loop,2,1); fourDigitWrite(13,3,0); fourDigitWrite((26+(alarmOn[_loop]*8)),4,0); // displays 26 (n) or 34 (y) depending on alarm state delay(20); _scrap++; if(_scrap>25){fourDigitWrite(13,4,0); delay(50); _scrap=0;} // blink every half-second } if(alarmOn[_loop]){ // if alarm is on then need to set it. byte _set=setAlarm(_loop); } } currentEncode = _wasEncoded; eepromWriteAlarms(); // Update EEPROM with new settings. } // end alarm management boolean setAlarm(byte _almNo){ byte _wasEncoded = currentEncode; byte _scrap =0; while(digitalRead (rotSw)){delay(50);} // wait for switch off encoded[1]=(alarmHour[_almNo]*3); while(!digitalRead (rotSw)){ fourDigitShowAlarm(_almNo); delay(20); alarmHour[_almNo]=encoded[1]/3; if(alarmHour[_almNo]>80){alarmHour[_almNo]=23; encoded[1]=23*3+1;} if(alarmHour[_almNo]>23){alarmHour[_almNo]=0; encoded[1]=0;} _scrap++; if(_scrap>25){fourDigitWrite(13,1,0); fourDigitWrite(13,2,1); delay(50); _scrap=0;} // blink every half-second } while(digitalRead (rotSw)){delay(50);} // wait for switch off encoded[1]=(alarmMin[_almNo]*3); while(!digitalRead (rotSw)){ fourDigitShowAlarm(_almNo); // indicator on this digit delay(20); alarmMin[_almNo]=encoded[1]/3; if(alarmMin[_almNo]>80){alarmMin[_almNo]=59; encoded[1]=59*3+1;} if(alarmMin[_almNo]>59){alarmMin[_almNo]=0; encoded[1]=0;} _scrap++; if(_scrap>25){fourDigitWrite(13,3,1); fourDigitWrite(13,4,0); delay(50); _scrap=0;} // blink every half-second } while(digitalRead (rotSw)){delay(50);} // wait for switch off encoded[1]=(alarmDay[_almNo]*3); while(!digitalRead (rotSw)){ byte _test = alarmDay[_almNo]; fourDigitAlarmDay(alarmDay[_almNo]); delay(20); alarmDay[_almNo]=encoded[1]/3; if(alarmDay[_almNo]>9){alarmDay[_almNo]=0; encoded[1]=0;} if(alarmDay[_almNo]<0){alarmDay[_almNo]=9; encoded[1]=31;} _scrap++; if(_scrap>25){fourDigitDisplayBlink(); _scrap=0;} // blink every half-second } while(digitalRead (rotSw)){delay(50);} // wait for switch off // Buzzer on? encoded[1]=(buzzerOn[_almNo]*3); while(!digitalRead (rotSw)){ delay(20); buzzerOn[_almNo]=encoded[1]/3; if(buzzerOn[_almNo]>1){buzzerOn[_almNo]=0; encoded[1]=1;} fourDigitWrite(5,1,0); fourDigitWrite(26,2,0); fourDigitWrite(18,3,0); fourDigitWrite((26+(buzzerOn[_almNo]*8)),4,0); // displays 26 (n) or 34 (y) depending on alarm state _scrap++; if(_scrap>25){fourDigitWrite(13,4,0); delay(50); _scrap=0;} // blink every half-second } currentEncode=_wasEncoded; // set encoder back to controlling display while(digitalRead (rotSw)){delay(50);} // wait for switch off return true; } void fourDigitShowAlarm(byte _alm){ fourDigitWrite((alarmHour[_alm]/10),1, false); fourDigitWrite((alarmHour[_alm]%10),2, true); fourDigitWrite((alarmMin[_alm]/10),3, false); fourDigitWrite((alarmMin[_alm]%10),4, armed); // indicator on this digit // fourDigitWrite((indicators, 5, false); - can use this for up to noOfAlarms LED indicators if we wish. } void fourDigitAlarmDay(byte _alm){ if (_alm==0){ // all days display as "d1-7" fourDigitWrite(18,1, false); fourDigitWrite(1,2, false); fourDigitWrite(11,3, false); fourDigitWrite(7,4, armed); // indicator on this digit return ; } if (_alm<8){ // for separate days show as "d1" etc. fourDigitWrite(18,1, false); fourDigitWrite(_alm,2, false); fourDigitWrite(13,3, false); fourDigitWrite(13,4, armed); // indicator on this digit return ; } if (_alm==8){ // for day 8 show as "d1-5" fourDigitWrite(18,1, false); fourDigitWrite(1,2, false); fourDigitWrite(11,3, false); fourDigitWrite(5,4, armed); // indicator on this digit return ; } if (_alm==9){ // for day 9 show as "d6-7" fourDigitWrite(18,1, false); fourDigitWrite(6,2, false); fourDigitWrite(11,3, false); fourDigitWrite(7,4, armed); // indicator on this digit return ; } // anything else is an error! fourDigitWrite(19,1, false); // Err fourDigitWrite(30,2, false); fourDigitWrite(30,3, false); fourDigitWrite(13,4, armed); // indicator on this digit } void displayAlarms(){ for(byte _almNo=0; _almNo2){return;}// Only start alarm in first 2 seconds of a minute to avoid re-start. for ( byte _loop=0; _loop5)){ alarmNow=true; alarmNumber = _loop; alarmState = 0; return; } } } } } // End alarms loop } // End Check Alarms routine void showAlarm(){ if (alarmState==0){alarmStartTime=millis(); alarmState=1; Serial.println("Alarm Activated State = 1 "); } // just started new alarm unsigned long _compTime = (millis()-alarmStartTime); // Serial.print("_compTime =");Serial.print(_compTime, DEC); Serial.print("alarmStepPeriod =");Serial.println(alarmStepPeriod, DEC); if (_compTime >= alarmStepPeriod){ // see if it's time to move up the ramp a step. if (alarmState<31){ // Are we at max? If not, step up. alarmStartTime=millis(); buzzerStartTime=alarmStartTime+alarmStepPeriod; alarmState++; Serial.print("alarm state ");Serial.println(alarmState, DEC); // re-set for next step. } else{ // We are in state 31 - finished ramping if (_compTime >= alarmDelayPeriod){ // have we reached the end of the alarm full-brighness period? alarmNow=false; // turn off alarm. Will also stop buzzer when we start the fade-out. alarmStartTime=millis(); // set time ready for fade-out } else{ // we have not reached the end of the delay period - need to check whether we should be alarming. // Serial.print("Buzzer check = ");Serial.println(buzzerOn[alarmNumber], DEC); if(buzzerOn[alarmNumber]){ if(!buzzerNow){ unsigned long _compTime2=millis()-buzzerStartTime; if(_compTime2>buzzerDelayMS){ buzzerNow=true; buzzerStartTime=millis(); } } else{ unsigned long _compTime2=millis()-buzzerStartTime; //Serial.print("Buzzing. Start time = ");Serial.print(buzzerStartTime, DEC);Serial.print("Comparison = ");Serial.println(_compTime2, DEC); if(_compTime2<=10000){ soundBuzzer(0); } if(_compTime2>10000 && _compTime2<=20000){ soundBuzzer(1); } if(_compTime2>20000 && _compTime2<=30000){ soundBuzzer(2); } if(_compTime2>30000){ soundBuzzer(3); } } } } //Serial.print("In delay loop. Delay = ");Serial.print(alarmDelayPeriod, DEC);Serial.print("State = ");Serial.print(alarmState, DEC); Serial.print(" Current = ");Serial.println(_compTime, DEC); } } showLamp(alarmState); } void fadeOutAlarm(){ digitalWrite(buzzer, LOW); // alarm is turned off so stop any buzzer-ing. unsigned long _compTime = (millis()-alarmStartTime); //Serial.print("_compTime =");Serial.print(_compTime, DEC); Serial.print("alarmFadePeriod =");Serial.println(alarmStepPeriod, DEC); if (_compTime >= alarmFadePeriod){ // see if it's time to move down the ramp a step. // Serial.println(alarmState, DEC); if (alarmState>0){ // Are we at min? If not, step down. alarmStartTime=millis(); alarmState--; // re-set for next step. } } showLamp(alarmState); } void showLamp(int _state){ if (powerControl && !_state){digitalWrite(power, HIGH);} // turn off main power if lights are off. else {digitalWrite(power, LOW);} // otherwise turn power on. ShiftPWM.SetOne(red1, redRamp[_state]/4); ShiftPWM.SetOne(green1, greenRamp[_state]/4); ShiftPWM.SetOne(blue1, blueRamp[_state]/4); ShiftPWM.SetOne(red2, redRamp[_state]/4); ShiftPWM.SetOne(green2, greenRamp[_state]/4); ShiftPWM.SetOne(blue2, blueRamp[_state]/4); ShiftPWM.SetOne(white1, whiteRamp[_state]/4); ShiftPWM.SetOne(white2, whiteRamp[_state]/4); // Serial.print("state= "); Serial.print(_state, DEC); Serial.print(" Red=");Serial.print(redRamp[_state], DEC); Serial.println(); } void eepromSetup(){ int _version = eepromCheck(); // retrieve EEPROM data fourDigitWrite(sketchVersion[0],1, false);// display sketch version fourDigitWrite(sketchVersion[1],2,false); fourDigitWrite((sketchVersion[2]/10),3,false); fourDigitWrite((sketchVersion[2]%10),4, false); delay(500); fourDigitWrite(13,1, false); // blank diaplay fourDigitWrite(13,2, false); fourDigitWrite(13,3, false); fourDigitWrite(13,4, armed); if(_version==0){ // nothing useful to read from EEPROM alarmsDefault(); return; // no point in saving default alarms - we'll save when we reset them. } if((_version >> 8)<=sketchVersion[0] && ((_version >> 4) & B0001111)<=sketchVersion[1] && (_version & B00001111)<=sketchVersion[2]) { // what is the newest version of saved data we can handle? boolean _read = eepromRetrieve(); if (_read){ for(byte _loop=1; _loop<5; _loop++){ for(byte _subloop=13; _subloop>9; _subloop--){ fourDigitWrite(_subloop, _loop, false); delay (50); // Just a unique sign so that we know we are reading from EEPROM } } fourDigitWrite(13,1, false); fourDigitWrite(13,2, false); fourDigitWrite(13,3, false); fourDigitWrite(13,4, armed); return; // we've read alarms. Don't need to default. } } alarmsDefault(); // If the EEPROM version is too new we can't be sure of format, so default } int eepromCheck(){ // EEPROM Layout: bytes 0-7 signature & version. //8 - No Alarms, 9-display intensity, 10-ramp time, 11-Hold time, 12-Armed? - might put that in RTC ram... //16 onwards, alarm data in 4 byte blocks: On, Day, Hour, Min. char _data[8]; for (int _loop=0; _loop<8; _loop++){ _data[_loop]=EEPROM.read(_loop); } if (_data[0]!='U' || _data[1]!='g' || _data[2]!='i'){fourDigitWrite(19,1, false);fourDigitWrite(1,2, false); delay (500); return 0;} // not one of my chips yet! if (_data[3]!='D' || _data[4]!='C'){fourDigitWrite(19,1, false);fourDigitWrite(2,2, false); delay (500); return 0;} // not containing Dawn Clock alarm data int _version; _version = ((_data[5] & B00001111)<<8); _version += ((_data[6] & B00001111)<<4); _version += (_data[7] & B00001111); // version saved as three bytes & transferred three 4-bit BCD digits in an "int" // Show sketch version from EEPROM fourDigitWrite(19,1, false); // E fourDigitWrite(_data[5],2, false); //Version MSD fourDigitWrite(_data[6],3, false); fourDigitWrite(_data[7],4, armed); delay(500); return _version; } boolean eepromRetrieve(){ noOfAlarms = EEPROM.read(8); if (noOfAlarms >10) {return false ;}// don't read if there are a mad number of alarms - corrupt data?! if(noOfAlarms){ LEDintensity=EEPROM.read(9); if (LEDintensity>15){LEDintensity=5;} rampTime=EEPROM.read(10); if (rampTime>60 || rampTime==0){rampTime=5;} // default if out of range alarmStepPeriod =(long(rampTime) *1875);// (1875 = 60 *1000 / 32)- Mins of alarm ramp duration (5) * 60 * 1000 / Number of steps of ramp (32) delayTime=EEPROM.read(11); if(delayTime>90 || delayTime==0){delayTime=30;} // default if out of range alarmDelayPeriod = (long(delayTime)*60000);//(30 * 60 * 1000) // 30 min duration of max light after ramping buzzerDelay=EEPROM.read(12); // - Buzzer delay after reach 100% lamp buzzerDelayMS = long(buzzerDelay)*60000; if(armed>1){armed=1;}// default if out of range for(byte _loop=0; _loop1; // is buzzer on? alarmDay[_loop]= EEPROM.read(17+(_loop*4)); // day / day code. alarmHour[_loop] = EEPROM.read(18+(_loop*4)); // hour alarmMin[_loop] = EEPROM.read(19+(_loop*4)); // Min if (alarmOn[_loop] != 1){alarmOn[_loop] = 0;} // if not a correct read then turn this alarm off. if (buzzerOn[_loop] !=1){buzzerOn[_loop]=0;} // Cleanup just in case. if (alarmDay[_loop] >9){alarmDay[_loop] = 0;} // default to every day if read error if (alarmHour[_loop] >23){alarmHour[_loop] = 7;} // default to 7am if (alarmMin[_loop] >59){alarmMin[_loop] = 0;} } return true; // may need to return false if we have to default but this will do for now! } noOfAlarms = 7; return false; // if no alarms set then default no of alarms and return false to set them to default. } // end eeprom.Retrieve void eepromUpdate(byte _address, byte _newByte){ // Update only if new enrty - avoids unnecessary write cycles. if (_newByte==EEPROM.read(_address)){ return ; // not updating 'cos already correct. } else{ EEPROM.write(_address, _newByte); // current value needs updating. fourDigitWrite(11,1, false); // flash display just so that we know when we are writing a byte to EEPROM fourDigitWrite(11,3, false); delay(50); fourDigitWrite(13,1, false); fourDigitWrite(13,2, false); fourDigitWrite(13,3, false); fourDigitWrite(13,4, armed); delay (50); return; } } boolean eepromWriteAlarms(){ fourDigitWrite(10,1, false);// Write bottom dashes fourDigitWrite(10,2, false); fourDigitWrite(10,3, false); fourDigitWrite(10,4, armed); eepromUpdate(0,'U'); eepromUpdate(1,'g'); eepromUpdate(2,'i'); eepromUpdate(3,'D'); eepromUpdate(4,'C'); eepromUpdate(5, sketchVersion[0]); eepromUpdate(6, sketchVersion[1]); eepromUpdate(7, sketchVersion[2]); eepromUpdate(8, noOfAlarms); for(byte _loop=1; _loop<5; _loop++){ for(byte _subloop=10; _subloop<14; _subloop++){ fourDigitWrite(_subloop, _loop, false); delay (50); // Just a unique sign so that we know we are writing to EEPROM } } for(byte _loop=0; _looplong(buzzerTiming[_tone][(buzzerState-1)]*10)){ // first buzzer time digitalWrite(buzzer, (!(buzzerState&B101))); // will be low on 1, 3 and 4. //Serial.print(buzzerState, DEC); Serial.print(" pin state =");Serial.println(!(buzzerState&B101), DEC); buzzerToneTime = millis(); buzzerState++; } if (buzzerState>4){buzzerState=0;} } void securityLight(){ Wire.beginTransmission(DS1307_Address); Wire.send(9); // set register to byte 9 Wire.send(1); // save Security light mode status (ON) to RTC so we return here if power is cut. Wire.endTransmission(); digitalWrite(power, LOW); // turn on main power for throbbing status light. //Serial.println("Security light"); byte _press=0; randomSeed(millis()); // this might give the same light pattern after each power cut but not too worried about that. byte _oldSeconds =0; byte _pulse=0; byte _step=1; byte _oldMinutes=minutes; unsigned long _lightStartTime=millis(); unsigned long _lightDelay = (15*60000); boolean _lightOn=0; byte _Str[18]={5, 19, 16, 32, 30, 24, 31, 34, 13, 1, 24, 21, 23, 31, 13, 13,13,13}; // SECURIty light fourDigitScroll(_Str, 18, 200); while (!_press){ _oldMinutes=minutes; _oldSeconds = seconds; RTCread(); if (_lightOn){ unsigned long _compTime=millis()-_lightStartTime; if (_compTime>_lightDelay){ _lightOn=false; _lightStartTime=millis(); } } if ((hours>=earliestOnTime || hours<1) && !_lightOn){ // light only acitve from earliestOnTime to 1:00 - make this controllable? if (minutes!=_oldMinutes){ byte _turnOn=random(1,delayMins); // probability of turning on each minute is 1 in delayMins if (_turnOn==1){ _lightOn=true; _lightStartTime=millis(); _lightDelay=long((random(1,onMins)*4)*58765);// Used an uneven time so not exactly whole mins but should be on somewhere between 1 & onMins minutes. } } } if (seconds != _oldSeconds){ fourDigitWrite(19,2, true);delay(50); // flick colon separator briefly every second to show we are awake } if(_lightOn){ ShiftPWM.SetAll(255); // set maximium light. We are emulating a room light so no messing about with ramps here. //Serial.println("Light On"); } else{ //Serial.println("light Off"); ShiftPWM.SetOne(white1,0); ShiftPWM.SetOne(red1,0); ShiftPWM.SetOne(green1,0); ShiftPWM.SetOne(blue1,_pulse); // Low throbbing lights to remind us we are not in alarm mode ShiftPWM.SetOne(white2,0); ShiftPWM.SetOne(red2,0); ShiftPWM.SetOne(green2, (50-_pulse)); ShiftPWM.SetOne(blue2,0); _pulse=_pulse+_step; if (_pulse>49 || _pulse<=0){_step=-_step;} // Security light pulse } fourDigitWrite(5,1, false); // SEC - for security light mode fourDigitWrite(19,2, false); fourDigitWrite(16,3, false); fourDigitWrite(13,4, _lightOn); // indicator on this digit - use it to mirror lamp state _press=digitalRead(rotSw); delay(30);// So pulse is not toooooo fast. } // we'll break out of this loop when encoder switch pressed. Wire.beginTransmission(DS1307_Address); Wire.send(9); // set register to byte 9 Wire.send(0); // save Security light mode status (OFF) to RTC Wire.endTransmission(); //Serial.println("Back to the clock"); while(digitalRead (rotSw)){delay(50);} // wait for switch off }