Files
DCF77-Analyzer-Clock-V2.0/DCF_77_ANALYZER_CLOCK.ino
2014-08-22 14:42:05 +02:00

1351 lines
48 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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
:() :() :() :() :() :() :() :() :() :() :() :() :() :() :() :() :() :() :() :() :() :() :() :() :() :() :()
This is a sketch, aimed at people who are just beginning to discover the wonderful DCF77 / Radio Clock world.
The C++ code is far from optimized because I myself am an Arduino and C++ newbie! ;)
But in writing this code and learning a LOT from others, I gradually comprehended the inner workings of
DCF and C++.
If you enjoy exploring the Arduino and DCF77 as much as I did, maybe by using and/or learning from this
sketch, my goal is reached. :o)
Erik de Ruiter
2014
*/
//----------------------------------------------------------------------------------------------------------
// 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>
// Maxim 7219 displays library ................................... http://playground.arduino.cc/Main/LEDMatrix
#include <DS1307RTC.h>
// a basic DS1307 library that returns time as a time_t .......... http://www.pjrc.com/teensy/td_libs_DS1307RTC.html
#include <LedControl.h>
//SPI interface library .......................................... http://arduino.cc/en/Reference/SPI
#include <SPI.h>
//STREAMING.h .................................................... http://arduiniana.org/libraries/streaming/
/*
New users sometimes wonder why the “Arduino language” doesnt provide the kind
of concatenation or streaming operations they have become accustomed to in Java/VB/C#/C++, etc.
lcd << "GPS #" << gpsno << " date: " << day << "-" << month << "-" << year << endl;
This library works for any class that derives from Print:
Serial << "Counter: " << counter;
lcd << "Temp: " << t.get_temperature() << " degrees";
my_pstring << "Hi Mom!" << endl;
With the new library you can also use formatting manipulators like this:
Serial << "Byte value: " << _HEX(b) << endl;
lcd << "The key pressed was " << _BYTE(c) << endl;
This syntax is familiar to many, is easy to read and learn, and, importantly, *** consumes no resources ***
(Because the operator functions are essentially just inline aliases for their print() counterparts,
no sketch gets larger or consumes more RAM as a result of their inclusion.)
*/
#include <Streaming.h>
// OneWire lets you access 1-wire devices made by Maxim/Dallas,
// such as DS18S20, DS18B20, DS1822 .............................. http://www.pjrc.com/teensy/td_libs_OneWire.html
// The DallasTemperature library can do all this work for you! ... http://milesburton.com/Dallas_Temperature_Control_Library
#include <OneWire.h>
//----------------------------------------------------------------------------------------------------------
// DS18B20 initialization
//----------------------------------------------------------------------------------------------------------
OneWire ds(8); // define Onewire instance DS on pin 8
//----------------------------------------------------------------------------------------------------------
// Maxim 7219 Matrix Display initialization
//----------------------------------------------------------------------------------------------------------
/*
clearDisplay(int addr) ........................................ clears the selected display
lc.shutdown(int addr, boolean) ................................ wake up the MAX72XX from power-saving mode (true = sleep, false = awake)
lc.setIntensity(int addr, value) .............................. set a medium brightness for the Leds (0=min - 15=max)
lc.setLed(int addr, int row, int col, boolean state) .......... switch on the led in row, column. remember that indices start at 0!
lc.setRow(int addr, int row, byte value) ...................... this function takes 3 arguments. example: lc.setRow(0,2,B10110000);
lc.setColumn(int addr, int col, byte value) ................... this function takes 3 arguments. example: lc.setColumn(0,5,B00001111);
lc.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
lc.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)
pin 12/7 is connected to the DataIn
pin 11/6 is connected to the CLK
pin 10/5 is connected to CS/LOAD
***** 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)
*/
// lc is for the Maxim Common CATHODE displays
LedControl lc = LedControl(12, 11, 10, 8, false); // Define pins for Maxim 72xx and how many 72xx we use
// lc1 is for the Maxim Common ANODE displays
LedControl lc1 = LedControl(7, 6, 5, 1, true); // Define pins for Maxim 72xx and how many 72xx we use
//----------------------------------------------------------------------------------------------------------
// Variable and array defenitions
//----------------------------------------------------------------------------------------------------------
// Define DCF routine parameters
#define DCFRejectionTime 700 // Pulse-to-Pulse rejection time.
#define DCFRejectPulseWidth 50 // Minimal pulse width
#define DCFSplitTime 180 // distinguishes pulse width 100 ms and 200 ms. In practice we see 130 ms and 230
#define DCFSyncTime 1600 // defines 2000 ms pulse for end of sequence
// define Arduino Pins
#define BUZZER A1 // Piezo buzzer on Analog port
#define DCF_INTERRUPT 0 // Interrupt number associated with pin
#define DCF77PIN 2 // Connection pin to DCF 77 device. Must be pin 2 or 3!
#define CHIMEPIN 3 // switch CHIME ON/OFF
#define DCF77SOUNDPIN 4 // switch DCF77 Sound ON/OFF
#define TEMPRESETPIN 9 // Push button to reset min/max temp memory
// define miscellaneous parameters
#define DEBUG_FLOW 0 // 1 = show info about functions calls
#define DS1307_I2C_ADDRESS 0x68 // define the RTC I2C address
// definition of Maxim 7219 display number sequence
// first Maxim 7219 in 'daisychain' must be '0', next '1' etc.
// COMMON CATHODE DISPLAYS
#define MaximLedRingInner 0
#define MaximLedRingOuter 1
#define MaximPeriodPulse 2
#define MaximBufferBitError 3
#define MaximWeek 4
#define MaximDate 5
#define MaximRtcTime 6
#define MaximLeds 7
// definition of Maxim 7219 display number sequence
// first Maxim 7219 in 'daisychain' must be '0', next '1' etc.
// COMMON ANODE DISPLAYS
#define MaximDcfTime 0
// definition of display brighness levels
#define BrightnessMaximLedRingOuter 1
#define BrightnessMaximLedRingInner 1
#define BrightnessMaximPeriodPulse 2
#define BrightnessMaximWeek 3
#define BrightnessMaximDate 9
#define BrightnessMaximRtcTime 1
#define BrightnessMaximLeds 6
#define BrightnessMaximDcfTime 4
#define BrightnessMaximBufferBitError 15
// definition of Status/Error Led's. Use division and modulo to seperate Row/Col values!!
// to lit a LED you need a ROW and COLUMN value.
// so, for example: BFLed/10 results in the value '1' (row)
// and BFLed%10 results in the value '3' (column)
/* Row/Col LED numbers of Maxim 7219 chip
Row Col
Sunday 0 0
Monday 0 1
Tuesday 0 2
Wednesday 0 3
Thursday 0 4
Friday 0 5
Saturday 0 6
Synced 0 7
RTCError 1 0
SummerTime 1 1
WinterTime 1 2
LeapYear 1 3
BF 1 4
EoM 1 5
EoB 1 6
rPW 1 7
rPT 2 0
DCFOk 2 1
Chime 2 2
*/
#define SyncedLed 07
#define RTCError 10
#define SummerTimeLed 11
#define WinterTimeLed 12
#define LeapYearLed 13
#define BFLed 14
#define EoMLed 15
#define EoBLed 16
#define rPWLed 17
#define rPTLed 20
#define DCFOKLed 21
#define ChimeLed 22
// Pulse flanks
static int leadingEdge = 0;
static int trailingEdge = 0;
static int previousLeadingEdge = 0;
// used in <Int0handler>
volatile unsigned char DCFSignalState = 0;
// used in <loop>
int ss = 0;
int previousSecond = 0;
int previousSecond2 = 0;
unsigned char previousSignalState;
// 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
/* not used at this moment
// Inserted a 'null' value ("") because an array starts with position zero '0' and I need it to start at '1'
const char dayNameLong[][10] = {"", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag", "Zondag"};
const char dayNameShort[][3] = {"", "Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"};
const char monthNameLong [][10] = {"", "Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"};
const char monthNameShort [][4] = {"", "Jan", "Feb", "Mrt", "Apr", "Mei", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"};
*/
static int bufferPosition = 0;
static int endOfMinute = 0;
static int previousMinute = 0;
static int previousHour = 0;
// used to check if switch state is changed
int buttonChimeToggle = 0;
int buttonDCF77SoundToggle = 0;
int buttonDisplayOffToggle = 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; //Returns the number of day in the year
int weekNumber; //Returns the number of the week in the year
// error counter variable
int errorCounter = 0;
// miscelleanous variables
boolean Chime = 0;
boolean daytime = 0;
int loopTime = 0; // check Loop() timing
//buzzer related
boolean chimeSwitch = 0;
boolean dcf77SoundSwitch = 0;
// temperature variables
byte present = 0;
byte DS18B20Data[12];
byte addr[8] = {0x28, 0x13, 0x95, 0x7B, 0x05, 0x00, 0x00, 0x70};
int maxTemp = 0;
int minTemp = 0;
int lsByte = 0;
int msByte = 0;
int tempReading = 0;
int tempCelcius = 0;
boolean tempResetButton = 0;
//==============================================================================
// SETUP
//==============================================================================
void setup()
{
// initialize Serial communication
Serial.begin(115200);
// define DCF77PIN as input
pinMode(DCF77PIN, INPUT);
// define pin to shut off display as input
pinMode(CHIMEPIN, INPUT);
pinMode(TEMPRESETPIN, INPUT);
pinMode(DCF77SOUNDPIN, INPUT);
// Initialize all variables and LED displays
initialize();
// Initialize DCF77 signal listener interrupt
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
lc.setLed(MaximLeds, RTCError / 10, RTCError % 10, true);
} else {
// RTC has set the system time - dim RTCError LED
lc.setLed(MaximLeds, RTCError / 10, RTCError % 10, false);
}
// use for test purposes
//setTime(23, 59, 40, 31, 12, 13);
//RTC.set(now());
}
//==============================================================================
// LOOP
//==============================================================================
void loop()
{
if (DEBUG_FLOW) Serial.println(" <Loop> ");
// check if switches are changed and act upon it
checkSwitches();
// check if time is changed
if (second() != previousSecond)
{
//display -Real Time Clock- Time
displayRtcTime();
// display 'HI' and 'LO' temperature on specific moments
switch (second())
{
case 30:
// display 'HI' on display for Hi temperature
lc1.clearDisplay(MaximDcfTime); // clear display
lc1.setChar(MaximDcfTime, 3, 'H', false); // Display 'H' (Celcius) character. Binary pattern to lite up individual segments in this order is: .ABCDEFG
lc1.setChar(MaximDcfTime, 2, '1', false); // Display 'I' (Celcius) character. Binary pattern to lite up individual segments in this order is: .ABCDEFG
break;
case 31:
case 32:
// display Max temperature on DCF display LED display
lc1.setChar(MaximDcfTime, 3, (maxTemp / 1000), false);
lc1.setChar(MaximDcfTime, 2, (maxTemp % 1000) / 100, true);
lc1.setChar(MaximDcfTime, 1, (maxTemp % 100) / 10, false);
lc1.setRow(MaximDcfTime, 0, B01001110); // Display 'C' (Celcius) character. Binary pattern to lite up individual segments in this order is: .ABCDEFG
lc1.setChar(MaximDcfTime, 5, 1, false); // activate top dot
break;
case 33:
// display 'LO' on display for Low temperature
lc1.clearDisplay(MaximDcfTime); // clear display
lc1.setChar(MaximDcfTime, 3, 'L', false); // Display 'L' (Celcius) character. Binary pattern to lite up individual segments in this order is: .ABCDEFG
lc1.setChar(MaximDcfTime, 2, '0', false); // Display 'O' (Celcius) character. Binary pattern to lite up individual segments in this order is: .ABCDEFG
break;
case 34:
case 35:
// display Min temperature on DCF display LED display
lc1.setChar(MaximDcfTime, 3, (minTemp / 1000), false);
lc1.setChar(MaximDcfTime, 2, (minTemp % 1000) / 100, true);
lc1.setChar(MaximDcfTime, 1, (minTemp % 100) / 10, false);
lc1.setRow(MaximDcfTime, 0, B01001110); // Display 'C' (Celcius) character. Binary pattern to lite up individual segments in this order is: .ABCDEFG
lc1.setChar(MaximDcfTime, 5, 1, false); // activate top dot
break;
case 36:
// display 'CU' on display for Current temperature
lc1.clearDisplay(MaximDcfTime); // clear display
lc1.setColumn(MaximDcfTime, 3, B01001110); // Display 'C' (Celcius) character. Binary pattern to lite up individual segments in this order is: .ABCDEFG
lc1.setColumn(MaximDcfTime, 2, B00111110); // Display 'O' (Celcius) character. Binary pattern to lite up individual segments in this order is: .ABCDEFG
break;
// display current temperature
default:
displayTemp();
break;
} //switch
previousSecond = second();
} //(second() != previousSecond)
// call scanSignal if interrupt is triggered by incoming DCF77 pulse
if (DCFSignalState != previousSignalState)
{
scanSignal();
previousSignalState = DCFSignalState;
} //if (DCFSignalState != previousSignalState)
// display date, week LED's, week nr etc. if time is changed
if (minute() != previousMinute)
{
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// DISPLAY SHUTS DOWN FROM 11 PM UNTIL 8 AM
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// check wether it is Day- or Nighttime
if (hour() >= 8 && hour() < 23)
{
// it's DAYTIME so time to activate displays
if (daytime == 0) // wake up displays is only once needed so check variable...
{
// activate all the displays and status LED's
for (int i = 0; i < 8; i++)
{
lc.shutdown(i, false); // Common Cathode displays wakeup
} //for
// Maxim Common Anode
lc1.shutdown(0, false); // Common Anode display wake up
} //if
daytime = 1; // set daytime variable to TRUE so display activation is disabled
displayData(); // display date, week LED's, week nr etc.
} //if
else
{
daytime = 0; // 'reset' variable so display wakeup is performed when daytime is reached
// time to shut down displays at night to save power...
for (int i = 0; i < 8; i++)
{
lc.shutdown(i, true); // display shut down
} //for
// Maxim Common Anode
lc1.shutdown(0, true); // display shut down
} //else
//display the DCF time
//displayDCFTime();
previousMinute = minute();
}
// reset errorCounter display every hour
if (dcfHour != previousHour)
{
// reser errorCounter to '0' every hour
errorCounter = 0;
ledDisplay(MaximBufferBitError, "R", 0);
previousHour = dcfHour;
}
} //loop
//================================================================================================================
//================================================================================================================
//================================================================================================================
//================================================================================================================
//================================================================================================================
//================================================================================================================
//
// scanSignal
//
// called from <loop>
//================================================================================================================
/*
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
*/
// Evaluates the signal as it is received. Decides whether we received a "1" or a "0"
void scanSignal()
{
if (DEBUG_FLOW) Serial.print(" <scanSignal> ");
// store time of received pulse
int flankTime = millis();
///////////////////////////////////////////////////////////////
// Check for flank UP and perform checks on received DCF signal
if (DCFSignalState == 1) {
leadingEdge = flankTime; // store flankTime to check validity of received period
// If this flank UP is detected quickly after previous flank up this
// will be an incorrect period lenght that we shall reject
if ((leadingEdge - previousLeadingEdge) < DCFRejectionTime) // DCFRejectionTime = 700, should be 1000ms
{
// rPT - ERROR: FLANK TO FLANK TIME IS TO SHORT -> REJECTED
error(rPTLed);
return;
} // if ((leadingEdge - previousLeadingEdge) < DCFRejectionTime)
// No further action is taken because pulse information is not complete yet, only after receiving DOWN flank
// we will know if the pulse is a '0' or '1' and we can do several checks.
return;
} // if (DCFSignalState == 1)
///////////////////////////////////////////////////////////
// Check for DOWN and perform checks on received DCF signal
if (DCFSignalState == 0) {
trailingEdge = flankTime; // store flankTime to calculate pulsewidth.
// If the detected pulse is too short it will be an incorrect pulse that we shall reject as well
if ((trailingEdge - leadingEdge) < DCFRejectPulseWidth) // DCFRejectPulseWidth = 50. should be 100 and 200 ms ideally
{
//rPW - ERROR: DETECTED PULSE IS TO SHORT -> REJECTED
error(rPWLed);
return;
} // if ((trailingEdge - leadingEdge) < DCFRejectPulseWidth)
// display pulse width on LED display
ledDisplay(MaximPeriodPulse, "R", (trailingEdge - leadingEdge));
// display period width on LED display
ledDisplay(MaximPeriodPulse, "L", (leadingEdge - previousLeadingEdge));
// END OF MINUTE check
//if (bufferPosition > 0 && (leadingEdge - previousLeadingEdge) > DCFSyncTime) // DCFSyncTime is 1600ms
if ((leadingEdge - previousLeadingEdge) > DCFSyncTime) // DCFSyncTime is 1600ms
{
// end of minute detected:
finalizeBuffer(); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
} // if
// refresh previousLeadingEdge time
previousLeadingEdge = leadingEdge;
// PROCESS RECEIVED DCFbit, distinguish between long and short pulses
if (trailingEdge - leadingEdge < DCFSplitTime) // DCFSplitTime == 180
{
appendSignal(0); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
if (dcf77SoundSwitch == 1) buzzer(100);
} //if
else
{
appendSignal(1); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
if (dcf77SoundSwitch == 1) buzzer(200);
} //else
} // if (DCFSignalState == 0)
} // void scanSignal();
//================================================================================================================
//
// appendSignal
//
// called from <scanSignal>
//================================================================================================================
/**
* Add new bit to buffer
*/
void appendSignal(unsigned char signal)
{
if (DEBUG_FLOW) Serial.print(" <appendSignal> ");
// display bufferPosition on LED display
lc.setChar(MaximBufferBitError, 7, bufferPosition / 10, false);
lc.setChar(MaximBufferBitError, 6, bufferPosition % 10, false);
// display received DCFbit on LED Display
lc.setChar(MaximBufferBitError, 4, signal, false);
// Fill array with DCFbit signal
DCFbitBuffer[bufferPosition] = signal;
// display received bits on 1st Matrix displays
lc.setLed(MaximLedRingInner, bufferPosition / 8, bufferPosition % 8, signal);
// increment bufferposition counter
bufferPosition++;
if (bufferPosition > 1)
{
// clear error messages on the screen. clear at second '1' to leave them a while the display...
lc.setLed(MaximLeds, BFLed / 10, BFLed % 10, false);
lc.setLed(MaximLeds, EoMLed / 10, EoMLed % 10, false);
lc.setLed(MaximLeds, EoBLed / 10, EoBLed % 10, false);
lc.setLed(MaximLeds, rPWLed / 10, rPWLed % 10, false);
lc.setLed(MaximLeds, rPTLed / 10, rPTLed % 10, false);
}
if (bufferPosition == 6)
{
// clear time status messages
}
if (bufferPosition == 56)
{
// warn for coming minute transition
//buzzer(100);
}
if (bufferPosition > 59)
{
// EoB ERROR - clear both DCFbit displays because buffer is full before at end of time-sequence
lc.clearDisplay(MaximLedRingInner); // clear display
lc.clearDisplay(MaximLedRingOuter); // clear display
lc.clearDisplay(MaximDcfTime); // clear display
// EoB - BUFFER IS FULL BEFORE END OF TIME-SEQUENCE, THIS MAY BE DUE TO NOISE GIVING ADDITIONAL PEAKS
error(EoBLed);
return;
} // if (bufferPosition > 59)
// buzzer sound with duration depending on DCFsignal
//signal == 0 ? buzzer(100) : buzzer(200);
} // void
//================================================================================================================
//
// finalizeBuffer
//
//================================================================================================================
/**
* Finalize filled buffer
*/
void finalizeBuffer(void) {
if (DEBUG_FLOW) Serial.print(" <finalizeBuffer> ");
if (bufferPosition == 59)
{
// BUFFER FULL
// reset RTC seconds to '0'
// display BF on TFT screen
lc.setLed(MaximLeds, BFLed / 10, BFLed % 10, true);
// display DCF 'OK' status TRUE
lc.setLed(MaximLeds, DCFOKLed / 10, DCFOKLed % 10, true);
// clear DCFbit led ring inner
lc.clearDisplay(MaximLedRingInner);
//copy minute information to DCFbt led ring outer
for (int i = 0; i < 59; i++) {
lc.setLed(MaximLedRingOuter, i / 8, i % 8, DCFbitBuffer[i]);
} // for
// process buffer and extract data
processBuffer();
// Reset running buffer
bufferPosition = 0;
// Reset DCFbitBuffer array, positions 0-58 (=59 bits)
for (int i = 0; i < 59; i++) {
DCFbitBuffer[i] = 0;
} // for
} // if (bufferPosition == 59)
else
{
// BUFFER NOT YET FULL at end of time-sequence
lc.setLed(MaximLeds, EoMLed / 10, EoMLed % 10, true);
// CLEAR DISPLAYS
// clear both DCFbit displays because Buffer is not yet full at end of time-sequence
lc.clearDisplay(MaximLedRingInner);
lc.clearDisplay(MaximLedRingOuter);
// Reset running buffer
bufferPosition = 0;
// Reset DCFbitBuffer array, positions 0-58 (=59 bits)
for (int i = 0; i < 59; i++) {
DCFbitBuffer[i] = 0;
}
} // else
} // void finalizeBuffer(void)
//================================================================================================================
//
// processBuffer
//
// called from <finalizeBuffer>
//================================================================================================================
/**
* Evaluates the information stored in the buffer. This is where the DCF77
* signal is decoded
*/
void processBuffer(void) {
if (DEBUG_FLOW) Serial.print(" <processBuffer> ");
// 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);
//Determine Summer- or Witertime
dcfDST = bitDecode(17, 17);
// determine Leap Year
leapYear = calculateLeapYear(dcfYear);
// set Arduino time and after that set RTC time
setTime(dcfHour, dcfMinute, 0, dcfDay, dcfMonth, dcfYear);
RTC.set(now());
// activate Synced LED
lc.setLed(MaximLeds, SyncedLed / 10, SyncedLed % 10, true);
} // return to finalizeBuffer()
//================================================================================================================
//
// bitDecode
//
// called from <processBuffer>
//================================================================================================================
int bitDecode (int bitStart, int bitEnd) {
if (DEBUG_FLOW) Serial.print(" <bitDecode> ");
// 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;
}
//================================================================================================================
//
// buzzer
//
//================================================================================================================
void buzzer(int duration)
{
tone(BUZZER, 1500, duration);
}
//================================================================================================================
//
// initialize
//
// calles from <setup>
//================================================================================================================
/**
* Initialize parameters
*/
void initialize(void)
{
if (DEBUG_FLOW) Serial.print(" <initialize> ");
//---------------------------------------------------
// Initialize Variables
//---------------------------------------------------
leadingEdge = 0;
trailingEdge = 0;
previousLeadingEdge = 0;
bufferPosition = 0;
// Reset DCFbitBuffer array, positions 0-58 (=59 bits)
for (int i = 0; i < 59; i++) {
DCFbitBuffer[i] = 0;
}
//---------------------------------------------------
// Initialize Maxim 72xx IC's
//---------------------------------------------------
// Maxim Common Cathode
for (int i = 0; i < 8; i++)
{
lc.shutdown(i, true); // display shut down
delay(100);
lc.shutdown(i, false); // display wake up
lc.clearDisplay(i); // clear display
}
// Maxim Common Anode
lc1.shutdown(0, true); // display shut down
delay(100);
lc1.shutdown(0, false); // display wake up
lc1.clearDisplay(0); // clear display
// Maxim Common Cathode
lc.setIntensity(MaximLedRingOuter, BrightnessMaximLedRingOuter);
lc.setIntensity(MaximLedRingInner, BrightnessMaximLedRingInner);
lc.setIntensity(MaximPeriodPulse, BrightnessMaximPeriodPulse);
lc.setIntensity(MaximWeek, BrightnessMaximWeek);
lc.setIntensity(MaximDate, BrightnessMaximDate);
lc.setIntensity(MaximRtcTime, BrightnessMaximRtcTime);
lc.setIntensity(MaximLeds, BrightnessMaximLeds);
lc.setIntensity(MaximBufferBitError, BrightnessMaximBufferBitError);
// Maxim Common Anode
lc1.setIntensity(MaximDcfTime, BrightnessMaximDcfTime);
//---------------------------------------------------
// Initialize Displays
//---------------------------------------------------
// set errorCounter display to '0'
ledDisplay(MaximBufferBitError, "R", 0);
}
//================================================================================================================
//
// int0handler
//
//================================================================================================================
/**
* Interrupt handler that processes up-down flanks into pulses and stores these in the buffer
*/
void int0handler()
{
if (DEBUG_FLOW) Serial.print(" <int0handler ");
DCFSignalState = digitalRead(DCF77PIN);
} // int0handler()
//================================================================================================================
//
// displayBuffer
//
//================================================================================================================
void displayBuffer(String message)
{
Serial << endl << message << endl;
Serial << "01234567890123456789012345678901234567890123456789012345678" << endl;
Serial << " | 1 | 2 | 3 | 4 | 5 | " << endl;
Serial << " 124 12 12 1 1248 " << endl;
Serial << " 1248000 124800 1248001241248012480000 " << endl;
Serial << " ZW mmmmmmmPhhhhhhPDDDDDDwwwMMMMMYYYYYYYYP" << endl;
// display DCFbit buffer contents
for (int i = 0; i < 59; i++) {
Serial << DCFbitBuffer [i];
}
Serial << endl;
}
//================================================================================================================
//
// ledDisplay on Maxim 8 digit displays
//
// calles from processBuffer
//================================================================================================================
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);
//Now print the number digit by digit
//preceding zero's are removed with tenary operator by 'printing' a space character.
lc.setChar(addr, 3 + shift, ((value < 1000) ? ' ' : thousands), false);
lc.setChar(addr, 2 + shift, ((value < 100) ? ' ' : hundreds), false);
lc.setChar(addr, 1 + shift, ((value < 10) ? ' ' : tens), false);
lc.setChar(addr, 0 + shift, ones, false);
}
//================================================================================================================
//
// checkSwitches
//
// called from scanSignal
//================================================================================================================
void checkSwitches(void)
{
// read stat of temp rest push button
tempResetButton = digitalRead(TEMPRESETPIN);
// reset temperature min/max values when push-button is pressed
if (tempResetButton == 1)
{
maxTemp = 0;
minTemp = 0;
}
//read state of switch CHIME
chimeSwitch = digitalRead(CHIMEPIN);
if (chimeSwitch == 1)
{
Chime = 1;
lc.setLed(MaximLeds, ChimeLed / 10, ChimeLed % 10, true);
} // if
else
{
Chime = 0;
lc.setLed(MaximLeds, ChimeLed / 10, ChimeLed % 10, false);
} // else
//read state of switch DCF77SOUNDPIN
int buttonDCF77Sound = digitalRead(DCF77SOUNDPIN);
if (buttonDCF77Sound == 1)
{
dcf77SoundSwitch = 1;
} // if
else
{
dcf77SoundSwitch = 0;
} // else
} // void checkButtons(void)
//================================================================================================================
//
// error
//
//================================================================================================================
// errorLed parameter is LED to be switched on
int error(int errorLed)
{
lc.setLed(MaximLeds, errorLed / 10, errorLed % 10, true);
// clear Led's/displays because of error condition
lc.setLed(MaximLeds, DCFOKLed / 10, DCFOKLed % 10, false);
lc.setLed(MaximLeds, SyncedLed / 10, SyncedLed % 10, false);
lc.clearDisplay(MaximLedRingOuter); // clear display
// increase errorCounter and display errorCount
errorCounter++;
ledDisplay(MaximBufferBitError, "R", errorCounter);
}
//================================================================================================================
//
// dayWeekNumber
//
//================================================================================================================
/*
Code from: http://forum.arduino.cc/index.php/topic,44476.0.html
you need to declare two global variables:
Code:
short dayNumber; //Returns the number of day in the year
short weekNumber; //Returns the number of the week in the year
and this is how you need to call the function in your loop:
Code:
dayWeekNumber(year(),month(),day(),weekday());
Take into account that i use TIME library in my sketch, so how you call to year, month, day and day of week could change for other libraries.
and this is an example of how to call the function and show the results:
Code:
dayWeekNumber(year(),month(),day(),weekday());
Serial.print("Day #");
Serial.print(dayNumber);
Serial.print(" at week # ");
Serial.print(weekNumber);
Serial.print(" of ");
Serial.println(year());
Be aware than in the function, there is a problematic line:
Code:
WN = (DN-7+10)/7;
This is because in the library the week starts on Sunday (0), however, in the ISO standard,
week start on monday (1), and sunday is the last day (7). For that reason, i included this especial case in the code.
*/
int dayWeekNumber(int y, int m, int d, int w)
{
int days[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; // Number of days at the beginning of the month in a (not leap) year.
//Start to calculate the number of day
if (m == 1 || m == 2) {
//for any type of year, it calculate the number of days for January or february
dayNumber = days[(m - 1)] + d;
} // if
// Now, try to calculate for the other months
else if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) { //those are the conditions to have a leap year
// if leap year, calculate in the same way but increasing one day
dayNumber = days[(m - 1)] + d + 1;
} // else if
else
{
//if not a leap year, calculate in the normal way, such as January or February
dayNumber = days[(m - 1)] + d;
} // else
// Now start to calculate Week number
if (w == 0) {
//if it is sunday (time library returns 0)
weekNumber = (dayNumber - 7 + 10) / 7;
} // if
else
{
// for the other days of week
weekNumber = (dayNumber - w + 10) / 7;
} // else
return weekNumber;
}
//================================================================================================================
//
// leapYear
//
//================================================================================================================
/*****
Purpose: Determine if a given year is a leap year
Parameters: int year // The year to test
Return value: int // '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;
}
}
//================================================================================================================
//
// displayDCFTime and day led's
//
//================================================================================================================
void displayData(void)
{
// sound buzzer on whole hour
if (minute() == 00 && Chime == 1)
{
// beep-beep on whole hour
tone(BUZZER, 4000, 180);
delay (250);
tone(BUZZER, 4000, 180);
}
// 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++) {
lc.setLed(MaximLeds, 0, i, false);
}
// Maxim 7219 LED's are numbered from zero so sunday '7' must be changed to '0'
lc.setLed(MaximLeds, 0, ((dcfWeekDay == 7) ? 0 : dcfWeekDay), true);
// display Weeknumber
lc.setChar(MaximWeek, 1, ((weekNumber < 10) ? ' ' : (weekNumber / 10)), false);
lc.setChar(MaximWeek, 0, weekNumber % 10, false);
// display Date - with dashes between D-M-Y
//lc.setChar(MaximDate, 7, ((dcfDay < 10) ? ' ' : (dcfDay / 10)), false);
lc.setChar(MaximDate, 7, dcfDay / 10, false);
lc.setChar(MaximDate, 6, dcfDay % 10, false);
lc.setChar(MaximDate, 5, '-', false);
lc.setChar(MaximDate, 4, dcfMonth / 10, false);
lc.setChar(MaximDate, 3, dcfMonth % 10, false);
lc.setChar(MaximDate, 2 , '-', false);
lc.setChar(MaximDate, 1, dcfYear / 10, false);
lc.setChar(MaximDate, 0, dcfYear % 10, false);
/*
// display Date - with dots between D.M.Y
lc.setChar(MaximDate, 7, dcfDay / 10, false);
lc.setChar(MaximDate, 6, dcfDay % 10, true);
lc.setChar(MaximDate, 5, dcfMonth / 10, false);
//lc.setChar(MaximDate, 5, ((dcfMonth < 10) ? ' ' : (dcfMonth / 10)), false);
lc.setChar(MaximDate, 4, dcfMonth % 10, true);
lc.setChar(MaximDate, 3, 2, false);
lc.setChar(MaximDate, 2, 0, false);
lc.setChar(MaximDate, 1, dcfYear / 10, false);
lc.setChar(MaximDate, 0, dcfYear % 10, false);
*/
/*
// display Date - moved day to right if month is <10
lc.setChar(MaximDate, 7, ((dcfDay < 10) ? ' ' : ((dcfMonth < 10) ? ' ' : (dcfDay / 10))), false);
lc.setChar(MaximDate, 6, ((dcfMonth < 10) ? ((dcfDay < 10) ? ' ' : (dcfDay / 10)) : (dcfDay % 10)), ((dcfMonth < 10) ? false : true));
lc.setChar(MaximDate, 5, ((dcfMonth < 10) ? (dcfDay % 10) : (dcfMonth / 10)), ((dcfMonth < 10) ? true : false));
lc.setChar(MaximDate, 4, dcfMonth % 10, true);
lc.setChar(MaximDate, 3, 2, false);
lc.setChar(MaximDate, 2, 0, false);
lc.setChar(MaximDate, 1, dcfYear / 10, false);
lc.setChar(MaximDate, 0, dcfYear % 10, false);
*/
// display Summer- or Wintertime LED
if (dcfDST = 1) {
lc.setLed(MaximLeds, SummerTimeLed / 10, SummerTimeLed % 10, true);
} else {
lc.setLed(MaximLeds, WinterTimeLed / 10, WinterTimeLed % 10, true);
}
// display Leap Year LED
if (leapYear = 1) {
lc.setLed(MaximLeds, LeapYearLed / 10, LeapYearLed % 10, false);
} // if
else
{
lc.setLed(MaximLeds, LeapYearLed / 10, LeapYearLed % 10, true);
} // else
}
//================================================================================================================
//
// displayDCFTime (NOT USED - TEMPERATURE IS DISPLAYED INSTEAD)
//
//================================================================================================================
void displayDCFTime(void)
{
// display DCF time
lc1.setChar(MaximDcfTime, 3, ((dcfHour < 10) ? ' ' : (dcfHour / 10)), false);
lc1.setChar(MaximDcfTime, 2, (dcfHour % 10), false);
lc1.setChar(MaximDcfTime, 1, (dcfMinute / 10), false);
lc1.setChar(MaximDcfTime, 0, (dcfMinute % 10), false);
lc1.setChar(MaximDcfTime, 4, 1, false);
}
//================================================================================================================
//
// displayRTCTime
//
//================================================================================================================
void displayRtcTime() {
//if (timeStatus() == timeSet) {
lc.setChar(MaximRtcTime, 3, ((hour() < 10) ? ' ' : (hour() / 10)), false);
lc.setChar(MaximRtcTime, 2, (hour() % 10), false);
lc.setChar(MaximRtcTime, 1, (minute() / 10), false);
lc.setChar(MaximRtcTime, 0, (minute() % 10), false);
lc.setChar(MaximRtcTime, 5, (second() / 10), false);
lc.setChar(MaximRtcTime, 4, (second() % 10), false);
//} else {
//Serial.println("The time has not been set. Please run the Time");
//Serial.println("TimeRTCSet example, or DS1307RTC SetTime example.");
//Serial.println();
//delay(4000);
//}
}
//================================================================================================================
//
// displayTemp - get temperature from DS18B20 sensor and display it
//
// called from loop() every 1 second
//================================================================================================================
void displayTemp(void)
{
ds.reset(); // The initialization sequence consists of a reset pulse transmitted by the bus master followed by presence pulse(s) transmitted by the slave(s).
//ds.skip(); // use this command when only 1 sensor is used to avoid specifying the address!
ds.select(addr); // select specific device
ds.write(0x44); // start conversion
delay(100); // delay is dependent on resolution!
present = ds.reset(); // The return value of ds.reset() command is stored in the variable PRESENT - The initialization sequence consists of a reset pulse transmitted by the bus master followed by presence pulse(s) transmitted by the slave(s).
ds.select(addr); // select specific device
ds.write(0xBE); // Read Scratchpad
//Serial.print(" Device Present = ");
//Serial.print(present, HEX); // show the result of RESET command: 1=present; 0=not present
//Serial.print(" Data = ");
for ( byte i = 0; i < 9; i++) { // we need 9 bytes
DS18B20Data[i] = ds.read(); // Read a Byte a a time
} // for
// Convert the data to actual temperature
// because the result is a 16 bit signed integer, it should
// be stored to an "int16_t" type, which is always 16 bits
// even when compiled on a 32 bit processor.
lsByte = DS18B20Data[0];
msByte = DS18B20Data[1];
tempReading = (msByte << 8) + lsByte;
tempCelcius = (6 * tempReading) + tempReading / 4; // multiply by (100 * 0.0625) or 6.25
// store min and max temperatures
if (minTemp == 0) minTemp = tempCelcius;
if (tempCelcius > maxTemp) maxTemp = tempCelcius;
if (tempCelcius < minTemp) minTemp = tempCelcius;
// display temperature on DCF display LED display
lc1.setChar(MaximDcfTime, 4, ' ', false); // deactivate colon digits
lc1.setChar(MaximDcfTime, 3, (tempCelcius / 1000), false);
lc1.setChar(MaximDcfTime, 2, (tempCelcius % 1000) / 100, true);
lc1.setChar(MaximDcfTime, 1, (tempCelcius % 100) / 10, false);
lc1.setRow(MaximDcfTime, 0, B01001110); // Display 'C' (Celcius) character. Binary pattern to lite up individual segments in this order is: .ABCDEFG
lc1.setChar(MaximDcfTime, 5, 1, false); // activate top dot
}
//================================================================================================================
//
// writeScratchpad - change data in memory of DS18B20 sensor
//
// USE ONLY once in LOOP() IF YOU WANT TO CHANGE RESOLUTION
//================================================================================================================
void writeScratchpad(void)
{
// This is done ONCE in setup()
// INITIALIZATION
// All transactions on the 1-Wire bus begin with an initialization sequence.
// The initialization sequence consists of a reset pulse transmitted by the bus master followed by presence pulse(s) transmitted by the slave(s).
// The presence pulse lets the bus master know that slave devices (such as the DS18B20) are on the bus and are ready to operate.
ds.reset(); // The initialization sequence consists of a reset pulse transmitted by the bus master followed by presence pulse(s) transmitted by the slave(s).
// Select a specific DS18x20 device
byte addr[8] = {0x28, 0x13, 0x95, 0x7B, 0x05, 0x00, 0x00, 0x70};
ds.select(addr);
// WRITE SCRATCHPAD
// This command allows the master to write 3 bytes of data to the DS18B20s scratchpad.
// The first data byte is written into the TH register (byte 2 of the scratchpad), the second byte is written into the TL register (byte 3),
// and the third byte is written into the configuration register (byte 4).
// Data must be transmitted least significant bit first. All three bytes MUST be written before the master issues a reset, or the data may be corrupted.
ds.write(0x4E);
ds.write(0); // write zero into the alarm register HIGH
ds.write(0); // write zero into the alarm register LOW
/* and write R1 and R2 into the configuration register to select the precision of the temperature
R1/R2 value | resolution | conversion time | increments
HEX DEC
0 0 0 = 9 bits | 93,75 ms | 0,5 Celsius
0 1 1 = 10 bits | 187,5 ms | 0,25
1 0 2 = 11 bits | 375 ms | 0,125
1 1 3 = 12 bits | 750 ms | 0,0625
CONFIGURATION REGISTER:
------ Bit ------
7 6 5 4 3 2 1 0
0 R1 R0 1 1 1 1 1
*/
// CURRENT SETTING:
ds.write(0 << 5); // << 5 means shift value 5 positions to the left. ds.write(B00011111) does the same
// WRITE SCRATCHPAD DATA TO EEPROM
ds.reset();
ds.select(addr);
// COPY SCRATCHPAD [48h]
// This command copies the contents of the scratchpad TH, TL and configuration registers (bytes 2, 3 and 4) to EEPROM.
// If the device is being used in parasite power mode, within 10μs (max) after this command is issued the master must
// enable a strong pullup on the 1-Wire bus for at least 10ms as described in the Powering the DS18B20 section.
ds.write(0x48);
}