Home    Master Clock   LCD Master Clock    Kenley Weather

Section   Top    Intro   Features  Super Filter   Clock Case   Dot Matrix Disp Panel Const   Main 7 Seg Disp Const  Main Vero Board Layout    

RTC Disp Mod   PIR Module  RTC Mod   Remote Ctrl  DOT Matrix Disp Reading    DCF77 Code Animation   Schematic    Atmega 328 Pin Table    4K Video  Code

 

 

 

 

Arduino DCF77 Tower Clock & Signal Analyzer

 

This Clock displays the received & decoded DCF77 time code on three 8x8 dot matrix displays and time, date and signal info on four 8 digit 7 segment displays.

It uses 2 x Atmega 328 microprocessors  (Arduino Uno) , 1 to control the DCF77 Analyzer and 1 to control a Udo Klein Super Filter.

The Super Filter is switchable and will allow reception of the DCF77 signal from a very noisy signal.

 

 

Click to view full screen animation

 

 

This clock is based on a DCF77 Analyzer Clock by Erik de Ruiter.

above Erik de Ruiter's DCF77 Analyzer Clock

Erik has provided full details of his clock here on GitHub

See pictures of his clock here Flickr

and Arduino Project Hub

and his other amazing clocks here Flickr

 

 

 

 

 

 

 

Clock Functions/Features

Receive and display, second by second the DCF77 coded signal from the transmitter in Germany.

Display the received signal on a 8x8 dot-matrix display.

Once received and if error checked OK display the stored data on a 2nd 8x8 dot-matrix display, update the real time clock

& display time and date on the top two 7 segment display modules

Display signal error codes, signal status, day of the week and display intensity on a 3rd dot-matrix display

If received signal has errors use a real time clock to display the time.

Uses 7 segment display modules to show the buffer, DCF Bit, no of errors, Period Time & Pulse Width

Switchable DCF77 signal filtering provided on a 2nd Atmega 328 IC using software by Udo Klein

Remote controlled individual reset of DCF77 Analyzer & Super Filter, DCF77 sound On/Off and Super Filter status LEDs On/Off.

PIR controlled display auto blanking. Display will auto blank if no movement is detected near the clock.

 

New v60 software.

Erik de Ruiter has released his new v1.72 software with added features including parity checking and display.

My v60 software now incorporates this update including an updated 3rd matrix display showing parity checks.

See new parity display here

 

above close up of front panel

 

below DCF77 code for the next minute is received in the buffer (matrix 1 left) @ 1 bit per second

at the end of the minute and if error checked OK the buffer is loaded into the current display store (matrix 2 middle)

and the real time clock (RTC) is updated

matrix 3 right shows Day of week, display intensity, clock status and clock info

 

 

 

 

Super Filter

When switched on the Udo Klein's Super Filter actively processes the incoming DCF77 signal

from the Antenna/receiver. Over a number of days of learning the Super Filter can predict the DCF77 signal and use this

to determine if the incoming signal contains any errors. The Super Filter will then synthesize a corrected DCF77 signal even if the signal is absent.

 

Above. Super Filter example

The top row shows the Super Filter turned on.

Once synchronized and tuned into the signal the Super Filter will synthesize a good signal even when the signal is completely lost.

On a noisy signal the Super Filter will search for known signal bits and keep itself synchronized to the transmitter.

The bottom row shows the Super Filter turned off. Whatever signal is received (good or bad) is sent to the decoder.

 

Below Super Filter correcting a noisy signal as displayed on the DCF77 Scope

Normal Signal No super filter - Normal signal from the DCF77 reciever

Noisy Signal No Super Filter - The aerial is move near a LCD screen to generate noise over the signal

No Signal- The aerial is disconnected and moved connected via the super filter

Noise On Superfilter On- The noise is filter out leaving a perfect signal.

 

 

Note the Super Filter input is always connected to the incoming DCF77 Signal even if the Super Filter is turned off.

As the Super Filter can take a few days to tune to maximum accuracy this ensures it is always tuned/synchronized at maximum accuracy to the DCF77 signal.

 

 

 

 

Clock Case

 

The case for this clock is based on a Tesco Black Glass & Metal Lantern. The case has a hinged door that I have positioned on the right of the clock.

The top glass panels are a good fit for the 3 x dot matrix displays.

I have added 18mm MDF top and bottom sections as well as 2 x 6mm plywood pieces to replace the side glass on the small top panels.

These panels are sprayed in black silk to match the metal case. The original metal handle on top of the case is cut away and 2 slots to take the remnants of the handle are

cut into the top MDF panel.

Dimensions H27xW15xD15cm

                 

 

 

 

 

Construction

 

Dot Matrix Display Module Construction

Completed Dot Matrix Display Modules

 

 

 

The display uses 3x 8x8 dot matrix display modules with onboard MAX7219 ICs

 

 

A small aluminium bar is cut to size and 6 x holes are drilled to take the matrix module mounting bolts.

Note on pre-built modules the lower connectors may need to be de-soldered and re-soldered on the rear of the boards.

Use extra nuts and bolts as spacers over the exposed solder pads as required.

 

 

 

A clear Perspex sheet is then cut to size with mounting holes drilled and is fitted on top of the aluminium bar.

 

 

 

Print out the display on Lazertran decal transfer paper and fix to Perspex sheet making sure it is aligned with the dot matrix modules.

Let the paper dry and don't spray with acrylic varnish to keep the transfer opaque to act as a diffuser for the LEDs.

 

 

 

Add a final sheet of white paper behind the Perspex to complete the diffused effect. The heavier the paper the more diffused (and dimmer) the matrix LEDs will be.

You can offset this effect is the software by setting different intensity values for the 7 segment displays and matrix displays.

Note if you don't want to use Lazertran you could print the display on white paper and fix it behind the Perspex.

 

 

 

 

 

 

 

 

Main Display Board Construction

The main display board to house the four 7 segment displays is made up from 2 Cero boards bolted together with L shaped insulated aluminium angles.

On the finished clock the main Vero Board housing the twin Arduinos are fixed to the top six mounting bolts.

 

 

Vero boards bolted together to mount displays. Board fronts are sprayed black.

 

Connection strips are soldered to the display boards.

The four 7 segment displays plugged into connection strips, positioned marked by the four labels.

Power and serial connections for the displays are run on the from of the display board.

 

 

 

Vero Board Layouts & Mounting

Note board fronts are shown orange for clarity actual board fronts are spay painted black

 

Front of display boards showing 7 segment display connection strips

 

 

 

below 7 segment display modules mounted along with black wooden blocks for fixing Perspex neutral density contrast display cover

note black tape colon masking on top display module

 

 

 

7 segment display modules on without Perspex neutral density contrast display cover

 

 

 

same display but with Perspex neutral density contrast display cover fixed to wooden mounting blocks

 

 

Perspex neutral density contrast display cover with Letroset applied

 

 

 

Display Positions in Main Case

 

 

 

 

 

Rear (solder side) of display boards

 

 

 

rear of display boards with insulated aluminium angles bolted on

 

 

 

main board fixed to rear of display boards on aluminium angles

 

 

 

 

 

 

 

Main Vero Board

Vero Layout of main board front top and rear bottom the Atemga 328 on the controls the DCF77 Analyzer and displays.

The Atmega 328 on the right controls the Super Filter.

 

 

 

 

 

 

Fully assembled main display

 

above & below assembled display and main board

 

 

 

 

 

 

Modification of the RTC Time 7 Segment Display Module to Show Colon Digit Seperators

The standard display only has decimal points to separate the digits

 

 

To display colons on digit 3 and 6 these 2 digits are set to display "o" lower case letter O permanently

This illuminates the bottom 4 led segments only

 

 

4 x strips of black plastic tape are then applied over the digits as below

 

 

when lower case "o" is displayed all you see is colons

 

 

 

 

 

PIR Detector and Module

The PIR sensor is mounted on the top of the clock with the PIR module PCB mounted on the underside of the lid.

Above. Case lid with handle and PIR Sensor Cover.The PIR sensor is de-soldered from the module PCB

and is fitted under the half blacked out sensor diffuser on the top of the lid.

Original sensor

 

The PIR sensor is de-soldered from the module PCB

and is fitted under the half blacked out sensor diffuser on the top of the lid.

Above PIR sensor fitted to PCB.

Below PIR PCB from below showing PIR sensor de-soldered and removed

 

Connect three wires from the PCB to the de-soldered PIR Transistor

 

Below. Underside of lid showing PIR sensor PCB and wiring to sensor on top of the lid.

Slots on the underside of lid allow the lid to sit flush over the original handle fixings.

 

 

 

 

 

 

 

RTC Modification

 

Modification of DS3231 AT24C32 I2C Precision Real Time Clock Module

My clock uses a DS3231 AT24C32 I2C Precision Real Time Clock Module instead of a DS1307.

The module comes supplied with a Lithium-Ion rechargeable battery see diagram above. I use a non rechargeable battery so have removed resistor R5

from the module as below.

 

Location of R5 on the DS3231 module.

 

Charging Resistor R5 removed.

 

 

 

 

 

 

 

 

 

 

Remote Control

The remote control has four functions

 

1. DCF77 Pulse sound On/Off

When switched On the incoming DCF77 Pulses can be monitored via the small active buzzer

 

2. Super Filter Reset

This will reset the Super Filter and remove all the learned tuning and will start the Super Filter unsynced.

The Super Filter Monitor LEDs if switched on will go out until the Filter resynchronizes itself

 

 

3. Super Filter Monitor LEDs

These 3 LEDs monitor the function of the Super Filter

LED1- DCF77 Second Pulse

LED2 DCF77 Signal Good

LED3- Synthesized difference

 

 

4.

DCF77 Analyzer Clock Reset

This will reset the clock and displays to zero and the clock will lose any sync it had.

If the RTC had been previously set RTC time will be displayed.

Note the Super Filter will not be reset and any learned tuning and sync will not be effected.

 

 

If remote control is not required then momentary switches can be used instead.

 

 

Remote Control Construction

 

The remote control is based on a PT2272 Wireless Receiver Module (433MHz version) and a PT2262 Wireless Transmitter Module (433MHz version)

             

I use the 433Mhz version here in the UK but there are 315Mhz versions available for use in other countries.

You can get a 315Mhz Transmitter ready built in a key fob including the separate receiver on Ebay. This option does not seem to be available in 433 Mhz.

 

If you go for this option you just plug the receiver into the main display board and you are ready to go.

 

The 433Mhz option requires either a small remote transmitter to be constructed or a learning key fob can be purchased and learnt from your transmitter.

 

      

Above Remote Control Transmitter for 415Mhz boards

The left four switches mounted on a small piece of vero board.

The transmitter is mounted on the reverse along with a 9v battery.

See instructions supplied with the module for wiring.

 

 

 

Alternatively a learning key fob can be purchased and can be programmed from the transmitter using

the simple circuit above. A PCB link is used to connected the micro switch to the four inputs in turn so the key fob can be programmed.

 

 

 

 

 

 

 

 

 

 

Super Filter On/Off Control Switch

A single switch controls the Super Filter and has 3 positions

On Super Filter - When On the Super Filter output is connected to the Analyzer Clock DCF77 input and 5v is connected to IC1 pin 5 (IDE pin 3) to light the Super Filter LED on display matrix 3.

No Connection- When in this position the Analyzer Clock DCF77 input is not connected to any signal

On DCF77 Signal- In this position the Analyzer Clock DCF77 input is connected directly to the DCF77 receiver/antenna.

Note in all positions the Super Filter is connected to the directly to the DCF77 receiver/antenna so it alway remains in sync.

 

 

 

 

 

 

 

 

 

 

Reading The Received and Decoded Matrix Buffer Displays

The received matrix buffer is on the left and contains the information for the next minute. The display is identical the

decoded matrix in the middle which shows the time, date etc for the current minute.

After each minute (if error checking is OK) the Next Minute buffer data is loaded into the Middle Matrix and becomes the Current Minute.

DCF77 data is received serially once per second and once the clock is in sync second 0 is loaded into buffer 00 (Matrix position M), second 1 into buffer 01 etc.

When a bit value of 1 is received the LED for that segment is lit but will remain unlit if the value is 0.

 

 

 

Second 0  Buffer 00  Matrix Label M

This is the start of the minute and will always be 0.

 

 

 

Second 1 to 14  Buffer 01 to 14  Matrix Label 01 to 14

