0

I have this project with my Arduino UNO where I want my servo motor SG90 to go full speed to 180 degrees when the PIR sensor sends HIGH value and go slowly back to zero when the value is LOW.

I have this code working where the servo goes full speed back to zero:

#include <Servo.h>

Servo myservo; //creates a servo object
int pos = 0; //variable to store servo position
//amount of time we give the sensor to calibrate(10-60 secs according to the datasheet)
int calibrationTime = 30;
//the time when the sensor outputs a low impulse
long unsigned int lowIn;
//the amount of milliseconds the sensor has to be low
//before we assume all motion has stopped
long unsigned int pause = 5000;
boolean lockLow = true;
boolean takeLowTime;
int pir = 12; //digital pin connected to the PIR's output
int pirPos = 13; //connects to the PIR's 5V pin

void setup() {
  myservo.attach(4); //attaches servo to pin 4
  Serial.begin(9600); //begins serial communication
  pinMode(pir, INPUT);
  pinMode(pirPos, OUTPUT);
  digitalWrite(pirPos, HIGH);
  //time for calibration
  
  Serial.println("calibrating sensor ");
  for (int i = 0; i < calibrationTime; i++) {
    Serial.print(calibrationTime - i);
    Serial.print("-");
    delay(1000);
  }
  Serial.println();
  Serial.println("done");
  
  //this waits until the PIR's output is low before ending setup
  while (digitalRead(pir) == HIGH) {
    delay(500);
  }
  
  Serial.print("SENSOR ACTIVE");
}

void loop() {
  if (digitalRead(pir) == HIGH)
    myservo.write(180);              // tell servo to go to position in variable 'pos'
  Serial.print("Motion detected!");

  if (digitalRead(pir) == LOW)
    myservo.write(0);              // tell servo to go to position in variable 'pos'
  Serial.print("Motion ended!");
}

I'd like the servo to go slowly back to zero, so I tried this :

#include <Servo.h>

Servo myservo; //creates a servo object
int pos = 0; //variable to store servo position
//amount of time we give the sensor to calibrate(10-60 secs according to the datasheet)
int calibrationTime = 30;
//the time when the sensor outputs a low impulse
long unsigned int lowIn;
//the amount of milliseconds the sensor has to be low
//before we assume all motion has stopped
long unsigned int pause = 5000;
boolean lockLow = true;
boolean takeLowTime;
int pir = 12; //digital pin connected to the PIR's output
int pirPos = 13; //connects to the PIR's 5V pin

void setup() {
  myservo.attach(4); //attaches servo to pin 4
  Serial.begin(9600); //begins serial communication
  pinMode(pir, INPUT);
  pinMode(pirPos, OUTPUT);
  digitalWrite(pirPos, HIGH);
  
  //time for calibration
  Serial.println("calibrating sensor ");
  for (int i = 0; i < calibrationTime; i++) {
    Serial.print(calibrationTime - i);
    Serial.print("-");
    delay(1000);
  }
  Serial.println();
  Serial.println("done");
  
  //this waits until the PIR's output is low before ending setup
  while (digitalRead(pir) == HIGH) {
    delay(500);
  }
  
  Serial.print("SENSOR ACTIVE");
}


void loop() {
  if (digitalRead(pir) == HIGH)
    myservo.write(180);              // tell servo to go to position in variable 'pos'
    
  Serial.print("Motion detected!");

  if (digitalRead(pir) == LOW)
    for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
      myservo.write(pos);              // tell servo to go to position in variable 'pos'
      delay(15);                       // waits 15ms for the servo to reach the position
    }
    
  Serial.print("Motion ended!");
}

The problem is with this code the servo does go slowly back to zero but then goes full speed back to 180 and again and again ....

Any idea? I'm a beginner in Arduino, so I assume it must not be that hard to figure out. Thanks.

3
  • 1
    If (digitalRead(pir) == LOW) then you tell the servo to jump to 180 with this line: for (pos = 180; pos >= 0; pos -= 1)
    – Dave X
    Commented Feb 11, 2021 at 14:02
  • 1
    I'd run your code through Tools/Auto Format in the IDE, which is something you should do before posting in the future, for the sanity of your readers, if not for your own.
    – timemage
    Commented Feb 11, 2021 at 14:45
  • Okay thanks for the tip guys !! Commented Feb 11, 2021 at 17:55

1 Answer 1

2

Don't set the pos to 180 in the (digitalRead(pir) == LOW) branch if you do not want it to repeatedly jump up to 180 for as long as the condition is true.

void loop() {
  if (digitalRead(pir) == HIGH)
     pos = 180;
  myservo.write(pos);              // tell servo to go to position in variable 'pos'
    
  Serial.print("Motion detected!");

  if (digitalRead(pir) == LOW)
    for (; pos > 0; pos -= 1) { // decay to 0 degrees
      myservo.write(pos);              // tell servo to go to position in variable 'pos'
      delay(15);                       // waits 15ms for the servo to reach the position
    }
    
  Serial.print("Motion ended!");
}

Alternately, you could use pos as a state variable and simplify things:

void loop() {
  if (digitalRead(pir) == HIGH)
     pos = 180;
  elif (pos > 0 ){
     pos--;
     delay(15);
  }
  myservo.write(pos);
}
4
  • If pir is LOW, the OP's speed-controlled code will keep cycling between 180 and zero again and again.
    – Dave X
    Commented Feb 11, 2021 at 14:30
  • "long as the condition is true" or maybe "as long as the input remains LOW". I'm going to delete my previous comments above (and this one later), because with your good clarifications they're going to look more out of sync.
    – timemage
    Commented Feb 11, 2021 at 14:43
  • I pasted the code, works like a charm, thanks a lot to you two guys !! Commented Feb 11, 2021 at 17:58
  • It sounds like you should "accept" this answer.
    – timemage
    Commented Feb 12, 2021 at 18:58

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.