Sound Modul entprellen

Hallo!

Ich habe ein ein Sound Modul, dass am Output low ausgibt, sobald ein bestimmter Geräuschpegel überschritten wird.
Den Schwellenwert kann man mittels Potentiometer, dass sich auf dem Modul befindet, einstellen.

Ich würde jetzt gerne folgendes mit einem UNO realisieren:
Sobald ich klatsche, möchte “Pegel überschritten” das im SerialMonitor sehen.
Sobald ich innerhalb eines bestimmten Intervalls 2x klatsche soll eine LED an oder ausgehen.
Sobald ich innerhalb eines bestimmten Intervalls 3x klatsche soll die LED 2x blinken.

Meine Probleme:
Sobald ich klatsche, bekomme ich 6 mal die Nachricht “Pegel überschritten” im SM zu sehen.
Demnach funktioniert mein vorhaben nicht so wie gewollt.
Ich müsste irgendwie das Signal entprellen, weiß allerdings nicht wie ich das geschickt anstelle…

Außerdem prüfe ich momentan das Intervall noch mit der Funktion millis() ab.
Dazu speichere ich mir ab, “wann” der Pegel überschritten wurde und sobald nochmal der Pegel überschritten wird, wird überprüft ob der letzte Zeitpunkt innerhalb des Intervalls liegt.
Nach ca. 42 Tagen läuft millis() ja soweit mir bekannt “über” - und beginnt von vorne.
Es ist zwar unwahrscheinlich, aber ich hätte dennoch gerne eine Lösung, damit es nicht passieren kann, dass der Zurückliegende Zeitpunkt beim Ende von millis() liegt und der neue dann beim Anfang von millis() weil es übergelaufen ist.

Ich hoffe jemand kann mir hier probate Mittel nennen :smiley:

Hier mal ein Beispielcode, der allerdings das 3x klatschen nicht beinhaltet:

#define SoundDetectroPin  2 
#define LEDPIN    13  // onboard LED

unsigned long SoundErkannt = 0;
int intervall = 1000; // millisekunden  
int ledStatus = 0;

void setup() 
{
  pinMode(LEDPIN, OUTPUT);
  Serial.begin(9600);    
  Serial.println("Test:");  

}/*--(end setup )---*/


void loop()
{
  
  if (digitalRead(SoundDetectroPin) == LOW)
  {
    Serial.println ("Pegel überschritten!");  
 
    if(millis() - SoundErkannt < intervall)  //wenn die beiden Tönne innerhalb des intervalls liegen
    {
      if (ledStatus==0)  
      {
        ledStatus=1;
      }
      else
      {
       ledStatus =0; 
      }
      digitalWrite(LEDPIN, ledStatus);  //led an oder ausschalten  
    }
    SoundErkannt = millis();
  }  
}

Mit Entprellen hat das direkt nicht zu tun, es handelt sich nicht um einen mechanischen Taster.

Wie ich vorgehen würde,

wenn die erste Signaländerung kommt, weitere Signaländerungen sperren für x ms.