These bits contain licensed encrypted Weather data for Europe and is used by DCF77 weather clocks.

This data is displayed but not used by this clock.

 

 

 

Second 15  Buffer 15   Matrix Label Flt This bit contains transmitter info and is 0 unless there are transmitter problems

Second 16  Buffer 16  Matrix Label LHr  Summer time announcement normally 0. Set to 1 during hour before change.

Second 17  Buffer 17  Matrix Label CEST  Set to 1 when CEST (Central European Summer Time) is in effect.

Second 18  Buffer 18  Matrix Label CET  Set to 1 when CET (Central European Time) is in effect.

Second 19  Buffer 19  Matrix Label LSec  Leap second announcement. Set to 1 during hour before leap second.

 

 

 

Second 20  Buffer 20  Matrix Label S  Start of encoded time. Always set to 1.

 

 

 

 

Second 21 to 27  Buffer 21 to 27  Matrix Label 01,02,04,08,10,20 & 40.

  These bits contain the minutes and are added together to show minutes from 00 to 59.

21 minutes would have 01 and the 20 LEDs lit, likewise 53 minutes would have 01,02,10 & the 40 LEDs lit

 

 

 

 

Second 28  Buffer 28  Matrix Label P1

This bit shows even parity over minute bits 2128.

Parity is a basic form of error checking. A Parity bit is added to the end of a string of digits in this case 7 bits from buffer 21 to 27.

The Parity bit indicates if the number of 1s in the buffer from 21 to 27 is even or odd.

If the number of 1s is even using Even Parity the Parity bit is 0 and if the number of 1s is odd then the Parity bit is 1.

For example if the minutes to be transmitted are 5. Buffer Bits (Yellow hi-light above) 01=1, 02=0, 04=1, 08=0, 10=0, 20=0 & 40=0

0r 1010000 if you add the number of 1s together you get 2. This is an even number so the Parity bit transmitted will be 0.

Therefore the transmitter will send 1010000 & 0.

At the receiver end the receiver does not know what code to expect but it knows to check the parity of bits 21 to 27.

If when receiving the bits 21 to 28 a miss reading occurs for example bit 22 is received as a 1 not a 0 the resulting bits would be 11100000.

The receiver would expect the last bit (Parity) to be 1 as there are a an odd number of 1s.

This indicates an error in the received signal and the received buffer would be rejected as it contains errors.

 

 

 

 

Second 29 to 34  Buffer 29 to 34  Matrix Label 01,02,04,08,10 & 20.

These bits contain the hours and are added together to show hours from 00 to 23.

10 hours would have only the 10 LED lit , likewise 23 hours would have 01,02 & the 20 LEDs lit

 

 

 

 

Second 35  Buffer 35  Matrix Label P2

This bit shows even parity over minute bits 29 to 35.

See explanation of Parity in P1 (seconds 28 above)

 

 

 

 

Second 36 to 41  Buffer 36 to 41  Matrix Label 01,02,04,08,10 & 20.

These bits contain the day of the month and are added together to show days from 01 to 31.

The 10th of the month would have only the 10 LED lit , likewise the 23rd of the month would have 01,02 & the 20 LEDs lit

 

 

 

 

Second 42 to 44  Buffer 42 to 44  Matrix Label 01,02 & 04.

These bits contain the day of the week and are added together to show  Day of week Monday=1, Sunday=7

Monday would have only the 01 LED lit , likewise Sunday would have the 01,02 & the 04 LEDs lit

 

 

 

 

Second 45 to 49  Buffer 45 to 49  Matrix Label 01,02, 04, 08 &10

These bits when added together contain the month number and range from 01 to 12.

January would have only the 01 LED lit , likewise July would have the 01,02 & the 04 LEDs lit

 

 

 

 

 

Second 50 to 57  Buffer 50 to 57  Matrix Label 01,02, 04, 08, 10,20,40 & 80

These bits when added together contain the year number within the centuary and range from 00 to 99.

Year 01 would have only the 01 LED lit , likewise the year 99 would have the 01,08, 10, & the 80 LEDs lit

 

 

 

 

 

Second 58  Buffer 58  Matrix Label P3

This bit shows even parity over minute bits 36 to 58.

See explanation of Parity in P1 (seconds 28 above)

 

 

 

Seconds 59 Buffer 58 Matrix Label

On the 59th second the minute mark no 1 or 0 are transmitted indicating the end of the buffer on the next received pulse the buffer is reset to 00.

 

 

 

Reading The Day, Display Intensity and Status/Info Matrix Display

The 3rd LED Matrix display shows the Day, the display intensity, the clock status and also info and is split in four sections

 

 

Day

This section simply shows the current day illuminated by an LED on the Matrix.

 

 

 

 

Display

This section of 16 LEDs indicate the auto adjusted Matrix and 7 segment display intensities.

The light level is checked once per second and the intensity is adjusted up and down accordingly.

 

 

 

 

Status

Eight LEDs indicate clock status messages.

DCF: When lit this shows the DCF77 signal is OK

Sync: When lit this shows the clock is synchronised to the DCF77 signal

PT: When lit this shows there is an error with the Period Time of the received signal

PW: When lit this indicates an error with the Pulse Width of the received signal

EoB: Lights when buffer is full before end of 60 second cycle

EoM: Lights when buffer is not full at end of 60 second cycle (end mark received too early)

BF: This indicates the Buffer is full and will normally light at the end of the 60 second receiving cycle.

If will also light at other times when lots of error pulses are being received.

When lit the buffer is reset to 00.

RTC: When there is an error with the Real Time Clock ie no stored time this will light

 

 

 

Parity

This is new to v60 software

The incoming signal displayed on the left hand Matrix is Parity checked at 3 points, P1 for mins, P2 for hrs and P3 date.

If Parity is an even value then "P1 OK" , "P2 OK" & "P3 OK" will light accordingly as the data comes in.

Any Parity checks that fail will light the relevant P1 to P3 fail LED.

These are reset on the end of each minute cycle.

 

Parity is a basic form of error checking. A Parity bit is added to the end of a string of digits in this case 7 bits from buffer 21 to 27.

The Parity bit indicates if the number of 1s in the buffer from 21 to 27 is even or odd.

If the number of 1s is even using Even Parity the Parity bit is 0 and if the number of 1s is odd then the Parity bit is 1.

For example if the minutes to be transmitted are 5. Buffer Bits (Yellow hi-light above) 01=1, 02=0, 04=1, 08=0, 10=0, 20=0 & 40=0

0r 1010000 if you add the number of 1s together you get 2. This is an even number so the Parity bit transmitted will be 0.

Therefore the transmitter will send 1010000 & 0.

At the receiver end the receiver does not know what code to expect but it knows to check the parity of bits 21 to 27.

If when receiving the bits 21 to 28 a miss reading occurs for example bit 22 is received as a 1 not a 0 the resulting bits would be 11100000.

The receiver would expect the last bit (Parity) to be 1 as there are a an odd number of 1s.

This indicates an error in the received signal and the received buffer would be rejected as it contains errors.

 

 

 

 

 

 

Info

Eight LEDs indicate clock information

Sig: This LED will flash with the received signal Pulse Width and is not effected by the Super Filter if switched on

Spk: When lit this indicates the received signal speaker is activated and signal pulses can be heard through the speaker

Fltr: When lit the Super Filter is turned on and the incoming signal is being processed and synthesised.

LEDs: When lit the Super Filter monitor LEDs are turned on.

Rmte: Lights when any button on the remote is pressed.

Win: Lights when the clock is in Wintertime

Sum: Lights when the clock is in Summertime

Leap: Light when it is a leap year

 

 

 

 

 

 

DCF77 Transmitted Code Translation for each Buffer value

 

 

 

 

 

 

DCF77 Code Display Animation

 

Above 65 second animation of DCF77 receiver/decoder matrix with RTC Time, Buffer & DCF Bit included for info

 Data for the next minute is loaded second by second into the left hand matrix

On receipt of the minute mark the data is loaded into the middle matrix and into the clock for decoding into time and date

Note. Normally the received 100mS and 200mS signal is indicated by the flashing "Sig" indicator bottom right of the 3rd matrix. This is not shown in this animation.

 

 

 

 

 

 

 

 

Schematic

 

Note if not supplied with display kits add 100nF and 10F capacitors close to every MAX7219 IC

Ensure good 5v and Gnd runs to every display module

 

 

 

 

Atmega 328 IC1 & 2 Pin connections

IC No. IC Pin IDE Pin Sketch Name Function
1 1 Reset Remote4 Reset DCF77 Analyzer
1 2 0 Tx & DCF_INTERRUPT Tx & Interrupt number associated with pin
1 3 1 Rx Rx
1 4 2 DCF77PIN  INPUT: Connection pin to DCF 77 device. Must be pin 2 or 3!
1 5 3 DCF77SW LED Turns LED on when switching to the superfilter output
1 6 4 Super Filter on LED Signal mon from super filter
1 7 NA   4.4v
1 8 NA   Gnd
1 9 NA   Xtal1
1 10 NA   Xtal2
1 11 5 Remote1 DCF77 Speaker Control
1 12 6 Spare Spare used to reset super filter not connected on IC1
1 13 7 Remote3 Superfilter monitor LED switch on
1 14 8 REmote3out Turns on Superfilter LEDs via transistor 1
1 15 9 RemoteLEDpin Remote control monitor LED. Lights when remote pressed
1 16 10 CS/LOAD Connected to the CS/LOAD of the MAX72XX IC
1 17 11 CLK Connected to the CLK of the MAX72XX IC
1 18 12 DataIn Connected to the DataIn of the MAX72XX IC
1 19 13 PIR Connected to the PIR detector
1 20 NA   4.4v
1 21 NA   AREF
1 22 NA   Gnd
1 23 A0 LDR LDR sensor
1 24 A1 BUZZER OUTPUT: Piezo buzzer on Analog port
1 25 A2 Sp LDR sensor
1 26 A3 Spare Spare
1 27 A4 RTC SDA
1 28 A5 RTC SCL
2 1 Reset Remote 2 Superfilter reset
2 2 0   Rx
2 3 1   Tx
2 4 2   Diff Synthesized
2 5 3   DCF77 Signal Good
2 6 4   Inverted Synthesized
2 7 NA   4.4v
2 8 NA   Gnd
2 9 NA   Xtal1
2 10 NA   Xtal2
2 11 5   Synthesized
2 12 6   Spare
2 13 7   DCF77 Difference
2 14 8   Spare
2 15 9   Spare
2 16 10   DCF77 Second Pulse
2 17 11   DCF77 Inverse Pin
2 18 12   DCF77 Filtered Pin
2 19 13   Spare
2 20 NA   4.4v
2 21 NA   AREF
2 22 NA   Gnd
2 23 A0   Difference Semi Synthesyzed
2 24 A1   Inverted Semi Synthesized
2 25 A2   Semi Synthesized
2 26 A3   Spare
2 27 A4   Signal Monitor Super Filter
2 28 A5   DCF77 Inverse Pin

 

 

 

 

4 K Ultra HD Video of completed Clock

 

 

 

 

 

 

Code

The main bulk of this code was written by Erik de Ruiter's DCF77 Analyzer Clock

Erik has provided full details of his code here on GitHub.

I have tried to keep his code as and where I have not used it I have commented it out.

Where I have added or modified it I have added a comment  starting // Brett

 

 

Download v60 code in zip format here DCF77 Analyzer Code

New v60 code

