Help with motion activated servo please.

Hi Guys,

I’m new to Arduino and I could really use some help please.

I’m trying to make a PIR controlled Servo. I want the servo to rotate to one position when the sensor detects motion, then immediately return to it’s default position, and stay there until motion is detected again (basically like an automatic door).

So far, I have managed to get it to move to the position and back again when motion is detected, but I can’t get it to stop. So once the servo is activated, it just keeps going back and forth continuously. I have tried lots of different commands in order to make it idle between sensor activations, but nothing I have tried works.

This is my code (apologies in advance, I’m sure it’s a mess):

#include <Servo.h>

 int timer = 500;
 int alarmPin = 0;
 int alarmValue = 0;
 Servo myservo;
 byte rotated = 0;

 void setup () {
 Serial.begin (9600);
 pinMode(alarmPin, INPUT);
 myservo.attach(11);
 delay (20);
 }

 void loop (){
 alarmValue = analogRead(alarmPin);

if ( (alarmValue > 1000) && (rotated == 0) ){
  rotation();
  rotated = 1;
 }
else if ( (alarmValue < 100) && (rotated == 1) ){
  reverotation();
  rotated = 0;
 }
 
 
 delay(timer);

 Serial.println (alarmValue);

 delay (20);

 }

void rotation() { 
    myservo.write (1200);
    delay(1000);
 }

void reverotation() {
    myservo.write (1800);
 }

Thanks

According to the servo.write() reference page

Syntax

servo.write(angle) Parameters

servo: a variable of type Servo angle: the value to write to the servo, from 0 to 180

Can you see where your code differs from this ? Fix that first then tell us what happens. Try putting some Serial.prints into your program so that you know which sections it is executing and the value of variables at the time

Can you see where your code differs from this ?

It doesn't need fixing. Read the source.

AWOL:

Can you see where your code differs from this ?

It doesn't need fixing. Read the source.

I have read it and see your point (and have learnt something), but I wonder if the OP knows what it is doing. Seeing the numbers involved in the code that he posted I did wonder if one of the things that he had tried was writeMicroseconds() at some time and had not changed the parameters when changing to write().

Incidentally, I wonder why the auto-switch to writeMicroseconds() is not mentioned in the reference pages.

I would change the structure of the program so that there is only one servo function and it decides what to do depending on the value collected by the analogRead(). For example

int servoPos = 1200;

void loop() {
  readPIR();
  moveServo();
}

void readPir() {
  alarmValue = analogRead(alarmPin);
}

void moveServo() {
  if (servoPos == 1200 && alarmValue > 1000) {
     myservo.writeMicroseconds(1800);
  }
  if (servoPos == 1800 && alarmValue < 100) {
     myservo.writeMicroseconds(1200);
  }
}

If nothing else it would make the logic clearer to me.

…R

A PIR sensor detects, or doesn’t detect, motion. Why are you reading a digital device using an analog pin?

In any case, you want the change from no motion to motion to trigger the servo, not the presence of motion. Look at the state change detection example.

Thank you everyone.

I've taken everything on-board and I'm working on it now. Like I said, I'm a newb, so it'll take me a while to get my head around it, but I'll post back asap with an update.

With regard to the question about why I'm using an analog pin, this project ( http://itp.nyu.edu/physcomp/sensors/Reports/PIRMotionSensor ) was my starting point, and I built my code from there. It is this exact PIR that I am using.

Thanks again.

The page that you link to says

The regular value without movement is above 1021, upon detecting it, it goes low to 17 - 18.

so your test for greater than 1000 is a bit too close for my liking. What sort of values do you get for alarmValue using your program ?

UKHeliBob:
The page that you link to says

The regular value without movement is above 1021, upon detecting it, it goes low to 17 - 18.

so your test for greater than 1000 is a bit too close for my liking. What sort of values do you get for alarmValue using your program ?

Sounds like some idiot using a digital sensor like an analog sensor to me. Use digitalRead(). Get HIGH when there is no movement; get LOW when there is.

Thanks guys! I tried again using all of the information you have all given, and I think I’m finally getting somewhere. :slight_smile:

There are just a couple of minor issues that I’d like to check with you please:
-When I run my hand past the sensor, it usually takes a few attempts before the servo moves. Is this just an issue with the integrity of the PIR rather than the coding?
-When it is in its resting state, the servo buzzes continuously (not loud though), is this normal?

Here is the code:

