Ein/Aus-Taster programmieren

Hallo,

ich habe hier einen Code für ein "Abstandswarnsystem".

#include <Average.h>

#include <Bounce.h>

int pingPin = 13;
int dataPin = 12;
int button  =  2;
int zustand_button;
int val_button;

Bounce button_Bouncer = Bounce(button,5 );                                     // 5 ms Bouncing für "Knopf_A"

int ledPin  =  4;
int piezo   =  8;

int array[9] = {0,0,0,0,0,0,0,0,0};                                                    // Array zur Speicherung mehrerer Abstandswerte um die Genauigkeit zu erhöhen
int average = 0;
int averageb = 0;
int min = 0;
int max = 0;

byte fall = 2;                                                                 // Fallunterscheidung

unsigned long duration;                                                        // Ermitteln der Variable "Länge der Pingrückmeldung"
unsigned long cm;                                                              // Ermitteln der Variable "Abstand in cm"

/**************************************************************************************************************************************************************************************************************************************************************/

void setup(){
  pinMode(pingPin, OUTPUT);
  pinMode(dataPin, INPUT);
  pinMode(button, INPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin,LOW);
  pinMode(piezo, OUTPUT);
  Serial.begin(9600);
  digitalWrite(pingPin, LOW);
  }

/**************************************************************************************************************************************************************************************************************************************************************/
 
void loop() {

switch (fall) {

case 1:

button_Bouncer.update ( );
val_button = button_Bouncer.read();
if (val_button != zustand_button && val_button == HIGH) {
  fall=2;
  }
break;

case 2:

do {
  for (byte x=0; x<9; x++) {
    digitalWrite(pingPin, HIGH);                                                   // Ping senden..
    delayMicroseconds(10);                                                         // ..von 10ms Dauer..
    digitalWrite(pingPin, LOW);                                                    // ..und den Ausgang wieder schließen
    duration = pulseIn(dataPin, HIGH);                                             // Länge des Signals am "dataPin" abfragen
    cm = microsecondsToCentimeters(duration);                                      // Abstand über die for-Schleife ins Array eintragen
    delay(58);
    array[x] = cm;
    }
  average = mean(array,9);
  min = minimum(array,9);
  max = maximum(array,9);
  } while (min<1 || max>3000 || max>average+1 || min<average-1);
/*  Serial.print("average: ");
  Serial.println(average);*/
  digitalWrite(ledPin,HIGH);
  fall=3;
  break;
  
case 3:

averageb=average;

do {
  for (byte x=0; x<9; x++) {
    digitalWrite(pingPin, HIGH);                                                   // Ping senden..
    delayMicroseconds(10);                                                         // ..von 10ms Dauer..
    digitalWrite(pingPin, LOW);                                                    // ..und den Ausgang wieder schließen
    duration = pulseIn(dataPin, HIGH);                                             // Länge des Signals am "dataPin" abfragen
    cm = microsecondsToCentimeters(duration);                                      // Abstand über die for-Schleife ins Array eintragen
    delay(40);
if(cm>3400) {
  cm=average;
}
    array[x] = cm;
    }
  min = minimum(array,9);
  max = maximum(array,9);
  averageb = mean(array,9);
  } while (averageb< average+2 & averageb> average-2);
  digitalWrite(ledPin,LOW);
  tone(piezo, 1400, 50);
  delay(50);
  tone(piezo, 700, 40);     
  delay(40);
  digitalWrite(ledPin,LOW);
  tone(piezo, 2800, 30);
  /*  Serial.println(array[0]);
  Serial.println(array[1]);
  Serial.println(array[2]);
  Serial.println(array[3]);
  Serial.println(array[4]);
  Serial.println(array[5]);
  Serial.println(array[6]);
  Serial.println(array[7]);
  Serial.println(array[8]);*/
  fall=2;
  break;
  }
}

unsigned long microsecondsToCentimeters(unsigned long microseconds){           // The speed of sound is 340 m/s or 29 microseconds per centimeter. The ping travels out and back, so to find the distance of the object we take half of the distance travelled.
return microseconds / 29 / 2;
}

Ich würde gerne die Abstandsmessung zu jedem Zeitpunkt per Taster ein- und ausschalten können.

