SRF05

Although we already have an abstraction for the SRF08 range finder, they are > £25.

Also available are the lower cost (£12) SRF05 modules. They are a little more raw in that they have a trigger pin and an echo pin (actually, you can combine them too), and everything is done by measureing echo delays.

source:/SRF05/doc/SRF05.jpg

Since we now have a good library for timing an interrupts, it seems like a good idea to give these a go.

Available from Rapid Electronics

There re tons of good resources about this. Here is what I have been reading at www.robot-electronics.co.uk

Abstraction Class

Below is the history of this project, that ends with there being a nice abstraction class to use. If you simply want to use this class, you can import it into your project

Hooking it up

source:/SRF05/doc/srf05_breadboard.jpg

SRF05 pin Signal mbed pin
1 +5v Supply Vu*
2 Echo output p10
3 Trigger input p9
4 Mode ** n/c
5 0v Ground GND

*Vu is only powered if you are plugged in to USB. If you wnat to unplug, you'll need yourself a battery pack and a 5v regulator.

** If you leave mode unconnected (it has internal pullup) trigger and echo are separate. If you ground it, trigger and echo share the trigger pin (you have 700uS to turn it around)

Hello World

Okay, so what I am doing here is :

  • Generating a trigger pulse manually
  • using a timer object to time the echo pule
  • Using .rise and .fall functions of the echo pin to start and stop the timer
#include "mbed.h"

Serial pc (USBTX,USBRX);

DigitalOut trigger (p9);
DigitalIn echo (p10);

Timer tmr;

int delay=0; // variable to read the echo time into

// Clear and start the timer at the begining of the echo pulse
void rising(void) {
    tmr.reset();
    tmr.start();
}

// Stop and read the timer at the end of the pulse
void falling(void) {
    tmr.stop();
    delay=tmr.read_us();
}

int main() {

    // Attach interrupts
    echo.rise(&rising);
    echo.fall(&falling);

    // send a trigger pulse, 20uS long
    trigger = 1;
    wait (0.000002);
    trigger = 0;

    // Timer starts on rising edge of echo
    // Timer stopped and read on falling edge
    // wait 50ms as a time out (there might be no echos)
    wait(0.050);
    
    pc.printf("Range is %f cm\n",delay/58.0);
}

source:/SRF05/doc/helloworld.gif

Next step

Rather than having to trigger, wait and read, it might be better if the thing was continuously ranging, so when you read, you get the last measurement that was taken (within 100ms - probably not an issue)

Do do this, i'll have a look at the ticker ...

float distance=0.0; // variable to read the echo time into

void startRange() {
    // send a trigger pulse, 20uS long
    trigger = 1;
    wait (0.000002);
    trigger = 0;
}


// Clear and start the timer at the begining of the echo pulse
void rising(void) {
    tmr.reset();
    tmr.start();
}

// Stop and read the timer at the end of the pulse
void falling(void) {
    tmr.stop();
    distance = tmr.read_us()/58.0;
}

int main() {

    // Attach interrupts
    echo.rise(&rising);
    echo.fall(&falling);
    range.attach(&startRange, 0.1); 
    
    while (1) {    
        pc.printf("Range is %.1f cm\n",distance);
        wait (0.2);
    }

}

Yup, that works a treat now. Now I want to make a class for it where it continually samples, and the value can be read back in cm, m or us. I also want to make the read block until a valid samples has been taken. In the current implementation I get round the while loop a good few times before the first sample has been made.

Making a class

Okay, so if I am going to build a class, this is what the API will look like :

  • SRF05(trigger, echo)
  • int read_cm() // number of centimeters to object
  • float read // Float in meter

Okay, so i have buit what appears to be a sensible class. It is not meant to be polished yet, but it does look vaguely sensible to me. the problem is it doenst compile.

The class header looks like this:

#ifndef MBED_SRF05_H
#define MBED_SRF05_H
#include "mbed.h"

namespace mbed {
    class SRF05 {
        // Public functions
        public:     
            SRF05(int trigger, int echo);
            int read_cm (void);
            float read (void);
        private :   
            DigitalOut _trigger;    
            DigitalIn _echo;    
            Timer _tmr;
            Ticker _range;
            void _rising (void);
            void _falling (void);
            void _startRange (void);
            float _dist;
            int _valid;
    };
}
#endif

