Go Down

Topic: Timer mit Button und zwei Phasen (Read 139 times) previous topic - next topic

sandra149

Jul 07, 2020, 01:52 pm Last Edit: Jul 07, 2020, 02:04 pm by sandra149
Hallo zusammen,

ich bin blutige Anfängerin beim Thema Programmierung und Arduino und beiße mir beim Thema Timer mit verschiedenen Phasen gerade die Zähne aus. Ich habe mittlerweile so viele verschiedene Codes gelesen, die mit Timern arbeiten, aber andere Hardware verbaut haben als ich, und die Variablen verwirren mich so sehr, dass ich es nicht schaffe, es auf mein Projekt zu übertragen. Ich wäre euch wirklich sehr dankbar, wenn ihr mir bitte weiterhelfen könntet.

Die Hardware ist fertig und funktioniert. Ich verwendet einen Arduino Nano, einen Ultraschallsensor, einen Buzzer, einen Neopixel-Stick mit 8 Neopixels und einen Button.

Mein Grundkonzept kurz erklärt: Mein Gerät soll den Arbeiter am Bildschirm daran erinnern, dass er min. 50 cm Abstand zum Bildschirm haben sollte, und alle 20 Minuten für 2 Minuten in die Ferne schauen soll (Stichwort Augenstress mindern).

Das Gerät wird auf dem Bildschirm platziert. Betätigt man den Button, wird die "Bildschirmphase" gestartet und der 20-Minuten-Timer fängt an. Die Abstandsmessung zum Bildschirm soll nur in dieser Phase stattfinden (die funktioniert schon). Nach 20 Minuten wird die "PausePhase" gestartet. Die Neopixels sollen kurz aufleuchten und der Buzzer gibt einen Signalton ab. Nach Ablauf der 2-minütigen "Pausephase" soll das Ganze wieder von vorne beginnen.

Unterbrochen wird der Loop durch erneutes Drücken des Buttons, dann ist quasi alles wieder auf "Off" und es wird gewartet, bis der Button wieder auf "On" gestellt wird.

Ich hoffe es ist verständlich, was ich meine. Falls es hilft, poste ich hier noch eine der vielen Code-Varianten, die nicht funktionieren. xD Da funktioniert das mit der Bildschirmphase schon, aber wenn er dann in die Pausephase springt, bleibt er da und geht nicht wieder zurück. Dass der Button beim erneuten Drücken auf Off geht, ist da auch noch nicht integriert.

Code: [Select]

.
.
.
//Einrichten des Timer-Buttons
  #define buttonPin  12
  int buttonState = 0;
  boolean oldState = HIGH;
  bool buttonPressed = false;
  int switchVar;
  boolean newState;

//MILLIS
  int eyeTimer = 0;
  int eyeTimeout = 20000; //20 Sek    //1200000 = 20 Min
  int eyeBreak = 5000;      //5 Sek

//Andere Globale Werte
  #define Warte 400

/**************************************
//SETUP
**************************************/

void setup() {
  Serial.begin(9600);

  // Definieren der Eingabe-Pins
  pinMode(echoPin, INPUT);
  pinMode(buttonPin, INPUT_PULLUP);
 
  // Definieren der Ausgabe-Pins
  pinMode(trigPin, OUTPUT);
  pinMode(buzzPin, OUTPUT);

  // Initialisieren des Neo-Pixel Strips
  strip.begin();
  strip.show();  // Initialisiert alle Pixels auf "Off"
 
  switchVar = 0;
}

/**************************************
//Loop
**************************************/