#include <Servo.h>

 int pos = 0;
 int PIR = 7;
 Servo myservo;

 void setup () {
 pinMode(PIR, INPUT);
 myservo.attach(11);
 }

 void loop (){
 myservo.write(pos);
  if (digitalRead(PIR) == HIGH) {
    myservo.write(300);
    delay(2000);
 }
if (digitalRead(PIR) == LOW) {
    myservo.write(0);
    delay(2000);
 }
 }

Other than that, it’s working as I had hoped :slight_smile:

 void loop (){
 myservo.write(pos);

Why are you defining a position for the servo on every pass through loop()?

-When it is in its resting state, the servo buzzes continuously (not loud though), is this normal?

When would that be? Yours never is.

PaulS: Why are you defining a position for the servo on every pass through loop()?

My bad, I've amended it.

-When it is in its resting state, the servo buzzes continuously (not loud though), is this normal?

When would that be? Yours never is.

When I say resting, I mean in the start position. I would prefer that it just stay in it's normal default/idle state + position, and only move when motion is detected, then return to it's idle position. I haven't figured out how to do that yet though, so defining two positions is the only way I know.

When I run my hand past the sensor, it usually takes a few attempts before the servo moves. Is this just an issue with the integrity of the PIR rather than the coding?

Try removing the calls to delay.

My bad, I've amended it.

But not reposted it.

When I say resting, I mean in the start position.

Servos don't have "start postion"s. They have whatever position they were in when they were powered off.

I would prefer that it just stay in it's normal default/idle state + position, and only move when motion is detected, then return to it's idle position.

When motion IS detected? Or, when motion BECOMES detected? Not the same thing at all.

wildbill:
Try removing the calls to delay.

Thanks, but I had to put delays in. Without any delays, the servo just rotates continuously and uncontrollably. With only one delay, it does a quarter rotation and snaps back.

PaulS:
But not reposted it.

Sorry, all I did was take that line out of the code. I’ll post the code below.

PaulS:
Servos don’t have "start postion"s. They have whatever position they were in when they were powered off.

Ah, ok thanks. It’s important that my servo rotates from the same location each time, so I think it is set up right for that then.

PaulS:
When motion IS detected? Or, when motion BECOMES detected? Not the same thing at all.

When it detects a movement (each time something passes the sensor).

Code:

#include <Servo.h>

 int pos = 0;
 int PIR = 7;
 Servo myservo;

 void setup () {
 pinMode(PIR, INPUT);
 myservo.attach(11);
 }

 void loop (){
  if (digitalRead(PIR) == HIGH) {
    myservo.write(300);
    delay(2000);
 }
if (digitalRead(PIR) == LOW) {
    myservo.write(0);
    delay(2000);
 }
 }

Thanks, but I had to put delays in. Without any delays, the servo just rotates continuously and uncontrollably. With only one delay, it does a quarter rotation and snaps back.

In that case it sounds like you will need a time based approach - the automatic door thing suggests that on movement detection, you wish to move the servo, have it stay for a while & then return to the default position.

I'd suggest that you drop the delays and record the time using millis every time you see movement. In addition when that happens, move the servo to the "door open" position - if it's already moved or moving, no harm done.

When you haven't seen movement for a while, return to your start position. This way, the system will react quickly to movement when needed, but not react to rapid changes in PIR state.

When it detects a movement (each time something passes the sensor).

The thing is a sensor reports movement by setting the pin LOW. If you detect when the pin CHANGES to LOW, rather than IS LOW, you'll stop the servo from moving more than once.

Look at the state change detection example.

wildbill: In that case it sounds like you will need a time based approach - the automatic door thing suggests that on movement detection, you wish to move the servo, have it stay for a while & then return to the default position.

I'd suggest that you drop the delays and record the time using millis every time you see movement. In addition when that happens, move the servo to the "door open" position - if it's already moved or moving, no harm done.

When you haven't seen movement for a while, return to your start position. This way, the system will react quickly to movement when needed, but not react to rapid changes in PIR state.

Thank you! That sounds ideal, I'll read up on it now and report back how I get on.

PaulS:

When it detects a movement (each time something passes the sensor).

The thing is a sensor reports movement by setting the pin LOW. If you detect when the pin CHANGES to LOW, rather than IS LOW, you'll stop the servo from moving more than once.

Look at the state change detection example.

Thanks for that! That explains a lot, I'll study the state change example and review my code.

Thanks again guys, much appreciated.