#include <Streaming.h>
Print &cout {Serial};

using uint = unsigned int;
using ulong = unsigned long;

constexpr byte LDR_PIN {2};
constexpr byte RELAIS_PIN1 {8};
constexpr byte RELAIS_PIN2 {9};
constexpr byte LED_PIN {LED_BUILTIN};

// Time im milliseconds (ms)
constexpr ulong MEASURE_DELAY_MS {15 * 1000};    // Time between two LDR measurements
constexpr ulong DAYLIGHT_DELAY_MS {5 * 1000};    // Delay time for daytime switching.
constexpr ulong DARKNESS_DELAY_MS {10 * 1000};   // {120 * 60 * 1000} Delay time for night switching.

// Program parameters end

// Class / struct Definition(s)

//////////////////////////////////////////////////////////////////////////////
/// @brief Helper class for non-blocking execution of
/// code sections at certain intervals.
///
//////////////////////////////////////////////////////////////////////////////
class Interval {
public:
  //////////////////////////////////////////////////////////////////////////////
  /// @brief  Determine whether the specified interval has expired.
  ///
  /// @param duration    Interval duration
  /// @return true       when the interval has elapsed
  /// @return false      interval not elapsed yet
  //////////////////////////////////////////////////////////////////////////////
  bool operator()(const ulong duration) {
    if (false == isStarted) { return start(false); }
    return (millis() - timeStamp >= duration) ? start(true) : false;
  }

private:
  bool start(bool state = false) {
    isStarted = !state;   // Set the value to true on the first call
    timeStamp = millis();
    return state;
  }

private:
  bool isStarted {false};   // Flag = true if the first Operator() call has been made.
  ulong timeStamp {0};
};

enum class LDRStatus : byte { daylight = 0, darkness, unchecked };

//////////////////////////////////////////////////////////////////////////////
/// @brief Class for reading in an LDR (digital output) with delayed response
///
//////////////////////////////////////////////////////////////////////////////
class Ldr {
public:
  Ldr(byte pin, Interval &timer, const ulong time) : pin {pin}, timer {timer}, time {time} {}
  Ldr(const Ldr &) = delete;                       // No copy constructor
  Ldr &operator=(const Ldr &) = delete;            // No assignment
  void begin() { pinMode(pin, INPUT); }            // Init LDR input pin
  bool operator()();                               // Check for value change
  LDRStatus getStatus() const { return status; }   // Returns the LDR status (light/dark)

private:
  const byte pin;                               // LDR Pin
  Interval &timer;                              // Timer for delaying readings
  const ulong time;                             // Length of the delay in milliseconds
  LDRStatus status {LDRStatus::daylight};       // Actual LDR read status
  LDRStatus prevStatus {LDRStatus::daylight};   // Previous LDR read status
  byte result[2] {0};                           // LDR read values for comparison
  byte index {0};                               // Index for the result array
};

//////////////////////////////////////////////////////////////////////////////
/// @brief Read in the LDR value and check whether it has changed since the last check.
///
/// @return true     LDR Value has changed
/// @return false    LDR Value hasn't changed
//////////////////////////////////////////////////////////////////////////////
bool Ldr::operator()() {
  if (timer(time)) {
    result[index++] = digitalRead(pin);
    status = (result[1]) ? LDRStatus::darkness : LDRStatus::daylight;
    if (result[0] == result[1] && status != prevStatus) {
      prevStatus = status;
      return true;
    }
    if (index > 1) { index = 0; };
  }
  return false;
}

struct SwdTimerData {
  const ulong swDelay[static_cast<byte>(LDRStatus::unchecked)];
  Interval &timer;
};

// Class / struct definition(s) end

Interval mdTimer;    // Measure delay timer
Interval swdTimer;   // Switch delay timer
// initialize SwdTimerData structure
SwdTimerData swdtData {
    {DAYLIGHT_DELAY_MS, DARKNESS_DELAY_MS},
    swdTimer
};   
Ldr ldr {LDR_PIN, mdTimer, MEASURE_DELAY_MS};            // create ldr object

//////////////////////////////////////////////////////////////////////////////
/// @brief Handles the switching of the relays
///
/// @param ldrStatus Status for deciding which relay switching should take place
//////////////////////////////////////////////////////////////////////////////
void switchRelais(LDRStatus ldrStatus) {
  switch (ldrStatus) {
    case LDRStatus::daylight:
      cout << F("Tageslichtschaltung aktiv\n");
      digitalWrite(RELAIS_PIN1, HIGH);
      digitalWrite(RELAIS_PIN2, LOW);
      digitalWrite(LED_PIN, HIGH);
      break;
    case LDRStatus::darkness:
      cout << F("Dunkelschaltung aktiv\n");
      digitalWrite(RELAIS_PIN1, LOW);
      digitalWrite(RELAIS_PIN2, HIGH);
      digitalWrite(LED_PIN, LOW);
      break;
    default: break;
  }
}

//////////////////////////////////////////////////////////////////////////////
/// @brief Control of the switching delays for day/night switching
///
/// @param status    Is it light or dark?
/// @param swdt      struct with timer data
/// @return true     The switching process has been completed
/// @return false    The delay time has not yet expired
//////////////////////////////////////////////////////////////////////////////
bool switchControl(LDRStatus status, SwdTimerData &swdt) {
  switch (status) {
    case LDRStatus::darkness: [[fallthrough]];
    case LDRStatus::daylight:
      if (swdt.timer(swdt.swDelay[static_cast<byte>(status)])) {
        switchRelais(status);
        return true;
      }
      break;
    default: break;
  }
  return false;
}

void setup() {
  Serial.begin(115200);
  cout << F("Programm ") << __FILE__ << F(" gestarted") << endl;
  pinMode(LED_PIN, OUTPUT);
  pinMode(RELAIS_PIN1, OUTPUT);
  pinMode(RELAIS_PIN2, OUTPUT);
  ldr.begin();
  switchRelais(LDRStatus::daylight);
}

void loop() {
  static LDRStatus ldrStatus {LDRStatus::daylight};
  static bool callSwitchControl {false};

  if (ldr()) {
    cout << F("Status geändert. Schaltverzögerung ein: ");
    ldrStatus = ldr.getStatus();
    callSwitchControl = true;
  }
  if (callSwitchControl) { callSwitchControl = not switchControl(ldrStatus, swdtData); }
}
Zum Einstellen des LDR, draufklicken und Schieber verstellen. Dunkel bei < 100Lux
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module