void loop() {

  switch(switchVar){
   
    case 0:
      Serial.println("Button wird gelesen");
      newState = digitalRead(buttonPin);
     
      if ((newState == LOW) && (oldState == HIGH)) {
        // Short delay to debounce button.
        delay(20);
        buttonPressed = true;
      } else {
        buttonPressed = false;
      }

      if(buttonPressed == true){
        switchVar = 1;
      }
     
     break;
     
     case 1:
     bool raus = false;
     int timer;
     Serial.println("Case 1 entered");
     colorWipe(strip.Color(  255, 0,   0), 50);    // Grün leuchtet kurz auf
     eyeTimer = millis(); //Timer wird gestartet
     eyeBreak = eyeTimer + eyeTimeout;

     while(!raus){

        timer = millis();

        if(timer >= eyeBreak){
          raus = true;
          Serial.println("eyeBreak entered");
          }
       
        Serial.println("Zielzeit:");
        Serial.println(eyeBreak);
        Serial.println("Millisekunden");
        Serial.println(timer);
        Serial.println("In While-Loop");
        abstandMessen();
      }

      tone(buzzPin, G5);
      delay(Warte);
      noTone(buzzPin);
      rainbow(10);

      switchVar = 0;
     
     break;
  }

    Serial.println("Button:");
    Serial.println(buttonState);
}
.
.
.


Danke und viele Grüße,
Sandra

HotSystems

#1
Jul 07, 2020, 01:59 pm Last Edit: Jul 07, 2020, 02:00 pm by HotSystems
Dein Sketch ist fast nicht zu lesen. In einem Mobilgerät überhaupt nicht.
Setze den bitte in Code-Tags.

Verwende dazu die Schaltfläche </> oben links im Editorfenster.
Das kannst du auch nachträglich machen.
Dazu den Sketch markieren und die Schaltfläche klicken, oder [*code] davor und [*/code] dahinter ohne *.


Damit wird dieser für alle besser lesbar.
Das kannst du auch noch nachträglich machen.

Und da die Zeiten des Arduino nicht sehr genau sind, empfehle ich dir die Verwendung einer RTC, die DS3231 ist sehr genau.
Gruß Dieter

I2C = weniger ist mehr: weniger Kabel, mehr Probleme. 8)

sandra149

Dein Sketch ist fast nicht zu lesen. In einem Mobilgerät überhaupt nicht.
Setze den bitte in Code-Tags.

Verwende dazu die Schaltfläche </> oben links im Editorfenster.
Das kannst du auch nachträglich machen.
Dazu den Sketch markieren und die Schaltfläche klicken, oder [*code] davor und [*/code] dahinter ohne *.


Damit wird dieser für alle besser lesbar.
Das kannst du auch noch nachträglich machen.

Und da die Zeiten des Arduino nicht sehr genau sind, empfehle ich dir die Verwendung einer RTC, die DS3231 ist sehr genau.

Danke für den Hinweis, da merkt man, dass ich ganz neu in diesem Forum bin. Habe ich gleich geändert und hoffe, es ist jetzt besser lesbar. :)

Die Empfehlung mit der RTC werde ich mir merken, danke. Ich würde es so zum Testen aber erstmal mit der integrierten Uhr probieren wollen.

agmue

#3
Jul 07, 2020, 04:21 pm Last Edit: Jul 07, 2020, 05:38 pm by agmue
Ein paar Anmerkungen:
  • Strg+t in der IDE formatiert Dein Programm.
  • Die Variablen- und Konstantentypen sind teils unglücklich, teils falsch.
  • strip.Color(  255, 0,   0) sollte entsprechend RGB Rot ergeben. Initialisierungsfehler im geheimen Teil Deines Programms.
  • Du verwendest eine Schrittkette und millis(), aber leider auch ein blockierndes while. Bleibe bei der Schrittkette, das ist einfacher.

Ausbaufähiger Ansatz:

Code: [Select]
#include <Adafruit_NeoPixel.h> // include the Neopixel Library in this Sketch
#define PIN 11 // This is the Arduino Pin controlling the LEDstrip.
#define NUMPIXELS 8
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

//Einrichten des Timer-Buttons
const byte buttonPin = 2;
const byte buzzPin = 13;
enum {WARTEN, BILDSCHIRMPHASE, BUZZEIN, INDIEFERNESCHAUEN};
byte schritt = WARTEN;

//MILLIS
uint32_t eyeTimer;
const uint32_t ARBEITSZEIT = 20000; //20 Sek    //1200000 = 20 Min
const uint32_t PAUSENZEIT = 5000;      //5 Sek
const uint32_t BUZZZEIT = 200;

