Keine Reaktion auf Taster (OneButton Library)

Hallo, mein kurzer Beispielcode zeigt leider überhaupt keine Reaktion auf jeglichen Tastendruck. Manchmal wird ein Tastendruck allerdings dann doch rein zufällig registriert und die entspr. Funktion ausgeführt. Ich dachte es liegt an einem vergessenen Pulldown Widerstand, da so kein definierter Spannungswert anliegt. Aber das zeigte leider auch keine Wirkung. Angeschlossen ist der Taster an einem Digitalpin und geht dann an Ground des Arduinos.

Hier mal mein Beispielcode (sehr rudimentär, allerdings wollte ich zuvor einfach erst mal testen ob die OneButton Library für meine Zwecke gut funktioniert. Im Prinzip möchte ich nur mehrere Displayseiten durchswitchen. Und irgendwie gibt mir der Compiler Fehler aus, wenn ich die attachPress Funktion nutze, obwohl in der Library Dokumentation beschrieben. Vielleicht könnt ihr mir ja etwas auf die Sprünge helfen:

#include "Arduino.h"
#include <Wire.h>							
#include <Adafruit_SSD1306.h>			
#include <Adafruit_GFX.h>				
#include <OneButton.h>						

#define OLED_RESET -1						
#define SCREEN_WIDTH 128					
#define SCREEN_HEIGHT 64
#define I2C_ADDRESS 0x3C					
#define buttonPin 12

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void einfacherKlick();
void zweifacherKlick();
void halteTaster();

OneButton b = OneButton(buttonPin, false);

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

	pinMode(buttonPin, OUTPUT);
	digitalWrite(buttonPin, LOW);

	b.attachClick(einfacherKlick);			
	b.attachDoubleClick(zweifacherKlick);	
	b.attachPress(halteTaster);			
	b.setPressTicks(2000);				
	 
	display.begin(SSD1306_SWITCHCAPVCC, 0x3C); 
	display.display();
	delay(100);
	display.clearDisplay();
}

void loop() {
	b.tick();	
}

void einfacherKlick(){
	display.clearDisplay();
	display.setTextSize(1);
	display.setTextColor(WHITE);
	display.setCursor(0,0);
	display.print("Teste einfachen Klick!");
	display.display();
}

void zweifacherKlick(){
	display.clearDisplay();
	display.setTextSize(1);
	display.setTextColor(WHITE);
	display.setCursor(0,0);
	display.print("Teste doppelten Klick!");
	display.display();
}

void halteTaster(){
	display.clearDisplay();
	display.setTextSize(1);
	display.setTextColor(WHITE);
	display.setCursor(0,0);
	display.print("Teste gedrueckt gehaltenen Taster!");
	display.display();
}

Nein...

2 Likes

Okay,...lags jetzt wirklich nur daran? Also Input statt Output? Gut, macht ja auch wirklich Sinn.

Kommt auf Deine Beschaltung an...

1 Like

Ausprobieren und Ergebnis mitteilen - dann sehen wir weiter :wink:

1 Like

Die Beschaltung sieht folgendermaßen aus:

Also, wenn ich den Pin als Input definiere, reagiert er zumindest wenn man zwei mal den Taster drückt. Allerdings auch wenn der Abstand zwischen den beiden malen Drücken recht groß ist. Jedenfalls springt er dann schon mal in die richtige Funktion zweifacherKlick.

Edit:
Im Konstruktor OneButton b = OneButton(buttonPin, true) den zweiten Parameter von false auf true gesetzt, was ja auch Sinn macht wenn der Taster auf Masse gezogen wird und er reagiert auch schon mal auf den einfachen Klick und springt in die richtige Funktion, allerdings sehr zeitverzögert.

Wenn Du auf Masse ziehst: Nimm den 10k-Widerstand raus und initialisiere mit OneButton(buttonPin, true, true);
In der Beschreibung der Lib kommt das als allererster Codeschnipsel:

#define BUTTON_PIN 4

/**
 * Initialize a new OneButton instance for a button
 * connected to digital pin 4 and GND, which is active low
 * and uses the internal pull-up resistor.
 */

OneButton btn = OneButton(
  BUTTON_PIN,  // Input pin for the button
  true,        // Button is active LOW
  true         // Enable internal pull-up resistor
);
1 Like

Ah stimmt, das hatte ich ganz vergessen, hatte ich beim Überfliegen auch gelesen. Jetzt klappt es schon besser. Danke schön!

Allerdings war alles noch etwas träge. Habe die beiden Zeilen:

pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, HIGH);

leicht abgeändert. Jetzt reagiert es komischerweise etwas schneller. Aber macht pinMode hier überhaupt Sinn und vor allem den Pin zuvor auf High zu ziehen?

