1

I want it running as a normal automatic door so both sensors work as a separate activate. The only problem I've seen is that the servo remains active waiting for another high read and you can hear the buzz of it trying to keep position.

It didn't used to do this before the code change. While its open, if it reads another high it will remember and rerun the open wait close after it closes.

Eg. The door activates and while open 3 people walk thru so when the door closes it will then run 3 more times with out anyone being there.

Edit: Code changed

    #include <Servo.h>

  Servo myservo; 

  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;

  int pirPin = 12; //digital pin connected to the PIR's output
  int pirPos = 13; //connects to the PIR's 5V pin
  int pirPin2 = 6; //digital pin connected to the PIR's output
  int pirPos2 = 10; //connects to the PIR's 5V pin
  const int position_closed = 0;      // door closed position [deg]
  const int position_open = 90;      // door open position [deg]
  const uint32_t delay_step = 15;     // delay between servo steps [ms]
  const uint32_t delay_open = 60000;  // how long the door stays open [ms]

  void setup()

  {
      myservo.attach(4); //attaches servo to pin 4
Serial.begin(9600); //begins serial communication
pinMode(pirPin, INPUT);
pinMode(pirPos, OUTPUT);
digitalWrite(pirPos, HIGH);
pinMode(pirPin2, INPUT);
pinMode(pirPos2, OUTPUT);
digitalWrite(pirPos2, HIGH);

//give the sensor time to calibrate
Serial.println("calibrating sensor ");
for(int i = 0; i < calibrationTime; i++)
{
    Serial.print(calibrationTime - i);
    Serial.print("-");
    delay(1000);
}
Serial.println();
Serial.println("done");
  }


  void loop()
  {
static enum { CLOSED, OPENING, OPEN, CLOSING } state;
static int position;          // door position
static uint32_t last_change;  // last time state or position changed
uint32_t now = millis();

// When presence is detected, switch to the OPENING state.
if ((digitalRead(pirPin) == HIGH || digitalRead(pirPin2) == HIGH)
        && state != OPENING) {
    state = OPENING;
    last_change = now;
}
else switch (state) {
    case CLOSED:
        break;
    case OPENING:
        if (position == position_open) {
            state = OPEN;
            last_change = now;
        } else if (now - last_change >= delay_step) {
            myservo.write(++position);
            last_change = now;
        }
        break;
    case OPEN:
        if (now - last_change >= delay_open) {
            state = CLOSING;
            last_change = now;
        }
        break;
    case CLOSING:
        if (position == position_closed) {
            state = CLOSED;
            last_change = now;
        } else if (now - last_change >= delay_step) {
            myservo.write(--position);
            last_change = now;
        }
        break;
      }
  }
3
  • Please edit your question to include what behaviour you DO see when it the code runs. e.g. Do you see the serial output expected during setup (calibrating sensor and SENSOR ACTIVE) ? Sep 8 '16 at 8:43
  • Are you trying to go from 0 to 180, delay, go from 180 to 90, delay, go from 90 to 0 ? (i.e. 3 stages :- open, close partway, close fully, with delays between) ? Sep 8 '16 at 8:49
  • On top of what @KennetRunner said we need to know the expected behaviour: do you want any PIR to open the door or do you want both PIRs to detect something in order to open the door? Because you are in the second condition from the code above.... Sep 8 '16 at 9:42
2

KennetRunner already explained what is wrong with your code. Here I will try to give advice on the proper way to implement the automatic door.

First thing is to get rid of delay(). While your program is inside a call to delay(), it can do nothing but wait until the delay is over. This is acceptable if the program has only one single task to perform, like blinking an LED. As soon as you have two tasks, delay() is a nuisance because it blocks all the tasks.

Case in hand: after keeping the door open for one minute, your program would close it, irrespective of whether there is someone in the way. It could not see the person come because it was busy delaying the closing of the door.

The standard way to get rid of delay() is to never wait inside loop(). Instead, if there is something to do right now, do it; if not, just don't do it, but do not wait. Pseudo-code:

void loop()
{
    if (it_is_time_to_do_foo()) {
        do_foo();
    }
    if (it_is_time_to_do_bar()) {
        do_bar();
    }
    /// etc...
}

You would use the millis() function to know when the time has come to do some task, as explained in the Blink Without Delay Arduino tutorial. This is really the second tutorial every Arduino user should read, right after learning how to blink an LED.

Next, you have to learn how to implement a finite state machine. I recommend the Finite State Machine tutorial by Majenko, a regular contributor to this site.

