Countdown Timer LCD Shield Arduino Uno

Hallo zusammen,

ich bin noch recht neu in der Welt des Arduino.

Ich habe einen Arduino Uno und ein lcd keypad shield. Ich möchte gerne einen einstellbaren Countdown haben der zum einen über die Tasten einstellbar ist und die Zeit anzeigt die Abläuft.

Ein bisschen was habe ich schon. Ich kann die Zeit einstellen und der Timer läuft eigentlich auch... Mit einer Einschränkung.

Über die Hoch und Runter Tasten auf dem LCD wähle ich einen Wert in Sekunden aus. Über die Select Taste soll der Timer dann starten und das Runterzählen auf dem Display anzeigen.

Der Timer startet aber ich muss immer wieder die Select Taste drücken damit der Wert aktualisiert wird.

Kann mir von euch jemand etwas helfen? Vielleicht bin ich ja auch ganz verkehrt damit.

Anbei der Code:

#include <LiquidCrystal.h>
#include <Bounce2.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); 


// define some values used by the panel and buttons
int lcd_key = 0;
int adc_key_in = 0;
int buttonState = 0;
int count_value = 10;
int new_count = 0;
int prestate = 0;

long hour = 23, minute = 59, second = 59;
long countdown_time = (hour*3600) + (minute * 60) + second;

#define btnRIGHT 0
#define btnUP 1
#define btnDOWN 2
#define btnLEFT 3
#define btnSELECT 4
#define btnNONE 5



int read_LCD_buttons(){ // read the buttons
  adc_key_in = analogRead(0); // read the value from the sensor


  if (adc_key_in > 1000) return btnNONE;
  if (adc_key_in < 50) return btnRIGHT;
  if (adc_key_in < 195) return btnUP;
  if (adc_key_in < 380) return btnDOWN;
  if (adc_key_in < 500) return btnLEFT;
  if (adc_key_in < 700) return btnSELECT;

return btnNONE; // when all others fail, return this.


}

void setup(){

  Serial.begin(9600);
  lcd.begin(16, 2); // start the library
  lcd.setCursor(0,0); // set the LCD cursor position
  lcd.print("3.1 Elektronik"); // print a simple message on the LCD
  lcd.setCursor(0,1); // set the LCD cursor position
  lcd.print("Set Timer:"); // print a simple message on the LCD
  lcd.setCursor(10,1); // set the LCD cursor position
  lcd.print(count_value + String("s"));
  

}


void loop()
{

 lcd.setCursor(10,1);            // move to the begining of the second line
 lcd_key = read_LCD_buttons();  // read the buttons
 long countdowntime_seconds = count_value - (millis() / 1000);

 switch (lcd_key)               // depending on which button was pushed, we perform an action
 {
   case btnRIGHT:
     {
     lcd.print("RIGHT ");
     break;
     }
   case btnLEFT:
     {
     lcd.print("LEFT   ");
     break;
     }
   case btnUP:
    {

      if(count_value != -1) {
        lcd.setCursor (11,1);
        lcd.print(' ');        
      }
    lcd.setCursor(10,1);
    
    new_count =  count_value++;
    lcd.print(count_value);
    Serial.println(new_count);
    delay(250);
    break;
    }
   case btnDOWN:
    { 
      if (count_value < 10){
        lcd.setCursor(11,1);
        lcd.print(' ');
      }
    lcd.setCursor(10,1);
    new_count = count_value--;
    lcd.print(new_count);
    Serial.println(new_count);
    delay(250);
    break;
    }

   case btnSELECT:
    {
      lcd.clear();
      lcd.setCursor(0,1);
      lcd.print("Time left: ");
      lcd.setCursor(13,1);
      lcd.print("s");
      if (btnSELECT == 4){
        digitalWrite(btnSELECT, HIGH);
        Serial.println(btnSELECT);
        delay(200);
          if (countdowntime_seconds  >=0 ) {
          long countdown_hour = countdowntime_seconds / 3600;
          long countdown_minute = ((countdowntime_seconds / 60)%60);
          long countdown_sec = countdowntime_seconds % 60;
          //Serial.println(countdown_sec);
          lcd.setCursor(10,1);
          lcd.print(countdown_sec); 
          Serial.println(countdown_sec); 
          // lcd.setCursor(10,1);
          //   // if (countdown_sec < 10) {
          //   //   lcd.print("0");
          //   // }
           }
           else{
             digitalWrite(btnSELECT, HIGH);
           }
    }
    }
 }
}

