#include <LiquidCrystal_I2C.h>
#include <RotaryEncoder.h>
//
// globale Konstanten
//
constexpr uint8_t laserPin = 9; // Pin für die Laserdiode
constexpr uint8_t ledPin = 10; // Pin für die Leuchtdiode
constexpr uint8_t ldrPin = A0; // Pin für die Fotozelle
constexpr uint8_t pin_vz_A = 2; // DT - Drehgeber für Verzögerung PIN A
constexpr uint8_t pin_vz_B = 3; // CLK - Drehgeber für Verzögerung PIN B
constexpr uint8_t pin_sw_A = 4; // DT - Drehgeber für Schwellenwert PIN A
constexpr uint8_t pin_sw_B = 5; // CLK - Drehgeber für Schwellenwert PIN B
constexpr unsigned int minTime_ms {0};
constexpr unsigned int maxTime_ms {1000};
//
// Klassendefinitionen
//
class Timer {
public:
void start() { timeStamp = millis(); }
bool operator()(const uint32_t duration) { return (millis() - timeStamp >= duration) ? true : false; }
private:
uint32_t timeStamp {0};
};
//
// Extend the RotaryEncoder class with
// 1. a lower and upper limit for a counter
// 2. a variable step count
//
template <typename T> class RotaryEncoderExt : public RotaryEncoder {
public:
RotaryEncoderExt(uint8_t pin1, uint8_t pin2, T valMin, T valMax, LatchMode mode = LatchMode::FOUR0)
: RotaryEncoder {pin1, pin2, mode}, valMin {valMin}, valMax {valMax} {}
template <typename V> bool query(V &value);
void setStep(int value) { step = value; }
private:
const T valMin; // value minimum
const T valMax; // value maximum
int step {1};
};
template <typename T> template <typename V> bool RotaryEncoderExt<T>::query(V &value) {
uint8_t flag {true};
tick();
switch (getDirection()) {
case RotaryEncoder::Direction::NOROTATION: flag = false; break;
case RotaryEncoder::Direction::CLOCKWISE: value = value < valMax ? value + step : valMin; break;
case RotaryEncoder::Direction::COUNTERCLOCKWISE: value = value > valMin ? value - step : valMax; break;
}
return flag;
}
// ---- RotaryEncoderExt End ------------
enum class Status : uint8_t { idle, active, waitReady };
//
// Globale Variablen/Objekte
//
// Erstelle einen Encoder-Objekt
using Encoder = RotaryEncoderExt<decltype(maxTime_ms)>;
Encoder encVz {pin_vz_A, pin_vz_B, minTime_ms, maxTime_ms, RotaryEncoder::LatchMode::FOUR3};
Encoder encSw {pin_sw_A, pin_sw_B, minTime_ms, maxTime_ms, RotaryEncoder::LatchMode::FOUR3};
Timer wait;
// Erstelle ein LCD-Objekt
LiquidCrystal_I2C lcd(0x27, 16, 2); // LCD-Adresse (muss ggf. angepasst werden)
unsigned int verz = 300; // Anfangswert für Verzögerung
unsigned int sw = 200; // Anfangswert für Schwellenwert
//
// Funktionen
//
void initDisplay() {
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("VZ-Wert:");
lcd.setCursor(0, 1);
lcd.print("SW-Wert:");
}
// Funktion zum Aktualisieren des Displays
void updateDisplay() {
// Erste Zeile für Verzögerung
lcd.setCursor(9, 0); // Setze den Cursor auf die zweite Zeile
lcd.print(" "); // Leere Zeile
lcd.setCursor(9, 0); // Setze den Cursor wieder auf die zweite Zeile
lcd.print(verz); // Zeige den aktuellen Wert an
// Zweite Zeile für Schwellenwert
lcd.setCursor(9, 1); // Setze den Cursor auf die zweite Zeile
lcd.print(" "); // Leere Zeile
lcd.setCursor(9, 1); // Setze den Cursor wieder auf die zweite Zeile
lcd.print(sw); // Zeige den aktuellen Wert an
}
//
// Hauptprogramm
//
void setup() {
Serial.begin(115200);
pinMode(laserPin, OUTPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(laserPin, HIGH); // Laserdiode einschalten
encVz.setStep(5); // Jeder klick verändert den Encoderwert je nach Drehrichtung um +/- 5
encSw.setStep(5);
initDisplay();
updateDisplay();
}
void loop() {
static Status swStatus {Status::idle};
unsigned int prevAnalogVal {0};
if (encVz.query(verz)) { updateDisplay(); }
if (encSw.query(sw)) { updateDisplay(); }
unsigned int analogVal = analogRead(ldrPin);
if (analogVal != prevAnalogVal) { // Folgendes nur durchführen, wenn sich der Messwert geändert hat
prevAnalogVal = analogVal;
if (analogVal > sw) {
wait.start(); // Der Schwellenwert wurde überschritten also Timer für die Verzögerungszeit starten.
swStatus = Status::active;
if (digitalRead(ledPin) == HIGH) { digitalWrite(ledPin, LOW); }
}
}
switch (swStatus) {
case Status::active:
if (wait(verz)) { // LED nach Ablauf der eingestellten Verzögerungszeit einschalten
wait.start(); // Timer für nächste Warteperiode starten
digitalWrite(ledPin, HIGH); // LED einschalten
swStatus = Status::waitReady;
}
break;
case Status::waitReady:
if (wait(1000)) { // Wenn zweite Warteperiode abgelaufen ist, LED wieder ausschalten.
digitalWrite(ledPin, LOW); // LED ausschalten
swStatus = Status::idle;
}
default: break;
}
}