I needed some ultrasonic range finders for my project. But most of the commercial sensors like Parallax’s PING sensor and other similar products are quite expensive, especially if multiple units are needed. So I thought why not building it myself?

The theory behind ultrasonic ranging is quite simple. Typically a short ultrasonic burst is transmitted from the transmitter. When there is an object in the path of the ultrasonic pulse, some portion of the transmitted ultrasonic wave is reflected and the ultrasonic receiver can detect such echo. By measuring the elapsed time between the sending and the receiving of the signal along with the knowledge of the speed of sound in the medium, the distance between the receiver and the object can be calculated. The picture below (source: Wikipedia) illustrates this basic principal:

Ultrasonic Ranging (Courtesy of Wikipedia)
Ultrasonic Ranging (Courtesy of Wikipedia)

In my design, I used separate transducers for transmitter and receiver. It is possible to multiplex the transmission and receiving with a single transducer (e.g. Maxbotix range finders), but the design would be significantly more complex.

Ultrasonic Transducer

There are quite a few ultrasonic transducers to choose from, and the main criteria are the resonant frequency, radiation pattern and sensitivity. Generally speaking, these parameters affect the measurement in the following ways: a higher resonant frequency can provide finer details of the surroundings due to the shorter wavelength. A more directional radiation pattern can also enhance the resolution of the measurement. Sensitivity affects the efficiency of the transducer and also attributes to the SNR (signal to noise ratio).

I bought these 24 kHz transducers on sale (see picture below). These transducers are very inexpensive (around a dollar each, and even cheaper when on sale) but effective. With properly designed circuits these sensors can easily achieve a range of more than 20 feet. Of course, using the higher priced 40 kHz sensors should achieve even better performance.

24 kHz Ultrasonic Transducers
24 kHz Ultrasonic Transducers

But for these DIY ultrasonic range finders, the choice of the transducers are really not that critical and this transducer really hits the performance to price sweet spot.


The Transmitter

The ultrasonic transmitter is powered from ATmega328’s counter 1 PWM output (chip pin 16 and Arduino digital pin 10). In order to achieve the maximum output power of the transducer for a given supply voltage, I used the bridged output design as shown in the following schematics:

Ultrasonic Transmitter
Ultrasonic Transmitter

This bridged circuit produces an output voltage roughly twice the Vcc. I used +5V for Vcc and the result is already quite good (more than 20 feet of range). For even longer range measurement, you can safely increase this driving voltage to around 12 Volts as most ultrasonic transducers can be driven with voltage as high as 20 to 30 volts. If you increase the voltage significantly above 5V however, you will have to change the transistors to allow more power dissipation. With 2N3904 and 2N3906 the transistors get warm during normal operation and would heat up drastically with voltage above 6V.

Here is the output of the ultrasonic burst measured at the output transducer’s terminals:

Output Waveform
Output Waveform

The small “ladders” at the half-way voltage point in the output waveform is due to the slight added delay of the inverted signal stage due to the use of an extra NPN transistor. To obtain purer rectangular wave form and reduce switching loss, a PNP transistor with similar timing parameters can be used on the side that is directly connected to the driving signal. For this application though, the waveform is more than adequate and the added switching loss is negligible.

The transmitter and receiver transducers can be mounted on a circuit board with approximately one inch of spacing (see below).

Ultrasonic Range Sensor
Ultrasonic Range Sensor

In order to reduce possible interference from the reflected ultrasonic waves, the components are mounted on the reverse side of the board (below is the H-bridge circuit that drives the ultrasonic transducer, a few decoupling capacitors are used to reduce noise and they are not shown in the schematics above):

H Bridge
H Bridge

The code to drive the transducer is similar to that I used previously, except that I changed the pre-scalar to 1 so that the output frequency can be controlled more precisely in the kHz range.

void startTransducer(float freq, float dutyCycle)
  if (dutyCycle > 0.5) dutyCycle = 0.5;
  else if (dutyCycle < 0) dutyCycle = 0;

  TCCR1B = _BV(WGM13) | _BV(CS10) | _BV(ICNC1);
  //f0 = fclk / (2 * N * Top)
  long topv = (long) ((float) F_CPU /(freq * 2.0 * 1.0));
  ICR1 = topv;

  OCR1A = (int) ((float) topv * dutyCycle);
  OCR1B = (int) ((float) topv * (1 - dutyCycle)); 
  DDRB |= _BV(PORTB1) | _BV(PORTB2);
  TCCR1A = _BV(COM1A1) | _BV(COM1B1); 

void stopTransducer()
  TCCR1B = 0;


The Receiver

The performance of the range sensor is largely determined by the sensitivity of the receiver for a given transmitter power level. Because the received signal is usually very weak (less than 1 mV), a high gain low noise amplifier is needed to ensure optimal performance.

I used a two stage inverted band-pass amplifier design (see below). Each stage has a gain of around 67 (36.5 dB) and the circuit has a combined voltage gain of 73 dB. The operational amplifier I used is National’s LPC662. In general, any operational amplifier with a sufficient gain bandwidth product should work just as well.

Each stage has a band-pass filter that is centered around the operation frequency (24 kHz). Because the amplifier has a very high gain, we must pay special attention to the circuit layout in order to prevent parasitic oscillation. The connection between the receiver transducer and the circuit input (6.8n capacitor) needs to be shielded to reduce noise and unwanted coupling.

Ultrasonic Receiver
Ultrasonic Receiver