/*
 ================================================================================
 DCF77 Analyzer / Clock
 ================================================================================
 This sketch is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
 
 This sketch 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
 Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 
 The C++ code is far from optimized because I myself am an Arduino and C++ novice.
 But even after learning some more now, I want to keep the code simpel and readable.
 That is why I maybe over-documented the code to help understand what's going on.

 HELP WANTED: 
 I'm not experienced enough to get to the point where the second-tick
 of the Real Time Clock display is exactly in sync with the DCF pulse.
 Now there's a small time lag.
 If you have a solution, please let me know! 
 

 
 
 Erik de Ruiter
 2014-2016
 
 May 2014 First version
 March 2016 - big overhaul...
 
 
 Version 1.72
 - Option: Use a cheap Ebay PIR detector to shut off selectable display's when no activity is detected. 
   The switch off delay can be set by the user to prevent the display shutting of if a person
   is not moving but the display should be on.
 - Now the display Night shut-down can be disabled by making both values 'POWERSAVINGOFFTIME'
   and 'POWERSAVINGONTIME' zero. 
 - Fixed temperature display not shutting off at powersave mode.  
 - errorCounter display did not reset every hour so that's fixed

 Version 1.71
 - User option to reset temperature min/max memory at midnight

 Version 1.7:
 - The resolution of the temperature display is improved: from 0.5 to 0.1 degrees Celsius
   Because of the time the DS18B20 sensor needs to convert the temperature and to keep the code clean, 
   the temperature display is updates once per minute.
 - Parity check routine optimized. 
 - More reliable check for bad DCF data, preventing RTC update with invalid data.
 - EoB error now clears inner LED ring as it should.
 - The DCF OK LED now displays the condition of the DCF signal more reliably. Turns off immediately if an error occurs
   and only turns ON when all 3 parity bits are OK.

 Version 1.6:
 - Changed temperature function to only calculate once per minute. Got strange errors before the change because
   I used a delay of 100ms to give the DS18B20 sensor time to calculate the temperature. But the delay function is
   a very bad idea in most c++ code so I finally got rid of it.

 Version 1.5:
 - Complete overhaul of the scanSignal function and the rest of the code! My first attempt worked but could be improved...
 - The rPW and rPT led's did not work as I intended so that is corrected now.
 - The End of Buffer error check routine does work now as it should.
 - I incorporated a Parity check of the incoming DCF signal. In the signal 3 Parity bits are sent so now these are
   checked and only if all three are OK, the received time information is accepted, the display is updated and the RTC synced.
   if desired, you can attach 3 extra dual-color LED's (Common Cathode) to see if each of the 3 Parity bits are OK or Failed.
 - I made wiring (or changing the wiring) much easier I think by putting all the PIN config in one easy to read table
 - As long as you use 1 DS18B20 temp. sensor, I edited the code so you no longer need to figure out the address of the I2C device.
 - Big clean-up of the code...
 - Powersaving by shutting off the displays (the clock remains functioning as normal)
   can now be configured somewhat easier by editing two variables POWERSAVINGONTIME and POWERSAVINGOFFTIME.
 - changed some variable names:
   - Maxim instances 'lc' and 'lc1' are now MaximCC and MaximCA
   - Display description MaximDcfTime is now MaximTemperature 
   - DCF77SOUNDPIN is now BUZZERPIN
 - LED/Display test after power up now build in
 
 ***********************************************************************************************
 Brett Oliver 25/05/2016
 Version 60
 Converted my v34 to Eric's v1.72 to work on my clock hardware.
 Parity LED's x6 added to MaximLEDs
 CA display & temperature display removed
 Time is displayed on a CC display with colon's added with a mask
 This version uses the unmodified LEDControl library as no CA displays are used
 No chime or power saving option used on this version
 PIR detection is always on, delay and sensitivity controlled by the PIR module
 Eriks LED test added to all LEDs on Maxim display and order of LEDs test modified to suit my hardware
 Remote control of;
 1 Udo Klein super filter indicator LEDs
 2 DCF77 buzzer On/Off
 3 Super Filter reset
 4 Main Arduino Reset
 Remote control signal received LED on Maxim LED display
 Note 1. I have a Udo Klein super filter on the DCF77 input to this clock. This can be switched On & Off
 Note 2. This clock displays the raw DCF77 signal and converts it to show date and day of week etc as CET.
 Time is shown as GMT so an hour before midnight GMT the date and Day of week will change early as per CET.
 
 Thanks to Erik for sharing his code and clock design!
 ***********************************************************************************************
 
  Short description:
   
  Power On:
    After power-on, first a LED test is performed. The LED's and displays lite up sequentially to keep the power consumption low.
    Then the clock starts receiving DCF pulses and when a Minute Mark (2 seconds gap) is detected, the Minute Marker LED is lit
    and the buffer counter is reset. The inner LED ring now will show the incoming DCF pulses which are also stored in the buffer.
    At 3 moments during reception of data the parity DCF bits are checked to see if the data is valid.

  Valid data received:
    When, at the end of the minute, after the Minute Mark is detected (BF (Buffer Full) LED is lit), all three parity bits are OK
    ('DCF OK' LED is lit), the buffer information is used to extract time and date information. 
    Then the RTC clock is updated ('RTC Synced' LED is lit) and the inner LED ring information is copied to the outer LED ring. 
    The time, date and week display, day LED, summer/wintertime and leap year LED information is updated with the new time information.

  No valid data:
    When one or more of the parity bits are not OK because of a noisy signal, receiving of DCF information is continued but
    will not be used to update the RTC, display's and LED's. The outer LED ring, 'RTC synced' and 'DCF OK' LED's will be reset. 
    Time, date, week, day LED, summer/wintertime LED and leap year LED are not affected and keep displaying the last received valid values.
    The 'Period Time' and/or 'Period With' error LED's will indicate the error(s) and the error counter display is updated. 
    Every hour, the error display will bet set to zero. 
    The EoB, End of Buffer LED is lit when more DCF pulses are received before the Minute Mark is detected due to a noisy signal.
    (When a minute Mark is detected we should have no more than 58 bits/pulses) 
    After the detection of the Minute Marker, a new cycle is started.
 
  Temperature:
    At the 30 second mark, the temperature display will show the High and Low values of the past period after the last reset.
  
  Chime:
    At the beginning of each hour, the Chime (if connected) will sound. 
    At night time, a time set by the user in the code itself, the chime is disabled.

  Power saving, two options:
    1. NIGHT SHUT OFF
       At times set by the user, the displays are shutt off at night and turned on in the morning.
       Look at the POWERSAVINGOFFTIME and POWERSAVINGONTIME variables. 
       Check the function <turnDisplaysOff> to select which displays you want to shut off at night.
    2. PIR SENSOR
       Connect a PIR sensor and activate the PIR option POWERSAVE_BY_PIR and the the delay at PIR_DELAY_TIME.
       Every time the PIR detector senses movement, a minute counter is reset but if no movement is detected
       longer than the PIR_DELAY_TIME, the displays are shut off. 
       When movement occurs, the displays immediately switch on. 
    Note: as said before, the clock will function normally while the displays are shut off. 
    The only thing is you can't see it... ;)

  DCF beep:
    With a switch, connected to pin BUZZERPIN, you can hear the received DCF bits coming in. 
    The tone duration is equivalent to pulse width of the DCF bits, so either 100 or 200 ms.
  
  Miscelleanous:
    When the RTC battery is empty or a connection fault is detected, the RTC Error LED is lit.




 CREDITS:
 I learned a lot from the work of Matthias Dalheimer and Thijs Elenbaas who made their own DCF77 decoders.
 Without their work I would not have known where to start.
 I ended up writing my own code (using bits and pieces of their ideas) so I could understand what is happening...
 My code is far from efficient or advanced but it does work and I know what is going on.
 
 * A big Thank You to Brett Oliver and Joop Tap for pointing out some errors!

 Interesting websites:

 - Brett Oliver         : http://home.btconnect.com/brettoliver1/
 - Joop Tap             : http://www.jooptap.nl
 - Thijs Ellenbaas      : http://thijs.elenbaas.net/2012/04/arduino-dcf77-radio-clock-receiver-hardware-2/
 - Mathias Dalheimer    : https://github.com/roddi/DCF77-Arduino/blob/master/DCF77Servoclock/DCF77.h
 - DCF77 wikipedia      : https://en.wikipedia.org/wiki/DCF77
 - Much more DCF77 info : http://www.picbasic.nl/indexes_uk.htm

 - My Flickr website    : https://www.flickr.com/photos/edr1924/albums
 - My Github website    : https://github.com/deruiter
 - The Instructables website for this clock: soon!

 */



//----------------------------------------------------------------------------------------------------------
// Libraries
//----------------------------------------------------------------------------------------------------------

// Arduino (new) Time library .................................... http://www.pjrc.com/teensy/td_libs_Time.html
#include <Time.h>

// Enable this line if using Arduino Uno, Mega, etc.
#include <Wire.h>

// a basic DS1307 library that returns time as a time_t .......... http://www.pjrc.com/teensy/td_libs_DS1307RTC.html
#include <DS1307RTC.h>

// Maxim 7219 displays library ................................... http://playground.arduino.cc/Main/LEDMatrix
// !!! NOTE: you must use a special version of the Ledcontrol.h library to get Common Anode support 
// because the Maxim chip is normally only suitable for common CATHODE displays!
// Brett standard lirary used in this clock
#include <LedControl.h>

//SPI interface library .......................................... http://arduino.cc/en/Reference/SPI
#include <SPI.h>



//----------------------------------------------------------------------------------------------------------
// Arduino UNO Pin connections in an easy to read table
//
                              // Pin  0 - input  - Rx - used for programming/communication with PC
                              // Pin  1 - output - Tx - used for programming/communication with PC
                              
#define DCF77PIN            2 // Pin  2 - input  - DCF signal from antenna pcb. Must be pin 2 or 3 because of interrupt!
#define DCF77Switch       3   // INPUT: from superfilter
#define BUZZERPIN           4 // Pin  4 - input  - SWITCH - turn on/off DCF77 'beep' piezo buzzer / ON = HIGH, OFF = LOW
#define MAXIMCCLD          10 // Pin 10 - output - CS/LOAD - Maxim Common Cathode 7 segment displays
#define MAXIMCCCLK         11 // Pin 11 - output - CLOCK   - Maxim Common Cathode 7 segment displays
#define MAXIMCCDATA        12 // Pin 12 - output - DATA    - Maxim Common Cathode 7 segment displays
#define BUZZER             A1 // Pin A1 - output - Piezo buzzer for DCF77 'beep' (to '+' of the buzzer)
// USED for DS1307 RTC        // Pin A4 - I2C DATA  - connect to Real Time Clock pcb
// USED for DS1307 RTC        // Pin A5 - I2C CLOCK - connect to Real Time Clock pcb



//----------------------------------------------------------------------------------------------------------
// Maxim 7219 Matrix Display initialization
//----------------------------------------------------------------------------------------------------------
/*
 clearDisplay(int addr) ........................................ clears the selected display
 MaximCC.shutdown(int addr, boolean) ................................ wake up the MAX72XX from power-saving mode (true = sleep, false = awake)
 MaximCC.setIntensity(int addr, value) .............................. set a medium brightness for the Leds (0=min - 15=max)
 MaximCC.setLed(int addr, int row, int col, boolean state) .......... switch on the led in row, column. remember that indices start at 0!
 MaximCC.setRow(int addr, int row, byte value) ...................... this function takes 3 arguments. example: MaximCC.setRow(0,2,B10110000);
 MaximCC.setColumn(int addr, int col, byte value) ................... this function takes 3 arguments. example: MaximCC.setColumn(0,5,B00001111);
 MaximCC.setDigit(int addr, int digit, byte value, boolean dp) ...... this function takes an argument of type byte and prints the corresponding digit on the specified column.
                                                                 The range of valid values runs from 0..15. All values between 0..9 are printed as digits,
                                                                 values between 10..15 are printed as their hexadecimal equivalent
 MaximCC.setChar(int addr, int digit, char value, boolean dp) ....... will display: 0 1 2 3 4 5 6 7 8 9 A B C D E F H L P; - . , _ <SPACE> (the blank or space char)
 
 ***** Please set the number of devices you have *****
 But the maximum default of 8 MAX72XX wil also work.
 LedConrol(DATAIN, CLOCK, CS/LOAD, NUMBER OF MAXIM CHIPS)
 */

// MaximCC is for the Maxim Common CATHODE displays
LedControl MaximCC = LedControl(MAXIMCCDATA, MAXIMCCCLK, MAXIMCCLD, 7); // Define pins for Maxim 72xx and how many 72xx we use

                             
//----------------------------------------------------------------------------------------------------------
// User settings, variable and array definitions
//----------------------------------------------------------------------------------------------------------

// The value below is not a PIN number but a value to set how many times the 'Lower volume' input on the sound board is activated
// so that way the volume of the sound board can be lowered after power up, if desired.
//#define SPEAKERVOLUME  12            

// Choose if you want a test of all LED's and Displays after a startup
// '1' = Yes, '0' = No
#define PERFORM_LED_TEST 1
// Delay between each 7 segment display in ms
#define LEDTEST_DELAY_DISPLAYS 1500
// Delay between each LED in the LED ring and other LED's in ms
#define LEDTEST_DELAY_LED_RING 25

