Stepper Probleme mit der AccelStepper Bibliothek

Hast recht AccelStepper schwirrt noch immer rum im Kopf.

Danke für deine Hinweise, soweit bin ich noch nicht vorgedrungen, sind ja auch erst 2 Tage der Kenntnis über MobaTools. Brauche noch ein bissel.
Nur, dass die Zeit jetzt drängt, die Tomatenpflanzen sind schon drin. Hier im Erzgebirge ist alles etwas später dran, wohne kurz vor dem Auersberg, Gartengrundstück hat eine Höhe von 660 m üNN. Im Freien wachsen die Dinger nicht!

Spaß beiseite, deine Tipps sind Gold wert. Habs gleich ausprobiert und bin begeistert.

DANKE!!!!
Das Ding flitzt los wie Bolle!
Und das mit den paar Zeilen. Kriegst 'n Orden!

Kann es sein, dass ich im setup() den Stepper gar nicht mehr aktivieren muss?

// Motor aktivieren
//digitalWrite(ENABLE_PIN, LOW);

Doch noch eine Frage:
Woran liegt es (an mir??), dass beim Programmstart der Stepper erst einmal mehrere hundert steps in Richtung des Endschalters fährt, anhält und danach weiterfährt und ordentlich die restliche Arbeit macht?
Ich kann das aus dem Code nicht nachvollziehen.

Habe ihn auch ein bisschen kommentiert:

#include <MobaTools.h>

// Motor Pin-Definitionen
#define STEP_PIN 9
#define DIRECTION_PIN 10
#define ENABLE_PIN 8
// Endschalter Pin-Definitionen
#define Auf 6
#define Zu 7

MoToStepper myStepper(400, STEPDIR);  // 400 Schritte pro Umdrehung

// Funktion für das Schließen der Tür
void tuerSchliessen() {
  myStepper.doSteps(-20000);  // so viele Schritte, dass der Endstop sicher erreicht wird.
  while (digitalRead(Zu) == HIGH)
    ;                      // Warten bis Endstop anspricht
  myStepper.doSteps(200);  // 100 Schritte zurück
  while (myStepper.moving())
    ;                   // warten bis Bewegungsende
  myStepper.setZero();  // Referenzpunkt setzen
  delay(5000);          // Warte 5 Sekunden
}

void setup() {
  Serial.begin(9600);  // Starte die serielle Kommunikation
  // Initialisiere die Pins
  pinMode(STEP_PIN, OUTPUT);
  pinMode(DIRECTION_PIN, OUTPUT);
  pinMode(ENABLE_PIN, OUTPUT);
  pinMode(Auf, INPUT_PULLUP);
  pinMode(Zu, INPUT_PULLUP);

  // Initialisiere den Stepper
  myStepper.attach(STEP_PIN, DIRECTION_PIN);
  myStepper.setSpeed(4000);   // 5000 Schritte pro Sekunde
  myStepper.setRampLen(100);  // Rampenlänge auf 10 Schritte setzen

  // Motor aktivieren kann wohl weg???
  //digitalWrite(ENABLE_PIN, LOW);

  // Führe die Türschließroutine einmal aus
  tuerSchliessen();
}
void loop() {
  // Hier kommt der restliche Code
}

Hängt von deiner Schaltung, und dem verwendeten Treiber ab. Wenn Du einen Pin auf OUTPUT setzt, ist der normalerweise erstmal LOW. Von daher ändert sich nichts, wenn Du ihn nochmal explizit auf LOW setzt.
Die Frage ist, ob Du den Motor ganz abschalten willst, wenn sich die Tür nicht bewegt. Stepper brauchen ja auch im Stillstand ordentlich Strom. Das könntest Du dann die MobaTools machen lassen. Allerdings hat ein Stepper ohne Strom kein Haltemoment ( nur ein kleines Rastmoment), und könnte leicht bewegt werden, womit er dann seine Position verliert. Ob das bei Dir ein mögliches Problem ist, kann ich nicht beurteilen. Wenn Du den Motor in Bewegungspausen abschalten willst, würde ich das Schließen der Tür immer als Referenzfahrt durchführen. Dann findet er da auf jeden Fall seine Position wieder und es können sich kleinere Positionsfehler nicht endlos aufaddieren.

Ich eigentlich auch nicht. Kann es sein dass dein Arduin erstmal noch einen zusätzlichen reset macht? ( wodurch auch immer ). Gib mal eine Initiierungsmeldung aus, dann sollte man sowas erkennen.
Ich hab' deinen Code mal in WOKWI simuliert, da passiert sowas nicht.

Ja, die Referenzfahrt steht bei den Temperaturabfragen immer voran, also erst TuerZU, dann von dort zur TuerAuf(int percenttemp) fahren, wobei hier percent für die prozentual ermittelten Öffnungsschritte steht.

// Berechnung der Schritte basierend auf dem Prozentsatz
  int stepsToOpen = map(percent, 0, 100, 0, 20000); // Anpassen für 100% Öffnung

  // Türöffnung abhängig von der Temperatur
  if (currentTemp < lowerThreshold) {
    // Tür zu
  myStepper.doSteps(-20000); // Zurück zur Startposition
  } else if (currentTemp < upperThreshold1) {
    // Türöffnung 20%
    stepper.moveTo(stepsToOpen * 0.2); // 20% der maximalen Schritte
  } else if (currentTemp < upperThreshold2) {
    // Türöffnung 50%
    myStepper.doSteps(stepsToOpen * 0.5); // 50% der maximalen Schritte
  } else {
    // Türöffnung 100%
  myStepper.doSteps(stepsToOpen); // 100% der maximalen Schritte
  }