Damit etwas "automatisch" angezeigt wird, müsste das passieren, wenn der Normalfall (btnNONE) vorliegt. Dann aber möglichst nur einmal je Sekunde, damit das Display nicht flackert.
Ein delay(1000) ist aber auch nicht gut, damit man schneller auf Tasteneingabe reagieren kann.
Besser man merkt sich den millis() - Wert, wann der Zähler um 1 runtergezählt (und angezeigt) wurde und macht das, wenn seitdem 1000 ms vergangen sind, wieder.
(Das berühmte BlinkWithoutDelay Beispiel, das nichts mit Blink aber mit WithoutDelay zu tun hat.

In deinem Code ist auch einiges andere merkwürdig


#define btnSELECT 4
...
  if (btnSELECT == 4){  // das ist immer erfüllt
        digitalWrite(btnSELECT, HIGH);   // Pin 4 wird vom LCD verwendet !  ??
        Serial.println(btnSELECT);  // warum gibst du eine "4" aus ??

:rofl: wie geschrieben... blutiger Anfänger.

Das heißt aber, dass ich die Ausführung des Countdowns in den Case von btnNONE einfügen müsste damit der Countdown angezeigt wird.

Ja, und in btnSelect ist nur der Timer zu starten.

Dann würde sich auch anbieten, bei UP/DOWN den Timer anzuhalten, in btnNONE also nur runterzuzählen, wenn zuletzt per btnSELECT der Zähler gestartet wurde.

So wie michaelfaske es realisiert haben möchte. Vllt dazu die Zeitprogrammierung am Backofen studieren :nerd_face:

:rofl: @paulpaulson wenns hilft warum nicht.

Wenn ich den Timer in btnNONE einbaue läuft dieser sofort nach Start los. Obwohl in der if Abfrage erst auf btnSELECT gewartet werden soll

case btnSELECT:
    {
      lcd.clear();
      lcd.setCursor(0,1);
      lcd.print("Time left: ");
      lcd.setCursor(13,1);
      lcd.print("s");
    }
  case btnNONE:{
    if (btnSELECT == 4){

          if (countdowntime_seconds  >=0 ) {
          long countdown_hour = countdowntime_seconds / 3600;
          long countdown_minute = ((countdowntime_seconds / 60)%60);
          long countdown_sec = countdowntime_seconds % 60;
          //Serial.println(countdown_sec);
          lcd.setCursor(10,1);
          lcd.print(countdown_sec); 
          Serial.println(countdown_sec); 
          lcd.setCursor(10,1);
            if (countdown_sec < 10) {
              lcd.print("0");
            }
           }
           else{
             digitalWrite(btnSELECT, HIGH);
           }
    }
    }

Genau aus diesen Gründen ist #define eine ganz böse Geschichte.

Wüsen die Taster erst mall im Setup initalisiert werden?

ArduinoPins sind standardmäßig Eingänge, so dass sie nicht explizit mit pinMode() als Eingänge deklariert werden müssen, wenn man sie als Eingänge verwendet.

Das ist ja das Kuriose.
Es gibt keinen BUTTON an Pin 4, sondern ist Teil einer Analogmatrix:

bitte poste deinen letzten Stand (voll kompilierbarer Sketch) und beschreibe exakt bei welchem Buttondruck was genau passiert, und was statt dessen passieren soll.
Poste dazu auch die Ausgaben von deiner Seriellen Schnittstelle.

const byte btnSELECT=4; verbessert da allerdings kein bisschen.

selbst eine Definition
enum KEYVAL { btnNONE, btnRIGHT, btnUP, btnDOWN, btnLEFT, btnSELECT } ;

erlaubt einen Unsinn wie digitalWrite(btnSELECT, HIGH);

Hinweis: besser wird's erst mit enum class


enum class KEY { NONE, RIGHT, UP, DOWN, LEFT, SELECT } ;
KEY read_LCD_buttons() {
    ...
    return KEY::NONE;
}
...
void loop() {
   switch (  read_LCD_buttons()  ) {
       case KEY::SELECT: break;
        ...
   }
}

Das meckert dann über
if (KEY::SELECT == 4)
und die Verwendung in digitalWrite

1 Like

Das stimmt...

Dann irgendwann doch :wink:
Danke für die Aufklärung.

Guten Morgen,

erstmal danke für eure Antworten. Für mich macht es den Eindruck, als würde es zusammen mit dem Display schwierig sein einen vernünftigen Ablauf hinzubekommen.

Hier mal der momentane Code und die Erklärung dazu:

#include <LiquidCrystal.h>
#include <Bounce2.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); 


// define some values used by the panel and buttons
int lcd_key = 0;
int adc_key_in = 0;
int buttonState = 0;
int count_value = 10;
int new_count = 0;
int prestate = 0;

long hour = 23, minute = 59, second = 59;
long countdown_time = (hour*3600) + (minute * 60) + second;

#define btnRIGHT 0
#define btnUP 1
#define btnDOWN 2
#define btnLEFT 3
#define btnSELECT 4
#define btnNONE 5



int read_LCD_buttons(){ // read the buttons
  adc_key_in = analogRead(0); // read the value from the sensor


  if (adc_key_in > 1000) return btnNONE;
  if (adc_key_in < 50) return btnRIGHT;
  if (adc_key_in < 195) return btnUP;
  if (adc_key_in < 380) return btnDOWN;
  if (adc_key_in < 500) return btnLEFT;
  if (adc_key_in < 700) return btnSELECT;

return btnNONE; // when all others fail, return this.


}

void setup(){

  Serial.begin(9600);
  lcd.begin(16, 2); // start the library
  lcd.setCursor(0,0); // set the LCD cursor position
  lcd.print("3.1 Elektronik"); // print a simple message on the LCD
  lcd.setCursor(0,1); // set the LCD cursor position
  lcd.print("Set Timer:"); // print a simple message on the LCD
  lcd.setCursor(10,1); // set the LCD cursor position
  lcd.print(count_value + String("s"));
  

}


void loop()
{

 lcd.setCursor(10,1);            // move to the begining of the second line
 lcd_key = read_LCD_buttons();  // read the buttons
 long countdowntime_seconds = count_value - (millis() / 1000);

 switch (lcd_key)               // depending on which button was pushed, we perform an action
 {
   case btnRIGHT:
     {
     lcd.print("RIGHT ");
     break;
     }
   case btnLEFT:
     {
     lcd.print("LEFT   ");
     break;
     }
   case btnUP:
    {

      if(count_value != -1) {
        lcd.setCursor (11,1);
        lcd.print(' ');        
      }
    lcd.setCursor(10,1);
    
    new_count =  count_value++;
    lcd.print(count_value);
    Serial.println(new_count);
    delay(250);
    break;
    }
   case btnDOWN:
    { 
      if (count_value < 10){
        lcd.setCursor(11,1);
        lcd.print(' ');
      }
    lcd.setCursor(10,1);
    new_count = count_value--;
    lcd.print(new_count);
    Serial.println(new_count);
    delay(250);
    break;
    }

   case btnSELECT:
    {
      lcd.clear();
      lcd.setCursor(0,1);
      lcd.print("Time left: ");
      lcd.setCursor(13,1);
      lcd.print("s");
    }
  case btnNONE:{
    
    if (btnSELECT == 4){

          if (countdowntime_seconds  >=0 ) {
          long countdown_hour = countdowntime_seconds / 3600;
          long countdown_minute = ((countdowntime_seconds / 60)%60);
          long countdown_sec = countdowntime_seconds % 60;
          //Serial.println(countdown_sec);
          lcd.setCursor(10,1);
          lcd.print(countdown_sec); 
          Serial.println(countdown_sec); 
          lcd.setCursor(10,1);
            if (countdown_sec < 10) {
              lcd.print("0");
            }
           }

    }
    }

  }
 }


Wenn das Programm startet startet der Timer umgehend und im Display werden die voreingestellten 10 Sekunden runtergezählt.
Danach kann ich über die Hoch und Runtertasten einen Wert einstellen.
Drücke ich dann Setup wird das Display geleert und es steht "Time Left" da aber dann passiert nichts mehr.

Vielen Dank :slight_smile:

Da Du den Wink mit dem Zaunpfahl ignoriert hast, frage ich erneut: Auf was prüfst Du da? Was erwartest Du da, das Du da prüfst?

Ich hatte erhofft, dass wenn ich den Taster btnSELECT drücke die die if Abfrage startet und der Timer losläuft. Aber er startet ja direkt nach dem Programmstart.

Ok. Dann mal von Anfang.
Du wertest aus, ob btnNone aktiv ist.

willst aber gleichzeitig feststellen, ob btnSELECT aktiv ist. Das geht nicht.
Mehr noch. Die Bedingung btnSELECT == 4 stimmt immer.
Denn Du hast btnSELECT den Wert 4 zugewiesen.

Es wäre auch schlimm, wenn sich der Inhalt verändern würde. Denn dann würde Deine Zuordnung

nicht mehr stimmen.

Nu hast Du für btnSelect aber zwei inhaltliche Funktionen. Sowohl:

Was dafür sorgt, das Du beim drücken des Tasters die Ausgabe auf dem Display erhälst.
und andererseis den Codeteil:

der auch irgendwo untergebracht gehört - aber eben in einer Abhängigkeit und nicht mit der von Dir geschriebenen Bedingung.

Beschreib mal, was Du genau in welcher Reihenfolge mit welchen Tasten machen willst.

Also :slight_smile:

mit den Tasten btnUP bzw. btnDOWN stelle ich die Zeit in Sekunden ein wie lange der Timer laufen soll. Und mit btnSELECT möchte ich den Timer starten

Die Anzeige soll sich dann wie in btnSELECT umstellen und die abgelaufene Zeit anzeigen.

Ist der Timer aubgelaufen soll es wieder zur ursprünglichen Anzeige zurückgehen und man kann die Zeit wieder einstellen.

versuch den mal.
Was der machen soll:

Er startet mit 10 sekunden Vorgabe.
Geht mit dem Start in den Countdown.
Mit Ablauf des Countdown oder wenn Du select drückst, geht er in die Einstellung.
Da kannst Du mit auf/ab die Sekunden einstellen udn mit select wieder den countdown starten.

Ist mehr oder weniger eine ausbaufähige Grundstruktur.
Es muss nicht funktionieren.
Kompiliert aber Fehler- und Warnungsfrei.
Wenn was nicht geht, bau Dir serielle Ausgaben ein.
Wenn Du was nicht verstehst, frag.

#include <LiquidCrystal.h>
//#include <Bounce2.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

enum btn {btnRIGHT, btnUP, btnDOWN, btnLEFT, btnSELECT, btnNONE};
enum dis {einstellen, countdown};

constexpr uint32_t oneSecond {1000};
uint32_t countDownSeconds = 10;
uint32_t startZeit;
byte displayType = dis::countdown;

byte myKey = btn::btnNONE;
byte lastKey = myKey;

byte read_LCD_buttons()  // read the buttons
{
  byte key = btn::btnNONE;
  switch (analogRead(0))
  {
    case  10 ...  40:
      key = btnRIGHT;
      break;
    case  60 ... 180:
      key = btnUP;
      break;
    case 210 ... 350:
      key = btnDOWN;
      break;
    case 380 ... 480:
      key = btnLEFT;
      break;
    case 500 ... 650:
      key = btnSELECT;
      break;
  }
  return key;
}

void setup()
{
  Serial.begin(9600);
  lcd.begin(16, 2); // start the library
  lcd.setCursor(0, 0); // set the LCD cursor position
  lcd.print("3.1 Elektronik"); // print a simple message on the LCD
  lcd.setCursor(0, 1); // set the LCD cursor position
  lcd.print("Set Timer:"); // print a simple message on the LCD
  lcd.setCursor(10, 1); // set the LCD cursor position
  displayCountDown();
  startZeit = millis();
}
//
void displayCountDown()
{
  lcd.setCursor(10, 1);
  if (countDownSeconds < 10)
  {
    lcd.print(' ');
  }
  lcd.print(countDownSeconds);
  Serial.println(countDownSeconds);
}
//
void loop()
{
  myKey = read_LCD_buttons();
  switch (displayType)
  {
    case dis::einstellen:
      if (myKey != lastKey)
      {
        lastKey = myKey;
        switch (myKey)
        {
          case btn::btnUP:
            countDownSeconds++;
            break;
          case btn::btnDOWN:
            countDownSeconds--;
            break;
          case btn::btnSELECT:
            displayType = dis::countdown;
            startZeit = millis();
            break;
            if (countDownSeconds >= 100 || countDownSeconds < 10)
            { countDownSeconds = 10; }
            displayCountDown();
            delay(50);   // rudimentärer debounce
        }
      }
      break;
    case dis::countdown:
      if (millis() - startZeit >= oneSecond)
      {
        startZeit += oneSecond;
        countDownSeconds--;
        displayCountDown();
      }
      if (countDownSeconds == 0)
      { displayType = dis::einstellen; }
      if (myKey != lastKey)
      {
        lastKey = myKey;
        switch (myKey)
        {
          case btn::btnSELECT:
            displayType = dis::einstellen;
            break;
        }
        delay(50);   // rudimentärer debounce
      }
  }
}