Components and supplies
Arduino Mega 2560
Arduino UNO
Project description
Code
Superfilter sketch
arduino
1// 2// This is the Superfilter sketch I use with the DCF Analyzer/Clock 2.0 3// Udo Klein did an amazing job with this filter 4// 5// Erik de Ruiter 6 7/* 8 Arduino Uno pin connections I used for the DCF Analyzer Clock 9 10 DCF input ................. A5 (19) = dcf77_sample_pin 11 Output DCF Filtered ....... 12 = dcf77_filtered_pin 12 Output DCF Semi Synthesized A2 (16) = dcf77_semi_synthesized_pin 13 Output DCF Synthesized .... 6 = dcf77_synthesized_pin 14 LED DCF output filtered ... A4 (18) = dcf77_monitor_pin = DCF Monitor LED 15 LED 1 Hz pulse ............ 10 = dcf77_second_pulse_pin = Filter Locked LED 16 LED DCF OK ................ 13 = dcf77_signal_good_indicator_pin = Signal Quality LED 17 LED Difference Filtered ... 7 = dcf77_filter_diff_pin \ 18 LED Difference Semi Synth.. A0 = dcf77_semi_synthesized_diff_pin -> = Signal Difference LED 19 LED Difference Synthesized 4 = dcf77_synthesized_diff_pin / 20*/ 21 22// 23// www.blinkenlight.net 24// 25// Copyright 2014, 2015 Udo Klein 26// 27// This program is free software: you can redistribute it and/or modify 28// it under the terms of the GNU General Public License as published by 29// the Free Software Foundation, either version 3 of the License, or 30// (at your option) any later version. 31// 32// This program is distributed in the hope that it will be useful, 33// but WITHOUT ANY WARRANTY; without even the implied warranty of 34// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 35// GNU General Public License for more details. 36// 37// You should have received a copy of the GNU General Public License 38// along with this program. If not, see http://www.gnu.org/licenses/ 39 40#include <dcf77.h> 41/* 42const uint8_t pon_pin = 51; // connect pon to ground !!! 43const uint8_t data_pin = 19; 44const uint8_t gnd_pin = 51; 45const uint8_t vcc_pin = 49; 46*/ 47 48const uint8_t dcf77_analog_samples = false; 49const uint8_t dcf77_analog_sample_pin = 5; 50const uint8_t dcf77_sample_pin = 19; // A5 51const uint8_t dcf77_inverted_samples = 0; 52#if defined(__AVR__) 53#define ledpin(led) (led) 54#else 55#define ledpin(led) (led<14? led: led+(54-14)) 56#endif 57 58const uint8_t dcf77_monitor_pin = ledpin(18); // A4 59 60const bool provide_filtered_output = true; 61const uint8_t dcf77_filtered_pin = ledpin(12); 62const uint8_t dcf77_inverted_filtered_pin = ledpin(11); 63const uint8_t dcf77_filter_diff_pin = ledpin(7); 64 65const bool provide_semi_synthesized_output = true; 66const uint8_t dcf77_semi_synthesized_pin = ledpin(16); 67const uint8_t dcf77_inverted_semi_synthesized_pin = ledpin(15); 68const uint8_t dcf77_semi_synthesized_diff_pin = ledpin(14); 69 70const bool provide_synthesized_output = true; 71const uint8_t dcf77_synthesized_pin = ledpin(6); 72const uint8_t dcf77_inverted_synthesized_pin = ledpin(5); 73const uint8_t dcf77_synthesized_diff_pin = ledpin(4); 74 75const uint8_t dcf77_second_pulse_pin = ledpin(10); 76 77 78const uint8_t dcf77_signal_good_indicator_pin = ledpin(13); 79 80volatile uint16_t ms_counter = 0; 81volatile Internal::DCF77::tick_t tick = Internal::DCF77::undefined; 82 83 84template <bool enable, uint8_t threshold, 85 uint8_t filtered_pin, uint8_t inverted_filtered_pin, uint8_t diff_pin> 86void set_output(uint8_t clock_state, uint8_t sampled_data, uint8_t synthesized_signal) 87{ 88 if (enable) { 89 const uint8_t filtered_output = clock_state < threshold? sampled_data: synthesized_signal; 90 digitalWrite(filtered_pin, filtered_output); 91 digitalWrite(inverted_filtered_pin, !filtered_output); 92 digitalWrite(diff_pin, filtered_output ^ sampled_data); 93 } 94} 95 96namespace { 97 struct Scope { 98 static const uint16_t samples_per_second = 1000; 99 static const uint8_t bins = 100; 100 static const uint8_t samples_per_bin = samples_per_second / bins; 101 102 volatile uint8_t gbin[bins]; 103 volatile boolean samples_pending = false; 104 volatile uint32_t count = 0; 105 106 void process_one_sample(const uint8_t sample) { 107 static uint8_t sbin[bins]; 108 109 static uint16_t ticks = 999; // first pass will init the bins 110 ++ticks; 111 112 if (ticks == 1000) { 113 ticks = 0; 114 memcpy((void *)gbin, sbin, bins); 115 memset(sbin, 0, bins); 116 samples_pending = true; 117 ++count; 118 } 119 sbin[ticks/samples_per_bin] += sample; 120 } 121 122 void print() { 123 uint8_t lbin[bins]; 124 125 if (samples_pending) { 126 noInterrupts(); 127 memcpy(lbin, (void *)gbin, bins); 128 samples_pending = false; 129 interrupts(); 130 131 // ensure the count values will be aligned to the right 132 for (int32_t val=count; val < 100000000; val *= 10) { 133 Serial.print(' '); 134 } 135 Serial.print((int32_t)count); 136 Serial.print(", "); 137 for (uint8_t bin=0; bin<bins; ++bin) { 138 switch (lbin[bin]) { 139 case 0: Serial.print(bin%10? '-': '+'); break; 140 case 10: Serial.print('X'); break; 141 default: Serial.print(lbin[bin]); 142 } 143 } 144 Serial.println(); 145 } 146 } 147 }; 148} 149 150Scope scope_1; 151Scope scope_2; 152 153uint8_t sample_input_pin() { 154 const uint8_t clock_state = DCF77_Clock::get_clock_state(); 155 const uint8_t sampled_data = 156 #if defined(__AVR__) 157 dcf77_inverted_samples ^ (dcf77_analog_samples? (analogRead(dcf77_analog_sample_pin) > 200) 158 : digitalRead(dcf77_sample_pin)); 159 #else 160 dcf77_inverted_samples ^ digitalRead(dcf77_sample_pin); 161 #endif 162 163 digitalWrite(dcf77_monitor_pin, sampled_data); 164 digitalWrite(dcf77_second_pulse_pin, ms_counter < 500 && clock_state >= Clock::locked); 165 166 const uint8_t synthesized_signal = 167 tick == Internal::DCF77::long_tick ? ms_counter < 200: 168 tick == Internal::DCF77::short_tick ? ms_counter < 100: 169 tick == Internal::DCF77::sync_mark ? 0: 170 // tick == DCF77::undefined --> default handling 171 // allow signal to pass for the first 200ms of each second 172 (ms_counter <=200 && sampled_data) || 173 // if the clock has valid time data then undefined ticks 174 // are data bits --> first 100ms of signal must be high 175 ms_counter <100; 176 177 set_output<provide_filtered_output, Clock::locked, 178 dcf77_filtered_pin, dcf77_inverted_filtered_pin, dcf77_filter_diff_pin> 179 (clock_state, sampled_data, synthesized_signal); 180 181 set_output<provide_semi_synthesized_output, Clock::unlocked, 182 dcf77_semi_synthesized_pin, dcf77_inverted_semi_synthesized_pin, dcf77_semi_synthesized_diff_pin> 183 (clock_state, sampled_data, synthesized_signal); 184 185 set_output<provide_synthesized_output, Clock::free, 186 dcf77_synthesized_pin, dcf77_inverted_synthesized_pin, dcf77_synthesized_diff_pin> 187 (clock_state, sampled_data, synthesized_signal); 188 189 ms_counter+= (ms_counter < 1000); 190 191 scope_1.process_one_sample(sampled_data); 192 scope_2.process_one_sample(digitalRead(dcf77_synthesized_pin)); 193 194 return sampled_data; 195} 196 197 198 199 200void output_handler(const Clock::time_t &decoded_time) { 201 // reset ms_counter for 1 Hz ticks 202 ms_counter = 0; 203 204 // status indicator --> always on if signal is good 205 // blink 3s on 1s off if signal is poor 206 // blink 1s on 3s off if signal is very poor 207 // always off if signal is bad 208 const uint8_t clock_state = DCF77_Clock::get_clock_state(); 209 digitalWrite(dcf77_signal_good_indicator_pin, 210 clock_state >= Clock::locked ? 1: 211 clock_state == Clock::unlocked? (decoded_time.second.digit.lo & 0x03) != 0: 212 clock_state == Clock::free ? (decoded_time.second.digit.lo & 0x03) == 0: 213 0); 214 // compute output for signal synthesis 215 Internal::DCF77_Encoder now; 216 now.second = BCD::bcd_to_int(decoded_time.second); 217 now.minute = decoded_time.minute; 218 now.hour = decoded_time.hour; 219 now.weekday = decoded_time.weekday; 220 now.day = decoded_time.day; 221 now.month = decoded_time.month; 222 now.year = decoded_time.year; 223 now.uses_summertime = decoded_time.uses_summertime; 224 now.leap_second_scheduled = decoded_time.leap_second_scheduled; 225 now.timezone_change_scheduled = decoded_time.timezone_change_scheduled; 226 227 now.undefined_minute_output = false; 228 now.undefined_uses_summertime_output = false; 229 now.undefined_abnormal_transmitter_operation_output = false; 230 now.undefined_timezone_change_scheduled_output = false; 231 232 now.advance_minute(); 233 tick = now.get_current_signal(); 234} 235 236void setup_serial() { 237 Serial.begin(115200); 238} 239 240void output_splash_screen() { 241 Serial.println(); 242 Serial.println(F("DCF77 Superfilter 3.0")); 243 Serial.println(F("(c) 2015 Udo Klein")); 244 Serial.println(F("www.blinkenlight.net")); 245 Serial.println(); 246 Serial.print(F("Sample Pin: ")); Serial.println(dcf77_sample_pin); 247 Serial.print(F("Inverted Mode: ")); Serial.println(dcf77_inverted_samples); 248 #if defined(__AVR__) 249 Serial.print(F("Analog Mode: ")); Serial.println(dcf77_analog_samples); 250 #endif 251 Serial.print(F("Monitor Pin: ")); Serial.println(dcf77_monitor_pin); 252 Serial.println(); 253 254 if (provide_filtered_output) { 255 Serial.println(F("Filtered Output")); 256 Serial.print(F(" Filtered Pin: ")); Serial.println(dcf77_filtered_pin); 257 Serial.print(F(" Diff Pin: ")); Serial.println(dcf77_filter_diff_pin); 258 Serial.print(F(" Inverse Filtered Pin: ")); Serial.println(dcf77_inverted_filtered_pin); 259 Serial.println(); 260 } 261 262 if (provide_semi_synthesized_output) { 263 Serial.println(F("Semi Synthesized Output")); 264 Serial.print(F(" Filtered Pin: ")); Serial.println(dcf77_semi_synthesized_pin); 265 Serial.print(F(" Diff Pin: ")); Serial.println(dcf77_semi_synthesized_diff_pin); 266 Serial.print(F(" Inverse Filtered Pin: ")); Serial.println(dcf77_inverted_semi_synthesized_pin); 267 Serial.println(); 268 } 269 270 if (provide_synthesized_output) { 271 Serial.println(F("Synthesized Output")); 272 Serial.print(F(" Filtered Pin: ")); Serial.println(dcf77_synthesized_pin); 273 Serial.print(F(" Diff Pin: ")); Serial.println(dcf77_synthesized_diff_pin); 274 Serial.print(F(" Inverse Filtered Pin: ")); Serial.println(dcf77_inverted_synthesized_pin); 275 Serial.println(); 276 } 277 278 Serial.print(F("Second Pulse Pin: ")); Serial.println(dcf77_second_pulse_pin); 279 Serial.print(F("Signal Good Pin: ")); Serial.println(dcf77_signal_good_indicator_pin); 280 281 Serial.println(); 282 283 Serial.println(); 284 Serial.println(F("Initializing...")); 285 Serial.println(); 286}; 287 288void setup_pins() { 289 if (provide_filtered_output) { 290 pinMode(dcf77_filtered_pin, OUTPUT); 291 pinMode(dcf77_filter_diff_pin, OUTPUT); 292 pinMode(dcf77_inverted_filtered_pin, OUTPUT); 293 } 294 295 if (provide_semi_synthesized_output) { 296 pinMode(dcf77_semi_synthesized_pin, OUTPUT); 297 pinMode(dcf77_semi_synthesized_diff_pin, OUTPUT); 298 pinMode(dcf77_inverted_semi_synthesized_pin, OUTPUT); 299 } 300 301 if (provide_synthesized_output) { 302 pinMode(dcf77_synthesized_pin, OUTPUT); 303 pinMode(dcf77_synthesized_diff_pin, OUTPUT); 304 pinMode(dcf77_inverted_synthesized_pin, OUTPUT); 305 } 306 307 pinMode(dcf77_monitor_pin, OUTPUT); 308 pinMode(dcf77_signal_good_indicator_pin, OUTPUT); 309 pinMode(dcf77_second_pulse_pin, OUTPUT); 310 pinMode(dcf77_sample_pin, INPUT); 311 digitalWrite(dcf77_sample_pin, HIGH); 312} 313 314void setup_clock() { 315 DCF77_Clock::setup(); 316 DCF77_Clock::set_input_provider(sample_input_pin); 317 DCF77_Clock::set_output_handler(output_handler); 318} 319 320void setup() { 321 setup_serial(); 322 output_splash_screen(); 323 setup_pins(); 324 setup_clock(); 325/* 326 pinMode(gnd_pin, OUTPUT); 327 digitalWrite(gnd_pin, LOW); 328 pinMode(pon_pin, OUTPUT); 329 digitalWrite(pon_pin, LOW); 330 pinMode(vcc_pin, OUTPUT); 331 digitalWrite(vcc_pin, HIGH); 332 */ 333} 334 335void loop() { 336 Clock::time_t now; 337 DCF77_Clock::get_current_time(now); 338 339 if (now.month.val > 0) { 340 Serial.println(); 341 Serial.print(F("Decoded time: ")); 342 343 DCF77_Clock::print(now); 344 Serial.println(); 345 } 346 347 Serial.print(DCF77_Clock::get_clock_state()); 348 Serial.print(' '); 349 DCF77_Clock::debug(); 350 351 scope_1.print(); 352 scope_2.print(); 353} 354
DCF Analyzer / Clock v2.1
arduino
2020-01-18 bug release: power save switch fixed
1/* 2 ================================================================================ 3 DCF77 Analyzer / Clock version 2 4 ================================================================================ 5 This sketch is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This sketch is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 ================================================================================ 19 20 This C++ code is far from optimized because I myself am an Arduino and C++ novice. 21 But even after learning some more now, I want to keep the code simpel and readable. 22 That is why I maybe over-documented the code to help understand what's going on. 23 24 Erik de Ruiter 25 2014-2020 26 27 28 29 May 2014 First version 30 March 2016 - big overhaul... 31 July 2016 - Start with building the 2.0 Clock and adapting the sketch 32 33 Version 2.1 date 2020-01-18 34 - Powersafe function fixed. 35 36 Version 2.0 37 - This sketch is adapted for my 2.0 version of the DCF/Analyzer Clock. It used the Arduino MEGA and the DCF Superfilter 38 by default and to drive the many seperate LED's I now use the ports of an Arduino Mega instead of a Maxim 7219 chip. 39 This is because driving LED's with many different Voltage/Current specs is problematic with the Maxim chip. 40 Lighting additional LED's for expample will influence (dim) the LED's already on. As I'm not an electronics engineer 41 my only solution was to use the extra ports of the Arduino Mega. Ofcourse you can use transistors or extra chips to 42 drive the LED's but for me this was the obvious solution. 43 - Removed all the Maxim Common Anode display code 44 45 46 Version 1.72 47 - Option: Use a cheap Ebay PIR detector to shut off selectable display's when no activity is detected. 48 The switch off delay can be set by the user to prevent the display shutting of if a person 49 is not moving but the display should be on. 50 - Now the display Night shut-down can be disabled by making both values 'POWERSAVINGOFFTIME' 51 and 'POWERSAVINGONTIME' zero. 52 - Fixed temperature display not shutting off at powersave mode. 53 - errorCounter display did not reset every hour -fixed 54 55 Version 1.71 56 - User option to reset temperature min/max memory at midnight 57 58 Version 1.7: 59 - The resolution of the temperature display is improved: from 0.5 to 0.1 degrees Celsius 60 Because of the time the DS18B20 sensor needs to convert the temperature and to keep the code clean, 61 the temperature display is updates once per minute. 62 - Parity check routine optimized. 63 - More reliable check for bad DCF data, preventing RTC update with invalid data. 64 - EoB error now clears inner LED ring as it should. 65 - The DCF OK LED now displays the condition of the DCF signal more reliably. Turns off immediately if an error occurs 66 and only turns ON when all 3 parity bits are OK. 67 68 Version 1.6: 69 - Changed temperature function to only calculate once per minute. Got strange errors before the change because 70 I used a delay of 100ms to give the DS18B20 sensor time to calculate the temperature. But the delay function is 71 a very bad idea in most c++ code so I finally got rid of it. 72 73 Version 1.5: 74 - Complete overhaul of the scanSignal function and the rest of the code! My first attempt worked but could be improved... 75 - The rPW and rPT led's did not work as I intended so that is corrected now. 76 - The End of Buffer error check routine does work now as it should. 77 - I incorporated a Parity check of the incoming DCF signal. In the signal 3 Parity bits are sent so now these are 78 checked and only if all three are OK, the received time information is accepted, the display is updated and the RTC synced. 79 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. 80 - I made wiring (or changing the wiring) much easier I think by putting all the PIN config in one easy to read table 81 - 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. 82 - Big clean-up of the code... 83 - Powersaving by shutting off the displays (the clock remains functioning as normal) 84 can now be configured somewhat easier by editing two variables POWERSAVINGONTIME and POWERSAVINGOFFTIME. 85 - changed some variable names: 86 - Maxim instances 'lc' and 'lc1' are now MaximCC and MaximCA 87 - Display description MaximDcfTime is now DisplayTempWeek 88 - DCF77SOUNDPIN is now BUZZERSWITCHPIN 89 - LED/Display test after power up now build in 90 91 92 93 Short description: 94 95 Power On: 96 After power-on, first a LED test is performed. The LED's and displays lite up sequentially to keep the power consumption low. 97 Then the clock starts receiving DCF pulses and when a Minute Mark (2 seconds gap) is detected, the Minute Marker LED is lit 98 and the buffer counter is reset. The inner LED ring now will show the incoming DCF pulses which are also stored in the buffer. 99 At 3 moments during reception of data the parity DCF bits are checked to see if the data is valid. 100 101 Valid data received: 102 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 103 ('DCF OK' LED is lit), the buffer information is used to extract time and date information. 104 Then the RTC clock is updated ('RTC Synced' LED is lit) and the inner LED ring information is copied to the outer LED ring. 105 The time, date and week display, day LED, summer/wintertime and leap year LED information is updated with the new time information. 106 107 No valid data: 108 When one or more of the parity bits are not OK because of a noisy signal, receiving of DCF information is continued but 109 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. 110 Time, date, week, day LED, summer/wintertime LED and leap year LED are not affected and keep displaying the last received valid values. 111 The 'Period Time' and/or 'Period With' error LED's will indicate the error(s) and the error counter display is updated. 112 Every hour, the error display will bet set to zero. 113 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. 114 (When a minute Mark is detected we should have no more than 58 bits/pulses) 115 After the detection of the Minute Marker, a new cycle is started. 116 117 Temperature: 118 At the 30 second mark, the temperature display will show the High and Low values of the past period after the last reset. 119 120 Chime: 121 If the CHIME switch is ON/HIGH, then at the beginning of each hour, the Chime (if connected) will sound 122 At night time, a time set by the user in the code itself, the chime is disabled. 123 124 Power saving - display's shut off 125 This will only work IF the Power Save switch is HIGH: 126 1. NIGHT SHUT OFF 127 At times set by the user, the displays are shutt off at night and turned on in the morning. 128 Look at the POWERSAVINGOFFTIME and POWERSAVINGONTIME variables. 129 Check the function <turnDisplaysOff> to select WHICH displays you want to shut off at night. 130 2. PIR SENSOR 131 Connect a PIR sensor and activate the PIR option POWERSAVE_BY_PIR and the the delay at PIR_DELAY_TIME. 132 Every time the PIR detector senses movement, a minute counter is reset but if no movement is detected 133 longer than the PIR_DELAY_TIME, the displays are shut off. 134 When movement occurs, the displays immediately switch on. 135 Note: as said before, the clock will function normally while the displays are shut off. 136 137 DCF beep: 138 With a switch, connected to pin BUZZERSWITCHPIN, you can hear the received DCF bits coming in. 139 The tone duration is equivalent to pulse width of the DCF bits, so either 100 or 200 ms. 140 141 Miscelleanous: 142 When the RTC battery is empty or a connection fault is detected, the RTC Error LED is lit. 143 144 145 146 147 CREDITS: 148 I learned a lot from the work of Matthias Dalheimer and Thijs Elenbaas who made their own DCF77 decoders. 149 Without their work I would not have known where to start. 150 I ended up writing my own code (using bits and pieces of their ideas) so I could understand what is happening... 151 My code is far from efficient or advanced but it does work and I know what is going on. 152 153 Interesting websites: 154 155 - Brett Oliver : http://home.btconnect.com/brettoliver1/ 156 - Joop Tap : http://www.jooptap.nl 157 - Thijs Ellenbaas : http://thijs.elenbaas.net/2012/04/arduino-dcf77-radio-clock-receiver-hardware-2/ 158 - Mathias Dalheimer : https://github.com/roddi/DCF77-Arduino/blob/master/DCF77Servoclock/DCF77.h 159 - DCF77 wikipedia : https://en.wikipedia.org/wiki/DCF77 160 - Much more DCF77 info : http://www.picbasic.nl/indexes_uk.htm 161 162 - My Flickr website : https://www.flickr.com/photos/edr1924/albums 163 - My Github website : https://github.com/deruiter 164 165 */ 166 167//---------------------------------------------------------------------------------------------------------- 168// Libraries 169//---------------------------------------------------------------------------------------------------------- 170 171// Arduino (new) Time library .................................... http://www.pjrc.com/teensy/td_libs_Time.html 172#include <Time.h> 173 174// Enable this line if using Arduino Uno, Mega, etc. 175#include <Wire.h> 176 177// a basic DS1307 library that returns time as a time_t .......... http://www.pjrc.com/teensy/td_libs_DS1307RTC.html 178#include <DS1307RTC.h> 179 180// Maxim 7219 displays library ................................... http://playground.arduino.cc/Main/LEDMatrix 181// !!! NOTE: you must use a special version of the Ledcontrol.h library to get Common Anode support 182// because the Maxim chip is normally only suitable for common CATHODE displays! 183#include <LedControl.h> 184 185//SPI interface library .......................................... http://arduino.cc/en/Reference/SPI 186#include <SPI.h> 187 188// OneWire lets you access 1-wire devices made by Maxim/Dallas, 189// such as DS18S20, DS18B20, DS1822 .............................. http://www.pjrc.com/teensy/td_libs_OneWire.html 190// The DallasTemperature library can do all this work for you! ... http://milesburton.com/Dallas_Temperature_Control_Library 191#include <OneWire.h> 192 193//---------------------------------------------------------------------------------------------------------- 194// Arduino UNO Pin connections in an easy to read table 195// 196// input - Rx - used for programming/communication with PC 197// output - Tx - used for programming/communication with PC 198#define DCF77PIN 2 // input - DCF signal from antenna pcb. Pin must an interrupt input! 199#define PIRDETECTORPIN 3 // input - PIR detector: check for activity in the room to activate displays 200#define BUZZERSWITCHPIN 4 // input - SWITCH - turn on/off DCF77 'beep' piezo buzzer / ON = HIGH, OFF = LOW 201#define CHIMESWITCHPIN 5 // input - SWITCH - turn on/off the hourly chime sound / ON = HIGH, OFF = LOW 202#define POWERSAVESWITCHPIN 6 // input - SWITCH - turn on/off the power save feature so display is always on / ON = HIGH, OFF = LOW 203#define TEMPSENSORPIN 8 // input - Dallas One Wire DS18B20 temperature sensor 204#define TEMPRESETPIN 9 // input - PUSH BUTTON - reset temperature min/max memory / HIGH = reset 205#define MAXIMCCLD 10 // output - CS/LOAD - pseudo SPI connection to the Maxim 7219 chip - 7 segment displays 206#define MAXIMCCCLK 11 // output - CLOCK - pseudo SPI connection to the Maxim 7219 chip - 7 segment displays 207#define MAXIMCCDATA 12 // output - DATA - pseudo SPI connection to the Maxim 7219 chip - 7 segment displays 208// !! Pins 22 through 53 are only to be used for LED's 209#define LED_SUNDAY 22 // output - LED - Sunday 210#define LED_MONDAY 23 // output - LED - Monday 211#define LED_TUESDAY 24 // output - LED - Tuesday 212#define LED_WEDNESDAY 25 // output - LED - Wednesday 213#define LED_THURSDAY 26 // output - LED - Thursday 214#define LED_FRIDAY 27 // output - LED - Friday 215#define LED_SATURDAY 28 // output - LED - Saturday 216#define LED_CEST 29 // output - LED - Summertime CEST 217#define LED_CET 30 // output - LED - Wintertime CET 218#define LED_LEAPYEAR 31 // output - LED - Leap year 219#define LED_RTCERROR 32 // output - LED - problem reading RTC data (empty battery/connection) 220#define LED_RTCSYNC 33 // output - LED - On when RTC is succesfully synced with the DCF time 221#define LED_TEMP 34 // output - LED - temperature is displayed 222#define LED_OPTION1 35 // output - LED - optional 1 data is displayed 223#define LED_OPTION2 36 // output - LED - optional 2 data is displayed 224#define LED_ERRORPT 37 // output - LED - DCF Period Time error 225#define LED_ERRORPW 38 // output - LED - DCF Period Width error 226#define LED_BUFFERFULL 39 // output - LED - Buffer full indicator, next the data will be analized 227#define LED_MINUTEMARKER 40 // output - LED - End of DCF data stream detected before buffer is filled, data is corrupt 228#define LED_BUFFEROVERFLOW 41 // output - LED - More data received in one minute than expected due to bad signal 229#define LED_DCFSTATUS 42 // output - LED - On when we have good DCF data 230#define LED_POWERSAVE 43 // output - LED - Power save mode is activated, some displays are off 231#define LED_PARITY1PASS 44 // output - LED - Parity 1 bit is OK 232#define LED_PARITY1FAIL 45 // output - LED - Parity 1 bit FAILED 233#define LED_PARITY2PASS 46 // output - LED - Parity 2 bit is OK 234#define LED_PARITY2FAIL 47 // output - LED - Parity 2 bit FAILED 235#define LED_PARITY3PASS 48 // output - LED - Parity 3 bit is OK 236#define LED_PARITY3FAIL 49 // output - LED - Parity 3 bit FAILED 237#define LED_PIRMOTION 50 // output - LED - On when PIR is detecting motion 238 239// Analog pins 240#define BUZZER A7 // output - Piezo buzzer for DCF77 'beep' (to '+' of the buzzer) 241#define SPEAKERVOLPIN A6 // output - Sound Board volume - LOW = volume one notch lower. SPEAKERVOLUME determines how many times this output is activated after power on 242#define CHIMEPIN A5 // output - Chime Activate - OUTPUT LOW = Activate Chime on Adafruit Soundboard FX 243// USED for DS1307 RTC // I2C DATA - connect to Real Time Clock pcb 244// USED for DS1307 RTC // I2C CLOCK - connect to Real Time Clock pcb 245 246//---------------------------------------------------------------------------------------------------------- 247// DS18B20 initialization 248//---------------------------------------------------------------------------------------------------------- 249OneWire ds(TEMPSENSORPIN); // define Onewire instance DS 250 251//---------------------------------------------------------------------------------------------------------- 252// Maxim 7219 Matrix Display initialization 253//---------------------------------------------------------------------------------------------------------- 254/* 255 clearDisplay(int addr) ............................................. clears the selected display 256 MaximCC.shutdown(int addr, boolean) ................................ wake up the MAX72XX from power-saving mode (true = sleep, false = awake) 257 MaximCC.setIntensity(int addr, value) .............................. set a medium brightness for the Leds (0=min - 15=max) 258 MaximCC.setLed(int addr, int row, int col, boolean state) .......... switch on the led in row, column. remember that indices start at 0! 259 MaximCC.setRow(int addr, int row, byte value) ...................... this function takes 3 arguments. example: MaximCC.setRow(0,2,B10110000); 260 MaximCC.setColumn(int addr, int col, byte value) ................... this function takes 3 arguments. example: MaximCC.setColumn(0,5,B00001111); 261 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. 262 The range of valid values runs from 0..15. All values between 0..9 are printed as digits, 263 values between 10..15 are printed as their hexadecimal equivalent 264 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) 265 POWERSAVESWITCHPIN 266 ***** Please set the number of devices you have ***** 267 But the maximum default of 8 MAX72XX wil also work. 268 LedConrol(DATAIN, CLOCK, CS/LOAD, NUMBER OF MAXIM CHIPS) 269 */ 270 271// lc is for the Maxim displays 272LedControl MaximCC = LedControl(MAXIMCCDATA, MAXIMCCCLK, MAXIMCCLD, 7, false); // Define pins for Maxim 72xx and how many 72xx we use 273 274//---------------------------------------------------------------------------------------------------------- 275// User settings, variable and array definitions 276//---------------------------------------------------------------------------------------------------------- 277 278// 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 279// so that way the volume of the sound board can be lowered after power up, if desired. 280#define SPEAKERVOLUME 12 281 282// Choose if you want a test of all LED's and Displays after a startup 283// '1' = Yes, '0' = No 284#define PERFORM_LED_TEST 1 285// Delay between each 7 segment display in ms 286#define LEDTEST_DELAY_DISPLAYS 600 287// Delay between each LED in the LED ring and other LED's in ms 288#define LEDTEST_DELAY_LED_RING 20 289 290// Choose if you want to configure the DS18B20 temperature sensor ONCE to the highest resolution. 291// this is needed after using the sensor for the first time. After running the software 292// with this setting ON one time, shut it off. 293// '1' = ON, '0' = OFF 294#define CONFIGURE_DS18B20 0 295 296// POWERSAVE TIME - MANDATORY values! 297// 298// to define day and night time. This is used for power saving display ON and OFF time 299// AND to determine if the chime will be activated (during the daytime). 300// 301// ONLY the displays are shut off at power saving time, the clock remains fully active. 302// TO DISABLE the display power-off at any time, simply set the Power-save switch to OFF 303// 304// Power save switch ON: displays will be shutt OFF at the set night time 305// AND if POWERSAVE_BY_PIR function if activated and there is no 306// movement for the set PIR_DELAY_TIME. 307// Power save switch OFF: displays always on, chime only at daytime. 308// 309// values are in 'Hour' format, so 8PM will be '20', NOT 20:00 or 8PM... 310#define POWERSAVINGOFFTIME 9 // displays are activated 311#define POWERSAVINGONTIME 22 // displays are shutt off 312 313// User option: activate the displays only when there is activity in the room 314// '1' = ON, '0' = OFF 315#define POWERSAVE_BY_PIR 1 316// delay in MINUTES to wait after no detection before shutting off the displays 317#define PIR_DELAY_TIME 30 318 319// User option to reset temperature min/max memory at midnight 320// '1' = Reset at midnight, '0' = Only manual reset 321#define TEMPRESET_MIDNIGHT 1 322 323//------------------------------------------------------------------------------- 324// define miscellaneous parameters 325#define DS1307_I2C_ADDRESS 0x68 // define the RTC I2C address 326#define DCF_INTERRUPT 0 // Interrupt number associated with pin 327 328// definition of Maxim 7219 display number wiring sequence 329// first Maxim 7219 in wiring 'daisychain' must be '0', next '1' etc. 330// COMMON CATHODE DISPLAYS 331#define LedRingInner 0 332#define LedRingOuter 1 333#define DisplayBufferBitError 2 334#define DisplayPeriodPulse 3 335#define DisplayTempWeek 4 336#define DisplayDate 5 337#define DisplayTime 6 338 339// definition of display brighness levels 340#define BrightnessLedRingOuter 1 341#define BrightnessLedRingInner 1 342#define BrightnessDisplayTime 1 343#define BrightnessDisplayDate 7 344#define BrightnessDisplayTempWeek 15 345#define BrightnessDisplayPeriodPulse 2 346#define BrightnessDisplayBufferBitError 15 347 348// Pulse flanks 349static unsigned long leadingEdge = 0; 350static unsigned long trailingEdge = 0; 351unsigned long previousLeadingEdge = 0; 352 353// used in <Int0handler> 354volatile unsigned int DCFSignalState = 0; // interrupt variables ALWAYS need volatile qualifier!! 355 356// used in <loop> 357int previousSecond = 0; 358unsigned int previousSignalState = 0; 359 360// DCF Buffers and indicators 361static int DCFbitBuffer[59]; // here, the received DCFbits are stored 362const int bitValue[] = {1, 2, 4, 8, 10, 20, 40, 80}; // these are the decimal values of the received DCFbits 363 364// only after start on a new minute, display received bits on inner LED ring 365boolean MinuteMarkerFlag = false; 366int bufferPosition = 0; 367int previousMinute = 0; 368int previousHour = 0; 369 370// variables to check if DCF bits are vald 371bool dcfValidSignal = false; 372int dcfP1counter = 0; 373int dcfP2counter = 0; 374int dcfP3counter = 0; 375int dcfParityCheckP1 = 0; 376int dcfParityCheckP2 = 0; 377int dcfParityCheckP3 = 0; 378 379// dcf variables to store decoded DCF time in 380int dcfMinute = 0; 381int dcfHour = 0; 382int dcfDay = 0; 383int dcfWeekDay = 0; 384int dcfMonth = 0; 385int dcfYear = 0; 386int dcfDST = 0; 387int leapYear = 0; 388 389// variables used to store weeknumber and daynumer values 390int dayNumber; 391int weekNumber; 392 393// error counter variable 394int errorCounter = 0; 395boolean errorCondition = false; 396 397// miscelleanous variables 398boolean daytimeChange = true; 399boolean dayTime = false; 400int dcf77SoundSwitch = 0; 401 402// temperature variables 403byte present = 0; 404byte DS18B20Data[12]; 405int maxTemp = 0; 406int minTemp = 0; 407int lowByte = 0; 408int highByte = 0; 409float tempReading = 0; 410int tempCelsius = 0; 411boolean tempResetButton = false; 412 413// PIR detector variables 414int pirActivity = 0; 415int pirDisplaysState = 1; 416unsigned int pirTimer = 0; 417unsigned long previousTimePIR = 0; 418 419//============================================================================== 420// SETUP 421//============================================================================== 422void setup() 423{ 424 // initialize Serial communication 425 Serial.begin(9600); 426 427 // initialize PIN connections 428 pinMode(DCF77PIN, INPUT); 429 pinMode(TEMPRESETPIN, INPUT); 430 pinMode(BUZZERSWITCHPIN, INPUT); 431 pinMode(CHIMESWITCHPIN, INPUT); 432 pinMode(POWERSAVESWITCHPIN, INPUT); 433 pinMode(PIRDETECTORPIN, INPUT); 434 pinMode(CHIMEPIN, OUTPUT); 435 pinMode(SPEAKERVOLPIN, OUTPUT); 436 // initialize LED pins 22 - 50 437 for (int i1 = 22; i1 <= 50; i1++) 438 { 439 pinMode(i1, OUTPUT); 440 } 441 442 // Initialize variables, LED displays and LED's 443 initialize(); 444 445 // Initialize DCF77 pulse interrupt on pin DCF_INTERRUPT, looking for a change of the signal, 446 // so either rising or falling edge pulses will trigger the interrupt handler and 447 // execute the int0handler function. 448 attachInterrupt(DCF_INTERRUPT, int0handler, CHANGE); 449 450 // Initialize RTC and set as SyncProvider. 451 // Later RTC will be synced with DCF time 452 setSyncProvider(RTC.get); // the function to get the time from the RTC 453 // check if RTC has set the system time 454 if (timeStatus() != timeSet) 455 { // Unable to sync with the RTC - activate RTCError LED 456 digitalWrite(LED_RTCERROR, HIGH); 457 } 458 else 459 { 460 // RTC has set the system time - dim RTCError LED 461 digitalWrite(LED_RTCERROR, LOW); 462 } 463 464 // After power on, set the speaker volume of the Adafruit Audio Board 465 // initialize both pins to LOW which is the default output state 466 digitalWrite(SPEAKERVOLPIN, LOW); 467 digitalWrite(CHIMEPIN, LOW); 468 469 // lower soundboard default volume with 'SPEAKERVOLUME' steps 470 for (int i = 0; i <= SPEAKERVOLUME; i++) 471 { 472 digitalWrite(SPEAKERVOLPIN, HIGH); 473 delay(100); 474 digitalWrite(SPEAKERVOLPIN, LOW); 475 delay(100); 476 } 477 478 // The following function should run only once. 479 // It is used to configure the temperature resolution of the DS18B20 sensor 480 if (CONFIGURE_DS18B20 == 1) 481 { 482 configureDS18B20(); 483 } 484 485 // use for test purposes and/or setting the RTC time manually 486 // setTime(23, 59, 40, 31, 12, 13); 487 // RTC.set(now()); 488 489 // Request the temperature conversion 490 calculateTemp(); 491 492 // check if a LED test is needed 493 if (PERFORM_LED_TEST == 1) 494 { 495 // do a LED test 496 ledTest(); 497 } 498 else 499 { 500 // if not doing a LED test, we need to wait a bit for the DS18B20 sensor to get ready 501 delay(750); 502 } 503 504 // Now get the temperature from the sensor and display it 505 displayTemp(); 506 507 // activate errorCounter display after LED test 508 ledDisplay(DisplayBufferBitError, "R", 0); 509} 510 511//============================================================================== 512// LOOP 513//============================================================================== 514void loop() 515{ 516 // check first if pulse direction is changed (rising or falling) 517 // else we would keep evaluating the same pulse 518 if (DCFSignalState != previousSignalState) 519 { 520 // 'reset' state of variable 521 previousSignalState = DCFSignalState; 522 523 // evaluate incoming pulse 524 scanSignal(); 525 } 526 527 // check if switches are changed and act upon it 528 checkSwitches(); 529 530 // check for PIR movement 531 checkPIR(); 532 533 // execute tasks that must happen only once every second, minute or hour 534 //---------------------------------------------------------------------------- 535 tasksEverySecond(); 536 tasksEveryMinute(); 537 tasksEveryHour(); 538} 539 540//================================================================================================================ 541// 542// Function name : processDcfBit 543// called from : <scanSignal> 544// 545// Purpose : Evaluates the signal as it is received. Decides whether we received a "1" or a "0" 546// and perform checks to see if the pulse timing is within limits 547// Parameters : none 548// Return value : none 549// 550//================================================================================================================ 551/* 552 pulse pulse 553 width width 554 |- -| |-- --| |----- END OF MINUTE marker:2000ms -----| 555 ___ _______ ___ ___ _______ 556 | 0 | | 1 | | 0 | | 0 | | 1 | 557 | | | | | | | | | | 558 | | | | | | | | | | 559 ______| |_______________| |___________| |___________________________________| |_______________| |__ _ _ _ 560 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 561 1000 2100 2000 2200 3000 3100 NO PULSE 5000 5100 6000 6200 << example millis() value 562 = end of Minute indication 563 ^ ^ ^ ^ ^ 564 DCFbit# 56 DCFbit# 57 DCFbit# 58 DCFbit# 0 DCFbit# 1 etc... << DCF bit received 565 566 ^ ^ ^ 567 previous leading trailing 568 leading edge edge edge 569 570 ^ ^ 571 flanktime (rising or falling) 572 573 */ 574 575void scanSignal() 576{ 577 //-------------------------------------------------------------------- 578 // Check for Rising-Edge signal and perform checks 579 //-------------------------------------------------------------------- 580 if (DCFSignalState == 1) 581 { 582 // store Rising-Edge Time to check later if the time between two pulses is valid 583 leadingEdge = millis(); 584 // not much to do now so exit. 585 return; 586 } 587 588 //-------------------------------------------------------------------- 589 // Check for Falling-Edge signal and perform checks 590 //-------------------------------------------------------------------- 591 592 if (DCFSignalState == 0) 593 { 594 // store Trailing-Edge Time to check later if the Pulse Width is valid 595 trailingEdge = millis(); 596 597 // display period width time on "L"eft side of the 8 digit Maxim 72xx LED display 598 ledDisplay(DisplayPeriodPulse, "L", (leadingEdge - previousLeadingEdge)); 599 // display pulse width time on the "R"ight side of the 8 digit Maxim 72xx LED display 600 ledDisplay(DisplayPeriodPulse, "R", (trailingEdge - leadingEdge)); 601 602 //-------------------------------------------------------------------------------- 603 // Check PERIOD TIME 604 //-------------------------------------------------------------------------------- 605 // If this flank UP is detected quickly after previous flank UP this is an incorrect 606 // Period Time (should be 1000ms -or 2000ms after second 58-) that we shall reject 607 if ((leadingEdge - previousLeadingEdge) < 900) 608 { 609 // rPW - ERROR: Periode Time (rising flank to rising flank) time is too short -> REJECTED 610 error(LED_ERRORPW); 611 errorCondition = true; 612 } 613 //-------------------------------------------------------------------------------- 614 // CHECK PULSE TIME 615 //-------------------------------------------------------------------------------- 616 // If the detected pulse is too short it will be an incorrect pulse that we shall reject 617 // should be 100 and 200 ms ideally 618 if (((trailingEdge - leadingEdge) < 70) || ((trailingEdge - leadingEdge) > 230)) 619 { 620 //rPT - ERROR: Pulse Width too short or too long -> REJECTED 621 error(LED_ERRORPT); 622 errorCondition = true; 623 } 624 625 // if we had an error return and start over 626 if (errorCondition == true) 627 { 628 errorCondition = false; 629 // although we have an error, store current rising edge time to compare at the next Rising-Edge. 630 previousLeadingEdge = leadingEdge; 631 return; 632 } 633 634 //-------------------------------------------------------------------- 635 // no errors found so now we can continue 636 //-------------------------------------------------------------------- 637 638 // first we turn any error Led's OFF 639 digitalWrite(LED_ERRORPW, LOW); 640 digitalWrite(LED_ERRORPT, LOW); 641 digitalWrite(LED_BUFFERFULL, LOW); // previous BF 642 digitalWrite(LED_BUFFEROVERFLOW, LOW); // previous EoB 643 digitalWrite(LED_MINUTEMARKER, LOW); // previous EoM 644 645 // END OF MINUTE check, looking for a gap of approx. 2000ms 646 if (leadingEdge - previousLeadingEdge > 1900 && leadingEdge - previousLeadingEdge < 2100) 647 { 648 // end of minute detected: 649 finalizeBuffer(); 650 } 651 652 // refresh previousLeadingEdge time with the new leading edge time 653 previousLeadingEdge = leadingEdge; 654 655 //-------------------------------------------------------------------------------- 656 // process DCF bits 657 //-------------------------------------------------------------------------------- 658 // distinguish between long and short pulses 659 if (trailingEdge - leadingEdge < 170) 660 { 661 // call processDcfBit function and sent it the value '0' 662 processDcfBit(0); 663 // if switch is HIGH, the DCF pulses are audible 664 if (dcf77SoundSwitch == 1) 665 buzzer(100); 666 } 667 else 668 { 669 // call processDcfBit function and sent it the value '1' 670 processDcfBit(1); 671 // if switch is HIGH, the DCF pulses are audible 672 if (dcf77SoundSwitch == 1) 673 buzzer(200); 674 } 675 } // if (DCFSignalState == 0) 676} // void scanSignal(); 677 678//================================================================================================================ 679// 680// Function name : processDcfBit 681// called from : <scanSignal> 682// 683// Purpose : after reception of one good DCF bit, do some checks and save it in the DCFbitBuffer array 684// Parameters : none 685// Return value : none 686// 687//================================================================================================================ 688 689void processDcfBit(int dcfBit) 690{ 691 //-------------------------------------------------------------------- 692 // display values on the 7 segment displays 693 //-------------------------------------------------------------------- 694 // display bufferPosition, digits 7,6 695 MaximCC.setChar(DisplayBufferBitError, 7, bufferPosition / 10, false); 696 MaximCC.setChar(DisplayBufferBitError, 6, bufferPosition % 10, false); 697 698 // display received DCFbit, digit 4 699 MaximCC.setChar(DisplayBufferBitError, 4, dcfBit, false); 700 701 //-------------------------------------------------------------------- 702 // display incoming DCF bits on inner LED ring 703 //-------------------------------------------------------------------- 704 // only if we have valid DCF data or after an Minute Mark (EoM) signal 705 // activate the inner LED ring and diplay incoming data 706 if (dcfValidSignal == true || MinuteMarkerFlag == true) 707 { 708 // display received bits on inner LED ring 709 MaximCC.setLed(LedRingInner, bufferPosition / 8, bufferPosition % 8, dcfBit); 710 } 711 712 //-------------------------------------------------------------------- 713 // // Fill DCFbitBuffer array with DCFbit 714 //-------------------------------------------------------------------- 715 DCFbitBuffer[bufferPosition] = dcfBit; 716 717 //-------------------------------------------------------------------- 718 // Parity check 719 //-------------------------------------------------------------------- 720 // DURING reception of the DCF bits, calculate and display the results of the DCF parity check. 721 // 722 // There is a Parity bit for the minutes, the hours and for the date. 723 // DCF77 works with EVEN parity, this works as follows: 724 // The hours for example have 6 bits plus a paritybit. The bits with value 1 are add up including the paritybit, 725 // 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, 726 // then the result is uneven. source: http://www.picbasic.nl/frameload_uk.htm?http://www.picbasic.nl/info_dcf77_uk.htm 727 728 if (bufferPosition == 0) 729 { 730 // reset the parity LED's 731 digitalWrite(LED_PARITY1PASS, LOW); 732 digitalWrite(LED_PARITY1FAIL, LOW); 733 digitalWrite(LED_PARITY2PASS, LOW); 734 digitalWrite(LED_PARITY2FAIL, LOW); 735 digitalWrite(LED_PARITY3PASS, LOW); 736 digitalWrite(LED_PARITY3FAIL, LOW); 737 // reset variables 738 dcfP1counter = 0; 739 dcfP2counter = 0; 740 dcfP3counter = 0; 741 dcfParityCheckP1 = 0; 742 dcfParityCheckP2 = 0; 743 dcfParityCheckP3 = 0; 744 } 745 746 // ---------------------------------------- 747 // First parity check: minute bits 748 // ---------------------------------------- 749 if (bufferPosition == 28) 750 { 751 for (int i = 21; i <= 27; i++) 752 { 753 // count the number of bits with the value '1' 754 dcfP1counter += DCFbitBuffer[i]; 755 } 756 757 // perform P1 parity check. Parity is OK if the sum is an EVEN value 758 if ((DCFbitBuffer[28] + dcfP1counter) % 2 == 0) 759 { 760 // Parity1 PASS LED ON 761 digitalWrite(LED_PARITY1PASS, HIGH); 762 // Parity P1 PASS 763 dcfParityCheckP1 = 1; 764 } 765 else 766 { 767 // Parity1 FAIL LED ON 768 digitalWrite(LED_PARITY1FAIL, HIGH); 769 // we have no valid data! 770 dcfValidSignal = false; 771 // Turn DCF OK LED OFF 772 digitalWrite(LED_DCFSTATUS, LOW); 773 } 774 } 775 776 // ---------------------------------------- 777 // Second parity check: hour bits 778 // ---------------------------------------- 779 if (bufferPosition == 35) 780 { 781 for (int i = 29; i <= 34; i++) 782 { 783 dcfP2counter += DCFbitBuffer[i]; 784 } 785 786 // perform P2 parity check. Parity is OK if the sum is an EVEN value 787 if ((DCFbitBuffer[35] + dcfP2counter) % 2 == 0) 788 { 789 // Parity2 PASS LED ON 790 digitalWrite(LED_PARITY2PASS, HIGH); 791 // Parity P2 PASS 792 dcfParityCheckP2 = 1; 793 } 794 else 795 { 796 // Parity2 FAIL LED ON 797 digitalWrite(LED_PARITY2FAIL, HIGH); 798 // we have no valid data! 799 dcfValidSignal = false; 800 // Turn DCF OK LED OFF 801 digitalWrite(LED_DCFSTATUS, LOW); 802 } 803 } 804 805 // ---------------------------------------- 806 // Third parity check: date bits 807 // ---------------------------------------- 808 if (bufferPosition == 58) 809 { 810 for (int i = 36; i <= 57; i++) 811 { 812 dcfP3counter += DCFbitBuffer[i]; 813 } 814 // perform P3 parity check. Parity is OK if the sum is an EVEN value 815 (DCFbitBuffer[58] + dcfP3counter) % 2 == 0 ? dcfParityCheckP3 = 1 : dcfParityCheckP3 = 0; 816 817 // Turn Parity2 'PASS' or 'FAIL' LED ON 818 if (dcfParityCheckP3 == 1) 819 { 820 // Parity2 PASS LED ON 821 digitalWrite(LED_PARITY3PASS, HIGH); 822 // Parity P3 PASS 823 dcfParityCheckP3 = 1; 824 } 825 else 826 { 827 // Parity2 FAIL LED ON 828 digitalWrite(LED_PARITY3FAIL, HIGH); 829 // we have no valid data! 830 dcfValidSignal = false; 831 // Turn DCF OK LED OFF 832 digitalWrite(LED_DCFSTATUS, LOW); 833 } 834 835 // ---------------------------------------- 836 // finally, check all Parity bits 837 // ---------------------------------------- 838 dcfParityCheckP1 + dcfParityCheckP2 + dcfParityCheckP3 == 3 ? dcfValidSignal = true : dcfValidSignal = false; 839 } 840 841 //-------------------------------------------------------------------- 842 // before continuing with the next bit, increment counter 843 //-------------------------------------------------------------------- 844 bufferPosition++; 845 846 //-------------------------------------------------------------------- 847 // check if we have not received too many pulses? 848 //-------------------------------------------------------------------- 849 if (bufferPosition > 59) 850 { 851 // Buffer Overflow ERROR - we have received more pulses before reaching 852 // the 2 second 'gap' signalling the end of the minute. 853 //This error may be due to a noisy signal giving addition peaks/dcfBits 854 // So clear both DCFbit displays and start again. 855 856 // Reset buffer counter 857 bufferPosition = 0; 858 // clear inner LED ring 859 MaximCC.clearDisplay(LedRingInner); 860 // turn Buffer Overflow Error LED ON 861 error(LED_BUFFEROVERFLOW); 862 // exit 863 return; 864 } 865 866 //-------------------------------------------------------------------- 867 // everything OK so we wait for next incoming DCFbit 868 //-------------------------------------------------------------------- 869} 870 871//================================================================================================================ 872// 873// Function name : finalizeBuffer 874// called from : <scanSignal> 875// 876// Purpose : Process the succesfully received DCF data of one minute 877// Parameters : none 878// Return value : none 879// 880//================================================================================================================ 881 882void finalizeBuffer(void) 883{ 884 //-------------------------------------------------------------------- 885 // We are here because of the detected 2 second 'gap'. 886 // Now check if it correspondends with the buffer counter 887 // 'bufferPosition' which should be value 59 888 //-------------------------------------------------------------------- 889 if (bufferPosition == 59 && dcfValidSignal == true) 890 { 891 // bufferPosition == 59 so turn Buffer Full LED ON 892 digitalWrite(LED_BUFFERFULL, HIGH); 893 894 // Turn DCF OK LED ON 895 digitalWrite(LED_DCFSTATUS, HIGH); 896 897 // Reset inner LED ring (incoming time information) 898 MaximCC.clearDisplay(LedRingInner); 899 900 // copy 'contents' of inner LED ring to the outer LED ring (current time information) 901 for (int i = 0; i < 59; i++) 902 { 903 MaximCC.setLed(LedRingOuter, i / 8, i % 8, DCFbitBuffer[i]); 904 } 905 906 // process buffer and extract data sync the time with the RTC 907 decodeBufferContents(); 908 909 // set Arduino time and after that set RTC time 910 setTime(dcfHour, dcfMinute, 0, dcfDay, dcfMonth, dcfYear); 911 RTC.set(now()); 912 913 // activate Synced LED 914 digitalWrite(LED_RTCSYNC, HIGH); 915 916 // Reset running buffer 917 bufferPosition = 0; 918 919 // Reset DCFbitBuffer array, positions 0-58 (=59 bits) 920 for (int i = 0; i < 59; i++) 921 { 922 DCFbitBuffer[i] = 0; 923 } 924 925 // reset flag 926 MinuteMarkerFlag = false; 927 928 } // if (bufferPosition == 59) 929 930 //-------------------------------------------------------------------- 931 // The buffer is not yet filled although the 2 second 'gap' was detected. 932 // Can be result of a noisy signal, starting in middle of receiving data etc. 933 // Turn 'Minute Mark' LED ON 934 //-------------------------------------------------------------------- 935 else 936 { 937 digitalWrite(LED_MINUTEMARKER, HIGH); 938 939 // Clear displays 940 MaximCC.clearDisplay(LedRingInner); 941 MaximCC.clearDisplay(LedRingOuter); 942 943 // Reset running buffer and start afresh. Now we are in sync with the incoming data 944 bufferPosition = 0; 945 946 // Reset DCFbitBuffer array, positions 0-58 (=59 bits) 947 for (int i = 0; i < 59; i++) 948 { 949 DCFbitBuffer[i] = 0; 950 } 951 952 // set flag so we can display incoming pulsed on the inner LED ring. 953 MinuteMarkerFlag = true; 954 } 955} 956 957//================================================================================================================ 958// 959// Function name : decodeBufferContents 960// called from : <finalizeBuffer> 961// 962// Purpose : Evaluates the information stored in the buffer. 963// This is where the DCF77 signal is decoded to time and date information 964// Parameters : none 965// Return value : none 966// 967//================================================================================================================ 968 969void decodeBufferContents(void) 970{ 971 // Buffer is full and ready to be decoded 972 dcfMinute = bitDecode(21, 27); 973 dcfHour = bitDecode(29, 34); 974 dcfDay = bitDecode(36, 41); 975 dcfWeekDay = bitDecode(42, 44); 976 dcfMonth = bitDecode(45, 49); 977 dcfYear = bitDecode(50, 57); 978 979 //call function to calculate day of year and weeknumber 980 dayWeekNumber(dcfYear, dcfMonth, dcfDay, dcfWeekDay); 981 982 // Get value of Summertime DCFbit. '1' = Summertime, '0' = wintertime 983 dcfDST = bitDecode(17, 17); 984 985 // determine Leap Year 986 leapYear = calculateLeapYear(dcfYear); 987} 988 989//================================================================================================================ 990// 991// bitDecode 992// 993// called from <processBuffer> 994//================================================================================================================ 995int bitDecode(int bitStart, int bitEnd) 996{ 997 // reset 'bitValue-array' counter 998 int i = 0; 999 int value = 0; 1000 1001 // process bitrange bitStart > bitEnd 1002 while (bitStart <= bitEnd) 1003 { 1004 // check if DCFbit in buffer is '1', discard when '0' 1005 if (DCFbitBuffer[bitStart] == 1) 1006 { 1007 // DCFbit in buffer == 1 so append its corresponding value to the variable 'value' 1008 value = value + bitValue[i]; 1009 } 1010 // increment 'bitValue-array' counter 1011 i++; 1012 // increment bit-range counter 1013 bitStart++; 1014 } 1015 return value; 1016} 1017 1018//================================================================================================================ 1019// 1020// Function name : tasksEverySecond 1021// called from : <loop> 1022// 1023// Purpose : perform tasks that must happen once every SECOND 1024// Parameters : none 1025// Return value : none 1026// 1027//================================================================================================================ 1028void tasksEverySecond() 1029{ 1030 // check if time is changed 1031 if (second() != previousSecond) 1032 { 1033 // 'reset' variable state 1034 previousSecond = second(); 1035 1036 //display the Real Time Clock Time 1037 displayRtcTime(); 1038 1039 // display 'HI' and 'LO' temperature on specific moments 1040 switch (second()) 1041 { 1042 case 0: 1043 // hourly chime output: ACTIVATE 1044 if (dayTime == 1 && minute() == 0 && digitalRead(CHIMESWITCHPIN) == HIGH) 1045 { 1046 digitalWrite(CHIMEPIN, HIGH); 1047 } 1048 1049 // reset temperature min/max memory at midnight 1050 // 1051 // I did put this in the 'tasks every second' section so 1052 // that this code is executed at one specific second only... 1053 // Else we would need an extra variable to prevent this code to run 1054 // every cycle during the whole '00' hour. 1055 if (TEMPRESET_MIDNIGHT == 1 && (hour() == 00 && minute() == 00)) 1056 { 1057 minTemp = tempCelsius; 1058 maxTemp = tempCelsius; 1059 } 1060 break; 1061 case 2: 1062 // hourly chime output: DEACTIVATE 1063 digitalWrite(CHIMEPIN, LOW); 1064 break; 1065 case 30: 1066 // display 'HI' on display for Hi temperature 1067 MaximCC.setChar(DisplayTempWeek, 5, ' ', false); // Clear part of the display 1068 MaximCC.setChar(DisplayTempWeek, 4, ' ', false); // Clear part of the display 1069 MaximCC.setChar(DisplayTempWeek, 7, 'H', false); // Display 'H' character. Binary pattern to lite up individual segments in this order is: .ABCDEFG 1070 MaximCC.setRow(DisplayTempWeek, 6, B00010000); // Display 'i' character. Binary pattern to lite up individual segments in this order is: .ABCDEFG 1071 break; 1072 case 31: 1073 case 32: 1074 // display Max temperature on DCF display LED display 1075 MaximCC.setChar(DisplayTempWeek, 7, (maxTemp / 100), false); 1076 MaximCC.setChar(DisplayTempWeek, 6, (maxTemp % 100) / 10, true); 1077 MaximCC.setChar(DisplayTempWeek, 5, (maxTemp % 10), false); 1078 MaximCC.setRow(DisplayTempWeek, 4, B11001110); // Display Degrees dot and 'C' (Celcius) character. Binary pattern to lite up individual segments in this order is: .ABCDEFG 1079 // NOTE: I physically rotated digit 4 so when I activate the decimal dot, this now is the 'degrees dot' in the upper left corner 1080 break; 1081 case 33: 1082 // display 'LO' on display for Low temperature 1083 MaximCC.setChar(DisplayTempWeek, 5, ' ', false); // Clear part of the display 1084 MaximCC.setChar(DisplayTempWeek, 4, ' ', false); // Clear part of the display 1085 MaximCC.setChar(DisplayTempWeek, 7, 'L', false); // Display 'L' character. Binary pattern to lite up individual segments in this order is: .ABCDEFG 1086 MaximCC.setRow(DisplayTempWeek, 6, B00011101); // Display 'o' character. Binary pattern to lite up individual segments in this order is: .ABCDEFG 1087 break; 1088 case 34: 1089 case 35: 1090 // display Min temperature on DCF display LED display 1091 MaximCC.setChar(DisplayTempWeek, 7, (minTemp / 100), false); 1092 MaximCC.setChar(DisplayTempWeek, 6, (minTemp % 100) / 10, true); 1093 MaximCC.setChar(DisplayTempWeek, 5, (minTemp % 10), false); 1094 MaximCC.setRow(DisplayTempWeek, 4, B11001110); // Display Degrees dot and 'C' (Celcius) character. Binary pattern to lite up individual segments in this order is: .ABCDEFG 1095 // NOTE: I physically rotated digit 4 so when I activate the decimal dot, this now is the 'degrees dot' in the upper left corner 1096 break; 1097 case 36: 1098 // befor displaying the temperature in the next second, 1099 // request temperature from DS18B20 sensor, available after delay of minimal 750 ms (at highest resolution) 1100 calculateTemp(); 1101 1102 // display 'CU' on display for Current temperature 1103 MaximCC.setChar(DisplayTempWeek, 5, ' ', false); // Clear part of the display 1104 MaximCC.setChar(DisplayTempWeek, 4, ' ', false); // Clear part of the display 1105 MaximCC.setRow(DisplayTempWeek, 7, B01001110); // Display 'C' character. Binary pattern to lite up individual segments in this order is: .ABCDEFG 1106 MaximCC.setRow(DisplayTempWeek, 6, B00011100); // Display 'u' character. Binary pattern to lite up individual segments in this order is: .ABCDEFG 1107 break; 1108 case 37: 1109 // read temperature, store in min/max memory and display current temperature 1110 // only once per minute this is done else things go wrong... ;) 1111 displayTemp(); 1112 break; 1113 } // switch 1114 } // (second() != previousSecond) 1115} // void tasksEverySecond() 1116 1117//================================================================================================================ 1118// 1119// Function name : tasksEveryMinute 1120// called from : <loop> 1121// 1122// Purpose : perform tasks that must happen once every MINUTE 1123// Parameters : none 1124// Return value : none 1125// 1126//================================================================================================================ 1127void tasksEveryMinute() 1128{ 1129 // display date, week LED's, week nr etc. if time is changed 1130 if (minute() != previousMinute) 1131 { 1132 // 'reset' state of variable 1133 previousMinute = minute(); 1134 1135 // display date, week LED's, week nr etc. 1136 if (dcfValidSignal == true) 1137 { 1138 displayData(); 1139 } 1140 1141 // increase PIR delay counter 1142 pirTimer++; 1143 } 1144} 1145 1146//================================================================================================================ 1147// 1148// Function name : tasksEveryHour 1149// called from : <loop> 1150// 1151// Purpose : perform tasks that must happen once every HOUR 1152// Parameters : none 1153// Return value : none 1154// 1155//================================================================================================================ 1156void tasksEveryHour() 1157{ 1158 if (hour() != previousHour) 1159 { 1160 // 'reset' variable state 1161 previousHour = hour(); 1162 1163 //-------------------------------------------------------------------- 1164 // reset error counter and display every hour 1165 //-------------------------------------------------------------------- 1166 errorCounter = 0; 1167 // update error counter display 1168 ledDisplay(DisplayBufferBitError, "R", errorCounter); 1169 1170 //--------------------------------------------------------------------- 1171 // Power saving function, shutting the displays off at night 1172 //--------------------------------------------------------------------- 1173 1174 // First, check if the night shut-down function is activated by the user 1175 // simply by adding up both value. If '0', the user has disabled the function 1176 if (POWERSAVINGOFFTIME != 0 && POWERSAVINGONTIME != 0) 1177 { 1178 // check whether it is Day- or Nighttime 1179 if (hour() >= POWERSAVINGOFFTIME && hour() <= POWERSAVINGONTIME) 1180 { 1181 // ---------------------------------------- 1182 // it's DAYTIME so activate the displays 1183 // ---------------------------------------- 1184 1185 // this is used to chime only if it is daytime... 1186 dayTime = 1; 1187 1188 // test variable because daytime routine is needed only once 1189 if (daytimeChange == 1) 1190 { 1191 // 'reset' variable state 1192 daytimeChange = 0; 1193 1194 // activate SELECTED displays and status LED's 1195 turnDisplaysOn(); 1196 } 1197 } 1198 else 1199 { 1200 // no chime at night... 1201 dayTime = 0; 1202 1203 // ---------------------------------------- 1204 // it's NIGHTTIME so time to deactivate displays 1205 // ---------------------------------------- 1206 // test variable because nighttime routine is needed only once 1207 if (daytimeChange == 0) 1208 { 1209 // 'reset' variable state 1210 daytimeChange = 1; 1211 1212 // deactivate all the displays and status LED's 1213 // ONLY if the powersafe switch is HIGH 1214 if (digitalRead(POWERSAVESWITCHPIN) == HIGH) 1215 { 1216 turnDisplaysOff(); 1217 } 1218 1219 } // if (daytimeChange == 0) 1220 } // else 1221 } // if(POWERSAVINGOFFTIME + POWERSAVINGONTIME != 0) 1222 } // if (dcfHour != previousHour) 1223} // void tasksEveryHour() 1224 1225//================================================================================================================ 1226// 1227// Function name : buzzer 1228// called from : <scanSignal> 1229// 1230// Purpose : generate 'beep' sound 1231// Parameters : duration in ms 1232// Return value : none 1233// 1234//================================================================================================================ 1235 1236void buzzer(int duration) 1237{ 1238 tone(BUZZER, 1500, duration); 1239} 1240 1241//================================================================================================================ 1242// 1243// Function name : initialize 1244// called from : <Setup> 1245// 1246// Purpose : initialize variables and displays after power-up 1247// Parameters : none 1248// Return value : none 1249// 1250//================================================================================================================ 1251 1252void initialize(void) 1253{ 1254 //--------------------------------------------------- 1255 // Initialize Variables 1256 //--------------------------------------------------- 1257 leadingEdge = 0; 1258 trailingEdge = 0; 1259 previousLeadingEdge = 0; 1260 bufferPosition = 0; 1261 1262 digitalWrite(CHIMEPIN, HIGH); // Set Chimepin to default state 1263 1264 // Reset DCFbitBuffer array, positions 0-58 (=59 bits) 1265 for (int i = 0; i < 59; i++) 1266 { 1267 DCFbitBuffer[i] = 0; 1268 } 1269 1270 //--------------------------------------------------- 1271 // Initialize Maxim 72xx 7 segment displays 1272 //--------------------------------------------------- 1273 // Maxim Common Cathode displays 1274 for (int i = 0; i < 8; i++) 1275 { 1276 // display wake up 1277 MaximCC.shutdown(i, false); 1278 // clear display 1279 MaximCC.clearDisplay(i); 1280 } 1281 1282 //--------------------------------------------------- 1283 // Set brightness of Maxim 72xx 7 segment displays 1284 //--------------------------------------------------- 1285 // Maxim Common Cathode displays 1286 MaximCC.setIntensity(LedRingOuter, BrightnessLedRingOuter); 1287 MaximCC.setIntensity(LedRingInner, BrightnessLedRingInner); 1288 MaximCC.setIntensity(DisplayTime, BrightnessDisplayTime); 1289 MaximCC.setIntensity(DisplayDate, BrightnessDisplayDate); 1290 MaximCC.setIntensity(DisplayTempWeek, BrightnessDisplayTempWeek); 1291 MaximCC.setIntensity(DisplayPeriodPulse, BrightnessDisplayPeriodPulse); 1292 MaximCC.setIntensity(DisplayBufferBitError, BrightnessDisplayBufferBitError); 1293 1294 //--------------------------------------------------- 1295 // Set all Arduino LED outputs to LOW (Led's OFF) 1296 //--------------------------------------------------- 1297 for (int i1 = 22; i1 <= 49; i1++) 1298 { 1299 digitalWrite(i1, LOW); 1300 } 1301} 1302 1303//================================================================================================================ 1304// 1305// Function name : int0handler 1306// called from : 1307// 1308// Purpose : when a rising or falling edge is detected on pin 2, this function is called 1309// Parameters : none 1310// Return value : none 1311// 1312//================================================================================================================ 1313 1314void int0handler() 1315{ 1316 DCFSignalState = digitalRead(DCF77PIN); 1317} 1318 1319//================================================================================================================ 1320// 1321// Function name : turnDisplaysOn 1322// called from : <tasksEveryHour> and <checkPIR> 1323// 1324// Purpose : turn ON selected 7 segment displays and LED's 1325// Parameters : none 1326// Return value : none 1327// 1328//================================================================================================================ 1329void turnDisplaysOn() 1330{ 1331 // activate SELECTED displays and status LED's 1332 MaximCC.shutdown(LedRingInner, false); 1333 MaximCC.shutdown(LedRingOuter, false); 1334 MaximCC.shutdown(DisplayTime, false); 1335 MaximCC.shutdown(DisplayDate, false); 1336 MaximCC.shutdown(DisplayTempWeek, false); 1337 MaximCC.shutdown(DisplayPeriodPulse, false); 1338 MaximCC.shutdown(DisplayBufferBitError, false); 1339} 1340 1341//================================================================================================================ 1342// 1343// Function name : turnDisplaysOff 1344// called from : <tasksEveryHour> and <checkPIR> 1345// 1346// Purpose : turn OFF selected 7 segment displays and LED's 1347// Parameters : none 1348// Return value : none 1349// 1350//================================================================================================================ 1351void turnDisplaysOff() 1352{ 1353 // below you can select which display's need to be shut down for the night 1354 // In this case, the time display remains ON 1355 1356 //MaximCC.shutdown(DisplayTime, true); 1357 MaximCC.shutdown(LedRingInner, true); 1358 MaximCC.shutdown(LedRingOuter, true); 1359 MaximCC.shutdown(DisplayDate, true); 1360 MaximCC.shutdown(DisplayTempWeek, true); 1361 MaximCC.shutdown(DisplayPeriodPulse, true); 1362 MaximCC.shutdown(DisplayBufferBitError, true); 1363} 1364 1365//================================================================================================================ 1366// 1367// Function name : ledDisplay 1368// called from : <processBuffer> 1369// 1370// Purpose : display a value on a selected 7 segment display 1371// Parameters : display 'number 'addr', 'value', "L"eft or "R"ight side.none 1372// Return value : none 1373// 1374//================================================================================================================ 1375 1376// example: ledDisplay( DisplayBufferBitError, "R", errorCounter ) 1377// so display the 'error counter' value on the RIGHT side of the 8 digit 1378// 7 segment display number 3 1379 1380void ledDisplay(int addr, String leftOrRight, int value) 1381{ 1382 int ones; 1383 int tens; 1384 int hundreds; 1385 int thousands; 1386 int shift; 1387 1388 //break down value in seperate digits for ones, tens, etc 1389 int v = value; // 'value' is needed later so copy it in 'v' 1390 ones = v % 10; 1391 v = v / 10; 1392 tens = v % 10; 1393 v = v / 10; 1394 hundreds = v % 10; 1395 thousands = v / 10; 1396 1397 //Select which side of the 8 digit display to be used 1398 (leftOrRight == "L" ? shift = 4 : shift = 0); 1399 1400 //Now print the number digit by digit 1401 //preceding zero's are removed with tenary operator by 'printing' a space character. 1402 MaximCC.setChar(addr, 3 + shift, ((value < 1000) ? ' ' : thousands), false); 1403 MaximCC.setChar(addr, 2 + shift, ((value < 100) ? ' ' : hundreds), false); 1404 MaximCC.setChar(addr, 1 + shift, ((value < 10) ? ' ' : tens), false); 1405 MaximCC.setChar(addr, 0 + shift, ones, false); 1406} 1407 1408//================================================================================================================ 1409// 1410// Function name : checkSwitches 1411// called from : <loop> 1412// 1413// Purpose : check if the temperature reset button button is pressed 1414// Parameters : none 1415// Return value : none 1416// 1417//================================================================================================================ 1418void checkSwitches(void) 1419{ 1420 //------------------------------------------------------------- 1421 // read state of push button tempResetButton 1422 tempResetButton = digitalRead(TEMPRESETPIN); 1423 // reset temperature min/max values when push-button is pressed 1424 if (tempResetButton == 1) 1425 { 1426 maxTemp = tempCelsius; 1427 minTemp = tempCelsius; 1428 } 1429 1430 //------------------------------------------------------------- 1431 //read state of switch BUZZERSWITCHPIN 1432 dcf77SoundSwitch = digitalRead(BUZZERSWITCHPIN); 1433 1434 //------------------------------------------------------------- 1435 //check led and display's powersafe switch 1436 if (digitalRead(POWERSAVESWITCHPIN) == HIGH) 1437 { 1438 digitalWrite(LED_POWERSAVE, HIGH); 1439 } 1440 else 1441 { 1442 digitalWrite(LED_POWERSAVE, LOW); 1443 turnDisplaysOn(); 1444 } 1445} 1446 1447//================================================================================================================ 1448// 1449// Function name : checkPIR 1450// called from : <loop> 1451// 1452// Purpose : check for PIR detector activity to shut off or activate the displays to save power 1453// Parameters : none 1454// Return value : none 1455// 1456//================================================================================================================ 1457void checkPIR() 1458{ 1459 //------------------------------------------------------------- 1460 // Read PIR input, check for movement 1461 // Only check for PIR activity if user option is '1' 1462 // AND only every second to prevent waisted processor time. 1463 if (POWERSAVE_BY_PIR == 1 && (millis() - previousTimePIR > 1000)) 1464 { 1465 // reset the 'once-per-second timer 1466 previousTimePIR = millis(); 1467 1468 // read the PIR detector PIN 1469 pirActivity = digitalRead(PIRDETECTORPIN); 1470 1471 // turn displays ON or OFF depending on state of pin 'pirActivity' 1472 if (pirActivity == 1) 1473 { 1474 // PIR activity detected... 1475 // activated the PIR LED 1476 digitalWrite(LED_PIRMOTION, HIGH); 1477 // Reset the PIR timer variable (used in <tasksEveryMinue> function) every time 1478 // the PIR is activated so effectually resetting the shut-off timer. 1479 pirTimer = 0; 1480 1481 // only continue if the display is now OFF 1482 if (pirDisplaysState == 0) 1483 { 1484 // Toggle variable to prevent executing the following 1485 // code every time the PIR detects activity 1486 pirDisplaysState = 1; 1487 1488 //displays ON 1489 turnDisplaysOn(); 1490 } 1491 } 1492 else 1493 { 1494 // No PIR activity detected... 1495 // turn the PIR LED off 1496 digitalWrite(LED_PIRMOTION, LOW); 1497 // check the pirTimer counter in the <tasksEveryMinue> function). 1498 // and if the delaytime has passed, only continue if the displays are now ON 1499 // else this code would be executed many times per second 1500 if (pirTimer > PIR_DELAY_TIME && pirDisplaysState == 1) 1501 { 1502 // Toggle variable to prevent executing the following 1503 // code every time the PIR detects activity 1504 pirDisplaysState = 0; 1505 1506 //shut off displays ONLY if powersafe switch is HIGH 1507 if (digitalRead(POWERSAVESWITCHPIN) == HIGH) 1508 { 1509 turnDisplaysOff(); 1510 } 1511 } 1512 } // else 1513 } // if (POWERSAVE_BY_PIR == 1 && (millis() - previousTimePIR > 1000)) 1514} // void checkPIR() 1515 1516//================================================================================================================ 1517// 1518// Function name : error 1519// called from : <scanSignal> 1520// 1521// Purpose : turn error LED ON, clear LED ring's and increase error counter display 1522// Parameters : error LED to turn on 1523// Return value : none 1524// 1525//================================================================================================================ 1526 1527void error(int errorLed) 1528{ 1529 // no valid data 1530 dcfValidSignal = false; 1531 1532 // turn 'dcfValidSignal = false on' 1533 digitalWrite(errorLed, HIGH); 1534 1535 // clear Led's/displays because of error condition 1536 digitalWrite(LED_DCFSTATUS, LOW); 1537 digitalWrite(LED_RTCSYNC, LOW); 1538 MaximCC.clearDisplay(LedRingOuter); // clear display 1539 1540 // increase errorCounter and display errorCount 1541 errorCounter++; 1542 ledDisplay(DisplayBufferBitError, "R", errorCounter); 1543 return; 1544} 1545 1546//================================================================================================================ 1547// 1548// Function name : dayWeekNumber 1549// called from : <decodeBufferContents> 1550// 1551// Purpose : calculate the WEEK number according to ISO standard, see comments in the ARCHIVE below 1552// Parameters : dcfYear, dcfMonth, dcfDay, dcfWeekDay 1553// Return value : weekNumber 1554// 1555//================================================================================================================ 1556//Code from: http://forum.arduino.cc/index.php/topic,44476.0.html 1557 1558int dayWeekNumber(int y, int m, int d, int w) 1559{ 1560 // Number of days at the beginning of the month in a normal (not leap) year. 1561 int days[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; 1562 1563 // Start to calculate the number of days of the first two months 1564 if (m == 1 || m == 2) 1565 { 1566 // for any type of year we calculate the number of days for January or february 1567 dayNumber = days[(m - 1)] + d; 1568 } 1569 1570 // now calculate for the other months 1571 // first, check for a leap year 1572 else if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) 1573 { 1574 // we have a leap year, so calculate in the same way but adding one day 1575 dayNumber = days[(m - 1)] + d + 1; 1576 } 1577 1578 else 1579 { 1580 //no leap year, calculate in the normal way, such as January or February 1581 dayNumber = days[(m - 1)] + d; 1582 } 1583 1584 // Now start to calculate Week number 1585 if (w == 0) 1586 { 1587 //if it is sunday (time library returns 0) 1588 weekNumber = (dayNumber - 7 + 10) / 7; 1589 } 1590 1591 else 1592 { 1593 // for the other days of week 1594 weekNumber = (dayNumber - w + 10) / 7; 1595 } 1596 1597 // finished! return with the week number as an INT value 1598 return weekNumber; 1599} 1600 1601//================================================================================================================ 1602// 1603// Function name : calculateLeapYear 1604// called from : <decodeBufferContents> 1605// 1606// Purpose : determine if a given year is a leap year 1607// Parameters : year - the year to test 1608// Return value : '1' if the year is a leap year, '0' otherwise 1609// 1610//================================================================================================================ 1611 1612int calculateLeapYear(int year) 1613{ 1614 if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) 1615 { 1616 return 1; 1617 } 1618 else 1619 { 1620 return 0; 1621 } 1622} 1623 1624//================================================================================================================ 1625// 1626// Function name : displayData 1627// called from : <tasksEveryMinute> 1628// 1629// Purpose : display LED's: day of week; status; Winter/Summer time; 1630// Parameters : none 1631// Return value : none 1632// 1633//================================================================================================================ 1634 1635void displayData(void) 1636{ 1637 // display Day of Week on LED's 1638 // first, clear all the 'Day' Led's before displaying new value 1639 digitalWrite(LED_SUNDAY, LOW); 1640 digitalWrite(LED_MONDAY, LOW); 1641 digitalWrite(LED_TUESDAY, LOW); 1642 digitalWrite(LED_WEDNESDAY, LOW); 1643 digitalWrite(LED_THURSDAY, LOW); 1644 digitalWrite(LED_FRIDAY, LOW); 1645 digitalWrite(LED_SATURDAY, LOW); 1646 1647 // switch on the Weekday LED 1648 switch (dcfWeekDay) 1649 { 1650 case 1: 1651 digitalWrite(LED_MONDAY, HIGH); 1652 break; 1653 case 2: 1654 digitalWrite(LED_TUESDAY, HIGH); 1655 break; 1656 case 3: 1657 digitalWrite(LED_WEDNESDAY, HIGH); 1658 break; 1659 case 4: 1660 digitalWrite(LED_THURSDAY, HIGH); 1661 break; 1662 case 5: 1663 digitalWrite(LED_FRIDAY, HIGH); 1664 break; 1665 case 6: 1666 digitalWrite(LED_SATURDAY, HIGH); 1667 break; 1668 case 7: 1669 digitalWrite(LED_SUNDAY, HIGH); 1670 break; 1671 } 1672 1673 // display Weeknumber 1674 MaximCC.setChar(DisplayTempWeek, 1, ((weekNumber < 10) ? ' ' : (weekNumber / 10)), false); 1675 MaximCC.setChar(DisplayTempWeek, 0, weekNumber % 10, false); 1676 1677 // display Date - with dashes between D-M-Y 1678 // for example: 03-08-2015, so with leading zero's 1679 MaximCC.setChar(DisplayDate, 7, dcfDay / 10, false); 1680 MaximCC.setChar(DisplayDate, 6, dcfDay % 10, false); 1681 MaximCC.setChar(DisplayDate, 5, '-', false); 1682 MaximCC.setChar(DisplayDate, 4, dcfMonth / 10, false); 1683 MaximCC.setChar(DisplayDate, 3, dcfMonth % 10, false); 1684 MaximCC.setChar(DisplayDate, 2, '-', false); 1685 MaximCC.setChar(DisplayDate, 1, dcfYear / 10, false); 1686 MaximCC.setChar(DisplayDate, 0, dcfYear % 10, false); 1687 1688 /* OTHER OPTIONS TO DISPLAY THE DATE: 1689 1690 // display Date - with dots between D.M.Y 1691 // for example: 03.08.15 1692 MaximCC.setChar(DisplayDate, 7, dcfDay / 10, false); 1693 MaximCC.setChar(DisplayDate, 6, dcfDay % 10, true); 1694 MaximCC.setChar(DisplayDate, 5, dcfMonth / 10, false); 1695 MaximCC.setChar(DisplayDate, 4, dcfMonth % 10, true); 1696 MaximCC.setChar(DisplayDate, 3, 2, false); 1697 MaximCC.setChar(DisplayDate, 2, 0, false); 1698 MaximCC.setChar(DisplayDate, 1, dcfYear / 10, false); 1699 MaximCC.setChar(DisplayDate, 0, dcfYear % 10, false); 1700 */ 1701 1702 /* 1703 // display Date - moved day to right if month is <10 1704 // for example: __3.8.15 or _22.7.15 or 24.12.15 (where _ is a blank display) 1705 MaximCC.setChar(DisplayDate, 7, ((dcfDay < 10) ? ' ' : ((dcfMonth < 10) ? ' ' : (dcfDay / 10))), false); 1706 MaximCC.setChar(DisplayDate, 6, ((dcfMonth < 10) ? ((dcfDay < 10) ? ' ' : (dcfDay / 10)) : (dcfDay % 10)), ((dcfMonth < 10) ? false : true)); 1707 MaximCC.setChar(DisplayDate, 5, ((dcfMonth < 10) ? (dcfDay % 10) : (dcfMonth / 10)), ((dcfMonth < 10) ? true : false)); 1708 MaximCC.setChar(DisplayDate, 4, dcfMonth % 10, true); 1709 MaximCC.setChar(DisplayDate, 3, 2, false); 1710 MaximCC.setChar(DisplayDate, 2, 0, false); 1711 MaximCC.setChar(DisplayDate, 1, dcfYear / 10, false); 1712 MaximCC.setChar(DisplayDate, 0, dcfYear % 10, false); 1713 */ 1714 1715 // display Summer- or Wintertime LED 1716 if (dcfDST == 1) 1717 { 1718 digitalWrite(LED_CEST, HIGH); 1719 digitalWrite(LED_CET, LOW); 1720 } 1721 else 1722 { 1723 digitalWrite(LED_CET, HIGH); 1724 digitalWrite(LED_CEST, LOW); 1725 } 1726 1727 // display Leap Year LED 1728 if (leapYear == 1) 1729 { 1730 digitalWrite(LED_LEAPYEAR, HIGH); 1731 } 1732 else 1733 { 1734 digitalWrite(LED_LEAPYEAR, LOW); 1735 } 1736} 1737 1738//================================================================================================================ 1739// 1740// Function name : displayRtcTime 1741// called from : <tasksEverySecond> 1742// 1743// Purpose : display the Real Time Clock time on the RTC display 1744// Parameters : none 1745// Return value : none 1746// 1747//================================================================================================================ 1748 1749void displayRtcTime() 1750{ 1751 MaximCC.setChar(DisplayTime, 7, ((hour() < 10) ? ' ' : (hour() / 10)), false); 1752 MaximCC.setChar(DisplayTime, 6, (hour() % 10), false); 1753 // activate only the segments 'a' and 'g' of the 7 segment display, representing the dots between the digits 1754 // Binary pattern to lite up individual segments in this order is: .ABCDEFG 1755 MaximCC.setRow(DisplayTime, 5, B00001001); 1756 MaximCC.setChar(DisplayTime, 4, (minute() / 10), false); 1757 MaximCC.setChar(DisplayTime, 3, (minute() % 10), false); 1758 // activate only the segments 'a' and 'g' of the 7 segment display, representing the dots between the digits 1759 // Binary pattern to lite up individual segments in this order is: .ABCDEFG 1760 MaximCC.setRow(DisplayTime, 2, B01001001); 1761 MaximCC.setChar(DisplayTime, 1, (second() / 10), false); 1762 MaximCC.setChar(DisplayTime, 0, (second() % 10), false); 1763} 1764 1765//================================================================================================================ 1766// 1767// Function name : calculateTemp 1768// called from : <loop> 1769// 1770// Purpose : get temperature from DS18B20 sensor and display it 1771// Parameters : none 1772// Return value : none 1773// 1774//================================================================================================================ 1775 1776void calculateTemp() 1777{ 1778 // The initialization sequence consists of a reset pulse transmitted by the bus master 1779 // followed by presence pulse(s) transmitted by the slave(s). 1780 ds.reset(); 1781 // use this command when only 1 sensor is used to avoid specifying the address! 1782 ds.skip(); 1783 // start conversion 1784 ds.write(0x44); 1785 // (!) AFTER THIS we need to wait for a time dependent on resolution 1786 // CONVERSION TIME: 1787 // 9 bit resolution, 93.75 ms 1788 // 10 bit resolution, 187.5 ms 1789 // 11 bit resolution, 375 ms 1790 // 12 bit resolution, 750 ms 1791 // 1792 // This is done in this sketch by requesting the conversion first and at minimal 1 second later reading it. 1793} 1794 1795//================================================================================================================ 1796// 1797// Function name : displayTemp 1798// called from : <loop> 1799// 1800// Purpose : get temperature from DS18B20 sensor and display it 1801// Earlier we requested the temperature from the DS18B20 sensor, 1802// now conversion is finished so we get the data 1803// Parameters : none 1804// Return value : none 1805// 1806//================================================================================================================ 1807 1808void displayTemp() 1809{ 1810 // The initialization sequence consists of a reset pulse transmitted by the bus master 1811 // followed by presence pulse(s) transmitted by the slave(s). 1812 ds.reset(); 1813 // use this command when only 1 sensor is used to avoid specifying the address! 1814 ds.skip(); 1815 // Read Scratchpad 1816 ds.write(0xBE); // Read Scratchpad 1817 // we need 9 bytes 1818 for (byte i = 0; i < 9; i++) 1819 { 1820 // Read a Byte a a time 1821 DS18B20Data[i] = ds.read(); 1822 } 1823 1824 // We have the data, next convert to actual temperature. 1825 lowByte = DS18B20Data[0]; 1826 highByte = DS18B20Data[1]; 1827 // because the result is a 16 bit signed integer, it should 1828 // be stored to an "int16_t" type, which is always 16 bits 1829 // even when compiled on a 32 bit processor. 1830 int16_t tempReading = (highByte << 8) + lowByte; 1831 // tempRaw is 4 digits; 2 integers, 2 fraction. For example: 2115 (= 21.15 degrees Celsius) 1832 int tempRaw = (6 * tempReading) + tempReading / 4; 1833 // We need 3 digits: 2 integers, 2 fraction. For example: 216 (= 21.6 degres Celsius) 1834 tempRaw % 10 >= 5 ? tempCelsius = (tempRaw / 10) + 1 : tempCelsius = tempRaw / 10; 1835 1836 // store min and max temperatures 1837 if (minTemp == 0) 1838 minTemp = tempCelsius; 1839 if (tempCelsius > maxTemp) 1840 maxTemp = tempCelsius; 1841 if (tempCelsius < minTemp) 1842 minTemp = tempCelsius; 1843 1844 // Display temperature on the multi purpose display 'TempWeek' 1845 1846 // Also light the 'Temp' LED on the front panel 1847 digitalWrite(LED_TEMP, HIGH); 1848 // Display Digit 3: example: temperature value 216 / 100 = 2 1849 MaximCC.setChar(DisplayTempWeek, 7, (tempCelsius / 100), false); 1850 // Display Digit 2: example: temperature value 216 % 100 / 10 = 1 1851 // also activate decimal dot on display 2 1852 MaximCC.setChar(DisplayTempWeek, 6, (tempCelsius % 100) / 10, true); 1853 // Display Digit 3: example: temperature value 216 % 10 = 6 1854 MaximCC.setChar(DisplayTempWeek, 5, (tempCelsius % 10), false); 1855 // Display Degrees dot and 'C' (Celcius) character. Binary pattern to lite up individual segments in this order is: .ABCDEFG 1856 // NOTE: I physically rotated digit 4 7 segement display so when I activate the decimal dot, 1857 // this now is the 'degrees dot' in the upper left corner of the display... 1858 MaximCC.setRow(DisplayTempWeek, 4, B11001110); 1859} 1860 1861//================================================================================================================ 1862// 1863// Function name : ledTest 1864// called from : <setup> 1865// 1866// Purpose : after a cold start, do a led test. 1867// Parameters : none 1868// Return value : none 1869// 1870//================================================================================================================ 1871void ledTest() 1872{ 1873 // The displays are lit up sequentially because of the current draw. 1874 // When all is lit up at the same time, you would need a bigger power supply. 1875 1876 //--------------------------------------------------------------------- 1877 // Outer LED ring 1878 for (int i = 0; i < 59; i++) 1879 { 1880 // LED's ON 1881 MaximCC.setLed(LedRingOuter, i / 8, i % 8, true); 1882 delay(LEDTEST_DELAY_LED_RING); 1883 } 1884 1885 for (int i = 58; i >= 0; i--) 1886 { 1887 // LED's OFF 1888 MaximCC.setLed(LedRingOuter, i / 8, i % 8, false); 1889 delay(LEDTEST_DELAY_LED_RING); 1890 } 1891 //--------------------------------------------------------------------- 1892 // Inner LED ring 1893 for (int i = 0; i < 59; i++) 1894 { 1895 // LED's ON 1896 MaximCC.setLed(LedRingInner, i / 8, i % 8, true); 1897 delay(LEDTEST_DELAY_LED_RING); 1898 } 1899 1900 for (int i = 58; i >= 0; i--) 1901 { 1902 // LED's OFF 1903 MaximCC.setLed(LedRingInner, i / 8, i % 8, false); 1904 delay(LEDTEST_DELAY_LED_RING); 1905 } 1906 //--------------------------------------------------------------------- 1907 // LED's 1908 for (int i = 22; i <= 53; i++) 1909 { 1910 // LED's ON 1911 digitalWrite(i, HIGH); 1912 delay(LEDTEST_DELAY_LED_RING); 1913 } 1914 // wait before turning the LED's off 1915 delay(LEDTEST_DELAY_DISPLAYS); 1916 for (int i = 53; i >= 22; i--) 1917 { 1918 // LED's OFF 1919 digitalWrite(i, LOW); 1920 } 1921 //--------------------------------------------------------------------- 1922 // Real Time Clock display 1923 for (int i = 0; i < 8; i++) 1924 { 1925 // LED's ON 1926 MaximCC.setChar(DisplayTime, i, 8, true); 1927 } 1928 // wait before turning the LED's off 1929 delay(LEDTEST_DELAY_DISPLAYS); 1930 // clear display 1931 MaximCC.clearDisplay(DisplayTime); 1932 //--------------------------------------------------------------------- 1933 // Date display 1934 for (int i = 0; i < 8; i++) 1935 { 1936 // LED's ON 1937 MaximCC.setChar(DisplayDate, i, 8, true); 1938 } 1939 // wait before turning the LED's off 1940 delay(LEDTEST_DELAY_DISPLAYS); 1941 // clear display 1942 MaximCC.clearDisplay(DisplayDate); 1943 //--------------------------------------------------------------------- 1944 // Temp and Week display 1945 for (int i = 0; i < 8; i++) 1946 { 1947 // LED's ON 1948 MaximCC.setChar(DisplayTempWeek, i, 8, true); 1949 } 1950 // wait before turning the LED's off 1951 delay(LEDTEST_DELAY_DISPLAYS); 1952 // clear display 1953 MaximCC.clearDisplay(DisplayTempWeek); 1954 //--------------------------------------------------------------------- 1955 // Period-Pulse display 1956 for (int i = 0; i < 8; i++) 1957 { 1958 // LED's ON 1959 MaximCC.setChar(DisplayPeriodPulse, i, 8, true); 1960 } 1961 // wait before turning the LED's off 1962 delay(LEDTEST_DELAY_DISPLAYS); 1963 // clear display 1964 MaximCC.clearDisplay(DisplayPeriodPulse); 1965 //--------------------------------------------------------------------- 1966 // Buffer-DCFbit-Errors display 1967 for (int i = 0; i < 8; i++) 1968 { 1969 // LED's ON 1970 MaximCC.setChar(DisplayBufferBitError, i, 8, true); 1971 } 1972 // wait before turning the LED's off 1973 delay(LEDTEST_DELAY_DISPLAYS); 1974 // clear display 1975 MaximCC.clearDisplay(DisplayBufferBitError); 1976} 1977 1978//================================================================================================================ 1979// 1980// Function name : configureDS18B20 1981// called from : <setup> 1982// 1983// Purpose : ON TIME function to configure the DS18B20 temperature sensor, 1984// setting the desired temperature resolution 1985// Parameters : none 1986// Return value : none 1987// 1988//================================================================================================================ 1989 1990void configureDS18B20() 1991{ 1992 // This is done ONCE in setup() 1993 1994 // INITIALIZATION 1995 // All transactions on the 1-Wire bus begin with an initialization sequence. 1996 // The initialization sequence consists of a reset pulse transmitted by the bus master followed by presence pulse(s) transmitted by the slave(s). 1997 // The presence pulse lets the bus master know that slave devices (such as the DS18B20) are on the bus and are ready to operate. 1998 1999 // The initialization sequence consists of a reset pulse transmitted by the bus master followed by presence pulse(s) transmitted by the slave(s). 2000 ds.reset(); 2001 2002 // use this command when only 1 sensor is used to avoid specifying the address! 2003 ds.skip(); 2004 2005 // Select a specific DS18x20 device 2006 //byte addr[8] = {0x28, 0xF0, 0xEC, 0xE2, 0x04, 0x00, 0x00, 0x47}; 2007 //ds.select(addr); 2008 2009 // WRITE SCRATCHPAD 2010 // This command allows the master to write 3 bytes of data to the DS18B20s scratchpad. 2011 // 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), 2012 // and the third byte is written into the configuration register (byte 4). 2013 // Data must be transmitted least significant bit first. All three bytes MUST be written before the master issues a reset, 2014 // or the data may be corrupted. 2015 ds.write(0x4E); 2016 2017 // write zero into the alarm register HIGH 2018 ds.write(0); 2019 // write zero into the alarm register LOW 2020 ds.write(0); 2021 /* Next, write R1 and R2 into the configuration register to select the precision of the temperature 2022 value | resolution | conversion time | increments 2023 0 = 9 bits | 93,75 ms | 0,5 Celsius 2024 1 = 10 bits | 187,5 ms | 0,25 2025 2 = 11 bits | 375 ms | 0,125 2026 3 = 12 bits | 750 ms | 0,0625 2027 CONFIGURATION REGISTER: 2028 ------ Bit ------ 2029 7 6 5 4 3 2 1 0 2030 0 R1 R0 1 1 1 1 1 2031 */ 2032 ds.write(B01111111); 2033 // WRITE SCRATCHPAD DATA TO EEPROM 2034 ds.reset(); 2035 // use this command when only 1 sensor is used to avoid specifying the address! 2036 ds.skip(); 2037 // Select a specific DS18x20 device 2038 //ds.select(addr); 2039 2040 // COPY SCRATCHPAD [48h] 2041 // This command copies the contents of the scratchpad TH, TL and configuration registers (bytes 2, 3 and 4) to EEPROM. 2042 // If the device is being used in parasite power mode, within 10s (max) after this command is issued the master must 2043 // enable a strong pullup on the 1-Wire bus for at least 10ms as described in the Powering the DS18B20 section. 2044 ds.write(0x48); 2045} 2046
Superfilter sketch
arduino
1// 2// This is the Superfilter sketch I use with the DCF Analyzer/Clock 3 2.0 4// Udo Klein did an amazing job with this filter 5// 6// Erik de Ruiter 7 8/* 9 10 Arduino Uno pin connections I used for the DCF Analyzer Clock 11 12 DCF 13 input ................. A5 (19) = dcf77_sample_pin 14 Output DCF Filtered ....... 15 12 = dcf77_filtered_pin 16 Output DCF Semi Synthesized A2 (16) = dcf77_semi_synthesized_pin 17 18 Output DCF Synthesized .... 6 = dcf77_synthesized_pin 19 LED DCF 20 output filtered ... A4 (18) = dcf77_monitor_pin = DCF Monitor 21 LED 22 LED 1 Hz pulse ............ 10 = dcf77_second_pulse_pin = 23 Filter Locked LED 24 LED DCF OK ................ 13 = dcf77_signal_good_indicator_pin 25 = Signal Quality LED 26 LED Difference Filtered ... 7 = dcf77_filter_diff_pin 27 \ 28 LED Difference Semi Synth.. A0 = dcf77_semi_synthesized_diff_pin 29 -> = Signal Difference LED 30 LED Difference Synthesized 4 = dcf77_synthesized_diff_pin 31 / 32*/ 33 34// 35// www.blinkenlight.net 36// 37// Copyright 38 2014, 2015 Udo Klein 39// 40// This program is free software: you can redistribute 41 it and/or modify 42// it under the terms of the GNU General Public License as 43 published by 44// the Free Software Foundation, either version 3 of the License, 45 or 46// (at your option) any later version. 47// 48// This program is distributed 49 in the hope that it will be useful, 50// but WITHOUT ANY WARRANTY; without even 51 the implied warranty of 52// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 53 See the 54// GNU General Public License for more details. 55// 56// You should 57 have received a copy of the GNU General Public License 58// along with this program. 59 If not, see http://www.gnu.org/licenses/ 60 61#include <dcf77.h> 62/* 63const 64 uint8_t pon_pin = 51; // connect pon to ground !!! 65const uint8_t data_pin = 66 19; 67const uint8_t gnd_pin = 51; 68const uint8_t vcc_pin = 49; 69*/ 70 71const 72 uint8_t dcf77_analog_samples = false; 73const uint8_t dcf77_analog_sample_pin = 74 5; 75const uint8_t dcf77_sample_pin = 19; // A5 76const uint8_t dcf77_inverted_samples 77 = 0; 78#if defined(__AVR__) 79#define ledpin(led) (led) 80#else 81#define ledpin(led) 82 (led<14? led: led+(54-14)) 83#endif 84 85const uint8_t dcf77_monitor_pin = ledpin(18); 86 // A4 87 88const bool provide_filtered_output = true; 89const uint8_t dcf77_filtered_pin 90 = ledpin(12); 91const uint8_t dcf77_inverted_filtered_pin = ledpin(11); 92const 93 uint8_t dcf77_filter_diff_pin = ledpin(7); 94 95const bool provide_semi_synthesized_output 96 = true; 97const uint8_t dcf77_semi_synthesized_pin = ledpin(16); 98const 99 uint8_t dcf77_inverted_semi_synthesized_pin = ledpin(15); 100const uint8_t dcf77_semi_synthesized_diff_pin 101 = ledpin(14); 102 103const bool provide_synthesized_output = true; 104const 105 uint8_t dcf77_synthesized_pin = ledpin(6); 106const uint8_t dcf77_inverted_synthesized_pin 107 = ledpin(5); 108const uint8_t dcf77_synthesized_diff_pin = ledpin(4); 109 110const 111 uint8_t dcf77_second_pulse_pin = ledpin(10); 112 113 114const uint8_t dcf77_signal_good_indicator_pin 115 = ledpin(13); 116 117volatile uint16_t ms_counter = 0; 118volatile Internal::DCF77::tick_t 119 tick = Internal::DCF77::undefined; 120 121 122template <bool enable, uint8_t threshold, 123 124 uint8_t filtered_pin, uint8_t inverted_filtered_pin, uint8_t diff_pin> 125void 126 set_output(uint8_t clock_state, uint8_t sampled_data, uint8_t synthesized_signal) 127{ 128 129 if (enable) { 130 const uint8_t filtered_output = clock_state < threshold? 131 sampled_data: synthesized_signal; 132 digitalWrite(filtered_pin, filtered_output); 133 134 digitalWrite(inverted_filtered_pin, !filtered_output); 135 digitalWrite(diff_pin, 136 filtered_output ^ sampled_data); 137 } 138} 139 140namespace { 141 struct 142 Scope { 143 static const uint16_t samples_per_second = 1000; 144 static 145 const uint8_t bins = 100; 146 static const uint8_t samples_per_bin 147 = samples_per_second / bins; 148 149 volatile uint8_t gbin[bins]; 150 151 volatile boolean samples_pending = false; 152 volatile uint32_t count 153 = 0; 154 155 void process_one_sample(const uint8_t sample) { 156 static 157 uint8_t sbin[bins]; 158 159 static uint16_t ticks = 999; // first pass 160 will init the bins 161 ++ticks; 162 163 if (ticks == 1000) 164 { 165 ticks = 0; 166 memcpy((void *)gbin, sbin, bins); 167 168 memset(sbin, 0, bins); 169 samples_pending = true; 170 171 ++count; 172 } 173 sbin[ticks/samples_per_bin] 174 += sample; 175 } 176 177 void print() { 178 uint8_t lbin[bins]; 179 180 181 if (samples_pending) { 182 noInterrupts(); 183 memcpy(lbin, 184 (void *)gbin, bins); 185 samples_pending = false; 186 interrupts(); 187 188 189 // ensure the count values will be aligned to the right 190 for 191 (int32_t val=count; val < 100000000; val *= 10) { 192 Serial.print(' 193 '); 194 } 195 Serial.print((int32_t)count); 196 Serial.print(", 197 "); 198 for (uint8_t bin=0; bin<bins; ++bin) { 199 switch 200 (lbin[bin]) { 201 case 0: Serial.print(bin%10? '-': '+'); 202 break; 203 case 10: Serial.print('X'); break; 204 205 default: Serial.print(lbin[bin]); 206 } 207 208 } 209 Serial.println(); 210 } 211 } 212 213 }; 214} 215 216Scope scope_1; 217Scope scope_2; 218 219uint8_t sample_input_pin() 220 { 221 const uint8_t clock_state = DCF77_Clock::get_clock_state(); 222 const 223 uint8_t sampled_data = 224 #if defined(__AVR__) 225 dcf77_inverted_samples 226 ^ (dcf77_analog_samples? (analogRead(dcf77_analog_sample_pin) > 200) 227 : 228 digitalRead(dcf77_sample_pin)); 229 #else 230 dcf77_inverted_samples 231 ^ digitalRead(dcf77_sample_pin); 232 #endif 233 234 digitalWrite(dcf77_monitor_pin, 235 sampled_data); 236 digitalWrite(dcf77_second_pulse_pin, ms_counter < 500 && clock_state 237 >= Clock::locked); 238 239 const uint8_t synthesized_signal = 240 tick 241 == Internal::DCF77::long_tick ? ms_counter < 200: 242 tick == Internal::DCF77::short_tick 243 ? ms_counter < 100: 244 tick == Internal::DCF77::sync_mark ? 0: 245 // 246 tick == DCF77::undefined --> default handling 247 // 248 allow signal to pass for the first 200ms of each second 249 (ms_counter 250 <=200 && sampled_data) || 251 // if 252 the clock has valid time data then undefined ticks 253 // 254 are data bits --> first 100ms of signal must be high 255 ms_counter 256 <100; 257 258 set_output<provide_filtered_output, Clock::locked, 259 dcf77_filtered_pin, 260 dcf77_inverted_filtered_pin, dcf77_filter_diff_pin> 261 (clock_state, 262 sampled_data, synthesized_signal); 263 264 set_output<provide_semi_synthesized_output, 265 Clock::unlocked, 266 dcf77_semi_synthesized_pin, dcf77_inverted_semi_synthesized_pin, 267 dcf77_semi_synthesized_diff_pin> 268 (clock_state, sampled_data, synthesized_signal); 269 270 271 set_output<provide_synthesized_output, Clock::free, 272 dcf77_synthesized_pin, 273 dcf77_inverted_synthesized_pin, dcf77_synthesized_diff_pin> 274 (clock_state, 275 sampled_data, synthesized_signal); 276 277 ms_counter+= (ms_counter < 1000); 278 279 280 scope_1.process_one_sample(sampled_data); 281 scope_2.process_one_sample(digitalRead(dcf77_synthesized_pin)); 282 283 284 return sampled_data; 285} 286 287 288 289 290void output_handler(const Clock::time_t 291 &decoded_time) { 292 // reset ms_counter for 1 Hz ticks 293 ms_counter = 0; 294 295 296 // status indicator --> always on if signal is good 297 // blink 298 3s on 1s off if signal is poor 299 // blink 1s on 3s off 300 if signal is very poor 301 // always off if signal is bad 302 303 const uint8_t clock_state = DCF77_Clock::get_clock_state(); 304 digitalWrite(dcf77_signal_good_indicator_pin, 305 306 clock_state >= Clock::locked ? 1: 307 clock_state 308 == Clock::unlocked? (decoded_time.second.digit.lo & 0x03) != 0: 309 clock_state 310 == Clock::free ? (decoded_time.second.digit.lo & 0x03) == 0: 311 0); 312 313 // compute output for signal synthesis 314 Internal::DCF77_Encoder now; 315 316 now.second = BCD::bcd_to_int(decoded_time.second); 317 now.minute 318 = decoded_time.minute; 319 now.hour = 320 decoded_time.hour; 321 now.weekday = decoded_time.weekday; 322 323 now.day = decoded_time.day; 324 now.month = 325 decoded_time.month; 326 now.year = decoded_time.year; 327 328 now.uses_summertime = decoded_time.uses_summertime; 329 now.leap_second_scheduled 330 = decoded_time.leap_second_scheduled; 331 now.timezone_change_scheduled 332 = decoded_time.timezone_change_scheduled; 333 334 now.undefined_minute_output 335 = false; 336 now.undefined_uses_summertime_output = 337 false; 338 now.undefined_abnormal_transmitter_operation_output = false; 339 now.undefined_timezone_change_scheduled_output 340 = false; 341 342 now.advance_minute(); 343 tick = now.get_current_signal(); 344} 345 346void 347 setup_serial() { 348 Serial.begin(115200); 349} 350 351void output_splash_screen() 352 { 353 Serial.println(); 354 Serial.println(F("DCF77 Superfilter 3.0")); 355 356 Serial.println(F("(c) 2015 Udo Klein")); 357 Serial.println(F("www.blinkenlight.net")); 358 359 Serial.println(); 360 Serial.print(F("Sample Pin: ")); Serial.println(dcf77_sample_pin); 361 362 Serial.print(F("Inverted Mode: ")); Serial.println(dcf77_inverted_samples); 363 364 #if defined(__AVR__) 365 Serial.print(F("Analog Mode: ")); 366 Serial.println(dcf77_analog_samples); 367 #endif 368 Serial.print(F("Monitor 369 Pin: ")); Serial.println(dcf77_monitor_pin); 370 Serial.println(); 371 372 373 if (provide_filtered_output) { 374 Serial.println(F("Filtered Output")); 375 376 Serial.print(F(" Filtered Pin: ")); Serial.println(dcf77_filtered_pin); 377 378 Serial.print(F(" Diff Pin: ")); Serial.println(dcf77_filter_diff_pin); 379 380 Serial.print(F(" Inverse Filtered Pin: ")); Serial.println(dcf77_inverted_filtered_pin); 381 382 Serial.println(); 383 } 384 385 if (provide_semi_synthesized_output) 386 { 387 Serial.println(F("Semi Synthesized Output")); 388 Serial.print(F(" 389 Filtered Pin: ")); Serial.println(dcf77_semi_synthesized_pin); 390 391 Serial.print(F(" Diff Pin: ")); Serial.println(dcf77_semi_synthesized_diff_pin); 392 393 Serial.print(F(" Inverse Filtered Pin: ")); Serial.println(dcf77_inverted_semi_synthesized_pin); 394 395 Serial.println(); 396 } 397 398 if (provide_synthesized_output) { 399 400 Serial.println(F("Synthesized Output")); 401 Serial.print(F(" 402 Filtered Pin: ")); Serial.println(dcf77_synthesized_pin); 403 Serial.print(F(" 404 Diff Pin: ")); Serial.println(dcf77_synthesized_diff_pin); 405 406 Serial.print(F(" Inverse Filtered Pin: ")); Serial.println(dcf77_inverted_synthesized_pin); 407 408 Serial.println(); 409 } 410 411 Serial.print(F("Second Pulse Pin: 412 ")); Serial.println(dcf77_second_pulse_pin); 413 Serial.print(F("Signal 414 Good Pin: ")); Serial.println(dcf77_signal_good_indicator_pin); 415 416 417 Serial.println(); 418 419 Serial.println(); 420 Serial.println(F("Initializing...")); 421 422 Serial.println(); 423}; 424 425void setup_pins() { 426 if (provide_filtered_output) 427 { 428 pinMode(dcf77_filtered_pin, OUTPUT); 429 pinMode(dcf77_filter_diff_pin, 430 OUTPUT); 431 pinMode(dcf77_inverted_filtered_pin, OUTPUT); 432 } 433 434 435 if (provide_semi_synthesized_output) { 436 pinMode(dcf77_semi_synthesized_pin, 437 OUTPUT); 438 pinMode(dcf77_semi_synthesized_diff_pin, OUTPUT); 439 pinMode(dcf77_inverted_semi_synthesized_pin, 440 OUTPUT); 441 } 442 443 if (provide_synthesized_output) { 444 pinMode(dcf77_synthesized_pin, 445 OUTPUT); 446 pinMode(dcf77_synthesized_diff_pin, OUTPUT); 447 pinMode(dcf77_inverted_synthesized_pin, 448 OUTPUT); 449 } 450 451 pinMode(dcf77_monitor_pin, OUTPUT); 452 pinMode(dcf77_signal_good_indicator_pin, 453 OUTPUT); 454 pinMode(dcf77_second_pulse_pin, OUTPUT); 455 pinMode(dcf77_sample_pin, 456 INPUT); 457 digitalWrite(dcf77_sample_pin, HIGH); 458} 459 460void setup_clock() 461 { 462 DCF77_Clock::setup(); 463 DCF77_Clock::set_input_provider(sample_input_pin); 464 465 DCF77_Clock::set_output_handler(output_handler); 466} 467 468void setup() { 469 470 setup_serial(); 471 output_splash_screen(); 472 setup_pins(); 473 setup_clock(); 474/* 475 476 pinMode(gnd_pin, OUTPUT); 477 digitalWrite(gnd_pin, LOW); 478 pinMode(pon_pin, 479 OUTPUT); 480 digitalWrite(pon_pin, LOW); 481 pinMode(vcc_pin, OUTPUT); 482 483 digitalWrite(vcc_pin, HIGH); 484 */ 485} 486 487void loop() { 488 Clock::time_t 489 now; 490 DCF77_Clock::get_current_time(now); 491 492 if (now.month.val > 0) 493 { 494 Serial.println(); 495 Serial.print(F("Decoded time: ")); 496 497 498 DCF77_Clock::print(now); 499 Serial.println(); 500 } 501 502 Serial.print(DCF77_Clock::get_clock_state()); 503 504 Serial.print(' '); 505 DCF77_Clock::debug(); 506 507 scope_1.print(); 508 509 scope_2.print(); 510} 511
DCF Analyzer / Clock v2.1
arduino
2020-01-18 bug release: power save switch fixed
1/* 2 ================================================================================ 3 4 DCF77 Analyzer / Clock version 2 5 ================================================================================ 6 7 This sketch is free software; you can redistribute it and/or 8 modify it under 9 the terms of the GNU Lesser General Public 10 License as published by the Free 11 Software Foundation; either 12 version 2.1 of the License, or (at your option) 13 any later version. 14 15 This sketch is distributed in the hope that it will 16 be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 Lesser General 21 Public License for more details. 22 23 You should have received a copy of the 24 GNU Lesser General Public 25 License along with this library; if not, write to 26 the Free Software 27 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 28 02110-1301 USA 29 ================================================================================ 30 31 32 This C++ code is far from optimized because I myself am an Arduino and C++ novice. 33 34 But even after learning some more now, I want to keep the code simpel and readable. 35 36 That is why I maybe over-documented the code to help understand what's going on. 37 38 39 Erik de Ruiter 40 2014-2020 41 42 43 44 May 2014 First version 45 March 46 2016 - big overhaul... 47 July 2016 - Start with building the 2.0 Clock and adapting 48 the sketch 49 50 Version 2.1 date 2020-01-18 51 - Powersafe function fixed. 52 53 54 Version 2.0 55 - This sketch is adapted for my 2.0 version of the DCF/Analyzer 56 Clock. It used the Arduino MEGA and the DCF Superfilter 57 by default and to 58 drive the many seperate LED's I now use the ports of an Arduino Mega instead of 59 a Maxim 7219 chip. 60 This is because driving LED's with many different Voltage/Current 61 specs is problematic with the Maxim chip. 62 Lighting additional LED's for expample 63 will influence (dim) the LED's already on. As I'm not an electronics engineer 64 65 my only solution was to use the extra ports of the Arduino Mega. Ofcourse you 66 can use transistors or extra chips to 67 drive the LED's but for me this was 68 the obvious solution. 69 - Removed all the Maxim Common Anode display code 70 71 72 73 74 Version 1.72 75 - Option: Use a cheap Ebay PIR detector to shut off selectable 76 display's when no activity is detected. 77 The switch off delay can be set by 78 the user to prevent the display shutting of if a person 79 is not moving but 80 the display should be on. 81 - Now the display Night shut-down can be disabled 82 by making both values 'POWERSAVINGOFFTIME' 83 and 'POWERSAVINGONTIME' zero. 84 85 - Fixed temperature display not shutting off at powersave mode. 86 - errorCounter 87 display did not reset every hour -fixed 88 89 Version 1.71 90 - User option to 91 reset temperature min/max memory at midnight 92 93 Version 1.7: 94 - The resolution 95 of the temperature display is improved: from 0.5 to 0.1 degrees Celsius 96 Because 97 of the time the DS18B20 sensor needs to convert the temperature and to keep the 98 code clean, 99 the temperature display is updates once per minute. 100 - Parity 101 check routine optimized. 102 - More reliable check for bad DCF data, preventing 103 RTC update with invalid data. 104 - EoB error now clears inner LED ring as it should. 105 106 - The DCF OK LED now displays the condition of the DCF signal more reliably. Turns 107 off immediately if an error occurs 108 and only turns ON when all 3 parity bits 109 are OK. 110 111 Version 1.6: 112 - Changed temperature function to only calculate 113 once per minute. Got strange errors before the change because 114 I used a delay 115 of 100ms to give the DS18B20 sensor time to calculate the temperature. But the delay 116 function is 117 a very bad idea in most c++ code so I finally got rid of it. 118 119 120 Version 1.5: 121 - Complete overhaul of the scanSignal function and the rest of 122 the code! My first attempt worked but could be improved... 123 - The rPW and rPT 124 led's did not work as I intended so that is corrected now. 125 - The End of Buffer 126 error check routine does work now as it should. 127 - I incorporated a Parity check 128 of the incoming DCF signal. In the signal 3 Parity bits are sent so now these are 129 130 checked and only if all three are OK, the received time information is accepted, 131 the display is updated and the RTC synced. 132 if desired, you can attach 3 extra 133 dual-color LED's (Common Cathode) to see if each of the 3 Parity bits are OK or 134 Failed. 135 - I made wiring (or changing the wiring) much easier I think by putting 136 all the PIN config in one easy to read table 137 - As long as you use 1 DS18B20 138 temp. sensor, I edited the code so you no longer need to figure out the address 139 of the I2C device. 140 - Big clean-up of the code... 141 - Powersaving by shutting 142 off the displays (the clock remains functioning as normal) 143 can now be configured 144 somewhat easier by editing two variables POWERSAVINGONTIME and POWERSAVINGOFFTIME. 145 146 - changed some variable names: 147 - Maxim instances 'lc' and 'lc1' are now MaximCC 148 and MaximCA 149 - Display description MaximDcfTime is now DisplayTempWeek 150 151 - DCF77SOUNDPIN is now BUZZERSWITCHPIN 152 - LED/Display test after power up 153 now build in 154 155 156 157 Short description: 158 159 Power On: 160 After 161 power-on, first a LED test is performed. The LED's and displays lite up sequentially 162 to keep the power consumption low. 163 Then the clock starts receiving DCF pulses 164 and when a Minute Mark (2 seconds gap) is detected, the Minute Marker LED is lit 165 166 and the buffer counter is reset. The inner LED ring now will show the incoming 167 DCF pulses which are also stored in the buffer. 168 At 3 moments during reception 169 of data the parity DCF bits are checked to see if the data is valid. 170 171 Valid 172 data received: 173 When, at the end of the minute, after the Minute Mark is detected 174 (BF (Buffer Full) LED is lit), all three parity bits are OK 175 ('DCF OK' LED 176 is lit), the buffer information is used to extract time and date information. 177 178 Then the RTC clock is updated ('RTC Synced' LED is lit) and the inner LED ring 179 information is copied to the outer LED ring. 180 The time, date and week display, 181 day LED, summer/wintertime and leap year LED information is updated with the new 182 time information. 183 184 No valid data: 185 When one or more of the parity 186 bits are not OK because of a noisy signal, receiving of DCF information is continued 187 but 188 will not be used to update the RTC, display's and LED's. The outer LED 189 ring, 'RTC synced' and 'DCF OK' LED's will be reset. 190 Time, date, week, day 191 LED, summer/wintertime LED and leap year LED are not affected and keep displaying 192 the last received valid values. 193 The 'Period Time' and/or 'Period With' error 194 LED's will indicate the error(s) and the error counter display is updated. 195 196 Every hour, the error display will bet set to zero. 197 The EoB, End of 198 Buffer LED is lit when more DCF pulses are received before the Minute Mark is detected 199 due to a noisy signal. 200 (When a minute Mark is detected we should have no 201 more than 58 bits/pulses) 202 After the detection of the Minute Marker, a new 203 cycle is started. 204 205 Temperature: 206 At the 30 second mark, the temperature 207 display will show the High and Low values of the past period after the last reset. 208 209 210 Chime: 211 If the CHIME switch is ON/HIGH, then at the beginning of each 212 hour, the Chime (if connected) will sound 213 At night time, a time set by the 214 user in the code itself, the chime is disabled. 215 216 Power saving - display's 217 shut off 218 This will only work IF the Power Save switch is HIGH: 219 1. 220 NIGHT SHUT OFF 221 At times set by the user, the displays are shutt off at 222 night and turned on in the morning. 223 Look at the POWERSAVINGOFFTIME and 224 POWERSAVINGONTIME variables. 225 Check the function <turnDisplaysOff> to 226 select WHICH displays you want to shut off at night. 227 2. PIR SENSOR 228 Connect 229 a PIR sensor and activate the PIR option POWERSAVE_BY_PIR and the the delay at PIR_DELAY_TIME. 230 231 Every time the PIR detector senses movement, a minute counter is reset but 232 if no movement is detected 233 longer than the PIR_DELAY_TIME, the displays 234 are shut off. 235 When movement occurs, the displays immediately switch on. 236 237 Note: as said before, the clock will function normally while the displays 238 are shut off. 239 240 DCF beep: 241 With a switch, connected to pin BUZZERSWITCHPIN, 242 you can hear the received DCF bits coming in. 243 The tone duration is equivalent 244 to pulse width of the DCF bits, so either 100 or 200 ms. 245 246 Miscelleanous: 247 248 When the RTC battery is empty or a connection fault is detected, the RTC Error 249 LED is lit. 250 251 252 253 254 CREDITS: 255 I learned a lot from the work of Matthias 256 Dalheimer and Thijs Elenbaas who made their own DCF77 decoders. 257 Without their 258 work I would not have known where to start. 259 I ended up writing my own code (using 260 bits and pieces of their ideas) so I could understand what is happening... 261 My 262 code is far from efficient or advanced but it does work and I know what is going 263 on. 264 265 Interesting websites: 266 267 - Brett Oliver : http://home.btconnect.com/brettoliver1/ 268 269 - Joop Tap : http://www.jooptap.nl 270 - Thijs Ellenbaas : http://thijs.elenbaas.net/2012/04/arduino-dcf77-radio-clock-receiver-hardware-2/ 271 272 - Mathias Dalheimer : https://github.com/roddi/DCF77-Arduino/blob/master/DCF77Servoclock/DCF77.h 273 274 - DCF77 wikipedia : https://en.wikipedia.org/wiki/DCF77 275 - Much more DCF77 276 info : http://www.picbasic.nl/indexes_uk.htm 277 278 - My Flickr website : https://www.flickr.com/photos/edr1924/albums 279 280 - My Github website : https://github.com/deruiter 281 282 */ 283 284//---------------------------------------------------------------------------------------------------------- 285// 286 Libraries 287//---------------------------------------------------------------------------------------------------------- 288 289// 290 Arduino (new) Time library .................................... http://www.pjrc.com/teensy/td_libs_Time.html 291#include 292 <Time.h> 293 294// Enable this line if using Arduino Uno, Mega, etc. 295#include 296 <Wire.h> 297 298// a basic DS1307 library that returns time as a time_t .......... 299 http://www.pjrc.com/teensy/td_libs_DS1307RTC.html 300#include <DS1307RTC.h> 301 302// 303 Maxim 7219 displays library ................................... http://playground.arduino.cc/Main/LEDMatrix 304// 305 !!! NOTE: you must use a special version of the Ledcontrol.h library to get Common 306 Anode support 307// because the Maxim chip is normally only suitable for common 308 CATHODE displays! 309#include <LedControl.h> 310 311//SPI interface library .......................................... 312 http://arduino.cc/en/Reference/SPI 313#include <SPI.h> 314 315// OneWire lets you 316 access 1-wire devices made by Maxim/Dallas, 317// such as DS18S20, DS18B20, DS1822 318 .............................. http://www.pjrc.com/teensy/td_libs_OneWire.html 319// 320 The DallasTemperature library can do all this work for you! ... http://milesburton.com/Dallas_Temperature_Control_Library 321#include 322 <OneWire.h> 323 324//---------------------------------------------------------------------------------------------------------- 325// 326 Arduino UNO Pin connections in an easy to read table 327// 328// input - Rx - used 329 for programming/communication with PC 330// output - Tx - used for programming/communication 331 with PC 332#define DCF77PIN 2 // input - DCF signal from antenna pcb. 333 Pin must an interrupt input! 334#define PIRDETECTORPIN 3 // input - PIR detector: 335 check for activity in the room to activate displays 336#define BUZZERSWITCHPIN 4 337 // input - SWITCH - turn on/off DCF77 'beep' piezo buzzer / ON = HIGH, OFF 338 = LOW 339#define CHIMESWITCHPIN 5 // input - SWITCH - turn on/off the hourly 340 chime sound / ON = HIGH, OFF = LOW 341#define POWERSAVESWITCHPIN 6 // input - SWITCH 342 - turn on/off the power save feature so display is always on / ON = HIGH, OFF = 343 LOW 344#define TEMPSENSORPIN 8 // input - Dallas One Wire DS18B20 temperature 345 sensor 346#define TEMPRESETPIN 9 // input - PUSH BUTTON - reset temperature 347 min/max memory / HIGH = reset 348#define MAXIMCCLD 10 // output - CS/LOAD 349 - pseudo SPI connection to the Maxim 7219 chip - 7 segment displays 350#define MAXIMCCCLK 351 11 // output - CLOCK - pseudo SPI connection to the Maxim 7219 chip - 7 352 segment displays 353#define MAXIMCCDATA 12 // output - DATA - pseudo SPI 354 connection to the Maxim 7219 chip - 7 segment displays 355// !! Pins 22 through 356 53 are only to be used for LED's 357#define LED_SUNDAY 22 // output - LED 358 - Sunday 359#define LED_MONDAY 23 // output - LED - Monday 360#define LED_TUESDAY 361 24 // output - LED - Tuesday 362#define LED_WEDNESDAY 25 // output - 363 LED - Wednesday 364#define LED_THURSDAY 26 // output - LED - Thursday 365#define 366 LED_FRIDAY 27 // output - LED - Friday 367#define LED_SATURDAY 28 // 368 output - LED - Saturday 369#define LED_CEST 29 // output - LED - Summertime 370 CEST 371#define LED_CET 30 // output - LED - Wintertime CET 372#define 373 LED_LEAPYEAR 31 // output - LED - Leap year 374#define LED_RTCERROR 32 // 375 output - LED - problem reading RTC data (empty battery/connection) 376#define LED_RTCSYNC 377 33 // output - LED - On when RTC is succesfully synced with the DCF time 378#define 379 LED_TEMP 34 // output - LED - temperature is displayed 380#define LED_OPTION1 381 35 // output - LED - optional 1 data is displayed 382#define LED_OPTION2 383 36 // output - LED - optional 2 data is displayed 384#define LED_ERRORPT 385 37 // output - LED - DCF Period Time error 386#define LED_ERRORPW 38 // 387 output - LED - DCF Period Width error 388#define LED_BUFFERFULL 39 // output 389 - LED - Buffer full indicator, next the data will be analized 390#define LED_MINUTEMARKER 391 40 // output - LED - End of DCF data stream detected before buffer is filled, 392 data is corrupt 393#define LED_BUFFEROVERFLOW 41 // output - LED - More data received 394 in one minute than expected due to bad signal 395#define LED_DCFSTATUS 42 // 396 output - LED - On when we have good DCF data 397#define LED_POWERSAVE 43 // 398 output - LED - Power save mode is activated, some displays are off 399#define LED_PARITY1PASS 400 44 // output - LED - Parity 1 bit is OK 401#define LED_PARITY1FAIL 45 // output 402 - LED - Parity 1 bit FAILED 403#define LED_PARITY2PASS 46 // output - LED - Parity 404 2 bit is OK 405#define LED_PARITY2FAIL 47 // output - LED - Parity 2 bit FAILED 406#define 407 LED_PARITY3PASS 48 // output - LED - Parity 3 bit is OK 408#define LED_PARITY3FAIL 409 49 // output - LED - Parity 3 bit FAILED 410#define LED_PIRMOTION 50 // 411 output - LED - On when PIR is detecting motion 412 413// Analog pins 414#define 415 BUZZER A7 // output - Piezo buzzer for DCF77 'beep' (to '+' of the buzzer) 416#define 417 SPEAKERVOLPIN A6 // output - Sound Board volume - LOW = volume one notch lower. 418 SPEAKERVOLUME determines how many times this output is activated after power on 419#define 420 CHIMEPIN A5 // output - Chime Activate - OUTPUT LOW = Activate Chime on Adafruit 421 Soundboard FX 422// USED for DS1307 RTC // I2C DATA - connect to Real Time Clock 423 pcb 424// USED for DS1307 RTC // I2C CLOCK - connect to Real Time Clock pcb 425 426//---------------------------------------------------------------------------------------------------------- 427// 428 DS18B20 initialization 429//---------------------------------------------------------------------------------------------------------- 430OneWire 431 ds(TEMPSENSORPIN); // define Onewire instance DS 432 433//---------------------------------------------------------------------------------------------------------- 434// 435 Maxim 7219 Matrix Display initialization 436//---------------------------------------------------------------------------------------------------------- 437/* 438 439 clearDisplay(int addr) ............................................. clears the 440 selected display 441 MaximCC.shutdown(int addr, boolean) ................................ 442 wake up the MAX72XX from power-saving mode (true = sleep, false = awake) 443 MaximCC.setIntensity(int 444 addr, value) .............................. set a medium brightness for the Leds 445 (0=min - 15=max) 446 MaximCC.setLed(int addr, int row, int col, boolean state) .......... 447 switch on the led in row, column. remember that indices start at 0! 448 MaximCC.setRow(int 449 addr, int row, byte value) ...................... this function takes 3 arguments. 450 example: MaximCC.setRow(0,2,B10110000); 451 MaximCC.setColumn(int addr, int col, 452 byte value) ................... this function takes 3 arguments. example: MaximCC.setColumn(0,5,B00001111); 453 454 MaximCC.setDigit(int addr, int digit, byte value, boolean dp) ...... this function 455 takes an argument of type byte and prints the corresponding digit on the specified 456 column. 457 The 458 range of valid values runs from 0..15. All values between 0..9 are printed as digits, 459 460 values between 461 10..15 are printed as their hexadecimal equivalent 462 MaximCC.setChar(int addr, 463 int digit, char value, boolean dp) ....... will display: 0 1 2 3 4 5 6 7 8 9 A B 464 C D E F H L P; - . , _ <SPACE> (the blank or space char) 465 POWERSAVESWITCHPIN 466 467 ***** Please set the number of devices you have ***** 468 But the maximum default 469 of 8 MAX72XX wil also work. 470 LedConrol(DATAIN, CLOCK, CS/LOAD, NUMBER OF MAXIM 471 CHIPS) 472 */ 473 474// lc is for the Maxim displays 475LedControl MaximCC = LedControl(MAXIMCCDATA, 476 MAXIMCCCLK, MAXIMCCLD, 7, false); // Define pins for Maxim 72xx and how many 72xx 477 we use 478 479//---------------------------------------------------------------------------------------------------------- 480// 481 User settings, variable and array definitions 482//---------------------------------------------------------------------------------------------------------- 483 484// 485 The value below is not a PIN number but a value to set how many times the 'Lower 486 volume' input on the sound board is activated 487// so that way the volume of the 488 sound board can be lowered after power up, if desired. 489#define SPEAKERVOLUME 490 12 491 492// Choose if you want a test of all LED's and Displays after a startup 493// 494 '1' = Yes, '0' = No 495#define PERFORM_LED_TEST 1 496// Delay between each 7 segment 497 display in ms 498#define LEDTEST_DELAY_DISPLAYS 600 499// Delay between each LED 500 in the LED ring and other LED's in ms 501#define LEDTEST_DELAY_LED_RING 20 502 503// 504 Choose if you want to configure the DS18B20 temperature sensor ONCE to the highest 505 resolution. 506// this is needed after using the sensor for the first time. After 507 running the software 508// with this setting ON one time, shut it off. 509// '1' 510 = ON, '0' = OFF 511#define CONFIGURE_DS18B20 0 512 513// POWERSAVE TIME - MANDATORY 514 values! 515// 516// to define day and night time. This is used for power saving 517 display ON and OFF time 518// AND to determine if the chime will be activated (during 519 the daytime). 520// 521// ONLY the displays are shut off at power saving time, the 522 clock remains fully active. 523// TO DISABLE the display power-off at any time, 524 simply set the Power-save switch to OFF 525// 526// Power save switch ON: displays 527 will be shutt OFF at the set night time 528// AND if POWERSAVE_BY_PIR 529 function if activated and there is no 530// movement for 531 the set PIR_DELAY_TIME. 532// Power save switch OFF: displays always on, chime only 533 at daytime. 534// 535// values are in 'Hour' format, so 8PM will be '20', NOT 20:00 536 or 8PM... 537#define POWERSAVINGOFFTIME 9 // displays are activated 538#define POWERSAVINGONTIME 539 22 // displays are shutt off 540 541// User option: activate the displays only 542 when there is activity in the room 543// '1' = ON, '0' = OFF 544#define POWERSAVE_BY_PIR 545 1 546// delay in MINUTES to wait after no detection before shutting off the displays 547#define 548 PIR_DELAY_TIME 30 549 550// User option to reset temperature min/max memory at midnight 551// 552 '1' = Reset at midnight, '0' = Only manual reset 553#define TEMPRESET_MIDNIGHT 1 554 555//------------------------------------------------------------------------------- 556// 557 define miscellaneous parameters 558#define DS1307_I2C_ADDRESS 0x68 // define the 559 RTC I2C address 560#define DCF_INTERRUPT 0 // Interrupt number associated 561 with pin 562 563// definition of Maxim 7219 display number wiring sequence 564// 565 first Maxim 7219 in wiring 'daisychain' must be '0', next '1' etc. 566// COMMON 567 CATHODE DISPLAYS 568#define LedRingInner 0 569#define LedRingOuter 1 570#define 571 DisplayBufferBitError 2 572#define DisplayPeriodPulse 3 573#define DisplayTempWeek 574 4 575#define DisplayDate 5 576#define DisplayTime 6 577 578// definition of display 579 brighness levels 580#define BrightnessLedRingOuter 1 581#define BrightnessLedRingInner 582 1 583#define BrightnessDisplayTime 1 584#define BrightnessDisplayDate 7 585#define 586 BrightnessDisplayTempWeek 15 587#define BrightnessDisplayPeriodPulse 2 588#define 589 BrightnessDisplayBufferBitError 15 590 591// Pulse flanks 592static unsigned long 593 leadingEdge = 0; 594static unsigned long trailingEdge = 0; 595unsigned long previousLeadingEdge 596 = 0; 597 598// used in <Int0handler> 599volatile unsigned int DCFSignalState = 0; 600 // interrupt variables ALWAYS need volatile qualifier!! 601 602// used in <loop> 603int 604 previousSecond = 0; 605unsigned int previousSignalState = 0; 606 607// DCF Buffers 608 and indicators 609static int DCFbitBuffer[59]; // here, 610 the received DCFbits are stored 611const int bitValue[] = {1, 2, 4, 8, 10, 20, 40, 612 80}; // these are the decimal values of the received DCFbits 613 614// only after 615 start on a new minute, display received bits on inner LED ring 616boolean MinuteMarkerFlag 617 = false; 618int bufferPosition = 0; 619int previousMinute = 0; 620int previousHour 621 = 0; 622 623// variables to check if DCF bits are vald 624bool dcfValidSignal = 625 false; 626int dcfP1counter = 0; 627int dcfP2counter = 0; 628int dcfP3counter = 0; 629int 630 dcfParityCheckP1 = 0; 631int dcfParityCheckP2 = 0; 632int dcfParityCheckP3 = 0; 633 634// 635 dcf variables to store decoded DCF time in 636int dcfMinute = 0; 637int dcfHour 638 = 0; 639int dcfDay = 0; 640int dcfWeekDay = 0; 641int dcfMonth = 0; 642int dcfYear 643 = 0; 644int dcfDST = 0; 645int leapYear = 0; 646 647// variables used to store weeknumber 648 and daynumer values 649int dayNumber; 650int weekNumber; 651 652// error counter 653 variable 654int errorCounter = 0; 655boolean errorCondition = false; 656 657// miscelleanous 658 variables 659boolean daytimeChange = true; 660boolean dayTime = false; 661int dcf77SoundSwitch 662 = 0; 663 664// temperature variables 665byte present = 0; 666byte DS18B20Data[12]; 667int 668 maxTemp = 0; 669int minTemp = 0; 670int lowByte = 0; 671int highByte = 0; 672float 673 tempReading = 0; 674int tempCelsius = 0; 675boolean tempResetButton = false; 676 677// 678 PIR detector variables 679int pirActivity = 0; 680int pirDisplaysState = 1; 681unsigned 682 int pirTimer = 0; 683unsigned long previousTimePIR = 0; 684 685//============================================================================== 686// 687 SETUP 688//============================================================================== 689void 690 setup() 691{ 692 // initialize Serial communication 693 Serial.begin(9600); 694 695 696 // initialize PIN connections 697 pinMode(DCF77PIN, INPUT); 698 pinMode(TEMPRESETPIN, 699 INPUT); 700 pinMode(BUZZERSWITCHPIN, INPUT); 701 pinMode(CHIMESWITCHPIN, INPUT); 702 703 pinMode(POWERSAVESWITCHPIN, INPUT); 704 pinMode(PIRDETECTORPIN, INPUT); 705 pinMode(CHIMEPIN, 706 OUTPUT); 707 pinMode(SPEAKERVOLPIN, OUTPUT); 708 // initialize LED pins 22 - 50 709 710 for (int i1 = 22; i1 <= 50; i1++) 711 { 712 pinMode(i1, OUTPUT); 713 } 714 715 716 // Initialize variables, LED displays and LED's 717 initialize(); 718 719 // 720 Initialize DCF77 pulse interrupt on pin DCF_INTERRUPT, looking for a change of the 721 signal, 722 // so either rising or falling edge pulses will trigger the interrupt 723 handler and 724 // execute the int0handler function. 725 attachInterrupt(DCF_INTERRUPT, 726 int0handler, CHANGE); 727 728 // Initialize RTC and set as SyncProvider. 729 // 730 Later RTC will be synced with DCF time 731 setSyncProvider(RTC.get); // the function 732 to get the time from the RTC 733 // check if RTC has set the system time 734 if 735 (timeStatus() != timeSet) 736 { // Unable to sync with the RTC - activate RTCError 737 LED 738 digitalWrite(LED_RTCERROR, HIGH); 739 } 740 else 741 { 742 // RTC 743 has set the system time - dim RTCError LED 744 digitalWrite(LED_RTCERROR, LOW); 745 746 } 747 748 // After power on, set the speaker volume of the Adafruit Audio Board 749 750 // initialize both pins to LOW which is the default output state 751 digitalWrite(SPEAKERVOLPIN, 752 LOW); 753 digitalWrite(CHIMEPIN, LOW); 754 755 // lower soundboard default volume 756 with 'SPEAKERVOLUME' steps 757 for (int i = 0; i <= SPEAKERVOLUME; i++) 758 { 759 760 digitalWrite(SPEAKERVOLPIN, HIGH); 761 delay(100); 762 digitalWrite(SPEAKERVOLPIN, 763 LOW); 764 delay(100); 765 } 766 767 // The following function should run only 768 once. 769 // It is used to configure the temperature resolution of the DS18B20 770 sensor 771 if (CONFIGURE_DS18B20 == 1) 772 { 773 configureDS18B20(); 774 } 775 776 777 // use for test purposes and/or setting the RTC time manually 778 // setTime(23, 779 59, 40, 31, 12, 13); 780 // RTC.set(now()); 781 782 // Request the temperature 783 conversion 784 calculateTemp(); 785 786 // check if a LED test is needed 787 if 788 (PERFORM_LED_TEST == 1) 789 { 790 // do a LED test 791 ledTest(); 792 } 793 794 else 795 { 796 // if not doing a LED test, we need to wait a bit for the DS18B20 797 sensor to get ready 798 delay(750); 799 } 800 801 // Now get the temperature 802 from the sensor and display it 803 displayTemp(); 804 805 // activate errorCounter 806 display after LED test 807 ledDisplay(DisplayBufferBitError, "R", 0); 808} 809 810//============================================================================== 811// 812 LOOP 813//============================================================================== 814void 815 loop() 816{ 817 // check first if pulse direction is changed (rising or falling) 818 819 // else we would keep evaluating the same pulse 820 if (DCFSignalState != previousSignalState) 821 822 { 823 // 'reset' state of variable 824 previousSignalState = DCFSignalState; 825 826 827 // evaluate incoming pulse 828 scanSignal(); 829 } 830 831 // check if 832 switches are changed and act upon it 833 checkSwitches(); 834 835 // check for 836 PIR movement 837 checkPIR(); 838 839 // execute tasks that must happen only once 840 every second, minute or hour 841 //---------------------------------------------------------------------------- 842 843 tasksEverySecond(); 844 tasksEveryMinute(); 845 tasksEveryHour(); 846} 847 848//================================================================================================================ 849// 850// 851 Function name : processDcfBit 852// called from : <scanSignal> 853// 854// Purpose 855 : Evaluates the signal as it is received. Decides whether we received a "1" 856 or a "0" 857// and perform checks to see if the pulse timing is 858 within limits 859// Parameters : none 860// Return value : none 861// 862//================================================================================================================ 863/* 864 865 pulse pulse 866 width width 867 |- 868 -| |-- --| |----- END OF MINUTE marker:2000ms -----| 869 870 ___ _______ ___ ___ 871 _______ 872 | 0 | | 1 | | 0 | 873 | 0 | | 1 | 874 | | 875 | | | | | | 876 | | 877 | | | | | | 878 | | | | 879 ______| |_______________| 880 |___________| |___________________________________| |_______________| 881 |__ _ _ _ 882 ^ ^ ^ ^ ^ ^ ^ 883 ^ ^ ^ ^ 884 1000 2100 2000 885 2200 3000 3100 NO PULSE 5000 5100 6000 886 6200 << example millis() value 887 = 888 end of Minute indication 889 ^ ^ ^ ^ 890 ^ 891 DCFbit# 56 DCFbit# 57 DCFbit# 58 892 DCFbit# 0 DCFbit# 1 etc... << DCF bit 893 received 894 895 ^ ^ ^ 896 previous 897 leading trailing 898 leading edge edge edge 899 900 901 ^ ^ 902 flanktime (rising or falling) 903 904 */ 905 906void scanSignal() 907{ 908 909 //-------------------------------------------------------------------- 910 // 911 Check for Rising-Edge signal and perform checks 912 //-------------------------------------------------------------------- 913 914 if (DCFSignalState == 1) 915 { 916 // store Rising-Edge Time to check later 917 if the time between two pulses is valid 918 leadingEdge = millis(); 919 // 920 not much to do now so exit. 921 return; 922 } 923 924 //-------------------------------------------------------------------- 925 926 // Check for Falling-Edge signal and perform checks 927 //-------------------------------------------------------------------- 928 929 930 if (DCFSignalState == 0) 931 { 932 // store Trailing-Edge Time to check later 933 if the Pulse Width is valid 934 trailingEdge = millis(); 935 936 // display 937 period width time on "L"eft side of the 8 digit Maxim 72xx LED display 938 ledDisplay(DisplayPeriodPulse, 939 "L", (leadingEdge - previousLeadingEdge)); 940 // display pulse width time 941 on the "R"ight side of the 8 digit Maxim 72xx LED display 942 ledDisplay(DisplayPeriodPulse, 943 "R", (trailingEdge - leadingEdge)); 944 945 //-------------------------------------------------------------------------------- 946 947 // Check PERIOD TIME 948 //-------------------------------------------------------------------------------- 949 950 // If this flank UP is detected quickly after previous flank UP this is an incorrect 951 952 // Period Time (should be 1000ms -or 2000ms after second 58-) that we shall 953 reject 954 if ((leadingEdge - previousLeadingEdge) < 900) 955 { 956 // 957 rPW - ERROR: Periode Time (rising flank to rising flank) time is too short -> REJECTED 958 959 error(LED_ERRORPW); 960 errorCondition = true; 961 } 962 //-------------------------------------------------------------------------------- 963 964 // CHECK PULSE TIME 965 //-------------------------------------------------------------------------------- 966 967 // If the detected pulse is too short it will be an incorrect pulse that we 968 shall reject 969 // should be 100 and 200 ms ideally 970 if (((trailingEdge 971 - leadingEdge) < 70) || ((trailingEdge - leadingEdge) > 230)) 972 { 973 //rPT 974 - ERROR: Pulse Width too short or too long -> REJECTED 975 error(LED_ERRORPT); 976 977 errorCondition = true; 978 } 979 980 // if we had an error return and 981 start over 982 if (errorCondition == true) 983 { 984 errorCondition = 985 false; 986 // although we have an error, store current rising edge time to 987 compare at the next Rising-Edge. 988 previousLeadingEdge = leadingEdge; 989 990 return; 991 } 992 993 //-------------------------------------------------------------------- 994 995 // no errors found so now we can continue 996 //-------------------------------------------------------------------- 997 998 999 // first we turn any error Led's OFF 1000 digitalWrite(LED_ERRORPW, LOW); 1001 1002 digitalWrite(LED_ERRORPT, LOW); 1003 digitalWrite(LED_BUFFERFULL, LOW); // 1004 previous BF 1005 digitalWrite(LED_BUFFEROVERFLOW, LOW); // previous EoB 1006 digitalWrite(LED_MINUTEMARKER, 1007 LOW); // previous EoM 1008 1009 // END OF MINUTE check, looking for a gap of 1010 approx. 2000ms 1011 if (leadingEdge - previousLeadingEdge > 1900 && leadingEdge 1012 - previousLeadingEdge < 2100) 1013 { 1014 // end of minute detected: 1015 finalizeBuffer(); 1016 1017 } 1018 1019 // refresh previousLeadingEdge time with the new leading edge time 1020 1021 previousLeadingEdge = leadingEdge; 1022 1023 //-------------------------------------------------------------------------------- 1024 1025 // process DCF bits 1026 //-------------------------------------------------------------------------------- 1027 1028 // distinguish between long and short pulses 1029 if (trailingEdge - leadingEdge 1030 < 170) 1031 { 1032 // call processDcfBit function and sent it the value '0' 1033 1034 processDcfBit(0); 1035 // if switch is HIGH, the DCF pulses are audible 1036 1037 if (dcf77SoundSwitch == 1) 1038 buzzer(100); 1039 } 1040 else 1041 1042 { 1043 // call processDcfBit function and sent it the value '1' 1044 processDcfBit(1); 1045 1046 // if switch is HIGH, the DCF pulses are audible 1047 if (dcf77SoundSwitch 1048 == 1) 1049 buzzer(200); 1050 } 1051 } // if (DCFSignalState == 0) 1052} // 1053 void scanSignal(); 1054 1055//================================================================================================================ 1056// 1057// 1058 Function name : processDcfBit 1059// called from : <scanSignal> 1060// 1061// Purpose 1062 : after reception of one good DCF bit, do some checks and save it in the 1063 DCFbitBuffer array 1064// Parameters : none 1065// Return value : none 1066// 1067//================================================================================================================ 1068 1069void 1070 processDcfBit(int dcfBit) 1071{ 1072 //-------------------------------------------------------------------- 1073 1074 // display values on the 7 segment displays 1075 //-------------------------------------------------------------------- 1076 1077 // display bufferPosition, digits 7,6 1078 MaximCC.setChar(DisplayBufferBitError, 1079 7, bufferPosition / 10, false); 1080 MaximCC.setChar(DisplayBufferBitError, 6, bufferPosition 1081 % 10, false); 1082 1083 // display received DCFbit, digit 4 1084 MaximCC.setChar(DisplayBufferBitError, 1085 4, dcfBit, false); 1086 1087 //-------------------------------------------------------------------- 1088 1089 // display incoming DCF bits on inner LED ring 1090 //-------------------------------------------------------------------- 1091 1092 // only if we have valid DCF data or after an Minute Mark (EoM) signal 1093 // 1094 activate the inner LED ring and diplay incoming data 1095 if (dcfValidSignal == 1096 true || MinuteMarkerFlag == true) 1097 { 1098 // display received bits on inner 1099 LED ring 1100 MaximCC.setLed(LedRingInner, bufferPosition / 8, bufferPosition 1101 % 8, dcfBit); 1102 } 1103 1104 //-------------------------------------------------------------------- 1105 1106 // // Fill DCFbitBuffer array with DCFbit 1107 //-------------------------------------------------------------------- 1108 1109 DCFbitBuffer[bufferPosition] = dcfBit; 1110 1111 //-------------------------------------------------------------------- 1112 1113 // Parity check 1114 //-------------------------------------------------------------------- 1115 1116 // DURING reception of the DCF bits, calculate and display the results of the 1117 DCF parity check. 1118 // 1119 // There is a Parity bit for the minutes, the hours 1120 and for the date. 1121 // DCF77 works with EVEN parity, this works as follows: 1122 1123 // The hours for example have 6 bits plus a paritybit. The bits with value 1 are 1124 add up including the paritybit, 1125 // the result must be an even number. If there 1126 is a bit wrong received, a 0 is as 1, or a 1 is as 0 received, 1127 // then the 1128 result is uneven. source: http://www.picbasic.nl/frameload_uk.htm?http://www.picbasic.nl/info_dcf77_uk.htm 1129 1130 1131 if (bufferPosition == 0) 1132 { 1133 // reset the parity LED's 1134 digitalWrite(LED_PARITY1PASS, 1135 LOW); 1136 digitalWrite(LED_PARITY1FAIL, LOW); 1137 digitalWrite(LED_PARITY2PASS, 1138 LOW); 1139 digitalWrite(LED_PARITY2FAIL, LOW); 1140 digitalWrite(LED_PARITY3PASS, 1141 LOW); 1142 digitalWrite(LED_PARITY3FAIL, LOW); 1143 // reset variables 1144 dcfP1counter 1145 = 0; 1146 dcfP2counter = 0; 1147 dcfP3counter = 0; 1148 dcfParityCheckP1 = 1149 0; 1150 dcfParityCheckP2 = 0; 1151 dcfParityCheckP3 = 0; 1152 } 1153 1154 // 1155 ---------------------------------------- 1156 // First parity check: minute bits 1157 1158 // ---------------------------------------- 1159 if (bufferPosition == 28) 1160 1161 { 1162 for (int i = 21; i <= 27; i++) 1163 { 1164 // count the number 1165 of bits with the value '1' 1166 dcfP1counter += DCFbitBuffer[i]; 1167 } 1168 1169 1170 // perform P1 parity check. Parity is OK if the sum is an EVEN value 1171 if 1172 ((DCFbitBuffer[28] + dcfP1counter) % 2 == 0) 1173 { 1174 // Parity1 PASS LED 1175 ON 1176 digitalWrite(LED_PARITY1PASS, HIGH); 1177 // Parity P1 PASS 1178 1179 dcfParityCheckP1 = 1; 1180 } 1181 else 1182 { 1183 // Parity1 FAIL 1184 LED ON 1185 digitalWrite(LED_PARITY1FAIL, HIGH); 1186 // we have no valid 1187 data! 1188 dcfValidSignal = false; 1189 // Turn DCF OK LED OFF 1190 digitalWrite(LED_DCFSTATUS, 1191 LOW); 1192 } 1193 } 1194 1195 // ---------------------------------------- 1196 // 1197 Second parity check: hour bits 1198 // ---------------------------------------- 1199 1200 if (bufferPosition == 35) 1201 { 1202 for (int i = 29; i <= 34; i++) 1203 { 1204 1205 dcfP2counter += DCFbitBuffer[i]; 1206 } 1207 1208 // perform P2 parity 1209 check. Parity is OK if the sum is an EVEN value 1210 if ((DCFbitBuffer[35] + dcfP2counter) 1211 % 2 == 0) 1212 { 1213 // Parity2 PASS LED ON 1214 digitalWrite(LED_PARITY2PASS, 1215 HIGH); 1216 // Parity P2 PASS 1217 dcfParityCheckP2 = 1; 1218 } 1219 else 1220 1221 { 1222 // Parity2 FAIL LED ON 1223 digitalWrite(LED_PARITY2FAIL, HIGH); 1224 1225 // we have no valid data! 1226 dcfValidSignal = false; 1227 // Turn 1228 DCF OK LED OFF 1229 digitalWrite(LED_DCFSTATUS, LOW); 1230 } 1231 } 1232 1233 1234 // ---------------------------------------- 1235 // Third parity check: date bits 1236 1237 // ---------------------------------------- 1238 if (bufferPosition == 58) 1239 1240 { 1241 for (int i = 36; i <= 57; i++) 1242 { 1243 dcfP3counter += DCFbitBuffer[i]; 1244 1245 } 1246 // perform P3 parity check. Parity is OK if the sum is an EVEN value 1247 1248 (DCFbitBuffer[58] + dcfP3counter) % 2 == 0 ? dcfParityCheckP3 = 1 : dcfParityCheckP3 1249 = 0; 1250 1251 // Turn Parity2 'PASS' or 'FAIL' LED ON 1252 if (dcfParityCheckP3 1253 == 1) 1254 { 1255 // Parity2 PASS LED ON 1256 digitalWrite(LED_PARITY3PASS, 1257 HIGH); 1258 // Parity P3 PASS 1259 dcfParityCheckP3 = 1; 1260 } 1261 else 1262 1263 { 1264 // Parity2 FAIL LED ON 1265 digitalWrite(LED_PARITY3FAIL, HIGH); 1266 1267 // we have no valid data! 1268 dcfValidSignal = false; 1269 // Turn 1270 DCF OK LED OFF 1271 digitalWrite(LED_DCFSTATUS, LOW); 1272 } 1273 1274 // 1275 ---------------------------------------- 1276 // finally, check all Parity bits 1277 1278 // ---------------------------------------- 1279 dcfParityCheckP1 + dcfParityCheckP2 1280 + dcfParityCheckP3 == 3 ? dcfValidSignal = true : dcfValidSignal = false; 1281 } 1282 1283 1284 //-------------------------------------------------------------------- 1285 // 1286 before continuing with the next bit, increment counter 1287 //-------------------------------------------------------------------- 1288 1289 bufferPosition++; 1290 1291 //-------------------------------------------------------------------- 1292 1293 // check if we have not received too many pulses? 1294 //-------------------------------------------------------------------- 1295 1296 if (bufferPosition > 59) 1297 { 1298 // Buffer Overflow ERROR - we have received 1299 more pulses before reaching 1300 // the 2 second 'gap' signalling the end of the 1301 minute. 1302 //This error may be due to a noisy signal giving addition peaks/dcfBits 1303 1304 // So clear both DCFbit displays and start again. 1305 1306 // Reset buffer 1307 counter 1308 bufferPosition = 0; 1309 // clear inner LED ring 1310 MaximCC.clearDisplay(LedRingInner); 1311 1312 // turn Buffer Overflow Error LED ON 1313 error(LED_BUFFEROVERFLOW); 1314 // 1315 exit 1316 return; 1317 } 1318 1319 //-------------------------------------------------------------------- 1320 1321 // everything OK so we wait for next incoming DCFbit 1322 //-------------------------------------------------------------------- 1323} 1324 1325//================================================================================================================ 1326// 1327// 1328 Function name : finalizeBuffer 1329// called from : <scanSignal> 1330// 1331// Purpose 1332 : Process the succesfully received DCF data of one minute 1333// Parameters 1334 : none 1335// Return value : none 1336// 1337//================================================================================================================ 1338 1339void 1340 finalizeBuffer(void) 1341{ 1342 //-------------------------------------------------------------------- 1343 1344 // We are here because of the detected 2 second 'gap'. 1345 // Now check if it 1346 correspondends with the buffer counter 1347 // 'bufferPosition' which should be 1348 value 59 1349 //-------------------------------------------------------------------- 1350 1351 if (bufferPosition == 59 && dcfValidSignal == true) 1352 { 1353 // bufferPosition 1354 == 59 so turn Buffer Full LED ON 1355 digitalWrite(LED_BUFFERFULL, HIGH); 1356 1357 1358 // Turn DCF OK LED ON 1359 digitalWrite(LED_DCFSTATUS, HIGH); 1360 1361 // 1362 Reset inner LED ring (incoming time information) 1363 MaximCC.clearDisplay(LedRingInner); 1364 1365 1366 // copy 'contents' of inner LED ring to the outer LED ring (current time information) 1367 1368 for (int i = 0; i < 59; i++) 1369 { 1370 MaximCC.setLed(LedRingOuter, 1371 i / 8, i % 8, DCFbitBuffer[i]); 1372 } 1373 1374 // process buffer and extract 1375 data sync the time with the RTC 1376 decodeBufferContents(); 1377 1378 // set 1379 Arduino time and after that set RTC time 1380 setTime(dcfHour, dcfMinute, 0, dcfDay, 1381 dcfMonth, dcfYear); 1382 RTC.set(now()); 1383 1384 // activate Synced LED 1385 1386 digitalWrite(LED_RTCSYNC, HIGH); 1387 1388 // Reset running buffer 1389 bufferPosition 1390 = 0; 1391 1392 // Reset DCFbitBuffer array, positions 0-58 (=59 bits) 1393 for 1394 (int i = 0; i < 59; i++) 1395 { 1396 DCFbitBuffer[i] = 0; 1397 } 1398 1399 1400 // reset flag 1401 MinuteMarkerFlag = false; 1402 1403 } // if (bufferPosition 1404 == 59) 1405 1406 //-------------------------------------------------------------------- 1407 1408 // The buffer is not yet filled although the 2 second 'gap' was detected. 1409 1410 // Can be result of a noisy signal, starting in middle of receiving data etc. 1411 1412 // Turn 'Minute Mark' LED ON 1413 //-------------------------------------------------------------------- 1414 1415 else 1416 { 1417 digitalWrite(LED_MINUTEMARKER, HIGH); 1418 1419 // Clear displays 1420 1421 MaximCC.clearDisplay(LedRingInner); 1422 MaximCC.clearDisplay(LedRingOuter); 1423 1424 1425 // Reset running buffer and start afresh. Now we are in sync with the incoming 1426 data 1427 bufferPosition = 0; 1428 1429 // Reset DCFbitBuffer array, positions 1430 0-58 (=59 bits) 1431 for (int i = 0; i < 59; i++) 1432 { 1433 DCFbitBuffer[i] 1434 = 0; 1435 } 1436 1437 // set flag so we can display incoming pulsed on the inner 1438 LED ring. 1439 MinuteMarkerFlag = true; 1440 } 1441} 1442 1443//================================================================================================================ 1444// 1445// 1446 Function name : decodeBufferContents 1447// called from : <finalizeBuffer> 1448// 1449// 1450 Purpose : Evaluates the information stored in the buffer. 1451// This 1452 is where the DCF77 signal is decoded to time and date information 1453// Parameters 1454 : none 1455// Return value : none 1456// 1457//================================================================================================================ 1458 1459void 1460 decodeBufferContents(void) 1461{ 1462 // Buffer is full and ready to be decoded 1463 1464 dcfMinute = bitDecode(21, 27); 1465 dcfHour = bitDecode(29, 34); 1466 dcfDay = 1467 bitDecode(36, 41); 1468 dcfWeekDay = bitDecode(42, 44); 1469 dcfMonth = bitDecode(45, 1470 49); 1471 dcfYear = bitDecode(50, 57); 1472 1473 //call function to calculate day 1474 of year and weeknumber 1475 dayWeekNumber(dcfYear, dcfMonth, dcfDay, dcfWeekDay); 1476 1477 1478 // Get value of Summertime DCFbit. '1' = Summertime, '0' = wintertime 1479 dcfDST 1480 = bitDecode(17, 17); 1481 1482 // determine Leap Year 1483 leapYear = calculateLeapYear(dcfYear); 1484} 1485 1486//================================================================================================================ 1487// 1488// 1489 bitDecode 1490// 1491// called from <processBuffer> 1492//================================================================================================================ 1493int 1494 bitDecode(int bitStart, int bitEnd) 1495{ 1496 // reset 'bitValue-array' counter 1497 1498 int i = 0; 1499 int value = 0; 1500 1501 // process bitrange bitStart > bitEnd 1502 1503 while (bitStart <= bitEnd) 1504 { 1505 // check if DCFbit in buffer is '1', 1506 discard when '0' 1507 if (DCFbitBuffer[bitStart] == 1) 1508 { 1509 // DCFbit 1510 in buffer == 1 so append its corresponding value to the variable 'value' 1511 value 1512 = value + bitValue[i]; 1513 } 1514 // increment 'bitValue-array' counter 1515 1516 i++; 1517 // increment bit-range counter 1518 bitStart++; 1519 } 1520 return 1521 value; 1522} 1523 1524//================================================================================================================ 1525// 1526// 1527 Function name : tasksEverySecond 1528// called from : <loop> 1529// 1530// Purpose 1531 : perform tasks that must happen once every SECOND 1532// Parameters : 1533 none 1534// Return value : none 1535// 1536//================================================================================================================ 1537void 1538 tasksEverySecond() 1539{ 1540 // check if time is changed 1541 if (second() != previousSecond) 1542 1543 { 1544 // 'reset' variable state 1545 previousSecond = second(); 1546 1547 //display 1548 the Real Time Clock Time 1549 displayRtcTime(); 1550 1551 // display 'HI' and 1552 'LO' temperature on specific moments 1553 switch (second()) 1554 { 1555 case 1556 0: 1557 // hourly chime output: ACTIVATE 1558 if (dayTime == 1 && minute() 1559 == 0 && digitalRead(CHIMESWITCHPIN) == HIGH) 1560 { 1561 digitalWrite(CHIMEPIN, 1562 HIGH); 1563 } 1564 1565 // reset temperature min/max memory at midnight 1566 1567 // 1568 // I did put this in the 'tasks every second' section so 1569 // 1570 that this code is executed at one specific second only... 1571 // Else we would 1572 need an extra variable to prevent this code to run 1573 // every cycle during 1574 the whole '00' hour. 1575 if (TEMPRESET_MIDNIGHT == 1 && (hour() == 00 && minute() 1576 == 00)) 1577 { 1578 minTemp = tempCelsius; 1579 maxTemp = tempCelsius; 1580 1581 } 1582 break; 1583 case 2: 1584 // hourly chime output: DEACTIVATE 1585 1586 digitalWrite(CHIMEPIN, LOW); 1587 break; 1588 case 30: 1589 // display 1590 'HI' on display for Hi temperature 1591 MaximCC.setChar(DisplayTempWeek, 5, 1592 ' ', false); // Clear part of the display 1593 MaximCC.setChar(DisplayTempWeek, 1594 4, ' ', false); // Clear part of the display 1595 MaximCC.setChar(DisplayTempWeek, 1596 7, 'H', false); // Display 'H' character. Binary pattern to lite up individual segments 1597 in this order is: .ABCDEFG 1598 MaximCC.setRow(DisplayTempWeek, 6, B00010000); 1599 // Display 'i' character. Binary pattern to lite up individual segments in this 1600 order is: .ABCDEFG 1601 break; 1602 case 31: 1603 case 32: 1604 // display 1605 Max temperature on DCF display LED display 1606 MaximCC.setChar(DisplayTempWeek, 1607 7, (maxTemp / 100), false); 1608 MaximCC.setChar(DisplayTempWeek, 6, (maxTemp 1609 % 100) / 10, true); 1610 MaximCC.setChar(DisplayTempWeek, 5, (maxTemp % 10), 1611 false); 1612 MaximCC.setRow(DisplayTempWeek, 4, B11001110); // Display Degrees 1613 dot and 'C' (Celcius) character. Binary pattern to lite up individual segments in 1614 this order is: .ABCDEFG 1615 // NOTE: I physically rotated digit 4 so when I 1616 activate the decimal dot, this now is the 'degrees dot' in the upper left corner 1617 1618 break; 1619 case 33: 1620 // display 'LO' on display for Low temperature 1621 1622 MaximCC.setChar(DisplayTempWeek, 5, ' ', false); // Clear part of the display 1623 1624 MaximCC.setChar(DisplayTempWeek, 4, ' ', false); // Clear part of the display 1625 1626 MaximCC.setChar(DisplayTempWeek, 7, 'L', false); // Display 'L' character. 1627 Binary pattern to lite up individual segments in this order is: .ABCDEFG 1628 MaximCC.setRow(DisplayTempWeek, 1629 6, B00011101); // Display 'o' character. Binary pattern to lite up individual 1630 segments in this order is: .ABCDEFG 1631 break; 1632 case 34: 1633 case 1634 35: 1635 // display Min temperature on DCF display LED display 1636 MaximCC.setChar(DisplayTempWeek, 1637 7, (minTemp / 100), false); 1638 MaximCC.setChar(DisplayTempWeek, 6, (minTemp 1639 % 100) / 10, true); 1640 MaximCC.setChar(DisplayTempWeek, 5, (minTemp % 10), 1641 false); 1642 MaximCC.setRow(DisplayTempWeek, 4, B11001110); // Display Degrees 1643 dot and 'C' (Celcius) character. Binary pattern to lite up individual segments in 1644 this order is: .ABCDEFG 1645 // NOTE: I physically rotated digit 4 so when I 1646 activate the decimal dot, this now is the 'degrees dot' in the upper left corner 1647 1648 break; 1649 case 36: 1650 // befor displaying the temperature in the 1651 next second, 1652 // request temperature from DS18B20 sensor, available after 1653 delay of minimal 750 ms (at highest resolution) 1654 calculateTemp(); 1655 1656 1657 // display 'CU' on display for Current temperature 1658 MaximCC.setChar(DisplayTempWeek, 1659 5, ' ', false); // Clear part of the display 1660 MaximCC.setChar(DisplayTempWeek, 1661 4, ' ', false); // Clear part of the display 1662 MaximCC.setRow(DisplayTempWeek, 1663 7, B01001110); // Display 'C' character. Binary pattern to lite up individual 1664 segments in this order is: .ABCDEFG 1665 MaximCC.setRow(DisplayTempWeek, 6, 1666 B00011100); // Display 'u' character. Binary pattern to lite up individual segments 1667 in this order is: .ABCDEFG 1668 break; 1669 case 37: 1670 // read temperature, 1671 store in min/max memory and display current temperature 1672 // only once per 1673 minute this is done else things go wrong... ;) 1674 displayTemp(); 1675 break; 1676 1677 } // switch 1678 } // (second() != previousSecond) 1679} // void tasksEverySecond() 1680 1681//================================================================================================================ 1682// 1683// 1684 Function name : tasksEveryMinute 1685// called from : <loop> 1686// 1687// Purpose 1688 : perform tasks that must happen once every MINUTE 1689// Parameters : 1690 none 1691// Return value : none 1692// 1693//================================================================================================================ 1694void 1695 tasksEveryMinute() 1696{ 1697 // display date, week LED's, week nr etc. if time is 1698 changed 1699 if (minute() != previousMinute) 1700 { 1701 // 'reset' state of variable 1702 1703 previousMinute = minute(); 1704 1705 // display date, week LED's, week nr etc. 1706 1707 if (dcfValidSignal == true) 1708 { 1709 displayData(); 1710 } 1711 1712 1713 // increase PIR delay counter 1714 pirTimer++; 1715 } 1716} 1717 1718//================================================================================================================ 1719// 1720// 1721 Function name : tasksEveryHour 1722// called from : <loop> 1723// 1724// Purpose 1725 : perform tasks that must happen once every HOUR 1726// Parameters : none 1727// 1728 Return value : none 1729// 1730//================================================================================================================ 1731void 1732 tasksEveryHour() 1733{ 1734 if (hour() != previousHour) 1735 { 1736 // 'reset' 1737 variable state 1738 previousHour = hour(); 1739 1740 //-------------------------------------------------------------------- 1741 1742 // reset error counter and display every hour 1743 //-------------------------------------------------------------------- 1744 1745 errorCounter = 0; 1746 // update error counter display 1747 ledDisplay(DisplayBufferBitError, 1748 "R", errorCounter); 1749 1750 //--------------------------------------------------------------------- 1751 1752 // Power saving function, shutting the displays off at night 1753 //--------------------------------------------------------------------- 1754 1755 1756 // First, check if the night shut-down function is activated by the user 1757 1758 // simply by adding up both value. If '0', the user has disabled the function 1759 1760 if (POWERSAVINGOFFTIME != 0 && POWERSAVINGONTIME != 0) 1761 { 1762 // 1763 check whether it is Day- or Nighttime 1764 if (hour() >= POWERSAVINGOFFTIME 1765 && hour() <= POWERSAVINGONTIME) 1766 { 1767 // ---------------------------------------- 1768 1769 // it's DAYTIME so activate the displays 1770 // ---------------------------------------- 1771 1772 1773 // this is used to chime only if it is daytime... 1774 dayTime = 1; 1775 1776 1777 // test variable because daytime routine is needed only once 1778 if 1779 (daytimeChange == 1) 1780 { 1781 // 'reset' variable state 1782 daytimeChange 1783 = 0; 1784 1785 // activate SELECTED displays and status LED's 1786 turnDisplaysOn(); 1787 1788 } 1789 } 1790 else 1791 { 1792 // no chime at night... 1793 1794 dayTime = 0; 1795 1796 // ---------------------------------------- 1797 1798 // it's NIGHTTIME so time to deactivate displays 1799 // ---------------------------------------- 1800 1801 // test variable because nighttime routine is needed only once 1802 if 1803 (daytimeChange == 0) 1804 { 1805 // 'reset' variable state 1806 daytimeChange 1807 = 1; 1808 1809 // deactivate all the displays and status LED's 1810 // 1811 ONLY if the powersafe switch is HIGH 1812 if (digitalRead(POWERSAVESWITCHPIN) 1813 == HIGH) 1814 { 1815 turnDisplaysOff(); 1816 } 1817 1818 1819 } // if (daytimeChange == 0) 1820 } // else 1821 } // if(POWERSAVINGOFFTIME 1822 + POWERSAVINGONTIME != 0) 1823 } // if (dcfHour != previousHour) 1824} // void 1825 tasksEveryHour() 1826 1827//================================================================================================================ 1828// 1829// 1830 Function name : buzzer 1831// called from : <scanSignal> 1832// 1833// Purpose : 1834 generate 'beep' sound 1835// Parameters : duration in ms 1836// Return value : 1837 none 1838// 1839//================================================================================================================ 1840 1841void 1842 buzzer(int duration) 1843{ 1844 tone(BUZZER, 1500, duration); 1845} 1846 1847//================================================================================================================ 1848// 1849// 1850 Function name : initialize 1851// called from : <Setup> 1852// 1853// Purpose : 1854 initialize variables and displays after power-up 1855// Parameters : none 1856// 1857 Return value : none 1858// 1859//================================================================================================================ 1860 1861void 1862 initialize(void) 1863{ 1864 //--------------------------------------------------- 1865 1866 // Initialize Variables 1867 //--------------------------------------------------- 1868 1869 leadingEdge = 0; 1870 trailingEdge = 0; 1871 previousLeadingEdge = 0; 1872 bufferPosition 1873 = 0; 1874 1875 digitalWrite(CHIMEPIN, HIGH); // Set Chimepin to default state 1876 1877 1878 // Reset DCFbitBuffer array, positions 0-58 (=59 bits) 1879 for (int i = 0; i 1880 < 59; i++) 1881 { 1882 DCFbitBuffer[i] = 0; 1883 } 1884 1885 //--------------------------------------------------- 1886 1887 // Initialize Maxim 72xx 7 segment displays 1888 //--------------------------------------------------- 1889 1890 // Maxim Common Cathode displays 1891 for (int i = 0; i < 8; i++) 1892 { 1893 // 1894 display wake up 1895 MaximCC.shutdown(i, false); 1896 // clear display 1897 MaximCC.clearDisplay(i); 1898 1899 } 1900 1901 //--------------------------------------------------- 1902 // Set brightness 1903 of Maxim 72xx 7 segment displays 1904 //--------------------------------------------------- 1905 1906 // Maxim Common Cathode displays 1907 MaximCC.setIntensity(LedRingOuter, BrightnessLedRingOuter); 1908 1909 MaximCC.setIntensity(LedRingInner, BrightnessLedRingInner); 1910 MaximCC.setIntensity(DisplayTime, 1911 BrightnessDisplayTime); 1912 MaximCC.setIntensity(DisplayDate, BrightnessDisplayDate); 1913 1914 MaximCC.setIntensity(DisplayTempWeek, BrightnessDisplayTempWeek); 1915 MaximCC.setIntensity(DisplayPeriodPulse, 1916 BrightnessDisplayPeriodPulse); 1917 MaximCC.setIntensity(DisplayBufferBitError, 1918 BrightnessDisplayBufferBitError); 1919 1920 //--------------------------------------------------- 1921 1922 // Set all Arduino LED outputs to LOW (Led's OFF) 1923 //--------------------------------------------------- 1924 1925 for (int i1 = 22; i1 <= 49; i1++) 1926 { 1927 digitalWrite(i1, LOW); 1928 } 1929} 1930 1931//================================================================================================================ 1932// 1933// 1934 Function name : int0handler 1935// called from : 1936// 1937// Purpose : when 1938 a rising or falling edge is detected on pin 2, this function is called 1939// Parameters 1940 : none 1941// Return value : none 1942// 1943//================================================================================================================ 1944 1945void 1946 int0handler() 1947{ 1948 DCFSignalState = digitalRead(DCF77PIN); 1949} 1950 1951//================================================================================================================ 1952// 1953// 1954 Function name : turnDisplaysOn 1955// called from : <tasksEveryHour> and <checkPIR> 1956// 1957// 1958 Purpose : turn ON selected 7 segment displays and LED's 1959// Parameters : 1960 none 1961// Return value : none 1962// 1963//================================================================================================================ 1964void 1965 turnDisplaysOn() 1966{ 1967 // activate SELECTED displays and status LED's 1968 MaximCC.shutdown(LedRingInner, 1969 false); 1970 MaximCC.shutdown(LedRingOuter, false); 1971 MaximCC.shutdown(DisplayTime, 1972 false); 1973 MaximCC.shutdown(DisplayDate, false); 1974 MaximCC.shutdown(DisplayTempWeek, 1975 false); 1976 MaximCC.shutdown(DisplayPeriodPulse, false); 1977 MaximCC.shutdown(DisplayBufferBitError, 1978 false); 1979} 1980 1981//================================================================================================================ 1982// 1983// 1984 Function name : turnDisplaysOff 1985// called from : <tasksEveryHour> and <checkPIR> 1986// 1987// 1988 Purpose : turn OFF selected 7 segment displays and LED's 1989// Parameters 1990 : none 1991// Return value : none 1992// 1993//================================================================================================================ 1994void 1995 turnDisplaysOff() 1996{ 1997 // below you can select which display's need to be shut 1998 down for the night 1999 // In this case, the time display remains ON 2000 2001 //MaximCC.shutdown(DisplayTime, 2002 true); 2003 MaximCC.shutdown(LedRingInner, true); 2004 MaximCC.shutdown(LedRingOuter, 2005 true); 2006 MaximCC.shutdown(DisplayDate, true); 2007 MaximCC.shutdown(DisplayTempWeek, 2008 true); 2009 MaximCC.shutdown(DisplayPeriodPulse, true); 2010 MaximCC.shutdown(DisplayBufferBitError, 2011 true); 2012} 2013 2014//================================================================================================================ 2015// 2016// 2017 Function name : ledDisplay 2018// called from : <processBuffer> 2019// 2020// Purpose 2021 : display a value on a selected 7 segment display 2022// Parameters : display 2023 'number 'addr', 'value', "L"eft or "R"ight side.none 2024// Return value : none 2025// 2026//================================================================================================================ 2027 2028// 2029 example: ledDisplay( DisplayBufferBitError, "R", errorCounter ) 2030// so display 2031 the 'error counter' value on the RIGHT side of the 8 digit 2032// 7 segment display 2033 number 3 2034 2035void ledDisplay(int addr, String leftOrRight, int value) 2036{ 2037 2038 int ones; 2039 int tens; 2040 int hundreds; 2041 int thousands; 2042 int shift; 2043 2044 2045 //break down value in seperate digits for ones, tens, etc 2046 int v = value; 2047 // 'value' is needed later so copy it in 'v' 2048 ones = v % 10; 2049 v = v / 10; 2050 2051 tens = v % 10; 2052 v = v / 10; 2053 hundreds = v % 10; 2054 thousands = v / 10; 2055 2056 2057 //Select which side of the 8 digit display to be used 2058 (leftOrRight == "L" 2059 ? shift = 4 : shift = 0); 2060 2061 //Now print the number digit by digit 2062 //preceding 2063 zero's are removed with tenary operator by 'printing' a space character. 2064 MaximCC.setChar(addr, 2065 3 + shift, ((value < 1000) ? ' ' : thousands), false); 2066 MaximCC.setChar(addr, 2067 2 + shift, ((value < 100) ? ' ' : hundreds), false); 2068 MaximCC.setChar(addr, 2069 1 + shift, ((value < 10) ? ' ' : tens), false); 2070 MaximCC.setChar(addr, 0 + shift, 2071 ones, false); 2072} 2073 2074//================================================================================================================ 2075// 2076// 2077 Function name : checkSwitches 2078// called from : <loop> 2079// 2080// Purpose : 2081 check if the temperature reset button button is pressed 2082// Parameters : none 2083// 2084 Return value : none 2085// 2086//================================================================================================================ 2087void 2088 checkSwitches(void) 2089{ 2090 //------------------------------------------------------------- 2091 2092 // read state of push button tempResetButton 2093 tempResetButton = digitalRead(TEMPRESETPIN); 2094 2095 // reset temperature min/max values when push-button is pressed 2096 if (tempResetButton 2097 == 1) 2098 { 2099 maxTemp = tempCelsius; 2100 minTemp = tempCelsius; 2101 } 2102 2103 2104 //------------------------------------------------------------- 2105 //read state 2106 of switch BUZZERSWITCHPIN 2107 dcf77SoundSwitch = digitalRead(BUZZERSWITCHPIN); 2108 2109 2110 //------------------------------------------------------------- 2111 //check led 2112 and display's powersafe switch 2113 if (digitalRead(POWERSAVESWITCHPIN) == HIGH) 2114 2115 { 2116 digitalWrite(LED_POWERSAVE, HIGH); 2117 } 2118 else 2119 { 2120 digitalWrite(LED_POWERSAVE, 2121 LOW); 2122 turnDisplaysOn(); 2123 } 2124} 2125 2126//================================================================================================================ 2127// 2128// 2129 Function name : checkPIR 2130// called from : <loop> 2131// 2132// Purpose : 2133 check for PIR detector activity to shut off or activate the displays to save power 2134// 2135 Parameters : none 2136// Return value : none 2137// 2138//================================================================================================================ 2139void 2140 checkPIR() 2141{ 2142 //------------------------------------------------------------- 2143 2144 // Read PIR input, check for movement 2145 // Only check for PIR activity if user 2146 option is '1' 2147 // AND only every second to prevent waisted processor time. 2148 2149 if (POWERSAVE_BY_PIR == 1 && (millis() - previousTimePIR > 1000)) 2150 { 2151 // 2152 reset the 'once-per-second timer 2153 previousTimePIR = millis(); 2154 2155 // 2156 read the PIR detector PIN 2157 pirActivity = digitalRead(PIRDETECTORPIN); 2158 2159 2160 // turn displays ON or OFF depending on state of pin 'pirActivity' 2161 if 2162 (pirActivity == 1) 2163 { 2164 // PIR activity detected... 2165 // activated 2166 the PIR LED 2167 digitalWrite(LED_PIRMOTION, HIGH); 2168 // Reset the PIR 2169 timer variable (used in <tasksEveryMinue> function) every time 2170 // the PIR 2171 is activated so effectually resetting the shut-off timer. 2172 pirTimer = 0; 2173 2174 2175 // only continue if the display is now OFF 2176 if (pirDisplaysState == 2177 0) 2178 { 2179 // Toggle variable to prevent executing the following 2180 2181 // code every time the PIR detects activity 2182 pirDisplaysState 2183 = 1; 2184 2185 //displays ON 2186 turnDisplaysOn(); 2187 } 2188 } 2189 2190 else 2191 { 2192 // No PIR activity detected... 2193 // turn the PIR 2194 LED off 2195 digitalWrite(LED_PIRMOTION, LOW); 2196 // check the pirTimer 2197 counter in the <tasksEveryMinue> function). 2198 // and if the delaytime has 2199 passed, only continue if the displays are now ON 2200 // else this code would 2201 be executed many times per second 2202 if (pirTimer > PIR_DELAY_TIME && pirDisplaysState 2203 == 1) 2204 { 2205 // Toggle variable to prevent executing the following 2206 2207 // code every time the PIR detects activity 2208 pirDisplaysState 2209 = 0; 2210 2211 //shut off displays ONLY if powersafe switch is HIGH 2212 if 2213 (digitalRead(POWERSAVESWITCHPIN) == HIGH) 2214 { 2215 turnDisplaysOff(); 2216 2217 } 2218 } 2219 } // else 2220 } // if (POWERSAVE_BY_PIR == 1 && 2221 (millis() - previousTimePIR > 1000)) 2222} // void checkPIR() 2223 2224//================================================================================================================ 2225// 2226// 2227 Function name : error 2228// called from : <scanSignal> 2229// 2230// Purpose : 2231 turn error LED ON, clear LED ring's and increase error counter display 2232// Parameters 2233 : error LED to turn on 2234// Return value : none 2235// 2236//================================================================================================================ 2237 2238void 2239 error(int errorLed) 2240{ 2241 // no valid data 2242 dcfValidSignal = false; 2243 2244 2245 // turn 'dcfValidSignal = false on' 2246 digitalWrite(errorLed, HIGH); 2247 2248 2249 // clear Led's/displays because of error condition 2250 digitalWrite(LED_DCFSTATUS, 2251 LOW); 2252 digitalWrite(LED_RTCSYNC, LOW); 2253 MaximCC.clearDisplay(LedRingOuter); 2254 // clear display 2255 2256 // increase errorCounter and display errorCount 2257 errorCounter++; 2258 2259 ledDisplay(DisplayBufferBitError, "R", errorCounter); 2260 return; 2261} 2262 2263//================================================================================================================ 2264// 2265// 2266 Function name : dayWeekNumber 2267// called from : <decodeBufferContents> 2268// 2269// 2270 Purpose : calculate the WEEK number according to ISO standard, see comments 2271 in the ARCHIVE below 2272// Parameters : dcfYear, dcfMonth, dcfDay, dcfWeekDay 2273// 2274 Return value : weekNumber 2275// 2276//================================================================================================================ 2277//Code 2278 from: http://forum.arduino.cc/index.php/topic,44476.0.html 2279 2280int dayWeekNumber(int 2281 y, int m, int d, int w) 2282{ 2283 // Number of days at the beginning of the month 2284 in a normal (not leap) year. 2285 int days[] = {0, 31, 59, 90, 120, 151, 181, 212, 2286 243, 273, 304, 334}; 2287 2288 // Start to calculate the number of days of the first 2289 two months 2290 if (m == 1 || m == 2) 2291 { 2292 // for any type of year we calculate 2293 the number of days for January or february 2294 dayNumber = days[(m - 1)] + d; 2295 2296 } 2297 2298 // now calculate for the other months 2299 // first, check for a leap 2300 year 2301 else if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) 2302 { 2303 // 2304 we have a leap year, so calculate in the same way but adding one day 2305 dayNumber 2306 = days[(m - 1)] + d + 1; 2307 } 2308 2309 else 2310 { 2311 //no leap year, calculate 2312 in the normal way, such as January or February 2313 dayNumber = days[(m - 1)] 2314 + d; 2315 } 2316 2317 // Now start to calculate Week number 2318 if (w == 0) 2319 2320 { 2321 //if it is sunday (time library returns 0) 2322 weekNumber = (dayNumber 2323 - 7 + 10) / 7; 2324 } 2325 2326 else 2327 { 2328 // for the other days of week 2329 2330 weekNumber = (dayNumber - w + 10) / 7; 2331 } 2332 2333 // finished! return with 2334 the week number as an INT value 2335 return weekNumber; 2336} 2337 2338//================================================================================================================ 2339// 2340// 2341 Function name : calculateLeapYear 2342// called from : <decodeBufferContents> 2343// 2344// 2345 Purpose : determine if a given year is a leap year 2346// Parameters : year 2347 - the year to test 2348// Return value : '1' if the year is a leap year, '0' otherwise 2349// 2350//================================================================================================================ 2351 2352int 2353 calculateLeapYear(int year) 2354{ 2355 if ((year % 4 == 0 && year % 100 != 0) || 2356 (year % 400 == 0)) 2357 { 2358 return 1; 2359 } 2360 else 2361 { 2362 return 2363 0; 2364 } 2365} 2366 2367//================================================================================================================ 2368// 2369// 2370 Function name : displayData 2371// called from : <tasksEveryMinute> 2372// 2373// 2374 Purpose : display LED's: day of week; status; Winter/Summer time; 2375// Parameters 2376 : none 2377// Return value : none 2378// 2379//================================================================================================================ 2380 2381void 2382 displayData(void) 2383{ 2384 // display Day of Week on LED's 2385 // first, clear 2386 all the 'Day' Led's before displaying new value 2387 digitalWrite(LED_SUNDAY, LOW); 2388 2389 digitalWrite(LED_MONDAY, LOW); 2390 digitalWrite(LED_TUESDAY, LOW); 2391 digitalWrite(LED_WEDNESDAY, 2392 LOW); 2393 digitalWrite(LED_THURSDAY, LOW); 2394 digitalWrite(LED_FRIDAY, LOW); 2395 2396 digitalWrite(LED_SATURDAY, LOW); 2397 2398 // switch on the Weekday LED 2399 switch 2400 (dcfWeekDay) 2401 { 2402 case 1: 2403 digitalWrite(LED_MONDAY, HIGH); 2404 break; 2405 2406 case 2: 2407 digitalWrite(LED_TUESDAY, HIGH); 2408 break; 2409 case 3: 2410 2411 digitalWrite(LED_WEDNESDAY, HIGH); 2412 break; 2413 case 4: 2414 digitalWrite(LED_THURSDAY, 2415 HIGH); 2416 break; 2417 case 5: 2418 digitalWrite(LED_FRIDAY, HIGH); 2419 break; 2420 2421 case 6: 2422 digitalWrite(LED_SATURDAY, HIGH); 2423 break; 2424 case 7: 2425 2426 digitalWrite(LED_SUNDAY, HIGH); 2427 break; 2428 } 2429 2430 // display Weeknumber 2431 2432 MaximCC.setChar(DisplayTempWeek, 1, ((weekNumber < 10) ? ' ' : (weekNumber / 10)), 2433 false); 2434 MaximCC.setChar(DisplayTempWeek, 0, weekNumber % 10, false); 2435 2436 2437 // display Date - with dashes between D-M-Y 2438 // for example: 03-08-2015, so 2439 with leading zero's 2440 MaximCC.setChar(DisplayDate, 7, dcfDay / 10, false); 2441 2442 MaximCC.setChar(DisplayDate, 6, dcfDay % 10, false); 2443 MaximCC.setChar(DisplayDate, 2444 5, '-', false); 2445 MaximCC.setChar(DisplayDate, 4, dcfMonth / 10, false); 2446 2447 MaximCC.setChar(DisplayDate, 3, dcfMonth % 10, false); 2448 MaximCC.setChar(DisplayDate, 2449 2, '-', false); 2450 MaximCC.setChar(DisplayDate, 1, dcfYear / 10, false); 2451 MaximCC.setChar(DisplayDate, 2452 0, dcfYear % 10, false); 2453 2454 /* OTHER OPTIONS TO DISPLAY THE DATE: 2455 2456 2457 // display Date - with dots between D.M.Y 2458 // for example: 03.08.15 2459 MaximCC.setChar(DisplayDate, 2460 7, dcfDay / 10, false); 2461 MaximCC.setChar(DisplayDate, 6, dcfDay % 10, true); 2462 2463 MaximCC.setChar(DisplayDate, 5, dcfMonth / 10, false); 2464 MaximCC.setChar(DisplayDate, 2465 4, dcfMonth % 10, true); 2466 MaximCC.setChar(DisplayDate, 3, 2, false); 2467 MaximCC.setChar(DisplayDate, 2468 2, 0, false); 2469 MaximCC.setChar(DisplayDate, 1, dcfYear / 10, false); 2470 MaximCC.setChar(DisplayDate, 2471 0, dcfYear % 10, false); 2472 */ 2473 2474 /* 2475 // display Date - moved day to 2476 right if month is <10 2477 // for example: __3.8.15 or _22.7.15 or 24.12.15 (where 2478 _ is a blank display) 2479 MaximCC.setChar(DisplayDate, 7, ((dcfDay < 10) ? ' ' 2480 : ((dcfMonth < 10) ? ' ' : (dcfDay / 10))), false); 2481 MaximCC.setChar(DisplayDate, 2482 6, ((dcfMonth < 10) ? ((dcfDay < 10) ? ' ' : (dcfDay / 10)) : (dcfDay % 10)), ((dcfMonth 2483 < 10) ? false : true)); 2484 MaximCC.setChar(DisplayDate, 5, ((dcfMonth < 10) ? 2485 (dcfDay % 10) : (dcfMonth / 10)), ((dcfMonth < 10) ? true : false)); 2486 MaximCC.setChar(DisplayDate, 2487 4, dcfMonth % 10, true); 2488 MaximCC.setChar(DisplayDate, 3, 2, false); 2489 MaximCC.setChar(DisplayDate, 2490 2, 0, false); 2491 MaximCC.setChar(DisplayDate, 1, dcfYear / 10, false); 2492 MaximCC.setChar(DisplayDate, 2493 0, dcfYear % 10, false); 2494 */ 2495 2496 // display Summer- or Wintertime LED 2497 2498 if (dcfDST == 1) 2499 { 2500 digitalWrite(LED_CEST, HIGH); 2501 digitalWrite(LED_CET, 2502 LOW); 2503 } 2504 else 2505 { 2506 digitalWrite(LED_CET, HIGH); 2507 digitalWrite(LED_CEST, 2508 LOW); 2509 } 2510 2511 // display Leap Year LED 2512 if (leapYear == 1) 2513 { 2514 2515 digitalWrite(LED_LEAPYEAR, HIGH); 2516 } 2517 else 2518 { 2519 digitalWrite(LED_LEAPYEAR, 2520 LOW); 2521 } 2522} 2523 2524//================================================================================================================ 2525// 2526// 2527 Function name : displayRtcTime 2528// called from : <tasksEverySecond> 2529// 2530// 2531 Purpose : display the Real Time Clock time on the RTC display 2532// Parameters 2533 : none 2534// Return value : none 2535// 2536//================================================================================================================ 2537 2538void 2539 displayRtcTime() 2540{ 2541 MaximCC.setChar(DisplayTime, 7, ((hour() < 10) ? ' ' 2542 : (hour() / 10)), false); 2543 MaximCC.setChar(DisplayTime, 6, (hour() % 10), false); 2544 2545 // activate only the segments 'a' and 'g' of the 7 segment display, representing 2546 the dots between the digits 2547 // Binary pattern to lite up individual segments 2548 in this order is: .ABCDEFG 2549 MaximCC.setRow(DisplayTime, 5, B00001001); 2550 MaximCC.setChar(DisplayTime, 2551 4, (minute() / 10), false); 2552 MaximCC.setChar(DisplayTime, 3, (minute() % 10), 2553 false); 2554 // activate only the segments 'a' and 'g' of the 7 segment display, 2555 representing the dots between the digits 2556 // Binary pattern to lite up individual 2557 segments in this order is: .ABCDEFG 2558 MaximCC.setRow(DisplayTime, 2, B01001001); 2559 2560 MaximCC.setChar(DisplayTime, 1, (second() / 10), false); 2561 MaximCC.setChar(DisplayTime, 2562 0, (second() % 10), false); 2563} 2564 2565//================================================================================================================ 2566// 2567// 2568 Function name : calculateTemp 2569// called from : <loop> 2570// 2571// Purpose : 2572 get temperature from DS18B20 sensor and display it 2573// Parameters : none 2574// 2575 Return value : none 2576// 2577//================================================================================================================ 2578 2579void 2580 calculateTemp() 2581{ 2582 // The initialization sequence consists of a reset pulse 2583 transmitted by the bus master 2584 // followed by presence pulse(s) transmitted 2585 by the slave(s). 2586 ds.reset(); 2587 // use this command when only 1 sensor is 2588 used to avoid specifying the address! 2589 ds.skip(); 2590 // start conversion 2591 2592 ds.write(0x44); 2593 // (!) AFTER THIS we need to wait for a time dependent on 2594 resolution 2595 // CONVERSION TIME: 2596 // 9 bit resolution, 93.75 ms 2597 // 2598 10 bit resolution, 187.5 ms 2599 // 11 bit resolution, 375 ms 2600 // 12 bit resolution, 2601 750 ms 2602 // 2603 // This is done in this sketch by requesting the conversion 2604 first and at minimal 1 second later reading it. 2605} 2606 2607//================================================================================================================ 2608// 2609// 2610 Function name : displayTemp 2611// called from : <loop> 2612// 2613// Purpose : 2614 get temperature from DS18B20 sensor and display it 2615// Earlier 2616 we requested the temperature from the DS18B20 sensor, 2617// now 2618 conversion is finished so we get the data 2619// Parameters : none 2620// Return 2621 value : none 2622// 2623//================================================================================================================ 2624 2625void 2626 displayTemp() 2627{ 2628 // The initialization sequence consists of a reset pulse 2629 transmitted by the bus master 2630 // followed by presence pulse(s) transmitted 2631 by the slave(s). 2632 ds.reset(); 2633 // use this command when only 1 sensor is 2634 used to avoid specifying the address! 2635 ds.skip(); 2636 // Read Scratchpad 2637 2638 ds.write(0xBE); // Read Scratchpad 2639 // we need 9 bytes 2640 for (byte i = 2641 0; i < 9; i++) 2642 { 2643 // Read a Byte a a time 2644 DS18B20Data[i] = ds.read(); 2645 2646 } 2647 2648 // We have the data, next convert to actual temperature. 2649 lowByte 2650 = DS18B20Data[0]; 2651 highByte = DS18B20Data[1]; 2652 // because the result is 2653 a 16 bit signed integer, it should 2654 // be stored to an "int16_t" type, which 2655 is always 16 bits 2656 // even when compiled on a 32 bit processor. 2657 int16_t 2658 tempReading = (highByte << 8) + lowByte; 2659 // tempRaw is 4 digits; 2 integers, 2660 2 fraction. For example: 2115 (= 21.15 degrees Celsius) 2661 int tempRaw = (6 * 2662 tempReading) + tempReading / 4; 2663 // We need 3 digits: 2 integers, 2 fraction. 2664 For example: 216 (= 21.6 degres Celsius) 2665 tempRaw % 10 >= 5 ? tempCelsius = 2666 (tempRaw / 10) + 1 : tempCelsius = tempRaw / 10; 2667 2668 // store min and max temperatures 2669 2670 if (minTemp == 0) 2671 minTemp = tempCelsius; 2672 if (tempCelsius > maxTemp) 2673 2674 maxTemp = tempCelsius; 2675 if (tempCelsius < minTemp) 2676 minTemp = tempCelsius; 2677 2678 2679 // Display temperature on the multi purpose display 'TempWeek' 2680 2681 // Also 2682 light the 'Temp' LED on the front panel 2683 digitalWrite(LED_TEMP, HIGH); 2684 // 2685 Display Digit 3: example: temperature value 216 / 100 = 2 2686 MaximCC.setChar(DisplayTempWeek, 2687 7, (tempCelsius / 100), false); 2688 // Display Digit 2: example: temperature value 2689 216 % 100 / 10 = 1 2690 // also activate decimal dot on display 2 2691 MaximCC.setChar(DisplayTempWeek, 2692 6, (tempCelsius % 100) / 10, true); 2693 // Display Digit 3: example: temperature 2694 value 216 % 10 = 6 2695 MaximCC.setChar(DisplayTempWeek, 5, (tempCelsius % 10), 2696 false); 2697 // Display Degrees dot and 'C' (Celcius) character. Binary pattern 2698 to lite up individual segments in this order is: .ABCDEFG 2699 // NOTE: I physically 2700 rotated digit 4 7 segement display so when I activate the decimal dot, 2701 // this 2702 now is the 'degrees dot' in the upper left corner of the display... 2703 MaximCC.setRow(DisplayTempWeek, 2704 4, B11001110); 2705} 2706 2707//================================================================================================================ 2708// 2709// 2710 Function name : ledTest 2711// called from : <setup> 2712// 2713// Purpose : 2714 after a cold start, do a led test. 2715// Parameters : none 2716// Return value 2717 : none 2718// 2719//================================================================================================================ 2720void 2721 ledTest() 2722{ 2723 // The displays are lit up sequentially because of the current 2724 draw. 2725 // When all is lit up at the same time, you would need a bigger power 2726 supply. 2727 2728 //--------------------------------------------------------------------- 2729 2730 // Outer LED ring 2731 for (int i = 0; i < 59; i++) 2732 { 2733 // LED's ON 2734 2735 MaximCC.setLed(LedRingOuter, i / 8, i % 8, true); 2736 delay(LEDTEST_DELAY_LED_RING); 2737 2738 } 2739 2740 for (int i = 58; i >= 0; i--) 2741 { 2742 // LED's OFF 2743 MaximCC.setLed(LedRingOuter, 2744 i / 8, i % 8, false); 2745 delay(LEDTEST_DELAY_LED_RING); 2746 } 2747 //--------------------------------------------------------------------- 2748 2749 // Inner LED ring 2750 for (int i = 0; i < 59; i++) 2751 { 2752 // LED's ON 2753 2754 MaximCC.setLed(LedRingInner, i / 8, i % 8, true); 2755 delay(LEDTEST_DELAY_LED_RING); 2756 2757 } 2758 2759 for (int i = 58; i >= 0; i--) 2760 { 2761 // LED's OFF 2762 MaximCC.setLed(LedRingInner, 2763 i / 8, i % 8, false); 2764 delay(LEDTEST_DELAY_LED_RING); 2765 } 2766 //--------------------------------------------------------------------- 2767 2768 // LED's 2769 for (int i = 22; i <= 53; i++) 2770 { 2771 // LED's ON 2772 digitalWrite(i, 2773 HIGH); 2774 delay(LEDTEST_DELAY_LED_RING); 2775 } 2776 // wait before turning 2777 the LED's off 2778 delay(LEDTEST_DELAY_DISPLAYS); 2779 for (int i = 53; i >= 22; 2780 i--) 2781 { 2782 // LED's OFF 2783 digitalWrite(i, LOW); 2784 } 2785 //--------------------------------------------------------------------- 2786 2787 // Real Time Clock display 2788 for (int i = 0; i < 8; i++) 2789 { 2790 // LED's 2791 ON 2792 MaximCC.setChar(DisplayTime, i, 8, true); 2793 } 2794 // wait before turning 2795 the LED's off 2796 delay(LEDTEST_DELAY_DISPLAYS); 2797 // clear display 2798 MaximCC.clearDisplay(DisplayTime); 2799 2800 //--------------------------------------------------------------------- 2801 // 2802 Date display 2803 for (int i = 0; i < 8; i++) 2804 { 2805 // LED's ON 2806 MaximCC.setChar(DisplayDate, 2807 i, 8, true); 2808 } 2809 // wait before turning the LED's off 2810 delay(LEDTEST_DELAY_DISPLAYS); 2811 2812 // clear display 2813 MaximCC.clearDisplay(DisplayDate); 2814 //--------------------------------------------------------------------- 2815 2816 // Temp and Week display 2817 for (int i = 0; i < 8; i++) 2818 { 2819 // LED's 2820 ON 2821 MaximCC.setChar(DisplayTempWeek, i, 8, true); 2822 } 2823 // wait before 2824 turning the LED's off 2825 delay(LEDTEST_DELAY_DISPLAYS); 2826 // clear display 2827 2828 MaximCC.clearDisplay(DisplayTempWeek); 2829 //--------------------------------------------------------------------- 2830 2831 // Period-Pulse display 2832 for (int i = 0; i < 8; i++) 2833 { 2834 // LED's 2835 ON 2836 MaximCC.setChar(DisplayPeriodPulse, i, 8, true); 2837 } 2838 // wait before 2839 turning the LED's off 2840 delay(LEDTEST_DELAY_DISPLAYS); 2841 // clear display 2842 2843 MaximCC.clearDisplay(DisplayPeriodPulse); 2844 //--------------------------------------------------------------------- 2845 2846 // Buffer-DCFbit-Errors display 2847 for (int i = 0; i < 8; i++) 2848 { 2849 // 2850 LED's ON 2851 MaximCC.setChar(DisplayBufferBitError, i, 8, true); 2852 } 2853 // 2854 wait before turning the LED's off 2855 delay(LEDTEST_DELAY_DISPLAYS); 2856 // clear 2857 display 2858 MaximCC.clearDisplay(DisplayBufferBitError); 2859} 2860 2861//================================================================================================================ 2862// 2863// 2864 Function name : configureDS18B20 2865// called from : <setup> 2866// 2867// Purpose 2868 : ON TIME function to configure the DS18B20 temperature sensor, 2869// setting 2870 the desired temperature resolution 2871// Parameters : none 2872// Return value 2873 : none 2874// 2875//================================================================================================================ 2876 2877void 2878 configureDS18B20() 2879{ 2880 // This is done ONCE in setup() 2881 2882 // INITIALIZATION 2883 2884 // All transactions on the 1-Wire bus begin with an initialization sequence. 2885 2886 // The initialization sequence consists of a reset pulse transmitted by the bus 2887 master followed by presence pulse(s) transmitted by the slave(s). 2888 // The presence 2889 pulse lets the bus master know that slave devices (such as the DS18B20) are on the 2890 bus and are ready to operate. 2891 2892 // The initialization sequence consists of 2893 a reset pulse transmitted by the bus master followed by presence pulse(s) transmitted 2894 by the slave(s). 2895 ds.reset(); 2896 2897 // use this command when only 1 sensor 2898 is used to avoid specifying the address! 2899 ds.skip(); 2900 2901 // Select a specific 2902 DS18x20 device 2903 //byte addr[8] = {0x28, 0xF0, 0xEC, 0xE2, 0x04, 0x00, 0x00, 2904 0x47}; 2905 //ds.select(addr); 2906 2907 // WRITE SCRATCHPAD 2908 // This command 2909 allows the master to write 3 bytes of data to the DS18B20s scratchpad. 2910 // The 2911 first data byte is written into the TH register (byte 2 of the scratchpad), the 2912 second byte is written into the TL register (byte 3), 2913 // and the third byte 2914 is written into the configuration register (byte 4). 2915 // Data must be transmitted 2916 least significant bit first. All three bytes MUST be written before the master issues 2917 a reset, 2918 // or the data may be corrupted. 2919 ds.write(0x4E); 2920 2921 // 2922 write zero into the alarm register HIGH 2923 ds.write(0); 2924 // write zero into 2925 the alarm register LOW 2926 ds.write(0); 2927 /* Next, write R1 and R2 into the 2928 configuration register to select the precision of the temperature 2929 value | 2930 resolution | conversion time | increments 2931 0 = 9 bits | 93,75 ms 2932 | 0,5 Celsius 2933 1 = 10 bits | 187,5 ms | 0,25 2934 2 2935 = 11 bits | 375 ms | 0,125 2936 3 = 12 bits | 750 ms | 2937 0,0625 2938 CONFIGURATION REGISTER: 2939 ------ Bit ------ 2940 7 6 5 4 3 2941 2 1 0 2942 0 R1 R0 1 1 1 1 1 2943 */ 2944 ds.write(B01111111); 2945 // 2946 WRITE SCRATCHPAD DATA TO EEPROM 2947 ds.reset(); 2948 // use this command when only 2949 1 sensor is used to avoid specifying the address! 2950 ds.skip(); 2951 // Select 2952 a specific DS18x20 device 2953 //ds.select(addr); 2954 2955 // COPY SCRATCHPAD [48h] 2956 2957 // This command copies the contents of the scratchpad TH, TL and configuration 2958 registers (bytes 2, 3 and 4) to EEPROM. 2959 // If the device is being used in parasite 2960 power mode, within 10s (max) after this command is issued the master must 2961 // 2962 enable a strong pullup on the 1-Wire bus for at least 10ms as described in the Powering 2963 the DS18B20 section. 2964 ds.write(0x48); 2965} 2966
Downloadable files
All files needed to make this project
All files needed to make this project
DCF77 Analyzer Clock v2.0 Schematic
DOWNLOAD to view details!
DCF77 Analyzer Clock v2.0 Schematic
All files needed to make this project
All files needed to make this project
DCF77 Analyzer Clock v2.0 Schematic
DOWNLOAD to view details!
DCF77 Analyzer Clock v2.0 Schematic
Documentation
PCB2: 7 Segment display, 8 Digits, Maxim 7219
This is my PCB design for the very compact smaller 7 segment displays
PCB2: 7 Segment display, 8 Digits, Maxim 7219
PCB: 7 Segment display, 8 Digits, Maxim 7219
This is a PCB I made for the Time and Date displays
PCB: 7 Segment display, 8 Digits, Maxim 7219
Audio files for the Adafruit Sound FX board
Ticking sound and Chime sound, see text for explanation
Audio files for the Adafruit Sound FX board
PCB: 7 Segment display, 8 Digits, Maxim 7219
This is a PCB I made for the Time and Date displays
PCB: 7 Segment display, 8 Digits, Maxim 7219
PCB2: 7 Segment display, 8 Digits, Maxim 7219
This is my PCB design for the very compact smaller 7 segment displays
PCB2: 7 Segment display, 8 Digits, Maxim 7219
Comments
Only logged in users can leave comments
Anonymous user
2 years ago
Well done! I will take a look at your filtering levels as this is the tricky thing to make DCF-77 work reliable. Kudos!
Anonymous user
2 years ago
Wow this is one of the coolest projects I have seen, not just the technical stuff, but the panel just looks amazing.
Anonymous user
2 years ago
Hi Erik Geweldig leuk project!! Complimenten hoor! Ik heb het inmiddels nagebouwd en alles lijkt prima te functioneren. Gelukkig waren er nog wat uitdagingen - frezen frontplaat bv - anders wordt het zo saai. Een ding heb ik echter niet kunnen vinden: de aansluiting van de option 1 en option 2 schakelaars. Zal er wel overheen hebben gekeken... Kun je me hiermee helpen? Alvast bedankt hiervoor Vriendelijke groet Bert de Jong
Anonymous user
2 years ago
Beste Erik, Heb de klok al vaak bekeken. Ik loop tegen enkele zaken aan die me nog steeds weerhouden om ermee te starten. Natuurlijk tijd en geld investeren. Maar vooral de printen en het voorfront. Ik heb een CNC frees en zou het dus met graveerplaat kunnen maken. Jij hebt het ergens laten drukken en daarna handmatig geboord. Dat lijkt me toch beter uiteindelijk. Dan de printen. Ik heb er geen enkele ervaring mee: bestanden opsturen en dan krijg ik de printen thuis. Zou je me wat meer info kunnen geven over het laten drukken van het voorfront en het laten maken van de printen. Arduino programmeren lukt me en de zaak aansluiten ook en de componenten bestellen ook. Complimenten in ieder geval voor het fantastisch mooie project!
Anonymous user
2 years ago
Its a big WOW!!! from me........ Looks professional and I can understand the hard work and dedication put into this wonderful project.
edr1924
2 years ago
Thank you! Yes it was hard work, hundreds of hours of learning but so rewarding...
Anonymous user
2 years ago
Yeah, you forgot the RTC Module. Doesn`t matter, I will add it accordingly. Many Thanks from Bavaria/Germany
Anonymous user
2 years ago
Congratulations for this well done project which I really like. I`ll try to downgrade it to its basics, time, date a.s.o. without the scientific part. Instead I will improve some functionalities like alarm clock function, display of temperature and humidity and will use huge 7 segment LEDs. Two question arose while studying the sketch and schematics: 1. You are using the RTC library, but I cannot find any RTC module. Does the DCF module deliver RTC code ? 2. Probably I will have to use 7 segment LEDs with common catode, which is the original way for the MAX72xx. Does this change your sketch for the MAX72xx ?
edr1924
2 years ago
Thanks Maxdidi for your comments... to answer your questions: 1. I can't believe I forgot to put the RTC in the schematic!?!? I connected a standard RTC module on the I2C port of the Arduino. 2. The Maxim chip IS designed to work with Common CATHODE displays so you have no problems using the Maxims. I would like to see your version of the clock, have fun making it!
Anonymous user
2 years ago
Nice project ! I am very impressed by your programming and constructing skills and searching for someone with that skills and passion for the materia of time working together on a bigger project. It would be nice if there is a way getting in contact to share some more Informations.
Anonymous user
2 years ago
Prachtig project!
Anonymous user
2 years ago
Beautiful and very inspiring project, really great. I will try to fit it to my design thoughts. I recognized one cool thing i don't really know how you did it: You have 2 ":" in your time display, but the PCBs are set up for standard 7-segment-LED-modules also for this position. Those 7-segment-display-modules (also the recommended Kingbright SA39-11SRWA) just have the one digit-point. Afaik, there are no modules (just) with round ":". How did you manage to get this feature? Did you just solder round mini-LEDs in or have you found special modules?
Anonymous user
2 years ago
Musikus, If you look at the photo section you can find out how special signs are made. https://www.flickr.com/photos/edr1924/albums/72157666568222444 I myself is harvesting parts to build one Analyser clock. Sture
Anonymous user
2 years ago
I have a question could you please make a list of the components or is there already one? I would like to build it because it is a nice project
Anonymous user
2 years ago
A question I still can they please at least tell me where they have the boards for the 7 segment display?
edr1924
2 years ago
I'm sorry I don't have a components list. It is an advanced project but with the provided sketch and schematic one should be able to build it as several others did already with succes. I hope you also will be able to complete this project, have fun!
edr1924
2 years ago
Well if you cannot find this basic thing by yourself, this project will be a challenge... I spent hundreds of hours searching the internet and fora to learn because I did not have any knowledge about the Arduino and receiving/decoding DCF77 signals. Anyway: try Ebay.com and type in the search field "8 digit 7219" You need 7 segment boards with the Maxim 7219 or 7221 chipset. Alternatively, you can download my PCB designs and order them at any PCB manufacturer. Due to health issues I cannot give more support, sorry about that...
brettoliver
2 years ago
Hi Erik. Another fantastic clock! I built a version of your clock some years back but always preferred your decoding LED layout to my Dot matrix layout. I have now built a MKII version using 1.5mm alluminium sheet to mount the displays. I have made a number of changes to the hardware ... 2 Unos and 2 cheap sound modules to name a few. I am currently writing up my build but you can see a sneak preview here http://www.brettoliver.org.uk/DCF77_Analyzer_Clock_Mk2/Arduino_DCF77_Analyzer_MK2.htm Thanks again for publishing all the details on your clocks. Brett.
Anonymous user
2 years ago
Hi, is this possible to buy Version 2.0 directly from you?
edr1924
2 years ago
No, sorry... this is DIY... ;)
Musikus
5 years ago
Beautiful and very inspiring project, really great. I will try to fit it to my design thoughts. I recognized one cool thing i don't really know how you did it: You have 2 ":" in your time display, but the PCBs are set up for standard 7-segment-LED-modules also for this position. Those 7-segment-display-modules (also the recommended Kingbright SA39-11SRWA) just have the one digit-point. Afaik, there are no modules (just) with round ":". How did you manage to get this feature? Did you just solder round mini-LEDs in or have you found special modules?
Sture_Nystrom
2 years ago
Musikus, If you look at the photo section you can find out how special signs are made. https://www.flickr.com/photos/edr1924/albums/72157666568222444 I myself is harvesting parts to build one Analyser clock. Sture
Anonymous user
5 years ago
Hi Erik Geweldig leuk project!! Complimenten hoor! Ik heb het inmiddels nagebouwd en alles lijkt prima te functioneren. Gelukkig waren er nog wat uitdagingen - frezen frontplaat bv - anders wordt het zo saai. Een ding heb ik echter niet kunnen vinden: de aansluiting van de option 1 en option 2 schakelaars. Zal er wel overheen hebben gekeken... Kun je me hiermee helpen? Alvast bedankt hiervoor Vriendelijke groet Bert de Jong
brettoliver
6 years ago
Hi Erik. Another fantastic clock! I built a version of your clock some years back but always preferred your decoding LED layout to my Dot matrix layout. I have now built a MKII version using 1.5mm alluminium sheet to mount the displays. I have made a number of changes to the hardware ... 2 Unos and 2 cheap sound modules to name a few. I am currently writing up my build but you can see a sneak preview here http://www.brettoliver.org.uk/DCF77_Analyzer_Clock_Mk2/Arduino_DCF77_Analyzer_MK2.htm Thanks again for publishing all the details on your clocks. Brett.
Anonymous user
6 years ago
Hi Erik, Thanks for sharing your project. I'm also building a clock with a DCF77 module (https://hackaday.io/project/93650-easy-alarm-clock). Currently the hardware only exists on breadboard. I have trouble getting acceptable DCF77 reception quality. I hoped to put the antenna and the receiver inside the same housing as the rest of the electronics (just like the commercial clock dials). I'm currently powering the circuitry with a USB output from the laptop. I noticed that the USB lines cause a lot of interference. A USB power bank works much better. I also get better results when keeping the receiver and antenna away from the other circuitry. I'm about 420km away from Mainflingen, so this should be well within range of the transmitter. You only touched it briefly in your video, but I saw that you're also leaving the antenna and DCF-receiver out of the housing. Do you only get acceptable receiving quality near a window? What DCF-module and antenna are you using? Have you evaluated different sources? I'm currently using the EM6 DCF 3V receiver from HKW with the AFET 77.5-10x100/70 antenna, also from HKW.
AlGio65
6 years ago
Hi, is this possible to buy Version 2.0 directly from you?
edr1924
2 years ago
No, sorry... this is DIY... ;)
roosiedb
6 years ago
Prachtig project!
Jackshobby
7 years ago
Beste Erik, Heb de klok al vaak bekeken. Ik loop tegen enkele zaken aan die me nog steeds weerhouden om ermee te starten. Natuurlijk tijd en geld investeren. Maar vooral de printen en het voorfront. Ik heb een CNC frees en zou het dus met graveerplaat kunnen maken. Jij hebt het ergens laten drukken en daarna handmatig geboord. Dat lijkt me toch beter uiteindelijk. Dan de printen. Ik heb er geen enkele ervaring mee: bestanden opsturen en dan krijg ik de printen thuis. Zou je me wat meer info kunnen geven over het laten drukken van het voorfront en het laten maken van de printen. Arduino programmeren lukt me en de zaak aansluiten ook en de componenten bestellen ook. Complimenten in ieder geval voor het fantastisch mooie project!
Anonymous user
7 years ago
Its a big WOW!!! from me........ Looks professional and I can understand the hard work and dedication put into this wonderful project.
edr1924
2 years ago
Thank you! Yes it was hard work, hundreds of hours of learning but so rewarding...
maxdidi
7 years ago
Yeah, you forgot the RTC Module. Doesn`t matter, I will add it accordingly. Many Thanks from Bavaria/Germany
maxdidi
7 years ago
Congratulations for this well done project which I really like. I`ll try to downgrade it to its basics, time, date a.s.o. without the scientific part. Instead I will improve some functionalities like alarm clock function, display of temperature and humidity and will use huge 7 segment LEDs. Two question arose while studying the sketch and schematics: 1. You are using the RTC library, but I cannot find any RTC module. Does the DCF module deliver RTC code ? 2. Probably I will have to use 7 segment LEDs with common catode, which is the original way for the MAX72xx. Does this change your sketch for the MAX72xx ?
edr1924
2 years ago
Thanks Maxdidi for your comments... to answer your questions: 1. I can't believe I forgot to put the RTC in the schematic!?!? I connected a standard RTC module on the I2C port of the Arduino. 2. The Maxim chip IS designed to work with Common CATHODE displays so you have no problems using the Maxims. I would like to see your version of the clock, have fun making it!
noel2905
8 years ago
I have a question could you please make a list of the components or is there already one? I would like to build it because it is a nice project
edr1924
2 years ago
Well if you cannot find this basic thing by yourself, this project will be a challenge... I spent hundreds of hours searching the internet and fora to learn because I did not have any knowledge about the Arduino and receiving/decoding DCF77 signals. Anyway: try Ebay.com and type in the search field "8 digit 7219" You need 7 segment boards with the Maxim 7219 or 7221 chipset. Alternatively, you can download my PCB designs and order them at any PCB manufacturer. Due to health issues I cannot give more support, sorry about that...
edr1924
2 years ago
I'm sorry I don't have a components list. It is an advanced project but with the provided sketch and schematic one should be able to build it as several others did already with succes. I hope you also will be able to complete this project, have fun!
noel2905
2 years ago
A question I still can they please at least tell me where they have the boards for the 7 segment display?
Anonymous user
8 years ago
Nice project ! I am very impressed by your programming and constructing skills and searching for someone with that skills and passion for the materia of time working together on a bigger project. It would be nice if there is a way getting in contact to share some more Informations.
Anonymous user
9 years ago
Well done! I will take a look at your filtering levels as this is the tricky thing to make DCF-77 work reliable. Kudos!
Anonymous user
9 years ago
Wow this is one of the coolest projects I have seen, not just the technical stuff, but the panel just looks amazing.
andreas_promoteus1965
a month ago
Good day, everyone, I’d like to take a moment to introduce myself. I am an electronics technician… and 60 years old! Currently, I am taking my first steps with Arduino. Some time ago, I came across the project DCF77 Analyzer/Clock v2.0. As far as I can see, I have understood it. However, implementing the two programs (main program and filter) is causing major problems! I am asking for help here… Just to clarify upfront… To the best of my knowledge, everything has been installed correctly. The include files have been integrated as far as I can tell. However, I suspect there are issues with the versions. I would like to exchange ideas… … so that I can complete this major project (and make it to the last bit of my retirement). Thanks for any responses!