Kannst Du beides weglassen. Das nötige macht die Library für Dich - hier ein Auszug aus dem Konstruktor, den Du aufrufst:

/**
 * Initialize the OneButton library.
 * @param pin The pin to be used for input from a momentary button.
 * @param activeLow Set to true when the input level is LOW when the button is pressed, Default is true.
 * @param pullupActive Activate the internal pullup when available. Default is true.
 */
OneButton::OneButton(const int pin, const boolean activeLow, const bool pullupActive)
{
  // OneButton();
...
  if (pullupActive) {
    // use the given pin as input and activate internal PULLUP resistor.
    pinMode(pin, INPUT_PULLUP);
  }
...
} /

Übrigens kannst Du auch die beiden "true" weglassen, die sind die Standardeinstellung, wenn Du außer dem Pin nix angibst - das findet sich in OneButton.h.

OneButton(const int pin, const boolean activeLow = true, const bool pullupActive = true);
1 Like

Super, lieben Dank!

Vielleicht noch eine Sache im Zusammenhang mit dem Taster. Wenn ich nun in die Funktionen einfacherKlick etc. springe, springt er ja per Doppelklick etc. in eine andere Funktion, weil ja die tick Funktion im Loop ausgeführt wird. Wenn ich nun aber bspw. in der Funktion einfacherKlick etwas im Loop ausführen möchte, komme ich mit while(1) nicht weiter, weil er dann nicht mehr auf den Taster reagiert und in der Schleife bleibt. Ich dachte, er würde dann trotzdem wegen der tick Funktion in der loop Funktion auf den Taster lauschen, reagieren und rausspringen, aber das macht er leider nicht. Gibt es eine Alternative, dass er bei einem Tasterdruck wieder aus der Schleife in eine der anderen Funktionen springt?

Edit 1:
Oder könnte ich auch jeweils eine zusätzliche loop Funktion in die jeweiligen Taster Funktionen packen, die dann beim jeweiligen anderen Tastendruck verlassen werden und in den loop der anderen Funktion springen?

Edit 2:
Oder gibt es zufällig sowas wie eine Boolean Funktion, die bspw. true zurückliefert wenn ein Knopfdruck über die tick Funktion registriert wird? Konnte in der Doku leider nichts dazu finden. Damit könnte ich dann über verschiedene Zustände unterschiedliche Funktionen/Displayoberflächen schalten.

Das musst Du dir gegebenenfalls selber machen. Die Funktionen, die Du hier

    b.attachClick(einfacherKlick);			
	b.attachDoubleClick(zweifacherKlick);	

der Lib mitteilst, werden bei dem jeweilgen Ereignis aus der tick-Funktion heraus aufgerufen. Da könntest Du dann auch einfach ein Flag setzen, dass Du im weiteren Verlauf von loop() abfragst.

1 Like

Also im Prinzip nur eine kleine Bool Variable anlegen und merken, wenn man in eine der Taster Funktionen springt. Und in der loop Funktion müsste man dann nur noch irgendwie prüfen, ob die tick Funktion aufgerufen wird?

Jein.

Es sind drei Funktionen, die Du an die Library hängst - die werden jeweils bei dem entsprechenden Ereignis aufgerufen.
Also wirst Du wohl drei boolean brauchen, für jedes Ereignis eins. In der Funktion setzt Du das dann und wertest es später in der loop() aus. tick() muss immer aufgerufen werden, damit OneButton überhaupt richtig funktioniert.

Nur runtergeschrieben, nicht kompiliert oder getestet und nur für ein Ereignis:

OneButton b = OneButton(buttonPin);
boolean einfachKlick; 

void einfacherKlick();

void setup()
{
	b.attachClick(einfacherKlick);	
}

void loop()
{
  b.tick();

  if (einfachKlick)
  {
    einfachKlick = false;
    // erledige das was bei einfachem Tastendruck zu tun ist
    // z.B. das Display befeuern
  }

  // alles andere
}

void einfacherKlick()
{
   // signalisiere einfachen Tastendruck
   einfachKlick = true;
}

Ist von der Konstruktion her ähnlich wie eine Interrupt Service Routine...

1 Like

