#include #include #include #include #include #include // Pin-Konfiguration const int LIGHT_BARRIER_PIN = 2; const int RTC_SQW_PIN = 3; const int SD_CS_PIN = 10; // Sensor-, Uhr- und GPS-Objekte RTC_DS3231 rtc; Adafruit_BME280 bme; TinyGPSPlus gps; // Volatile Variablen für die Interrupts volatile unsigned long totalPulses = 0; volatile unsigned long ppsMicros = 0; volatile unsigned long currentRtcEpoch = 0; // Hilfsvariablen für die Datenübergabe aus dem Interrupt volatile unsigned long targetPulseNumber = 0; volatile unsigned long targetMicros = 0; volatile unsigned long targetEpoch = 0; volatile unsigned long targetPpsMicros = 0; volatile bool logFlag = false; // Status-Variablen für Langzeitstabilität bool sdCardReady = false; unsigned long lastGpsSyncDay = 0; void pulseInterrupt() { totalPulses++; // Alle 600 Impulse Daten-Schnappschuss einfrieren if (totalPulses % 600 == 0) { targetPulseNumber = totalPulses; targetMicros = micros(); targetEpoch = currentRtcEpoch; targetPpsMicros = ppsMicros; logFlag = true; } } void ppsInterrupt() { ppsMicros = micros(); } void setup() { Serial.begin(115200); Serial1.begin(9600); // GPS an Hardware-Serial (Pin 0/1) Wire.begin(); pinMode(LIGHT_BARRIER_PIN, INPUT_PULLUP); pinMode(RTC_SQW_PIN, INPUT_PULLUP); // 1. RTC initialisieren if (rtc.begin()) { rtc.writeSqwPinMode(DS3231_SquareWave1Hz); currentRtcEpoch = rtc.now().unixtime(); } else { Serial.println("Kritischer Fehler: RTC nicht gefunden!"); } // 2. BME280 initialisieren if (!bme.begin(0x76)) { Serial.println("Warnung: BME280 nicht gefunden!"); } // 3. Erster SD-Karten-Testversuch checkSDCard(); // Interrupts aktivieren attachInterrupt(digitalPinToInterrupt(RTC_SQW_PIN), ppsInterrupt, RISING); attachInterrupt(digitalPinToInterrupt(LIGHT_BARRIER_PIN), pulseInterrupt, FALLING); Serial.println("Dauerbetrieb gestartet. Warte auf Impulse..."); } void loop() { // Hintergund-Task 1: GPS-Daten für tägliche RTC-Korrektur parsen while (Serial1.available() > 0) { gps.encode(Serial1.read()); } // Hintergrund-Task 2: Sekundenzähler der RTC aktualisieren static unsigned long lastPpsMicrosCheck = 0; if (ppsMicros != lastPpsMicrosCheck) { lastPpsMicrosCheck = ppsMicros; currentRtcEpoch++; // Einmal am Tag RTC mit GPS-Zeit abgleichen (um Mitternacht bzw. bei Satellitenkontakt) if (gps.time.isValid() && gps.date.isValid() && gps.date.day() != lastGpsSyncDay) { if (gps.time.age() < 500) { // Daten dürfen nicht älter als 500ms sein DateTime gpsTime(gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second()); rtc.adjust(gpsTime); currentRtcEpoch = gpsTime.unixtime(); lastGpsSyncDay = gps.date.day(); Serial.println("RTC erfolgreich mit GPS synchronisiert."); } } } // Log-Task: Wenn 600 Impulse erreicht wurden if (logFlag) { logFlag = false; // Flag sofort zurücksetzen // Lokale Kopien sichern unsigned long pulseNum = targetPulseNumber; unsigned long rtcSec = targetEpoch; unsigned long mMicros = targetMicros; unsigned long pMicros = targetPpsMicros; // Sensorwerte auslesen float temp = bme.readTemperature(); float press = bme.readPressure() / 100.0F; float hum = bme.readHumidity(); // Hochgenaue Sub-Sekunden-Berechnung unsigned long offsetMicros = mMicros - pMicros; if (mMicros < pMicros) { offsetMicros = (0xFFFFFFFF - pMicros) + mMicros + 1; } unsigned long exactEpochMs = (rtcSec * 1000) + (offsetMicros / 1000); unsigned int remainingMicros = offsetMicros % 1000; // SD-Karten-Verfügbarkeit prüfen if (!sdCardReady) { checkSDCard(); // Versuche neu zu initialisieren, falls Karte zuvor entnommen wurde } if (sdCardReady) { File dataFile = SD.open("datalog.csv", FILE_WRITE); if (dataFile) { dataFile.print(pulseNum); dataFile.print(";"); dataFile.print(exactEpochMs); dataFile.print(";"); dataFile.print(remainingMicros); dataFile.print(";"); dataFile.print(temp, 2); dataFile.print(";"); dataFile.print(press, 2); dataFile.print(";"); dataFile.println(hum, 2); dataFile.close(); Serial.print("Daten auf SD geschrieben bei Impuls: "); Serial.println(pulseNum); } else { Serial.println("Schreibfehler: Karte physisch entfernt?"); sdCardReady = false; // Status zurücksetzen für den nächsten Loop-Versuch } } else { // Karte fehlt: Daten werden im Seriellen Monitor ausgegeben, damit nichts verloren geht Serial.print("SD fehlt! Daten verloren vermieden -> Impuls: "); Serial.print(pulseNum); Serial.print(" | Zeit MS: "); Serial.println(exactEpochMs); } } } // Funktion zur sicheren und wiederholten Initialisierung der SD-Karte void checkSDCard() { // SD.begin kann auf dem Uno R4 mehrfach aufgerufen werden, um ein Wiederreinstecken zu erkennen if (SD.begin(SD_CS_PIN)) { sdCardReady = true; // Header schreiben, falls die Karte komplett neu/leer ist File dataFile = SD.open("datalog.csv", FILE_WRITE); if (dataFile) { if (dataFile.size() == 0) { dataFile.println("Impuls_Nummer;Zeitstempel_Epoch_ms;Sub_Mikrosekunden;Temperatur_C;Luftdruck_hPa;Feuchtigkeit_Prozent"); } dataFile.close(); } Serial.println("SD-Karte bereit und aktiv."); } else { sdCardReady = false; Serial.println("SD-Karte nicht erkannt oder entnommen. Programm läuft weiter..."); } }