Because we are using a single power supply the output voltage of the opamp is centered at around Vcc/2 (2.5V). In order to make it easier to process the echo, a diode (IN4148), capacitor (0.1uF) and resistor (10k) are used to demodulate the signal and a coupling capacitor (1uF) is used to rid the demodulated signal of the DC component.

You can see the demodulated envelope waveform from the following oscilloscope screenshots (you can ignore the frequency measurement as these signals are none-periodical the frequency readings are meaningless). The higher amplitude waveforms in both images are the results of the ultrasonic burst, the lower amplitude waveforms are from the echo. In the first screenshot on the left, two echoes can be seen.

Echo and the demodulated waveform 1
Echo and the demodulated waveform 1
Echo and the demodulated waveform 2
Echo and the demodulated waveform 2

The following screenshot shows the relationship between the ultrasonic pulses (measured from ATmega328 pin 16) from the transmitter and the demodulated echo output. One key observation is that the received signal takes much longer time to fade then the original pulse duration and thus we must add in some delay after the transmission of the ultrasonic pulses. A delay of 1 to 2 millisecond is typical. With a 1 millisecond delay, the shortest measurable distance is around 30 centimeters or one foot.

Demodulated Echo
Demodulated Echo

And here is a picture of the finished project.

Ultrasonic Range Finder
Ultrasonic Range Finder

Range Calculation

Since the measured distance is a function of the time interval between the time at which the pulse is transmitted and the time at which the echo is received, we need to reliability detect the echo.

Empirically, we can measure the peak of the received echo and use the time displacement to calculate the distance. We assume that the strongest echo comes from the closest object (this may not always be true as the reflectivity of different objects are different, but generally achieves very good results in real-world situations) and thus the peak measurement corresponds to the closest object’s position.

The code snippet below assumes that we are interested in measuring objects with a range of up to about 20 feet. After the pulses are transmitted from the transmitter, we wait for a millisecond for the initial receiver signal to fade. Then we search the peak value in the next 20 milliseconds or so (the loop limit 256 is set empirically, in the code below this setting corresponds to a 20 milliseconds interval between transmitted pulses which is suitable for distance measurement up to approximately 20 feet. To measure longer distance, the upper limit for the loop needs to be increased correspondingly) and assume that the peak comes from the first echo.

byte a = 0;
unsigned long t_start = 0;
unsigned long t_peak = 0;
unsigned long t = 0;
byte v_peak = 0;
const float SPEED_OF_SOUND_20C = 0.0003432; //meters per micro-second
float d = 0;