// Choose if you want to configure the DS18B20 temperature sensor ONCE to the highest resolution.
// this is needed after using the sensor for the first time. After running the software
// with this setting ON one time, shut it off.
// '1' = ON, '0' = OFF
//#define CONFIGURE_DS18B20 0

// define power saving display OFF and ON time
// values are in 'Hour' format
// ONLY the displays are shut off at power saving time, the clock remains active.
// To disable this feature, make both values zero
#define POWERSAVINGOFFTIME 0  // displays are activated
#define POWERSAVINGONTIME  0 // displays are shutt off

// User option to reset temperature min/max memory at midnight
// '1' = Reset at midnight, '0' = Only manual reset
//#define TEMPRESET_MIDNIGHT 1

// User option: activate the displays only when there is activity in the room
// '1' = ON, '0' = OFF
//#define POWERSAVE_BY_PIR 1
// delay in MINUTES to wait after no detection before shutting off the displays 
//#define PIR_DELAY_TIME 5 

//-------------------------------------------------------------------------------
// define miscellaneous parameters
#define DS1307_I2C_ADDRESS  0x68      // define the RTC I2C address
#define DCF_INTERRUPT       0         // Interrupt number associated with pin

// definition of Maxim 7219 display number wiring sequence
// first Maxim 7219 in wiring 'daisychain' must be '0', next '1' etc.
// COMMON CATHODE DISPLAYS
#define MaximLedRingInner   0
#define MaximLedRingOuter   1
#define MaximPeriodPulse    6
#define MaximBufferBitError 5
#define MaximWeek           7 // Brett not used
#define MaximDate           4 
#define MaximRtcTime        3
#define MaximLeds           2
/*
// COMMON ANODE DISPLAYS
#define MaximTemperature    0
*/
// definition of display brighness levels
// Brett use intensity if different intensity levels required on Dot matrix and 7 seg displays
//#define BrightnessMaximLedRingInner     intensity2
//#define BrightnessMaximLedRingOuter     intensity2
//#define BrightnessMaximLeds             intensity2
#define BrightnessMaximLedRingInner     intensity
#define BrightnessMaximLedRingOuter     intensity
#define BrightnessMaximLeds             intensity
#define BrightnessMaximRtcTime          intensity
#define BrightnessMaximDate             intensity
#define BrightnessMaximBufferBitError   intensity
#define BrightnessMaximPeriodPulse      intensity
//#define BrightnessMaximWeek             3 // Brett week display not used


// definition of Status/Error Led's. We use division and modulo to seperate Row/Col values
// to lit a LED you need a ROW and COLUMN value.
// so, for example: BFLed (with value 14) divided by 10 results in the value '1' (row)
// and BFLed % (Modulo) 10 results in the value '4' (column)
// Modulo explained: http://www.cprogramming.com/tutorial/modulus.html

/* Row/Col LED numbers of Maxim 7219 chip
 LED            Row Col
 -------------- --- ---  ----------------------------------------------------------------
 Sunday          0   1
 Monday          0   2
 Tuesday         0   3
 Wednesday       0   4
 Thursday        0   5
 Friday          0   6
 Saturday        0   7
 Synced          5   1  ON when RTC is set by the received DCF time
 RTCError        5   7  ON when there is no response from the RTC (connection / battery empty)
 SummerTime      7   5  ON when Central European Summer Time is set
 WinterTime      7   4  ON when Central European Time is set
 LeapYear        7   6  ON when we have a Leap Year... Duh!
 BF              5   6  Flashes ON shortly when the DCF buffer if full and can be processed
 EoM             5   5  Flashed ON shortly when the Minute Mark is detected BEFORE the DCF buffer is filled (after power up or other errors)
 EoB             5   4  Flashes ON shortly when too many pulses are received before the Minute Marker (noisy signal)
 rPW             5   3  Flashes ON shortly when the time between two pulse periods is out of set limits
 rPT             5   2  Flashes ON shortly when the pulse time is out of set limits
 DCFOk           5   0  ON when whe have a received a full minute of valid DCF data
 SpkLED      7    0
 FilterLED   7    1
 Remote R3   7    2
 LEDs        7    3
 Remote      7    4

 Chime           6   7  ON when the Chime is enabled. If it's night time or wh switch the Chime off, this LED should be OFF
 LEDP1Pass       6   0  ---
 LEDP1Fail       6   1  Parity Check LED's, use if desired. 
 LEDP2Pass       6   2  WHILE receiving the incoming bits, at 3 moments, you can see if the Parity bits are OK or FAILED... 
 LEDP2Fail       6   3  I use 3 dual color 3mm LED's for this purpose but you can also only use the 'PASS' LED's)
 LEDP3Pass       6   4    
 LEDP3Fail       6   5  ---  

*/
#define SignalLED      70
#define SyncedLed      51
#define RTCError       57
#define SummerTimeLed  76
#define WinterTimeLed  75
#define LeapYearLed    77
#define BFLed          56
#define EoMLed         55
#define EoBLed         54
#define rPWLed         53
#define rPTLed         52
#define DCFOKLed       50
#define ChimeLed       67
#define LEDP1Pass      60
#define LEDP1Fail      61
#define LEDP2Pass      62
#define LEDP2Fail      63
#define LEDP3Pass      64
#define LEDP3Fail      65
#define SpkLED         71
#define FilterLED      72
#define LEDs           73
#define RemoteLED      74
#define intensityLED   intensityDisp

// Pulse flanks
static unsigned long flankTime    = 0;
static unsigned long leadingEdge  = 0;
static unsigned long trailingEdge = 0;
unsigned long previousLeadingEdge = 0;

// used in <Int0handler>
volatile unsigned int DCFSignalState = 0; // interrupt variables ALWAYS need volatile qualifier!!

// used in <loop>
int previousSecond      = 0;
int previousSignalState = 0;

// DCF Buffers and indicators
static int DCFbitBuffer[59]; // here, the received DCFbits are stored
const int bitValue[] = {1, 2, 4, 8, 10, 20, 40, 80}; // these are the decimal values of the received DCFbits

// only after start on a new minute, display received bits on inner LED ring
boolean MinuteMarkerFlag = false;
int bufferPosition       = 0;
int previousMinute       = 0;
int previousHour         = 0;

// used to check if switch state is changed



// variables to check if DCF bits are vald
bool dcfValidSignal  = false;
int dcfP1counter     = 0;
int dcfP2counter     = 0;
int dcfP3counter     = 0;
int dcfParityCheckP1 = 0;
int dcfParityCheckP2 = 0;
int dcfParityCheckP3 = 0;

int GMThour = 0;

// display intensity Brett
int intensity = 0; // Intensity of MAXIM 7 digit LEDs

int intensity2 = 0; // intensity of MAXIM Matrix LEDs
int intensityDisp = 20;
int ldr = A0; // LDR connected to analogue A2 pin
unsigned int ldrValue; // LDR value

int filterSw = 0;

// PIR detector for blanking displays Brett
int PIRval = 0; // infrared value
int PIR = 13; // set pin 13 as input from PIR


// Remote control switch toggles Brett
int remote1val; // current value of remote sw no1
int remote1prev = 0; // previous value of remote sw no1
int remote1in =5;  // remote control input 4 is pin 5 (pin 11 on IC)

int remote2val; // current value of remote sw no2
int remote2prev = 0; // previous value of remote sw no2
int remote2in =6;  // remote control input 3 is pin 6 (pin 12 on IC)

int remote3val; // current value of remote sw no3
int remote3prev = 0; // previous value of remote sw no3
int remote3in = 7;  // remote control input 2 is pin 7 (pin 13 on IC)
int LEDctrl = 0;
int remote3out = 8;

/*
int remote4val = 0; // current value of remote sw no4
int remote4prev = 0; // previous value of remote sw no4
int remote4in =8;  // remote control input 1 is pin 8 (pin 14 on IC)
*/
 // Remote control monitor LED lights when any remote button pressed
int RemoteLEDpin = 9;
int Remotepin = 0;

// DCF77 Signal LED
int SignalLEDpin = 4;
int SignalLEDval = 0;



// dcf variables to store decoded DCF time in
int dcfMinute  = 0;
int dcfHour    = 0;
int dcfDay     = 0;
int dcfWeekDay = 0;
int dcfMonth   = 0;
int dcfYear    = 0;
int dcfDST     = 0;
int leapYear   = 0;

// variables used to store weeknumber and daynumer values
int dayNumber;
int weekNumber;

// error counter variable
int errorCounter        = 0;
boolean errorCondition  = false;

// miscelleanous variables
boolean daytimeChange   = true;
boolean dayTime         = false;        
int dcf77SoundSwitch    = 0;


//==============================================================================
// SETUP
//==============================================================================
void setup()
{
  // initialize Serial communication
  //Serial.begin(115200);

  // initialize PIN connections
  pinMode(DCF77PIN,      INPUT);
  
  //pinMode(TEMPRESETPIN,  INPUT);
  pinMode(BUZZERPIN,     INPUT);
   pinMode(PIR,INPUT);           // Brett PIR detector for blanking displays
//  pinMode(PIRDETECTORPIN,INPUT);
//  pinMode(CHIMEPIN,      OUTPUT);
//  pinMode(SPEAKERVOLPIN, OUTPUT);
pinMode(remote1in, INPUT);  //Brett remote sw 
  pinMode(remote2in, INPUT);  //Brett remote sw
  pinMode(remote3in, INPUT);  //Brett remote sw
  pinMode(remote3out, OUTPUT);  //Brett remote sw outputs on pin 14 on ic when button3 pressed (Superfilter LEDs on
//  pinMode(remote4in, INPUT);  //Brett remote sw
  pinMode(RemoteLEDpin, INPUT); //Brett remote ctrl monitor led
  pinMode(SignalLEDpin, INPUT); //Brett DCF77 Signal monitor LED
 
  // Initialize variables and LED displays
  initialize();

  // Initialize DCF77 pulse interrupt on pin DCF_INTERRUPT, looking for a change of the signal,
  // so either rising or falling edge pulses will trigger the interrupt handler and
  // execute the int0handler function.
  attachInterrupt(DCF_INTERRUPT, int0handler, CHANGE);

  // Initialize RTC and set as SyncProvider.
  // Later RTC will be synced with DCF time
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  // check if RTC has set the system time
  if (timeStatus() != timeSet)
  { // Unable to sync with the RTC - activate RTCError LED
    MaximCC.setLed(MaximLeds, RTCError / 10, RTCError % 10, true);
  } 
  else {
    // RTC has set the system time - dim RTCError LED
    MaximCC.setLed(MaximLeds, RTCError / 10, RTCError % 10, false);
  }

  
  // use for test purposes and/or setting the RTC time manually
  // setTime(23, 59, 40, 31, 12, 13);
  // RTC.set(now());

 
  // check if a LED test is needed 
  
  if(PERFORM_LED_TEST == 1)
  {
    // do a LED test
    ledTest();
  }
  else
  {
    // if not doing a LED test, we need to wait a bit for the DS18B20 sensor to get ready
    delay(750);
  }
  


  // activate errorCounter display after LED test
  ledDisplay(MaximBufferBitError, "R", 0);//Brett was R

}


//==============================================================================
// LOOP
//==============================================================================
void loop()
{
  // check first if pulse direction is changed (rising or falling)
  // else we would keep evaluating the same pulse
  if (DCFSignalState != previousSignalState)
  {
    // 'reset' state of variable
    previousSignalState = DCFSignalState;

    // evaluate incoming pulse
    scanSignal();
  }

  // check if switches are changed and act upon it
  checkSwitches();



// Brett DCF77 signal monitor
        
        SignalLEDval = digitalRead(SignalLEDpin); // reads OP from remote sw output
        
        if (SignalLEDval == 0)
        {
          
          MaximCC.setLed(MaximLeds, SignalLED / 10, SignalLED % 10, false);
        }
        else
                 
          MaximCC.setLed(MaximLeds, SignalLED / 10, SignalLED % 10, true);
        /*  
          Serial.print("SignalLEDval ");
      Serial.println(SignalLEDval);
     */   
        // // Brett DCF77 signal monitor
 

     
        // Brett light remote LED on operation of any remote button
        
        Remotepin = digitalRead(RemoteLEDpin); // reads OP from remote sw output
        
        if (Remotepin == 0)
        {
          
          MaximCC.setLed(MaximLeds, RemoteLED / 10, RemoteLED % 10, false);
        }
        else
                 
          MaximCC.setLed(MaximLeds, RemoteLED / 10, RemoteLED % 10, true);
        /*  
          Serial.print("Remotepin ");
      Serial.println(Remotepin);
     */   
        // Brett light remote LED on operation of any remote button
 
 


  
  // execute tasks that must happen only once every second, minute or hour
  //----------------------------------------------------------------------------
  tasksEverySecond();
  tasksEveryMinute();
  tasksEveryHour();
}






