Sonar

I’ve been working on building an audio range finding device using my Arduino. The basic idea is:

  1. transmit 4000hz pulse to a piezo
  2. record timestamp for transmit time
  3. wait for an echo pulse from 567 tone decoder to go LOW on first echo detection or timeout. The 567 outputs a continuous HIGH until it detects the set frequency then it goes low for the length of the pulse.
  4. if an echo is detected record a second timestamp for echo reception
    5 calculate the distance to object using time of flight

I’m pretty new to the Arduino, so I was wondering if the group could offer any suggestions on improving the software?

//setup transmit/receive pin

int transmitPin = 9;

void setup() {                

//setup serial communication
  Serial.begin(9600);
//
  pinMode(transmitPin, OUTPUT);
}

void loop() {

// 
  long echo1Duration, echo1, echo1Distance, transmitTimeStamp, receiveTimeStamp,timeout;

timeout = 0;

tone(transmitPin, 4000, 0.2);

transmitTimeStamp = micros();

pinMode(transmitPin, INPUT);

while ((echo1 == HIGH) || (timeout < 100)){
echo1 = digitalRead(transmitPin);
timeout++;
}

if (echo1 == LOW) {
receiveTimeStamp = micros();
echo1Duration = receiveTimeStamp - transmitTimeStamp;
echo1Distance = calculateDistance(echo1Duration);
Serial.println(echo1Distance); 
delay(100);
}

}

//based on Ping distance calculation
long calculateDistance(long microseconds){
return microseconds / 74 / 2;
}

Are you looking for improvement (meaning that it works, just less than optimally), meaning this already works?

I'd definately not put a pinmode declaration in loop() unless really needed, like to manipulate pullup resistors.

I think a better approach might be to attach an interrupt to the state change provided by the decoder. You'll find reference to attachInterrupt() in the Reference section, it's a standard Arduino function.

Basically, instead of your sample loop, you just have the Interrupt Service Routine grab the time.. you can't miss the pulse, and you'll get VERY precise timing compared to what you've done... one important caveat is that Millis() counter is disabled during the ISR routine execution, but since all you are doing is grabbing the time and exiting, the non-updated millis() value shouldn't be a concern.

http://arduino.cc/en/Reference/AttachInterrupt

Ok, the tone didn’t work for me. It produced a really weak pulse.

I changed to use a function I came across while back (my apologies I lost the credit in the code if anybody knows who wrote the code please let me know and I will add it back in) and it seems to work better.

Here is the new code with the attachinterrupt. Did I get this right?

int transmitPin = 9;
int receivePin = 0;
int pitchval = 1;
int val = 0;
long echo1Duration, echo1, echo1Distance, transmitTimeStamp, receiveTimeStamp;

void setup() {                
  pinMode(transmitPin, OUTPUT);
  pinMode(receivePin, INPUT);
}

void loop() {

// transmit 

signalOut(4000, 2);

transmitTimeStamp = micros();
attachInterrupt(receivePin, signalIn, LOW);

void signalOut(int freq, int t){
  int hperiod; //calculate 1/2 period in us
  long cycles, i;
  hperiod = (500000 / ((freq - 7) * pitchval));
  cycles = ((long)freq * (long)t) / 1000;
    for (i=0; i<=cycles; i++){
      digitalWrite(transmitPin, 255);
      delayMicroseconds(hperiod);
      digitalWrite(transmitPin, 0);
      delayMicroseconds(hperiod - 1);
  }
}

void signalIn(){
  receiveTimeStamp = micros();
  echo1Duration = receiveTimeStamp - transmitTimeStamp;
  echo1Distance = calculateDistance(echo1Duration);
  println(echo1Distance);
}

long calculateDistance(long microseconds){
return microseconds / 74 / 2;
}

Did I get this right?

No, as you've probably found out, this dialect of C doesn't allow nested function declarations.

Are you sure your hardware is working. It sounds like a bit of a hairy thing to do, not to mention very annoying if you happen to be standing close.