Logikproblem: Stoppuhr mit 2 Tastern und 2 Bedingungen

Hallo,

aktuell arbeite ich an einem Projekt, welches mich seit einiger ZEit beschäftigt.
Ich baue ein Gerät, welches die Zeit vom öffnen eines Ventils bis zu dessen Verschluss misst.

Dabei gibt es folgende Bedingungen:

Situation 1: Schalter A (NC=Normally Closed) geschlossen, Schalter B (NO = Normally Open) ist offen ==> Begrüßungstext auf Stoppuhrdisplay

Situation 2: Schalter A offen, Schalter B offen ==> Stoppuhr startet, LED geht an

Situation 3: Schalter A offen, Schalter B geschlossen ==> Stoppuhr läuft weiter

Situation 4a: Schalter A geschlossen, Schalter B offen ==> Stoppuhr beendet, Resultat wird angezeigt

Situation 4b: Schalter A geschlossen, Schalter B war NICHT geschlossen ==> Anzeige "ungültig"

Diese Abfolge ist sozusagen logisch aufeinander folgend. Situation 3 dient nur dazu um zu verifizieren, dass das Ventil auch ganz geöffnet war.

Nun die Frage: Egal wie ich es drehe und wende, ich bekomme es nicht hin. Habe es mit verschiedenen Optionen probiert. Hat jemand eine Idee für mich, oder ist die Logik einfach nur zu wild?

Im folgenden mein Code:

//OLED Bibliotheken

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define OLED_RESET 4 // not used / nicht genutzt bei diesem Display
Adafruit_SSD1306 display(OLED_RESET);

//Button INT
int ledPin1 = 10;
int buttonApin = 9;
int buttonBpin = 8;
int buttonState1 = 0;
int lastButtonState1 = 0;
int buttonState2 = 0;
int lastButtonState2 = 0;


//Stoppuhr Init
int milisec;
int seconds;
int minutes;



byte leds = 0;
unsigned long start, finished, elapsed;


void setup()   {


  // OLED SETUP
  // initialize with the I2C addr 0x3C / mit I2C-Adresse 0x3c initialisieren
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(15, 1);
  display.print("Trichter");
  display.setCursor(18, 16);
  display.print("2 0 1 9");
  display.display();

  // random start seed / zufälligen Startwert für Random-Funtionen initialisieren
  randomSeed(analogRead(0));

  // BUTTON SETUP
  pinMode(ledPin1, OUTPUT);
  pinMode(buttonApin, INPUT_PULLUP);   //9
  pinMode(buttonBpin, INPUT_PULLUP);  //8
}

#define DRAW_DELAY 118
#define D_NUM 47

int i;

void displayResult() {
  int s, ms;
  unsigned long over;
  elapsed = finished - start;
  over = elapsed % 3600000;
  over = over % 60000;
  s = int(over / 1000);
  ms = over % 1000;
  display.print(s);
  display.print(",");
  display.print(ms);
  display.print("s");
  display.display();

}
void loop() {
  display.clearDisplay();
  buttonState1 = digitalRead(buttonApin);
  buttonState2 = digitalRead(buttonBpin);

  if (buttonState1 == LOW && buttonState2 == HIGH )
  { start = millis();
    display.invertDisplay(true);
    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(20, 1);
    display.print("TEXTbla");
    display.setCursor(40, 15);
    display.setTextSize(2);
    display.print("GO!");
    display.display();

    digitalWrite(ledPin1, HIGH);

    lastButtonState1 = buttonState1;
  }

  if (buttonState2 == LOW)
  { lastButtonState2 = buttonState2;
  }

  if (buttonState1 == HIGH && ButtonState2 == HIGH && lastButtonState1 == LOW && lastButtonState2 == LOW)
  {
    display.invertDisplay(false);
    finished = millis();
    delay(100);
    display.setCursor(1, 4);
    display.setTextSize(3);
    displayResult();
    display.display();

    digitalWrite(ledPin1, LOW);




  }





}

Vielen Dank für etwaige Hilfe :slight_smile:

Hi

Du willst eine State-Maschine bauen.
Du hast vier mögliche Bedingungen:
00
01
10
11
Du musst 'nur' schauen, von welchem Status Du zu welchem Status wechselst und darauf reagieren.

Auch würde ich mir Hilfsvariablen erstellen, damit ich nicht mit LOW und HIGH durcheinander komme.
So hätte ich

V1AUF=LOW;
V1ZU=!V1AUF; //negiert den Zustand von V1AUF
V2AUF=HIGH;
V2ZU=!V2AUF;

Den aktuellen Status würde ich mir 'berechnen' lassen