Erst mal danke schön! Habe es jetzt fast genauso umgesetzt. Nur, da ich eigentlich nur zwischen 2 Seiten hin und herschalten möchte, mit einem Boolean. Den gehaltenen Taster habe ich erst mal aussortiert. Der steht von Beginn an auf true, damit die Hauptseite angezeigt wird und wenn der Taster gedrückt wird, wird er auf false gesetzt, damit er im loop in der if Abfrage die andere Seite anzeigt usw. Also bei einfachen Klick soll Seite 2 angezeigt werden, bei doppeltem Klick wieder Seite 1 usw. Leider reagiert das Programm nur irgendwann mal zufällig auf den Taster. Finde einfach nicht heraus woran es liegt. Logikfehler oder sollte ich den Boolean als Referenz an die Funktionen mit übergeben, aber ist ja eigentlich global angelegt und sollte ausreichen? Der Code sieht folgendermaßen aus:

OneButton b = OneButton(buttonPin);
boolean zweifachKlick = true; 

void einfacherKlick();
void zweifacherKlick();

void setup()
{
	b.attachClick(einfacherKlick);	
        b.attachDoubleClick(zweifacherKlick);
}

void loop()
{
  b.tick();

  if (zweifachKlick)
  {
// erledige das was bei zweifachem Tastendruck zu tun ist
    // z.B. das Display befeuern
  }
else
{
// erledige das was bei einfachem Tastendruck zu tun ist
}

  // alles andere
}

void einfacherKlick()
{
   // signalisiere einfachen Tastendruck
   zweifachKlick = false;
}

void zweifacherKlick()
{
   // signalisiere einfachen Tastendruck
   zweifachKlick = true;
}

Edit 1:
Oder muss ich den Taster entprellen und es liegt daran? Die reset() Funktion der OneButton Library ist ja nicht damit gleichgestellt? Obwohl, ich habe gerade gelesen, dass sich die Library schon um das Entprellen kümmert. Also vielleicht doch besser richtigen Pulldown?

Edit 2:
Okay, arbeite jetzt mit einem richtigen Pullup Widerstand und es funktioniert auch alles soweit, nur die Latenz zwischen Knopfdruck und Ereignis ist noch ganz schön träge. Weiß zufällig jemand woran das liegen könnte?

Hallo,
warum baust Du denn die if Abfrage noch mal in den Loop ein, mach das was Du machen willst doch direkt in der entsprechenden Function
Schau dir doch mal die Beispiele aus der lib an in #1 hast du es ja richtig gemacht
Heinz

Ach so, damit er das was per Tastendruck passieren soll, also die jeweilige Displayanzeige permanent (im Loop) angezeigt wird, bis eben wieder ein neuer Tastendruck stattfindet.

Dazu braucht sie einen genügend häufigen Aufruf von tick().

Schwer zu erraten - habe nie mit der Library gearbeitet. Es wundert mich allerdings, dass es mit dem internen Pullup schlechter gehen soll als mit einem externen. Wieviel kOhm hast Du denn reingebaut und wie wird der Button jetzt initialisiert?

Spekulation: Es könnte sein, dass Du selbst irgendwo anders im Programm für die Verzögerung selbst verantwortlich bist (ein versteckter delay()?.

Bau Dir einen Mininmalsketch, der noch träge ist und mit Ablaufausgaben gespickt ist (Serial mit 115200bps, damit das nicht bremst). Im Monitor die Zeit mit anzeigen lassen und dann siehst Du schon wo es hakt.

1 Like

Hallo,
Sorry das hatte ich übersehen. So ein Seitenwechsel für ein Display macht man ehr mit einem Taster und rollt dann durch die Seiten 1,2,3,1,2,3 usw. oder auch mit zwei Tastern dann aber vor und zurück. Das kann dann zu einem "drei Tasten Menue" führen, mit dem Du durch mehrere Seiten navigieren und in einen Eingabe-Modus wechseln kannst um ein paar Werte eingeben zu können. z.B
Taster plus
Taster minus
Taster Modus
da kann man dann auch schön sinnvoll mit den Click, doubleClick, DuringLongPress Ereignissen arbeiten. Ich habe mal Dein Gerüst ein bisschen umgebaut auf 3 Seiten und nur einen Taster.

#include <OneButton.h>
const byte buttonPin = 3;
OneButton b = OneButton(buttonPin, true);
byte seite = 1;

void setup()
{
  Serial.begin(115200);
  b.attachClick(einfacherKlick);
}

void loop()
{
  b.tick();
  switch (seite) {
    case 1:
      Serial.println("Seite1 aktiv");
      break;
    case 2:
      Serial.println("Seite2 aktiv");
      break;
    case 3:
      Serial.println("Seite3 aktiv");
      break;
  }
  // alles andere
  
}

void einfacherKlick()
{
  // signalisiere einfachen Tastendruck
  seite++;
  if (seite == 4) seite = 1;
  Serial.println(seite);
}

Ein gesamtes Beispiel für ein 3Tasten Menüe findest Du z.B hier.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.