Meinberg Emulator

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);
    }
}
Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.