Your problem can be very naturally mapped into a machine with four states: CLOSED, OPENING, OPEN and CLOSING. You then write the rules about what to do in each state, and when to transition to another state. For example:

  • when the door is CLOSED
    • if the sensors detect someone, transition to the OPENING state
  • when the door is OPENING
    • if it's time to move the servo by one step, do it
    • if we have already moved all the way to the open position, we can say the door is OPEN now
  • when the door is OPEN
    • if it's time to close it, transition to the CLOSING state
    • if the sensors detect someone, reset the timer <<<< note this!
  • when the door is CLOSING
    • if the sensors detect someone, transition to the OPENING state
    • if it's time to move the servo by one step, do it
    • if we have already moved all the way to the closed position, we say the door is now CLOSED

A few things to note here:

  • See the line marked by “<<<<”: this will prevent the aforementioned problem of the door closing into someone.
  • The first rule for the CLOSING state follows from a similar rationale.
  • I made up some of these rules, as the job of your program was not completely specified in your question. Feel free to change those rules to your taste.

And here is my proposed implementation. Warning: not tested. As usual with FSMs, it is based on a big switch/case statement going through all the possible states. However, to avoid repeating rules, I factored the “if there is someone, start OPENING” rule out of the switch. I also folded “if there is someone, reset the timer” into the same rule.

const int position_closed = 0;      // door closed position [deg]
const int position_open = 180;      // door open position [deg]
const uint32_t delay_step = 15;     // delay between servo steps [ms]
const uint32_t delay_open = 60000;  // how long the door stays open [ms]

void loop()
{
    static enum { CLOSED, OPENING, OPEN, CLOSING } state;
    static int position;          // door position
    static uint32_t last_change;  // last time state or position changed
    uint32_t now = millis();

    // When presence is detected, switch to the OPENING state.
    if ((digitalRead(pirPin) == HIGH || digitalRead(pirPin2) == HIGH)
            && state != OPENING) {
        state = OPENING;
        last_change = now;
    }
    else switch (state) {
        case CLOSED:
            break;
        case OPENING:
            if (position == position_open) {
                state = OPEN;
                last_change = now;
            } else if (now - last_change >= delay_step) {
                myservo.write(++position);
                last_change = now;
            }
            break;
        case OPEN:
            if (now - last_change >= delay_open) {
                state = CLOSING;
                last_change = now;
            }
            break;
        case CLOSING:
            if (position == position_closed) {
                state = CLOSED;
                last_change = now;
            } else if (now - last_change >= delay_step) {
                myservo.write(--position);
                last_change = now;
            }
            break;
    }
}
4
  • thanks for the code ive included it over what i had the only problem ive seen is that the servo remains active waiting for another high read and u can hear the buzz of it trying to keep position and while its open if it reads another high it will remember and rerun the open wait close after it closes
    – ANDREW
    Sep 9 '16 at 6:11
  • @ANDREW: I couldn't quite make a sense of your last comment. “the servo remains active [...] trying to keep position”: this is normal, and it's exactly what a servo is meant to do when you do not ask it to move elsewhere. “it will remember and rerun the open wait close”: I could not replicate the behavior you describe. Sep 10 '16 at 11:38
  • U can hear the constant buzzing noise it creates when running. the last code I used only made a noise when moving not when waiting for next high. I was thinking of adding detach at the end and attach at the start that might help
    – ANDREW
    Sep 11 '16 at 11:04
  • You expect the servo to be noisy when it's moving or when, in a static position, it is fighting some load. If static and unloaded, it shouldn't make much noise. Sep 11 '16 at 19:42
1

Your code is looping 180 times, EACH TIME it :

  • Sets the servo to 180
  • Delays for 2 seconds
  • Sweeps the servo from 180 to 90 in single steps
  • Sets the servo to 90
  • Delays for 60 seconds
  • Sweeps the servo from 90 to 180 in single steps

So you code will take more than 3 hours to complete.

I think you would be better pulling the code that moves the servo out into its own function: Something like:

void moveServo(servoStart, servoEnd, servoStep, servoInterval)
{
  for(int servoPos = servoStart; servoStart < servoEnd; servoStep)
  {
     myservo.write(servoPos);
     delay(servoInterval);
  }
}

then simply call it from your main loop, like this:

void loop()
{

    if((digitalRead(pirPin) == HIGH) && (digitalRead(pirPin2) == HIGH))
    {
      moveServo(0, 180, 1, 15);
      delay(2000);
      moveServo(180, 90, -1, 15);
      delay(60000);
      moveServo(90, 0, -1, 15);
    }
}

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