Problem mit Button kurz oder lang drücken

Hallo zusammen,
ich will mit einem kurzen bzw. langen Tastendruck jeweils ein Monoflop ansteuern, welches 5 min. läuft. Ein Prellen der Taste kann vernachlässigt werden, da inzwischen das Monoflop getriggert wurde. Als Muster verwende ich zwei LEDs, um die Zustände zu sehen.
Meine Vorlage ist ein Sketch von dieser Seite. Im Original wird der jeweilige Zustand (kurz bzw. lang gedrückt) richtig im Monitor angezeigt. Nach meiner Erweiterung mit else usw. werden die LEDs nicht richtig geschaltet. Als Schönheitsfehler wird beim Start auch gleich ein kurzer Tastendruck angezeigt:

14:39:39.250 -> Start
14:39:39.250 -> A short press is detected

Was mache ich falsch?

Hier der gesamte Sketch:

/*
 * Created by ArduinoGetStarted.com
 *
 * This example code is in the public domain
 *
 * Tutorial page: https://arduinogetstarted.com/tutorials/arduino-button-long-press-short-press
 */

// constants won't change. They're used here to set pin numbers:
const int BUTTON_PIN = 2; // the number of the pushbutton pin
int pin_LED1 = 3;        
int pin_LED2 = 4;   
const int SHORT_PRESS_TIME = 1000; // 1000 milliseconds
const int LONG_PRESS_TIME  = 1000; // 1000 milliseconds

// Variables will change:
int lastState = LOW;  // the previous state from the input pin
int currentState;     // the current reading from the input pin
unsigned long pressedTime  = 0;
unsigned long releasedTime = 0;


void setup() {
  Serial.begin(9600);
  Serial.println("Start");
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  digitalWrite(pin_LED1, LOW);
  digitalWrite(pin_LED2, LOW);
}

void loop() {
  // read the state of the switch/button:
  currentState = digitalRead(BUTTON_PIN);

  if(lastState == HIGH && currentState == LOW)        // button is pressed
    pressedTime = millis();
  else if(lastState == LOW && currentState == HIGH) { // button is released
    releasedTime = millis();

    long pressDuration = releasedTime - pressedTime;

    if( pressDuration < SHORT_PRESS_TIME )
      {
      Serial.println("A short press is detected");
      digitalWrite(pin_LED1, HIGH);
      }
      else
      {
        digitalWrite(pin_LED1, LOW);
      }

    if( pressDuration > LONG_PRESS_TIME )
      {
      Serial.println("A long press is detected");
      digitalWrite(pin_LED2, HIGH);
      }
      else
      {
        digitalWrite(pin_LED2, LOW);
      }
  }

  // save the the last state
  lastState = currentState;
}

Für den richtigen Tipp bin ich wie immer dankbar.

spricht was gegen die Verwendung einer funktionierenden Library?
Die OneButton z.B. kann click, langer Click, Doppelclick und vieles mehr.. würde ich nicht neu erfinden an deiner Stelle.

Sollte die LONG Zeit nicht länger sein als die für SHORT?

Einfache Lösung: Nach dem Drücken LONG Zeit warten. Wenn dann die Taste immer noch gedrückt ist dann LONG sonst SHORT gedrückt.

@m266
und vieleicht beschreibst du mal was soll eigentlich bei einem kurzen Impuls passieren, was bei einem langen Impuls.

Wodurch soll welche LED eingeschaltet werden?
Wodurch soll welche LED wieder ausgeschaltet werden?

weil eigentlich machen deine if/else wenig Sinn.

edit:
so macht das Beispiel Sinn.
Wenn man den Button debounced funktionierts:

/*
   Created by ArduinoGetStarted.com

   debounced by noiasca

   Tutorial page: https://arduinogetstarted.com/tutorials/arduino-button-long-press-short-press
*/

