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?
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.
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
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.
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...
}
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
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.
@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.