Multifunktionstaster - Abfrage eines Status für eine Bestimmte Zeit?!

Hallo liebe Arduino-Gemeinde,

folgendes: ich saß heute im Hörsaal in dem die Tafeln ja elektrisch verschiebbar sind. Da dachte ich mir, dass es für den Dozenten doch doof sei so lange auf den Taster zu drücken bis die Tafel oben ist.

Mein gedanke war ein Multifunktionstaster, der folgender Weise funktionier:
-länger als 500ms gedrückt halten --> Tafel fährt bis man loslässt
-2 mal drücken/tippen --> Tafel fährt ganz hoch (endschalter!)
-3 mal drücken/tippen --> Tafel fährt so hoch, dass die andere Tafel die ganz oben ist zu sehen ist. (Sind ja zwei Tafeln hinter einander)

Abfolgen und Programmschnipsel hab ich mir schon ausgedacht doch das ging irgendwie gegen den Baum...
ausschnitt:

int tastehoch = 1;
int tastehochState = 0;
int tasterunter = 2;
int tasterunterState = 0;
int motorhoch = 3;
int motorrunter = 4;
int endschalteroben = 5;
int endschalterobenState = 0;
int endschalterhaelfte = 6;
int endschalterhaelfteState = 0;
int zaehler = 0;
int t = 450; //zeit die es brauch damit taster anspricht

void setup()
{
  pinMode(tastehoch, INPUT);
  pinMode(tasterunter, INPUT);
  pinMode(motorhoch, OUTPUT);
  pinMode(motorrunter, OUTPUT);
  pinMode(endschalteroben, INPUT);
  pinMode(endschalterhaelfte, INPUT);
}

void loop()
{
  tastehochState = digitalRead(tastehoch);
  
  if (tastehochState == HIGH)
  {
    delay(t);
    tastehochState = digitalRead(tastehoch);
    if (tastehochState == HIGH)
    {
      endschalterobenState = digitalRead(endschalteroben);
      while(endschalterobenState == HIGH)
      {
        digitalWrite(motorhoch, HIGH);
      }
    }
    else
    {
      zaehler = zaehler + 1;
    }  
  }
}

Hauptfrage wäre: wie kann ich abfragen, dass der Taster für mindestens 500ms gedrückt ist?

so... das war jetzt viel... aber ich freue mich auf Anregungen und danke dafür.
mfg
Paul

Antwort zur Hauptfrage:
mit millis()
millis() bei Tastendruck abspeichern und wiederholt kontrollieren ob die Zeit verstrichen ist.
siehe

Laß den Dozenten nur ruhig auch mal einige stupide Sachen machen, sonst schweben einige ganz über dem normalen Fußvolk.

Als Projektübung für Dich ist nichts einzuwenden.

Grüße Uwe

Hey,

vielen Dank für diesen Gedankenanstoß Uwe! :slight_smile:

Ach... ich studiere doch gar nicht. Habe mich bloß mal rein gesetzt um festzustellen ob Informatik was für mich wäre. Und da kommt man auf so blödsinnige Ideen :smiley:

mfg
Paul

Okay... hier meine Lösung zu dem Problem (falls jemand Anderes mal danach sucht):

int tastehoch = 2;
int tastehochState = 0;
int druckzeit = 2000; //zeit die es brauch damit taster anspricht
unsigned long aktuellezeit;
long vorherigezeit;
int a = 0;

void setup()
{
  pinMode(tastehoch, INPUT);
  pinMode(13, OUTPUT);
}

void loop()
{  
  tastehochState = digitalRead(tasterhoch);
  if (tastehochState == HIGH)
  {
    if (a == 0)
    {
      vorherigezeit = millis();
      a = 1;
    }
    aktuellezeit = millis();
    if (aktuellezeit - vorherigezeit >= druckzeit)
    {
      digitalWrite(13, HIGH);
    }
  }
  else
  {
    digitalWrite(13, LOW);
    a = 0;
  }
}