if (digitalRead(Ventil1)==V1AUF){
  if (digitalRead(Ventil2)==V2AUF){
    statusneu=0;//AUF-AUF
  }else{
    statusneu=1;//AUF-ZU
  }
}else{
  if (digitalRead(Ventil2)==V2AUF){
    statusneu=2;//ZU-AUF
  }else{
    statusneu=3;//ZU-ZU
  }
}

Im Code nehme ich eine switch(status) Verzweigung und prüfe in den einzelnen Status (jupp, die Mehrzahl ist ebenfalls Status), OB eine Status-Änderung vorliegt und wenn Ja, was beim Wechsel von Diesem zum Nächsten verarbeitet werden muß.
Hinter dieser switch-Anweisung 'status=statusneu;' um den neuen Status zu übernehmen.

Zum Thema State-Maschine:combie's "Standardantwort zu Ablaufsteuerungen"

MfG

Hallo,

mit deinem 4a und 4b Zustand komme ich nicht klar. Du hast 2 Schalter, also 2 Bit. Damit sind 4 Zustände unterscheidbar. Einen 5. gibts dabei nicht. Das müßtest du näher erklären.

Doc_Arduino:
Hallo,

mit deinem 4a und 4b Zustand komme ich nicht klar. Du hast 2 Schalter, also 2 Bit. Damit sind 4 Zustände unterscheidbar. Einen 5. gibts dabei nicht. Das müßtest du näher erklären.

Das musst du im zeitlichen Ablauf sehen: 4b kann nur eintreten, wenn 3 NICHT vorher eingetreten ist.

postmaster-ino:
Hi

Du willst eine State-Maschine bauen.
Du hast vier mögliche Bedingungen:
00
01
10
11
Du musst 'nur' schauen, von welchem Status Du zu welchem Status wechselst und darauf reagieren.

Auch würde ich mir Hilfsvariablen erstellen, damit ich nicht mit LOW und HIGH durcheinander komme.
So hätte ich

V1AUF=LOW;

V1ZU=!V1AUF; //negiert den Zustand von V1AUF
V2AUF=HIGH;
V2ZU=!V2AUF;



Den aktuellen Status würde ich mir 'berechnen' lassen


if (digitalRead(Ventil1)==V1AUF){
 if (digitalRead(Ventil2)==V2AUF){
   statusneu=0;//AUF-AUF
 }else{
   statusneu=1;//AUF-ZU
 }
}else{
 if (digitalRead(Ventil2)==V2AUF){
   statusneu=2;//ZU-AUF
 }else{
   statusneu=3;//ZU-ZU
 }
}



Im Code nehme ich eine switch(status) Verzweigung und prüfe in den einzelnen Status (jupp, die Mehrzahl ist ebenfalls Status), OB eine Status-Änderung vorliegt und wenn Ja, was beim Wechsel von Diesem zum Nächsten verarbeitet werden muß.
Hinter dieser switch-Anweisung 'status=statusneu;' um den neuen Status zu übernehmen.