// constants won't change. They're used here to set pin numbers:
const int BUTTON_PIN = A0; // the number of the pushbutton pin
const int SHORT_PRESS_TIME = 500; // 500 milliseconds

// Variables will change:
int lastState = LOW;  // the previous state from the input pin
int currentState;     // the current reading from the input pin
unsigned long pressedTime  = 0;
unsigned long releasedTime = 0;
unsigned int debounceTime = 50;

void setup() {
  Serial.begin(9600);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop() {
  // read the state of the switch/button:
  currentState = digitalRead(BUTTON_PIN);

  if (lastState == HIGH && currentState == LOW)       // button is pressed
    pressedTime = millis();
  else if (lastState == LOW && currentState == HIGH && millis() - pressedTime > debounceTime) { // button is released
    releasedTime = millis();

    long pressDuration = releasedTime - pressedTime;

    if ( pressDuration < SHORT_PRESS_TIME )
    {
      Serial.println("A short press is detected");
    }
    else
    {
      Serial.println("A long press is detected");
    }
  }
  // save the the last state
  lastState = currentState;
}

Damit hatte ich schon experimentiert, aber keine befriedigende Lösung gefunden.

Im Original-Sketch funktioniert das einwandfrei:
Kurzer Tastendruck: "A short press is detected"
Langer Tastendruck: "A long press is detected"

funktioniert einwandfrei:
kurz toggelt LED A
lang toggelt LED B

// by noiasca
#include "OneButton.h" // The library internals are explained at  http://www.mathertel.de/Arduino/OneButtonLibrary.aspx

constexpr byte inputPin = A0;
constexpr byte ledAPin = 13;
constexpr byte ledBPin = 11;

OneButton button(inputPin, true); // The 2. parameter activeLOW is true, because external wiring sets the button to LOW when pressed.

int ledAState = LOW;
int ledBState = LOW;

void click()
{
  Serial.println("click");
  ledAState = !ledAState; // reverse the LED
  digitalWrite(ledAPin, ledAState);
}

void longPressStop()
{
  Serial.println("longPressStop");
  ledBState = !ledBState; // reverse the LED
  digitalWrite(ledBPin, ledBState);
}

void setup()
{
  Serial.begin(9600);
  Serial.println("One Button Example with polling.");
  pinMode(ledAPin, OUTPUT); // sets the digital pin as output
  pinMode(ledBPin, OUTPUT); // sets the digital pin as output
  button.attachClick(click);
  button.attachLongPressStop(longPressStop);
} // setup

void loop()
{
  button.tick();  // keep watching the push button:
  // You can implement other code in here or just wait a while
}

Bei einem kurzen Impuls wird Monoflop 1 getriggert; beim langen Impuls Monoflop 2. Jeweils mit LOW, deshalb werden nach Einbau in das gesamte Projekt LOW und HIGH getauscht. Die LEDs sind nur zum Check vorhanden und werden später nicht mehr benötigt.
Es muss auch ein Impuls sein, sonst laufen die Monoflops nicht ab und bleiben aktiv. Der Impuls wird durch die Länge des Tastendrucks bestimmt.
Ich hoffe, so ist es verständlich beschrieben.

Wie lange muss der Impuls low sein damit du das erkennst? bzw. wie lange bei kurz, wie lange bei lang?

Genau, aber die LEDs bleiben an. Sie sollten nach Loslassen der Taste wieder ausgehen.
Wie kann man das ändern?

in dem du definierst:

Hallo @m266,

ein versierter Supporter wie @noiasca hat schon eine elegante (kurze, übersichtliche und funktionsfähige(!) ) Lösung gezeigt ... Falls es Dich für die Zukunft interessiert, was oben "schief gelaufen" ist, kannst Du einen vielleicht auch längeren Blick in den folgenden Code werfen, der in seiner Programmlogik dem Sketch aus dem Post 1 entspricht; er funktioniert also genau so gut oder eben schlecht, wie der Eingangscode ... Ich habe ihn nur ein wenig so umgestellt, dass er etwas lesbarer wird:

  1. Die loop() "entschlackt", also nicht alles dorthin verlagern (siehe auch bei @noiasca )
  2. In etwas kürzere, für sich testbare Funktionen aufgebrochen (siehe auch bei @noiasca)

Alles hintereinander und ineinander verdreht zu Schreiben hieß früher mal "Spaghetti-Code" :wink:

Das Geheimnis erfolgreicher Software-Entwicklung besteht (neben viel Erfahrung und ständiger Lernbereitschaft) zu einem nicht unerheblichen Teil auf der Berücksichtigung der beiden o.a. Schritte ...

Zusätzlich hilft es natürlich auch, das Rad nicht neu zu erfinden (ebenfalls - seufz - @noiasca, der macht's einfach gut :wink: ) Das stimmt, aber manchmal dient das "Selbstmachen" ja auch dem Erfahrungsgewinn :innocent:

Ich habe zusätzlich einige Teile anders formatiert, damit es (zumindest für mich) nachvollziehbarer wird (ich schreibe viel in FreePascal/Lazarus/Delphi und mache es dort auch so ...).

/*
   Created by ArduinoGetStarted.com

   This example code is in the public domain

   Tutorial page: https://arduinogetstarted.com/tutorials/arduino-button-long-press-short-press
*/

// constants won't change. They're used here to set pin numbers:
const int BUTTON_PIN = 2; // the number of the pushbutton pin
int pin_LED1 = 3;
int pin_LED2 = 4;
const int SHORT_PRESS_TIME = 1000; // 1000 milliseconds
const int LONG_PRESS_TIME  = 1000; // 1000 milliseconds

// Variables will change:
int           lastState = LOW;  // the previous state from the input pin
int           currentState;     // the current reading from the input pin
unsigned long pressedTime  = 0;
unsigned long releasedTime = 0;
long          pressDuration = 0;


void EvaluateButtonTiming() {
  if ( pressDuration < SHORT_PRESS_TIME ){
    Serial.println("A short press is detected");
    digitalWrite(pin_LED1, HIGH);
  } else {
    digitalWrite(pin_LED1, LOW);
  }

  if ( pressDuration > LONG_PRESS_TIME ) {
    Serial.println("A long press is detected");
    digitalWrite(pin_LED2, HIGH);
  } else {
    digitalWrite(pin_LED2, LOW);
  }
  
}

void CheckButtonTiming(){
  // read the state of the switch/button:
  currentState = digitalRead(BUTTON_PIN);
  
  if (lastState == HIGH && currentState == LOW) {      // button is pressed
    pressedTime = millis();
  } else {
    if (lastState == LOW && currentState == HIGH) { // button is released
            releasedTime = millis();
            pressDuration = releasedTime - pressedTime;
            EvaluateButtonTiming();
    }
  }  
  // save the the last state
  lastState = currentState;
}

void setup() {
  Serial.begin(9600);
  Serial.println("Start");
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  digitalWrite(pin_LED1, LOW);
  digitalWrite(pin_LED2, LOW);
/*  
 *   Hier könnte man jetzt z.B. den Anteil EvaluateButtonTiming()
 *   eigenständig testen, z.B. so:
 * 
 * pressDuration = 500;
 * EvaluateButtonTiming();
 * delay(2000);
 * pressDuration = 1000;    // Ein interessanter Wert ...
 * EvaluateButtonTiming();  // Was passiert hier wohl? ;-))
 * pressDuration = 1500;
 * EvaluateButtonTiming();
 * while(1); // Stoppt hier
 *    
 */

  
}

void loop() {
  CheckButtonTiming();
  }

Mit dem Arduino habe ich noch nicht so viel Erfahrung; grabe mich aber immer durch - bis ich die Lösung habe. Wenn das nicht klappt, frage ich im Forum nach.

Ich habe vor über 10 Jahren mit WordPress begonnen und mich darin eingearbeitet. Im Laufe der Zeit habe ich mehr und mehr Kenntnisse in PHP (ist ähnlich wie C++) erworben und etliche Plugins für die Allgemeinheit programmiert, welche gerne genutzt werden. Im dortigen Forum gebe ich aus meiner mittlerweile vorhandenen Erfahrung auch gerne Hilfe an Einsteiger.
...und Lernen tue ich immer - das hört sicher nicht auf.

Leider funktioniert dein Code nicht. Wenn du LEDs an Pin3, 4 anklemmst, kannst du das nachvollziehen. Zu Beginn kommt diese Meldung im Monitor und LED1 leuchtet schwach:

18:20:09.022 -> Start
18:20:09.022 -> A short press is detected

Mit dem Sketch soll wie beschrieben ein Impuls zur Ansteuerung der beiden Monoflops erzeugt werden. Als Triggerimpuls wird einfach die Länge des Tastendrucks genommen; ist also unkritisch.
Die beiden Monoflops habe ich in der bisherigen Version meines Projekts mit zwei Tasten (aus der Fernbedienung der Garage) angesteuert. Meine neue FB hat nur eine Taste und deshalb versuche ich es mit kurz/lang.
Zu deinem Code:
So weit bin ich noch lange nicht, lagere aber auch die einzelnen Teile meines Projekts in Funktionen aus.

kurzer Klick: Ausgang A pulst für 250ms auf LOW
langer Klick: Ausgang B pulst für 250ms (beim Loslassen!(!!!) - wenn anders gewünscht - Lib Doku lesen!)

// by noiasca
#include "OneButton.h" // The library internals are explained at  http://www.mathertel.de/Arduino/OneButtonLibrary.aspx

constexpr byte inputPin = A0;
constexpr byte ledAPin = 13;
constexpr byte ledBPin = 11;

OneButton button(inputPin, true); // The 2. parameter activeLOW is true, because external wiring sets the button to LOW when pressed.

class Output {
    uint32_t previousMillis = 0;   // time management
    const uint16_t interval = 250; // how long is the pin low
    const byte pin;                // GPIO
  public :
    Output(byte pin) : pin (pin) {}
    void begin()
    {
      digitalWrite(pin, HIGH);
      pinMode(pin, OUTPUT);
    }
    void pulse()
    {
      digitalWrite(pin, LOW);
      previousMillis = millis();
    }
    void tick()
    {
      if (digitalRead(pin) == LOW && millis() - previousMillis > interval)
      {
        digitalWrite(pin, HIGH);
      }
    }
};

Output outputA(13);  // 
Output outputB(11);

void click()
{
  Serial.println("click");
  outputA.pulse();
}

void longPressStop()
{
  Serial.println("longPressStop");
  outputB.pulse();
}

void setup()
{
  Serial.begin(9600);
  Serial.println("One Button Example with polling.");
  outputA.begin();
  outputB.begin();
  button.attachClick(click);
  button.attachLongPressStop(longPressStop);
} // setup

void loop()
{
  button.tick();  // keep watching the push button
  outputA.tick();
  outputB.tick();
  // You can implement other code in here or just wait a while
}
1 Like

Da liegt sicher ein Missverständnis vor; der von mir gepostete Code ist nicht "mein Code", sondern eigentlich Deiner ... nur anders strukturiert, siehe aus meinem Post ...

... den folgenden Code werfen, der in seiner Programmlogik dem Sketch aus dem Post 1 entspricht; er funktioniert also genau so gut oder eben schlecht, wie der Eingangscode ...

Mein Vorschlag für den von mir etwas angepassten Ursprungscode (also nicht das, was z.B. @noiasca geposted hat) wäre, die einzelnen Routinen getrennt zu testen, bis sie tun, was sie sollen und dann erst alles zusammenführen, weil Dir die Erfahrungen daraus für die Zukunft helfen könnten! Was Du aber tatsächlich damit machst, liegt natürlich komplett bei Dir!

Du schreibst:

Als Triggerimpuls wird einfach die Länge des Tastendrucks genommen; ist also unkritisch.

Da wäre ich nicht so sicher ... Wenn Du Dir Deinen Code genau anschaust (also den Anteil, den ich als CheckButtonTiming() extrahiert habe, durchläuft der Sketch die beiden if-Clauses im Falle eines Bouncings mehrfach innerhalb weniger Millisekunden. Damit müsste es auch mehrere (u.U. sehr zahlreiche) Durchläufe durch die Auswertung mit extrem kurzer "pressDuration" und der damit verbundenen Ausgabe geben ... Damit Bouncing keine Auswirkungen hat, dürfte man die Auswertung nur dann ausführen, wenn pressDuration größer als eine Minimalzeit ist, korrekt?

Du schreibst:

So weit bin ich noch lange nicht

Auch da möchte ich Dir widersprechen: Du bist auf jeden Fall soweit, insbesondere wenn Du bereits umfangreiche Erfahrungen mit PHP hast! Das Strukturieren in kleineren Funktionen sollte man von Anfang an nutzen: Erstens kann man die Programmlogik wesentlich besser im Blick behalten und zweitens lassen sich mit ein wenig Nachdenken die einzelnen Aufgaben schnell und überschaubar einzeln testen. Das erspart einem manches "graue" Haar und Frust wegen falsch gesetzter Klammern oder fehlerhafter Bedingungen (z.B. Zuweisung = statt Vergleich ==); ich spreche aus leidvoller Erfahrung. Wenn man die Beispiele von @noiasca anschaut, kann man das prima nachverfolgen!

Das ist aber nur ein Angebot, kein Muss ... Ich will Dir auf keinen Fall auf den Geist gehen :wink:

Na ja, eigentlich ist es intuitiver, wenn man sofort (bzw. so bald wie möglich) eine Reaktion sieht. Einen kurzen Tastendruck erkennt man sofort, wenn die Taste wieder losgelassen wird (und das nicht mehr als "Prellen" zählt). Den langen nach Ablauf der Zeit, wenn dann immer noch gedrückt ist.

Im Code aus #13 von noiasca funktioniert das bereits so:
Kurzer Tastendruck: Nach Loslassen Impuls
Langer Tastendruck: dto

Beim ersten Prellen wird das Monoflop bereits gestartet und da es 5 min. läuft, stört das kurze Nachtriggern nicht. Ich hatte auch zum Test die Taste entprellt, aber es gab keine Verbesserung. Die alte Version meiner Anlage läuft schon über ein Jahr nach diesem Prinzip: Taste (entspricht dem Kontakt des FB-Empfängers) nicht entprellt und startet das Monoflop einwandfrei.

Zu spät...:slight_smile:

Gehst mir nicht auf den Geist. Ich versuche immer, aus der Unterstützung des Forums zu lernen.

Vielen Dank. Fast perfekt!
Die Pins 3+4 geben auch einen Impuls als LOW heraus, wie er zum Triggern der Monoflops gebraucht wird. Momentan hängen meine beiden LEDs zu Testen an Masse, so dass sie im Ruhezustand leuchten (was hier normal ist).
Beim Start flackern sie allerdings wild und würden sofort die beiden Monoflops triggern.
Folgendes konnte ich beobachten:
LED 1 blinkt zwei mal kurz und nach einer kleinen Pause leuchtet sie dauerhaft (Pin = HIGH). Dabei leuchtet dann auch LED 2. Wie kann man das ändern, dass die LEDs beim Start nicht flackern?

Aber es sind noch welche da ... (Haare) :innocent:

Ja sicher. Aber das Grau ist einem dezenten Weiß gewichen und ein wenig kommt die Kniescheibe durch...