/**************************************
  //SETUP
**************************************/

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(buzzPin, OUTPUT);
  strip.begin();
  strip.fill(strip.Color(0 , 0 , 255), 0, strip.numPixels());
  strip.show();  // Initialisiert alle Pixels auf "Off"
}

/**************************************
  //Loop
**************************************/

void loop() {
  switch (schritt) {
    case WARTEN:
      if (!digitalRead(buttonPin)) { // gedrückt = LOW
        strip.fill(strip.Color(0 , 255 , 0), 0, strip.numPixels());
        strip.show();  // Initialisiert alle Pixels auf "Off"
        eyeTimer = millis();
        schritt = BILDSCHIRMPHASE;
      }
      break;

    case BILDSCHIRMPHASE:
      if (millis() - eyeTimer >= ARBEITSZEIT) {
        strip.fill(strip.Color(255 , 0 , 0), 0, strip.numPixels());
        strip.show();  // Initialisiert alle Pixels auf "Off"
        digitalWrite(buzzPin, HIGH);
        eyeTimer = millis();
        schritt = BUZZEIN;
      }
      break;
    case BUZZEIN:
      if (millis() - eyeTimer >= BUZZZEIT) {
        digitalWrite(buzzPin, LOW);
        schritt = INDIEFERNESCHAUEN;
      }
      break;

    case INDIEFERNESCHAUEN:
      if (millis() - eyeTimer >= PAUSENZEIT) {
        strip.fill(strip.Color(0 , 255 , 0), 0, strip.numPixels());
        strip.show();  // Initialisiert alle Pixels auf "Off"
        eyeTimer = millis();
        schritt = BILDSCHIRMPHASE;
      }
      break;
  }
}
Die Vorstellungskraft ist wichtiger als Wissen, denn Wissen ist begrenzt. (Albert Einstein)

Rentner

Hallo,

Du solltest erst mal Deine Aufgabe in zeitliche Abschnitte gliedern , dann kommst Du schnell drauf das eine Ablaufsteuerung , Schrittkette, Endlicher Automat , Zustandsautomat gut geeignet ist. Das sind alles Begriffe für letztlich das gleiche. Es soll ein Ablauf in einer festgelegten Reihenfolge gesteuert werden. Beispiele dazu kannst Du hier im Forum finden.

Warten auf ein Schalter
Messen 20 min
Pause 2 min

Für die Zustände messen und pause erstellst Du Dir dann je eine eigene Funktion damit kannst Du das auch einzeln testen.

Zusätzich fragst Du den Schalter immer ab damit du jederzeit in den wartetzstand zurück kannst.

Das Thema millis() hast Du ja bereits angewendet.

Heinz

sandra149


[/quote]
Ein paar Anmerkungen:
  • Strg+t in der IDE formatiert Dein Programm.
  • Die Variablen- und Konstantentypen sind teils unglücklich, teils falsch.
  • strip.Color(  255, 0,   0) sollte entsprechend RGB Rot ergeben. Initialisierungsfehler im geheimen Teil Deines Programms.
  • Du verwendest eine Schrittkette und millis(), aber leider auch ein blockierndes while. Bleibe bei der Schrittkette, das ist einfacher.

Ausbaufähiger Ansatz:

Code: [Select]
#include <Adafruit_NeoPixel.h> // include the Neopixel Library in this Sketch
#define PIN 11 // This is the Arduino Pin controlling the LEDstrip.
#define NUMPIXELS 8
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

//Einrichten des Timer-Buttons
const byte buttonPin = 2;
const byte buzzPin = 13;
enum {WARTEN, BILDSCHIRMPHASE, BUZZEIN, INDIEFERNESCHAUEN};
byte schritt = WARTEN;

//MILLIS
uint32_t eyeTimer;
const uint32_t ARBEITSZEIT = 20000; //20 Sek    //1200000 = 20 Min
const uint32_t PAUSENZEIT = 5000;      //5 Sek
const uint32_t BUZZZEIT = 200;

/**************************************
  //SETUP
**************************************/

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(buzzPin, OUTPUT);
  strip.begin();
  strip.fill(strip.Color(0 , 0 , 255), 0, strip.numPixels());
  strip.show();  // Initialisiert alle Pixels auf "Off"
}

/**************************************
  //Loop
**************************************/

