Zwei ineinander verschachtelte Timer, verschiedene, zyklische Aufgaben

Hallo zusammen,

irgendwie hänge ich gedanklich fest...

Alle, z.B. 7-8std soll etwas für z.B. 15min angehen, soweit kein Problem.

Aber wenn es dann an ist, soll etwas anderes für z.B. 3min angehen, und dann ausbleiben, bis die 3std wieder vorbei sind.

Hintergrund:

Alle z.B. 3std schaltet der Arduino für ca 15min via SSR 230V ein, an denen ein Ladegerät für eine Powerbank und ein Trafo für eine kleine Pumpe hängt. Ich möchte nicht die ganze Zeit den Trafo ungenutzt unter Spannung halten.

Wenn dann die 3std vorbei sind, also alles Spannung hat, auch der 12V Trafo, sollen 4 Bodenfeuchtesensoren eingelesen werden, und bei Bedarf die Pumpe (via IRLZ) für eine definierte Zeit (z.B. 3 min) laufen.

Wenn die 3min vorbei sind, soll die Messung NICHT mehr ausgeführt werden, bis wiederum die 3std vorbei sind, und 5V und 12V wieder eingeschaltet sind.

In den "restlichen 12min wird dann einfach nur die kleine Powerbank geladen.

Ich hoffe, ich konnte halbwegs nachvollziehbar erklären, was die Schaltung machen soll?

Die Zeiten stimmen natürlich nicht, sondern sind wesentlich kürzer, damit ich alles schnell an 2 LED's sehen kann.
Ich habe diverse Versuche in Anlehnung an Blink without delay gemacht, die kläglich gescheitert sind, daher fehlt dieser Teil im Programm z.Zt. völlig.
Ich möchte das Messen und ggf pumpen gerne in ein Unterprogramm packen, welches dann, wenn die 15min beginnen, aufgerufen wird.
Messen und Mittelwert bilden habe, das ist kein Problem. Es würde mir reichen, einen Anfänger-lesbaren Tipp zu bekommen, für die "PumpZeit" LED2 einzuschalten, und dass sie nicht nochmal in der "AnZeit" aufgerufen wird.

Besten Dank im Voraus

Basis ist folgender Code:

const byte led1 = 9; // pin
const byte led2 = 10; // pin

unsigned long previousMillis_1 = 0l;
unsigned long previousMillis_2 = 0l;

const unsigned long AnZeit  =  5000l; //ms
const unsigned long Intervall = 10000l; //ms
const unsigned long PumpZeit  =  2000l; //ms


void setup() 
{
  pinMode(led1,OUTPUT);
  pinMode(led2,OUTPUT);
}
void loop()
{
  if (millis() - previousMillis_1 > AnZeit)
  {
    digitalWrite(led1, LOW);
  }
  if (millis() - previousMillis_1 > Intervall)
  {
    previousMillis_1 = millis();    
    digitalWrite(led1, HIGH);
    MeinProgramm();
  }
}

