sheen.bot-Logo

← Einblicke

Warum registriert ein Arduino-Taster fünf Betätigungen statt einer?

21. Okt. 2026·Sheen Robotics
Warum registriert ein Arduino-Taster fünf Betätigungen statt einer?

Taster schließen Kontakte nicht sauber: Federndes Metall prellt physikalisch über mehrere Millisekunden und löst bei schnellen Mikrocontrollern Mehrfachauslöser aus. So funktioniert das Schalterprellen und so beheben Sie es ohne delay().

Wenn ein Arduino bei einem einzelnen Tastendruck fünf oder sechs Zählimpulse registriert, ist weder der Mikrocontroller defekt noch der Code verhext. Der Taster prellt schlichtweg physikalisch. Ein Kurzhubtaster enthält zwei dünne Federbleche, die beim Drücken aufeinandertreffen. Aus menschlicher Sicht fühlt sich das Klicken augenblicklich an. Auf der Ebene des Mikrocontrollers – wo ein standardmäßiger ATmega328P mit 16 MHz etwa sechzehn Befehle pro Mikrosekunde abarbeitet – prallen die Kontaktzungen aufeinander, federn zurück, berühren sich erneut und kommen erst nach zwei bis zwanzig Millisekunden zur Ruhe. Für den Arduino wirkt dieses mechanische Flattern wie fünf separate Tastendrücke.

Wie mechanisches Schalterprellen tatsächlich aussieht

In einer typischen Steckbrett-Schaltung mit einem externen Pull-down-Widerstand oder dem internen INPUT_PULLUP-Widerstand überwacht der Mikrocontroller-Pin einen Spannungswechsel zwischen 0V und 5V. Prüft der Code den Pin innerhalb einer schnellen void loop() mit einer einfachen Bedingung:

if (digitalRead(buttonPin) == LOW) {
  pressCount++;
}

treten zwei unterschiedliche Probleme gleichzeitig auf:

  • Pegelerkennung versus Flankenerkennung: Der Code zählt bei jedem Durchlauf der Schleife einen Treffer, solange die Taste gedrückt gehalten wird. Wenn die Schleife 200 Mikrosekunden benötigt, werden bei einer halben Sekunde gedrücktem Taster bereits 2.500 Auslösungen gezählt.
  • Kontaktprellen: Selbst bei sofortigem Loslassen des Tasters schwankt die Spannung über 5 bis 15 Millisekunden hinweg mehrfach zwischen High und Low, während die Metallfedern ausschwingen. Der Prozessor ist bei Weitem schnell genug, um jeden einzelnen Rückprall als gültige Flanke zu erfassen.

Der typische Anfängerreflex ist der Griff zu delay(). In einem rudimentären Test funktioniert das zwar, in echten Projekten entstehen dadurch jedoch weitaus schwerwiegendere Probleme als das, welches gelöst werden sollte.

Warum delay(50) die Funktion Ihres Roboters gravierend beeinträchtigt

Weist man den Arduino an, unmittelbar nach Erkennen eines Tastendrucks fünfzig Millisekunden zu warten – delay(50); –, ist das Prellen des Schalters vorüber, bevor der Mikrocontroller den Pin erneut abfragt. Der Zähler steigt nun sauber um eins.

Allerdings ist delay() eine blockierende Funktion. Sie stoppt die Befehlsausführung vollständig. Während sich der Chip in dieser 50-ms-Pause befindet, kann er weder die Pulsweitenmodulation der Motoren aktualisieren noch den Echo-Pin eines Ultraschallsensors abfragen, Ticks von Drehgebern einlesen oder eingehende serielle Befehle eines ESP32 oder Bluetooth-Moduls dekodieren. Bei einem stationären Zähler fällt eine blockierende Verzögerung kaum auf. Bei einem fahrbaren Roboter oder einer automatisierten Schranke führt ein 50-ms-Stillstand bei jedem Auslösen eines Endschalters jedoch zu verpassten Wegpunkten, Motorstottern und unberechenbarer Navigation.

Hardware-Entprellung – etwa durch einen keramischen 100-nF-Kondensator parallel zu den Schalterkontakten oder ein RC-Tiefpassfilter mit Schmitt-Trigger – bereinigt das Signal, bevor es den Mikrocontroller erreicht. Das ist schaltungstechnisch elegant, erfordert jedoch zusätzliche Bauteile, führt zu Löt- oder Steckbrett-Chaos und treibt die Materialkosten in einem Kurs mit 30 Lernenden in die Höhe. Im Unterricht ist die softwareseitige Zustandsverfolgung fast immer die bessere Wahl.

Die saubere Lösung: Flankenerkennung und millis()

