A simple ultrasonic range finder for embedded projects: presence detector, robotics, car parking.

Overview

A simple ultrasonic range finder for embedded projects: presence detector, robotics, car parking. With a few cheap components and less than 200 bytes of code, it works from 30 to 200 cm with approximately 1 cm accuracy.

Component side
Component side
Solder side
Solder side

How it Works

Sound travels at approximately 340 m/s in air. The principle is simple: send a 40 KHz ultrasonic pulse, then listen for the echo. The time between transmission and reception gives you the distance.

The PWM output at 40 KHz drives the ultrasonic transmitter via a transistor and resonator circuit. The resonator circuit boosts the voltage to about 20V, giving a range of up to 200 cm. The receiver is connected directly to the PIC ADC input.

Scope screen capture
Scope screen capture

Circuit Schematic

Ultrasonic range finder circuit schematic
Ultrasonic range finder circuit schematic

C Source Code

sonar.c
/*
 * file         : sonar.c
 * project      : Simple UltraSonic Range Finder
 * compiler     : mikroC V6.2
 * date         : september 30, 2006
 *
 * description  :
 *      This is a basic ultrasonic range finder, from 30 to 200 centimeters
 *
 * target device :
 *      PIC16F877A with 8 Mhz crystal
 *      or any PIC with at least one ADC and PWM channel
 *
 * configuration bits :
 *      HS clock, no watchdog, no power up timer,
 *      no brown out, LVP disabled, ICD disabled
 *
 *******************************************************************************
 */

/********************
 * MACRO DEFINITIONS
 ********************/
#define PULSELEN        300     // ultra sonic pulse length in microseconds
#define BUFSIZE         10      // circular buffer size for averaging

#define LCDPORT PORTD
#define LCDTRIS TRISD

/*******************
 * GLOBAL VARIABLES
 *******************/
unsigned char   outOfRange ;            // out of range flag
unsigned int    buf[BUFSIZE] ;          // samples buffer
unsigned char   idx = 0 ;               // current sample index

/*****************************************
 * INTERRUPT SERVICE ROUTINE
 * handles TIMER1 overflow only
 *****************************************/
void    interrupt(void)
        {
        if(PIR1.TMR1IF)
                {
                outOfRange = 1 ;
                PIR1.TMR1IF = 0 ;
                }
        }

/************
 * MAIN LOOP
 ************/
void    main()
        {
        ADCON1 = 0 ;            // enables ADC

        TRISA = 0xff ;          // PORTA as inputs
        PORTA = 0 ;

        TRISC = 0 ;             // PORTC as outputs
        PORTC = 0 ;

        T1CON = 0b00001100 ;    // prescaler 1:1, osc enabled, internal clk, stopped

#ifdef   LCDPORT
        Lcd_Init(&LCDPORT) ;
        Lcd_Cmd(Lcd_CLEAR) ;
        Lcd_Cmd(Lcd_CURSOR_OFF) ;
        Lcd_Out(1, 1, "UltraSonicRanger") ;
        Lcd_Out(2, 5, "cm") ;
#endif

        // init PWM : 40 KHz, 50% duty cycle
        PWM1_Init(40000) ;
        PWM1_Change_Duty(128) ;

        INTCON.GIE = 1 ;
        INTCON.PEIE = 1 ;
        PIE1.TMR1IE = 0 ;
        PIR1.TMR1IF = 0 ;

        for(;;)
                {
                unsigned char   i ;
                unsigned long   cm ;
                unsigned char   str[4] ;

                // prepare timer
                T1CON.TMR1ON = 0 ;
                outOfRange = 0 ;
                TMR1H = 0 ;
                TMR1L = 0 ;

                T1CON.TMR1ON = 1 ;              // start timer 1
                PIE1.TMR1IE = 1 ;               // enable overflow interrupt

                // send pulse
                PWM1_Start() ;                  // pulse at ultrasonic frequency
                Delay_us(PULSELEN) ;            // during PULSELEN microseconds
                PWM1_Stop() ;

                Delay_us(PULSELEN * 2) ;        // wait to prevent false start

                while(Adc_Read(1) < 1)          // while no echo detected
                        {
                        if(outOfRange) break ;  // too late, out of range
                        }

                T1CON.TMR1ON = 0 ;              // stop timer 1
                PIE1.TMR1IE = 0 ;

#ifdef LCDPORT
                if(outOfRange)
                        {
                        Lcd_Out(2, 8, "OverRange") ;
                        }
                else if(TMR1H < ((PULSELEN * 6 * Clock_kHz()) / (1000 * 4 * 256)))
                        {
                        Lcd_Out(2, 8, "UnderRnge") ;
                        }
                else
                        {
                        buf[idx] = TMR1H ;              // build 16-bit value from timer1
                        buf[idx] <<= 8 ;
                        buf[idx] += TMR1L ;

                        idx++ ;
                        if(idx == BUFSIZE)
                                {
                                idx = 0 ;
                                }

                        cm = 0 ;
                        for(i = 0 ; i < BUFSIZE ; i++)
                                {
                                cm += buf[i] ;
                                }
                        cm /= BUFSIZE ;                 // average samples

                        /*
                         * cm contains now the number of clock cycles
                         * from start of ultrasonic transmission to first echo
                         * duration in seconds: s = cm / (Clock_Khz() * 1000 / 4)
                         * sound speed in air: 340 m/s
                         * distance (forth and back): d = s * 340 * 100 / 2
                         * d = 34 * 2 * cm / Clock_Khz()
                         */
                        cm *= 34 * 2 ;
                        cm /= Clock_Khz() ;

                        ByteToStr(cm, str) ;
                        Lcd_Out(2, 1, str) ;
                        Lcd_Out(2, 8, "         ") ;
                        }
#endif

                Delay_ms(10) ;                  // 10 ms before next sample
                }
        }

Note

No separate project ZIP file was provided for this project. The source code above is the complete mikroC program — copy and paste it into your mikroC IDE to build the project.