static uint32_t lastMillis;
if(millis() - lastMillis >= #WERT#)

Wir ersparen uns beim Beispiel jetzt einmal die komplette Zahlenfolge von uint32_t und machen den test mit uint8_t

millis == 10;
lastMillis == 253;

if(millis() - lastMillis >= 15)

Die Ergebnis setzt sich im MCU nun so zusammen. 10 - 253 ergibt einen negativen Wert. -243. Das heißt, von +10 nach 0 fallen die ersten 10 Zeichen weg und es wird 255 - 243 gerechnet. Somit wäre 12 nicht >= 15

Leon333: Hier mal ein Beispielcode, der allerdings das 3x klatschen nicht beinhaltet:

Hier mal ein Beispielcode, bei dem Du beliebig oft klatschen kannst:

#define SOUNDPIN  2 
#define LEDPIN    13  // onboard LED
#define intervallMax 1000 // millisekunden  
#define intervallMin 150  // millisekunden  

void setup() 
{
  pinMode(SOUNDPIN, INPUT);
  pinMode(LEDPIN, OUTPUT);
  Serial.begin(9600);    
  Serial.println("Test:");  

}/*--(end setup )---*/


byte klatschCount = 0;

void loop()
{
  static long lastKlatschTime=0; // Zum merken der Zeit, wann zuletzt geklatsch wurde
  if (digitalRead(SOUNDPIN)==LOW && millis()-lastKlatschTime>intervallMin)
  {
    klatschCount++;
    lastKlatschTime=millis();
    Serial.println ("Klatsch!");
  }
  if (millis()-lastKlatschTime>intervallMax)
  {
    if (klatschCount>0)
    {
      Serial.print(klatschCount);
      Serial.println(" mal geklatscht");
      if (klatschCount==2) digitalWrite(LEDPIN,HIGH);
      else digitalWrite(LEDPIN,LOW);
    }
    klatschCount=0;
    lastKlatschTime=millis(); // Zähler mitziehen um Probleme mit Timerüberlauf zu vermeiden
  }
}

Die Programmlogik ist wie folgt: Wenn ein Klatschen auftritt, nachdem das vorherige Klatschen länger als 150ms her ist, wird gezählt Wenn länger als eine Sekunde nicht geklatscht wird, wird die Klatschzahl ausgewertet Nach dem Auswerten der Klatschzahl wird der Zähler resettet

@ sschultewolter Was bedeutet das?

millis == 10; lastMillis == 253;

Zeit schlafen zu gehen??? ;) ;) ;) ;) ;)

Gute Nacht Uwe

uwefed: @ sschultewolter Was bedeutet das?

millis == 10; lastMillis == 253;

Zeit schlafen zu gehen??? ;) ;) ;) ;) ;)

Gute Nacht Uwe

Die sollten eigentlich vor dem Code Teil stehen. Einfach nur der Fall wenn millis 10 und lastMillis 253 entspricht, rein zur Veranschaulichung, dass ein Overload der millis funktion fast egal ist.

Moin, es gibt auch ne nette Debounce Lib die mehrfache Trigger, lange Trigger und sowas komfortabel auswertet. Bei Interesse schau ich nach, wo die rumliegt.

Wow, hier ist ja die gesamte Prominenz vertreten!
Hatte gar nicht mit so viel gerechnet.

Also erstmal: Danke!

jurs dein Code funktioniert bestens. Soweit war ich noch nicht und wäre da wahrscheinlich auch nicht so sauber hin gekommen.
Vielen Dank!

Allerdings verstehe ich nicht, warum an Ende noch einem

lastKlatschTime=millis();

gesetzt wird. Scheint also das ich das etwas grundlegendes nicht verstanden habe, weshalb mir das mit dem “Überlauf” auch nicht in den Kopf will.

Zum overload:
für den Fall:

millis == 10;
lastMillis == 253;

würde die Bedingung

if(millis() - lastMillis >= 15)

doch eigentlich nicht erfüllt sein oder?
Weil eben millis - lastMillis ==-253 ist und somit < als 15.

Was passiert da also, damit millis - lastMillis ==12 ist?

Leon333: Allerdings verstehe ich nicht, warum an Ende noch einem

lastKlatschTime=millis();

gesetzt wird. Scheint also das ich das etwas grundlegendes nicht verstanden habe, weshalb mir das mit dem "Überlauf" auch nicht in den Kopf will.

Ganz einfach. Wenn Du die Vergleichsvariable lastKlatschTime als "unsigned long" definierst, kannst Du mit dieser Differenz zwar stets bis zu einer Höchstdauer von knapp 50 Tagen feststellen, ob die Zeit intervallMax vergangen ist, seit zuletzt geklatscht wurde. Und zwar auch, wenn zwischenzeitlich ein Überlauf von millis() stattfindet:

if (millis()-lastKlatschTime>intervallMax)

Soweit alles perfekt, ohne Überlauf, mit Überlauf. Solange spätestens alle 49 Tage mindestens einmal geklatscht wird.

Nun kann aber auch der Fall eintreten, dass 50 Tage lang überhaupt nicht geklatscht wird, und in dem Fall tritt ein Überlaufen der Zeit lastKlatschTime auf. D.h. Du kannst dann nicht mehr unterscheiden, ob vor 20 Millisekunden oder vor knappen 50 Tagen zuletzt geklatscht wurde. Wenn also die Möglichkeit besteht, dass die Variable lastKlatschTime während 50 Tagen nicht aktualisiert wird, würde ca. 50 Tage nach dem letzten Klatschen automatisch die Bedingung

