Probleme mit mehreren Timer Abfragen die auf einem Event aufbauen

Hallo Gemeinde,
ich sitze hier vor einem Kleinem Problem was normal nicht so schwer zu lösen sein sollte.

Leider versuche ich über die Timer Funktion verschiedene zustände abzufragen und eine Meldung auszugeben, nur leider Funktioniert das nicht so wie ich es gerne hätte.

Ich habe ein Automatikventil was verschiedene Fehlerzustände ausgibt diese werden von 1-6 sec gehandhabt.
Nun war mein Ansatz gewesen, ich nehme einen Timer und Frage die Dauer wielange der Eingang "VENTIL_ERROR" auf HIGH liegt abund lasse dieses mit der Timer Funktion ausgeben.
1.1 sec Fehler 1
.
.
6 sec Fehler 6

bool error() {
  static unsigned long errZ = 0;
  if (digitalRead(VENTIL_ERROR) == HIGH) {
    errZ = 0;
    return false;
  }
  // Wir setzten die Startzeit und geben false zurück
    if (errZ == 0) {
    errZ = millis();
    return false;
  }
  // Gib zurück, ob welcher ERROR Status anliegt
  if (millis() - errZ >= 1100); {
    Serial.println(F("Error: Stromausfall am Ventil"));
  }
  if (millis() - errZ >= 2100); {
    Serial.println(F("Error: Positions Error"));
  }
  if (millis() - errZ >= 3100); {
    Serial.println(F("Error: Strom Error"));
  }
  if (millis() - errZ >= 4100); {
    Serial.println(F("Error: Strom Errorstate"));
  }
  if (millis() - errZ >= 5100); {
    Serial.println(F("Error: Einzelner Hall Errorstate"));
  }
  if (millis() - errZ >= 6000); {
    Serial.println(F("Error: Hall Errorstate"));
  }
}

Leider scheint dies aber der Falsche Ansatz zu sein da ich keine Ausgabe im Seriellen Monitor bekomme.
Daher meine Frage ob mir da jemand den Richtigen Ansatz dazu geben kann?

vielen Dank für eure Hilfe.

Hi,

der Code hat keine chanche in die Auswertung zu kommen, da errZ IMMER 0 ist.

Ist der Fehlerausgang aktiv High oder aktiv Low ?

Wie soll man denn überhaupt da hinkommen?
Du setzt bei Einsprung

Damit springst Du im zweiten if zurück.

cu

Da war jemand schneller ... :grinning:

Da sind noch bei der Auswertung die Simikolons zu viel...

ich habe hier mal enen Gegenentwurf skizziert

bool error() {
  static bool LastErrorState = false;
  static unsigned long errZ = 0;
  bool errState = (digitalRead(VENTIL_ERROR) == HIGH);
  
  if (errState != LastErrorState) {
        // nur bei Signalflanken aktiv werden
	if (errState) {
		// Start des Fehlersignals
		errZ = millis();
	} else {
		long errEnd = millis();
		if (errEnd - errZ >= 6000) {
			// Große Zeiten zu erst prüfen
			Serial.println(F("Error: Hall Errorstate"));
		} else if (errEnd - errZ >= 5100) {
			// else if damit nur eine Fehlermeldung kommt
		}
		/// ... usw
	}
       LastErrorState = errState;
  }
}

Danke für eure Antworten, ich werde das einmal in Ruhe durchgehen.
@Arduino-zwei, der Fehlereingang ist dann auf HIGH und soll nur ein Seriell Print ausgeben. Später wird diese Meldung via Modbus übertragen.

Hi,

leider komme ich da nicht weiter mit der Sache, ich bekomme es nicht hin das er mir eine Ausgabe bringt.