Wie macht man das denn?

Versuche mit Interrupts waren sehr unbefriedigend, da die Unterbrechungen dann teilweise nicht bei Tastendruck erfolgen, sondern der Code zunächst mal weiter abgearbeitet wird.

Gruß Chris

Wenn Du es mit Interrupt verwirklichen willst, mußt Du den Taster über HW entprellen. Dann funktioniert es.
HW Entprellung über den MC14490 oder über ein RC Glied.
Grüße Uwe

Mit HW meinst Du sicher Hardware, oder?
Also komme ich mit der Bounce.h Library hier definitiv nicht weiter?
Könnte evtl. eine "Neuprogrammierung" unter Zuhilfenahme dieser Library sinnvoll sein:

http://www.pjrc.com/teensy/td_libs_MsTimer2.html

Dank Dir!

Gruß Chris

In der Interruptroutine kannst Du keine debounce-Funktion aufrufen, implementieren oder zuviel Zeit vertrödeln weil jeder neue Interruptaufruf die Interruptroutine neu startet.
Darum mußt Du extern entprellen.

Grüße Uwe

Hallo,

da ich ohne zusätzliche HW arbeiten möchte, würde ich den kompletten Code gerne umschreiben.

Damit ich mich in die richtige Richtung bewege, würde ich gerne noch Folgendes wissen:

Wenn ich es schaffe den Code ohne Verwendung von delay() aufzubauen, kann ich dann ohne HW-Debouncing auskommen und ist es dann u.U. möglich das laufende Programm mit einem Tastendruck jederzeit zu "unterbrechen"?

Gruß Chris

Den Sketch kannst Du ohne Interrupt nicht in jedem Moment unterbrechen. Du kannst den Sketch so schreiben daß der Taster ofters kontrolliert wird.
Ich sehe bei Deinem Sketch nicht so das Problem beim delay() sondern in den "do while" Schleifen. Versuch die zu eliminieren. Die loop() funktion muß mindestens 10 mal die Sekunde durchlaufen werden und dort kontrolliert werden ob der Taseter gedrückt ist.

Grüße Uwe

Zum Verständnis für mich:

Die Aussage mit den zehn Mal pro Sekunde hast Du deshalb geschrieben, da Du ganz grob gesagt davon ausgehst, dass ein Tastendruck ca. 100ms lang andauert?

Danke.

Gruß Chris

ja, so in etwa. Weil ein Tastendruck eine gewisse Länge hat und weil längere Wartezeiten auf eine Reaktion als unangenehm empfunden wird.
Grüße Uwe

Sorry wenn ich mich hier einmische. :slight_smile:

Also ich debounce in der Interrupt Routine, was auch ohne Probleme funktioniert.
Allerdings nutze ich weder Debounce.h noch delay().

Im folgenden Code geht es um eine 8 Tasten Tastatur mit Beleuchtung, ein Menümodus in den bei Tastendruck länger als 2 Sekunden gewechselt wird,ist auch dabei. Interrupt ist 0 (Digital in 3) und am Analog 0 Pin werden die Tasten eingelesen.

Der Sketch ist sehr wild durcheinander. Hoffe du kannst trotzdem etwas damit anfangen.

#include <LCD.h>
#include <LiquidCrystal_SR3W.h>
#include <TimerOne.h>

LiquidCrystal_SR3W iLCD(4,3,13); 
int menu = 0;
int stat=0;
const int keys = 8;
const int keyPin = A0;
int keyPinValue = 0;
int keyPinValuechk = 0;
const int D1 = 10;
const int CLK = 9;
const int STR = 11;
unsigned long duration;
volatile unsigned long milis =0;
volatile unsigned long seks = 0;
int zahl = 0;
int counter = 0;
boolean pressed = false;
volatile unsigned int key = 0;
volatile unsigned int lastkey = 0;
volatile unsigned int war = 0;
volatile unsigned int keydown =0;

// TASTATUR AN A0
int table[8][2]={
  {900,1000}, // 1
  {800,899}, // 2
  {680,780}, // 3
  {580,679}, // 4
  {480,579}, // 5
  {390,579}, // 6
  {280,389}, // 7
  {100,279}  // 8
};
  