//================================================================================================================
//
// Function name : processDcfBit
// called from   : <scanSignal>
//
// Purpose       : Evaluates the signal as it is received. Decides whether we received a "1" or a "0"
//                 and perform checks to see if the pulse timing is within limits
// Parameters    : none
// Return value  : none
//
//================================================================================================================
/*
       pulse                 pulse
       width                 width
       |- -|               |--   --|           |----- END OF MINUTE marker:2000ms -----|
        ___                 _______             ___                                     ___                 _______
       | 0 |               |   1   |           | 0 |                                   | 0 |               |   1   |
       |   |               |       |           |   |                                   |   |               |       |
       |   |               |       |           |   |                                   |   |               |       |
 ______|   |_______________|       |___________|   |___________________________________|   |_______________|       |__ _ _ _
       ^   ^               ^       ^           ^   ^               ^                   ^   ^               ^       ^
       1000 2100           2000    2200        3000 3100         NO PULSE              5000 5100           6000    6200         << example millis() value
                                                                 = end of Minute indication
       ^                   ^                   ^                                       ^                   ^
       DCFbit# 56          DCFbit# 57          DCFbit# 58                               DCFbit# 0           DCFbit# 1  etc...   << DCF bit received
       
       ^                   ^        ^
       previous            leading  trailing
       leading edge        edge     edge
       
       ^   ^
       flanktime (rising or falling)
 
 */

void scanSignal()
{
  //--------------------------------------------------------------------
  // Check for Rising-Edge signal and perform checks
  //--------------------------------------------------------------------
  if (DCFSignalState == 1) 
  {
    // store Rising-Edge Time to check later if the time between two pulses is valid
    leadingEdge = millis();
    // not much to do now so exit.
    return;
  }

  //--------------------------------------------------------------------
  // Check for Falling-Edge signal and perform checks
  //--------------------------------------------------------------------
  
  if (DCFSignalState == 0)
  {
    // store Trailing-Edge Time to check later if the Pulse Width is valid
    trailingEdge = millis();

    // display period width time on "L"eft side of the 8 digit Maxim 72xx LED display
    ledDisplay(MaximPeriodPulse, "L", (leadingEdge - previousLeadingEdge));
    // display pulse width time on the "R"ight side of the 8 digit Maxim 72xx LED display
    ledDisplay(MaximPeriodPulse, "R", (trailingEdge - leadingEdge));

    //--------------------------------------------------------------------------------
    // Check PERIOD TIME
    //--------------------------------------------------------------------------------
    // If this flank UP is detected quickly after previous flank UP this is an incorrect
    // Period Time (should be 1000ms -or 2000ms after second 58-) that we shall reject
    if ((leadingEdge - previousLeadingEdge) < 900)
    {
      // rPW - ERROR: Periode Time (rising flank to rising flank) time is too short -> REJECTED
      error(rPWLed);
      errorCondition = true;
    }
    //--------------------------------------------------------------------------------
    // CHECK PULSE TIME
    //--------------------------------------------------------------------------------
    // If the detected pulse is too short it will be an incorrect pulse that we shall reject
    // should be 100 and 200 ms ideally
    if (((trailingEdge - leadingEdge) < 70) || ((trailingEdge - leadingEdge) > 230))
    {
      //rPT - ERROR: Pulse Width too short or too long -> REJECTED
      error(rPTLed);
      errorCondition = true;
    }

    // if we had an error return and start over
    if (errorCondition == true)
    {
      errorCondition = false;
      // although we have an error, store current rising edge time to compare at the next Rising-Edge.
      previousLeadingEdge = leadingEdge;
      return;
    }


    //--------------------------------------------------------------------
    // no errors found so now we can continue
    //--------------------------------------------------------------------
   
    // first we turn any error Led's OFF
    MaximCC.setLed(MaximLeds, rPWLed / 10, rPWLed % 10, false);
    MaximCC.setLed(MaximLeds, rPTLed / 10, rPTLed % 10, false);
    MaximCC.setLed(MaximLeds, BFLed  / 10, BFLed  % 10, false);
    MaximCC.setLed(MaximLeds, EoBLed / 10, EoBLed % 10, false);
    MaximCC.setLed(MaximLeds, EoMLed / 10, EoMLed % 10, false);
    
    // END OF MINUTE check, looking for a gap of approx. 2000ms
    if ( leadingEdge - previousLeadingEdge > 1900 && leadingEdge - previousLeadingEdge < 2100)
    {
     // end of minute detected:
      finalizeBuffer();
    }

    // refresh previousLeadingEdge time with the new leading edge time
    previousLeadingEdge = leadingEdge;

    //--------------------------------------------------------------------------------
    // process DCF bits
    //--------------------------------------------------------------------------------
    // distinguish between long and short pulses
    if (trailingEdge - leadingEdge < 170)
    {
      // call processDcfBit function and sent it the value '0'
      processDcfBit(0);
      // if switch is HIGH, the DCF pulses are audible
      if (dcf77SoundSwitch == 1) buzzer(100);
    }
    else
    {
      // call processDcfBit function and sent it the value '1'
      processDcfBit(1);
      // if switch is HIGH, the DCF pulses are audible
      if (dcf77SoundSwitch == 1) buzzer(200);
    }
  } // if (DCFSignalState == 0)
} // void scanSignal();

//================================================================================================================
//
// Function name : processDcfBit
// called from   : <scanSignal>
//
// Purpose       : after reception of one good DCF bit, do some checks and save it in the DCFbitBuffer array
// Parameters    : none
// Return value  : none
//
//================================================================================================================

void processDcfBit(int dcfBit)
{
  //--------------------------------------------------------------------
  // display values on the 7 segment displays
  //--------------------------------------------------------------------
  // display bufferPosition, digits 7,6
  MaximCC.setChar(MaximBufferBitError, 0, bufferPosition / 10, false);
  MaximCC.setChar(MaximBufferBitError, 1, bufferPosition % 10, false);
  
  // display received DCFbit, digit 4
  MaximCC.setChar(MaximBufferBitError, 3, dcfBit, false);
  
  //--------------------------------------------------------------------
  // display incoming DCF bits on inner LED ring
  //--------------------------------------------------------------------
  // only if we have valid DCF data or after an Minute Mark (EoM) signal 
  // activate the inner LED ring and diplay incoming data
  if (dcfValidSignal == true || MinuteMarkerFlag == true) 
  {
    // display received bits on inner LED ring
    MaximCC.setLed(MaximLedRingInner, bufferPosition / 8, bufferPosition % 8, dcfBit);
  }

  //--------------------------------------------------------------------
  //   // Fill DCFbitBuffer array with DCFbit 
  //--------------------------------------------------------------------
  DCFbitBuffer[bufferPosition] = dcfBit;
 
  //--------------------------------------------------------------------
  // Parity check
  //--------------------------------------------------------------------
  // DURING reception of the DCF bits, calculate and display the results of the DCF parity check.
  //
  // There is a Parity bit for the minutes, the hours and for the date.
  // DCF77 works with EVEN parity, this works as follows:
  // The hours for example have 6 bits plus a paritybit. The bits with value 1 are add up including the paritybit,
  // the result must be an even number. If there is a bit wrong received, a 0 is as 1, or a 1 is as 0 received, 
  // then the result is uneven.  source: http://www.picbasic.nl/frameload_uk.htm?http://www.picbasic.nl/info_dcf77_uk.htm

  if (bufferPosition == 0)
  {
    // reset the parity LED's
    MaximCC.setLed(MaximLeds, LEDP1Pass / 10, LEDP1Pass % 10, false);
    MaximCC.setLed(MaximLeds, LEDP1Fail / 10, LEDP1Fail % 10, false);
    MaximCC.setLed(MaximLeds, LEDP2Pass / 10, LEDP2Pass % 10, false);
    MaximCC.setLed(MaximLeds, LEDP2Fail / 10, LEDP2Fail % 10, false);
    MaximCC.setLed(MaximLeds, LEDP3Pass / 10, LEDP3Pass % 10, false);
    MaximCC.setLed(MaximLeds, LEDP3Fail / 10, LEDP3Fail % 10, false);
    // reset variables
    dcfP1counter = 0;
    dcfP2counter = 0;
    dcfP3counter = 0;
    dcfParityCheckP1 = 0;
    dcfParityCheckP2 = 0;
    dcfParityCheckP3 = 0;
  }

  // ----------------------------------------
  // First parity check: minute bits
  // ----------------------------------------
  if (bufferPosition == 28)
  {
    for(int i = 21; i <= 27; i++)
    {
      // count the number of bits with the value '1'
      dcfP1counter += DCFbitBuffer[i];
    }

    // perform P1 parity check. Parity is OK if the sum is an EVEN value
    if((DCFbitBuffer[28] + dcfP1counter) % 2 == 0)
    {
      // Parity1 PASS LED ON
      MaximCC.setLed(MaximLeds, LEDP1Pass / 10, LEDP1Pass % 10, true);
      // Parity P1 PASS
      dcfParityCheckP1 = 1;
    }
    else 
    {
      // Parity1 FAIL LED ON
      MaximCC.setLed(MaximLeds, LEDP1Fail / 10, LEDP1Fail % 10, true);
      // we have no valid data!
      dcfValidSignal = false;
      // Turn DCF OK LED OFF
      MaximCC.setLed(MaximLeds, DCFOKLed / 10, DCFOKLed % 10, false);
    }
  }

  // ----------------------------------------
  // Second parity check: hour bits
  // ----------------------------------------
  if (bufferPosition == 35)
  {
    for(int i = 29; i <= 34; i++)
    {
      dcfP2counter += DCFbitBuffer[i];
    }

    // perform P2 parity check. Parity is OK if the sum is an EVEN value
    if((DCFbitBuffer[35] + dcfP2counter) % 2 == 0)
    {
      // Parity2 PASS LED ON
      MaximCC.setLed(MaximLeds, LEDP2Pass / 10, LEDP2Pass % 10, true);
      // Parity P2 PASS
      dcfParityCheckP2 = 1;
     }
    else 
    {
      // Parity2 FAIL LED ON
      MaximCC.setLed(MaximLeds, LEDP2Fail / 10, LEDP2Fail % 10, true);
      // we have no valid data!
      dcfValidSignal = false;
      // Turn DCF OK LED OFF
      MaximCC.setLed(MaximLeds, DCFOKLed / 10, DCFOKLed % 10, false);
    }
  }

  // ----------------------------------------
  // Third parity check: date bits
  // ----------------------------------------
  if (bufferPosition == 58)
  {
    for(int i = 36; i <= 57; i++)
    {
      dcfP3counter += DCFbitBuffer[i];
    }
    // perform P3 parity check. Parity is OK if the sum is an EVEN value
    (DCFbitBuffer[58] + dcfP3counter) % 2 == 0 ? dcfParityCheckP3 = 1 : dcfParityCheckP3 = 0;

      // Turn Parity2 'PASS' or 'FAIL' LED ON
    if(dcfParityCheckP3 == 1)
    {
      // Parity2 PASS LED ON
      MaximCC.setLed(MaximLeds, LEDP3Pass / 10, LEDP3Pass % 10, true);
      // Parity P3 PASS
      dcfParityCheckP3 = 1;
    }
    else 
    {
      // Parity2 FAIL LED ON
      MaximCC.setLed(MaximLeds, LEDP3Fail / 10, LEDP3Fail % 10, true);
      // we have no valid data!
      dcfValidSignal = false;
      // Turn DCF OK LED OFF
      MaximCC.setLed(MaximLeds, DCFOKLed / 10, DCFOKLed % 10, false);
    }
  
    // ----------------------------------------
    // finally, check all Parity bits
    // ----------------------------------------
    dcfParityCheckP1 + dcfParityCheckP2 + dcfParityCheckP3 == 3 ? dcfValidSignal = true : dcfValidSignal = false;
  }

  //--------------------------------------------------------------------
  // before continuing with the next bit, increment counter
  //--------------------------------------------------------------------
  bufferPosition++;


  //--------------------------------------------------------------------
  // check if we have not received too many pulses?
  //--------------------------------------------------------------------
  if (bufferPosition > 59)
  {
    // End of Buffer (EoB) ERROR - we have received more pulses before reaching
    // the 2 second 'gap' signalling the end of the minute. 
    //This error may be due to a noisy signal giving addition peaks/dcfBits
    // So clear both DCFbit displays and start again.

    // Reset buffer counter
    bufferPosition = 0;
    // clear inner LED ring
    MaximCC.clearDisplay(MaximLedRingInner);
    // turn EoB Error LED ON
    error(EoBLed);
    // exit
    return;
  }

  //--------------------------------------------------------------------
  // everything OK so we wait for next incoming DCFbit
  //--------------------------------------------------------------------
}