vielen Dank nochmal für den Hinweis!
gute Nacht
Paul

vorherigezeit; sollte auch vom Typ unsigned long sein.
Gute Nacht Uwe

Hallo zusammen,
der Thread ist zwar schon etwas älter, aber ich fange gerade mit Arduino & Co an.
Ich wollte den Sketch gerade ausprobieren, aber er funzt nicht - so wie oben geschrieben.
Als erstes scheint in Zeile 16 "tasterhoch" versehentlich für "tastehoch" (ohne "r") geschrieben worden zu sein.

Dennoch kann ich den Taster drücken so lange ich will - die LED bleibt dunkel.

Evtl. kann mir da jemand helfen.

Ich möchte nämlich für ein Projekt auch einen Taster mit zwei Funktionen belegen. Einmal ein/aus und einmal "toggeln".

Danke und Gruß
der "Stevie"

steview_de:
Als erstes scheint in Zeile 16 "tasterhoch" versehentlich für "tastehoch" (ohne "r") geschrieben worden zu sein.

Stimmt!

steview_de:
Dennoch kann ich den Taster drücken so lange ich will - die LED bleibt dunkel.

Bei mir geht die LED nach zwei Sekunden an :slight_smile: Am Sketch kann es dann wohl nicht liegen.

steview_de:
Dennoch kann ich den Taster drücken so lange ich will - die LED bleibt dunkel.

Evtl. kann mir da jemand helfen.

Ich habe den Sketch selbst nicht getestet, aber für Dich als Arduino-Anfänger der Hinweis zur Beschaltung des Tasters:

 pinMode(tastehoch, INPUT);

Wenn der Pin des Testers auf INPUT gesetzt wird, ist die Beschaltung mit einem externen Pull-Down Widerstand am Taster ZWINGEND NOTWENDIG.

Hast Du den Pull-Down Widerstand in Deiner Schaltung?

Andernfalls müßte der pinMode auf INPUT_PULLUP gesetzt werden (Aktivierung des internen Pull-Up Widerstands) und der Pegel mit inverser Logik ausgelesen werden (Schalter unbetätigt == HIGH, Schalter betätigt == LOW).

P.S.: Den Schreibfehler im Variablennamen beim geposteten Code kann ich bestätigen

P.P.S.; Der Code scheint auch gar nicht das zu machen, was Du möchtest, nämlich mit einer Taste zwei verschiedene Funktionen steuern.

Ich selbst müßte noch irgendwo einen Code liegen haben, bei dem drei Funktionen mit einer Taste gesteuert werden können:

  • kurzer Klick (Taste einmal kurz drücken)
  • Doppelklick (Taste zweimal schnell nacheinander kurz drücken)
  • langer Klick (Taste für eine Mindestzeit lange drücken)
    Wenn Du möchtest, könnte ich mal auf die Suche gehen, wo ich den Code habe (eventuell sogar schon mal irgendwo hier im Forum gepostet).

jurs:
Ich habe den Sketch selbst nicht getestet, aber für Dich als Arduino-Anfänger der Hinweis zur Beschaltung des Tasters:

 pinMode(tastehoch, INPUT);

Wenn der Pin des Testers auf INPUT gesetzt wird, ist die Beschaltung mit einem externen Pull-Down Widerstand am Taster ZWINGEND NOTWENDIG.

Hast Du den Pull-Down Widerstand in Deiner Schaltung?

... ah, stimmt.
Habe jetzt noch:

digitalWrite(tastehoch,HIGH);

hinzugefügt.

Jetzt passiert folgendes: Die LED geht nach zwei Sekunden an - gut. Wenn ich den Taster drücke, geht sie LED immer 2 Sekunden nach loslassen an. Soll das so?

jurs:
Andernfalls müßte der pinMode auf INPUT_PULLUP gesetzt werden (Aktivierung des internen Pull-Up Widerstands) und der Pegel mit inverser Logik ausgelesen werden (Schalter unbetätigt == HIGH, Schalter betätigt == LOW).