void loop()
  startTransducer(24000.0, 0.5);

  v_peak = 0;
  t_start =micros();
  t_peak = t_start;

  for (int i = 0; i < 256; i++) {
    a = analogRead(0);
    t = micros();

    if (a > v_peak) {
      t_peak = t;
      v_peak = a;

  t = t_peak - t_start;
  d = (float) t * SPEED_OF_SOUND_20C / 2.0;
  Serial.println(d , 2);

Here’s the full code listing for this project:

The bill of material for this ultrasonic range finder is less than 5 dollars (excluding the MCU since it can be incorporated into your projects).


I would like to thank Thomas for pointing out the mistakes in my H-Bridge schematic. The PNP transistor’s collector and emitter are swapped by mistake (I should have double checked the schematics. Anyway, the photo shows the correct orientation) and I have updated the image.

Also on the receiver side, the first OP’s output was missing connection to the 0.1uF capacitor, I have updated it as well.

Be Sociable, Share!

286 Thoughts on “A Sensitive DIY Ultrasonic Range Sensor”

  • Hi everyone,

    I have two ultrasonic sensor, and planned to use it for distance measuring. But I am new to this thing.
    May I ask which one is the Transmitter and the Receiver?

    Here’s the picture of my sensor:

    Bottom view : http://goo.gl/H772c
    Top view : http://goo.gl/Z4Pzp

    Will it work if I connect it directly to my PIC16F84a mcu IO pin?

    If you know the link to the documentation of this sensor, please let me know.

    thanks in advance and hope you can help me.


  • On the transmitter, I’d probably replace all the transistors for one of those H-bridge ICs, used for DC motor drivers.
    You can also use some RS232 IC and pull out some +/-15V to drive the Xducer from a PIC pin
    At the receiver, I’d use a 3rd opamp as Schmitt trigger, to clean up the output a bit.

    Note that 24KHz falls within the hearing range of dogs and cats. At the 10Vpp you’re using, your circuit can easily output some 90-100dB -which dogs will love to hear. If you have pets at home, you can use 40KHz Xducers.

    The receiver circuit was great, thanks for posting! Been digging on Google for a while, trying to find some info about the voltage levels at the receiver side, before and after the amplifier.


  • Hey this circuit of yours id pretty useful =)
    I wana know what changes will I have to make if my sonar pair works on 40khz?
    waiting for ur reply

    • You can change the RC filters to a higher resonant frequency (> 40kHz) and also change the following line

      startTransducer(24000.0, 0.5);


      startTransducer(40000.0, 0.5);

      so the circuit will generate signals at 40kHz instead of 24kHz.

  • Kerry Wong, This is a great circuit. Can I use this to measure the water level in a tank? The distance I will be measuring will be about 10 Feet maximum. If I use 40KHZ transducers, can I drive the transducer without bridge circuit? Can I connect it directly to GND and PIN 10? And also can you please suggest the RC values 40KHZ transducers?

    Thanks in Advance

    • Hi Lavan,

      I haven’t tried measuring water depth, but it seems that it should work since the water surface will reflect the sound wave. You cannot connect the transducer directly to PIN 10 as each pin does not have sufficient power to power the transducer. You could use a simple transistor to amplify the signal if you intend to use a single supply, but the range would be greatly affected.

      Regarding the RC values, the values shown in the circuit above are actually wider than necessary (with a bandwidth of 19 to 60kHz) so it should work with a 44kHz transducer as well (of course you will have to change the transducer frequency via the startTransducer() function.

  • hi kwong sir, your design is very nice. and i’m a beginner to this actually. but i know some basics of electronics. so i need a calculation mode for putting a resistor and capacitor in receiver side. i use 40khz transceiver.so for that you suggest 1n in place of 6.8n, 4k in place of 1.2k, like that… and can you please give some links to get information about, how to produce a desired gain from LPC662. and also for about H bridge design.

  • ???

    The GBW of the LPC662 is 0.35 MHz. How can you possibly expect to get a gain of 67 out of it at 24 KHz? Don’t you need a GBW of at least 1.6 MHz?

    • You are correct. That was the only opamp I had at the time of the build. LPC662’s GBP isn’t quite sufficient in this scenario and the realistic gain is probably around 15 per stage.

  • Dear Mr. Wong
    Can you help me to translate your code for Mega 2560 board,
    I am pretty new guy in this area (software)…
    I have started to learn about Timers, Interrupts but I need time to understant these.
    Can I use Timer3 on Mega; because on Timer1 I have an 20×4 LCD? (I can use PWM8 and9)
    Please specify again: PWM8 goes to TX and PWM9 toRX?
    The RxTx ultrasound board is ready and seems to work but I need the signal from Mega!

  • hi Sir,
    I am doing a project in Germany, and this project needs to have sensors to detect the presence of vehicles and any moving object inside at least 1 kilometer. Do you think using a high frequency ultrasonic sensor would help?
    Thanks anyway for your article, it was highly informative.

    • Using a higher frequency transducer can increase the resolution due to the shorter wavelength, but I don’t know if there are any civilian ultrasound transducers that could handle such long distance (1 km) though. This type of long distance object detection is usually done via radar not ultrasonic sensors.

      • ow… how do you that 1 km ultrasonic sensor? that’s nice, that may help me to our research… can i know how you do that?? thanks…

  • do you know what’s the maximum range you were able to measure? I’m trying to build a cusotm tape measure, like the Stanley used to measure house rooms… so if it could work more than 20 feet would be great…

    • For my particular build, the range was right around 20 feet. The actual range depends on the particular transducers you use and also depends on the driving voltage.

  • Hello
    Beautiful piece of code
    I found out that you also use the pin 9 to generate the signal. Maybe this was designed to feed the H bridge to maximize the signal.
    Maybe you don’t use it because the left most transistor act as inverter ?
    I don’t have a bridge yet and I am trying to use you code to feed an audio piezo directly with pin 9 & 10
    I have an old single channel oscilloscope and when I use a duty cycle different than 0.5 and can see that the signal from pin 9 & 10 are “mirrored”
    If I use a duty cycle of 0.5 no sound are produced because the sum of the signal equals zero
    Is this a flaw ?
    can you update the code to have the two signal in opposition when the duty cycle is 0.5 ? (to complex for me to do)

    Thanks a lot


    • Hi Deladriere,

      Sorry I didn’t get a chance to get back to you earlier. I wouldn’t be able to assist you with your particular request as I simply don’t have the time to do that, I got lots of similar requests from users directly. I would suggest that you post your questions on Arduino forum and you might get your answer there.

      Good luck!

  • hello, Mr Kwong
    i have searched a lot for designing an ultrasonic distance finder.
    m having a pair of sensor(40khz) planing to implement your design but unable to figure out dis things
    >do this sensor have polarities i mean which terminal should be connected to ground an all
    >where should i put d decoupling capacitor in d transmitter circuit(that too m not able to figure from your image)
    >you have updated a Power Supply Decoupling circuit do i need to build that also to make dis thing work
    >i m using atmega32 can i give a 2ms pulse from a pin directly to the transmitter and planing to receive the o/p of receiver in another pin (i hope i will be getting o/p as logic 1 or 0)

    i will posting more doubt as i progress looking forward for your guidance

    • Hi Sumit,

      Ultrasonic sensors usually do not have polarity and can be driven either way (think of a speaker).
      The decoupling capacitor should be placed as close to your circuits (e.g. near the Vcc pin on Atmega). The power supply decoupling is not strictly necessary. It depends on how good your power source is. If you are not using the same power source to drive large motors, you probably don’t need it.

      You can’t use the MCU pin to drive the ultrasonic sensors directly as it does not provide enough power and may damage the MCU as a result.

      Hope it helps.

      • Thanks MR Wong for d reply
        >2ms pulse from a pin directly to the transmitter……
        i mean making a pin 1 den providing 2ms delay ans again making it 0.(without using timer/pwm)
        and giving dis as a input to your transmitter circuit.
        >can i use diode IN4007 instead of IN4148
        >m using OP LM324 in receiver coz m nt getting LPC662 in my area
        the hardware part is done working on code rite nw

  • small question can i use the R/t 40K sensors directly with the microprocessor (pic16f877a) and is there a difference between the terminals of the sensors positive negative if there is how can i tell them apart , the terminals are the same height . Thank you :)

  • hey. i am using 40khz transducer and you have said that you have used capacitors to reduce noise effect which are not shown in schematic kindly tell me their connections or if you can publish the schematic with capacitors connected as well. secondly tell me that for 40kh the h bridge’s transistors would remain same or not.

    thanks alot
    waiting for reply

    • The bypassing capacitors added on the H-Bridge board are simply small capacitors (e.g. 100nF) between Vcc and Gnd. The same circuit can be used to drive 40KHz transducers without any issue.

      • sir I am using PIC18f877a microcontroller to generate 40khz pulse which I will use to drive my transducer. It means that I dont have to make any changes to H-Bridge circuit and receiver circit as well.

        thanks for your reply sir.

  • HI Mr Kerry Wong,
    I am doing a project on ultrasonic flow meter, specifically measuring blood flow flowing through the hemodialysis tube. I was given a pic18f2520. For the transmitter part, I managed to write a simple code to transmit 40khz to the transmitter (pwm function). Then I measure the receiver part using the oscilloscope. Thankfully it reads 40khz on the oscilloscope.

    But now, the hard part is the receiver. I am aware that signal received is very weak hence need op-amp to amplify the weak signal. I am not sure how to patch up the op amp, and what value of the resistors. I did google on ultrasonic receiver schematic but what I got is receiver for different use (distance measurement, motion detector,etc) and didn’t use pic controller which I think might be no use for my purpose of this project.

    My question is:
    How do I patch up the circuit for the op amp? and maybe for the filter based on 40khz?
    If I manage to get the receiver working, I will test this on plain water first, then on blood so is 40khz is efficient for blood flow measurement?
    If I manage to get the receiver working, how do I write the program codes on how to capture the signal at one time, or within a fixed/given period, or even instantaneously?

    I will be greatly appreciate if I atleast manage to get the idea from your explanations.

    Thank you

    Diploma in Biomedical Informatics Engineering
    Temasek Polytechnic

  • hey kwong

    i have a qustions…can i use this receive circuit for my 40 khz transducter and uP atmega 32
    on ATMega would be possible to bring the size of rectangular pulses of 5V

  • Hey Kerry,

    I’m unable to get any form of output from the Digital pin 10 of my Arduino Mega Board. I check using a scope, could you please help? Is something wrong with the code? I’m using an Arduino Mega

  • I checked all the digital pins as well and I have no output from any of them. I honestly did not understand your code and I am new to microcontroller programming, if you could comment your existing code or write a code using the arduino library to help me out I would be really grateful.


    • If I remember correctly (please double check the datasheet to make sure), the timer 1 pins are different on the mega board. Instead of 9,10 they are 11,12. So you will need to set the corresponding pins to output mode in your setup routine. Instead of using

      pinMode(9, OUTPUT);
      pinMode(10, OUTPUT);

      you would use

      pinMode(11, OUTPUT);
      pinMode(12, OUTPUT);

      Since I don’t have a mega board, I couldn’t try it out…

  • Thanks Kerry, That helped me with the Transmission aspect of the sensor. Do you know if I need to watch out for something on the receiving side ? I’m using an LM358 as the OP-AMP instead of the LPC662, will that be a problem?

    • Since you have a scope, it would be relatively easy to tune the receiver side (e.g. adjust gain and filter frequency, etc.) I think you should be able to use LM358 for this.

      • Thanks for your response Kerry. Currently I modified the circuit on the receiving side a bit (I added a schmitt trigger) and the output I am getting is a square wave. The time taken for the square wave to show up on the scope depends on the distance between the two transducers (both are facing each other now)i.e if I keep them close the receiving side waveform starts near the end of the transmitting pulse. Could I use your arduino code to get the distance? Please let me know if I am on the right track.


        • Sounds like everything is working properly. Yes, you can use the same code to calculate the distance as that portion of the code is independent of the hardware.

  • Hey Kerry,

    Although I’m able to send and receive a square wave of 40khz and the amplitude of both waves is 5 volts I’m not getting any distance measurement right. I’m always getting a value of 0.19 printed out in the serial monitor. Could you help me out with this problem?

    • Hmm… the algorithm I used was pretty simple, it basically uses the largest value in the echo envelop to determine the time delay. My guess is that your transducer does not decay as fast as the one I used so if you increase the delay (right after t_peak = t_start;) slightly, you should be able to see your calculated value change.

      The draw back is that the minimum detectable distance would also increase slightly.

  • Hello Mr. Wong,

    You mentioned that the ultrasonic transceivers you purchased were around a dollar a piece. I am trying to make an ultrasonic distance sensor for the same reason as you, but am having no such luck in finding cheap components. If you can remember (I understand this was posted over a year ago), would you mind sharing which transceivers you purchased, and where? I apologize if you already posted this information and I have overlooked it.



  • Question on the transmitter,The output I am getting (transducer terminals) is not the same as you. It doesn’t double, nor does it produce a neat square wave as it does for you. I’m using a function generator instead of the arduino digital pin and giving a 5Vpp square wave of 24kHZ.

  • Hello Kerry, thank you for sharing this project.

    I want to make this too, and in my project i use 40 kHz transducer. Will this affect the demodulator circuit?
    Also in your project, is it true that the lowpass filter cutoff frequency is about 60 kHz, and the highpass filter in the second stage cutoff frequency is 1.3 kHz? If it’s true, how do you pick these values? (Because I think the bandwidth should be around 20-30 kHz, because yours is 24 kHz)

    Thank you, great tutorial!

  • Hey, I am trying to design this circuit and I am looking at the oscilloscope and I am getting transitions like yours on the transmitter however the voltage is high the whole time to 5V then transitions from 0V to 5V. Yours is 0V then transitions from 10V to 0V. I am using a VCC of approximately 5V and on my oscilloscope I am only getting about 950Hz. I am measuring one leg of the transmitter I hope that is correct. Also I have triple checked the circuit and replaced all the transistors just in case. What do you thiink could be causing this issue?

    • Hi Stephen,

      Without seeing your circuit, it’s a bit difficult to say. But before measuring the h-bridge’s output, you should be able to see the PWM signal from the MCU pins with the correct frequency, could you verify that? If you can see the 20kHz (or whatever frequency your transducer operates on) PWM wave on the MCU pins, then I’d check the H bridge.

      • I am able to see the 40KHz frequency that I want from the arduino. I then looked again at the input of the transducer and I see the 40kHz however I am still only getting VCC as my volts. I then looked into the recievers output and I am getting just noise. I am going to look further into the reciever to see if I can find whats happening. The only thing I changed was the resistors from 80K to 100K and a op amp LMC662CN

        • I looked more into the transmitter side to see if I can find out why it isnt putting out ~10V. I cannot really find a reason why it isnt. I am measuring on one of the legs of the transducer. Is that correct? I am using a arduino uno with the 10 pwm and a vcc of 5v with all the same components as yours and in the same configuration. I understand how the circuit works but dont you need more voltage at the 3906 base then there is at collector? Is there any debug that you can help me with?

          • OK, let’s take a step back. Try measuring one side of the bridge first (where the to collectors of the NPN/PNP are connected). That point should show a PWM signal with roughly 5V Vpp (to ground). And if this works, the other side of the bridge should have a similar 5V Vpp PWM signal. These two signals are of opposite phase and thus the voltage across the transducer would be roughly 2xVpp which would be 10V.

            In order to make sure that the signal phases are reversed, an additional NPN is added to one side of the bridge to drive the base so at the very least, you should see the driving signal at the junctures of the 1.5K resistors to be opposite phase PWM signals.

          • Kwong is on the right track. The 10V you see depends on how the scope is hooked up. You will only see 0V/5V transitions with “measuring one leg of the transmitter”. You have to hook up your scope scross the two leads of the transducer and use DC coupling (if you want an identical pic to the one shown). Make sure then scope’s ground is isolated from the circuit’s ground! (ie: circuit is powered by battery or an isolated power supply) otherwise you will ground out the point you are measuring (bad).

            In simple terms, the circuit just connects one lead of the transducer to Vcc, the other to Gnd. Then, when the PWM signal changes phase, the ultrasonic lead that was connected to Vcc is now grounded and the other previously grounded lead is now connected to Vcc. As mentioned, this causes the transducer to see +/- Vcc which gives the effect of doubling the voltage. If you connected your scope to the circuit’s Gnd then you won’t “see” what the transducer “sees”.

  • I have finally got back around to looking at the circuit. I have the transmitter side showing the same waveform as you know. So I can check that as ok. I have moved on to the reciever and the changes I have made to the circuit is I used a 680 Ohm instead of a 1.2K and a 100K instead of a 80K. Which that should be fine. I checked the output of the first bandpass and I get no noise and a good but a weeker signal. I check the output of the other op amp and I am having some very bad noise. Any suggestions

  • Hello Mr. Kwong.


    I have one question.
    Currently, I am doing a project regarding on the development of driving circuit for ultrasonic transceiver sensor on the application of distance measurement.
    By referring from your website, you use a pair of ultrasonic sensor (transmitter & receiver) for the same application.
    The question is. Can I use a single ultrasonic transceiver sensor by using the driving circuit that you provide from your website. Is there any modification that need to be done.

    Thank you.
    Kindly waiting for your response.

    • Using a single ultrasonic sensor to transmit and receive is possible, but will require a completely different design of the circuit. Since the circuit must switch the sensor between transmitting and receiving mode.

  • Greeting Mr Kwong

    Mr kwong your design has helped me allot in understanding the design I am required to do.
    I am building a liquid level indicator using the arduino.,
    My Question, is it possible to get maximum range of 1 meter and maximum detectable distance of around 1cm? basically a measurement of closer objects or smaller ranges. What is it that I would have to change in your design

    Regards Sam

    • Hi Sam,

      I assume you wanted a minimum distance of 1cm. This would be a bit difficult to do as the the time interval for the sound wave to bounce back is around 30 us, which is extremely short compared to the decaying characteristics of most ultrasonic sensors.

      Also, at 1cm you are approaching the wavelength of the ultrasonic sensor (even at 40khz, it’s very close to 1cm) and you will not be able to get the resolution you needed unless you resort to a higher frequency ultrasonic sensor.

  • Now I do have a complete driving circuit to activate ultrasonic transceiver sensor at frequency 40 kHz.

    I would like to ask you a question, do I need to make some modification or any changes on the driver if use ultrasonic transceiver sensor at different frequency (probably at 390 kHz or above) or the same driver can be used for any range of frequency

    • You would need a higher bandwidth opamp for that and also you would need to recalculate the RC filters to ensure that the correct frequency range is band-passed. But the basic principal remains the same.

  • Mr Kwong I want to measure short distances can you please tell what exactly would i have to modify in your design to achieve a shortest measurable distance of 5cm or 10cm and range not long.I am using 40Khz Transducers.

    • Measuring short distances with an ultrasonic sensor can be tricky. The main constraint is that the even after you stop transmitting, the vibration of the transmitter does not stop immediately but would decay over time. And this effect can interfere with your detection logic. To reduce this interference, you would typically reduce the transmission power and make the pulse shorter.

      Also, as the distance becomes closer, you will need shorter pulse trains so that you do not end up receiving the reflected pulses while you are still transmitting. Also as I mentioned in answering someone else’s question earlier, the distance resolution you get is typically limited to a few centimeters because of the wavelength of the transducers and 10cm is probably the closed you can achieve.

  • Hi,

    I am trying to do the same but with 40khz transducer doing both send and receive … and in fact, I want to use 2 of them :
    A send to B
    and then reverse
    B send to A

    and I compare the timing …(should be the same)
    as you said : “In my design, I used separate transducers for transmitter and receiver. It is possible to multiplex the transmission and receiving with a single transducer (e.g. Maxbotix range finders), but the design would be significantly more complex.”, I found this that is close to what I try to do : http://www.maxbotix.com/documents/MB1000_Datasheet.pdf

    you can see there schematic on page 2 but …. it’s not working, I also tried this one : http://www.hexamite.com/hx40str.htm
    the ATMEGA sample …
    with no luck.

    I know that I feed a 40khz pulse to the US, but the other one don’t get anything …. (I would love a version that light up or make a sound when it send or receive, that would save my life …).

    If you have any sample on how to achieve a : one send, the other receive and then reverse, I would appreciate !


    • well that’s precisely why you should use an opamp
      i got the same here, with scoping the pins of the receiver, i got a really weak signal and i can’t tell if it is noise or actual signal from the emmiter

  • Thanks for the great post. A couple of short questions:

    1. What about the idea of using a difference amplifier on the receiver transducer?

    2. Why use the H-Bridge versus a transistor-based push/pull amplifier?

    Or more specifically, what would the pros/cons be of the above ideas.

    Thanks again,

    • Thanks Mike. Differential amplifier on the input would increase the common mode rejection ratio and improve SNR. In theory, an instrumentation opamp would be an even better choice since it has much higher input impedance besides being fully differential, of course these solutions will add to the BOM.

      Regarding the second point, a bridged output stage can achieve higher output power as the output voltage can be driven to 2Vcc (across the transducer) whereas the maximum output voltage of a push-pull stage is at most Vcc.

      • Kerry,

        Thanks for your response. That’s a great point regarding the HBridge; totally should have thought of that.

        Have you ever thought of winding a custom transformer for the transmitter to bump the voltage up to max of the sensor?

        I’m research how to do this for a 200khz underwater transducer, but for some reason there just isn’t a lot of reference circuits on the interweb :)


    • A3901 will definitely work (a bit of an overkill though), and the op-amp may work, but the GBP is 0.8Mhz which is even smaller than the op-amp I used (technically speaking, you really need an op-amp with higher GBP than what I have used), but it may work for you depending on the transducer you use.

  • hello sir,
    i read out your article and understood the basic function of all your components used. i am having a query about the brown coloured device(between the 4 BJT) in the middle of your h bridge. please do reply asap.. thanks for sharing the information.

    • TL072 is an really old op-amp. It is typically operated using dual power rails of 18V. So it will make the circuit a lot more complicated if you use TL072.

  • So which opamp from texas instrument would you recommend other than LCP662? cOZ i cant find anywhere the LCP662. tHANKS

    • Try LM358/LM324, they are very common and I have heard feedback that they work. Since the design was based on OpAmps with higher gain-bandwidth-product in mind, your mileage may vary…

  • Hi Kwong

    I hope you can help me on this one.

    My circuit seems to be working, but my problem concerns the serial monitor. The results are displayed very fast, they pass through the scrren very fast. Isn’t there a way for me to make the results be displayed very slow indeed. So I can be able to know what is the actual distance?

    • lol
      this is more about programation skills than digital electronics
      you do an average of the data you get
      or you put a delay in your main loop

  • Hello Kerry

    Thank you very much for this intro on ultranonic transmitters and recievers, the electronics and the code.
    I’t is a great starting place on the subject. Short, instructive, and functional. I will try a modified version of your circuit and code for wind speed measurements.

    One question thought? Why didn’t you use external interrupts. They are an elegant replacement for the clumsy for loop.

    Thank you again and all the best!

  • Sorry I write before thinking, and the hour is late :-)

    OK I was talking about a digigital interrupt trigered at a rising edge whilst you are searching for t(max_voltage). I guess these two are simillar but not completely interchagable.

    The “professionals” (so I read) do a convolution of the transmitted and received signal. Convolution involves a relatively simple operation on two data sets, but the data sets are in principle prohibitevely large for the atmega328P. This method is much more accurate, because it gathers information from more measurements not just one point as you do it.

    There are two tricks you could do:

    1. you could do a measurement the way you do it, to get the approximate time of flight and then sample only at times where you expect the signal. You pad the missing time with zeros.

    2. you sample all the time but “remember” only the last N data until you see the filtered envelope. From that time on you sample till the envelope ends. You do the convolution with the padded zeros.


    • Hi Peter,

      Thanks for your comments. Yes the convolution method you were referring to is the so called matched filter which in theory will give you better results if implemented correctly. For a simple range finder like this one though, I don’t know how much realistic performance gains you will be able to achieve. But it would definitely be interesting to try out.

  • Thanks for the link! I tried it out and checked the signals on the oscilloscope…For a wind meter I will need something more acurate as 1m/s wind resolution means ~0.003 relative change in TOF. For a range finder, I agree, it’s enough.


  • hello,

    i have two 40khz t & r
    i was wondering if the chip referenced on this page would work

    what do you mean by GBP ?

    cant find the LCP662 anywhere too

    can you guide me for this ?


  • after testing your code, the starttransducer wich use timer 1 does not output anything on pins 9/10 althought they are set as output

  • Kerry,

    I’m working on a prototype not that unlike yours. However, one of the things I want to do is capture multiple echos on each ping and then display them on a fishfinder display (depth and greyscale for strength.

    Below is the code I came up with. So far the timing works well and it seems to be capturing analog conversions and filling my echo structs. But, one ting I wondered about is whether there would be a way to get a precise sampling rate on the analog input vs just polling it for a period of time? If the ADC is setup at 125khz, does that mean that I can count on my samples as being converted at that rate? Or in free running mode does the sample rate differ?

    // Firmware for AVR Sonar
    // first version 40 khz in air

    #define ARRAY_SIZE 40

    // Put this section in pings.h
    typedef struct _echo {
    int echo_strength;
    int echo_depth;

    typedef struct _ping {
    Echo ping_echos[ARRAY_SIZE]; //array to hold echos
    int gps; //to be used when adding GPS functionality

    // Put this section in pings.h

    //#define F_CPU 16000000UL //

    #include “Arduino.h”
    #include “pings.h”

    #define t1 1000
    #define t2 1000

    volatile unsigned long transmit_time = 0; //- time of echo
    volatile int timeout = 500; // user adjustable in the future
    volatile int echo_threshold = 1; // user adjustable in the future
    volatile unsigned long time1; // task timer
    volatile unsigned long time2; // task timer
    volatile boolean transmitted = false;
    Ping new_ping;

    void startTransducer(void); //used to start transmit
    void stopTransducer(void); //used to stop transmit
    long mirosecondsToInches(void); //used to calculate echo depth
    long microsecondsToCentimeters(long microseconds);
    void initialize(void); //initialize timer and adc
    int readADC(void);
    //Ping capture_Ping(void); // capture_Ping
    void capture_Ping(void); // capture_Ping
    void serialDump(Ping);

    //timer 0 compare ISR
    ISR (TIMER2_COMPA_vect){
    //Decrement the three times if they are not already zero
    if (time1>0) –time1;
    if (time2>0) –time2;

    // Serial.println(“Task One”);



    void loop(){

    // digitalWrite(13, HIGH);

    if (time1==0){
    } //run task when ready

    if (time2==0){
    //digitalWrite(13, LOW);
    // serialDump(png);

    // Serial.println(png.ping_echos[0].echo_depth);



    void setup(){

    Serial.begin(38400); // set up Serial library at 9600 bps

    Serial.println(“Sonar is starting up…”);

    // digitalWrite(13, HIGH);

    //initialize timer and adc

    pinMode(9, OUTPUT); // set pin to input
    pinMode(10, OUTPUT); // set pin to input
    pinMode(13, OUTPUT); // set pin to input
    //digitalWrite(13, HIGH);


    TIMSK2= (1<<OCIE2A); //turn on timer 0 cmp match ISR
    TCCR2B |= (1<<CS10); //set prescalar to divide by 64
    TCCR2A= (1<<WGM01); // turn on clear-on-match
    OCR2A = 249; //set the compare reg to 250 time ticks

    time1=t1; //init the task timers
    time2=t2; //init the task timers

    ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); //setup ADC with 125khz
    ADMUX |= (1 << REFS0); // set reference voltage to five volts
    ADMUX &= ~(1<<REFS1);
    ADCSRB &= ~((1<<ADTS2)|(1<<ADTS1)|(1<<ADTS0)); //ADC in free-running mode
    ADCSRA |= (1<<ADATE); //Signal source, in this case is the free-running
    ADCSRA |= (1 << ADEN); //enable ADC
    ADCSRA |= (1 << ADSC); //start conversions
    sei(); // crank up the interrupts


    void serialDump(Ping dumpPing){
    //put code to output values
    //digitalWrite(13, HIGH);
    Serial.println("Task Two");
    //digitalWrite(13, LOW);

    for (int i=0; i< ARRAY_SIZE; i++){

    // Serial.println("Task Two");

    // Serial.print("Echo:");
    // Serial.print("Depth:");
    // Serial.print("Strength:");

    // digitalWrite(13, LOW);


    int readADC(void){
    return ADCW;

    void capture_Ping(void){

    Ping current_png;

    //initialize echos in current Ping

    for (int i=0; i< ARRAY_SIZE; i++){
    current_png.ping_echos[i].echo_depth = 0;
    current_png.ping_echos[i].echo_strength = 0;

    Serial.println("Task One");

    int ping_counter = 0;

    startTransducer(40000.0, 0.5); //create ping of 40khz
    // delayMicroseconds(300);

    transmit_time = micros(); //beging time count
    // Serial.println(transmit_time);

    // listen on adc until timeout value (transmit time + X milliseconds)

    unsigned long ping_elapsed_time = 0;
    unsigned long time1 = millis();
    unsigned long time2 = 0;
    unsigned long total_time = 0;
    int echo_counter = 0;

    //for (int i=0; i<5; i++){
    while (total_time echo_threshold){

    unsigned long echo_time_two = micros();
    unsigned long echo_time = echo_time_two – transmit_time;
    // Serial.println(echo_time);
    unsigned long depth = microsecondsToInches(echo_time);
    // Serial.println(echo_counter);

    if (echo_counter 0.5) dutyCycle = 0.5;
    else if (dutyCycle < 0) dutyCycle = 0;

    TCCR1B = _BV(WGM13) | _BV(CS10) | _BV(ICNC1);
    //f0 = fclk / (2 * N * Top)
    long topv = (long) ((float) F_CPU /(freq * 2.0 * 1.0));
    ICR1 = topv;

    //target timer count = (1/target frequency) / (1/Timer Clock Frequency) – 1

    OCR1A = (int) ((float) topv * dutyCycle);
    OCR1B = (int) ((float) topv * (1 – dutyCycle));
    DDRB |= _BV(PORTB1) | _BV(PORTB2);
    TCCR1A = _BV(COM1A1) | _BV(COM1B1);

    void stopTransducer()

    TCCR1B = 0;
    // sei();

    void delayMilliseconds(int ms) {
    for (int i = 0; i < ms; i++) {

    • For more precise timing you can use interrupt. Whether or not the data can be processed with the interrupt interval depends on the complexity of the code. But at 125kHz, you are only going to get less than 10 us processing time which translates into maybe only a dozen simple statements.

  • Good day!

    I am new with arduino and ultrasonic circuit. I am currently using your circuit (but I replaced some components as they are not available here in my country), but I’m getting random values for the distance. One of the components I’ve replaced is LPC662. I used a LM358 instead. Is that ok?

  • goodday kwong, i am working on a thesis project, its a white cane using a ultrasonic sensor(us-100) with a arduino uno rev3 board.. Each time the user(a blind person) comes close to an object, the sensor detects the object and advice the user to either turn left of turn right provided dere is no obstruction.my question is
    “how do i convert the distance measured by the sensor in voltages levels to enable me activate the alarm(buzzer) at different levels”???? thank u

    • Hi Chris, if I understood correctly what you wanted to achieve is change the sound interval depending on the distance measurement?

      • yes that is it…..using an ultrasonic sensor(us-100) for the distance measurement and a buzzer(cause we culdnt find a voice ic in dis area) as the output for sound……using arduino uno…….

        • Here’s a simple way to do this (pseudo code below). Basically, you want to make your beeping interval dependent on the distance (take a look at the variable soundOnDuration).

          void setup() {
          ts = millis();

          void loop() {
          distance = UltrasonicSensorDistance;
          soundOnDuration = distance * factor; //the delay between the beeps
          if (millis() – ts > soundOnDuration) {
          soundOn=!soundOn; //soundOn is a boolean

          if (soundOn) {
          turn on the speaker;
          } else {
          turn off the speaker;
          ts = millis(); //reset the timer

  • Hi

    Thanks for your great work,
    I build the circuit exactly as you have, it runs great at 5V, but I wanna run it at higher voltage to get better range, I changed the transistors to TIP31c and TIP32c with 220 case, but they got to hot at 18V, what do you think is the problem?


    • You might want to check a couple of things.

      1. make sure that the transistors are fully saturated when switched on. If they are in their linear regions, the power dissipation could be significant.

      2. make sure that the driving waveform is clean (if the slew rate is too low then the transistors will stay in linear regions longer and possibly causing both the PNP and NPN on both side of the bridge to conduct at the same time).

      • Thx,

        I have one more question, I’m trying to reach a 60′ range (not successful so far), which one will go farther, 40kHz or 25kHz?


  • kwong. ur project is really amazing. pls i need the value of the resistance and capacitor for the resonance circuit using a 40KHZ transmitter.

    also. my serial com is displaying too fast. i know i need to add a dela. but i dont know where to add the delay. so it wont affect the frequency of the arfuino output to the transducer.

    can the transmitter be used for the receiver and vice versa??? and why is the output on the serial board displaying 0.02 value. what unit is your program measuring wit. Cm or M… thanks so much. and any one aside kwong who knows should pls help out. am a final year student. and my project has really been giving me hard time. i will also luv to make a friend who i can chat wit. and he can put me through every step.

    • Hi Victor,

      Thanks for your comment. Regarding the RC filter values, you can use the equation f=1/(2*pi*R*C) to calculate. The display unit is meter. Whether or not a transmitter/receiver can be interchanged depends on the manufacture of the sensors. In general, they are interchangeable.

      • Thanks sir. What about the speed of the serial com display, how can I slow it down to track down the measurement well.

        Also, I noticed you used 24khz transducer but your resonance circuit gives a reading of over 80khz. can’t I use, the circuit since it is likewise above 40. Or what value of frequency do I.need to achieve the desired resonance

        Lastely sir, I used 100 micro farad decoupling capacitor at the output the receiver. This is because the 1 micro I your circuit wasnt giving any output. can I.have your view and suggestion any this sir.

        • Hi Victor,

          You could add a delay after the display statement while debugging.

          Yes, the RC values I gave should also work for 40kHz sensors, but you definitely need to change the code to generate the pulses at 40kHz instead of 20kHz.

  • Kerry. Thanks alot. I have one last challenge,

    the serial board is not displaying right value, even at constant distance, the variation in value is very high. I can’t maintain a constant value pls what could be wrong?

    • Sounds like the received signal is not clean. This could be caused by interference from a motor or wiring. But you should be able to observe the waveform using an oscilloscope at output of the op-amp.

Leave a Reply

Your email address will not be published. Required fields are marked *