#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