if (millis()-lastKlatschTime>intervallMax)

plötzlich wieder erfüllt wäre. Und zwar ohne dass tatsächlich geklatscht wird, nur wegen des Überlaufens der Variablen.

Deshalb ziehe ich mit dem Timeout von einer Sekunde die Variable lastKlatschTime mit und setze sie auf den aktuellen millis() Wert. So kann diese Variable dann niemals vom millis() Timer überlaufen werden, weil es im Programm keine logische Möglichkeit gibt, dass diese Variable 50 Tage lang nicht aktualisiert wird. Die Variable wird beim Klatschen aktualisiert, sie wird aber auch beim Timeout nach einem längeren Nichtklatschen aktualisiert.

Edit/Nachtrag: Wenn ich nochmal drüber nachdenke, könnte man das Nachziehen der lastKlatschTime-Variablen in Deinem Programm auch in fast allen Fällen weglassen, denn wenn kein Klatschen gezählt wurde, würde selbst bei erfüllter if-Bedingung ja nichts weiter passieren, weil auch nach 50 Tagen der klatschCount Zähler noch auf 0 steht und daher keine Aktion veranlasst wird, selbst wenn der millis() Zähler die lastKlatschTime-Variable überläuft.

Wenn Du folgenden Fall vernachlässigen kannst, kannst Du das Nachziehen der Variablen auch weglassen: Bei der Klatscherkennung wird ein Klatschen nur dann gezählt, wenn eine Mindestzeit seit dem letzten Klatschen vergangen ist:

if (digitalRead(SOUNDPIN)==LOW && millis()-lastKlatschTime>intervallMin)

Die Mindestzeit verhindert, dass ein einzelnes Klatschen bei hoher Messrate doppelt gezählt wird. Falls aber nun der millis() Zähler die Vergleichsvariable lastKlatschTime überläuft, entsteht nach knapp 50 Tagen eine Zeitspanne von 150 Millisekunden, in dem ein Klatschen nicht erkannt werden würde.

Falls Du also verschmerzen kannst, dass nach ca. 50 Tagen Nicht-Klatschens ein Klatschen während einer Zeitspanne von 0,15s nach dem Überlaufen der Variablen nicht erkannt wird: Lasse das Mitziehen der Variablen weg!

Leon333:
Zum overload:
für den Fall:

millis == 10;

lastMillis == 253;




würde die Bedingung


if(millis() - lastMillis >= 15)




doch eigentlich nicht erfüllt sein oder?
Weil eben millis - lastMillis ==-253 ist und somit < als 15.

Was passiert da also, damit millis - lastMillis ==12 ist?

Bei lastMillis == 12 ist bedingung erfüllt und somit der kein Overflow Problem. Musst nur daran denken, mit unsigned und nicht signed zu artbeiten.

Hatte gerade einen schönen Beitrag geschrieben und dabei ist der Groschen nun gefallen....

lastKlatschTime wir am Anfang initialisiert, aber zum einen nicht als unsigned (anscheinend ein kleiner Fehler vom ProgrammierGuru jurs im Eifer des Gefechts) und zum anderen nur einmal und zwar wenn die schleife das erste mal durchläuft... irgendwie hatte ich im Kopf, dass es jedes mal genullt wird, was natürlich totaler Quatsch ist.... es muss also so heißen

 static unsigned long lastKlatschTime=0;

Dadurch kamen dann eine Reihe von falschen Annahmen...

Wichtig ist, dass die Bedingung

 if (millis()-lastKlatschTime>intervallMax)

IMMER erfüllt ist... auch wenn gerade mal nicht geklatscht wurde (vorausgesetzt das Intervall ist kleiner als der Zeitraum zwischen aktueller Zeit und letztem Klatsch....) Eigentlich klar, aber wegen oben hatte ich da n' Denkfehler, den ich jetzt nicht mal mehr reproduzieren kann :D

Dann verstehe ich jetzt auch das "mitziehen" von lastKlatschTime.

Den Speicherüberlauf habe ich aber dennoch nicht verstanden und stefan hat meinen Beitrag nicht verstanden.

Also nochmal hoffentlich sauberer:

Die aktuelle Zeit

millis()

ist nach speicherüberlauf angenommen == 10

Vor Speicherüberlauf waren die Werte SEHR groß... Es wird angenommen, dass zum letzten mal relativ kurz vor dem Speicherüberlauf geklatscht wurde. Um es leichter zu machen, beschränken wir uns auf uint8_t und damit ist lastMillis z.B. == 250.

Die Bedingung lautete

if(millis() - lastMillis >= 15)

Die nach meiner vorherigen Logik nie erfüllt sein kann, weil millis ja wieder nur hochwandert, bis es irgendwann (bei 255) wieder überläuft. Somit ist der Wert von

millis() - lastMillis

(maximal) 255 - 250 = 5. Davor müsste millis() - lastMillis immer negativ sein, biss irgendwann 5 erreicht ist...

Zumindest wenn das Arduino einfach nur mit Zahlenwerten rechnet...

Wenn es einen intelligenten Rechenweg benutzt, bei dem automatisch der Überlauf mit einbezogen wird, also erst bis NULL subtrahiert und dann von 255 weiter runter rechnet, dann gibt es dazu meinerseits keine weiteren Fragen :D .... also sagt mir bitte das es so ist :-)

Zum Code von jurs: Die Bedingung   if (millis()-lastKlatschTime>intervallMax) ist wenn meine letzte Annahme stimmt und ich das alles Richtig verstehe, dann IMMER erfüllt, wenn zwischen der aktuellen Zeit und dem letzten mitschleifen ein größerer Zeitraum als intervallMax liegt. Somit wird die Variable lastKlatschTime alle "intervallMax"-Millisekunden aktualisiert.

Das wiederum bedeutet, dass wenn ich es nicht mitschleifen würde, dass dann die Bedingung

if (digitalRead(SOUNDPIN)==LOW && millis()-lastKlatschTime>intervallMin)

nach dem überlauf der Variablen für den Zeitraum von millis() == lastKlatschTime bis millis() == lastKlatschTime + intervallMin nicht erfüllt würde, wenn in diesem Zeitraum ein Klatschen erfolgen würde.

Hat glaube ich bei mir geklingelt!

Ich könnte auch einen größeren Zeitraum verkraften, indem das Programm nicht reagiert.... wenn ich mal klatsche und es tut sich nichts, dann dauert es bestimmt über 2 Sekunden in denen man begreift, dass es nicht geklappt hat und man einen neuen "Klatscher" gemacht hat.

Wenn das Ardunio also so "schlau" rechnet wie ich es hoffe, dann ist alles in Butter.

Leon333: Hatte gerade einen schönen Beitrag geschrieben und dabei ist der Groschen nun gefallen....

Das nennt sich rubber duck debugging: http://en.wikipedia.org/wiki/Rubber_duck_debugging

Ist nicht das erste mal, dass mir das passiert ist :D Aber das rechnen wird so gemacht, dass auch die Subtraktion den Überlauf berücksichtigt?

Leon333: es muss also so heißen

 static unsigned long lastKlatschTime=0;

Ganz genau!

Immer "unsigned long" für die Merkvariable. Mein Fehler.

Leon333: Wenn das Ardunio also so "schlau" rechnet wie ich es hoffe, dann ist alles in Butter.

Ja, das ist so schlau gemacht. Einerseits ist es schlau gemacht vom Erfinder des Integer-Datenformats, dass negative Zahlen mit gesetztem High-Bit als Zweierkomplement gespeichert werden.

Andererseits ist es schlau gemacht, den Rückgabewert der millis() Funktion als "long" zu definieren, so dass mit der Rechnung Spätere Zeit "long" minus alte Zeit "unsigned long" stets die korrekte Zeitdifferenz der beiden Zahlen ermittelt werden kann, auch wenn ein Überlauf dazuwischenliegt.

Kleines Programmbeispiel:

unsigned long alteZeit=4294967276; // unsigned long 20ms vor dem Überlaufen

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.println();
  Serial.print("Vorher: ");Serial.print(alteZeit);
  Serial.print("  Nach Ueberlauf: ");Serial.print(millis());
  Serial.print("  Differenz: ");Serial.println(millis()-alteZeit);
  delay(1000);
}