void loop() {
  switch (schritt) {
    case WARTEN:
      if (!digitalRead(buttonPin)) { // gedrückt = LOW
        strip.fill(strip.Color(0 , 255 , 0), 0, strip.numPixels());
        strip.show();  // Initialisiert alle Pixels auf "Off"
        eyeTimer = millis();
        schritt = BILDSCHIRMPHASE;
      }
      break;

    case BILDSCHIRMPHASE:
      if (millis() - eyeTimer >= ARBEITSZEIT) {
        strip.fill(strip.Color(255 , 0 , 0), 0, strip.numPixels());
        strip.show();  // Initialisiert alle Pixels auf "Off"
        digitalWrite(buzzPin, HIGH);
        eyeTimer = millis();
        schritt = BUZZEIN;
      }
      break;
    case BUZZEIN:
      if (millis() - eyeTimer >= BUZZZEIT) {
        digitalWrite(buzzPin, LOW);
        schritt = INDIEFERNESCHAUEN;
      }
      break;

    case INDIEFERNESCHAUEN:
      if (millis() - eyeTimer >= PAUSENZEIT) {
        strip.fill(strip.Color(0 , 255 , 0), 0, strip.numPixels());
        strip.show();  // Initialisiert alle Pixels auf "Off"
        eyeTimer = millis();
        schritt = BILDSCHIRMPHASE;
      }
      break;
  }
}


Großartig!! Das war mir eine riesige Hilfe und hat mir die benötigte "Erleuchtung" gebracht. :) Vielen tausend Dank! Du hast Recht, das die Variablen ziemlich falsch/unglücklich formuliert waren, so habe ich jetzt wirklich besser durchgesehen. Ich habe mich heute Nachmittag nochmal mit einem Kommilitonen drangesetzt und wir haben jetzt noch integriert, dass ein weiterer ButtonPush wieder auf die Warteposition zurück wechselt. Somit funktioniert jetzt alles ungefähr so, wie ich es wollte. Danke nochmal.

sandra149

Hallo,

Du solltest erst mal Deine Aufgabe in zeitliche Abschnitte gliedern , dann kommst Du schnell drauf das eine Ablaufsteuerung , Schrittkette, Endlicher Automat , Zustandsautomat gut geeignet ist. Das sind alles Begriffe für letztlich das gleiche. Es soll ein Ablauf in einer festgelegten Reihenfolge gesteuert werden. Beispiele dazu kannst Du hier im Forum finden.

Warten auf ein Schalter
Messen 20 min
Pause 2 min

Für die Zustände messen und pause erstellst Du Dir dann je eine eigene Funktion damit kannst Du das auch einzeln testen.

Zusätzich fragst Du den Schalter immer ab damit du jederzeit in den wartetzstand zurück kannst.

Das Thema millis() hast Du ja bereits angewendet.

Heinz
Vielen Dank für deinen Tipp, Heinz. Ich habe die Logik jetzt ungefähr so aufgebaut. :) Ein paar Bugs sind noch drin, aber für einen ersten Test reicht es erstmal. Der Hinweis mit den eigenen Funktionen für jede Phase ist sehr gut, das werde ich mal ausprobieren. Ich werde meinen "finalen" Code hier dann nochmal posten, falls jemand mal ein ähnliches Problem haben sollte. Vielleicht hilft es ja jemandem weiter. ^^

agmue

Das war mir eine riesige Hilfe und hat mir die benötigte "Erleuchtung" gebracht. :)
Bitte gerne :)

Als ich durch dieses Forum die Schrittkette (=endlicher Automat =finite state machine) und die Verwendung von millis gelernt hatte, kam ich mir auch wie erleuchtet vor, weil Aufgaben viel leichter zu lösen waren.

Ich werde meinen "finalen" Code hier dann nochmal posten, falls jemand mal ein ähnliches Problem haben sollte. Vielleicht hilft es ja jemandem weiter. ^^
Ein Forum sollte aus Nehmen und Geben bestehen, daher ja bitte, ich würde mich freuen.

Die Vorstellungskraft ist wichtiger als Wissen, denn Wissen ist begrenzt. (Albert Einstein)

Go Up