ich wollte es halt so realisieren wie bei einer SPS Programmierung, aber das scheint hier nicht so zu Funktionieren wie ich es mir gedacht habe.
bei der SPS ist es einfacher das ganze zu machen,

	IF (ftrig8.Q) THEN
		IF (lasttime < 1.1)    THEN err_6WVT:=1;  // Stromausfall, Kabelbruch
		ELSIF (lasttime < 2.1) THEN err_6WVT:=2;  // Mehrere Positionen gleichzeitig angesteuert, Spannungsversorgung der Eingänge nicht ausreichend
		ELSIF (lasttime < 3.1) THEN err_6WVT:=3;  // Aquastar nicht fest genug auf Ventil angeschraubt, Pumpe nicht ausgeschaltet, Ventil reinigen
		ELSIF (lasttime < 4.1) THEN err_6WVT:=4;  // Aquastar nicht fest genug, oder zu fest auf Ventil angeschraubt, Kabelbruch, Motor defekt, Ventil defekt, Pumpe nicht ausgeschaltet, Gesamtwassersäule >3m, Einer der Motoren ist falsch angeschlossen (rt bei rot und bl bei blauem Kabel
		ELSIF (lasttime < 5.1) THEN err_6WVT:=5;  // Elektronik defekt
		ELSIF (lasttime < 6.1) THEN err_6WVT:=6;  // Elektronik defekt, Versorgungspannung nicht im zulässigen Bereich, Motor defekt, Kabelbruch, Endschalter nicht angeschlossen, Motor nicht angeschlossen
		ELSE err_6WVT:=0; // kein Fehler

das wollte ich halt im arduino nanoESP32 auch so umsetzen. Daher seit nachsichtig mit mir, ich mache das zu selten mit einem Arduino

jetzt musst du mir helfen... was steckt hinter

das ist ein Trigger der auf Fallende Flanke reagiert.
F-TRIG genannt.

Dann müsste ja auch noch ein Programmteil mit

IF (rtrig8.Q) THEN
     lasttime = jetzt()

sein...

ja den gibt es auch

IF (E_IN or ECODE) THEN
	SysTaskActualCycleTime(SysTaskActualCycleTime => actual_time);
	lasttime := lasttime + TO_LREAL(actual_time);
	ECODE := TRUE;
	rtrig8( CLK := E_IN);
	IF (rtrig8.Q) THEN lasttime := 0; END_IF; // steigende flanke last time zurücksetzen
	ftrig8( CLK := E_IN);

Err := 2#0000; // Es existiert kein Fehler !! nochfolgende vergleiche löschen das bit 15 0x8
	IF (ftrig8.Q) THEN
		IF (lasttime < 1.1)    THEN err_6WVT:=1;  // Stromausfall, Kabelbruch
		ELSIF (lasttime < 2.1) THEN err_6WVT:=2;  // Mehrere Positionen gleichzeitig angesteuert, Spannungsversorgung der Eingänge nicht ausreichend
		ELSIF (lasttime < 3.1) THEN err_6WVT:=3;  // Aquastar nicht fest genug auf Ventil angeschraubt, Pumpe nicht ausgeschaltet, Ventil reinigen
		ELSIF (lasttime < 4.1) THEN err_6WVT:=4;  // Aquastar nicht fest genug, oder zu fest auf Ventil angeschraubt, Kabelbruch, Motor defekt, Ventil defekt, Pumpe nicht ausgeschaltet, Gesamtwassersäule >3m, Einer der Motoren ist falsch angeschlossen (rt bei rot und bl bei blauem Kabel
		ELSIF (lasttime < 5.1) THEN err_6WVT:=5;  // Elektronik defekt
		ELSIF (lasttime < 6.1) THEN err_6WVT:=6;  // Elektronik defekt, Versorgungspannung nicht im zulässigen Bereich, Motor defekt, Kabelbruch, Endschalter nicht angeschlossen, Motor nicht angeschlossen
		ELSE err_6WVT:=0; // kein Fehler
	 END_IF;		
	END_IF;
	
	IF (err_6WVT = 0) THEN
		// kein Fehler, weiterlaufen
	ELSIF (err_6WVT = 2) THEN
		// probiere neu zu positionieren
		Z:=1;
		ECODE 	  := FALSE;
		block 	  := FALSE;
		Pumpe_FI  := FALSE;
		Pumpe_BW  := FALSE;
		fil_hand  := FALSE;
	ELSE // Keine Aktionen mehr ausführen anlage auf Störung setzen
		Err := err_6WVT;
	END_IF;
	// zustand welche können das sein, und was ist der vorherige zustand ?
	// vorherigen Zustand setzen, 6 s Fehler v_reset auf 3 leitungen und hier raus, die anlage neu hier reinlaufen lassen
	// zähler wie oft versucht (50 ms takt) wichtig, 6s für v_reset abwarten frühestens nach 6 s wenn der fehler noch besteht dauerhafte fehler ausgabe auf error
	return; // hier enden !!! 
 END_IF;
END_IF;