int led[8] ={
  128,64,32,16,8,4,2,1};
  
  // IRQ 0 ROUTINE MIT DEBOUNCE UND TIMER 
void checkInput() {
          milis = 0;
        seks =0; 
 
       int val;
        keyPinValue = analogRead(keyPin);
        val=keyPinValue;
  
   //alle tasten durchgehen
 for (int i = 0; i < keys; i++) {
   
   // übereinstimmung gefunden?
   if(val >= table[i][0] && table[i][1]){
  milis = 0;
  seks =0;
     while(!milis >= 30000){}
     //wert neu einlesen
     keyPinValuechk = analogRead(keyPin);
     //nochmal, übereinstimmung?
     if(keyPinValuechk >=table[i][0]&& keyPinValuechk <= table[i][1]){
       key = i+1;
         milis = 0;
        seks =0;
      keydown=1;
       //gleich wie letzte taste? nein?
       if(key != lastkey) {
         digitalWrite(STR,LOW);
         shiftOut(D1,CLK,MSBFIRST,led[i]);
         digitalWrite(STR,HIGH);
         lastkey = key;
       milis = 0;
        seks =0;
          
       }
       // doch?
       if(key == lastkey) {
              
        // return key;
         break;
       }
     }
   }
 }
 if( val <=50) {
  milis = 0;
  seks =0;
 
   // taste losgelassen, oder keine gedrückt
   key=0;
   keydown=0;   
   duration=0;

 }
       milis = 0;
        seks =0;
   
}
void setup() {
  analogReference(DEFAULT);
     pinMode(2,INPUT);
     
     pinMode(5,OUTPUT);
     pinMode(6,OUTPUT);
     pinMode(7,OUTPUT);
     pinMode(8,OUTPUT);
     pinMode(9,OUTPUT);
     pinMode(10,OUTPUT);
     pinMode(11,OUTPUT);
     pinMode(13,OUTPUT);
      digitalWrite(5,LOW);
       digitalWrite(6,LOW);
        digitalWrite(7,LOW);
         digitalWrite(8,LOW);
          digitalWrite(9,LOW);
           digitalWrite(10,LOW);
            digitalWrite(11,LOW);
           digitalWrite(13,LOW);
     
  pinMode(D1,OUTPUT);
  pinMode(CLK,OUTPUT);
  pinMode(STR,OUTPUT);
  digitalWrite(D1,LOW);
  digitalWrite(CLK,LOW);
  digitalWrite(STR,LOW);

  
  //digitalWrite(STR,HIGH);
  shiftOut(D1,CLK,MSBFIRST,led[0]);
  digitalWrite(STR,HIGH);
  delay(200);
  iLCD.begin ( 40, 2 );
  iLCD.home();
  iLCD.print("        MIDI-MASTER ROUTER V1.0         "); 
  iLCD.setCursor ( 0, 1 );        // go to the next line
  iLCD.print("                                        ");

  // STATE MACHINE ANFAHREN
  Timer1.initialize(10000); 
  Timer1.attachInterrupt( timerIsr );
  // TASTATUR IRQ ANFAHREN
  attachInterrupt(0,checkInput,CHANGE);
  
}
 
