HW-201 Sensor timing

Hi everyone. I have a project that has moving figurines in a Christmas village. I've set up a HW-201 sensor to open a gate as the figurine gets close. It's timed to close 7 seconds after the figurine passes through the gate. This works perfect with the code I have here. The problem I'm having is if I place a 2nd figurine a short distance behind the 1st figurine, I thought the gate timing would reset itself to the 7 second closing time because of the 2nd figurine re-tripping the sensor but it doesn't. The gate closes while the 2nd figurine is just getting to the gate causing timing issues. Where have I gone wrong in my code or what did I miss in my code? Here is the code (sketch).

#include <Servo.h>

Servo servo;
const int analogInPin = A0;            // Set the 'out' signal of the Sensor to this pin (A0)
int val=0;			                       // Set the 'out' signal of the Servo to this pin (9)
int servoPin=9;
int angle=90;



void setup() {
servo.attach(servoPin);
}

void loop() {
  val = analogRead(analogInPin);   // This reads the value of the sensor to see if an object is detected then saves in the val variable.          
 
  if(val < 600)
  {
  while (angle >= 75)             // Command to move from 1 degree to 90 degrees and 1 equals speed. Opens Clockwise.
  {
    servo.write(angle);
    angle--;
    delay(8);                    // This controls the speed of the Servo (gate) opening. Higher# lower speed.
  }

  delay(7000);                  // Sets the delay time. 7000 means the Servo (gate) stays open for 5 seconds before returning.

    while (angle <= 180)
    {
    servo.write(angle);         // If object detected the Servo rotates to 90 degrees.
    angle++;
    delay(8);                   // This controls the speed of the Servo (gate) returning. Higher# lower speed.
    }                   
}

For the actual details of the sensor timing, consult the data sheet. Please post a link to it.

But isn't the timing of the gate opening and closing done with code in Arduino? I know how to control the distance measuring. Only the timing of the sensor to open my gate and reset itself to zero if detecting another object before the timer counts to zero. The data sheet doesn't tell me the code for timing jremington.

The reason is simple. Nothing at all can happen during this:

  delay(7000);                  // Sets the delay time. 7000 means the Servo (gate) stays open for 5 seconds before returning.

Any trigger input is just ignored while it's sitting there delaying...

No, it doesn't. You will apparently have to write some.

I come here for help and all I’m getting is so far are insulting remarks like “write sensible code” and “of course nothing happens because of this “
Can anyone advise me on what code is missing?

Post #4 gave you a very big google-able clue: your delay() is probably causing your problem.

So, what to do? Hmmm. How about: google get rid of delay in Arduino code?

If you did that and read some of the first hits, you would have noticed that using millis() for timing is a better approach. That, and using a state machine approach.

So, more self-learning opportunities (googling) for you. Also check out those topics in the "Introductory tutorials" section of this forum.

That is, unless you want someone to write your code for you, in which case go to the "gigs and collaborations" section.

any microcontroller starts with a normal worded discription of the wanted functionality.

As far as I understand it

You have multiple figures that move towards a gate. If the figure arrives at a certain position a sensor detects the figure.
when figure is detected start a timer which will initiate an action after 7 seconds.
Now if another figure is detected by the sensor the timer shall be reset to 7 seconds again.
And in case a third figure, a fourth figure is detected reset timer again and again.

the function delay() would have better this name
freeze_microcontroller_stop_ALL_code_execution_until_freezing_time_is_over()
delay() is totally blocking any code-execution

The functionality like described above requires non-blocking.
This is done based on function millis().
I have written a tutorial that explains how this works with an everyday example and with an approach which is more intuitive than the standard blink without understanding-example of the Arduino-IDE
You can read it here

Your wanted functionality is calling for another thing:
different modes of operation

mode 1:

  • just do reading in sensor if a figure is detected

mode 2:

  • if a figure is detected start timing but in parallel keep on
    reading in sensor if another figure is detected

  • check if waiting time (the 7 seconds) are over if yes start closing gate

  • if another figure ist detected reset timer to start the 7 seconds new

mode 3:

  • closing the gate
  • maybe still in parallel check if another figure ist detected
    if another figure is detected open gate immidiately (and fast?)

Such a functionality can be achieved by using a state-machine
Each state is a mode of operation.
You can read a tutorial about state-machines here

quite some things to read
quite some things to run the demo-codes to understand in more detail how it works
quite some time needed to start modifying the demo-codes to understand even more

best regards Stefan

I have written the code for the functionality like described above
still I'm not sure if this is exactly what you want

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *

// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a
// Serial.print is executed
// end of macros dbg and dbgi
// print only once when value has changed
#define dbgc(myFixedText, variableName) \
  do { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  } while (false);
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *

#include <Servo.h>

Servo servo;
const int analogInPin = A0;
int val = 0;
const byte servoPin = 9;
int angle = 90;
int targetAngle = 90;

unsigned long MyTestTimer = 0;                   // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 2;
unsigned long ServoTimer;
unsigned long myWaitTimer;


// for easy identifiying which code-Version is flashed
void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}

// constnats fo rthe different modes of operation
const byte sm_CheckForFigure  = 0;
const byte sm_openGate        = 1;
const byte sm_waitSomeSeconds = 2;
const byte sm_closeGate       = 3;

// the variable for the mode of operation
byte myState = sm_CheckForFigure;