While the .cpp file for the class looks like this :

#include "SRF05.h"
#include "mbed.h"


/* ==================================================================
 * Constructor
 ================================================================= */
SRF05::SRF05(int trigger, int echo) 
    : _trigger(trigger), _echo(echo) {  
    
    _valid = 0;        // clear the valid sample flag
    
    // Attach interrupts
    _echo.rise(&_rising);
    _echo.fall(&_falling);
    _range.attach(&_startRange, 0.1);     
}

    
void SRF05::_startRange() {
    // send a trigger pulse, 20uS long
    _trigger = 1;
    wait (0.000002);
    _trigger = 0;
}

// Clear and start the timer at the begining of the echo pulse
void SRF05::_rising(void) {
    _tmr.reset();
    _tmr.start();
}

// Stop and read the timer at the end of the pulse
void SRF05::_falling(void) {
    _tmr.stop();
    _dist = _tmr.read_us()/58.0;
    _valid=1;
}


int SRF05::read_cm(void) {
    // spin until there is a good value
    while (_valid == 0) {}
    return ((int)_dist);
}

float SRF05::read(void) {
    // spin until there is a good value
    while (_valid == 0) {}
    return (_dist/100.0);
}

The errors are :

"/SRF05/src/SRF05.h", line 24: Warning:  #1-D: last line of file ends without a newline
"/SRF05/src/SRF05.cpp", line 15: Error:  #167: argument of type "void (mbed::SRF05::*)()" is incompatible with parameter of type "void (*)()"
"/SRF05/src/SRF05.cpp", line 16: Error:  #167: argument of type "void (mbed::SRF05::*)()" is incompatible with parameter of type "void (*)()"
"/SRF05/src/SRF05.cpp", line 17: Error:  #304: no instance of overloaded function "mbed::Ticker::attach" matches the argument list
      _range.attach(&_startRange, 0.1);
             ^
/SRF05/src/SRF05.cpp: 1 warning, 3 errors

I can fix the first one easily enough. A bit puzzles about the issues with attaching functions to interrupts and tickers. Need advice some someone who knows what they are doing....

<fast forward a few months>

So, the problem with ths compilation was that the libraries didn't support the ability to attach member function to a ticker or an interrupt inside the class. The new release of the libraries do, so here is what the final class looks like :

#include "SRF05.h"
#include "mbed.h"

/*
 * Constructor
 */
SRF05::SRF05(PinName trigger, PinName echo) 
    : _trigger(trigger), _echo(echo) {    
        
    // Attach interrupts
    _echo.rise(this, &SRF05::_rising);
    _echo.fall(this, &SRF05::_falling);
    _ticker.attach(this, &SRF05::_startRange, 0.1);     
}
  
void SRF05::_startRange() {
    // send a trigger pulse, 20uS long
    _trigger = 1;
    wait (0.000002);
    _trigger = 0;
}

// Clear and start the timer at the begining of the echo pulse
void SRF05::_rising(void) {
    _timer.reset();
    _timer.start();
}

// Stop and read the timer at the end of the pulse
void SRF05::_falling(void) {
    _timer.stop();
    _dist = _timer.read_us()/58.0;
}

float SRF05::read(void) {
    // spin until there is a good value
    return (_dist);
}

SRF05::operator float() {
    return read();
}

*************************** Fast forward even further:

Now that Chris has done all the hard stuff and worked out a "Hello Word" for the SRF05 and secreted it in the header file, I'll take over and do the easy bit and put it here. I use the Nokia 6610 display as the primary output so the program reflects that.

#include "mbed.h"
#include "SRF05.h"
#include "MobileLCD.h"

MobileLCD lcd(p5, p6, p7, p8, p9);
SRF05 srf(p13, p14);

// Print measured distance  
 
int main() { 
     while(1) { 
         lcd.locate(0,0);
         lcd.printf("Distance = %.1f", srf.read());              
         wait(0.2); 
     } 
 }