void timerIsr()
{ 
  milis += 10000;
  seks += 10000;
  // ÜBERLAUF KILLEN
  if(milis >= 100000) { milis = 0;}
  if(seks >= 10000000) { seks = 0;}
 
}
void dings(int key){
if( key != 0){
    
  iLCD.home();
  iLCD.print("MENU MODUS ENTER NAME:__________________"); 
  iLCD.setCursor ( 0, 1 );        // go to the next line
  iLCD.print("0123456789ABCDEFGHIJKLMNAOPRSTUVWXYZ  OK");
  iLCD.setCursor ( 22, 0 );
  iLCD.blink();
 
 }
}
void blinkIsr()
{
  if(stat==0){stat=9;}
  else{stat=0;}
   digitalWrite(STR,LOW);
  shiftOut(D1,CLK,MSBFIRST,stat);
  digitalWrite(STR,HIGH);

}
void warte(unsigned int mils) {
  milis = 0;
  while((mils - milis) == 0){
  }
}
void loop() {
  
  // STATE MACHINE ABFRAGEN UND GEDÖNS
 if(keydown == 1 && seks >=2000000 && menu == 0) {
  
    menu = 1;
    dings(key);
     warte(600000);
  }
  if(keydown == 1 && menu == 1 && key==8){
    menu = 0;
    iLCD.home();
    iLCD.print("        MIDI-MASTER ROUTER V1.0         "); 
    iLCD.setCursor ( 0, 1 );        // go to the next line
    iLCD.print("                                        ");

  }
  if(seks == 100000 && menu == 0) {
   int dat= analogRead(keyPin);
 iLCD.home();

// TASTEN CODES AUSGEBEN
iLCD.setCursor ( 12, 1 );        // go to the next line
 iLCD.print("Key:");
 iLCD.setCursor ( 17, 1 );        // go to the next line
 iLCD.print(key);
  iLCD.setCursor ( 20, 1 );        // go to the next line
 iLCD.print("          ");
 
 iLCD.setCursor ( 20, 1 );        // go to the next line
 iLCD.print(dat);
  }

}

In einer Interruptroutine kann man kein SW Debouce machen, da die Interruptroutine so kurz wie möglich sein soll. Dies weil ein weiterer Aufruf richtig abgearbeitet werden soll, bzw nicht verloren gehen soll.
@ daimonea Dir funktioniert es weil niemand die Taste so oft und schnell drückt und weil das Analogread auch einige Zeit verliert. Für einen Drehgeber ist Dein Sketch nicht verwendbar.

Interrupt 0 ist beim Arduino 2009 und UNO an D2 nicht an D3. Wie sind die Tasten an D2 angeschlossen?

Viele grüße Uwe

@uwe
Das einzig wichtige bei einer irq routine ist, dass es keine endlosschleife gibt;) :smiley:

Mit D3 meinte ich natürlich arduino digital pin 2. Hab die pinbelegung vom controller genannt, ist natürlich quatsch in dem zusammenhang.
Ob diese routine für drehgeber funktioniert oder nicht, war weder die frage noch die antwort. Hehe.
Es geht um taster. Und das macht sie wirklich perfekt!
Für einen drehgeber hast du am arduino uno ja zur not noch irq 1 (jetzt aber wirklich pin d3).
Also gibt es auch da kein problem.
Und wann immer sw debounce gewünscht ist, ist es völlig egal ob du die zeit von mir aus 40ms in einem irq verbrätst, oder in hauptloop, macht keinen unterschied.
Die timer betrifft das sowieso nicht, die laufen auch trotz irq 0 oder 1 weiter.
Da ist das periodische abfragen verschwenderischer.
Meine Meinung nach jedenfalls. Soll halt jeder wie er will oder muss. :smiley:

Zur tastatur mach ich dir gerne einen schaltplan. Muss nur schnell zum rechner huschen.

Edit: Schaltplan im Anhang

daimonea:
@uwe
Das einzig wichtige bei einer irq routine ist, dass es keine endlosschleife gibt;) :smiley:

Und die 2. Bedingung ist, daß die Interruptroutine abgearbeitet ist, bevor einen neuer interrupt kommt.
Grüße Uwe

uwefed:

daimonea:
@uwe
Das einzig wichtige bei einer irq routine ist, dass es keine endlosschleife gibt;) :smiley:

Und die 2. Bedingung ist, daß die Interruptroutine abgearbeitet ist, bevor einen neuer interrupt kommt.
Grüße Uwe

Sagt wer? Wenn die irq routine läuft, werden alle interrupts gesperrt!
Ein erneutes auslösen per interrupt ist also unmöglich!
Erst wenn das ende der irq routine erreicht ist wird der assembler befehl reti ausgeführt und somit die interrupts wieder möglich.
Das ist ja auch der grund warum eine irq routine so kurz wie möglich sein soll, weil eben sonst nichts ausgeführt wird und man den hauptloop (und nicht nur den) ausbremst.
Was aber in meinem beispiel zu vernachlässigen ist.