ich hoffe so hast du einen besseren überblick
Z1 ist der Zustand den das Ventil anfahren soll. das wäre beim Automatikventil die Filterpos. da diese die Grundstellung ist. Aber mir geht es ja nur um den Teil der Ausgabe der Fehler.

Den SPS-Code kann man fast 1:1 in C übertragen:

void loop() {
	// IF (E_IN or ECODE) THEN
	// 	SysTaskActualCycleTime(SysTaskActualCycleTime => actual_time);
	// 	lasttime := lasttime + TO_LREAL(actual_time);
	//	ECODE := TRUE;
	int err_6WVT=0; // vorbesetzen der Fehlervariable mit "kein Fehler"

	//	rtrig8( CLK := E_IN);
	//	IF (rtrig8.Q) THEN lasttime := 0; END_IF; // steigende flanke last time zurücksetzen
	bool errState = (digitalRead(VENTIL_ERROR) == HIGH);
	if (LastErrorState != errState) { // Eine Veränderung am Pin also eine Flanke
		if (errState) { // Signal ist jetzt High also war es eione steigende Flanke
			startTime = millis(); // anstelle jeden Zyklus die vergangene Zeit hochzuzählen merken wir und die Startzeit.
		}
	}
	
	//	ftrig8( CLK := E_IN);
	//	Err := 2#0000; // Es existiert kein Fehler !! nochfolgende vergleiche löschen das bit 15 0x8
	//	IF (ftrig8.Q) THEN
	if (LastErrorState != errState) { // Eine Veränderung am Pin also eine Flanke
		if (!errState) { // Signal ist jetzt LOW also war es eine fallende Flanke
			unsigned long endTime = millis(); // Zeit fixieren, da millis sich während der auswertung ändern könnte
			//		IF (lasttime < 1.1)    THEN err_6WVT:=1;  // Stromausfall, Kabelbruch
			if ( (endTime-startTime) < 1100) { err_6WVT=1; }
			//	ELSIF (lasttime < 2.1) THEN err_6WVT:=2;  // Mehrere Positionen gleichzeitig angesteuert, Spannungsversorgung der Eingänge nicht ausreichend
			else if ( (endTime-startTime) < 2100) { err_6WVT=2; }
			//	ELSIF (lasttime < 3.1) THEN err_6WVT:=3;  // Aquastar nicht fest genug auf Ventil angeschraubt, Pumpe nicht ausgeschaltet, Ventil reinigen
			else if ( (endTime-startTime) < 3100) { err_6WVT=3; }
			//	ELSIF (lasttime < 4.1) THEN err_6WVT:=4;  // Aquastar nicht fest genug, oder zu fest auf Ventil angeschraubt, Kabelbruch, Motor defekt, Ventil defekt, Pumpe nicht ausgeschaltet, Gesamtwassersäule >3m, Einer der Motoren ist falsch angeschlossen (rt bei rot und bl bei blauem Kabel
			else if ( (endTime-startTime) < 4100) { err_6WVT=4; }
			//	ELSIF (lasttime < 5.1) THEN err_6WVT:=5;  // Elektronik defekt
			else if ( (endTime-startTime) < 5100) { err_6WVT=5; }
			//	ELSIF (lasttime < 6.1) THEN err_6WVT:=6;  // Elektronik defekt, Versorgungspannung nicht im zulässigen Bereich, Motor defekt, Kabelbruch, Endschalter nicht angeschlossen, Motor nicht angeschlossen
			else if ( (endTime-startTime) < 6100) { err_6WVT=6; }
			//	ELSE err_6WVT:=0; // kein Fehler
			else { err_6WVT=0; }
		//	 END_IF;		
		}
	//	END_IF;
	}
	LastErrorState = errState; // letzten Stand merken um eine Flanke zu erkennen
	
	// was auch immer du mit err_6WVT machen willst...
}

Ich hätte noch folgende Variante:

#include <digitalWriteFast.h>

constexpr uint8_t MEASURE_PIN {4};

