Fun with motion sensors, fade and timers.

Hey All.
Ok, here’s what I’m trying to do.

  • When someone walks within range of the PIR motion sensors I want to brighten the leds using pwm and analogWrite from 0 - 255
  • hold the lights ON/255 while the PIR is detecting motion and is HIGH
  • When motion is no longer being detected PIR sensor LOW
  • run a timer
  • fade the leds half way say, 255 to 128
  • run a timer
  • fade leds to off, 128 to 0

I’ve written some code, (see below) that brightens 0 to 255 and dims 255 to 0 but if motion is detected during the dim function it finishes the countdown to zero first and then brightens up to 255. How can I write the code to stop the dim function, recognize where it is, say 100 and brighten it from there?

My next questions(s) are related to timers. In addition the timers I mention above I’d like to control the speed of the dim and fade; my current code does it faster than I’d like, I want to slow it down. My guess is I need to add a timer to my for loop which looks like this ( the entire function is below)

      for(int fadeValue = 0; fadeValue <= 255; fadeValue +=1) {

Maybe something like this?

      for(int fadeValue = 0; fadeValue <= 255; fadeSpeed = 50; fadeValue +=1) {

The timers I’ve written in the past have started with the example Blink Delay sketch but this current project is gonna have a bunch of different timers, I think I need something more robust.

I did some preliminary research and see there are some timer libraries out there, this one looks most appealing to me.
http://playground.arduino.cc/Code/ArduinoTimerObject
My question here is what are best practices when it comes to multiple timers and what makes the most sense for my current project?

// Motion Sensor Setup
int long delay_time = 5000; 
int alarmPin_1 = 42;
int long time_on_1 = 0;

time_on_1 = lightsOn(alarmPin_1, ledPin_4, time_on_1, delay_time); // this is how I call the function from the main loop


int long lightsOn(int alarmPin, int ledPin, int long lights_on, int long delay_m) {

  int alarmValue = digitalRead(alarmPin);
  if (lights_on == 0) { // lights ON
    if (alarmValue == HIGH){      
        // motion detected
      Serial.println(">>> Turning lights on");   

      for(int fadeValue = 0; fadeValue <= 255; fadeValue +=1) { 
        // sets the value (range from 0 to 255):
        Serial.println(fadeValue);
        analogWrite(ledPin, fadeValue); 
        delay(1);
      }  
      return millis();
    }
  } 
  else { // lights on
    if (alarmValue == HIGH) {
      // lights are on AND there's motion
      // keep timer start to now
      return millis();
    } 
    else {
      int long time_now = millis();

      if ((time_now - lights_on) > delay_m) {
        Serial.println(">>> Turning lights off");
        // dim them down halfway
        for(int fadeValue = 255; fadeValue >= 128; fadeValue -=1) { 
          // sets the value (range from 0 to 255):
          Serial.println(fadeValue);
          analogWrite(ledPin, fadeValue); 

        }  
      } 
      else {
        // use the previous time_on
        return lights_on;
      }
    }
  }
  return 0L;
}

Maybe something like this?

No. A for statement has 3 parts. You can't just invent syntax that you would like to use.

You have to give up on for loops. On every pass through loop(), it may, or may not, be time to do something.

If it is, that may involve stepping up the brightness or stepping down the brightness.

Let loop() handle the looping. After all, the function isn't called doSomeStuff().

So I'm guessing I'll need to increment and detriment the fadeValue using an if statement? I'm trying to write this out but I'm falling short. How do I handle this?
Thanks
Rich

How do I handle this?

Specifically, what do you want to do?

Typically, if you want to fade all the way up, you’d use:

   if(fadeAmount + fadeIncrement < 255)
      fadeAmount += fadeIncrement;

richiep:
...and detriment the fadeValue using an if statement?

Oooh, don't do that.

:wink:

Hmmm, a for statement won’t work, an if statement is a bad idea. Then what?

an if statement is a bad idea.

No, that is not true.

The term is decrement, not detriment. That was what BulldogLowell was commenting on.

PaulS:
No, that is not true.

The term is decrement, not detriment. That was what BulldogLowell was commenting on.

  • <------here is my joke

:-\ <------OP’s head

OK, now I’m tracking the conversations, we’re talking about grammar and spelling not syntax…wooo boy!! I’d like to take this opportunity to blame spell check.

Anyway I tried what you suggested Paul but it only adds and subtracts 1 to the fadeValue. How can I write this to take the value up to 255? Also note that I have added some delays in here which work but iit’s delay() which locks up the code and I don’t want that.

int fadeValue = 0;
int long lightsOn(int alarmPin, int ledPin, int long lights_on, int long delay_m) {

  int alarmValue = digitalRead(alarmPin);
  if (lights_on == 0) { // lights ON
    if (alarmValue == HIGH){      
        // motion detected
      Serial.println(">>> Turning lights on");   

      for(int fadeValue = 0; fadeValue <= 255; fadeValue +=1) { 
//      if(fadeValue + 1 <= 255) {
//        fadeValue += 1; 
        // sets the value (range from 0 to 255):
        Serial.println(fadeValue);
        analogWrite(ledPin, fadeValue); 
        delay(15);
       }  
      return millis();
    }
  } 
  else { // lights on
    if (alarmValue == HIGH) {
      // lights are on AND there's motion
      // keep timer start to now
      return millis();
    } 
    else {
      int long time_now = millis();
      //     Serial.println("Millis since motion: "+(time_now-time_on));
      if ((time_now - lights_on) > delay_m) {
        Serial.println(">>> Turning lights off");
        // dim them down halfway
       for(int fadeValue = 255; fadeValue >= 50; fadeValue -=1) { 
//      if(fadeValue - 1 >= 0) {
//        fadeValue -= 1; 
          // sets the value (range from 0 to 255):
          Serial.println(fadeValue);
          analogWrite(ledPin, fadeValue); 
        }
        delay(30);     
      } 
      else {
        // use the previous time_on
        return lights_on;
      }
    }
  }
  return 0L;
}

You need to use Ctrl-A and Ctrl-X on that code.

Start over, KNOWING that you can NOT use a for statement and can not use a delay statement.

You need to check the state of the PIR, and set a flag. NOTHING else.

Then, you need to check the state of the flag, and take some action, if it is time.

Look at the state change detection example, to see how to determine that the PIR has determined that motion has started or that motion has stopped.

Look at the blink without delay example to see how to determine that it is, or is not, time to do something.

Start with this skeleton:

void loop()
{
   checkMotionSensor();

   if(motionDetected)
     fadeLightsUp();
   else
     fadeLightsDown();
}

But, before you write ANY code, see if you can describe what each function is supposed to do. If we agree that you have that right, then you can write the code. If not, we'll help you get the description right, and then you can write the code.

ok, I took your advice and scrapped what I have and came up with the following. This isn’t fully fleshed out, or tested, but it does compile. But I wanted to make sure I’m on the right track. I’m still confused as to how to control the speed of the brightening and dimming.

// Motion Sensor Setup
int alarmPin = 42;

const int ledPin = 12;

int fadeValue = 0;

boolean motionDetected;

// Timers
int long delayTime = 5000; 
int long lightsOn = 0;

int fadeDelayTime = 10;

void setup() {
 pinMode(alarmPin, INPUT);
 pinMode(ledPin, OUTPUT);
 Serial.begin(115200);
 delay(2000);
 Serial.println("Let's detect some motion!");
}

void loop() {
     checkMotionSensor();
     
   if(motionDetected)
     fadeLightsUp();
   else
     fadeLightsDown();
}

void checkMotionSensor(){
  int alarmValue = digitalRead(alarmPin);
    if (alarmValue == HIGH){      
    motionDetected = 1;
    }
    if (alarmValue == LOW){      
    motionDetected = 0;
   }
 return motionDetected;
}

void fadeLightsUp(){
int long time_now = millis();
  if(motionDetected = 1){
     if ((time_now - lightsOn) > delayTime) {
      if(fadeValue + 1 <= 255) {
        fadeValue += 1; 
        Serial.println(fadeValue);
        analogWrite(ledPin, fadeValue); 
      }
  }
}
}

void fadeLightsDown(){

}

If checkMotionSensor is going to return a value then it can't be defined as returning void. Either bool or int would work there. I would use bool myself since the possible values are limited to true or false. I would change the 0 and 1 in the code to true or false to reflect that.

Actually, since motionDetected is global, a third option would be to leave it as a void function and remove the return statement.

 int long delayTime = 5000;

No, unsigned long. Not int long.

I also don't think you need the check for motinDetected in fadeLightsUp since you are only calling that function of motion is detected that is a redundant check.

like this?

// Motion Sensor Setup
int alarmPin = 42;

const int ledPin = 12;

int fadeValue = 0;

boolean motionDetected;

// Timers
unsigned long delayTime = 5000; 
int long lightsOn = 0;

int fadeDelayTime = 10;

void setup() {
 pinMode(alarmPin, INPUT);
 pinMode(ledPin, OUTPUT);
 Serial.begin(115200);
 delay(2000);
 Serial.println("Let's detect some motion!");
}

void loop() {
     checkMotionSensor();
     
   if(motionDetected)
     fadeLightsUp();
   else
     fadeLightsDown();
}

void checkMotionSensor(){
  int alarmValue = digitalRead(alarmPin);
    if (alarmValue == HIGH){      
    motionDetected = true;
    }
    if (alarmValue == LOW){      
    motionDetected = false;
   }
 return motionDetected;
}

void fadeLightsUp(){
int long time_now = millis();
     if ((time_now - lightsOn) > delayTime) {
      if(fadeValue + 1 <= 255) {
        fadeValue += 1; 
        Serial.println(fadeValue);
        analogWrite(ledPin, fadeValue); 
      }
  }
}

void fadeLightsDown(){

}

You still have a void function with a return statement trying to return a value. That's going to throw an error for sure.

You also still have the types mixed up. You need unsigned long, not int long.

ok, I made those tweaks.

Now I’m gonna set up a test board and motion sensor, not the live project, and start testing, note that I’m testing on a Uno, the live project is hosted on a Mega; I’ve changed the pins to reflect this.

I’m still confused in regards to controlling the fade speed without delay. Do I need to create three more variables and use another if statement? Will I have to do this for every timer I create? This gets to the question of timer libraries which I mentioned at the beginning of this thread. What are best practices for this kind of thing?

I’ve inserted a delay where I want to use mills below.

Thanks for your help so far everyone!

// Motion Sensor Setup
int alarmPin = 3;
boolean motionDetected;

const int ledPin = 9;

// Timers
unsigned long delayTime = 5000; 
int long lightsOn = 0;

int fadeValue = 0;
int fadeSpeed = 10;

void setup() {
 pinMode(alarmPin, INPUT);
 pinMode(ledPin, OUTPUT);
 Serial.begin(115200);
 delay(2000);
 Serial.println("Let's detect some motion!");
}

void loop() {
     checkMotionSensor();
     
   if(motionDetected)
     fadeLightsUp();
   else
     fadeLightsDown();
}

void checkMotionSensor(){
  int alarmValue = digitalRead(alarmPin);
    if (alarmValue == HIGH){      
    motionDetected = true;
    }
    if (alarmValue == LOW){      
    motionDetected = false;
   }
}

void fadeLightsUp(){
unsigned long time_now = millis();
     if ((time_now - lightsOn) > delayTime) {
      if(fadeValue + 1 <= 255) {
        fadeValue += 1; 
        delay(10);
        Serial.println(fadeValue);
        analogWrite(ledPin, fadeValue); 
      }
  }
}

void fadeLightsDown(){

}

Nope, you don't need that delay. Loop keeps repeating and calling that function but that first if that checks for the delayTime against millis is going to mean that most of the time nothing happens. Once every delayTime that's true and it fades up one level.

You still have some "int long" in there.