void MeinProgramm() //Pseudocode
{  
Messen,
If (MittelWert_Messungen < Schwelle)
{
PumpZeit pumpen
}

Und warum verwendest du für die Schaltzeiten keine RTC ?
Zumindest wir das genauer und sicher auch einfacher. :wink:

Einigermaßen.

Du möchtest endliche Automaten bauen.
Keine verschachtelten Timer.
Sondern Automaten, welche miteinander kommunizieren.

1 Like
const byte led1 = 9; // pin
const byte led2 = 10; // pin

unsigned long previousMillis;

const unsigned long PumpZeit  =  2000; //ms
const unsigned long AnZeit  =  PumpZeit + 3000; //ms
const unsigned long Intervall = AnZeit + 5000; //ms


void setup()
{
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
}
//
void loop()
{
  if (millis() - previousMillis > intervall)
  {
    previousMillis = millis();
    digitalWrite(led1, HIGH);
    digitalWrite(led2, HIGH);
  }
  if (millis() - previousMillis > AnZeit)
  {
    digitalWrite(led1, LOW);
  }
  if (millis() - previousMillis > PumpZeit)
  {
    digitalWrite(led2, LOW);
  }
}

?

1 Like

Ich verstehe und sehe bei dir nur 1 Timer, und mehrere Aktionen die relativ zu diesem Zyklus aktiviert werden sollen.

Ich nehme an, die "zB 7-8std" und die "3std" sind das Gleiche. Das ist dein Zyklus.
In jedem Zyklus soll eine Aktion 15 Minuten lang laufen, eine andere 3 Minuten lang.
Beide Aktionen, damit es noch einfacher ist, mit derselben Startzeit 0 relativ zum Zyklus.

Das hat dir @my_xy_projekt gezeigt.

1 Like

Bin mir nicht sicher, aber hier noch eine Variante mit dem bereits vorgeschlagenen endlichen Automaten:

const byte led1 = 8; // pin
const byte led2 = 9; // pin
const byte led3 = 10; // pin

unsigned long previousMillis_0 = millis() - 2000;
unsigned long previousMillis_1 = 0;
unsigned long previousMillis_2 = 0;
unsigned long previousMillis_3 = 0;

const unsigned long Interval_0 = 10000; //ms
const unsigned long Interval_1 = 3000; //ms
const unsigned long Interval_2 = 1000; //ms
const unsigned long Interval_3 = 5000; //ms

byte schritt = 0;

void setup()
{
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
}

void loop()
{
  unsigned long jetzt = millis();

  switch (schritt)
  {
    case 0:
      if (millis() - previousMillis_0 >= Interval_0)
      {
        digitalWrite(led1, HIGH);
        digitalWrite(led2, HIGH);
        previousMillis_0 = jetzt;
        previousMillis_1 = jetzt;
        previousMillis_3 = jetzt;
        schritt++;
      }
      break;
    case 1:
      if (millis() - previousMillis_1 >= Interval_1)
      {
        digitalWrite(led2, LOW);
        digitalWrite(led3, HIGH);
        previousMillis_2 = jetzt;
        schritt++;
      }
      break;
    case 2:
      if (millis() - previousMillis_2 >= Interval_2)
      {
        digitalWrite(led3, LOW);
        schritt++;
      }
      break;
    case 3:
      if (millis() - previousMillis_3 >= Interval_3)
      {
        digitalWrite(led1, LOW);
        schritt = 0;
      }
      break;
  }
}

Bei der Benennung habe ich bewußt nur nichtsagende Zahlen verwendet, aussagekräftige Namen findest Du besser :slightly_smiling_face:

1 Like

Ich hab mich mal an Deinem zu schaffen gemacht.

const byte led1 = 8; // pin
const byte led2 = 9; // pin
const byte led3 = 10; // pin


const unsigned long Interval_2 = 1000; //ms
const unsigned long Interval_1 = Interval_2 + 2000; //ms
const unsigned long Interval_3 = Interval_1 + 2000; //ms
const unsigned long Interval_0 = Interval_3 + 5000; //ms

unsigned long previousMillis = millis() - Interval_0;

byte schritt = 0;

void setup()
{
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
}

void loop()
{
  unsigned long jetzt = millis();
  switch (schritt)
  {
    case 0:
      if (millis() - previousMillis >= Interval_0)
      {
        digitalWrite(led1, HIGH);
        digitalWrite(led2, HIGH);
        previousMillis = jetzt;
        schritt++;
      }
      break;
    case 1:
      if (millis() - previousMillis >= Interval_1)
      {
        digitalWrite(led2, LOW);
        digitalWrite(led3, HIGH);
        previousMillis = jetzt;
        schritt++;
      }
      break;
    case 2:
      if (millis() - previousMillis >= Interval_2)
      {
        digitalWrite(led3, LOW);
        previousMillis = jetzt;
        schritt++;
      }
      break;
    case 3:
      if (millis() - previousMillis >= Interval_3)
      {
        digitalWrite(led1, LOW);
        previousMillis = jetzt;
        schritt = 0;
      }
      break;
  }
}

?
Eigentlich gehört intervall passend zum schritt...
Aber die vielen Variablen haben mich gestört...

1 Like

Hat es einen tieferen Grund, warum Du mal millis() und mal jetzt verwendest?

Deins könnte man noch etwas eindampfen, wenn man die Zeitabfrage außerhalb des endlichen Automaten ansiedelt:

const byte led1 = 8; // pin
const byte led2 = 9; // pin
const byte led3 = 10; // pin

const unsigned long Interval_2 = 1000; //ms
const unsigned long Interval_1 = Interval_2 + 2000; //ms
const unsigned long Interval_3 = Interval_1 + 2000; //ms
const unsigned long Interval_0 = Interval_3 + 5000; //ms
unsigned long intervall = Interval_0; //ms

unsigned long previousMillis = millis() - Interval_0;

byte schritt = 0;

void setup()
{
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
}

void loop()
{
  unsigned long jetzt = millis();
  if (jetzt - previousMillis >= intervall)
  {
    switch (schritt)
    {
      case 0:
        digitalWrite(led1, HIGH);
        digitalWrite(led2, HIGH);
        previousMillis = jetzt;
        intervall = Interval_1;
        schritt++;
        break;
      case 1:
        digitalWrite(led2, LOW);
        digitalWrite(led3, HIGH);
        previousMillis = jetzt;
        intervall = Interval_2;
        schritt++;
        break;
      case 2:
        digitalWrite(led3, LOW);
        previousMillis = jetzt;
        intervall = Interval_3;
        schritt++;
        break;
      case 3:
        digitalWrite(led1, LOW);
        previousMillis = jetzt;
        intervall = Interval_0;
        schritt = 0;
        break;
    }
  }
}
1 Like

Was fragst Du mich?
Das ist Dein Code. :slight_smile:

Das macht so aber keinen Sinn, weil Du eine weitere Schachtelebene aufmachst.
Dann gehört die Schrittzuweisung auch ausserhalb und nur das zuweisen des Intervallfaktors in den Schritt.

1 Like

also wenn ich das richtig gelesen habe, beschreibst du eine kleine State Machine.
Aktuelle sehe ich 3 Status.
Einmal springst du abhängig von der Feuchtigkeit in den einen oder anderen Status.

Im Prinzip reicht eine Variable "previousMillis" für alle Zeitgesteuerten Status-Übergänge.

digraph G { 
  graph [
    label="Finite State Machine"
    labelloc="t"
    fontname="sans-serif"
  ]
  node [
    style=filled
    fillcolor=gray95
    fontname="sans-serif"
  ]
  
  step0 -> step1 [label="nach 3h" fontsize=10];  
  step1 -> step0 [label="nach 15min & ist trocken" fontsize=10];  
  step1 -> step2 [label="nach 15min & ist feucht" fontsize=10];  
  step2 -> step0 [label="nach 3min" fontsize=10];  
 
  step0[label = "IDLE"]
  step1[label = "SSR_EIN"]
  step2[label = "PUMPE_EIN"]

}

zum Grafik anpassen --> hier.

1 Like

Autsch, sorry :flushed:

1 Like

Hallo gummiq

Hier kommt ein Programm für das "Sparbewässerung-System".

In dem Programm sind noch die Anpassungen für die Anzahl der verwendeten Pumpstationen (Messpunkte) und Zeiten vorzunehmen.

Das Programm ist mit einen Mega und einem Messpunkt getestet.

Viel Spass beim Testen und Spielen.

// https://forum.arduino.cc/t/zwei-ineinander-verschachtelte-timer-verschiedene-zyklische-aufgaben/1144211/1
#define ProjectName "Zwei ineinander verschachtelte Timer, verschiedene, zyklische Aufgaben"
// make variables
constexpr uint8_t SsrPin {9};
constexpr uint32_t SsrOnTime {2000};
constexpr uint32_t SsrOffTime {5000};
constexpr uint32_t SsrOnOffTime[] {SsrOffTime, SsrOnTime};
constexpr uint8_t SoilMoistureSensors[] {A8};
constexpr uint8_t PumpPins[] {10};
constexpr uint16_t SoilMoistures[] {512};
constexpr uint32_t MoistureTimes[] {2000};
uint32_t ssrCurrentTime;
// make structures
struct MEASUREMENTPOINT
{
  uint8_t soilMoistureSensor;
  uint8_t pumpPin;
  uint16_t soilMoisture;
  uint32_t moistureTime;
  uint32_t previousTime;
  uint8_t moistureControl;
  void make(uint8_t soilMoistureSensor_, uint8_t pumpPin_, uint16_t soilMoisture_, uint32_t moistureTime_)
  {
    Serial.println(__func__);
    soilMoistureSensor = soilMoistureSensor_;
    pumpPin = pumpPin_;
    pinMode (pumpPin, OUTPUT);
    digitalWrite (pumpPin, HIGH);
    delay (1000);
    digitalWrite (pumpPin, LOW);
    soilMoisture = soilMoisture_;
    moistureTime = moistureTime_;
  }
  void check(uint32_t currentMillis)
  {
    Serial.println(__func__);
    digitalWrite(pumpPin, analogRead(soilMoistureSensor) <= soilMoisture);
    Serial.print(F("Pumpe"));
    Serial.println(digitalRead(pumpPin) ? " an" : " aus");
    if (digitalRead(pumpPin) == HIGH)
    {
      moistureControl = HIGH;
      previousTime = currentMillis;
    }
  }
  void runIt(uint32_t currentMillis)
  {
    if (currentMillis - previousTime >= moistureTime  && moistureControl)
    {
      moistureControl = LOW;
      digitalWrite(pumpPin, LOW);
      Serial.println(F("Pumpe aus"));
    }
  }
} measuringPoints[sizeof(PumpPins)];
// make support
void heartBeat(int LedPin, uint32_t currentMillis)
{
  static bool setUp = false;
  if (!setUp) pinMode (LedPin, OUTPUT), setUp = !setUp;
  digitalWrite(LedPin, (currentMillis / 500) % 2);
}
void setup()
{
  Serial.begin(115200);
  Serial.println(ProjectName);
  pinMode (SsrPin, OUTPUT);
  digitalWrite (SsrPin, HIGH);
  delay (1000);
  digitalWrite (SsrPin, LOW);
  uint8_t element = 0;
  for (auto &measuringPoint : measuringPoints)
  {
    measuringPoint.make(SoilMoistureSensors[element], PumpPins[element], SoilMoistures[element], MoistureTimes[element]);
    element++;
  }
}
void loop()
{
  uint32_t currentMillis = millis();
  heartBeat(LED_BUILTIN, currentMillis);
  if (currentMillis - ssrCurrentTime >= SsrOnOffTime[digitalRead(SsrPin)])
  {
    ssrCurrentTime = currentMillis;
    digitalWrite (SsrPin, digitalRead(SsrPin) ? LOW : HIGH);
    Serial.print(F("Ladegeät"));
    Serial.println(digitalRead(SsrPin) ? " an" : " aus");
    if (digitalRead(SsrPin) == LOW)
    {
      Serial.println(F("Bodenfeuchtesensoren eingelesen"));
      for (auto &measuringPoint : measuringPoints) measuringPoint.check(currentMillis);
    }
  }
  for (auto &measuringPoint : measuringPoints) measuringPoint.runIt(currentMillis);
}

Ich wünsche einen geschmeidigen Tag und viel Spass beim Programmieren in C++.

2 Likes

Hallo Zusammen,

Ui... Da ist ja einiges an Antworten gekommen, vielen Dank dafür.
Dann werde ich jetzt mal alles in Ruhe durchgehen und dann die Lösung nehmen die funktioniert UND die ich gut verstehe.

Mit jeder Entwicklungsstufe also eine andere version :slight_smile:

Die Schönheit liegt im Auge des Betrachters

:slight_smile:

2 Likes

So... vielen Dank allen, die Zeit und Hirnschmalz investiert haben.
Ich habe mich für die Lösung von my_xy_projekt entschieden und Diese dann ein wenig meinen Bedürfnissen angepasst.
Klar, die gewählten Zeiten sind noch im Versuchsstadium, aber ich habe mit verschiedenen Zeiten experimentiert und feststellen können, dass es innerhalb eines Zyklus auch nicht zu mehrfachen Aufrufen kommt, selbst wenn die Pausenzeit ein mehrfaches der Pump- und Ladezeiten ist.

Bestimmt gibt es noch viele, interessante Wege nach Rom, aber dieser ist der Meine :slight_smile:

Ein Vorschlag war eine RTC, die kommt später vlt noch dazu, auch weil ich da ein paar Variablen speichern kann, ohne extra eine SD-Karte nutzen zu müssen.
(Es kommt später noch ein Menu dazu, in welchem ich mit einem KV040 Werte einstelle, z.Zt. stelle ich die Zeiten über Potis an analogen Eingängen ein)
Um das Hochbeet und die Tomaten ausreichend zu gießen reicht vorerst diese Lösung.

Und JA, ich BIN ein Fan aussagekräftiger Variablen-Namen :wink:

Danke allen, besonders my_xy_projekt und schöne Grüße aus dem Bergischen Land.
Michael

const byte O_Laden = 9; // Output SSR
const byte O_Pumpen = 10; // Output IRLZ


unsigned long prev_Millis_Pause = 0;
unsigned long prev_Millis_Pumpe = 0;
unsigned long prev_Millis_Laden = 0;

const unsigned long t_Pause  = 6000; //Zyklus
const unsigned long t_Pumpen = 1500; //Pumpzeit
const unsigned long t_Laden  = 3000; //Ladezeit
const byte Schwelle_Bodenfeuchte = 40;

byte Schritt = 0;
byte Bodenfeuchte = 0;

void setup()
{
  pinMode(O_Laden, OUTPUT);
  pinMode(O_Pumpen, OUTPUT);
}

void loop()
{
  Messen();
  switch (Schritt)
  {
  case 0:
        if (millis() - prev_Millis_Pause >= t_Pause)
        {
        digitalWrite(O_Laden, HIGH);
        if (Bodenfeuchte <= Schwelle_Bodenfeuchte) digitalWrite(O_Pumpen, HIGH); else digitalWrite(O_Pumpen, LOW);
        prev_Millis_Pause = millis();
        prev_Millis_Pumpe = millis();
        prev_Millis_Laden = millis();
        Schritt++;
       }
  break;
  case 1:
        if (millis() - prev_Millis_Pumpe >= t_Pumpen)
        {
        digitalWrite(O_Pumpen, LOW);
        Schritt++;
        }
  break;
  case 2:
        if (millis() - prev_Millis_Laden >= t_Laden)
        {
        digitalWrite(O_Laden, LOW);
        Schritt = 0;
        }
  break;
  }  //---------- Ende switch ----------
}  //---------- Ende Loop ----------

void Messen()
{
  Bodenfeuchte = 39;
}

Erstmal danke für Deine Rückmeldung, die natürlich gleich kritisch beäugt wird.

Die Funktion Messen(); rufst Du ständig in loop auf, willst Du einen Mittelwert bilden? Sonst sollte ein einmaliges Messen in der Schrittkette vor der Auswertung genügen.

DS3231 ist recht genau und benötigt nur I²C zum Anschluß. Die Pins solltest Du also freihalten zur späteren Nutzung.

Kreative Menschen brauchen ja Träume und Ziele, daher könntest Du auch über eine WLAN-Verbindung die Zeit holen und Daten verschicken. Ich mache das derzeit mit meinem Stromzähler und Grafik auf meinem Läppi. Gestärkt von selbstgezogenen Tomaten wäre das eine Programmieraufgabe für den Winter. Ein Einstieg können die Seiten von Fips sein, eventuell mit meiner Anleitung: Einführung zu fipsok.de.

Grüße ins Bergischen Land, mußte erstmal suchen, wo das ist :slightly_smiling_face:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.