This example is very similar to the simple clock example. However its output conforms to the Meinberg protocoll. Thus you could use my clock as a replacement for an expensive Meinberg master clock. Take notice that there is a reason why the Meinberg clocks are more expensive. The reason is the superior local osciallator of these clock. So although my clock speaks Meinberg’s protocoll it is still playing in a lower league with regard to short term Alan variance. With regard to noise resilence it most probably plays in the same or in a higher league. If someone would be so kind to give me a Meinberg master clock I will find out 😉
Also notice that its output is 9600 Baud 7 data pits, even parity, 2 stop bits aka “9600 7e2”. This is perfectly suitable for use with ntpd’s parse clock driver.
//
// www.blinkenlight.net
//
// Copyright 2014 Udo Klein
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
// Resources:
// http://www.obbl-net.de/dcf77.html
// http://home.arcor.de/armin.diehl/dcf77usb/
// http://www.meinbergglobal.com/english/info/ntp.htm#cfg
// http://www.meinbergglobal.com/download/ntp/docs/ntp_cheat_sheet.pdf
// http://www.meinberg.de/german/specs/timestr.htm
// http://www.eecis.udel.edu/~mills/ntp/html/parsedata.html
#include <dcf77.h>
const uint8_t dcf77_analog_sample_pin = 5;
const uint8_t dcf77_sample_pin = A5; // A5 == d19
const uint8_t dcf77_inverted_samples = 1;
const uint8_t dcf77_analog_samples = 1;
const uint8_t dcf77_signal_good_indicator_pin = 13;
const uint8_t dcf77_monitor_pin = A4; // A4 == d18
uint8_t sample_input_pin() {
const uint8_t sampled_data =
dcf77_inverted_samples ^ (dcf77_analog_samples? (analogRead(dcf77_analog_sample_pin) > 200)
: digitalRead(dcf77_sample_pin));
digitalWrite(dcf77_monitor_pin, sampled_data);
return sampled_data;
}
void workaround_to_enable_7e2_serial_communication() {
// At this time my clock will only compile successfully with Arduino 1.0.
// Attention: it seems to compiler with higher versions but the result
// does not process correctly.
// NTPD wants serial transmission mode 7E2 for the Meinberg setup.
// Unfortunatly this is only available with Arduino 1.0.1 or higher.
// My current workaround is to overwrite the Control and Status
// register after initializing serial communication.
// So instead of an upgrade to a higher Arduino version the line
// below has to suffice.
UCSR0C = 0x2C;
}
void setup() {
using namespace DCF77_Encoder;
Serial.begin(9600);
workaround_to_enable_7e2_serial_communication();
//Serial.begin(115200);
Serial.println();
Serial.println(F("Meinberg Emulator"));
Serial.println(F("(c) Udo Klein 2014"));
Serial.println(F("www.blinkenlight.net"));
Serial.println();
Serial.print(F("Sample Pin: ")); Serial.println(dcf77_sample_pin);
Serial.print(F("Inverted Mode: ")); Serial.println(dcf77_inverted_samples);
Serial.print(F("Analog Mode: ")); Serial.println(dcf77_analog_samples);
Serial.print(F("Monitor Pin: ")); Serial.println(dcf77_monitor_pin);
Serial.print(F("Signal Good Indicator Pin:")); Serial.println(dcf77_signal_good_indicator_pin);
Serial.println();
Serial.println();
Serial.println(F("Initializing..."));
Serial.println();
pinMode(dcf77_monitor_pin, OUTPUT);
pinMode(dcf77_sample_pin, INPUT);
digitalWrite(dcf77_sample_pin, HIGH);
pinMode(dcf77_signal_good_indicator_pin, OUTPUT);
digitalWrite(dcf77_signal_good_indicator_pin, LOW);
DCF77_Clock::setup();
DCF77_Clock::set_input_provider(sample_input_pin);
}
void paddedPrint(BCD::bcd_t n) {
Serial.print(n.digit.hi);
Serial.print(n.digit.lo);
}
void loop() {
const char STX = 2;
const char ETX = 3;
DCF77_Clock::time_t now;
DCF77_Clock::get_current_time(now);
if (now.month.val > 0) {
//Serial.println();
Serial.print(STX);
Serial.print("D:");
paddedPrint(now.day);
Serial.print('.');
paddedPrint(now.month);
Serial.print('.');
paddedPrint(now.year);
Serial.print(';');
Serial.print("T:");
Serial.print(now.weekday.digit.lo);
Serial.print(';');
Serial.print("U:");
paddedPrint(now.hour);
Serial.print('.');
paddedPrint(now.minute);
Serial.print('.');
paddedPrint(now.second);
Serial.print(';');
uint8_t state = DCF77_Clock::get_clock_state();
Serial.print(
state == DCF77::useless || state == DCF77::dirty? '#' // not synced
: ' ' // good
);
Serial.print(
state == DCF77::synced || state == DCF77::locked? ' ' // DCF77
: '*' // crystal clock
);
digitalWrite(dcf77_signal_good_indicator_pin, state >= DCF77::locked);
Serial.print(now.uses_summertime? 'S': ' ');
Serial.print(
now.timezone_change_scheduled? '!':
now.leap_second_scheduled? 'A':
' '
);
Serial.print(ETX);
}
}