daimonea:
... Wenn die irq routine läuft, werden alle interrupts gesperrt!
Ein erneutes auslösen per interrupt ist also unmöglich!
Erst wenn das ende der irq routine erreicht ist wird der assembler befehl reti ausgeführt und somit die interrupts wieder möglich.
Das ist ja auch der grund warum eine irq routine so kurz wie möglich sein soll, weil eben sonst nichts ausgeführt wird und man den hauptloop (und nicht nur den) ausbremst.
Was aber in meinem beispiel zu vernachlässigen ist.

Nein. Der Compiler fügt automatisch kein SEI bzw CLI ein. Du mußt es mit SEI bzw CLI oder mit interrupts()
bzw noInterrupts() selbst programmieren.

Grüße Uwe

Alternativ kann man das Auslesen des Tasters auch in eine eigene Funktion packen, die dann mehrmals an verschiedenen sinnvollen Stellen in der loop() aufgerufen wird. Die Funktion liefert TRUE oder FALSE zurück und in Abhängigkeit davon, kann man das Verhalten der Anwendung Steuern (z.B. keine Abstandswarnungen mehr). Das muß ja auch gemacht werden, wenn der Taster per Intterrupt abgefragt wird. Irgendwo in der loop() muss ja dann ein Flag ausgewertet werden, um zu wissen in welchem Zustand (An / Aus) man ist. Und aus Performancegründen sollte das sicher auch an mehreren Stellen sein, jenachdem wie schnell ein loop() Zyklus dauert.

mkl0815:
Alternativ kann man das Auslesen des Tasters auch in eine eigene Funktion packen, die dann mehrmals an verschiedenen sinnvollen Stellen in der loop() aufgerufen wird. Die Funktion liefert TRUE oder FALSE zurück und in Abhängigkeit davon, kann man das Verhalten der Anwendung Steuern (z.B. keine Abstandswarnungen mehr). Das muß ja auch gemacht werden, wenn der Taster per Intterrupt abgefragt wird. Irgendwo in der loop() muss ja dann ein Flag ausgewertet werden, um zu wissen in welchem Zustand (An / Aus) man ist. Und aus Performancegründen sollte das sicher auch an mehreren Stellen sein, jenachdem wie schnell ein loop() Zyklus dauert.

Da hätte ich eher den Vorschlag, die loop() Funktion zu überdenken, so dass sie so schnell ist, dass einmal pro Zyklus den Taster ( oder das von der ISR gesetzte Flag ) zu lesen ausreicht. Wenn ein mal pro Zyklus nicht reicht, ist zwei mal meist auch keine echte Lösung.

@uwe : klar sollte eine ISR so schnell wie möglich fertig sein, und vor allem nicht auf andere Interrupt-Routinen warten ( Serial ). Aber wenn sie denn endlich fertig ist und ein neuer Interrupt schon ansteht, ist das auch ok.
( Wenn man damit leben kann, dass loop() nie mehr dran kommt )

uwefed:

daimonea:
... Wenn die irq routine läuft, werden alle interrupts gesperrt!
Ein erneutes auslösen per interrupt ist also unmöglich!
Erst wenn das ende der irq routine erreicht ist wird der assembler befehl reti ausgeführt und somit die interrupts wieder möglich.
Das ist ja auch der grund warum eine irq routine so kurz wie möglich sein soll, weil eben sonst nichts ausgeführt wird und man den hauptloop (und nicht nur den) ausbremst.
Was aber in meinem beispiel zu vernachlässigen ist.

Nein. Der Compiler fügt automatisch kein SEI bzw CLI ein. Du mußt es mit SEI bzw CLI oder mit interrupts()
bzw noInterrupts() selbst programmieren.

Grüße Uwe

Hallo Uwe

