I have a PIR sensor which sometimes give false readings and I have added a ultrasonic sensor to compensate for that. I think my algorithm can work, however currently it is very prone to give false positives.
Algorithm theory
My idea is to have an arduino which will obtain six samples and wait 25 milliseconds between each sample and then calculate the variance. If the variance exceeds a specific threshold, which I have set to a thousnad, it will send an motion event over the serial bus.
Problem with the algorithm
Ultrasonic sensors can give faulty responds which usually isn't hard to detect. My HC-SR04 sensor can only accurately read the distance between 2 cm and 400 cm. All the faulty readings are usually way over 400 cm or lower than 2 cm, therefore if is not within these boundaries a new try will be made to get a value within the boundaries, if it fails for three times it will assume the distance is exactly 400 cm.
It is redundant to wait for more than 23280 µs because 400*29.1*2 is the corresponding microseconds it would take for the distance to be 400 cm. pulseIn
has a timeout value which can be used for this.
In theory this sounds great to me but very often this algorithm will erroneously send motion events. My belief for this is that when the ultrasonic sensor transmitts a wave and wait for it come back, pulseIn
will timeout and retry and yet again ask the sensor to send a wave. This happens before the first wave has come back thus the distance will be incorrect and vary from time to time making the variance higher than my threshold.
If the distance is much lower than 4 metres my algorithm works very well.
What I tried
I figured I might be able to block for some time until the echo pin on the ultrasonic sensor turns low or a time of 100ms has passed. The code hanged and I couldn't communicate with my arduino any more. I can't see the problem with my code to obtain the distance pursuant to the ultrasonic sensor.
for(tries=3;tries;--tries) {
digitalWrite(TRIG_PIN), LOW); // Set trigger pin low.
delayMicroseconds(2); // Let signal settle.
digitalWrite(TRIG_PIN, HIGH); // Set trigger pin high.
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW); // Ping has now been sent.
unsigned long duration = pulseIn(ECHO_PIN, HIGH, 23280); // Duration is in microseconds
unsigned long time = micros();
while(!(PINB & 0b00000010)) { // ECHO_PIN is PB1 or digital pin 9
if((micros() - time) >= 100000) {
continue;
}
}
if(duration >= 116L && duration <= 23280L) { // Is duration within boundaries?
return ((float)(duration)/2.0) / 29.1;
}
}
return 400.0f;
This just blocks my program indefinently and I had to upload a new program removing that code for my arduino to respond again.
So my question boils down to: how can I resolve this issue where my algorithm very frequently sends motion events erroneously? Is there a better way to detect motion with an ultrasonic sensor?
(EDIT: I was really tired when I wrote the code above and Majenko corrected me, I changed the code for this
for(tries=3;tries;--tries) {
digitalWrite(TRIG_PIN), LOW); // Set trigger pin low.
delayMicroseconds(2); // Let signal settle.
digitalWrite(TRIG_PIN, HIGH); // Set trigger pin high.
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW); // Ping has now been sent.
unsigned long duration = pulseIn(ECHO_PIN, HIGH, 23280); // Duration is in microseconds
unsigned long time = micros();
while(PINB & 0b00000010) { // ECHO_PIN is PB1 or digital pin 9
if((micros() - time) >= 100000) {
goto retry;
}
}
if(duration >= 116L && duration <= 23280L) { // Is duration within boundaries?
return ((float)(duration)/2.0) / 29.1;
}
retry:
continue;
}
return 400.0f;