Hoping for advice: Using Mic and mulitple requirments to control LED

Hello Arduino folks,

Warning: noob.
I'm brand new to the forum, to arduino and I've never coded anything. I'm a self taught industrial designer experimenting with code for the first time. Youtube and sticky threads have been my friends thus far, and I've gone through many of the Example Sketches looking for answers. Sincere apologies in advance if I've overlooked obvious resources that might solve my issues.

Goal
I'm designing a light that is controlled by breathing on a sensor rather than flicking a switch. In an ideal world, blowing on the mic as if you were stoking a fire turns on the light, blowing some more stokes the fire (ie. the light's brightness increases) and a quick exhalation (ie. blowing out a candle) turns the light off.

Issues:

  • I'm struggling with Analog Inputs, Digital Outputs, and having multiple constraints that need to be satisfied to trigger the LED.

Achievements so far:

  • I've successfully hooked up the board, mic, and LED. I have cobbled together a sketch based on examples and resources found online that allows blowing on the mic to turn on the LED, increase the brightness with a Fade, and decrease the brightness with more breath.

Help please
In plain English, this is what I would like to accomplish:

Turn on: IF LED is OFF, AND IF mic senses input above 550 AND input duration is < 0.5s, LED turns ON.

Turn up brightness: IF mic senses input above 550 AND input duration is > 0.5s, INCREASE BRIGHTNESS 4 steps per second for duration of input until maximum brightness is achieved.

Turn off: IF LED is ON, AND IF mic senses input above 550 AND input duration is < 0.5s, LED turns OFF.

I really like the interaction of breathing on a mic to turn on a light.
I'm not tied to the specifics and I welcome any and all help.

I've gotten as far as understanding that Boolean statements are involved in the answer, but I got lost in parentheses trying to get my inputs to play nice together.

Gratefully,
Jake

/***********************************************
  name:BreathingLight
  Goal: Use a mic to control a light, with an interaction based on fire. 
  Blow to start the fire, blow to stoke the fire, blow to turn out the fire (aka. candle).

  
//Many thanks for work from
//- www.sunfounder.com 
//online resources at ardx.org
//the model from Alison Kotin as seen on Vimeo

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

const int ledPin = 3; //led plugged into pin 3, plug an led straight into 3 with smaller side into digital GRND
const int soundPin = A0; //voice sensor attach to A0
int brightness = 0;    // how bright the LED is
int fadeAmount = 6;    // how many points to fade the LED by


void setup()
{
  pinMode(ledPin, OUTPUT); //set pin3 as OUTPUT
  Serial.begin(9600); //initialize serial monitor
}

void loop()
{
  int value = analogRead(soundPin); //read the value of voice sensor
  Serial.println(value); //print the value (don't need for system, but good for dubugging.)
  if (value > 550) //if the value is greater than 550
  {
    analogWrite(ledPin, brightness);

    // change the brightness for next time through the loop:
    brightness = brightness + fadeAmount;

    // reverse the direction of the fading at the ends of the fade:
    if (brightness == 0 || brightness == 255) {
      fadeAmount = -fadeAmount ;
    }
    // wait for 30 milliseconds to see the dimming effect
    delay(30);
  }
}

How exactly does the program behaviour differ from what you want or expect?

aarg:
How exactly does the program behaviour differ from what you want or expect?

Thanks, the question alone made me go back and consider what I have vs. what I want.

Turning light on
Currently: Any sounds that meets the threshold turns the light on. Even a really short one like snapping a finger.
What I want: To turn the light on, the sound needs to be above the threshold AND sustained for at least 0.25s

Turning light brightness up
Currently: Blowing on the mic increases the brightness until max. Then continuing to blow on the mic fades the light down.
What I want: Blowing on the mic turns the brightness up until the LED hits max. There is no 'fading' down.

Turning light off
Currently: The user has to time their breathing so that they stop breathing just as the LED dims down to 0. It is awkward and hard to do.
What I want: A user can blow quickly on the mic so that the LED turns off, just like blowing out a candle.

I hope this helps.
jake

Well, just for starters, your requirement for turning the light on demands that you use timing with millis(). So start looking at that. The same requirement has a mathematical problem built in. You require a "sustained" signal but the samples are instantaneous, not averaged. So a sustained signal of full amplitude at 1000 Hz, for example, will fail to exceed the threshold value of 550 more than half the time if individual instantaneous samples are used. That would be the normal case, without any kind of averaging routines.

To turn the light on, the sound needs to be above the threshold AND sustained for at least 0.25s

Is "sound is sustained for .25s" the same as "input on the analog in is above 2.5v for .25s"? Won't the input on the analog in be noisy - sometimes zero, sometimes peaking? If your mic is coil rather than a resistor, won'tthe votage be negative half the time?

To smooth out a noisy signal, you want to do two things.

First, control the sample rate. If you work on your code with debugging output and don't explicitly control sample rate, the behaviour of your code will change when you remove the debugging code because it's sampling more quickly.

To control sample rate, I divide the current time millis() by whatever, and compare that value now to that value last time I took a sample. if it has changed, time has moved on a bit.

Next, filter. I like to use: value = 0.75 * value + 0.25 * sample; , which is a simple and effective low-pass filter. It simulates an RC circuit, and seems to work pretty well.

You know: by fiddling with the parameters for the sample rate and rolling average, you could use this as your "timer" and not have to do it the usual way.

If you turning that .75 all the way up to .99 or further, it will take several samples for a blow to register in the average. Tune that and your sample rate, and you'll get your 'fanning the candle'/'blowing the candle out' effect without having to worry about timers.

The main thing you have to do is be sure to reset the rolling average to zero or even a negative value when the candle is 'blown on', or else it will immediately be blown out again.

  unsigned long prevSample_t;
  float average;

  void loop() {
    unsigned long thisSample_t = millis()/4; /* or micros() on 250., or whatever */
    if(prevSample _t!= thisSample_t) {
      average = average * .75 + .25 * analogRead(A0);
      prevSample_t = thisSample_t; 
    }

    // below here is PSUDEOCODE, not compilable C++

    if light is off  {
      if(hard blow) {
        turn light on
        set average to a negative value to stop an immediate blow-out
      }
    }
    else {
      if(hard blow) {
        turn light off
        set average to a negative value to stop an immediate blow-on
      }
      else if(gentle blow) {
        increase light, maybe in proportion to the strength of the blow
      }
    }
  }

PaulMurrayCbr:
Snip

@PaulMurrayCbr,
Thanks so much for the input, it's taking me some time to digest this (and plenty of tutorials), but I'm getting the gist of it. I'll update as I make progress.

jake