Ich habe nicht vom Compiler gesprochen, sondern davon, was auf Assembler Ebene passiert bzw passieren muss!
Würde man eine Interrupt Routine mit RET statt mit RETI beenden, würde zwar der Stack zurückgeholt, aber es gäbe keinen weiteren Interrupt mehr.
So, und wenn ich deine Posts vorhin richtig verstanden habe, meintest du, dass wenn ein weiterer Interrupt 0 wie in dem Beispiel vorhin, ausgelöst würde, ehe die bereits laufende interrupt routine abgearbeitet wäre, wäre das problematisch...
Ich sage, nein. weil die interrupt routine nur dann unterbrochen wird, wenn sie am ende angelangt ist (sei es, weil sie komplett durchläuft oder per break Anweisung beendet wird) und der assembler befehl RETI abgesetzt wurde.
Das bedeutet, dass eine Interrupt Routine, auch wenn sie ein paar millisekunden dauert, IMMER erst abgearbeitet wird, ehe ein neuer Interrupt passieren kann.
Schon alleine wegen des Stacks wäre alles andere fatal.
Auch im Umkehrschluss würde das bedeuten, dass egal wie kurz eine IRQ Routine gehalten wird, tritt ein weiterer IRQ nur schnell genug auf, hilft nur noch Reset.
Mehr wollte ich damit auch gar nicht sagen.

Die Arduino-Entwicklerumgebung bzw Compiler Linker ecc fügen automatisch keine Befehle/Funktionen ein, mit denen ein Interrupt während der Interruptroutinenabarbeitung blockiert wird. Ein Interrupt ruft immer die Interruptroutine auf, egal was der Kontroller gerade macht.
Wenn Du willst, daß der Interrupt gesperrt wird, mußt Du die entsprechende Funktion/Opcode einfügen.
Grüße Uwe

uwefed:
Die Arduino-Entwicklerumgebung bzw Compiler Linker ecc fügen automatisch keine Befehle/Funktionen ein, mit denen ein Interrupt während der Interruptroutinenabarbeitung blockiert wird. Ein Interrupt ruft immer die Interruptroutine auf, egal was der Kontroller gerade macht.
Wenn Du willst, daß der Interrupt gesperrt wird, mußt Du die entsprechende Funktion/Opcode einfügen.
Grüße Uwe

Hallo Uwe,
ich will mich echt nicht streiten, aber es interessiert mich jetzt einfach. :smiley:
Versuche folgendes:
lege eine volatile Variable zB volatile int irq = 0; an
füge ganz Oben im Code der Interrupt Routine irq++; ein
Danach ein delay(300000);
und lass dir im Loop per serielle Schnittstelle oder so wie ich den Inhalt der irq variable auf einem lcd display anzeigen.
Danach starte das Programm auf einem Arduino und löse Interrupts aus.
So viele du willst. Drück die Taste schnell, langsam so oft wie möglich und dann warte wie viele Interrupts gezählt wurden.

Deiner Meinung nach müssten es bei 100 Tastendrücken 200 irqs (rise & fall), auch dann, wenn delay eigentlich noch gar nicht abgelaufen ist.
Denn weder meine irq routine, noch delay sperren die interrupts. Richtig?

Falsch! Es werden in dem Zeitraum, wenn überhaupt 3 irqs gezählt.

Warum?

Hallo,

nach stundenlanger Arbeit habe ich nun den Sketch so umgeschrieben, dass nun kein delay() mehr verwendet wird.

Mangelnde Geduld kann man mir mittlerweile definitiv nicht (mehr) vorwerfen. :slight_smile:

Leider funktioniert die Geschichte mit der Taste als Ein-/Ausschalter noch immer nicht zuverlässig. :frowning:

Hier der Code:

#include <Average.h>

#include <Bounce.h>

int pingPin = 13;
int dataPin = 12;
int button  =  2;
int zustand_button;
int val_button;
unsigned long buttonzaehler = 0;

Bounce button_Bouncer = Bounce(button,10 );                                     // 10 ms Bouncing für "Knopf_A"

int ledPin  =  4;
int piezo   =  8;

int array[9] = {0,0,0,0,0,0,0,0,0};                                            // Array zur Speicherung mehrerer Abstandswerte
int x = 0;
int average = 0;
int averageb = 0;
int min = 0;
int max = 0;

byte zustand = 1;                                                              // Zustandsdefinition

unsigned long duration;                                                        // Ermitteln von "Länge der Pingrückmeldung"
unsigned long cm;                                                              // Ermitteln von "Abstand in cm"

unsigned long zeit = 0;
unsigned long neuzeit = 0;

/**************************************************************************************************************************************************************************************************************************************************************/