//
// Klasse zum Messen der Zeitabstands zwischen zwei Flankenwechseln
// Mit HIGH initialisiert -> von Low nach High - Messung - High nach Low).
// Mit LOW initialisiert ->  von HIGH nach Low - Messung - Low nach HIGH).
// HIGH ist default.
//
class Measure {
public:
  Measure(uint8_t pin_, uint8_t level_ = HIGH) : pin {pin_}, level {level_} {}
  void begin() { pinMode(pin, INPUT); }
  uint32_t doTheMeasurment() {
    delay(20); // primitiv Entprellung
    pinState = (digitalReadFast(pin) == level);
    if (pinState != lastPinState) {
      lastPinState = pinState;
      switch (pinState) {
        case true:
          timeStampStart = millis();
          duration = 0;
          Serial.println("Start");
          break;
        default: duration = millis() - timeStampStart; Serial.println("Stop");
      }
    }
    return duration;
  }
  void clearResult() { duration = 0; }

private:
  uint8_t pin;
  uint8_t level;
  uint32_t timeStampStart {0};
  uint32_t duration {0};
  bool pinState {false};
  bool lastPinState {false};
};
Measure mmt {MEASURE_PIN};

void setup() {
  Serial.begin(115200);
  mmt.begin();
}

void loop() {
  uint32_t duration = mmt.doTheMeasurment();
  if (duration != 0) {
    switch (duration) {
      case 0 ... 1100: Serial.println("Error 1"); break;
      case 1101 ... 2100: Serial.println("Error 2"); break;
      case 2101 ... 3100: Serial.println("Error 3"); break;
      case 3101 ... 4100: Serial.println("Error 4"); break;
      case 4101 ... 5100: Serial.println("Error 5"); break;
      case 5101 ... 6100: Serial.println("Error 6"); break;
      default: Serial.println("Zeitspanne > 6 Sekunden!"); break;
    }
    mmt.clearResult();
  }
}

Zum Ausprobieren, den Button (der prellt nicht) entsprechend lange drücken.

1 Like

Danke dir, ich werde es ausprobieren heute abend, ich gebe dir dann bescheid mit dem ergebniss.

gruß

Danke dir. diese Variante Funktioniert nun auch. Leider stimmen die millis nicht mit den sec überein.

ich kann nicht genau sagen woran es liegt aber 6000 millis sind nicht 6 sec bei mir und es führt dadurch zu einer falschen Anzeige.
ich habe mir die "errState" ausgeben lassen und da waren es schon 11 sec, wobei laut handbuch des ventils aber nur 6 sec sind.

wenn ich das ganze ohne einen vTaskDelay(20); // 20ms warten
laufen lasse zeigt er mehrer sachen an .

14:06:51.468 -> Fehler 1
14:07:01.451 -> Kein Fehler

warte ich aber die 20ms dann passt die Ausgabe nach dem schaltverhalten vom Ventil. Ich muss nur die Zeiten dementsprechend anpassen.

14:02:37.212 -> Fehler 6
14:02:48.206 -> Fehler 6

wie du sehen kannst sind es auch 11 sec laut serieller Ausgabe.

Aber danke für deine Mühe und deiner sehr guten Hilfe

Auch dir ein Danke für deine Mühe. ich habe mir das ganze Angeschaut, aber noch nicht weiter getestet.

Suchst Du sowas hier?

void error()
{
  static unsigned long errStart = millis();     // deinition & initialisierung
  static bool ausgeloest = false;               // Merker
  if (digitalRead(VENTIL_ERROR) == HIGH)        // Auslöser
  {
    if (!ausgeloest)                            // War noch nicht ausgelöst
    {
      errStart = millis();                        // Startzeit merken
      ausgeloest = true;                          // merker setzen
    }
  }
  else                                          // nicht mehr ausgelöst
  {
    if (ausgeloest)                             // merker war gesetzt
    {
      switch (millis() - errStart)              // Auswahl, was passiertist
      {
        case 0 ... 1100:
          Serial.println(F("Error: Stromausfall am Ventil"));
          break;
        case 1101 ... 2100:
          Serial.println(F("Error: Positions Error"));
          break;
        case 2101 ... 3100:
          Serial.println(F("Error: Strom Error"));
          break;
        case 3101 ... 4100:
          Serial.println(F("Error: Strom Errorstate"));
          break;
        case 4101 ... 5100:
          Serial.println(F("Error: Einzelner Hall Errorstate"));
          break;
        default:  // Alles über 5100
          Serial.println(F("Error: Hall Errorstate"));
          break;
      }
      ausgeloest = false;
    }
  }
}

Wenn das zu messende Signal prellt, dann muss noch eine "Entprellung" in den Code. Bei allen Beispielen hier. Sonst gibt es in der Tat falsche Ergebnisse.

Das auch.

@luzie
Ich hätte ja gerne gewusst, was das auslösende Ereignis ist und auf was gezählt werden soll.
Weil mit den returns in Deinem Code kann das nicht sauber kompiliert haben und der Rest war eh nur raten.

