1

I am having trouble with my digital sound sensor. My goal: Clap 2 times and a led goes on. So I wrote this code. I don't know if it is correct or not. This happens: If I clap, from the serial monitor I don't get one HIGH, but a few (I tried changing the sensitivity of the soundsensor, but it doesn't help), but in my code I have some conditions where only 1 HIGH at a time is allowed. Thus, I don't know if my code is incorrect, or my sensor isn't working well. Can you guys help me please? This is my code:

int soundPin = 13;             // Digital soundPin
int ledPin = 7;
int claps = 0;                 // The amount of times I clapped
int readingState ;             // The first reading from the soundPin
int readingState2 ;            // The second reading from the soundPin
int soundDetectionTime ;       // The time after the first clap
int soundDetectionTime2 ;      // The time after the second clap
int timeBetweenSounds = 1000;  // The time between the sounds I want. There 
                                  is an If-statement further on. 

void setup(){
  pinMode(soundPin, INPUT);
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop(){
  readingState = digitalRead(soundPin);    // The first reading form the 
                                              soundPin (digital pin)

  Serial.println(readingState);

  if (readingState == HIGH){               // If the reading from the 
                                             soundPin is HIGH, then start 
                                             timing. Also claps value 
                                             increased by 1 and the 
                                             second reading is happening. 
    soundDetectionTime = millis();
    claps++ ;
    readingState2 = digitalRead(soundPin);   

      if (readingState2 == HIGH){          // If the above statement is 
                                              true, then this if-statement 
                                              is executed. If the second 
                                              reading is HIGH, 
                                              soundDetectionTime two will 
                                              start counting
    soundDetectionTime2 = millis();    // Also the claps get increased by 1, 
                                          so now claps = 2. 
        claps++;
   }
   }

   if ((soundDetectionTime - soundDetectionTime2) > timeBetweenSounds && 
claps==2){                            // If there is 1 second between the 
                                         two readings and if I clapped 
                                         twice, then LED is HIGH. 
    digitalWrite(ledPin, HIGH);
   }
   }

UPDATE: I managed to get two HIGHs seperate from each other and the led is now on. So the problem must be the sensor giving me too much HIGHs if I clap once, like: 0,0,0,0,1,1,1,0,0. What do you guys think?

2
  • What is hooked up to the sound pin? Can you provide a schematic? link to the sensor?
    – esoterik
    Commented Jul 16, 2018 at 20:44
  • This is the sensor: hobbyking.com/en_us/… Also, the schematic is very easy. I connected the GND and the 5V. And the digital pin of the sound sensor to a digital pin on the arduino.
    – jan
    Commented Jul 16, 2018 at 20:50

2 Answers 2

1

Here is a "stripped down version" of a sketch I used to replicate the Clap On, Clap Off lamp switch we had in the 1980's. It works with the KY-038 sound sensor module you currently have. You will need to install the VirtualDelay Library.

The sound sensor module is very sensitive. A quarter turn of the potentiometer around the "sweet spot" makes a huge difference. Too sensitive, and the digital output is constantly high. If it's not sensitive enough, then you have to physically "tap" the microphone to get a high/low transition out of it.

Please note: This sketch uses delay() which is "blocking code" and it's use is usually not recommended.

#include "avdweb_VirtualDelay.h"
byte clapCounter = 0;
const byte soundSensorPin = 2;
const unsigned long loopDelay = 1;
const unsigned long clapCounterDelay = 400;
unsigned long clapCounterTimerDuration = 1000;
unsigned long previousMillis = 0;

void setup(){
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop(){

  // Timer object.
  static VirtualDelay clapCounterTimer;

  // Get the current time.
  unsigned long currentMillis = millis();

  // Check for a clapping sound (1 ms works good). If too much time
  // elapses here, you could miss the "beginning" of the clap.
  if(myTimer1(loopDelay, currentMillis) == 1){

    // Clap detected.
    if(digitalRead(soundSensorPin) == 1){

      clapCounter += 1;

      // When the first the clap is detected, start a timer
      // to reset the clap counter to 0 after 1 second.
      if(clapCounter == 1){
        clapCounterTimer.start(clapCounterTimerDuration);
      }

      // Delay execution of the loop. 400 ms works good.
      delay(clapCounterDelay);

      // If a second clap is detected, toggle the LED ON/OFF.
      if(clapCounter > 1){
        digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
      }

    }
  }

  // Reset the clap counter to 0 after 1 second. If
  // you don't do this, you could clap once, wait a
  // minute, then clap again and the LED will change state.
  if(clapCounterTimer.elapsed()){
    clapCounter = 0;
  }

}

// Clap loop timer.
byte myTimer1(unsigned long loopDelay, unsigned long currentMillis){
  if(currentMillis - previousMillis >= loopDelay){previousMillis += loopDelay;return 1;}
  else{return 0;}
}

EDIT: To answer your question regarding the multiple High to Low transitions from the module's digital output, you can use a DSO to look at the relationship between the analog and digital signals it produces.

In the following image, the digital signal (yellow trace) and the analog signal (blue trace) are superimposed over each other. The white dashed line (E), is set to 2.8 VDC which is approximately the analog output pin's voltage with ambient room noise. Once you clap in front of the microphone, this wave form is generated.

enter image description here

The entire clap lasts about 5.6 ms and the module gives you 18 High to Low transitions on it's digital output pin. When the analog voltage drops below a certain voltage, the digital output goes High. When the analog voltage goes above that voltage, the digital output goes Low.

The problem is, the sound of a person clapping generates a noisy signal and the sound sensor module does not know the difference between noise and the sound you want to trigger it.

Instead of using delay() like I did, you could increment a "clap counter" to equal "1" on the first Low to High transition of the digital output pin. Next, start a non-blocking millis() timer that waits before you read the digital output pin again. If digital out is High after 400 ms, it's your second clap so change the LED state. A second millis() timer could reset the "clap counter" to 0 after 1 second.

1
  • Thank you so much man, I think it wil work now. I am going to try now
    – jan
    Commented Jul 17, 2018 at 7:47
0

Your logic is flawed; you don't have any delay between readingState and readingState2 other than a serial.Print() which you shouldn't rely on for a delay.

You need to keep track of state in your loop. You need to capture the time of the first input, see it go low, then record the time of the second input. N.b. time between instructions is blindingly fast even on 'slow' proccessors.

something like:

// edit: just gonna refactor a bit
// edit: added debounce
unsigned long start, end, time;
int state = 0;
...
#define BOUNCE 10
int dbounce(unsigned long time, unsigned long start, unsigned long len)
{
    return (time - start) > len;
}
...
void loop()
{
  pin = digitalRead(soundPin);
  time = millis();
  switch(state)
  {
  case 0:
      if (pin == HIGH)
      {
        start = millis();
        state = 1;
      }
      break;
  case 1:
      if dbounce(time,start,BOUNCE) 
          break; // one way to debounce
      if( pin == LOW)
          state = 2;
      break;
  case 2:
      if(pin == HIGH)
      {
         end = millis();
         state = 3;
      }
      break;
  case 3:
      if(dbounce(end,start,1000))
      {
         // do stuff
      }
      state = 4;
      break;
  case 4:
      if((pin == LOW) && dbounce(time,end,BOUNCE)) //another way to debounce
         state = 0; // start over  
      break;
  }
}
5
  • So, should I do it with the pulseIn function? That way I can measure the time between HIGH and LOW. How do you recommend me doing it. I just started a week ago with Arduino, I am not very familiar with different coding techniques.
    – jan
    Commented Jul 16, 2018 at 22:13
  • @jan pulse in measures how long the pin is high for, you might be able to pulseIn(pin, HIGH); start = millis(); pulsein(pin, HIGH); end = millis(); if(end-start) ...
    – esoterik
    Commented Jul 16, 2018 at 23:32
  • @jan it just occured to me that time = pulseIn(pin, LOW); might do exactly what you want.
    – esoterik
    Commented Jul 16, 2018 at 23:36
  • But the sound sensor still is a problem. Because it gives me a lot of HIGHs when I clap, not just one. Sometimes it gives: 0,0,1,0,1. The I have two HIGHs very fast with one clap
    – jan
    Commented Jul 17, 2018 at 8:00
  • @jan I added debouncing to the code, and made it a little better; hope you understand how this works.
    – esoterik
    Commented Jul 17, 2018 at 18:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.