//================================================================================================================
//
// Function name : finalizeBuffer
// called from   : <scanSignal>
//
// Purpose       : Process the succesfully received DCF data of one minute
// Parameters    : none
// Return value  : none
//
//================================================================================================================

void finalizeBuffer(void) 
{
  //--------------------------------------------------------------------
  // We are here because of the detected 2 second 'gap'.
  // Now check if it correspondends with the buffer counter
  // 'bufferPosition' which should be value 59
  //--------------------------------------------------------------------
  if (bufferPosition == 59 && dcfValidSignal == true)
  {
    // bufferPosition == 59 so turn Buffer Full LED ON
    MaximCC.setLed(MaximLeds, BFLed / 10, BFLed % 10, true);

    // Turn DCF OK LED ON
    MaximCC.setLed(MaximLeds, DCFOKLed / 10, DCFOKLed % 10, true);

    // Reset inner LED ring (incoming time information)
    MaximCC.clearDisplay(MaximLedRingInner);

    // copy 'contents' of inner LED ring to the outer LED ring (current time information)
    for (int i = 0; i < 59; i++) 
    {
      MaximCC.setLed(MaximLedRingOuter, i / 8, i % 8, DCFbitBuffer[i]);
    }

    // process buffer and extract data sync the time with the RTC
    decodeBufferContents();
  
    // set Arduino time and after that set RTC time
    setTime(dcfHour, dcfMinute, 0, dcfDay, dcfMonth, dcfYear);
    RTC.set(now());
  
    // activate Synced LED
    MaximCC.setLed(MaximLeds, SyncedLed / 10, SyncedLed % 10, true);

    // Reset running buffer
    bufferPosition   = 0;

    // Reset DCFbitBuffer array, positions 0-58 (=59 bits)
    for (int i = 0; i < 59; i++) {
      DCFbitBuffer[i] = 0;
    }

    // reset flag
    MinuteMarkerFlag = false;
    
  } // if (bufferPosition == 59)


  //--------------------------------------------------------------------
  // The buffer is not yet filled although the 2 second 'gap' was detected.
  // Can be result of a noisy signal, starting in middle of receiving data etc.
  // Turn End of Message LED ON
  //--------------------------------------------------------------------
  else
  {
    MaximCC.setLed(MaximLeds, EoMLed / 10, EoMLed % 10, true);

    // Clear displays
    MaximCC.clearDisplay(MaximLedRingInner);
    MaximCC.clearDisplay(MaximLedRingOuter);

    // Reset running buffer and start afresh. Now we are in sync with the incoming data
    bufferPosition   = 0;

    // Reset DCFbitBuffer array, positions 0-58 (=59 bits)
    for (int i = 0; i < 59; i++) 
    {
      DCFbitBuffer[i] = 0;
    }

    // set flag so we can display incoming pulsed on the inner LED ring.
    MinuteMarkerFlag = true;
  }
}

//================================================================================================================
//
// Function name : decodeBufferContents
// called from   : <finalizeBuffer>
//
// Purpose       : Evaluates the information stored in the buffer. 
//                 This is where the DCF77 signal is decoded to time and date information
// Parameters    : none
// Return value  : none
//
//================================================================================================================
 
void decodeBufferContents(void) 
{
  // Buffer is full and ready to be decoded
  dcfMinute  = bitDecode(21, 27);
  dcfHour    = bitDecode(29, 34);
  dcfDay     = bitDecode(36, 41);
  dcfWeekDay = bitDecode(42, 44);
  dcfMonth   = bitDecode(45, 49);
  dcfYear    = bitDecode(50, 57);

  //call function to calculate day of year and weeknumber
  dayWeekNumber(dcfYear, dcfMonth, dcfDay, dcfWeekDay);

  // Get value of Summertime DCFbit. '1' = Summertime, '0' = wintertime
  dcfDST     = bitDecode(17, 17);

  // determine Leap Year
  leapYear   = calculateLeapYear(dcfYear);
}

//================================================================================================================
//
// bitDecode
//
// called from <processBuffer>
//================================================================================================================
int bitDecode (int bitStart, int bitEnd) 
{
  // reset 'bitValue-array' counter
  int i = 0;
  int value = 0;

  // process bitrange bitStart > bitEnd
  while (bitStart <= bitEnd)
  {
    // check if DCFbit in buffer is '1', discard when '0'
    if (DCFbitBuffer[bitStart] == 1) {
      // DCFbit in buffer == 1 so append its corresponding value to the variable 'value'
      value = value + bitValue[i];
    }
    // increment 'bitValue-array' counter
    i++;
    // increment bit-range counter
    bitStart++;
  }
  return value;
}

//================================================================================================================
//
// Function name : tasksEverySecond
// called from   : <loop>
//
// Purpose       : perform tasks that must happen once every SECOND
// Parameters    : none
// Return value  : none
//
//================================================================================================================
void tasksEverySecond()
{
  // check if time is changed
  if (second() != previousSecond)
  {
    // 'reset' variable state
    previousSecond = second();

    // Start of remote control switch reading Brett
    // Switch 1
    {
      remote1val = digitalRead(remote1in); // reads OP from remote sw output

      // if the input just went from LOW and HIGH toggle the output pin

        if ( remote1val == 1 && remote1prev == 0 ) {
        if (dcf77SoundSwitch == 1){
          dcf77SoundSwitch = 0;
          MaximCC.setLed(MaximLeds, SpkLED / 10, SpkLED % 10, false);
        }
        else
        {
          dcf77SoundSwitch = 1;
          MaximCC.setLed(MaximLeds, SpkLED / 10, SpkLED % 10, true);

        }
      }
      /*
      Serial.print("remote1val ");
      Serial.println(remote1val);

      Serial.print("remote1prev ");
      Serial.println(remote1prev);

      Serial.print("dcf77SoundSwitch ");
      Serial.println(dcf77SoundSwitch);

      */

      remote1prev = remote1val;
    }
    
    
   /* 
    // Switch 2
    {
      remote2val = digitalRead(remote2in); // reads OP from remote sw output changes DCF77 source to super filter

      // if the input just went from LOW and HIGH toggle the output pin

        if ( remote2val == 1 && remote2prev == 0 ) {
        if (filterSw == DCF77PIN){
          filterSw = DCF77aPIN;
          lc.setLed(MaximLeds, FilterLED / 10, FilterLED % 10, true);//DCF77 Filter on
        }
        else
        {
          filterSw = DCF77PIN;
          lc.setLed(MaximLeds, FilterLED / 10, FilterLED % 10, false);//DCF77 Filter off

        }
      }
      
      Serial.print("remote2val ");
      Serial.println(remote2val);

      Serial.print("remote2prev ");
      Serial.println(remote2prev);

      Serial.print("filterSw ");
      Serial.println(filterSw);
      
     

      //digitalWrite(remote1out, datesw);

      remote2prev = remote2val;
    }
    
   */ 

// Start of manual DCF77 signal switch to turn Filter LED on
  // Manual Switch 1
    {
      filterSw = digitalRead(DCF77Switch); // reads the manual switch

      

        if ( filterSw == 1 ) { // if switch set to superfilter
          
          MaximCC.setLed(MaximLeds, FilterLED / 10, FilterLED % 10, true);//DCF77 FilterLED on
        }
        else
        {
          
          MaximCC.setLed(MaximLeds, FilterLED / 10, FilterLED % 10, false);//DCF77 FilterLED off as un-filtered signal is being received

        }
      
   
     
    }



   // Switch 3 Super filter LEDs on
    {
      remote3val = digitalRead(remote3in); // reads OP from remote sw output turns Super Filter LEds on/off

      // if the input just went from LOW and HIGH toggle the output pin

        if ( remote3val == 1 && remote3prev == 0 ) {
        if (LEDctrl == 1){
          LEDctrl = 0;
          MaximCC.setLed(MaximLeds, LEDs / 10, LEDs % 10, false);//DCF77 Filter on
        }
        else
        {
          LEDctrl = 1;
          MaximCC.setLed(MaximLeds, LEDs / 10, LEDs % 10, true);//Super Filter LEDs off

        }
      }
 /*     
      Serial.print("remote3val ");
      Serial.println(remote3val);

      Serial.print("remote3prev ");
      Serial.println(remote3prev);

      Serial.print("LEDctrl ");
      Serial.println(LEDctrl);
*/
      digitalWrite(remote3out, LEDctrl);

      remote3prev = remote3val;
    }


    //display the Real Time Clock Time
    displayRtcTime();
    motiondetection(); //Brett check motion detection every second and blank/show displays
      // Brett check LDR value once per seconds and display value
    intensityValue();
    ldrValue = analogRead(ldr); // read value of LDR
    
    
   //Serial.print ("LDR Value ");
 // Serial.println (ldrValue, DEC);
  //Serial.print (" ");
  //  intensityValue(); // converts LDR value into intensity value 1 to 16 and also value foe MaximLED display
  //  Serial.print ("Intensity "); 
  //  Serial.print (intensity);
  //  Serial.println (" ");
  //  Serial.print ("IntensityDisp "); 
  //  Serial.print (intensityDisp);
  //  Serial.println (" ");
    
    
    
    // End Brett check LDR value once per seconds and display value
 
  // Brett blank Rows 1,4 and 6 on MaximLED display
  MaximCC.setRow(2,1,B0000000);  // blank maximled row 1
  MaximCC.setRow(2,4,B0000000);  // blank maximled row 4
 // MaximCC.setRow(2,6,B0000000);  // blank maximled row 6
 

    // display 'HI' and 'LO' temperature on specific moments
    switch (second())
    {
    
    case 2:
      // hourly chime output: DEACTIVATE
 //     digitalWrite(CHIMEPIN, LOW);
      break;
  
    }// switch
  }// (second() != previousSecond)
}// void tasksEverySecond()


//================================================================================================================
//
// Function name : tasksEveryMinute
// called from   : <loop>
//
// Purpose       : perform tasks that must happen once every MINUTE
// Parameters    : none
// Return value  : none
//
//================================================================================================================
void tasksEveryMinute()
{  
  // display date, week LED's, week nr etc. if time is changed
  if (minute() != previousMinute)
  {
    // 'reset' state of variable
    previousMinute = minute();

    // display date, week LED's, week nr etc.
    if (dcfValidSignal == true)
    {
      displayData(); 
    }

    // increase PIR delay counter
  //  pirTimer++;

  }
}

