programming question about HC-SR04 false triggers

I am new to this site. I am new to Arduinos. I have been into it for 2 weeks now.

I am sorry if I put this post in the wrong forum. I wasn't sure if it should be in programming questions, troubleshooting, sensors, or robotics, so I thought I would start with programming.
I am new to programming also, actually I'm not a programmer. I copy and paste and steal and cheat. Then I tweak a little. I am figuring it out but I hit a wall.

I have a quad copter, a drone. I added a retractable landing gear to it and even before I started this project, I knew I wanted to automate it. I wanted to "mod my mod". I added an Arduino Nano and a HC-SR04 ultrasonic sensor. I have the sensor looking down to the ground and when the quad is less than 3 feet from the ground the landing gear comes down.

It works very reliably when it is less than 3 feet from the ground. My problem is when it is hundreds of feet in the air, the landing gear triggers by itself. I found out the ultrasonic sensor is "seeing" my rotor wash, turbulence from the props. I think I need to add something like "look, wait for a second then look again". If the two answers match, then perform an action. Does this make sense?

I don't have a clue how to do this and I don't know where to start. Here?

Here is my code so far, again none of this is mine. I copied and pasted from the internet.
Any help will be greatly appreciated and if you are a programmer and can fix this for me I will fly around Southern California with a banner proclaiming your greatness! for a week or so anyway.
Here's my code, let me know your thoughts...

#include <Servo.h>

Servo servoLeft;          
Servo servoRight;         
#define echoPin 2         
#define trigPin 3

void setup() { 
  servoLeft.attach(5);  
  servoRight.attach(6);  
  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
} 

void loop() {            
  long duration, distance;
  digitalWrite(trigPin, LOW);  
  delayMicroseconds(2); 
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10); 
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = (duration/2) / 74; // 74=inches 29=cm
  if (distance < 36) {  //  160cm=5'  62cm=2' 60"=5' 24"=2'
  servoLeft.write(80);
  servoRight.write(90);  //LANDING
   }
  else {
  servoLeft.write(180);
  servoRight.write(0);  //FLYING
    }
  delay(2000);
}

I think I need to add something like "look, wait for a second then look again". If the two answers match, then perform an action. Does this make sense?

Yes, it does.

I don't have a clue how to do this and I don't know where to start. Here?

Start with putting the code that gets the distance measurement in a function. Make the function return a value. Call that function in loop().

Once you do that, calling the function once a second, or once every half second, will be trivial. Comparing the current reading against the previous reading will be easy.

sdtag:
It works very reliably when it is less than 3 feet from the ground. My problem is when it is hundreds of feet in the air, the landing gear triggers by itself.

Do you have a dedicated Arduino for this single task?
I'm asking because you are using blocking functions like "pulseIn()" and "delay()" to stop program execution every now and then, so that this Arduino can only do this slow control action and nothing else, the way you are programming this single task.

And even worse, if no pulse is returned because you are out of range of the ultrasonic sensor and get no echo at all, you measure a duration of zero:

 duration = pulseIn(echoPin, HIGH);

Then you calculate a distance of zero from a duration of zero:

 distance = (duration/2) / 74; // 74=inches 29=cm

