#include #include #include #include const char* ssid = "DEIN_WLAN_NAME"; const char* password = "DEIN_PASSWORT"; // NTP-Server & Zeitzone für Deutschland const char* ntpServer = "pool.ntp.org"; const char* tzInfo = "CET-1CEST,M3.5.0,M10.5.0/3"; FastAccelStepperEngine engine = FastAccelStepperEngine(); FastAccelStepper *stepper = NULL; // Pins #define dirPinStepper D5 #define enablePinStepper D6 #define stepPinStepper D9 #define buttonPlus D8 #define buttonMinus D7 #define buttonStop D10 #define senTemp // ################ Variabeln ################ const uint32_t MAX_SPEED_HZ = 106000; const int32_t COLDTEMP_C = 50; bool clickPlus = 0; bool clickMinus = 0; bool clickStop = 0; bool holdPlus = 0; bool holdMinus = 0; bool holdStop = 0; int32_t lastControl = 0; uint32_t lastPlotTime = 0; uint32_t lastDispTime = 0; bool autoStop = false; bool bSettings = false; uint32_t curTemp = 0; struct tm timeinfo; struct settings{ int settingIdx = 0; int startHour = 0; int startMin = 0; int endHour = 0; int endMin = 0; bool timerEnabled = false; bool editMaxTemp = false; int maxTemp = 70; //in °C bool editAcc = false; int32_t accelRate = 2000; } enum TimeEditStep { EDIT_NONE = 0, // Normales Menü (Navigation) EDIT_START_HOUR, // 1. Start-Stunde einstellen (0-23) EDIT_START_MIN, // 2. Start-Minute einstellen (0-59) EDIT_END_HOUR, // 3. End-Stunde einstellen (0-23) EDIT_END_MIN // 4. End-Minute einstellen (0-59) }; TimeEditStep timeEditStep = EDIT_NONE; // Überladung des Präfix-Operators (++timeEditStep) TimeEditStep& operator++(TimeEditStep& step) { step = (step == EDIT_END_MIN) ? EDIT_NONE : static_cast(step + 1); return step; } void initWiFiAndTime() { WiFi.begin(ssid, password); // Zeitzone und NTP-Server setzen configTzTime(tzInfo, ntpServer); // Warten auf Verbindung (optional mit Timeout) int retries = 0; while (WiFi.status() != WL_CONNECTED && retries < 20) { delay(500); retries++; } } //####################################################### //####################### Setup ######################### //####################################################### void setup() { Serial.begin(115200); delay(1000); // Zeit für USB-CDC-Verbindung geben engine.init(); stepper = engine.stepperConnectToPin(stepPinStepper); if (stepper) { stepper->setDirectionPin(dirPinStepper); stepper->setEnablePin(enablePinStepper, true); stepper->setAutoEnable(true); stepper->setSpeedInHz(MAX_SPEED_HZ); } // Pin Config pinMode(buttonPlus, INPUT_PULLUP); pinMode(buttonMinus, INPUT_PULLUP); pinMode(buttonStop, INPUT_PULLUP); initWiFiAndTime() // Start Animation } bool getCurrentTime(int &hour, int &minute, int &second) { if (!getLocalTime(&timeinfo, 50)) { // 50ms Timeout return false; // Noch keine NTP-Zeit synchronisiert } hour = timeinfo.tm_hour; minute = timeinfo.tm_min; second = timeinfo.tm_sec; return true; } // Rückgabe von Flanken (Klick) und Pegel (Halten) void getInputs(bool &clickPlus, bool &clickMinus, bool &clickStop, bool &holdPlus, bool &holdMinus, bool &holdStop) { // Physische Pegel einlesen (Active-Low) bool curPlus = (digitalRead(buttonPlus) == LOW); bool curMinus = (digitalRead(buttonMinus) == LOW); bool curStop = (digitalRead(buttonStop) == LOW); // Speicher für vorherigen Durchlauf static bool lastPlus = false; static bool lastMinus = false; static bool lastStop = false; // Nur bei Veränderung true (Sperre) clickPlus = (curPlus && !lastPlus); clickMinus = (curMinus && !lastMinus); clickStop = (curStop && !lastStop); // Dauerzustand (für Stepper-Handbetrieb) holdPlus = curPlus; holdMinus = curMinus; holdStop = curStop; // Zustand für nächsten Durchlauf merken lastPlus = curPlus; lastMinus = curMinus; lastStop = curStop; } // Liefert die Aktuelle Temperatur in °C int getTempInC(uint8_t senTemp){ int milliVolt = analogReadMilliVolts(senTemp); return ((milliVolt - 500)/10) } // Liefert true wenn der Timer aktuell Active ist und false wenn nicht aktiv bool isTimerActiveNow(int curH, int curM, int startH, int startM, int endH, int endM) { int current = curH * 60 + curM; int start = startH * 60 + startM; int end = endH * 60 + endM; // Fall A: Zeitfenster am selben Tag (z. B. 08:00 bis 16:00) if (start <= end) { return (current >= start && current < end); } // Fall B: Zeitfenster über Mitternacht (z. B. 22:00 bis 04:00) else { return (current >= start || current < end); } } // Die Funktion behandelt das gesammte Einstellungs menu void setting(){ //######### Timer Edit ######### if (timeEditStep != EDIT_NONE) { //PLUS GEDRÜCKT: Wert erhöhen if (clickPlus || holdPlus) { switch (timeEditStep) { case EDIT_START_HOUR: settings.startHour = (settings.startHour + 1) % 24; break; case EDIT_START_MIN: settings.startMin = (settings.startMin + 5) % 60; break; case EDIT_END_HOUR: settings.endHour = (settings.endHour + 1) % 24; break; case EDIT_END_MIN: settings.endMin = (settings.endMin + 5) % 60; break; default: break; } } // MINUS GEDRÜCKT: Wert verringern if (clickMinus) { switch (timeEditStep) { case EDIT_START_HOUR: settings.startHour = (settings.startHour == 0) ? 23 : settings.startHour - 1; break; case EDIT_START_MIN: settings.startMin = (settings.startMin == 0) ? 59 : settings.startMin -5; break; case EDIT_END_HOUR: settings.endHour = (settings.endHour == 0) ? 23 : settings.endHour - 1; break; case EDIT_END_MIN: settings.endMin = (settings.endMin == 0) ? 59 : settings.endMin - 5; break; default: break; } } // STOP GEDRÜCKT: Zum nächsten Wert springen if (clickStop) { ++timeEditStep; } return; // Wichtig: Rest der Funktion überspringen, solange editiert wird! } //######### Max Temp Edit ######### if(settings.editMaxTemp){ if(clickPlus){ settings.maxTemp = (settings.maxTemp + 5) % 140; } if(clickPlus){ settings.maxTemp = (settings.maxTemp == 0) ? -20 : settings.maxTemp - 5; } if(clickStop){ settings.editMaxTemp = false; } return; // Wichtig: Rest der Funktion überspringen, solange editiert wird! } //######### Beschleunigung Edit ######### if(settings.editAcc){ if(clickPlus){ settings.accelRate = (settings.accelRate + 1000) % 20000; } if(clickPlus){ settings.accelRate = (settings.accelRate == 0) ? 0 : settings.accelRate - 1000; } if(clickStop){ settings.editAcc = false; } return; // Wichtig: Rest der Funktion überspringen, solange editiert wird! } //######### Menü ######### // Menü bewegen if(clickPlus){ if(settings.settingIdx <= 2){ settings.settingIdx++; } } if(clickMinus){ if(settings.settingIdx >=1){ settings.settingIdx--; } } // Stop zum bestätigen if (clickStop) { if (settings.settingIdx == 0) { // Zeitschaltuhr-Einstellung starten timeEditStep = EDIT_START_HOUR; } else if (settings.settingIdx == 1) { // Zeitschaltuhr ON/OFF settings.timerEnabled = !settings.timerEnabled; }else if (settings.settingIdx == 2) { // Temperatur Grenze einstellen settings.editMaxTemp = true; }else if (settings.settingIdx == 3) { // Beschleunigung settings.editAcc = true; }else if (settings.settingIdx == 4) { // Menü verlassen bSettings = false; } } } void updateDisplay(){ if (millis() - lastDispTime >= 50) { lastDispTime = millis(); int32_t currentSpeed = stepper->getCurrentSpeedInUs() == 0 ? 0 : (1000000 / stepper->getCurrentSpeedInUs()); // einstellungs fenster if(bSettings){ // Mein Menu // Uhrzeit // Cursor mit settingIdx //EDIT_START_HOUR //EDIT_START_MIN //EDIT_END_HOUR //EDIT_END_MIN } } } void printStatus(FastAccelStepper *stepper, int speed, int clickPlus, int clickMinus){ // Plotter-Ausgabe alle 50 ms (überflutet den Bus nicht) if (millis() - lastPlotTime >= 50) { lastPlotTime = millis(); int32_t currentSpeed = stepper->getCurrentSpeedInUs() == 0 ? 0 : (1000000 / stepper->getCurrentSpeedInUs()); // Format kompatibel für den Arduino Serial Plotter Serial.print("Current_Speed_U/Min:"); Serial.print((60*currentSpeed)/3200); Serial.print(" Accel_Control:"); Serial.print(speed); Serial.print(" Btn_Up:"); Serial.print(clickPlus ? 5000 : 0); Serial.print(" Btn_Down:"); Serial.print(clickMinus ? -5000 : 0); Serial.println(); } } //############################################################# //#################### MAIN ################################### //############################################################# void loop() { int32_t speed = 0; if (!stepper) { Serial.println("[HALT] Kein Stepper-Objekt aktiv."); delay(1000); return; } // Inputs getInputs(clickPlus, clickMinus, clickStop, holdPlus, holdMinus, holdStop); curTemp = getTempInC(senTemp); // zeitschaltuhr prüfen int curH, curM, curS; if (settings.timerEnabled && getCurrentTime(curH, curM, curS)) { bool shouldRun = isTimerActiveNow(curH, curM, settings.startHour, settings.startMin, settings.endHour, settings.endMin); } if(curTemp >= settings.maxTemp || !shouldRun){ autoStop = true; }else if(curTemp <= COLDTEMP_C){ autoStop = false; } // Main Control if(bSettings){ settings() }else if(clickStop || autoStop){ stepper->stopMove(); speed = 0; if(clickStop && clickPlus){ bSettings = true; } }else if(holdPlus){ speed = accelRate; }else if(holdMinus){ speed = -accelRate; }else{ speed = 0; } // Action if (speed != lastSpeed) { stepper->moveByAcceleration(speed, true); lastSpeed = speed; } updateDisplay() // Status ausgabe auf Terminal printStatus(stepper, speed, clickPlus, clickMinus); } }