//================================================================================================================
//
// Function name : tasksEveryHour
// called from   : <loop>
//
// Purpose       : perform tasks that must happen once every HOUR
// Parameters    : none
// Return value  : none
//
//================================================================================================================
void tasksEveryHour()
{ 
  if (hour() != previousHour)
  {
    // 'reset' variable state
    previousHour = hour();

    //--------------------------------------------------------------------
    // reset error counter and display every hour
    //--------------------------------------------------------------------
    errorCounter = 0;
    // update error counter display
    ledDisplay(MaximBufferBitError, "R", errorCounter);//Brett was R

    //---------------------------------------------------------------------
    // Power saving function, shutting the displays off at night
    //---------------------------------------------------------------------

    // First, check if the night shut-down function is activated by the user
    // simply by adding up both value. If '0', the user has disabled the function
    if(POWERSAVINGOFFTIME != 0 && POWERSAVINGONTIME != 0)
    { 
      // check whether it is Day- or Nighttime
      if (hour() >= POWERSAVINGOFFTIME && hour() <= POWERSAVINGONTIME)
      {
        // ----------------------------------------
        // it's DAYTIME so activate the displays
        // ----------------------------------------
        
        // this is used to chime only if it is daytime...
        dayTime = 1; 

         // test variable because daytime routine is needed only once
        if (daytimeChange == 1)
        {
          // 'reset' variable state
          daytimeChange = 0;

          // activate SELECTED displays and status LED's
          turnDisplaysOn();
        }
      }
      else
      {
        // no chime at night...
        dayTime = 0; 

        // ----------------------------------------
        // it's NIGHTTIME so time to deactivate displays
        // ----------------------------------------
        // test variable because nighttime routine is needed only once
        if (daytimeChange == 0) 
        {
          // 'reset' variable state
          daytimeChange = 1; 

          // deactivate all the displays and status LED's
          turnDisplaysOff();

        }// if (daytimeChange == 0)
      }// else
    }// if(POWERSAVINGOFFTIME + POWERSAVINGONTIME != 0)
  }// if (dcfHour != previousHour)
}// void tasksEveryHour()


//================================================================================================================
//
// Function name : buzzer
// called from   : <scanSignal>
//
// Purpose       : generate 'beep' sound
// Parameters    : duration in ms
// Return value  : none
//
//================================================================================================================

void buzzer(int duration)
{
  tone(BUZZER, 1500, duration);
}

//================================================================================================================
//
// Function name : initialize
// called from   : <Setup>
//
// Purpose       : initialize variables and displays after power-up
// Parameters    : none
// Return value  : none
//
//================================================================================================================

void initialize(void)
{
  //---------------------------------------------------
  // Initialize Variables
  //---------------------------------------------------
  leadingEdge           = 0;
  trailingEdge          = 0;
  previousLeadingEdge   = 0;
  bufferPosition        = 0;

//  digitalWrite(CHIMEPIN, HIGH); // Set Chimepin to default state

  // Reset DCFbitBuffer array, positions 0-58 (=59 bits)
  for (int i = 0; i < 59; i++) {
    DCFbitBuffer[i] = 0;
  }


  //---------------------------------------------------
  // Initialize Maxim 72xx 7 segment displays
  //---------------------------------------------------
  // Maxim Common Cathode displays
  for (int i = 0; i < 7; i++)
  {
    // display wake up
    MaximCC.shutdown(i, false);
    // clear display
    MaximCC.clearDisplay(i);
  }
 
  //---------------------------------------------------
  // Set brightness of Maxim 72xx 7 segment displays
  //---------------------------------------------------
  // Maxim Common Cathode displays
  MaximCC.setIntensity(MaximLedRingOuter,   BrightnessMaximLedRingOuter);
  MaximCC.setIntensity(MaximLedRingInner,   BrightnessMaximLedRingInner);
  MaximCC.setIntensity(MaximPeriodPulse,    BrightnessMaximPeriodPulse);
 // MaximCC.setIntensity(MaximWeek,           BrightnessMaximWeek);
  MaximCC.setIntensity(MaximDate,           BrightnessMaximDate);
  MaximCC.setIntensity(MaximRtcTime,        BrightnessMaximRtcTime);
  MaximCC.setIntensity(MaximLeds,           BrightnessMaximLeds);
  MaximCC.setIntensity(MaximBufferBitError, BrightnessMaximBufferBitError);
 
}

//================================================================================================================
//
// Function name : int0handler
// called from   : 
//
// Purpose       : when a rising or falling edge is detected on pin 2, this function is called
// Parameters    : none
// Return value  : none
//
//================================================================================================================

void int0handler()
{
  DCFSignalState = digitalRead(DCF77PIN);
}

//================================================================================================================
//
// Function name : turnDisplaysOn
// called from   : <tasksEveryHour> and <checkPIR>
//
// Purpose       : turn ON selected 7 segment displays and LED's
// Parameters    : none
// Return value  : none
//
//================================================================================================================
void turnDisplaysOn()
{
  // activate SELECTED displays and status LED's
  MaximCC.shutdown(MaximRtcTime, false);
  MaximCC.shutdown(MaximDate, false);
  MaximCC.shutdown(MaximWeek, false);
  MaximCC.shutdown(MaximLeds, false);
  MaximCC.shutdown(MaximLedRingInner, false);
  MaximCC.shutdown(MaximLedRingOuter, false);
  MaximCC.shutdown(MaximPeriodPulse, false);
  MaximCC.shutdown(MaximBufferBitError, false);
  
}

//================================================================================================================
//
// Function name : turnDisplaysOff
// called from   : <tasksEveryHour> and <checkPIR>
//
// Purpose       : turn OFF selected 7 segment displays and LED's
// Parameters    : none
// Return value  : none
//
//================================================================================================================
void turnDisplaysOff()
{
  // below you can select which display's need to be shut down for the night
  // In this case, the time display remains ON

  //MaximCC.shutdown(MaximRtcTime, true);
  MaximCC.shutdown(MaximDate, true); 
  MaximCC.shutdown(MaximWeek, true);
  MaximCC.shutdown(MaximLeds, true);
  MaximCC.shutdown(MaximLedRingInner, true);
  MaximCC.shutdown(MaximLedRingOuter, true);
  MaximCC.shutdown(MaximPeriodPulse, true);
  MaximCC.shutdown(MaximBufferBitError, true);
 
}

//================================================================================================================
//
// Function name : ledDisplay
// called from   : <processBuffer>
//
// Purpose       : display a value on a selected 7 segment display
// Parameters    : display 'number 'addr', 'value', "L"eft or "R"ight side.none
// Return value  : none
//
//================================================================================================================

// example: ledDisplay( MaximBufferBitError, "R", errorCounter )
// so display the 'error counter' value on the RIGHT side of the 8 digit
// 7 segment display number 3

void ledDisplay(int addr, String leftOrRight, int value)
{
  int ones;
  int tens;
  int hundreds;
  int thousands;
  int shift;

  //break down value in seperate digits for ones, tens, etc
  int v = value; // 'value' is needed later so copy it in 'v'
  ones = v % 10;
  v = v / 10;
  tens = v % 10;
  v = v / 10;
  hundreds = v % 10;
  thousands = v / 10;

  //Select which side of the 8 digit display to be used
  //(leftOrRight == "L" ? shift = 4 : shift = 0);
  (leftOrRight == "R" ? shift = 4 : shift = 0); //Brett was "L" shift = 4: shift = 0

  //Now print the number digit by digit
  //preceding zero's are removed with tenary operator by 'printing' a space character.
  MaximCC.setChar(addr, 0 + shift, ((value < 1000) ? ' ' : thousands), false);
  MaximCC.setChar(addr, 1 + shift, ((value < 100) ? ' ' : hundreds), false);
  MaximCC.setChar(addr, 2 + shift, ((value < 10) ? ' ' : tens), false);
  MaximCC.setChar(addr, 3 + shift, ones, false);
}

//================================================================================================================
//
// Function name : checkSwitches
// called from   : <loop>
//
// Purpose       : check if the temperature reset button button is pressed
// Parameters    : none
// Return value  : none
//
//================================================================================================================
void checkSwitches(void)
{

 
}

//================================================================================================================
//
// Function name : checkPIR
// called from   : <loop>
//
// Purpose       : check for PIR detector activity to shut off or activate the displays to save power 
// Parameters    : none
// Return value  : none
//
//================================================================================================================

//================================================================================================================
//
// Function name : error
// called from   : <scanSignal>
//
// Purpose       : turn error LED ON, clear LED ring's and increase error counter display
// Parameters    : error LED to turn on 
// Return value  : none
//
//================================================================================================================

void error(int errorLed)
{
  // no valid data
  dcfValidSignal = false;

  // turn 'dcfValidSignal = false on'
  MaximCC.setLed(MaximLeds, errorLed / 10, errorLed % 10, true);

  // clear Led's/displays because of error condition
  MaximCC.setLed(MaximLeds, DCFOKLed / 10, DCFOKLed % 10, false);
  MaximCC.setLed(MaximLeds, SyncedLed / 10, SyncedLed % 10, false);
  MaximCC.clearDisplay(MaximLedRingOuter); // clear display

  // increase errorCounter and display errorCount
  errorCounter++;
  ledDisplay(MaximBufferBitError, "R", errorCounter);//Brett was R
  return;
}

//================================================================================================================
//
// Function name : dayWeekNumber
// called from   : <decodeBufferContents>
//
// Purpose       : calculate the WEEK number according to ISO standard, see comments in the ARCHIVE below
// Parameters    : dcfYear, dcfMonth, dcfDay, dcfWeekDay 
// Return value  : weekNumber
//
//================================================================================================================
//Code from: http://forum.arduino.cc/index.php/topic,44476.0.html