P.S.: Den Schreibfehler im Variablennamen beim geposteten Code kann ich bestätigen

P.P.S.; Der Code scheint auch gar nicht das zu machen, was Du möchtest, nämlich mit einer Taste zwei verschiedene Funktionen steuern.

Ich selbst müßte noch irgendwo einen Code liegen haben, bei dem drei Funktionen mit einer Taste gesteuert werden können:

  • kurzer Klick (Taste einmal kurz drücken)
  • Doppelklick (Taste zweimal schnell nacheinander kurz drücken)
  • langer Klick (Taste für eine Mindestzeit lange drücken)
    Wenn Du möchtest, könnte ich mal auf die Suche gehen, wo ich den Code habe (eventuell sogar schon mal irgendwo hier im Forum gepostet).

Schon klar. Ich will mich ja Stück für Stück herantasten.
Gerne nehme ich jede Hilfe in Form von Code-Schnipseln an.

Danke und Gruß
der "Stevie"

steview_de:
Jetzt passiert folgendes: Die LED geht nach zwei Sekunden an - gut. Wenn ich den Taster drücke, geht sie LED immer 2 Sekunden nach loslassen an. Soll das so?

Nein, mit pinMode INPUT_PULLUP ist die Pegel-Logik jetzt natürlich vertauscht und Du mußt beim Auslesen das Signal mit dem '!' (NOT) Operator invertieren:

tastehochState = !digitalRead(tasterhoch);

steview_de:
Gerne nehme ich jede Hilfe in Form von Code-Schnipseln an.

Ich fange mal an zu suchen ...

[Edit] So, fertig gesucht: Etwas in der Art habe ich bisher noch nicht im Forum gepostet, und auch bei mir auf der Festplatte konnte ich nur Fragmente entdecken. Ich hatte die Verwendung solcher Button-Funktionen aufgegeben, nachdem ich gemerkt hatte, wie lahm die Bedienung dadurch wird: Nur ein "Doubleclick" wird in der Zeit erkannt, die man für den Doubleclick tatsächlich benötigt, alle anderen Funktionen werden nur mit Verzögerung erkannt: Ein "Singleclick" wird erst erkannt, nachdem die maximal erlaubte Doubleclick-Time vergangen ist, innerhalb der aus einem Singleclick noch ein Doubeclick werden könnte. Und Longclick Tastendrücke brauchen lange, weil die Taste eine bestimmte Mindestdauer gedrückt bleiben muss. Das hatte mir dann alles nicht gefallen und ich habe es beiseite gelegt, obwohl es funktioniert hat.

Mal schauen, ob ich den Code da rausziehen und ein geeignetes Programmierbeispiel dazu machen kann.

jurs:
Nein, mit pinMode INPUT_PULLUP ist die Pegel-Logik jetzt natürlich vertauscht und Du mußt beim Auslesen das Signal mit dem '!' (NOT) Operator invertieren:

tastehochState = !digitalRead(tasterhoch);

... ach, so "einfach" ist das. Okay!?
Danke!

jurs:
Ich fange mal an zu suchen ...

[Edit] So, fertig gesucht: Etwas in der Art habe ich bisher noch nicht im Forum gepostet, und auch bei mir auf der Festplatte konnte ich nur Fragmente entdecken. Ich hatte die Verwendung solcher Button-Funktionen aufgegeben, nachdem ich gemerkt hatte, wie lahm die Bedienung dadurch wird: Nur ein "Doubleclick" wird in der Zeit erkannt, die man für den Doubleclick tatsächlich benötigt, alle anderen Funktionen werden nur mit Verzögerung erkannt: Ein "Singleclick" wird erst erkannt, nachdem die maximal erlaubte Doubleclick-Time vergangen ist, innerhalb der aus einem Singleclick noch ein Doubeclick werden könnte. Und Longclick Tastendrücke brauchen lange, weil die Taste eine bestimmte Mindestdauer gedrückt bleiben muss. Das hatte mir dann alles nicht gefallen und ich habe es beiseite gelegt, obwohl es funktioniert hat.