// move servo slowy in a non.blocking way
void moveServoSlow(byte targetAngle, unsigned long waitingPeriod) {
  int  deltaAngle;
  static unsigned long ServoTimer;

  // dbgc("m0",angle);
  if (angle != targetAngle) {
    if (angle > targetAngle) {
      deltaAngle = -1;
    }

    if (angle < targetAngle) {
      deltaAngle = 1;
    }
    if ( TimePeriodIsOver(ServoTimer, waitingPeriod) ) {
      angle += deltaAngle;
      dbgi("a+da",angle,1000);
      servo.write(angle);
    }
  }

}


void myStateMachine() {
  static unsigned long TimeToWait;
  dbgc("State:",myState);
  val = analogRead(analogInPin);   // This reads the value of the sensor to see if an object is detected then saves in the val variable.
  dbgi("above switch",val,1000);

  switch (myState) {

    case sm_CheckForFigure:
      if (val < 600) {
        Serial.println("figure detected start opening gate");
        targetAngle = 75;
        ServoTimer = millis();
        myState = sm_openGate;
      }
      break; // immidiately jump down to End-of-Switch


    case sm_openGate:
      moveServoSlow(targetAngle, 20);
      dbgc("openG",angle);
      if (angle == targetAngle) {
        Serial.println("gate opened start waiting");
        myWaitTimer = millis();
        myState = sm_waitSomeSeconds;
      }
      break; // immidiately jump down to End-of-Switch

    case sm_waitSomeSeconds:
      TimeToWait = (7000 - (millis() - myWaitTimer) ) / 1000; // print seconds left until closing starts
      dbgi("wait",TimeToWait,1000);

      if (val < 600) { // check for figure
        myWaitTimer = millis(); // if figure ist detected reset timer to start new
      }
      if ( TimePeriodIsOver(myWaitTimer, 7000) ) { // check if waiting time is over
        Serial.println("Waiting-Time over start closing gate");
        targetAngle = 180;
        ServoTimer = millis();
        myState = sm_closeGate;
      }
      break; // immidiately jump down to End-of-Switch

    case sm_closeGate:
      moveServoSlow(targetAngle, 20);
      if (angle == targetAngle) {
        Serial.println("gate closed check for figure");
        myState = sm_CheckForFigure;
      }
      break; // immidiately jump down to End-of-Switch
  } // End-of-Switch
}


void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  PrintFileNameDateTime();
  servo.attach(servoPin);
  angle = 90;
  targetAngle = 90;
}


void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 250);

  myStateMachine();
}

here is a WOKWI-simulation of the code
The linear potentiometer represents the sensor

best regards Stefan

Stefan..... I didn't expect anyone to actually write the code but you have and I have tested it. I know enough about Arduino Code to figure out where I can alter the speeds of the gate opening and closing as wells the timing of the gate staying open. I am not a coder, I am a hobbyist who works on Christmas Villages displays in my own home and post to YouTube every 2 years. I have not the time to learn Arduino Code like you guys everyday use. I'm into 3D modelling and stuff. So I can't thank you enough Stefan for this code. It's way beyond my pay grade (knowledge) to know what even half this code means but you're a genius to me. thank you so very very much. If you ever need anything in 3D or something like that let me know. Thank you. :pray:

In case you wanted to see what I mean Stefan.... here is a YouTube link to one of my videos from last year.

Suppose your car won't move. I come along and see that it's tied to a fire hydrant. I tell you about it. Did I not help you because I failed to untie the rope myself?

Wow this Christmas Village has so many details. Very nice. And a lot of moving people in there.
At the beginning of the video in the right corner in the background there is something that caught my attention . It looks like firework made with leds. Very cool detail.

best regards Stefan

Yes , it would help me. But that is assuming I know how to untie a rope. With Arduino code I know not of all the parameters and it would take me far too long to learn completely for the requirements I need. I code maybe once or twice in a year. I do not do it often enough to retain nor in depths like you guys. My forte is 3D modelling and if you asked me during your modelling how to do something, I would tell you how and with what tools. I would not tell you to to go read a book, Google it or just go learn. I would actually offer useful information that would guide you in the right direction knowing that you’re a beginner in 3D. Telling me a certain line in my code is not constructive criticism but rather the opposite, criticism. Anyway….. regardless of that, thank you for your input.

I identified a broadly well known problem in Arduino programming that you unfortunately stumbled on. I told you about it. The list of reference links in the reference threads already has numerous articles on the same subject.

Immediately after you first complained about the responses here in your reply #6, you were handed the solution verbatim. Also it was suggested there, what to look for and where to find it. Subsequently even more help was provided.

Here is not a competition among helpers to see who is the fastest or smartest. Also we have different approaches to teaching, remember that this is mainly a learning environment. Some people are not here to learn, maybe they just want to make some project work once. But few posters wave a flag in the first post and make that explicit. So my replies are teaching replies. Teaching is usually a mixture of presenting solutions and attempting to train people to find them independently. I personally lean towards the latter. Sometimes I see that a solution is compact and simple enough that I will just post the solution, as I can see that it is in itself educational and self-explanatory. It may take a few posts back and forth for me to determine where on the scale of silver spoon to teasing question, I should place my replies.

The topic of timing is simply too complex to fully explain in a single thread. I'm sorry that my post only identified a problem. It was supposed to motivate you to pursue the solution yourself, whether through research or your own investigations.

If you are a non-learner, interested only in some project, it would be helpful to mention that up front, so people don't waste time trying to educate you.

In your post, you said, "where have I gone wrong?". I simply answered your question. From that, I took the idea that you wanted to learn, not be handed a stock solution.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.