-
-
Save wolph/0094b278a54f8f472c76b7eb0d0a8f89 to your computer and use it in GitHub Desktop.
#include <Arduino.h> | |
// Uses https://github.com/PaulStoffregen/TimerOne for sending on a regular interval | |
#include <TimerOne.h> | |
// ECHO pin, needs to be a pin that supports interrupts! | |
#define ULTRASONIC_PIN_INPUT 2 | |
// TRIG pin, can be any output pin | |
#define ULTRASONIC_PIN_OUTPUT 3 | |
// update interval, make sure to keep it above 20ms | |
#define ULTRASONIC_TIMER_US 50000 | |
// the time the pulse was sent | |
volatile long ultrasonic_echo_start = 0; | |
// the distance once it's been measured | |
volatile long ultrasonic_distance = 0; | |
void ultrasonicPulse(){ | |
// Sets the trigger on HIGH state for 10 micro seconds to send a series of pulses | |
digitalWrite(ULTRASONIC_PIN_OUTPUT, HIGH); | |
// blocks 10 microseconds from the interrupt, I think we'll live :) | |
delayMicroseconds(10); | |
// disable the sending again so we can wait for a response | |
digitalWrite(ULTRASONIC_PIN_OUTPUT, LOW); | |
// record the send time | |
ultrasonic_echo_start = micros(); | |
} | |
void ultrasonicEcho(){ | |
// don't do anything if no pulse has been sent | |
if(ultrasonic_echo_start != 0){ | |
// calculate the distance by measuring how long it took to return the sound | |
// The speed of sound is 343 m/s and we need half the time it took (since | |
// the sound has to travel towards the object _AND_ back). So a single echo does | |
// 1/(343/2) = 0.005831 seconds per meter | |
ultrasonic_distance = (micros() - ultrasonic_echo_start) / 58; | |
ultrasonic_echo_start = 0; | |
} | |
} | |
void setup(){ | |
Serial.begin(115200); | |
// set the echo pin to receive interrupts, on an arduino uno only pin 2 and 3 can link to interrupts | |
pinMode(ULTRASONIC_PIN_INPUT, INPUT_PULLUP); | |
pinMode(ULTRASONIC_PIN_OUTPUT, OUTPUT); | |
// set the update interval for sending the trigger | |
Timer1.initialize(ULTRASONIC_TIMER_US); | |
// link the trigger function to the timer | |
Timer1.attachInterrupt(ultrasonicPulse); | |
// link the echo function to the echo pin | |
attachInterrupt(digitalPinToInterrupt(ULTRASONIC_PIN_INPUT), ultrasonicEcho, RISING); | |
} | |
void loop() { | |
Serial.print("Distance: "); | |
Serial.println(ultrasonic_distance); | |
delay(ULTRASONIC_TIMER_US / 1000); | |
} |
Strangely, I get a constant 8cm distance when using this code. I ended up on this page because I had written my own version and was getting the 8cm issue. Our programs are nearly identical, so I copied yours to see if it worked better. Everything works perfectly fine if I don't use the pin interrupt and instead just use pulseIn during the timer interrupt, but of course that needlessly ties things up. I'll do some more digging tomorrow. Just figured I would mention it in case anybody has a similar problem in the future.
For good practice, add these three lines to the start of ultrasonicPulse()
to clear the trigger pin.
// Clears the trigger Pin
digitalWrite(ULTRASONIC_PIN_OUTPUT, LOW);
delayMicroseconds(2);
like so
void ultrasonicPulse(){
// Clears the trigger Pin
digitalWrite(ULTRASONIC_PIN_OUTPUT, LOW);
delayMicroseconds(2);
// Sets the trigger on HIGH state for 10 micro seconds to send a series of pulses
digitalWrite(ULTRASONIC_PIN_OUTPUT, HIGH);
// blocks 10 microseconds from the interrupt, I think we'll live :)
delayMicroseconds(10);
// disable the sending again so we can wait for a response
digitalWrite(ULTRASONIC_PIN_OUTPUT, LOW);
// record the send time
ultrasonic_echo_start = micros();
}
Also, the echo is a falling edge (usually), unless the signal has been inverted, so
attachInterrupt(digitalPinToInterrupt(ULTRASONIC_PIN_INPUT), ultrasonicEcho, RISING);
should be
attachInterrupt(digitalPinToInterrupt(ULTRASONIC_PIN_INPUT), ultrasonicEcho, FALLING);
One other thing is that there is an assumption made in your code that the echo pin has risen HIGH, when you execute this line:
ultrasonic_echo_start = micros();
Logically, it should have but it might not be the case.
Aiming to code the same functionality I found this conversation and I thank you for the examples.
I'd like to share my version of the code, where I don't use a timer for the trigger (maybe should be a good pratice...) but I try to improve the accuracy considering only the time between the two echo edges (anyway, the same goal could have been reached by compensating the offset).
#include <TimerInterrupt_Generic.h>
const int trigPin = 4;
const int echoPin = 3; //3x 3.3kOhm used to divide voltage
volatile bool echoFallen = false;
volatile unsigned long echoStart, echoEnd;
unsigned long duration;
unsigned int distance;
void setup(){
Serial.begin(9600);
pinMode(echoPin, INPUT);
pinMode(trigPin, OUTPUT);
attachInterrupt(digitalPinToInterrupt(echoPin), ultrasonicEcho, CHANGE);
}
void loop() {
//send trigger
ultrasonicTrig();
//wait for falling edge
if(echoFallen){
duration = echoEnd - echoStart;
//s = v*t, sound speed is 343 m/s, wave reaches object and comes back
distance = .0343*(duration/2);
//unset falling edge flag
echoFallen = false;
}
//output
Serial.print("Distance (cm): ");
Serial.println(distance);
delay(1000);
}
//trigger
void ultrasonicTrig(){
//clear pin
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
//send pulse
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
}
//ISR
void ultrasonicEcho(){
switch(digitalRead(echoPin)){
case HIGH:
echoStart = micros();
break;
case LOW:
echoEnd = micros();
//set falling edge flag
echoFallen = true;
}
}
I've tried it with this thing and it seemed to work fine: https://wiki.keyestudio.com/Ks0192_keyestudio_4WD_Bluetooth_Multi-functional_Car
I'm not ruling out that there are bugs though... but it seemed to work perfect for a distance range between ~20cm and 1.5m if I remember correctly.