void setup(){
  pinMode(pingPin, OUTPUT);
  pinMode(dataPin, INPUT);
  pinMode(button, INPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin,LOW);
  pinMode(piezo, OUTPUT);
  Serial.begin(9600);
  digitalWrite(pingPin, LOW);
  }

/**************************************************************************************************************************************************************************************************************************************************************/
 
void loop() {

button_Bouncer.update ( );
val_button = button_Bouncer.read();
if (val_button != zustand_button && val_button == HIGH){
  zustand=2;
}
zustand_button = val_button;

button_Bouncer.update ( );
val_button = button_Bouncer.read();
if (val_button != zustand_button && val_button == LOW){
  zustand=1;
  digitalWrite(ledPin,LOW);
}
zustand_button = val_button;

if (zustand == 2){                                                               // Ping senden
  digitalWrite(pingPin, HIGH);
  zeit=millis();
  zustand=3;
}

if (zustand == 3 && millis()>zeit+10){                                           // Nach 10ms Ping auswerten
  digitalWrite(pingPin, LOW);
  duration = pulseIn(dataPin, HIGH);                                             // Länge des Signals am "dataPin" abfragen
  cm = microsecondsToCentimeters(duration);
  zustand=4;
}

if (zustand == 4 && millis()>zeit+68){                                           // Überprüfen, ob ein nicht veränderlicher Abstand ermittelt wurde.
  array [x]=cm;
  x++;
  if(x==9){
    x=0;
  }
  average = mean(array,9);
  min = minimum(array,9);
  max = maximum(array,9);
  
  if(min>5 && max<3000 && max<average+2 && min>average-2){                       // Falls Kriterien erfüllt werden..
    zustand=5;                                                                   // ..Zustand "Abstandsveränderung" aktivieren.
  }
  else{
    zustand=2;
    Serial.println(min);
    Serial.println(max);
    Serial.println(average);
  } 
}

if (zustand == 5){  // Ping senden
  digitalWrite(ledPin,HIGH);
  digitalWrite(pingPin, HIGH);
  zeit=millis();
  zustand=6;
}

if (zustand == 6 && millis()>zeit+10){                                           // Nach 10ms Ping auswerten
  digitalWrite(pingPin, LOW);
  duration = pulseIn(dataPin, HIGH);                                             // Länge des Signals am "dataPin" abfragen
  cm = microsecondsToCentimeters(duration);
  zustand=7;
}

if (zustand == 7 && millis()>zeit+50){                                           // Überprüfen, ob sich der Abstand über das definierte Maß verändert hat.
  array [x]=cm;
  x++;
  
  if(x==9){
    x=0;
  }
  
  if(cm>3400) {
  cm=average;
  }
  
  averageb = mean(array,9);
  min = minimum(array,9);
  max = maximum(array,9);
  
  if(averageb> average+2 || averageb< average-2){                                // Falls Kriterien erfüllt werden..
    zustand=8;                                                                   // ..Zustand für Abstandsveränderung aktivieren.
  }
  else{
    zustand=5;
    Serial.println(min);
    Serial.println(max);
    Serial.println(average);
  } 
}

if (zustand == 8){
  tone(piezo, 1400, 50);
  delay(50);
  tone(piezo, 700, 40);     
  delay(40);
  digitalWrite(ledPin,LOW);
  tone(piezo, 2800, 30);
  digitalWrite(ledPin,LOW);
  zustand=2;
}
  
}

/**************************************************************************************************************************************************************************************************************************************************************/

unsigned long microsecondsToCentimeters(unsigned long microseconds){           // The speed of sound is 340 m/s or 29 microseconds per centimeter. The ping travels out and back, so to find the distance of the object we take half of the distance travelled.
return microseconds / 29 / 2;
}

Das hier funktioniert nicht richtig:

button_Bouncer.update ( );
val_button = button_Bouncer.read();
if (val_button != zustand_button && val_button == HIGH){
  zustand=2;
}
zustand_button = val_button;

button_Bouncer.update ( );
val_button = button_Bouncer.read();
if (val_button != zustand_button && val_button == LOW){
  zustand=1;
  digitalWrite(ledPin,LOW);
}
zustand_button = val_button;

Vielleicht kann ja doch noch einmal jmd. drübergucken..?

Danke.

Gruß Chris