Auch auf die Gefahr hin, dass das nicht die Lösung ist, habe ich meinen ersten Vorschlag etwas aufgebohrt und eine Entprellung eingebaut, falls das Signal, welches gemessen werden soll, beim Flankenwechsel prellt.

#include <digitalWriteFast.h>
#include <Streaming.h>
Print &cout {Serial};

constexpr uint8_t MEASURE_PIN {4};
//
// Class for measuring the time interval between two edge changes
// Initialized with HIGH -> from low to high - measurement - high to low).
// Initialized with LOW -> from HIGH to Low - measurement - Low to HIGH).
// HIGH is default.
//
class SptMeasurement {  // spt -> signal propagation time
public:
  SptMeasurement(uint8_t pin_, uint8_t activeState_ = HIGH) : pin {pin_}, activeState {activeState_} {}
  void begin() {
    pinMode(pin, INPUT);
    lastPinState = actPinState = !activeState;
  }
  uint32_t doTheMeasurment();
  bool checkSignalChange();
  void setDebounceTime(uint16_t dbt) { debounceTime = dbt; }
  void clearResult() { duration = 0; }

private:
  uint8_t pin;
  uint8_t activeState;
  uint32_t timeStampDebounce {0};
  uint16_t debounceTime {5};
  uint8_t actPinState;
  uint8_t lastPinState;
  uint32_t timeStampStart {0};
  uint32_t duration {0};
};

bool SptMeasurement::checkSignalChange() {
  bool signalHasChanged = false;
  uint8_t pinState = digitalReadFast(pin);
  if (pinState != lastPinState) {   // Restart the debounce timer with every edge change
    lastPinState = pinState;
    timeStampDebounce = millis();
  } else if (pinState != actPinState && (millis() - timeStampDebounce >= debounceTime)) {
    // No more edge changes within the debounce time but the pinState is permanently unequal to the
    // previous one before the test.
    actPinState = pinState;
    signalHasChanged = true;
  }
  return signalHasChanged;
}

uint32_t SptMeasurement::doTheMeasurment() {
  if (checkSignalChange() == true) {   // Signal has been changed from low to high or vice versa
    if (actPinState == activeState) {
      timeStampStart = millis();
      duration = 0;
      cout << F("Start\n");
    } else {
      duration = millis() - timeStampStart;
    }
  }
  return duration;
}
// Class definition end //////////////////////////////////////////////////////////////

SptMeasurement spt {MEASURE_PIN};   // Create new Object

void setup() {
  Serial.begin(115200);
  spt.begin();   // init Object
}

void loop() {
  uint32_t duration = spt.doTheMeasurment();
  if (duration != 0) {
    cout << F("Stop: ") << duration << " ms: ";
    switch (duration) {
      case 0 ... 1100: cout << F("Error 1\n"); break;
      case 1101 ... 2100: cout << F("Error 2\n"); break;
      case 2101 ... 3100: cout << F("Error 3\n"); break;
      case 3101 ... 4100: cout << F("Error 4\n"); break;
      case 4101 ... 5100: cout << F("Error 5\n"); break;
      case 5101 ... 6100: cout << F("Error 6\n"); break;
      default: cout << F("Zeitspanne > 6 Sekunden!"); break;
    }
    spt.clearResult();
  }
}

Laut Datenblatt gibt es 6 verschiedene Zustände die per Relay ausgegeben werden. Es gibt dir dann ein HIGH Level aus und liegt eben für
die Zeit von 1-6 sec an. wenn ich nun < 1.1 sec abfrage habe ich das erste ereignis genauso wenn wenn <2.1 sec u.s.w abfrage. so ist gewährleistet das ich die richtige zeit erhalte. Deswegen auch die Fallende flanke was ihr entprellen nennt denke ich mal. Solange das signal aniegt wird mitgezählt. überschreite ich die 1.1 sec und bleibe unter die 2 habe ich eben die 1 u.s.w.

in der SPS läuft das nun seit 5 Jahren und zeigt auch die Richtigen zustände an. aber da ich von diesem RevPI der keine echte SPS ist und da noch Raspian System darunter liegt was ich pflegen muss möchte ich von diesem weg und habe ich mich halt für den ESP32 entschieden. bei diesem ändert sich nicht viel vieleicht mal die Firmware, aber sonst nichts großes.

1 Like