#include <Wire.h>
#include <OneButtonTiny.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <Debouncer.h>
#include <SSD1306Ascii.h>
#include <SSD1306AsciiWire.h>
#define I2C_ADDRESS 0x3C
#define INCLUDE_SCROLLING 1
#define INITIAL_SCROLL_MODE SCROLL_MODE_AUTO
SSD1306AsciiWire oled;
#define TRIGGER_PIN 2 //10
#define ECHO_PIN 3 //11
#define ENDSCH_PIN 5 //5
#define TAST_PIN 6 //6
#define RELAIS_PIN 7 //4
#define LED_ZUHAUSEPIN 8 //5
#define EEPROMSTART 10 //Erste Speicherstelle für Einstellwerte
#define SM_UNDEF 0
#define SM_ZUHAUSE 1
#define SM_ZUHAUSEGEPRUEFT 2
#define SM_VERLASSEN 3 //nicht mehr zu Hause, Relais noch aus
#define SM_UNTERWEGS 4 //Relais ein
#define SM_NACHHAUSE 5 //Endschalter, Relais aus
const char VER1[17] = "R2D2 V18";
const char VER2[17] = "2025-05-24";
const char ZEILELEER[17] = " ";
int iEinstAbstandDaheim; //Abstand, unter dem zu Hause erkannt wird
int iEinstDaheimSensor; //Sekunden, um festzustellen, dass er wieder im Haus ist
int iEinstVerlHaus; //Sekunden, um festzustellen, dass er das Haus verlassen hat
int iEinstDraussen; //Minuten, bis wieder zurückschalten 35x60
boolean bDaheimSensor = false; //direkter Sensoreingang ohne Prüfung
unsigned long previousMillis = 0; // Store old millis
boolean bHalbSekTakt = false;
boolean bHalbSekPuls = false;
boolean bSekTakt = false;
boolean bSekPuls = false;
boolean bRelaisEin = true;
boolean bWarteBisRelaisAus = false;
boolean bEndsch = false;
boolean bDaheimGeprueft = false;
boolean bNachHauseES = false; //nach Hause wegen Endschalter
boolean bEinstellungMinus = false;
boolean bLcdBacklight = true;
int iAnzeigeMode = 0; //0: Standard SM-Status, 1: Anzeige Eingänge 2: Einstellung Abstand 3: Einstellung Sek. zu Hause geprüft 4: Einstellung Sek. Verlassen bis Relais ein Abstand 5: Einstellung Minuten draussen bis Relais aus
int iStateMach = SM_UNDEF; //0: undefined 1: zu Hause 2: zu Hause gep
int* piEinstellWert;
int iHalbSekZaehler = 0;
int iVerlasseHausZeit = 0; //runterzählen, bis Relais ein
int iDraussenZeit = 0; //runterzählen, bis Relais aus
int iDraussenZeitSav = 0; //runterzählen, bis Relais aus, Spezial, zählt immer (auch bei Relais man. aus)
int iDaheimZeit = 0; //runterzählen, bis daheim festgestellt
int duration_ms = 50;
Debouncer debouncer(ENDSCH_PIN, duration_ms);
OneButtonTiny TastOB = OneButtonTiny(
TAST_PIN, // Input pin for the button
true, // Button is active LOW
true // Enable internal pull-up resistor
);
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
void setup() {
Wire.begin();
Wire.setClock(400000L);
oled.begin(&Adafruit128x64, I2C_ADDRESS);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(TRIGGER_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(RELAIS_PIN, OUTPUT); //Relais
pinMode(LED_ZUHAUSEPIN, OUTPUT); //Relais
pinMode(TAST_PIN, INPUT_PULLUP);
pinMode(ENDSCH_PIN, INPUT_PULLUP);
Serial.begin(9600);
lcd.init();
lcd.backlight();
delay(250);
lcd.noBacklight();
delay(100);
lcd.backlight();
delay(100);
// Single Click event attachment
TastOB.attachClick(handleClickTast);
// Long Press Start event attachment
TastOB.attachLongPressStart(handleLongPressedTast);
// Double Click event attachment with lambda
TastOB.attachDoubleClick(handleDoubleClickTast);
checkEEpromStandardVal();
readEinstellwerte();
delay(100);
AnzeigeStart();
delay(500);
Serial.println ("Anzeigestart Ende");
// lcd.noBacklight();
// bLcdBacklight = false;
}
void AnzeigeStart() {
Serial.println("Anzeigestart ");
// Serial.println(iAnzeigeMode);
char myDecString1[17];
char myDecString2[17];
sprintf(myDecString2, " ");
strcpy(myDecString2,ZEILELEER);
sprintf(myDecString1, VER1 );
sprintf(myDecString2, VER2 );
Anzeige(myDecString1,0);
Anzeige(myDecString2,1);
}
void loop() {
// oled.setFont(System5x7); // Auswahl der Schriftart
oled.setFont(fixed_bold10x15); // Auswahl der Schriftart
// oled.setFont(fixednums15x31); // Auswahl der Schriftart
oled.clear(); //Löschen der aktuellen Displayanzeige
oled.println("123 Liter"); //Text in der ersten Zeile. "Println" sorgt dabei für einen Zeilensprung.
// oled.print("45"); // Text in der zweiten Zeile. Da es keine dritte Zeile gibt, wird hier kein Zeilenumsprung benötigt.
// oled.set1X();
oled.set2X();
int iAbstand;
bool bTaster;
char myDecString1[17];
char myDecString2[17];
debouncer.update(); // you should update debouncer first
if (debouncer.edge()) { // if edge is detected
if (debouncer.rising()) // if edge is rising
bEndsch = false;
Serial.println("rise");
if (debouncer.falling()) // if edge is falling
bEndsch = true;
Serial.println("fall");
}
TastOB.tick();
sekundenZaehlen();
bTaster = !digitalRead(TAST_PIN);
digitalWrite(LED_BUILTIN, bTaster);
if (bEndsch && (iDraussenZeit>1)) { iDraussenZeit = 1; bNachHauseES = true; bRelaisEin = false; }
if (bHalbSekPuls) {
iAbstand = AbstandMessen();
DisplayAnzeige(iAbstand, bTaster);
}
bDaheimSensor = (iAbstand < iEinstAbstandDaheim);
if (bDaheimSensor and !bDaheimGeprueft and (iDaheimZeit == 0) ) iDaheimZeit = iEinstDaheimSensor; //LedOnOffBlink(2,LED_ZUHAUSEPIN);
if (!bDaheimSensor) iDaheimZeit = 0;
StateMachine();
// bRelaisEin = iStateMach == SM_UNDEF || iStateMach ==SM_UNTERWEGS;
digitalWrite(RELAIS_PIN, !bRelaisEin);
if (bDaheimSensor) {
if (iStateMach == SM_ZUHAUSEGEPRUEFT) LedOnOffBlink(1,LED_ZUHAUSEPIN);
else LedOnOffBlink(2,LED_ZUHAUSEPIN);
}
else LedOnOffBlink(0,LED_ZUHAUSEPIN);
//delay(1000);
bHalbSekPuls = false;
bSekPuls = false;
sprintf(myDecString2, "Starttext");
/*
if (digitalRead(5)) digitalWrite(4, LOW);
else digitalWrite(4, HIGH);
//digitalWrite(4, HIGH);
//delay(500);
//digitalWrite(4, LOW);
//delay(500);
iAbstand = AbstandMessen();
lcd.setCursor(0, 0);
lcd.print("Sepp");
sprintf(myDecString2, "Abstand:%d ", iAbstand);
lcd.setCursor(1, 1);
lcd.print(myDecString2);
// lcd.print("das funzt ja");
delay(500);
*/
}
//============== StateMachine
void StateMachine(){
switch (iStateMach){
case SM_UNDEF: {
if (bDaheimGeprueft){
iStateMach = SM_ZUHAUSEGEPRUEFT;
bRelaisEin = false;
}
break;
}
case SM_NACHHAUSE:{
if (bRelaisEin) { //manuell eingeschaltet->zurück zu unterwegs
iStateMach = SM_UNTERWEGS;
iDraussenZeit =iDraussenZeitSav;
bEndsch = false;
bNachHauseES = false;
}
if (bDaheimGeprueft){
iStateMach = SM_ZUHAUSEGEPRUEFT;
bRelaisEin = false;
}
break;
}
case SM_ZUHAUSEGEPRUEFT: {
if (!bDaheimSensor) {
iVerlasseHausZeit = iEinstVerlHaus;
iStateMach = SM_VERLASSEN;
}
break;
}
case SM_VERLASSEN: {
if (iVerlasseHausZeit==0) {
iDraussenZeit = iEinstDraussen * 60;
iDraussenZeitSav = iDraussenZeit;
iStateMach = SM_UNTERWEGS;
bNachHauseES = false;
bRelaisEin = true;
}
break;
}
case SM_UNTERWEGS: {
if (iDraussenZeit==0) {
iStateMach = SM_NACHHAUSE;
bRelaisEin = false;
}
break;
}
}
}
//============== Taster
static void handleClickTast() {
Serial.println("Clicked!");
int iVal;
//readEEPROM();
//return;
if (iAnzeigeMode < 2) {
bLcdBacklight = !bLcdBacklight;
if (bLcdBacklight) lcd.backlight();
else lcd.noBacklight();
}
else {
iVal = *piEinstellWert;
if (bEinstellungMinus) iVal--;
else iVal++;
*piEinstellWert = iVal;
}
}
static void handleLongPressedTast() {
iAnzeigeMode++;
iAnzeigeMode %= 6;
writeEEpromEinstellWerte();
Serial.print("Long Pressed! Anzeigemode: ");
Serial.println(iAnzeigeMode);
// writeEEPROM(2,234);
}
static void handleDoubleClickTast() { //Doppelklick: Einstellungen +- umschalten oder im Standard-Anzeige-Modus: Relais ein/ausschalten
Serial.println("Doubleclicked");
Serial.println("AnzeigeMode: " + String(iAnzeigeMode));
Serial.println("bRelaisEin: " + String(bRelaisEin));
if (iAnzeigeMode < 2 ) bRelaisEin = !bRelaisEin;
// Serial.println("bEinstellungMinus VOR : " + String(bEinstellungMinus));
else bEinstellungMinus = !bEinstellungMinus;
Serial.println("bRelaisEin: " + String(bRelaisEin));
// Serial.println("bEinstellungMinus NACH: " + String(bEinstellungMinus));
}
//============== Anzeige
int EinstellungAendern(int* piWert, int iMin, int iMax, char strText[17]){
char myDecString[17];
piEinstellWert = piWert;
Serial.println("EinstellungAendern: EinstellWert: " + String(*piEinstellWert));
sprintf(myDecString, "Einstellung ");
if (bEinstellungMinus) strcat(myDecString,"- ");
else strcat(myDecString,"+ ");
Anzeige(myDecString,0);
// strcpy(myDecString,strText)
sprintf(myDecString, strText, *piWert);
Anzeige(myDecString,1);
Serial.println("EinstellungAendern Ende: iEinstellWert: " + String(*piEinstellWert));
return *piEinstellWert;
}
void DisplayAnzeige(int iAbstand, bool bTaster) {
// Serial.print("iAnzeigeMode: ");
// Serial.println(iAnzeigeMode);
switch (iAnzeigeMode) {
case 0: StandardAnzeige(); break;
case 1: EingangswerteAnzeigen(iAbstand, bTaster); break;
case 2: {
iEinstAbstandDaheim=EinstellungAendern(&iEinstAbstandDaheim,1,70,"Abstand(cm): %3d"); break;
// Serial.println("Abstandseinst Neu: " + String(iEinstAbstandDaheim)); break;
}
case 3: {
iEinstDaheimSensor=EinstellungAendern(&iEinstDaheimSensor,1,70,"Zeitdaheim(s):%2d"); break;
// Serial.println("Zeit daheim Neu: " + String(iEinstDaheimSensor)); break;
}
case 4: {
iEinstVerlHaus=EinstellungAendern(&iEinstVerlHaus,1,70,"Zeit Verl.(s):%2d"); break;
// Serial.println("Verlassen Haus Neu: " + String(iEinstVerlHaus)); break;
}
case 5: {
iEinstDraussen=EinstellungAendern(&iEinstDraussen,1,70,"Zeit draus(m):%2d");
// Serial.println("Draussen Neu: " + String(iEinstDraussen)); break;
}
}
}
void StandardAnzeige() {
char myDecString1[17];
char myDecString2[17];
sprintf(myDecString2, " ");
strcpy(myDecString2,ZEILELEER);
sprintf(myDecString1, "bNachHauseES: %2d",bNachHauseES );
// Serial.println(myDecString1);
// Serial.println("bNachHauseES") + String(bNachHauseES);
switch (iStateMach) {
case SM_UNDEF:
sprintf(myDecString1, "undefiniert ");
// sprintf(myDecString2, "Relais: %2d ", bRelaisEin);
sprintf(myDecString2, "Rel:%2d H:%2d ", bRelaisEin,iDaheimZeit);
break;
case SM_ZUHAUSE: {
if (bNachHauseES) sprintf(myDecString1, "zuxHause ES R:%d ",bRelaisEin);
else sprintf(myDecString1, "zu Hause R:%d ",bRelaisEin);
break;
}
case SM_ZUHAUSEGEPRUEFT: {
if (bNachHauseES) sprintf(myDecString1, "zu Hause ES R:%1d ",bRelaisEin);
else sprintf(myDecString1, "zu Hause R:%1d ",bRelaisEin);
break;
}
case SM_VERLASSEN: sprintf(myDecString1, "verlassen R:%d",bRelaisEin); sprintf(myDecString2, "Zeit bis R: %2d s", iVerlasseHausZeit); break;
case SM_UNTERWEGS: sprintf(myDecString1, "unterwegs R:%d",bRelaisEin); sprintf(myDecString2, "Restzeit: %2dm%2ds",iDraussenZeit / 60, iDraussenZeit % 60); break;
case SM_NACHHAUSE: {
if (bNachHauseES) sprintf(myDecString1, "nach HauseES R:%d",bRelaisEin);
else sprintf(myDecString1, "nach Hause R:%d",bRelaisEin);
sprintf(myDecString2, "Sensor: %1d H:%2ds", bDaheimSensor,iDaheimZeit);break;
}
// if (bDaheimSensor) strcat(myDecString, " *"); else strcat(myDecString, " ");
}
Anzeige(myDecString1,0);
Anzeige(myDecString2,1);
}
void EingangswerteAnzeigen(int iAbstand, bool bTaster) {
char myDecString[17];
sprintf(myDecString, "Abst: %4d cm", iAbstand);
if (bDaheimSensor) strcat(myDecString, " *"); else strcat(myDecString, " ");
// sprintf(myDecString, "Taster: %d %5d cm", bTaster, iAbstand);
Anzeige(myDecString,0);
sprintf(myDecString, "Rel: %d Ends: %d", bRelaisEin, bEndsch);
Anzeige(myDecString,1);
}
void Anzeige(char caText[17], int iZeile){
lcd.setCursor(0, iZeile);
lcd.print(caText);
}
// Zeit runterzählen mit entsprechenden Aktionen bei Zeit = 0
//-----------------------------------------------------------
// iVerlasseHausZeit: Relais ein
// iDraussenZeit: Relais aus
// iDaheimZeit: bDaheimGeprueft = true
//-----------------------------------------------------------
void sekundenZaehlen() {
unsigned long currentMillis = millis();
if ((currentMillis - previousMillis) > 500) {
previousMillis = currentMillis;
// DigiKeyboard.println("Halbe Sekunde");
bHalbSekPuls = true;
bHalbSekTakt = !bHalbSekTakt;
iHalbSekZaehler += 1;
if (iHalbSekZaehler == 2) {
iHalbSekZaehler = 0;
bSekTakt = !bSekTakt;
bSekPuls = true;
} else {
return;
}
//Sekunden runterzählen
// DigiKeyboard.println("Sekunde");
if (iVerlasseHausZeit > 0) {
iVerlasseHausZeit -= 1;
// DigiKeyboard.println("iVerlasseHausZeit");
// DigiKeyboard.println(iVerlasseHausZeit);
if (iVerlasseHausZeit == 0) {
// DigiKeyboard.println("E");
// bRelaisEin = true;
iDraussenZeit = iEinstDraussen;
bDaheimGeprueft = false;
}
}
if (iDraussenZeit > 0) {
iDraussenZeit -= 1;
// DigiKeyboard.println("draussYeit");
// DigiKeyboard.println(iDraussenZeit);
if (iDraussenZeit == 0) {
// DigiKeyboard.println("A");
// bRelaisEin = false;
bWarteBisRelaisAus = true;
}
}
if (iDraussenZeitSav > 0) {
iDraussenZeitSav -= 1;
// DigiKeyboard.println("draussZeitSav");
// DigiKeyboard.println(iDraussenZeitSav);
}
if (iDaheimZeit > 0) {
// toggleLed = ! toggleLed;
// DigiKeyboard.println("dahYeit");
// DigiKeyboard.println(iDaheimZeit);
iDaheimZeit -= 1;
if (iDaheimZeit == 0) {
bDaheimGeprueft = true;
// DigiKeyboard.println("G");
}
}
}
}
//============== Abstand
int AbstandMessen() {
int iDistance;
long duration;
digitalWrite(TRIGGER_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIGGER_PIN, HIGH); //Ping
delayMicroseconds(10); //Need to futs with this value now to make it work better on digispark
digitalWrite(TRIGGER_PIN, LOW);
// iDistance = pulseIn(ECHO_PIN, HIGH,10000L) / 58;
duration = pulseIn(ECHO_PIN, HIGH,10000L);
iDistance = duration * 0.034 / 2;
if (iDistance < 1) iDistance = 99;
// Serial.println(duration);
// iDistance = duration * 0.034 / 2;
/// iDistance = microsecondsToCentimeters(duration);
// duration = pulseIn(ECHO_PIN, HIGH);
// iDistance = microsecondsToCentimeters(duration);
return iDistance;
}
//============== EEPROM
void readEinstellwerte() {
int iVal;
int iAdd = EEPROMSTART;
iEinstAbstandDaheim = EEPROM.read(iAdd++);
iEinstDaheimSensor = EEPROM.read(iAdd++);
iEinstVerlHaus = EEPROM.read(iAdd++);
iEinstDraussen = EEPROM.read(iAdd++);
Serial.println("readEinstellwerte");
}
void writeEEpromEinstellWerte() {
int iAdd = EEPROMSTART;
Serial.println("writeEEpromEinstellWerte: Adresse: " + String(iAdd) + " Abstanddaheim: " + String(iEinstAbstandDaheim));
writeEEPROM(iAdd, iEinstAbstandDaheim);
iAdd++;
writeEEPROM(iAdd, iEinstDaheimSensor);
iAdd++;
writeEEPROM(iAdd, iEinstVerlHaus);
iAdd++;
writeEEPROM(iAdd, iEinstDraussen);
}
void checkEEpromStandardVal(){ //Standardwerte Schreiben bei bisher nicht benutztem EEPROM oder Wechsel der Speicheradresse
int val;
int iAdd = EEPROMSTART;
setEpromStandardVal(iAdd, 12); //Abstand daheim 12
iAdd++;
setEpromStandardVal(iAdd, 6); //Zeit daheim geprüft 10
iAdd++;
setEpromStandardVal(iAdd, 10); //Zeit verlassen bis Relais ein 20
iAdd++;
setEpromStandardVal(iAdd, 5); //Zeit draussen (Minuten) 35
}
void setEpromStandardVal(int iAddress, int iStandardVal){
int iVal = EEPROM.read(iAddress);
if ((iVal < 1) || (iVal >250)) writeEEPROM(iAddress, iStandardVal);
}
void writeEEPROM(int iAddress, int iVal) {
EEPROM.update(iAddress, iVal);
}
//============== LED
void LedOnOffBlink(int iMode, int iPin) { //0: aus 1: ein 2: langsam 3: schnell
if (iMode == 0) {
digitalWrite(iPin, LOW);
}
else if (iMode == 1) {
digitalWrite(iPin, HIGH);
}
else if (iMode == 2) {
digitalWrite(iPin, bSekTakt);
}
else if (iMode == 3) {
digitalWrite(iPin, bHalbSekTakt);
}
}