Zum Thema State-Maschine:[combie's "Standardantwort zu Ablaufsteuerungen"](http://forum.arduino.cc/index.php?topic=586959.msg3993031#msg3993031)

MfG

Das ist auf jedenfall nen sehr interessanter Ansatz! Die Frage ist jedoch, ob mir das aktuell hilft, da ich ja zu jedem Status 10,11... etc. genau erfassen muss ob der Schalter auf LOW oder HIGH liegt. Oder ist !V1AUF (zum Beispiel) automatisch HIGH?

MfG, Marius

Hallo,

das sind deine beschriebenen Zustände. Fällt dir etwas auf?
Du hast 3x den gleichen Zustand beschrieben.
"Stoppuhr läuft weiter" Was soll das bewirken? Ich rate ins Blaue, Zwischenzeitnahme?

A B Situation Aktion
geschlossen offen 1 Begrüßungstext
offen offen 2 Stoppuhr startet
offen geschlossen 3 Stoppuhr läuft weiter
geschlossen offen 4 Stoppuhr beendet
geschlossen offen 5 Anzeige "ungültig"

Doc_Arduino:
Hallo,

das sind deine beschriebenen Zustände. Fällt dir etwas auf?
Du hast 3x den gleichen Zustand beschrieben.
"Stoppuhr läuft weiter" Was soll das bewirken? Ich rate ins Blaue, Zwischenzeitnahme?

A B Situation Aktion
geschlossen offen 1 Begrüßungstext
offen offen 2 Stoppuhr startet
offen geschlossen 3 Stoppuhr läuft weiter
geschlossen offen 4 Stoppuhr beendet
geschlossen offen 5 Anzeige "ungültig"

Hallo, ja es sind drei gleiche Zustände. Zuerst ist der Zustand 1 aktiv. Bei öffnen des Hebels wird durch einen Schalter Situation 2 aktiviert, nur bei kompletter Öffnung des Hebels (Sit. 3) ist Situation 4 aktiv, andernfalls wird Situation 5 ausgelöst.

Hi

Das ! negiert den Wert.
Wenn ich für V1AUF=HIGH festgelegt habe, wird durch das !V1AUF der 'gegenpolige Zustand' für den anderen Zustand gesetzt.
SO ist der Sketch etwas wartungsfreundlicher, sollte sich was an den Schaltern ändern oder (egal wer) den Sketch für Sich benutzen wollen.

Dort gibst Du an, was der Arduino bei Zustand 'AUF' sieht.
Da ich in den IFs nur den AUF-Zustand benutze, könnte man sich die Anderen auch sparen.

Du musst NICHT erfassen, ob bei STatus x/y/z einer der Schalter offen ist - DAS ist ja bereits Dein Status (bzw. statusneu).
Durch die beiden Werte status und statusneu 'siehst' Du, was gerade passiert ist.
Wenn vorher beide Ventile ZU waren und jetzt ist Ventil 1 AUF, dann hat der Status von ZU-ZU nach AUF-ZU gewechselt.
Was dabei passieren soll überlasse ich Deiner Phantasie.

Auch kannst Du den Status (bzw. bei Allen) unterschiedlich reagieren, je nachdem, ob die Messung gerade erst gestartet wurde 'Hallo da draußen', oder ob die Messung ungültig ist - wobei Das ja eine definierte Status-Änderung ist ... AUF-AUF -> ZU-AUF = Fehler (fehlendes ZU bei Ventil 2).

MALE Dir Das auf, was wann passieren soll.
Bei 2 Ventilen und pro Ventil 2 Stellungen ergeben sich halt nur 4 Möglichkeiten - Deine Status.
Überlege, was passieren muß, wenn Du von ZU-ZU zu den drei möglichen Status wechselst.
Das machst Du bei allen vier Möglichkeiten.
'Ablauf-Diagramm' könnte Dir Da helfen.

MfG

Edit: AUS->AUF

Hallo,
ich vermute bei Deinen beiden Schaltern handet es sich um die Endlagen des Ventils auf und zu. Messen willst Du die Zeit wie lange es nicht geschlossen war. Also von verlassen der Endlage gechlossen über offen bis wieder geschlossen.

Du schreibst es funktioniert nicht, was klappt nicht ? Bekommst du die Uhr nicht zum laufem , hält sie nicht mehr an ?

Eigendliche versuchst Du mehrere Aufgaben zu verwurschlteln, ich würde das trenen.

  1. Aufgabe Zeitmessung das geht nach deinen Bedingungen mit dem Schalter A
    Schalter A offen Stoppuhr läuft , schalter A geschnlossen Zeit wird angezeigt.

  2. Kontrolle der Logik
    Dazu kannst Du eine Laufzeit überwachung einbauen, d.h beide Schalter nicht belegt darf nicht viel lännger als die normale Fahrzeit dauern, ansonsten Fehler.

ich denke das sollte reichen.

Heinz

Kurzes Update:

Habe es mittlerweile gelöst bekommen, jedoch auf eine etwas andere Weise, wobei mir jedoch vor allem die Idee mit der State Maschine sehr geholfen hat.
Konnte das Design sogar nun auf nur einen Knopf reduzieren.

Hier mein nun funktionierender Code:

//OLED Bibliotheken

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define OLED_RESET 4 // not used / nicht genutzt bei diesem Display
Adafruit_SSD1306 display(OLED_RESET);

//Button INT
int ledPin1 = 10;
int ledPin2 = 6;
int buttonApin = 9;
int buttonBpin = 8;
int buttonState1 = 0;
int lastButtonState1 = 0;
int buttonState2 = 0;
int lastButtonState2 = 0;

//Stoppuhr Init
int milisec;
int seconds;
int minutes;



byte leds = 0;
unsigned long start, finished, elapsed;


void setup()   {


  // OLED SETUP
  // initialize with the I2C addr 0x3C / mit I2C-Adresse 0x3c initialisieren
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(15, 1);
  display.print("Trichter");
  display.setCursor(18, 16);
  display.print("2 0 1 9");
  display.display();

  // random start seed / zufälligen Startwert für Random-Funtionen initialisieren
  randomSeed(analogRead(0));

  // BUTTON SETUP
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(buttonApin, INPUT_PULLUP);
  pinMode(buttonBpin, INPUT_PULLUP);


}

#define DRAW_DELAY 118
#define D_NUM 47

int i;

void displayResult() {
  int s, ms;
  unsigned long over;
  elapsed = finished - start;
  over = elapsed % 3600000;
  over = over % 60000;
  s = int(over / 1000);
  ms = over % 1000;
  display.print(s);
  display.print(",");
  display.print(ms);
  display.print("s");
  display.display();

}
void loop() {
  display.clearDisplay();
  buttonState1 = digitalRead(buttonApin); // NC
  buttonState2 = digitalRead(buttonBpin);  //NO


  if (digitalRead(buttonBpin) == HIGH && lastButtonState1 == 0)
  { start = millis();
    display.invertDisplay(true);
    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(20, 1);
    display.print("Trichter 2019");
    display.setCursor(40, 15);
    display.setTextSize(2);
    display.print("GO!");
    display.display();

    digitalWrite(ledPin1, HIGH);
    digitalWrite(ledPin2, LOW);

    lastButtonState1 = 1;
  }


  if (digitalRead(buttonBpin) == LOW && lastButtonState1 != 0)
  {
    display.invertDisplay(false);
    finished = millis();
    delay(100);
    display.setCursor(1, 4);
    display.setTextSize(3);
    displayResult();
    display.display();

    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin1, LOW);

   delay(100000);  //Dient dazu das Ergebnis auf dem Display zu halten

Danke allen :slight_smile:

Hallo,

schön das es erstmal funktioniert. Nur das du dir mit den Delays den Controller bzw. die Bedienbarkeit lahm legst ist dir bewusst? Das letzte Delay macht ihn satte 100s unbedienbar.

Da Programm in #9 und Beschreibung in #0 nicht übereinstimmen, habe ich mich an #0 orientiert. Leider ist nicht definiert, was bei einem nicht ganz geöffnetem Ventil passieren soll, weshalb das Programm an dieser Stelle blockiert. Ebenfalls ist der Übergang zur nächsten Messung nicht beschrieben. Ein endlicher Automat mit switch/case bildet den Programmkern:

//OLED Bibliotheken
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 99 // not used / nicht genutzt bei diesem Display
Adafruit_SSD1306 display(OLED_RESET);

const byte ledPin1 = 10;
const byte ledPin2 = 6;
const byte buttonApin = 9;
const byte buttonBpin = 8;

bool buttonAstate;
bool lastbuttonAstate;
bool buttonBstate;
bool lastbuttonBstate;
unsigned long zeit;
enum {WARTEN, START, OFFEN, ERGEBNIS};
byte schritt = WARTEN;

void setup()   {
  // OLED SETUP
  // initialize with the I2C addr 0x3C / mit I2C-Adresse 0x3c initialisieren
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  displayInit();

  // BUTTON SETUP
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(buttonApin, INPUT_PULLUP);
  pinMode(buttonBpin, INPUT_PULLUP);
}
void displayInit() {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(15, 1);
  display.print("Trichter");
  display.setCursor(18, 16);
  display.print("2 0 1 9");
  display.display();
}
void displayGo() {
  display.clearDisplay();
  display.invertDisplay(true);
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(20, 1);
  display.print("Trichter 2019");
  display.setCursor(40, 15);
  display.setTextSize(2);
  display.print("GO!");
  display.display();
}

void displayResult() {
  unsigned int s = zeit / 1000;
  unsigned int ms = zeit % 1000;
  display.invertDisplay(false);
  display.clearDisplay();
  display.setCursor(1, 4);
  display.setTextSize(3);
  display.print(s);
  display.print(",");
  display.print(ms);
  display.print("s");
  display.display();
}

void loop() {
  buttonAstate = digitalRead(buttonApin); // NC
  buttonBstate = digitalRead(buttonBpin);  //NO
  switch (schritt)
  {
    case WARTEN:
      if (digitalRead(buttonApin))
      { zeit = millis();
        displayGo();
        digitalWrite(ledPin1, HIGH);
        digitalWrite(ledPin2, LOW);
        schritt = START;
      }
      break;
    case START:
      if (!digitalRead(buttonBpin))
      {
        schritt = OFFEN;
      }
      break;
    case OFFEN:
      if (!digitalRead(buttonApin))
      {
        zeit = millis() - zeit;
        displayResult();
        digitalWrite(ledPin1, LOW);
        digitalWrite(ledPin2, HIGH);
        schritt = ERGEBNIS;
      }
      break;
    case ERGEBNIS:
      delay(5000);  //Dient dazu das Ergebnis auf dem Display zu halten
      displayInit();
      schritt = WARTEN;
      break;
  }
}

Doc_Arduino:
Hallo,

schön das es erstmal funktioniert. Nur das du dir mit den Delays den Controller bzw. die Bedienbarkeit lahm legst ist dir bewusst? Das letzte Delay macht ihn satte 100s unbedienbar.

Hallo,

da das Gerät immer nach beenden der Stoppuhr komplett ausgeschaltet wird, ist das nicht so schlimm.