int dayWeekNumber(int y, int m, int d, int w)
{
  // Number of days at the beginning of the month in a normal (not leap) year.
  int days[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; 
  
  // Start to calculate the number of days of the first two months
  if (m == 1 || m == 2) 
  {
    // for any type of year we calculate the number of days for January or february
    dayNumber = days[(m - 1)] + d;
  }

  // now calculate for the other months
  // first, check for a leap year
  else if ((y % 4 == 0 && y % 100 != 0) ||  y % 400 == 0) 
  { 
    // we have a leap year, so calculate in the same way but adding one day
    dayNumber = days[(m - 1)] + d + 1;
  }
 
  else
  {
    //no leap year, calculate in the normal way, such as January or February
    dayNumber = days[(m - 1)] + d;
  }
  
  // Now start to calculate Week number
  if (w == 0) 
  {
    //if it is sunday (time library returns 0)
    weekNumber = (dayNumber - 7 + 10) / 7;
  }
  
  else
  {
    // for the other days of week
    weekNumber = (dayNumber - w + 10) / 7;
  }

  // finished! return with the week number as an INT value
  return weekNumber;
}


//================================================================================================================
//
// Function name : calculateLeapYear
// called from   : <decodeBufferContents>
//
// Purpose       : determine if a given year is a leap year
// Parameters    : year - the year to test
// Return value  : '1' if the year is a leap year, '0' otherwise
//
//================================================================================================================

int calculateLeapYear(int year)
{
  if ( (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) ) 
  {
    return 1;
  } 
  else 
  {
    return 0;
  }
}

//================================================================================================================
//
// Function name : displayData
// called from   : <tasksEveryMinute>
//
// Purpose       : display LED's: day of week; status; Winter/Summer time;
// Parameters    : none
// Return value  : none
//
//================================================================================================================

void displayData(void)
{
  // display Day of Week on LED's
  // first, clear all the 'Day' Led's (row '0') before displaying new value
  for (int i = 0; i < 7; i++) 
  {
    MaximCC.setLed(MaximLeds, 0, i, false);
  }

  // Maxim 7219 LED's are numbered from zero so sunday '7' must be changed to '0'
  MaximCC.setLed(MaximLeds, 0, ((dcfWeekDay == 7) ? 0 : dcfWeekDay), true);

  // display Weeknumber
  MaximCC.setChar(MaximWeek, 1, ((weekNumber < 10) ? ' ' : (weekNumber / 10)), false);
  MaximCC.setChar(MaximWeek, 0, weekNumber % 10, false);

  // display Date - with dashes between D-M-Y
  // for example: 03-08-2015, so with leading zero's
  MaximCC.setChar(MaximDate, 0, dcfDay / 10, false);
  MaximCC.setChar(MaximDate, 1, dcfDay % 10, false);
  MaximCC.setChar(MaximDate, 2, '-', false);
  MaximCC.setChar(MaximDate, 3, dcfMonth / 10, false);
  MaximCC.setChar(MaximDate, 4, dcfMonth % 10, false);
  MaximCC.setChar(MaximDate, 5  , '-', false);
  MaximCC.setChar(MaximDate, 6, dcfYear / 10, false);
  MaximCC.setChar(MaximDate, 7, dcfYear % 10, false);

/* OTHER OPTIONS TO DISPLAY THE DATE:

  // display Date - with dots between D.M.Y
  // for example: 03.08.15
   MaximCC.setChar(MaximDate, 7, dcfDay / 10, false);
   MaximCC.setChar(MaximDate, 6, dcfDay % 10, true);
   MaximCC.setChar(MaximDate, 5, dcfMonth / 10, false);
   MaximCC.setChar(MaximDate, 4, dcfMonth % 10, true);
   MaximCC.setChar(MaximDate, 3, 2, false);
   MaximCC.setChar(MaximDate, 2, 0, false);
   MaximCC.setChar(MaximDate, 1, dcfYear / 10, false);
   MaximCC.setChar(MaximDate, 0, dcfYear % 10, false);
   */

  /*
  // display Date - moved day to right if month is <10 
  // for example: __3.8.15 or _22.7.15 or 24.12.15 (where _ is a blank display)
   MaximCC.setChar(MaximDate, 7, ((dcfDay < 10) ? ' ' : ((dcfMonth < 10) ? ' ' : (dcfDay / 10))), false);
   MaximCC.setChar(MaximDate, 6, ((dcfMonth < 10) ? ((dcfDay < 10) ? ' ' : (dcfDay / 10)) : (dcfDay % 10)), ((dcfMonth < 10) ? false : true));
   MaximCC.setChar(MaximDate, 5, ((dcfMonth < 10) ? (dcfDay % 10) : (dcfMonth / 10)), ((dcfMonth < 10) ? true : false));
   MaximCC.setChar(MaximDate, 4, dcfMonth % 10, true);
   MaximCC.setChar(MaximDate, 3, 2, false);
   MaximCC.setChar(MaximDate, 2, 0, false);
   MaximCC.setChar(MaximDate, 1, dcfYear / 10, false);
   MaximCC.setChar(MaximDate, 0, dcfYear % 10, false);
   */

  // display Summer- or Wintertime LED
  if (dcfDST == 1) 
  {
    MaximCC.setLed(MaximLeds, SummerTimeLed / 10, SummerTimeLed % 10, true);
    MaximCC.setLed(MaximLeds, WinterTimeLed / 10, WinterTimeLed % 10, false);
  } 
  else {
    MaximCC.setLed(MaximLeds, WinterTimeLed / 10, WinterTimeLed % 10, true);
    MaximCC.setLed(MaximLeds, SummerTimeLed / 10, SummerTimeLed % 10, false);
  }

  // display Leap Year LED
  if (leapYear = 1)  
  {
    MaximCC.setLed(MaximLeds, LeapYearLed / 10, LeapYearLed % 10, true);
  } 
  else
  {
    MaximCC.setLed(MaximLeds, LeapYearLed / 10, LeapYearLed % 10, false);
  } 
}

//================================================================================================================
//
// Function name : displayRtcTime
// called from   : <tasksEverySecond>
//
// Purpose       : display the Real Time Clock time on the RTC display
// Parameters    : none
// Return value  : none
//
//================================================================================================================

void displayRtcTime() 
{
   MaximCC.setChar(MaximRtcTime, 2, 'o',  false); // Black tape mask over "o" displays colon
  MaximCC.setChar(MaximRtcTime, 3, (minute() / 10), false);
  MaximCC.setChar(MaximRtcTime, 4, (minute() % 10), false);
  MaximCC.setChar(MaximRtcTime, 5, 'o', false); // Black tape mask over "o" displays colon
  MaximCC.setChar(MaximRtcTime, 6, (second() / 10), false);
  MaximCC.setChar(MaximRtcTime, 7, (second() % 10), false);

  
  // Brett GMT conversion for RTC
  GMThour = hour() -1 ;
 // Serial.print ("GMT hours ");
 // Serial.println (GMThour);
 // Serial.print ("CET hours ");
 // Serial.println (hour());
 // Serial.println (" ");
  if (GMThour == -1)
  {
    GMThour = 23;
  }
  else
 { 
  GMThour = GMThour;
 }
  MaximCC.setChar(MaximRtcTime, 0, (GMThour / 10), false);
  //lc.setChar(MaximRtcTime, 0, ((GMThour < 10) ? ' ' : (GMThour / 10)), false); // removes leading zero when <10
  MaximCC.setChar(MaximRtcTime, 1, (GMThour % 10), false);

 
}

//================================================================================================================
//
// Function name : calculateTemp
// called from   : <loop>
//
// Purpose       : get temperature from DS18B20 sensor and display it
// Parameters    : none
// Return value  : none
//
//================================================================================================================


//================================================================================================================
//
// Function name : ledTest
// called from   : <setup>
//
// Purpose       : after a cold start, do a led test. 
// Parameters    : none
// Return value  : none
//
//================================================================================================================
void ledTest()
{
  // The displays are lit up sequentially because of the current draw. 
  // When all is lit up at the same time, you would need a bigger power supply. 
  
   //---------------------------------------------------------------------
   // Inner LED ring
  for (int i = 0; i < 59; i++) 
  {
    // LED's ON
    MaximCC.setLed(MaximLedRingInner, i / 8, i % 8, true);
    delay(LEDTEST_DELAY_LED_RING);
  }
  
  for (int i = 58; i >= 0; i--) 
  {
    // LED's OFF
    MaximCC.setLed(MaximLedRingInner, i / 8, i % 8, false);
    delay(LEDTEST_DELAY_LED_RING);
  }
  //---------------------------------------------------------------------
  
  //---------------------------------------------------------------------
  // Outer LED ring
  for (int i = 0; i < 59; i++) 
  {
    // LED's ON
    MaximCC.setLed(MaximLedRingOuter, i / 8, i % 8, true);
    delay(LEDTEST_DELAY_LED_RING);
  }
  
  for (int i = 58; i >= 0; i--) 
  {
    // LED's OFF
    MaximCC.setLed(MaximLedRingOuter, i / 8, i % 8, false);
    delay(LEDTEST_DELAY_LED_RING);
  }
 
//---------------------------------------------------------------------
  // LED's
  for (int i = 0; i < 64; i++) 
  {
    // LED's ON
    MaximCC.setLed(MaximLeds, i / 8, i % 8, true);
    delay(LEDTEST_DELAY_LED_RING);
  }

  for (int i = 63; i >= 0; i--) 
  {
    // LED's OFF
    MaximCC.setLed(MaximLeds, i / 8, i % 8, false);
    delay(LEDTEST_DELAY_LED_RING);
  }

  //---------------------------------------------------------------------
  // Real Time Clock display
  for (int i = 0; i < 8; i++) 
  {
    // LED's ON
    MaximCC.setChar(MaximRtcTime, i, 8, true);
  }
  // wait before turning the LED's off
  delay(LEDTEST_DELAY_DISPLAYS);
  // clear display
  MaximCC.clearDisplay(MaximRtcTime);
  //---------------------------------------------------------------------
  // Date display
  for (int i = 0; i < 8; i++) 
  {
    // LED's ON
    MaximCC.setChar(MaximDate, i, 8, true);
  }
  // wait before turning the LED's off
  delay(LEDTEST_DELAY_DISPLAYS);
  // clear display
  MaximCC.clearDisplay(MaximDate);
  //---------------------------------------------------------------------
 
//---------------------------------------------------------------------
  // Buffer-DCFbit-Errors display
  for (int i = 0; i < 8; i++) 
  {
    // LED's ON
    MaximCC.setChar(MaximBufferBitError, i, 8, true);
  }
  // wait before turning the LED's off
  delay(LEDTEST_DELAY_DISPLAYS);
  // clear display
  MaximCC.clearDisplay(MaximBufferBitError);

    //---------------------------------------------------------------------
  // Period-Pulse display
  for (int i = 0; i < 8; i++) 
  {
    // LED's ON
    MaximCC.setChar(MaximPeriodPulse, i, 8, true);
  }
  // wait before turning the LED's off
  delay(LEDTEST_DELAY_DISPLAYS);
  // clear display
  MaximCC.clearDisplay(MaximPeriodPulse);
  //---------------------------------------------------------------------
}



//================================================================================================================
//
// Brett check motion detection every second and blank/show displays
//
//================================================================================================================ 

void motiondetection() {
  
 int PIR = digitalRead(13); // reads infrared detector output
   //Serial.print ("PIR value ");
   //Serial.println (PIRval);
 //  Serial.print ("PIR ");
  // Serial.println (PIR);
  
    // check if motion detected
    if (PIR == 1 )
    
      {
        // activate all the displays and status LED's
        for (int i = 0; i < 8; i++)
        {
          MaximCC.shutdown(i, false);  // Common Cathode displays wakeup
        } 

      }
    else if  (PIR == 0 )

    {
      // no motion detected  deactivate displays
      
        // deactivate all the displays and status LED's
        for (int i = 0; i < 8; i++)
        {
          MaximCC.shutdown(i, true);  // Common Cathode displays wakeup
        } 
        

    } 

}


//================================================================================================================
//================================================================================================================
//
// intensity of 7 segment display Brett
//
//================================================================================================================ 


void intensityValue() {  
   
  
  if (ldrValue < 100)
  {
    intensity = 0;
    intensity2 = 0;
    intensityDisp = 20; // Maxim matrix intenisity 2,0 (3rd row bottom LED lit when intensity = 0 
  }
  else if (ldrValue >= 100 && ldrValue < 150)
  {
    intensity = 1;
    intensity2 = 0;
    intensityDisp = 21; // Maxim matrix intenisity 2,1 (3rd row 2nd LED lit when intensity = 1
  }


  else if (ldrValue >= 150 && ldrValue < 200)
  {
    intensity = 2;
    intensity2 = 0;
    intensityDisp = 22;
  }

  else if (ldrValue >= 200 && ldrValue < 250)
  {
    intensity = 3;
    intensity2 = 0;
    intensityDisp = 23;
  }

  else if (ldrValue >= 250 && ldrValue < 300)
  {
    intensity = 4;
    intensity2 = 1;
    intensityDisp = 24;
  }


  else if (ldrValue >= 300 && ldrValue < 350)
  {
    intensity = 5;
    intensity2 = 1;
    intensityDisp = 25;
  }


  else if (ldrValue >= 350 && ldrValue < 400)
  {
    intensity = 6;
    intensity2 = 2;
    intensityDisp = 26;
  }


  else if (ldrValue >= 400 && ldrValue < 450 )
  {
    intensity = 7;
    intensity2 = 3;
    intensityDisp = 27;
  }



  else if (ldrValue >= 450 && ldrValue < 500)
  {
    intensity = 8;
    intensity2 = 4;
    intensityDisp = 30;
  }



  else if (ldrValue >= 500 && ldrValue < 550)
  {
    intensity = 9;
    intensity2 = 5;
    intensityDisp = 31;
  }



  else if (ldrValue >= 550 && ldrValue < 600)
  {
    intensity = 10;
    intensity2 = 6;
    intensityDisp = 32;
  }



  else if (ldrValue >= 600 && ldrValue < 700)
  {
    intensity = 11;
    intensity2 = 7;
    intensityDisp = 33;
  }
  else if (ldrValue >= 700 && ldrValue < 800)
  {
    intensity = 12;
    intensity2 = 8;
    intensityDisp = 34;
  }
  else if (ldrValue >= 800 && ldrValue < 900)
  {
    intensity = 13;
    intensity2 = 9;
    intensityDisp = 35;
  }
  else if (ldrValue >= 900 && ldrValue < 999)
  {
    intensity = 14;
    intensity2 = 10;
    intensityDisp = 36;
  }


  else if (ldrValue >= 999)
  {
    intensity = 15;
    intensity2 = 11;
    intensityDisp = 37;
  }
  
 
  
  MaximCC.setIntensity(MaximLedRingInner, BrightnessMaximLedRingInner);
  MaximCC.setIntensity(MaximLedRingOuter, BrightnessMaximLedRingOuter);
  MaximCC.setIntensity(MaximLeds, BrightnessMaximLeds);
  MaximCC.setIntensity(MaximRtcTime, BrightnessMaximRtcTime);
  MaximCC.setIntensity(MaximDate, BrightnessMaximDate);
  MaximCC.setIntensity(MaximBufferBitError, BrightnessMaximBufferBitError);
  MaximCC.setIntensity(MaximPeriodPulse, BrightnessMaximPeriodPulse);
  MaximCC.setIntensity(MaximPeriodPulse, BrightnessMaximPeriodPulse);
  
  
  
   // first, clear all the 'Intensity' Led's (row '2 & 3') before displaying new value
  for (int i = 0; i < 8; i++) {
   MaximCC.setLed(MaximLeds, 2, i, false);
   MaximCC.setLed(MaximLeds, 3, i, false);
   // display intensity value LED's
   MaximCC.setLed(MaximLeds, intensityDisp / 10, intensityDisp % 10, true);
}
  
} 


//================================================================================================================