Mal schauen, ob ich den Code da rausziehen und ein geeignetes Programmierbeispiel dazu machen kann.

Also einen Duoble-Click benötige ich nicht.
Ich habe mir das so vorgestellt:
Ich habe drei LED Ausgänge. Im "Ruhezustand" sind alle aus. Bei Single-Click geht LED1 an und bei weiteren Single-Clicks werden die LEDs "durch getoggelt". Das läuft so weit schon. Jetzt muss "nur noch" die Ausschalt-Funktion implementiert werden. Die soll über eine längere Haltedauer des Tasters realisiert werden.

Danke und Gruß
der "Stevie"

Natürlich ist es erstmal naheliegend, sowas über die Zeit zu machen, aber ich finde das trotzdem nicht sonderlich "elegant" - wenn auch mit dem geringsten Aufwand verbunden. Was passiert denn, wenn die Motoren mal (z.B. wg. Staub, zähem Öl) schwergängig werden oder im Sommer bei 35 Grad besonders leicht laufen? Richtig: Die Positionen stimmen nicht mehr.

Ich würde zumindes die Wegenenden nicht nur mit Endschaltern schützen, sondern auch dem Arduino signalisieren. Ebenso 1..2 wichtige "Zwischenstationen" durch Gabel-Lichtschranken, Hallsensoren oder Reed-/Mikrotaster signalisieren. Falls man dann mal von ganz Unten nach ganz oben (ode rumgekehrt) fährt, kann man ja nebenbei die Zeit messen und das System quasi neu kalibrieren. DAS ist intelligent ... :slight_smile:

Bist du sicher, dass du im richtigen Thread bist?

steview_de:
Also einen Duoble-Click benötige ich nicht.
Ich habe mir das so vorgestellt:
Ich habe drei LED Ausgänge. Im "Ruhezustand" sind alle aus. Bei Single-Click geht LED1 an und bei weiteren Single-Clicks werden die LEDs "durch getoggelt". Das läuft so weit schon. Jetzt muss "nur noch" die Ausschalt-Funktion implementiert werden. Die soll über eine längere Haltedauer des Tasters realisiert werden.

Warum dann überhaupt ein unterschiedlicher Klick?
Warum nicht beim dritten Klick "Aus"?

Also eine Finite-State-Machine mit drei Zuständen:

  • erster Klick "LED1 an"
  • zweiter Klick "die LEDs werden durch getoggelt"
  • dritter Klick "aus / Ruhezustand"

Warum nicht so?

Ich habe beispielsweise eine 3-Funktionen LED-Taschenlample, die funktioniert so:

  • erster Klick "superhelles Licht"
  • zweiter Klick "dunkleres Licht, Energiesparen"
  • dritter Klick "blinkendes Licht"
  • vierter Klick "aus"
    Diese LED-Taschenlample steuert also eine State-Machine mit vier Zuständen und braucht dafür nur einen einzigen Schalter, der immer nur auf dieselbe Art geklickt werden muss, aber niemals unterschiedliche Klicks für unterschiedliche Funktionen benötigt.

Die Software muß dazu nur mit jedem Klick in einen anderen "State" der "State Machine" weiterschalten.

jurs:
Warum dann überhaupt ein unterschiedlicher Klick?
Warum nicht beim dritten Klick "Aus"?

Also eine Finite-State-Machine mit drei Zuständen:

  • erster Klick "LED1 an"
  • zweiter Klick "die LEDs werden durch getoggelt"
  • dritter Klick "aus / Ruhezustand"

Warum nicht so?

..., weil ich es nicht will :wink:

Die LED Lampe ist "dimmbar", allerdings in drei Stufen. Jede Stufe hat einen eigenen Eingang.
Also:

  • erster Klick "LED, dunkel" an, andere LEDS aus
  • zweiter Klick "LED, mittel" an, andere LEDs aus
  • dritter Klick "LED, hell" an, andere LEDs aus
  • vierter Klick, wie erster Klick
  • fünfter Klick, wie zweiter Klick
  • usw.

... das ist für mich "toggeln".

Und weil die ja immer durchgetoggelt werden, möchte ich beim langen Drücken des Tasters alle LEDs ausschalten.

Feddisch

... aber das werde ich dann evtl. im anderen Thread anmerken, somst wird's hier ot.

Danke und Gruß
der "Stevie"

steview_de:
Die LED Lampe ist "dimmbar", allerdings in drei Stufen. Jede Stufe hat einen eigenen Eingang.
Also:

  • erster Klick "LED, dunkel" an, andere LEDS aus
  • zweiter Klick "LED, mittel" an, andere LEDs aus
  • dritter Klick "LED, hell" an, andere LEDs aus
  • vierter Klick, wie erster Klick
  • fünfter Klick, wie zweiter Klick
  • usw.

... das ist für mich "toggeln".

Und weil die ja immer durchgetoggelt werden, möchte ich beim langen Drücken des Tasters alle LEDs ausschalten.

Bei der Lampe mußt Du dann aber eine Bedienungsanleitung beilegen! Wenn jemand die Lampe sonst aus Versehen mit einem kurzen Klick einschaltet, ohne das Bedienkonzept zu kennen, bekommt er sie intuitiv sonst durch noch so häufiges Klicken nicht mehr ausgeschaltet.

Hier ist ein Code, der drei Funktionen unterscheiden kann: Click, Doubleclick und LongClick:

// Tastenerkennung SHORTCLICK, DOUBLECLICK, LONGCLICK
#define INPUTMODE INPUT_PULLUP  // INPUT oder INPUT_PULLUP
#define PRELLZEIT 5             // Prellzeit in Millisekunden
// #define SHORTCLICKTIME 250      // Längste Zeit für einen SHORTCLICK
#define DOUBLECLICKTIME 400     // Längste Zeit für den zweiten Klick beim DOUBLECLICK
#define LONGCLICKTIME 600       // Mindestzeit für einen LONGGLICK
byte buttonPins[]={2,3,A3}; // Arduino Pins (mehrere als Array möglich)
#define NUMBUTTONS sizeof(buttonPins)
byte buttonState[NUMBUTTONS];  // Aktueller Status des Buttons HIGH/LOW
enum {NONE, FIRSTDOWN, FIRSTUP, SHORTCLICK, DOUBLECLICK, LONGCLICK}; 
byte buttonResult[NUMBUTTONS]; // Aktueller Klickstatus der Buttons NONE/SHORTCLICK/LONGCLICK

void setup() {
  Serial.begin(9600);
  Serial.println("Button test");
  for (int i=0;i<NUMBUTTONS;i++) pinMode(buttonPins[i],INPUTMODE);
}