wobei (stepsToOpen) dannn in den MobaTools in der Zeile

myStepper.doSteps(stepsToOpen); steht.

Mal sehen, ob es funktioniert.

Vermutlich nicht :wink:

So kannst Du nicht zur Startposition fahren. Die Zahl der Schritte dorthin hängt ja davon ab, wo Du gerade bist. Du musst deine 'tuerSchliessen' Funktion aufrufen.

Das muss alles mit moveTo passieren. Und wo wird 'stepsToOpen' definiert?

Ja, ging wohl zu schnell!
Ich schrieb:
Ja, die Referenzfahrt steht bei den Temperaturabfragen immer voran, also erst TuerZU,

und so soll es sein.
Den vor. Code habe ich schnell nur zur Erklärung aus den "alten" AccesLibary.h Sketch übertragen, muss ihn nun noch richtig anpassen.

Habe mit Serial.Print bissel gesucht, den Fehler aber nicht gefunden.
Werde den Arduino Mega entweder resetten, hat viel mitmachen müssen, oder einen anderen einbauen.

Eigentlich, aber nur eigentlich bin ich jetzt fertig mit dem Projekt. Alles im Gehäuse eingebaut und funktioniert ... bis auf die Funktion

/ Funktion TuerZu(temp)
void tuerZu(float tempnow) {
...

  • siehe Sketch -

in Verbindung mit einer Logik, die ermitteln soll, ob die Temperaturänderung signifikant ist, um zu verhindern, dass eine Tür nicht immer bewegt wird, wenn die Temperatur gleich bleibt.
Also, die Schleife wird durchlaufen und eine Aktion soll nur dann ausgeführt werden, wenn sich die Temperaturwerte geändert haben.
Alles schon mal gehabt, nur, dass hier die Temperaturänderungen einen Bereich umfassen und einer zusätzlichen Hysterese unterliegen

Bereich1: tempnow < 18 - 0.8
Bereich2: 18 <= tempnow < 21 - 0.8
Bereich3: 21 <= tempnow < 24 - 0.8
Bereich4: > 24 + 0.8
Dafür habe ich eine globale Variable float letzteTemp = -1; (liegt im Sommer außerhalb jeglichen Bereichs), um den letzten Temperaturwert zu speichern.

Am Ende des Programms prüfe ich, ob sich die Temperatur geändert hat:
if (tempnow >= 21 && tempnow < 24 - hysteresewert) {
tuerZu(tempnow);
letzteTemp = tempnow;

Die Abfrage sollte logisch sein, mittels switch Funktion lasse ich die Schleife hinter mir, wenn der Wert gleich ist und übergebe 0.

Denoch wird der Türstatus städig neu abgefragt und sie bewegt sich laufend hin und her.

Aus C++ weiß ich, dass jede if - oder else -Anweisung innerhalb einer Funktion stehen sollte.

Deshalb steht die breakfunktion ab Zeile 71 auch in der Funktion drin. Setze ich sie in einem anderen Programmteil außerhalb der Funktion, dann funktioniert sie. Was ist für Arduino richtig?

Hier der Code: (ohne Fehlermeldung)

#include <MobaTools.h>
#include <LiquidCrystal_I2C.h>
// LCD-Objekt erstellen
LiquidCrystal_I2C lcd(0x26, 20, 4);
LiquidCrystal_I2C lcd1(0x27, 20, 4);
#include <OneWire.h>
#include <DallasTemperature.h>
//#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;

// PINBELEGUNG Regensensor
const int rainDigitalPin = 13;
const int rainAnalogPin = A0;
const int rainSensorPowerPin = A1;
// PINBELEGUNG Temperatursensor
// OneWire oneWire(TEMP_PIN);
#define ONE_WIRE_BUS 46
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// PINBELEGUNG Stepper-Motor NEMA 17
const int enablePin = 8;  // blau
const int stepPin = 9;    // schwarz
const int dirPin = 10;    // rot
// PINBELEGUNG für die Endschalter der Linearführung
#define Auf 6  // gelb
#define Zu 7   // rot
// Definiere eine Variable für den Temperatursensor
float tempStatus;
float tempnow;
// Globale Variable, um den letzten Temperaturwert zu speichern
float letzteTemp = -1;
int a;
float hysteresewert = 0.8;

// PINBELEGUNG der Sensoren
#define RAIN_PIN A0  // Regensensor
#define TEMP_PIN A2  // Temperatursensor
// Anzahl der Sensoren und Pumpen
const int n = 6;
int sensorPins[n] = { A3, A4, A5, A6, A7, A8 };  // Sensoren Pins (analog)
int pumpPins[n] = { 30, 31, 32, 33, 34, 35 };    // Pumpen Pins (digital)
// Feuchtigkeitswerte (0 - 1023)
int moistureValues[n];
// Pumpenzustände (0 oder 1)
int pumpStates[n];
int schrittStatus;
int schrittstatusOld;


// Schwellwerte für Pumpensteuerung (in Prozent)
const int lowerThreshold = 55;
const int upperThreshold = 68;

// Start. und Endzeit festlegen
#define START_HOUR 8                  // Startstunde des Programms
#define END_HOUR 20                   // Endstunde des Programms
MoToStepper myStepper(300, STEPDIR);  // 300 Schritte pro Umdrehung

// Funktion tuerZu ohne Parameter
void tuerZu() {
  myStepper.doSteps(-20000);
  while (digitalRead(Zu) == HIGH)
    ;
  myStepper.doSteps(200);
  while (myStepper.moving())
    ;
  myStepper.setZero();
  delay(500);
}

  // Funktion tuerZu mit Temperaturparameter
  void tuerZu(float tempnow) {
    const int schritteProVoll = 13624;  // Anzahl der Schritte für eine volle Öffnung
    int schritte = 0;
    if (tempnow < 18 - 0.8) {
      tuerZu();
      schritte = schritteProVoll;
    } else if (tempnow >= 18 && tempnow < 21 - 0.8) {
      schritte = schritteProVoll * 3 / 4;
    } else if (tempnow >= 21 && tempnow < 24 - 0.8) {
      schritte = schritteProVoll / 2;
    } else if (tempnow >= 24) {
      schritte = schritteProVoll;
    }
    
    tuerZu();
    // Schritte, wenn nicht NULL
    if (schritte != 0) {
      myStepper.doSteps(schritte);
      while (myStepper.moving());  // Warten bis Bewegungsende
    }
    // Setzen Sie den Referenzpunkt, wenn die Tür vollständig geöffnet ist
    if (schritte == 0) {
      myStepper.setZero();
    }
  }

// Funktion zur Überprüfung der Temperatur und Steuerung der Tür
void pruefeTemperaturUndSteuerTuer(float tempnow) {
  if (letzteTemp == tempnow) {
    a = 0;
  } else {
    a = 1;
    letzteTemp = tempnow;
  }
  switch (a) {
    case 0:
      break;
    case 1:
      tuerZu(tempnow);
      break;
  }
}

  void setup() {
    Serial.begin(9600);
    lcd.init();       //Im Setup wird das erste  LCD gestartet
    lcd1.init();      //Im Setup wird das zweite LCD gestartet
    lcd.backlight();  //Hintergrundbeleuchtung vom LCD einschalten
    lcd1.backlight();
    pinMode(pumpPins[n], OUTPUT);
    pinMode(sensorPins[n], INPUT);
    //PINMODUS festlegen
    pinMode(8, OUTPUT);   //Enable           BLAU
    pinMode(9, OUTPUT);   //Puls             SCHWARZ
    pinMode(10, OUTPUT);  //Direction        ROT
    pinMode(enablePin, OUTPUT);

    // PINMODUS für Pullup festlegen
    pinMode(4, INPUT_PULLUP);  // Tür manuell auf
    pinMode(5, INPUT_PULLUP);  // Tür manuell zu
    pinMode(6, INPUT_PULLUP);  // Tür auf   GELB
    pinMode(7, INPUT_PULLUP);  // Tür zu    ROT
    // Pumpen Pins als Ausgänge definieren
    for (int i = 0; i < n; i++) {
      pinMode(pumpPins[i], OUTPUT);
      digitalWrite(pumpPins[i], HIGH);  // Pumpen ausschalten
      pumpStates[i] = 0;                // Pumpenzustände auf aus setzen
    }
    // PINMODUS für Regensensor festlegen
    //pinMode(rainDigitalPin, INPUT);
    pinMode(rainAnalogPin, INPUT);
    pinMode(rainSensorPowerPin, OUTPUT);
    // Initialisiere den Stepper
    myStepper.attach(stepPin, dirPin);
    myStepper.setSpeed(4000);   // 5000 Schritte pro Sekunde
    myStepper.setRampLen(100);  // Rampenlänge auf 10 Schritte setzen
    digitalWrite(enablePin, LOW);

    // Führe die Türschließroutine einmal aus
    tuerZu();

    Wire.begin();
    rtc.begin();
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

    // Abfrage, ob Zeitmodul gestartet ist
    if (!rtc.begin()) {
      lcd.print("RTC nicht gefunden!");
      while (1)
        ;
    }
    // Anzeige am Display zur Orientierung
    lcd.setCursor(0, 0);
    lcd.print("   Anfahren der");
    lcd.setCursor(0, 1);
    lcd.print("   Homeposition");
    lcd.setCursor(0, 3);
    lcd.print("   Bitte warten");
  }


  void loop() {
    //  ******* Zu Programmbeginn werden Uhrzeit, Datum und Status auf dem LCD Display angezeigt *******
    DateTime now = rtc.now();
    lcd.setCursor(0, 0);
    if (now.hour() < 10) lcd.print("0");
    lcd.print(now.hour(), DEC);
    if (now.hour() < 10) lcd.print("0");
    lcd.print(':');
    if (now.minute() < 10) lcd.print("0");
    lcd.print(now.minute(), DEC);
    if (now.minute() < 10) lcd.print("0");
    lcd.print("  ");
    if (now.day() < 10) lcd.print("0");
    lcd.print(now.day(), DEC);
    lcd.print('.');
    if (now.month() < 10) lcd.print("0");
    lcd.print(now.month(), DEC);
    lcd.print('.');
    lcd.print(now.year(), DEC);
    if (now.hour() >= START_HOUR && now.hour() < END_HOUR) {
      lcd.setCursor(0, 1);
      lcd.print("  Programm bereit!");
    } else {
      lcd.setCursor(0, 1);
      lcd.print("  Ausser der Zeit");
    }
    // ABFRAGE DES REGENSENSORS ANALOG
    digitalWrite(rainSensorPowerPin, HIGH);
    delay(500);
    int rainAnalog = analogRead(rainAnalogPin);
    Serial.print(rainAnalog);

    if (rainAnalog >= 900) {
      lcd.setCursor(0, 3);
      lcd.print("Kein Regen");
      Serial.println("kein Regen");
      delay(500);
    }
    if (900 > rainAnalog && rainAnalog >= 750) {
      lcd.setCursor(0, 3);
      lcd.print("leichter Regen");
      Serial.println("leichter Regen");
      delay(500);
    }
    if (rainAnalog < 750) {
      lcd.setCursor(0, 3);
      lcd.print("starker Regen");
      Serial.println("starker Regen");
      delay(500);
    }
    digitalWrite(rainSensorPowerPin, LOW);
    delay(1000);
    // TEMPERATURABFRAGE
    tempnow = sensors.getTempCByIndex(0);
    lcd.init();
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print("Die Innentemperatur");
    sensors.requestTemperatures();
    lcd.setCursor(0, 1);
    lcd.print("betr\xE1gt:    ");
    lcd.print(sensors.getTempCByIndex(0));
    lcd.print("\337C");
    delay(1000);
    // Sensorwerte lesen und auf LCD Display anzeigen
    lcd1.setCursor(0, 0);                // Cursor auf erste Zeile setzen
    lcd1.print("Feuchte (%) - Pumpen");  // Überschrift schreiben
    for (int i = 0; i < n; i++) {
      moistureValues[i] = analogRead(sensorPins[i]);                  // Sensorwert lesen
      int moisturePercent = map(moistureValues[i], 0, 1023, 100, 0);  // Sensorwert in Prozent umrechnen
      Serial.print("Sensor ");                                        // Sensorname auf serieller Konsole ausgeben
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.println(moisturePercent);  // Sensorwert auf serieller Konsole ausgeben
      lcd1.setCursor(i * 3 + 2, 1);     // Cursor auf zweite Zeile setzen
      lcd1.print(i + 1);                // Sensornummer auf LCD Display ausgeben
      lcd1.setCursor(i * 3 + 1, 2);     // Cursor auf dritte Zeile setzen
      lcd1.print(moisturePercent);      // Sensorwert auf LCD Display ausgeben
      delay(500);                       // Halbe Sekunde warten
    }
    // Pumpenzustände bestimmen und auf LCD Display anzeigen
    lcd1.setCursor(0, 3);  // Cursor auf vierte Zeile setzen
    delay(1000);
    for (int i = 0; i < n; i++) {
      int moisturePercent = map(moistureValues[i], 0, 1023, 100, 0);  // Sensorwert in Prozent umrechnen
      if (moisturePercent < lowerThreshold) {                         // Wenn Boden zu trocken ist
        digitalWrite(pumpPins[i], HIGH);                              // Pumpe einschalten
        pumpStates[i] = 1;                                            // Pumpenzustand auf an setzen
      } else if (moisturePercent > upperThreshold) {                  // Wenn Boden zu nass ist
        digitalWrite(pumpPins[i], LOW);                               // Pumpe ausschalten
        pumpStates[i] = 0;                                            // Pumpenzustand auf aus setzen
      }
      lcd1.setCursor(i * 3 + 1, 3);  // Cursor auf vierte Zeile setzen
      if (pumpStates[i] == 1) {      // Wenn Pumpe an ist
        lcd1.print("AN");            // AN auf LCD Display ausgeben
      } else {                       // Wenn Pumpe aus ist
        lcd1.print("AU");            // AU auf LCD Display ausgeben
      }
    }
    // TEMPERATURABFRAGE
    float tempnow = sensors.getTempCByIndex(0);

    // Prüfen, ob sich die Temperatur geändert hat
    pruefeTemperaturUndSteuerTuer(tempnow);
    if (tempnow < 18 - hysteresewert) {
      // Bereich: tempnow < 18 - 0.8
      tuerZu(tempnow);
      letzteTemp = tempnow;
      delay(1000);
      letzteTemp = tempnow;
    } else if (tempnow >= 18 && tempnow < 21 - hysteresewert) {
      // Bereich: 18 <= tempnow < 21 - 0.8
      tuerZu(tempnow);
      letzteTemp = tempnow;
      Serial.print(letzteTemp);
      delay(1000);
    } else if (tempnow >= 21 && tempnow < 24 - hysteresewert) {
      tuerZu(tempnow);
      letzteTemp = tempnow;
    } else if (tempnow >= 24 + hysteresewert) {
      tuerZu(tempnow);
      letzteTemp = tempnow;
    }
  }

Das ganze kanst in 4 Zeilen erledigen

char uhrzeitAnzeige[20];
  snprintf(uhrzeitAnzeige, 20, "%.1i:%.2i:%.2i",
           now.hour(), now.minute(), now.second());
lcd.print(uhrzeitAnzeige);

Ist alles formatiert und "tanzt" nicht auf dem Display
ist jetzt nur für Uhrzeit , ist aber kein Problem Datum dazu binden

https://en.cppreference.com/w/c/io/fprintf

Ganz ehrlich, ich verstehe dein ganzes Konzept mit der Türbewegung nicht. Warum willst Du die Tür überhaupt immer wieder erst zumachen, wenn sich die Tür aufgrund geänderter Temperatur auf eine andere Öffnungsposition bewegen soll? Stell sie doch einfach auf die neue Position, wenn sich der Temperaturbereich geändert hat.
Ich verstehe auch deine Temperaturbereiche nicht wirklich. Soll sie unter 18 zu sein, bei höheren Temperaturen in 2 Schritten öffnen, und über 24 ganz auf sein?
Das passt dann mit 3/4 und 1/2 aber auch nicht. Das musst Du mal klarstellen.

Wenn Sie ganz zugehen soll und noch nicht zu ist, machst Du eine Referenzfahrt, ansonsten stellst Du sie einfach auf die gewünschte Öffnung. Grundsätzlich sollte das einfach so gehen:

void tuerZu(float tempnow) {
    const long schritteProVoll = 13624;  // Anzahl der Schritte für eine volle Öffnung

    if (tempnow < 18 - 0.8) {
        // unter 18° Tür zu. Wenn die Tür nicht schon zu ist, eine Referenzfahrt machen
        if ( myStepper.currentPosition() > 0 ) {
            tuerZu();
        }
    } else if (tempnow >= 18 && tempnow < 21 - 0.8) {
        myStepper.moveTo( schritteProVoll * 3 / 4 );
    } else if (tempnow >= 21 && tempnow < 24 - 0.8) {
        myStepper.moveTo( schritteProVoll / 2 );
    } else if (tempnow >= 24) {
        myStepper.moveTo( schritteProVoll ); // über 24° ganz auf
    }
}

P.S. Den Name 'tuerZu' für die Positionierung auf verschiedene Öffnungen finde ich etwas suboptimal :wink:

  • du kannst einige Byte SRAM sparen, wenn du für einstellige Zeichen das einfache Hochkomma statt den Anführungszeichen verwendest. Machst beim Punkt ja schon, kannst also für die Vornull auch machen.
    Ein '.' ist ein Zeichen = 1 Byte.
    Ein "0" ist eine Zeichenkette die mit dem Nullterminator abgeschlossen ist und hat daher 2 Byte.
  • DEC brauchst du im Regelfall nicht hinschreiben, weil das der default ist.
  • du könntest dir für die Ausgabe der bedingten Vornull auch eine Funktion schreiben, ungetestest sowas in der Art:
void print10(int value) {
   if (value < 10) lcd.print('0');
   lcd.print(value);   
}

Aufruf dann z.B. mit print10(now.day());
Geht sicher noch eleganter, aber so würde ich mal anfangen.

  • snprintf schaut zwar bequem aus, aber achte auf den Speicherbedarf. Die derzeitigen if sind meiner Meinung auf einem kleinen Microcontroller effizienter.

Scheint mir auf einem Mega nicht so kritisch - da ist noch mehr als reichlich Platz. Und für ungenutzten Speicher gibt's kein Geld zurück :wink:

@Eibenger:
Warum prüfst Du die Temperaturbereiche mehrfach? Sowohl im loop(), um tuerZu() aufzurufen, und dann in tuerZu() nochmal. Das macht keinen Sinn. Ich denke in dem Sketch solltest Du mal aufräumen :smile:.

Danke für die Tipps mit den LCD. Soweit bin ich nco nicht gekommen, habe die Codes aus dem von AzDelivery mitgelieferten Beispiel übernommen.

Microbanhner: Den Name 'tuerZu' für die Positionierung auf verschiedene Öffnungen finde ich etwas suboptimal :wink:

Bin schon daran, eine Statusvariable zu definieren, komme erst heute Abend wieder zum Code schreiben, will es lieber nicht als Programmieren bezeichnen.

Und zu der Frage mit der häufigen Anfahrt TuerZu.

Im vergangenen Jahr hatte ich das Teil schon mal programmiert, da hatte ich als Linearführung einen 1m lange Trapezgewindespindel. Die Tür händisch zu öffnen, machte Probleme, z.B., wenn man mal schnell reinschauen wollte und die Tür war lt. Programm geschlossen. Allerdings waren die steps exakt. Ich betone noch einmal, das Zeug braucht niemand wirklich nur für die paar Tomaten, es dient nur zum Üben des Programmierens in Arduino (habe früher mal C gemacht) und nun zur Vorbereitung meines Projektes Flugsimulator für eine CESNA 172, habs schon mal erwähnt, mit 2x Garmin sowie alle Schalter und Taster über Mobiflight und Arduino an 2x Megas usw.)
Und so wird die Tür über einen Riementrieb bewegt. Und wenn doch mal im Wochenendhaus der Strom ausfallen sollte, oder die Tür halb geöffnet ist und man mal erin will, dann kann man sie einfach aufschieben. Wenn dann der Stepper die Tür an die neu erfasste Position bringen soll, dann fährt er eben erst in die Startposition TuerZu und von dort aus zur errechnteten Pos. Da kann auch mal ein Igel die Tür kurz blockieren, der Riemenschlupf ist besser als wenn die Trapezstange Druck ausübt. Und JA, die Igel gehen ein und aus im Tomatenhäuschen. Die Bewegung wird mittels Kamera von zu Hause überwacht und auch nachts in der Cloud gespeichert.

Genug geschwafelt, jetzt geht's nach Hause und abends an den PC zum "Programmieren". Stelle hoffentlich noch heute den Code ein.

Es scheint vollbracht. Die Routine mit der prozentualen Türöffnung funktioniert wunderbar.
Ich werde morgen noch eine Funktion initialisiereTuerZustand , die einmal im setup() aufgerufen wird, um den initialen Zustand der Tür zu bestimmen und die entsprechende Aktion auszuführen. Dies stellt sicher, dass die Tür in den richtigen Zustand gebracht wird, basierend auf der aktuellen Temperatur, wenn das Programm gestartet wird.

Es könnte sonst sein, dass nach der Ruhephase von z.B. 20 Uhr bis 8 Uhr der Wert des Türzustandes gespeichert bleibt und die Tür 50% geöffnet würde, sofern der Wert so in der letzten Abfrage stand.
Der Regen kommt noch als Parameter hinzu, was kein Problem ist. Dann sind es eben 5 statusse.

Hier der Code, wen's interessiert.
Danke für die konkreten Hinweise zu LCD und auch, das ist besonders für mich als Neuling wichtig, die entsprechende Dokumentation als Link!

Im Code habe ich das noch nicht berücksichtigt, poste hier nur die relevanten Teile.
Von den MobaTools bin ich hellauf begeistert. Bestimmt kann ich den code noch optimieren, will aber erst mal ernten und beim Wachsen zuschauen, während ich das nächste Projekt mit der Cessna starte.

#include <MobaTools.h>
#include <LiquidCrystal_I2C.h>
// LCD-Objekt erstellen
LiquidCrystal_I2C lcd(0x26, 20, 4);
LiquidCrystal_I2C lcd1(0x27, 20, 4);
#include <OneWire.h>
#include <DallasTemperature.h>
//#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;

// PINBELEGUNG Regensensor
const int rainDigitalPin = 13;
const int rainAnalogPin = A0;
const int rainSensorPowerPin = A1;
// PINBELEGUNG Temperatursensor
// OneWire oneWire(TEMP_PIN);
#define ONE_WIRE_BUS 46
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// PINBELEGUNG Stepper-Motor NEMA 17
const int enablePin = 8;  // blau
const int stepPin = 9;    // schwarz
const int dirPin = 10;    // rot
// PINBELEGUNG für die Endschalter der Linearführung
#define Auf 6  // gelb
#define Zu 7   // rot
// Definiere eine Variable für den Temperatursensor
float tempStatus;
float tempnow;
// Globale Variable, um den letzten Temperaturwert zu speichern
float letzteTemp = -1;
int a;
float hysteresewert = 0.8;

// PINBELEGUNG der Sensoren
#define RAIN_PIN A0  // Regensensor
#define TEMP_PIN A2  // Temperatursensor
// Anzahl der Sensoren und Pumpen
const int n = 6;
int sensorPins[n] = { A3, A4, A5, A6, A7, A8 };  // Sensoren Pins (analog)
int pumpPins[n] = { 30, 31, 32, 33, 34, 35 };    // Pumpen Pins (digital)
// Feuchtigkeitswerte (0 - 1023)
int moistureValues[n];
// Pumpenzustände (0 oder 1)
int pumpStates[n];
int schrittStatus;
int schrittstatusOld;


// Schwellwerte für Pumpensteuerung (in Prozent)
const int lowerThreshold = 55;
const int upperThreshold = 68;

const int schritteProVoll = 13700;  // Anzahl der Schritte für eine volle Öffnung

// Start. und Endzeit festlegen
#define START_HOUR 8                  // Startstunde des Programms
#define END_HOUR 22                   // Endstunde des Programms
MoToStepper myStepper(300, STEPDIR);  // 300 Schritte pro Umdrehung

enum TuerZustand { TuerZu,
                   ViertelOffen,
                   HalbOffen,
                   VollOffen };
TuerZustand tuerZustand = TuerZu;

// Funktion tuerZu ohne Parameter
void tuerZu() {
  myStepper.doSteps(-20000);
  while (digitalRead(Zu) == HIGH)
    ;
  myStepper.doSteps(200);
  while (myStepper.moving())
    ;
  myStepper.setZero();
  delay(500);
}

// Funktion tuerZu mit Zustandsparameter
void tuerZu(TuerZustand zustand) {
  if (zustand == TuerZu) {
    // Führe die Schritte aus, um die Tür zu schließen
    myStepper.doSteps(-20000);
    while (digitalRead(Zu) == HIGH)
      ;
    myStepper.doSteps(200);
    while (myStepper.moving())
      ;
    myStepper.setZero();
    delay(500);
  } else {
    int schritte = 0;
    switch (zustand) {
      case ViertelOffen:
        schritte = schritteProVoll * 1 / 4;
        break;
      case HalbOffen:
        schritte = schritteProVoll * 1 / 2;
        break;
      case VollOffen:
        schritte = schritteProVoll;
        break;
    }
    if (schritte != 0) {
      tuerZu();
      myStepper.doSteps(schritte);
      while (myStepper.moving())
        ;  // Warten bis Bewegungsende
    }
    if (zustand == VollOffen) {
      // Führe die Schritte aus, um die Tür zu öffnen
      myStepper.doSteps(20000);
      while (digitalRead(Auf) == HIGH)
        ;
      myStepper.doSteps(-200);
      while (myStepper.moving())
        ;
      myStepper.setZero();
      delay(500);
    }
  }
}


void setup() {
  Serial.begin(9600);
  lcd.init();       //Im Setup wird das erste  LCD gestartet
  lcd1.init();      //Im Setup wird das zweite LCD gestartet
  lcd.backlight();  //Hintergrundbeleuchtung vom LCD einschalten
  lcd1.backlight();
  pinMode(pumpPins[n], OUTPUT);
  pinMode(sensorPins[n], INPUT);

  //PINMODUS festlegen
  pinMode(8, OUTPUT);   //Enable           BLAU
  pinMode(9, OUTPUT);   //Puls             SCHWARZ
  pinMode(10, OUTPUT);  //Direction        ROT
  pinMode(enablePin, OUTPUT);

  // PINMODUS für Pullup festlegen
  pinMode(4, INPUT_PULLUP);  // Tür manuell auf
  pinMode(5, INPUT_PULLUP);  // Tür manuell zu
  pinMode(6, INPUT_PULLUP);  // Tür auf   GELB
  pinMode(7, INPUT_PULLUP);  // Tür zu    ROT
  // Pumpen Pins als Ausgänge definieren
  for (int i = 0; i < n; i++) {
    pinMode(pumpPins[i], OUTPUT);
    digitalWrite(pumpPins[i], HIGH);  // Pumpen ausschalten
    pumpStates[i] = 0;                // Pumpenzustände auf aus setzen
  }
  // PINMODUS für Regensensor festlegen
  //pinMode(rainDigitalPin, INPUT);
  pinMode(rainAnalogPin, INPUT);
  pinMode(rainSensorPowerPin, OUTPUT);

  // Initialisiere den Stepper
  myStepper.attach(stepPin, dirPin);
  myStepper.setSpeed(4000);   // 5000 Schritte pro Sekunde
  myStepper.setRampLen(100);  // Rampenlänge auf 10 Schritte setzen
  digitalWrite(enablePin, LOW);

  // Führe die Türschließroutine einmal aus
  tuerZu();


  Wire.begin();
  rtc.begin();
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  // Abfrage, ob Zeitmodul gestartet ist
  if (!rtc.begin()) {
    lcd.print("RTC nicht gefunden!");
    while (1)
      ;
  }
}


void loop() {
  float tempnow = sensors.getTempCByIndex(0);
  TuerZustand neuerZustand = tuerZustand;

  /*
  //  ******* Zu Programmbeginn werden Uhrzeit, Datum und Status auf dem LCD Display angezeigt *******
  DateTime now = rtc.now();
  lcd.setCursor(0, 0);
  if (now.hour() < 10) lcd.print("0");
  lcd.print(now.hour(), DEC);
  if (now.hour() < 10) lcd.print("0");
  lcd.print(':');
  if (now.minute() < 10) lcd.print("0");
  lcd.print(now.minute(), DEC);
  if (now.minute() < 10) lcd.print("0");
  lcd.print("  ");
  if (now.day() < 10) lcd.print("0");
  lcd.print(now.day(), DEC);
  lcd.print('.');
  if (now.month() < 10) lcd.print("0");
  lcd.print(now.month(), DEC);
  lcd.print('.');
  lcd.print(now.year(), DEC);
  if (now.hour() >= START_HOUR && now.hour() < END_HOUR) {
    lcd.setCursor(0, 1);
    lcd.print("  Programm bereit!");
  } else {
    lcd.setCursor(0, 1);
    lcd.print("  Ausser der Zeit");
  }
  */

  // ABFRAGE DES REGENSENSORS ANALOG
  digitalWrite(rainSensorPowerPin, HIGH);
  delay(500);
  int rainAnalog = analogRead(rainAnalogPin);
  if (rainAnalog >= 900) {
    lcd.setCursor(0, 3);
    lcd.print("Kein Regen");
  }
  if (900 > rainAnalog && rainAnalog >= 750) {
    lcd.setCursor(0, 3);
    lcd.print("leichter Regen");
  }
  if (rainAnalog < 750) {
    lcd.setCursor(0, 3);
    lcd.print("starker Regen");
  }
  digitalWrite(rainSensorPowerPin, LOW);
  delay(500);

  // TEMPERATURABFRAGE
  tempnow = sensors.getTempCByIndex(0);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Die Innentemperatur");
  sensors.requestTemperatures();
  lcd.setCursor(0, 1);
  lcd.print("betr\xE1gt:    ");
  lcd.print(sensors.getTempCByIndex(0));
  lcd.print("\337C");
  delay(500);

  /*
  // Sensorwerte lesen und auf LCD Display anzeigen
  lcd1.setCursor(0, 0);                // Cursor auf erste Zeile setzen
  lcd1.print("Feuchte (%) - Pumpen");  // Überschrift schreiben
  for (int i = 0; i < n; i++) {
    moistureValues[i] = analogRead(sensorPins[i]);                  // Sensorwert lesen
    int moisturePercent = map(moistureValues[i], 0, 1023, 100, 0);  // Sensorwert in Prozent umrechnen
    Serial.print("Sensor ");                                        // Sensorname auf serieller Konsole ausgeben
    Serial.print(i + 1);
    Serial.print(": ");
    Serial.println(moisturePercent);  // Sensorwert auf serieller Konsole ausgeben
    lcd1.setCursor(i * 3 + 2, 1);     // Cursor auf zweite Zeile setzen
    lcd1.print(i + 1);                // Sensornummer auf LCD Display ausgeben
    lcd1.setCursor(i * 3 + 1, 2);     // Cursor auf dritte Zeile setzen
    lcd1.print(moisturePercent);      // Sensorwert auf LCD Display ausgeben
    delay(500);                       // Halbe Sekunde warten
  }
  // Pumpenzustände bestimmen und auf LCD Display anzeigen
  lcd1.setCursor(0, 3);  // Cursor auf vierte Zeile setzen
  delay(500);
  for (int i = 0; i < n; i++) {
    int moisturePercent = map(moistureValues[i], 0, 1023, 100, 0);  // Sensorwert in Prozent umrechnen
    if (moisturePercent < lowerThreshold) {                         // Wenn Boden zu trocken ist
      digitalWrite(pumpPins[i], HIGH);                              // Pumpe einschalten
      pumpStates[i] = 1;                                            // Pumpenzustand auf an setzen
    } else if (moisturePercent > upperThreshold) {                  // Wenn Boden zu nass ist
      digitalWrite(pumpPins[i], LOW);                               // Pumpe ausschalten
      pumpStates[i] = 0;                                            // Pumpenzustand auf aus setzen
    }
    lcd1.setCursor(i * 3 + 1, 3);  // Cursor auf vierte Zeile setzen
    if (pumpStates[i] == 1) {      // Wenn Pumpe an ist
      lcd1.print("AN");            // AN auf LCD Display ausgeben
    } else {                       // Wenn Pumpe aus ist
      lcd1.print("AU");            // AU auf LCD Display ausgeben
    }
  }
  */
  Serial.print("Die aktuelle Temperatur beträgt ");
  Serial.print(tempnow);
  Serial.println(" °C)");
  delay(500);

  // Prüfen, ob sich die Temperatur außerhalb der Hysteresebereiche befindet
  if (tempnow < 18 - hysteresewert) {
    neuerZustand = TuerZu;
  } else if (tempnow >= 18 && tempnow < 21 - hysteresewert) {
    neuerZustand = ViertelOffen;
  } else if (tempnow >= 21 && tempnow < 24 - hysteresewert) {
    neuerZustand = HalbOffen;
  } else if (tempnow >= 24 + hysteresewert) {
    neuerZustand = VollOffen;
  }
  Serial.print("neuer Zustand: ");
  Serial.println(neuerZustand);

  // Wenn sich der Zustand geändert hat, führe die entsprechende Aktion aus
  if (neuerZustand != tuerZustand) {
    tuerZu(neuerZustand);        // Rufe die Funktion tuerZu mit dem neuen Zustand auf
    tuerZustand = neuerZustand;  // Aktualisiere den Zustand
  }
}

Hm die Buchstabenfolge "Cessna" kenne ich bisher nur als Flugzeug.
Handelt es sich bei dem "Cessna"-Projekt um das manntragende Flugzeug?
Da willst du als Hobbygebastel hoffentlich nur am Boden und im Hangar einen Luftfeuchtigkeismesser bauen und nicht etwa den Höhenmesser im Flugzeug ersetzen.

Wenn es ein Modellflugzeug ist hoffentlich auch nix was die Steuerfunktionen betrifft.
Oder geht es bei "Cessna" um etwas ganz anderes?

Cockpit, in dem alle Anzeige- und Steuermodule wie Joke, Höhenmesser, Navi (2x GARMIN) in einen sperrholzbasierten Aufbau maßstäblich der Originalversion nachgebaut werden.
Das gibt es zwar zu kaufen, ist aber selbst ohne PC ziemlich teuer.
Als Grundlage gibt es das Programm von Microsoft Flugsinulator MS2020 , in diesem Jahr kommt MS2024 raus.
Dafür und auch f.a. gibt es die Bedien- und Anzeienteile in verschiedenen Ausführungen auf dem Markt. Man stellt sie entweder auf den Schreibtich und bau sie wieder ab nach Verwendung, oder man baut sie fest in ein Modell ein.
Genau so wie die MobaTools, die ich hier kennengelernt habe, gibt es die MobiFlight Bibliothek. Anregung gab mir dieses Video HIER und auch dieses hier.
Damit kommt ein realistisches feeling auf und zu meinem 80. im Sommer 2025 will ich mal mit dem Flugzeug durch Europa in Echtzeit und Echt- Wetter düsen.
Interessant war es z.B., als voriges Jahr wieder mal ein heftiger Vulkanausbruch war, konnte ich durch die Aschewolken fliegen. Die Übertragung der in Echtzeit vorliegenden Wetterdaten machts möglich! Leistungsstarker Rechner vorausgesetzt, und den habe ich nunmal.
Wenn alles im Kasten ist, dann kommt im nächsten Jahr die A 720 dran mit weiteren features, wenn ich nicht unterwegs abgestürzt bin ;-).

Traust mir wohl gar nichts zu? :face_with_hand_over_mouth:

Doch doch. Eine große Modelleisenbahn mit mehreren Etagen und 10 Zügen die gleichzeitig fahren ist ne dolle Nummer.

Nein so war das nicht gemeint. Aber weil wir uns eben nicht kennen vorsichtshalber die Nachfrage. In einem Simulator kann natürlich nix schlimmes passieren. Manchmal gibt es im Forum Leute die wirklich haarsträubend gefährliche Dinge machen wollen.

1 Like