Um dieses Problem im Code sauber zu lösen, sind zwei logische Schritte erforderlich: erkennen, wann sich der Zustand ändert (Flankenerkennung), und überprüfen, ob seit dem letzten Wechsel genügend Zeit vergangen ist, bevor ein neuer Zustand akzeptiert wird (Entprellzeit).

Dafür werden drei Variablen benötigt:

  • Der aktuelle Messwert des Pins.
  • Der vorherige Messwert des Pins aus dem vorangegangenen Schleifendurchlauf.
  • Ein Unsigned-Long-Zeitstempel, der festhält, wann sich der Zustand zuletzt geändert hat.

Eine robuste, nicht-blockierende Implementierung sieht wie folgt aus:

const int buttonPin = 2;
int buttonState = HIGH;
int lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 40; // milliseconds
int counter = 0;

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

void loop() {
  int reading = digitalRead(buttonPin);

  // Check if the pin state changed from bounce or physical press
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }

  // Only evaluate the state if it has held stable longer than our lockout
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // If the verified state has changed
    if (reading != buttonState) {
      buttonState = reading;
      
      // Trigger only on the press down (falling edge for INPUT_PULLUP)
      if (buttonState == LOW) {
        counter++;
        Serial.print("Verified presses: ");
        Serial.println(counter);
      }
    }
  }

  lastButtonState = reading;
  // The rest of loop() runs without interruption or delays
}

In dieser Architektur fungiert millis() als nicht-blockierende Stoppuhr. Der Code prüft den Pin kontinuierlich, setzt bei jeder Flankenänderung einen Zeitstempel zurück und akzeptiert einen Zustandswechsel erst dann, wenn der Pin 40 Millisekunden lang durchgehend stabil geblieben ist. Zwischen den Abfragen bleibt der Prozessor zu 100 % frei, um PID-Berechnungen durchzuführen oder Not-Aus-Taster abzufragen.

Wie man das in zehn Minuten am Whiteboard vermittelt

Lernende tun sich mit dem Entprellen oft schwer, wenn es als Wand aus if-Bedingungen präsentiert wird. Trennen Sie für eine verständliche Vermittlung die Mechanik von der Syntax anhand einer einfachen Analogie aus dem Alltag:

  1. Die zuschlagende Tür: Zeichnen Sie eine schwere Holztür, die ins Schloss fällt. Sie bleibt beim Auftreffen nicht sofort unbeweglich stehen, sondern klappert für den Bruchteil einer Sekunde in der Falle. Erklären Sie, dass ein Taster aus Messing- oder Stahlfedern besteht, die sich exakt genauso verhalten.
  2. Der hyperschnelle Sicherheitsdienst: Fragen Sie die Klasse: „Wenn ein Wachmann alle Personen zählt, die durch eine Tür gehen, aber nur alle fünf Minuten die Augen aufmacht – was passiert?“ Er verpasst Personen. „Was aber, wenn er die Tür 16.000 Mal pro Sekunde überprüfen kann?“ Er zählt jedes noch so kleine Klappern der Klinke als eine neue Person. Genau das tut ein Arduino mit digitalRead().
  3. Zustand vs. Flanke: Schreiben Sie die Wörter „IST“ und „WURDE“ an die Tafel. Zeigen Sie auf, dass der ursprüngliche Code fragte: „Ist der Taster gedrückt?“ (zutreffend für 200.000 Zyklen). Der neue Code muss fragen: „Wurde er gerade gedrückt?“
  4. Die 40-ms-Regel: Erklären Sie, dass das Sicherheitspersonal die Augen nicht schließt (delay), sondern auf die Armbanduhr blickt (millis). Klappert die Tür, starten wir einen kurzen 40-Millisekunden-Timer neu. Erst wenn die Tür 40 Millisekunden lang völlig stillsteht, wird ein Besucher notiert.

Wenn Sie ein Schullabor ausstatten oder Physical Computing im Unterricht der Senior Phase einführen, finden Sie im Sheen Robotics store strukturierte Lehrplanpakete und geprüfte Bauteilsätze, die gezielt unzuverlässige Steckbrettkomponenten vermeiden, um die mechanische Fehlersuche nicht unnötig zu verkomplizieren.

Sobald Lernende verstehen, dass Schalter unvollkommene mechanische Objekte sind, die mit extrem schnellen Computern interagieren, wirkt Entprellen nicht mehr wie ein rätselhafter Softwarefehler, sondern wird zu ihrer ersten echten Lektion in der Entwicklung von Embedded Systems.

#Arduino#Elektronik#Robotik#Programmieren#Unterricht

Mehr aus Einblicke