boolean eingabe()
// Rückgabewert false ==> Prellzeit läuft, Taster wurden nicht abgefragt
// Rückgabewert true ==> Taster wurden abgefragt und Status gesetzt
{
  static unsigned long lastRunTime;
  static unsigned long buttonDownTime[NUMBUTTONS];
  unsigned long now=millis();
  if (now-lastRunTime<PRELLZEIT) return false; // Prellzeit läuft noch
  lastRunTime=now;
  for (int i=0;i<NUMBUTTONS;i++)
  {
    byte curState=digitalRead(buttonPins[i]); 
    if (INPUTMODE==INPUT_PULLUP) curState=!curState; // Vertauschte Logik bei INPPUT_PULLUP
    if (buttonResult[i]>=SHORTCLICK) buttonResult[i]=NONE; // Letztes buttonResult löschen
    if (curState!=buttonState[i]) // Flankenwechsel am Button festgestellt
    {
      if (curState)   // Taster wird gedrückt, Zeit merken
      {
        if (buttonResult[i]==FIRSTUP && now-buttonDownTime[i]<DOUBLECLICKTIME)
          buttonResult[i]=DOUBLECLICK;
        else
        {  
          buttonDownTime[i]=now; 
          buttonResult[i]=FIRSTDOWN;
        } 
      }
      else  // Taster wird losgelassen
      {
        if (buttonResult[i]==FIRSTDOWN) buttonResult[i]=FIRSTUP;
        if (now-buttonDownTime[i]>=LONGCLICKTIME) buttonResult[i]=LONGCLICK;
      }
    }
    else // kein Flankenwechsel, Up/Down Status ist unverändert
    {
      if (buttonResult[i]==FIRSTUP && now-buttonDownTime[i]>DOUBLECLICKTIME) 
        buttonResult[i]=SHORTCLICK;
    }
    buttonState[i]=curState;
  } // for
  return true;
}


void verarbeitung()
{
  // dummy function 
}


void ausgabe()
// Klickstatus von geklickten Buttons mit Zeitstempel ausgeben
{
  for (int i=0;i<NUMBUTTONS;i++)
  {
    if (buttonResult[i]>=SHORTCLICK) // Ein Button wurde geklickt
    {
      Serial.print(millis()/1000.0,3); // Zeitstempel mit Millisekundenauflösung
      Serial.print("\tPin-");Serial.print(buttonPins[i]);
      if (buttonResult[i]==SHORTCLICK) Serial.println(" CLICK");
      else if(buttonResult[i]==DOUBLECLICK) Serial.println(" DOUBLE CLICK");
      else if(buttonResult[i]==LONGCLICK) Serial.println(" LONG CLICK");
    }
  }
}

void benchmark() {
  static unsigned long counterStartTime;
  static unsigned long counter;
  counter++;
  if (counter>=1000000L)
  {
    Serial.print("Average Time per loop(): ");
    Serial.print((micros()-counterStartTime)/1000000.0);
    Serial.println(" microseconds");
    counter=0;
    counterStartTime=micros();
  }
}


void loop() {
  if (eingabe())
  {
    verarbeitung();
    ausgabe();
  }
  benchmark();
}

Die Zeiten sind im Code frei konfigurierbar.

Wenn Du auf den "Doubleclick" verzichten möchtest, setzt Du die DOUBLECLICKTIME am besten auf eine Zeit, die niedriger als die PRELLZEIT ist, zum Beispiel:

#define DOUBLECLICKTIME 4

Dann werden keine Doppelklicks mehr ausgewertet.

Die "eingabe()" Funktion wertet übrigens ein ganzes Array von Button-Pins aus, falls Du nicht nur einen Schalter mit zwei oder drei Funktionen haben möchtest, sondern "viele Schalter mit mehreren Funktionen".

1 Like

... danke.
Ich probier das auch mal aus.
Eine Frage als Newbie habe ich aber schon mal.

Was ist der Unterschied zwischen bspw.:

#define doublecklicktime 400

int doubleclicktime = 400

Danke und Gruß
der "Stevie"

Grob gesagt, das zweite ist eine Variable, der verschiedene Werte innerhalb des laufenden Programms zugeordnet werden kann.

Das erste ist eine Anweisung an den Compiler, überall im Programmcode das Wort doublclicktime durch die 3 Zeichen 400 zu ersetzen.

und eigentlich willst du

const int doubleclicktime=400;

Da weiss der Compiler:

  1. was für ein Datentyp das ist
  2. Dass er keine Variable im RAM dafür braucht, wenn es sich vermeiden lässt, weil der Wert sich nie ändert.

michael_x:
und eigentlich willst du

const int doubleclicktime=400;

Es sei denn, er will mit dem 4-fach Klick die Zeit für den Doppelklick verändern :wink: