Moin @txbll ,
Dein Code scheint sich auf einen DCF77-Empfänger zu beziehen.
Ich habe für diesen Fall einen DCF77-Simulator und einen passende Empfänger-Code erstellt:
DCF77Simple.ino
/*
DCF77 Simulator and Receiver/receiver
Created by ec2021
2026/03/27
*/
#include "DCF77_Simulator.h"
const char daysOfTheWeek[7][3] = { "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So" };
class DCF77PulseReceiver {
private:
unsigned long _lowTime;
unsigned long _highTime;
unsigned long _lastChange;
byte _state;
byte _lastState;
byte _pin;
public:
void begin(byte pin) {
_pin = pin;
pinMode(_pin, INPUT_PULLUP); // To allow for timeouts if not connected
_lastState = digitalRead(_pin);
};
bool pinIsHigh() {
return _state;
};
unsigned long getHighTime() {
return _highTime;
};
unsigned long getLowTime() {
return _lowTime;
};
bool timedOut() {
return (millis() - _lastChange > DCF77::TIME_OUT);
};
bool hasChanged() {
byte state = digitalRead(_pin);
if (state != _state) {
_state = state;
if (_state) {
_lowTime = millis() - _lastChange;
} else {
_highTime = millis() - _lastChange;
}
_lastChange = millis();
return true;
}
return false;
}
};
struct DateTime {
byte hour;
byte minute;
byte wtag;
byte day;
byte month;
byte year;
} dt;
DCF77Simulator simulator(9);
DCF77PulseReceiver receiver;
enum class STATE { IDLE,
SYNC,
TIMEOUT,
ZERO,
ONE };
byte count = 0;
void setup() {
Serial.begin(115200);
simulator.setTime(23, 58, 29, 5, 26, 5); // Fr, 27.03.24, 23:57
simulator.begin();
Serial.println(F("\nDCF77 Simple v1.0"));
receiver.begin(2);
}
bool synced = false;
void loop() {
if (!synced && dcfState() == STATE::SYNC) {
Serial.println(F("Sync Found"));
synced = true;
}
if (synced) {
synced = syncedStateMachine();
if (!synced){
Serial.println(F("Sync Lost"));
}
}
}
bool parityError(uint32_t val, byte pBit) {
int p = 0;
for (int i = 0; i < 32; i++) {
if (bitRead(val, i)) p++;
}
return (p % 2 != pBit);
}
STATE dcfState() {
STATE value = STATE::IDLE;
if (receiver.hasChanged()) {
if (receiver.pinIsHigh()) {
value = (receiver.getLowTime() > 1500) ? STATE::SYNC : STATE::IDLE;
} else {
value = (receiver.getHighTime() < 150) ? STATE::ZERO : STATE::ONE;
}
}
if (receiver.timedOut()) {
value = STATE::TIMEOUT;
}
return value;
}
bool syncedStateMachine() {
static byte bitNo = 0;
static uint32_t bitPattern = 0;
byte aBit;
STATE state = dcfState();
switch (state) {
case STATE::TIMEOUT:
count = 0;
Serial.println(F("\nTimeOut"));
return false;
break;
case STATE::IDLE:
return true;
break;
case STATE::SYNC:
count = 0;
Serial.println(F("\nRe-Synced"));
return true;
break;
case STATE::ZERO:
aBit = 0;
break;
case STATE::ONE:
aBit = 1;
break;
}
switch (count) {
case DCF77::BIT_START... DCF77::BIT_SEC_MARKER:
if (count == DCF77::BIT_START) {
Serial.print(F("Bits 0..20 "));
}
Serial.print(aBit);
if (count == DCF77::BIT_SEC_MARKER) {
Serial.print(F("\nSekundenmarker "));
if (aBit == 1) {
Serial.print(F("\t Ok"));
} else {
count = 0;
Serial.println(F("\tNOT Ok"));
return false;
}
}
break;
case DCF77::BIT_MIN_START... DCF77::BIT_MIN_P1:
if (count == DCF77::BIT_MIN_START) {
Serial.print(F("\nMinute "));
bitNo = 0;
bitPattern = 0;
}
Serial.print(aBit);
if (count == DCF77::BIT_MIN_P1) {
if (parityError(bitPattern, aBit)) {
Serial.print("Parity Error");
}
Serial.print(F("\t = "));
printlnPadded(dt.minute);
} else {
decode(dt.minute, bitNo, aBit);
bitPattern = (bitPattern << 1) + aBit;
}
break;
case DCF77::BIT_HOUR_START... DCF77::BIT_HOUR_P2:
if (count == DCF77::BIT_HOUR_START) {
Serial.print(F("Stunde "));
bitNo = 0;
bitPattern = 0;
}
Serial.print(aBit);
if (count == DCF77::BIT_HOUR_P2) {
if (parityError(bitPattern, aBit)) {
Serial.print("Parity Error");
}
Serial.print(F("\t = "));
printlnPadded(dt.hour);
} else {
decode(dt.hour, bitNo, aBit);
bitPattern = (bitPattern << 1) + aBit;
}
break;
case DCF77::BIT_DAY_START... DCF77::BIT_WDAY_START - 1:
if (count == DCF77::BIT_DAY_START) {
Serial.print(F("Tag "));
bitNo = 0;
bitPattern = 0;
}
Serial.print(aBit);
decode(dt.day, bitNo, aBit);
bitPattern = (bitPattern << 1) + aBit;
if (count == DCF77::BIT_WDAY_START - 1) {
Serial.print(F("\t = "));
printlnPadded(dt.day);
}
break;
case DCF77::BIT_WDAY_START... DCF77::BIT_MONTH_START - 1:
if (count == DCF77::BIT_WDAY_START) {
Serial.print("WTag ");
bitNo = 0;
}
Serial.print(aBit);
decode(dt.wtag, bitNo, aBit);
bitPattern = (bitPattern << 1) + aBit;
if (count == DCF77::BIT_MONTH_START - 1) {
Serial.print(F("\t = "));
printPadded(dt.wtag);
Serial.print(" - ");
Serial.println(daysOfTheWeek[dt.wtag - 1]);
};
break;
case DCF77::BIT_MONTH_START... DCF77::BIT_YEAR_START - 1:
if (count == DCF77::BIT_MONTH_START) {
Serial.print("Monat ");
bitNo = 0;
}
Serial.print(aBit);
decode(dt.month, bitNo, aBit);
bitPattern = (bitPattern << 1) + aBit;
if (count == DCF77::BIT_YEAR_START - 1) {
Serial.print(F("\t = "));
printlnPadded(dt.month);
}
break;
case DCF77::BIT_YEAR_START... DCF77::BIT_DATE_P3:
if (count == DCF77::BIT_YEAR_START) {
Serial.print(F("Jahr "));
bitNo = 0;
}
Serial.print(aBit);
if (count == DCF77::BIT_DATE_P3) {
if (parityError(bitPattern, aBit)) {
Serial.print("Date Parity Error");
}
Serial.print(F(" = "));
printlnPadded(dt.year);
} else {
decode(dt.year, bitNo, aBit);
bitPattern = (bitPattern << 1) + aBit;
}
break;
case DCF77::BIT_DATE_P3 + 1 ... 255:
count = 0;
return true;
break;
default:
Serial.println(F("Default: This should never show up!"));
break;
}
count++;
return true;
}
void decode(byte& value, byte& bN, byte aBit) {
const uint8_t weights[] = { 1, 2, 4, 8, 10, 20, 40, 80 };
if (bN == 0) value = 0;
if (aBit > 0 && bN < 8) {
value += weights[bN];
}
bN++;
}
void printlnPadded(int v) {
printPadded(v);
Serial.println();
}
void printPadded(int v) {
if (v < 10) Serial.print(F("0"));
Serial.print(v);
}
DCF77_Constants.h
#ifndef DCF77_CONSTANTS_H
#define DCF77_CONSTANTS_H
#include <Arduino.h>
namespace DCF77 {
static constexpr char VERSION[] = "10.2";
static constexpr uint32_t TICK_US = 100000UL;
static constexpr unsigned long TIME_OUT = 5000;
static constexpr uint8_t BIT_START = 0;
static constexpr uint8_t BIT_SEC_MARKER = 20;
static constexpr uint8_t BIT_MIN_START = 21;
static constexpr uint8_t BIT_MIN_LEN = 7;
static constexpr uint8_t BIT_MIN_P1 = 28;
static constexpr uint8_t BIT_HOUR_START = 29;
static constexpr uint8_t BIT_HOUR_LEN = 6;
static constexpr uint8_t BIT_HOUR_P2 = 35;
static constexpr uint8_t BIT_DAY_START = 36;
static constexpr uint8_t BIT_DAY_LEN = 6;
static constexpr uint8_t BIT_WDAY_START = 42;
static constexpr uint8_t BIT_WDAY_LEN = 3;
static constexpr uint8_t BIT_MONTH_START = 45;
static constexpr uint8_t BIT_MONTH_LEN = 5;
static constexpr uint8_t BIT_YEAR_START = 50;
static constexpr uint8_t BIT_YEAR_LEN = 8;
static constexpr uint8_t BIT_DATE_P3_LEN = 22;
static constexpr uint8_t BIT_DATE_P3 = 58;
static constexpr uint32_t SYNC_GAP_US = 1500000UL;
static constexpr uint32_t BIT_THRES_US = 150000UL;
inline bool read64(uint64_t f, uint8_t n) { return (f & (1ULL << n)) != 0; }
inline void write64(volatile uint64_t *f, uint8_t n, bool b) {
if (b) *f |= (1ULL << n); else *f &= ~(1ULL << n);
}
}
#endif
DCF77_Simulator.h
#ifndef DCF77_SIMULATOR_H
#define DCF77_SIMULATOR_H
#include <Arduino.h>
#include <TimerOne.h>
#include "DCF77_Constants.h"
class DCF77Simulator {
public:
DCF77Simulator(uint8_t pin) : _outPin(pin) { _instance = this; }
void setTime(uint8_t h, uint8_t m, uint8_t d, uint8_t mo, uint8_t y, uint8_t dw) {
_hour = h; _minute = m; _day = d; _month = mo; _year = y; _dow = dw;
prepareNextFrame();
}
void begin() {
pinMode(_outPin, OUTPUT);
_currentSec = 55; _subSec = 0;
prepareNextFrame(); _activeFrame = _nextFrame;
Timer1.initialize(DCF77::TICK_US); Timer1.attachInterrupt(isrWrapper);
}
private:
static DCF77Simulator* _instance;
volatile uint64_t _activeFrame = 0;
uint64_t _nextFrame = 0;
volatile uint8_t _currentSec = 0, _subSec = 0;
uint8_t _hour=0, _minute=0, _day=0, _month=0, _year=0, _dow=0, _outPin;
static void isrWrapper() { if (_instance) _instance->handleInterrupt(); }
void writeBcd(uint8_t start, uint8_t len, uint8_t val) {
uint8_t units = val % 10;
uint8_t tens = val / 10;
for (uint8_t i = 0; i < len; i++) {
bool bit = (i < 4) ? (units & (1 << i)) : (tens & (1 << (i - 4)));
DCF77::write64(&_nextFrame, start + i, bit);
}
}
void setParity(uint8_t start, uint8_t len, uint8_t pPos) {
bool p = false;
for (uint8_t i = start; i < start + len; i++) {
if (DCF77::read64(_nextFrame, i)) p = !p;
}
DCF77::write64(&_nextFrame, pPos, p);
}
void prepareNextFrame() {
_nextFrame = 0;
DCF77::write64(&_nextFrame, 20, 1);
writeBcd(DCF77::BIT_MIN_START, DCF77::BIT_MIN_LEN, _minute);
setParity(DCF77::BIT_MIN_START, DCF77::BIT_MIN_LEN, DCF77::BIT_MIN_P1);
writeBcd(DCF77::BIT_HOUR_START, DCF77::BIT_HOUR_LEN, _hour);
setParity(DCF77::BIT_HOUR_START, DCF77::BIT_HOUR_LEN, DCF77::BIT_HOUR_P2);
writeBcd(DCF77::BIT_DAY_START, DCF77::BIT_DAY_LEN, _day);
writeBcd(DCF77::BIT_WDAY_START, DCF77::BIT_WDAY_LEN, _dow);
writeBcd(DCF77::BIT_MONTH_START, DCF77::BIT_MONTH_LEN, _month);
writeBcd(DCF77::BIT_YEAR_START, DCF77::BIT_YEAR_LEN, _year);
setParity(DCF77::BIT_DAY_START, DCF77::BIT_DATE_P3_LEN, DCF77::BIT_DATE_P3);
}
void advanceDate() {
_dow = (_dow % 7) + 1; // Wochentag 1-7 (Mo-So)
static const uint8_t daysInMonth[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (++_day > daysInMonth[_month]) {
_day = 1;
if (++_month > 12) {
_month = 1;
_year = (_year + 1) % 100;
}
}
}
void handleInterrupt() {
if (_currentSec == 59) digitalWrite(_outPin, LOW);
else {
if (_subSec == 0) digitalWrite(_outPin, HIGH);
else if (_subSec == 1 && !DCF77::read64(_activeFrame, _currentSec)) digitalWrite(_outPin, LOW);
else if (_subSec >= 2) digitalWrite(_outPin, LOW);
}
if (++_subSec >= 10) {
_subSec = 0;
if (++_currentSec >= 60) {
_currentSec = 0;
if (++_minute >= 60) {
_minute = 0;
if (++_hour >= 24) {
_hour = 0;
advanceDate();
}
}
prepareNextFrame();
_activeFrame = _nextFrame;
}
}
}
};
DCF77Simulator* DCF77Simulator::_instance = nullptr;
#endif
Der Simulator überträgt das DCF77-Protokoll auf Pin 9 eines Uno oder Nano (und benutzt dazu die TimerOne-Bibliothek), der Empfänger liest in der loop() über den Pin 2 ein und decodiert Zeit- und Datumswerte.
@Kai-R :
Im Empfänger werden die einzelnen Daten bitweise separat zusammengestellt, sobald sie empfangen werden.
Der Simulator verwendet uint64_t zum Zusammensetzen und Versenden der Daten. Hier ist bei AVR-Controllern zu berücksichtigen, dass die bitWrite()-Funktion aufgrund der Umsetzung über bitSet() / bitClear() auf 32 Bit-Variablen beschränkt ist:
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitToggle(value, bit) ((value) ^= (1UL << (bit)))
#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet(value, bit) : bitClear(value, bit))
siehe u.a. hier
https://forum.arduino.cc/t/bitread-and-bitwrite-for-numbers-bigger-than-a-byte/1160471/3
bitRead() tut es hingegen ...
Ich benutze aber vorsichtshalber diese Funktionen (in DCF77_Constants.h hinterlegt):
inline bool read64(uint64_t f, uint8_t n) { return (f & (1ULL << n)) != 0; }
inline void write64(volatile uint64_t *f, uint8_t n, bool b) {
if (b) *f |= (1ULL << n); else *f &= ~(1ULL << n);
}