Then you check for distance and fire the landing gear:

 if (distance < 36) {  //  160cm=5'  62cm=2' 60"=5' 24"=2'

If you really like do blocking programming for a slow reacting task, you better do something like:

  duration = pulseIn(echoPin, HIGH);
  if (duration>0)  distance = (duration/2) / 74; // 74=inches 29=cm
  else distance=999; // out of range

yes a dedicated nano. I have the sensor mounted to the nano. I took battery power from the quad and converted it to 5 vdc using a BEC. The 2 servos hook up to the nano. A stand alone dedicated system.
Blocking functions? I don't know what I like. I'm new to this. I just want this to work without it looking like a bird flapping it's wings. haha

sdtag:
I just want this to work without it looking like a bird flapping it's wings. haha

One thing is, that you should not consider "no echo == distance zero". In that case, the landing gear will be activated each time the sensor gets no echo back because no echo is received.

I've shown you some code how to set the range to "999" instead of "0" when no echo is received.

The other thing is your switching limit of <36 with no hysteresis.

So if your device is "near 36", it may measure 35-36-35-36-35-36-35-36 and your landing gear will indeed start flapping its wings in that situation.

The switching points for "Flying" and "Landing" have to be different, to avoid flapping at the single switching point.

Something like:

  if (distance < 36) {  //  160cm=5'  62cm=2' 60"=5' 24"=2'
  servoLeft.write(80);
  servoRight.write(90);  //LANDING
   }
  else if (distance > 40){
  servoLeft.write(180);
  servoRight.write(0);  //FLYING
    }

In that case the range of 36...40 is the "hysteresis" that will avoild flapping when close to the switching point. Switching only occurs if distance<36 or if distance>40, and in the range in between the last state is kept the same.

In that case the range of 36...40 is the "hysteresis" that will avoild flapping when close to the switching point. Switching only occurs if distance<36 or if distance>40, and in the range in between the last state is kept the same.

That's no fun. I wanted to see a video of the landing gear going up and down as the aircraft got close to the ground. Then, we could have told OP how to avoid the problem.

thanks guys.
here's your reward

it's not as bad as a bird flapping his wings but it is very unstable over 3 feet.
When it sees the ground at less than 3 feet it is solid, the gear stays down.

it's not as bad as a bird flapping his wings but it is very unstable over 3 feet.

Is that before or after adding the hysteresis?

getting close to 3 feet was never the issue.
The issue is false triggers when it is high up in the air.
Someone told me the sensor is seeing my rotor wash, the turbulent air coming out of the props.
I think I just need to add a variable and check the current pass against the last pass (separated by a second or two) and if they match then lower the gear.
I added a 2 second delay at the end of the sketch thinking it would debounce this, but it doesn't. My first amateur attempt at programming. haha
I'm not a programmer and I'm lost, that's why I came here. I don't know the structure or the language. I keep the arduino language reference open but I have to know what I'm looking for and I don't. It looks like I have a lot of reading to do. I'll get there. I thought maybe one of you could give me a shortcut and open my eyes.

I encourage you to hook the Nano up to a computer and print distance readings out to the serial monitor, right now we are just guessing what is going wrong. Since it acts up when you hold the quad shoulder high that should be possible.

To answer your original question, one way to accomplish what you want is to only read the sensor at certain intervals. The Arduino will tell you how many milliseconds it has been since the program started up. By adding a constant to this value you can calculate the next time to take a reading.

You want the distance to be below a certain value for a given number of readings, so keeping a count of the consecutive times the threshold was met is sufficient, there is no reason to store the values of past readings (as there would be if you were taking an average).

The code below addresses your original question. The constants MATCH_COUNT and READ_INTERVAL may be set to specify the number of required consecutive reads less than three feet to signal landing and the time between readings, respectively. There is no reason to call delay().

#include <Servo.h>

Servo servoLeft;          
Servo servoRight;        
#define echoPin 2        
#define trigPin 3

const int MATCH_COUNT = 2;        // # of matches for distance we need in a row
int matches = 0;                  // running count of matches
const long READ_INTERVAL = 500;   // how often to take a reading
long nextReading;                 // time for next reading

void setup() {
  //  Serial.begin(9600);
  servoLeft.attach(5);  
  servoRight.attach(6);  
  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  nextReading = millis();  // read right away
}

void loop() {            
  long duration, distance;

  // time for a reading?
  if( millis() >= nextReading )
  {

    digitalWrite(trigPin, LOW);  
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
    duration = pulseIn(echoPin, HIGH);
    distance = (duration/2) / 74; // 74=inches 29=cm

    if (distance < 36) {  //  160cm=5'  62cm=2' 60"=5' 24"=2'
      matches++; // increment count

    } 
    else {
      matches=0;  // start over

    }

    nextReading = millis() + READ_INTERVAL;  // set time for next read

  }
  // if we got MATCH_COUNT in a row
  if( matches >= MATCH_COUNT )
  {
    servoLeft.write(80);
    servoRight.write(90);  //LANDING
    //    Serial.println(matches);
  }
  else {
    servoLeft.write(180);
    servoRight.write(0);  //FLYING
  }
  // delay(2000);
}

I think you did it! Thank you very very much. That would have taken me a year.
I have a duplicate setup on my desk and I am testing it now.
I still think I might need to delay it a little between reads and since I originally asked this question I added some more lines for LED indicators, landing or flying.
And again, the issue was never around 3 feet. The issue was at a hundred feet and the sensor was seeing rotor wash, turbulence from the props.
thank you thank you thank you

Since you seem to know what you are doing and I have been into this for about two weeks now can I ask you another question? Is there a way to slow down the speed of the servos? They kind of snap the gear up or down and I would like to slow them down a little if I could. TIA.

#include <Servo.h>

Servo servoLeft;         
Servo servoRight;       
#define echoPin 2       
#define trigPin 3
int led1 = 8; 
int led2 = 9; 

const int MATCH_COUNT = 2;        // # of matches for distance we need in a row
int matches = 0;                  // running count of matches
const long READ_INTERVAL = 500;   // how often to take a reading
long nextReading;                 // time for next reading

void setup() {
  //  Serial.begin(9600);
  servoLeft.attach(5); 
  servoRight.attach(6); 
  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  nextReading = millis();  // read right away
}

void loop() {           
  long duration, distance;

  // time for a reading?
  if( millis() >= nextReading )
  {

    digitalWrite(trigPin, LOW); 
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
    duration = pulseIn(echoPin, HIGH);
    distance = (duration/2) / 74; // 74=inches 29=cm

    if (distance < 36) {  //  160cm=5'  62cm=2' 60"=5' 24"=2'
      matches++; // increment count

    }
    else {
      matches=0;  // start over

    }

    nextReading = millis() + READ_INTERVAL;  // set time for next read

  }
  // if we got MATCH_COUNT in a row
  if( matches >= MATCH_COUNT )
  {
    servoLeft.write(80);
    servoRight.write(90);  //LANDING
    digitalWrite(led1, HIGH);
    digitalWrite(led2, LOW);  
    
    //    Serial.println(matches);
  }
  else {
  servoLeft.write(180);  //FLYING
  servoRight.write(0);  
  digitalWrite(led1, LOW);
  digitalWrite(led2, HIGH);
 }
}

I put the delay back in but then I removed it again and changed the count from 2 to 4. I think this will do it.
No delay and a 4 count. 4 counts separated by 1/2 second is the same as a 2 second delay so I have a good buffer there. I am experimenting with it on my desk right now. I might try 3 counts. When I'm done I'll upload the sketch to the arduino on my quad and I'll try it out this weekend. It looks promising - videos to follow.
The key will be how fast I descend from 3 feet. I'll need to go slow, I do anyway, to give it time to see the ground and lower the gear before I land on my 600.00 camera!.
Thanks again for all your help.

I can't believe it but it still happens.
I even changed the count to 8 counts with a read interval of 250 and it still false triggers.
It works great on my desk and when I walk around with my quad but not when it is flying.
Thank you for trying but I'm still stuck.

#include <Servo.h>

Servo servoLeft;         
Servo servoRight;       
#define echoPin 2       
#define trigPin 3
int led1 = 8; 
int led2 = 9; 

const int MATCH_COUNT = 8;        // # of matches for distance we need in a row
int matches = 0;                  // running count of matches
const long READ_INTERVAL = 250;   // how often to take a reading
long nextReading;                 // time for next reading

void setup() {
  //  Serial.begin(9600);
  servoLeft.attach(5); 
  servoRight.attach(6); 
  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  nextReading = millis();  // read right away
  servoLeft.write(80);
  servoRight.write(90);  
}

void loop() {           
  long duration, distance;

  // time for a reading?
  if( millis() >= nextReading )
  {

    digitalWrite(trigPin, LOW); 
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
    duration = pulseIn(echoPin, HIGH);
    distance = (duration/2) / 74; // 74=inches 29=cm

    if (distance < 36){  //  160cm=5'  62cm=2' 60"=5' 24"=2'
      matches++; // increment count

    }
    else {
      matches=0;  // start over

    }

    nextReading = millis() + READ_INTERVAL;  // set time for next read

  }
  // if we got MATCH_COUNT in a row
  if( matches >= MATCH_COUNT )
  {
    servoLeft.write(75);
    servoRight.write(90);  //LANDING
    digitalWrite(led1, HIGH);
    digitalWrite(led2, LOW);  
    
    //    Serial.println(matches);
  }
  else {
  servoLeft.write(180);  //FLYING
  servoRight.write(10);  
  digitalWrite(led1, LOW);
  digitalWrite(led2, HIGH);
 }
  // delay(1000);
}

new plan - add a second sensor
look twice at each

it just came to me in a flash of brilliance - updates to follow
(this pointless post is really just a bump)

sdtag:
new plan - add a second sensor
look twice at each

it just came to me in a flash of brilliance - updates to follow
(this pointless post is really just a bump)

Have you considered using the 'NewPing' library? It false triggers far less than the standard method of using a HC-SR04 sensor. It also has a built-in function to take a number of readings, (default 5), and return the average, 'ping_median'.
For more advanced use, it also includes timer-based 'pinging', firing an interrupt at regular intervals, to allow you to do other things while the ranging is going on.

Edit: I was having similar problems to you, with the HC-SR04 triggering for no apparent reason. That no longer happens with 'NewPing'.

sdtag:
new plan - add a second sensor
look twice at each

it just came to me in a flash of brilliance - updates to follow
(this pointless post is really just a bump)

Stupid is as stupid does.
(Forrest Gump)

In reply #2 I tried to tell you about your wrong distance calculations when the sensor is "out of range" and gets no echo back.

In reply #12 you are still posting the wrong distance calculation code.

It is NOT the same when the sensor is "out of range" and when the "distance is zero". You are still handling "distance out of range" the same as "distance < 36".

jurs:
Stupid is as stupid does.
(Forrest Gump)

In reply #2 I tried to tell you about your wrong distance calculations when the sensor is "out of range" and gets no echo back.

In reply #12 you are still posting the wrong distance calculation code.

It is NOT the same when the sensor is "out of range" and when the "distance is zero". You are still handling "distance out of range" the same as "distance < 36".

'NewPing' returns 0 when no echo is received. In my first, simple test of the lib, I handled it like this:-

if((lDistancecm==0)||(lDistancecm >= THRESHOLD))
   {
       digitalWrite(pGreenLED,1);
       digitalWrite(pRedLED,0);
   }
   else
   {
       digitalWrite(pGreenLED,0);
       digitalWrite(pRedLED,1);
   }

Edit: I just noticed that I had posted this code without enclosing code tags. Sorry. I don't know what I was thinking. Fixed now.

jurs:
Stupid is as stupid does.
(Forrest Gump)

In reply #2 I tried to tell you about your wrong distance calculations when the sensor is "out of range" and gets no echo back.

In reply #12 you are still posting the wrong distance calculation code.

It is NOT the same when the sensor is "out of range" and when the "distance is zero". You are still handling "distance out of range" the same as "distance < 36".

Life is like a box of jurs - you never know what you're going to get.

wow thanksalot I come here looking for help and you call me stupid.
it must be nice to know everything.

#include <Servo.h>

Servo servoLeft;
Servo servoRight;
#define echoPin 2
#define trigPin 3
int led1 = 8;
int led2 = 9;

const int MATCH_COUNT = 4;        // # of matches for distance we need in a row
int matches = 0;                  // running count of matches
const long READ_INTERVAL = 500;   // how often to take a reading
long nextReading;                 // time for next reading

void setup() {
  //  Serial.begin(9600);
  servoLeft.attach(5);
  servoRight.attach(6);
  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  nextReading = millis();  // read right away
  servoLeft.write(75);  //LANDING
  servoRight.write(95);
}

void loop() {
  long duration, distance;

  // time for a reading?
  if ( millis() >= nextReading )
  {

    digitalWrite(trigPin, LOW);
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
    duration = pulseIn(echoPin, HIGH);
    distance = (duration / 2) / 74; // 74=inches 29=cm

    if (distance < 36) { //  160cm=5'  62cm=2' 60"=5' 24"=2'
      matches++; // increment count

    }
    else {
      matches = 0; // start over
      distance = 999;
    }

    nextReading = millis() + READ_INTERVAL;  // set time for next read

  }
  // if we got MATCH_COUNT in a row
  if ( matches >= MATCH_COUNT )
  {
    servoLeft.write(75);  //LANDING
    servoRight.write(95);
    digitalWrite(led1, HIGH);
    digitalWrite(led2, LOW);

    //    Serial.println(matches);
  }
  else {
    servoLeft.write(180);  //FLYING
    servoRight.write(0);
    digitalWrite(led1, LOW);
    digitalWrite(led2, HIGH);
  }
  //delay(1000);
}

I was also going to try placing a small tube around the receiver, kind of like a rifle. Maybe about an inch or two long. That's about 50mm for you Euro's. I'm thinking it might make it more focused. Anyone try this yet?

sdtag:
I was also going to try placing a small tube around the receiver, kind of like a rifle. Maybe about an inch or two long. That's about 50mm for you Euro's. I'm thinking it might make it more focused. Anyone try this yet?

I read somewhere the other day, (sorry, can't remember where), that someone had done this with good results.
Have you tried the 'NewPing' library yet? It'll make your coding for multiple readings much simpler.