Freesteel Blog » Ultra sonic anemometer plausibly solved on an Arduino

Ultra sonic anemometer plausibly solved on an Arduino

Wednesday, September 16th, 2015 at 7:40 pm Written by:

Following on from last week’s episode and the Emil’s sterling work reverse engineering the HC-SR04 echo location board, JD tapped in to a couple of pins on the surface mounted microcontroller so we could get the echo signal directly.


I’ll try to report this as straightforwardly as possible without too many diversions about how we worked it all out.

With two HC-SR04s facing one another, and as per the datasheet, the scope tracing is as follows:
Cyan is the trigger to device A, Magenta is the 40kHz 8-cycle wave generated by device A (delayed to allow the MAX232A chip to charge up), Yellow is its corresponding echo pin which is programmed to go high once the 8 cycle signal goes out, and Blue is the received signal in device B. [The corresponding wave generated by device B is received at the same time by device A which is programmed to take its the echo pin (yellow) low.]

Zoomed in on the initial cycle to see there are 8 and that their wavelength is four to a vertical division, or about 25microseconds each, corresponding to 40kHz. The transducers probably resonate at this frequency (to give a better energy to sound conversion) which is why so many more waves are received than sent out.

This is the received signal, overlaying the half-second of samples (about 10 because I’m repeating the loop with a delay of 50milliseconds) proving that it’s pretty stable.

By zooming in on a single wavelength and averaging over the previous 64 we can make it dance with the application of the fan

Device B (the receiver) is the one by the oscilloscope. By directing the fan along the sound vector we’re able to blow back the sound signal by approximately half a wavelength or about 12microseconds.

I’ve spent hours playing with this setup, but it’s only going to be any good if we can read the values using a microcontroller, like an Arduino.

After a certain amount of looking at pulseIn() function and hard-coding the pin ports, we have code that is as follows.

First this is the global array of 16 time measurements:

unsigned short ws[16]; 

We are going to log each event in this array by counting loops, and use the fact that 65536 provides a very useful upper limit to trap for overflows in our loops so we don’t crash. See below that we bail out when (c++ == 0) in the loop.

Now set the trigger pins on both HC-SR04s HIGH for 10microseconds, as required in their instructions.

PORTB &= 0b11111100; 
PORTB |= 0b00000011; 
PORTB &= 0b11111100; 

Now the loop to detect the 8 cycle signal (in magenta):

unsigned short c = 0; // 2 byte counter
unsigned short* pws = ws;  
do {
    do { if (++c == 0)  return; } while ((PIND & 0x04) == 0); // pin 2 LOW
    do { if (++c == 0)  return; } while ((PIND & 0x04) != 0); // pin 2 HIGH  
    *pws = c; 
} while (pws != (ws+8)); 

And finally the loop (running on from the above) to detect the first 8 cycles (in blue)

do {
    do { if (++c == 0)  return; } while ((PIND & 0x40) == 0); // pin 6 LOW
    do { if (++c == 0)  return; } while ((PIND & 0x40) != 0); // pin 6 HIGH
    *pws = c; 
} while (pws != (ws+16)); 

Let’s have a look at that first do-while block in assembly code, because something amazing was done by the compiler:

     ldi  r30, 0x16  ; ws -> Z-register (low byte)
     ldi  r31, 0x01  ; ws -> Z-register (high byte)
     ldi  r24, 0x00  ; c = 0 (low byte)
     ldi  r25, 0x00  ; c = 0 (low byte)
166: adiw r24, 0x01  ; increment c by 1      (2 cycles)
     breq .+54       ; branch if c == 0 to 1a0 (location of return statement) (1 cycle)
     sbis 0x09, 2    ; skip if bit in I/O register is set !!!! (1 cycle)
     rjmp .-8        ; jump to 166 (2 cycles)

16e: adiw r24, 0x01  ; increment c by 1
     breq .+46       ; branch if c == 0 to 1a0 (return statement)
     sbic 0x09, 2    ; skip if but in I/O register is clear !!!!
     rjmp .-8        ; jump to 16e
     st Z+, r24      ; *pws = c; ++pws (low byte)
     st Z+, r25      ; *pws = c; ++pws (high byte)

     ldi r18, 0x01   ; (ws+8) high byte
     cpi r30, 0x26   ; (ws+8) low byte
     cpc r31, r18    ; compare with carry high byte (how to compare two bytes consecutively)
     brne .-28       ; branch not equal to 166
1a0: ret

I have no idea how the compiler got clever enough to discover those SBIS and SBIC commands, which can only be applied because it could see only one bit set in the (PIND & 0x40) statement (bit 2, in fact), but it’s amazing.

What this means is the loop is only 6 processor cycles long, or 6/16=0.375microseconds, which is a pretty good resolution to be working with.

Unfortunately, because it’s the same precision we’re measuring the signal going out as going in, we have to double it to 0.75microseconds that we can measure the duration of sound travels.

The only way to get back to the 0.375 precision is if we ping off the HC-SR04’s microcontroller and generate the wave from the MAX232A directly, because then we’ll know exactly when it’s going out.

Now to look at the output, by running the function that shows us the timing differences:

for (int i = 1; i < 16; i++) {
    Serial.print(" "); 
    Serial.print(ws[i] - ws[i-1]); 

We get lines like:

721 67 67 66 67 67 67 67 4625 67 66 67 65 65 65 64 
722 67 67 66 67 67 67 67 4623 67 66 66 66 66 65 65 
719 66 67 67 67 67 67 66 4624 67 66 66 66 65 65 65 

which are pretty stable.

But do the numbers add up?

Well, 67 loops amounts to 67*6=402 processor cycles which, for a processor running at 16megahertz comes to 25.125microseconds, or 39801 cycles per second, which is 0.5% of being exactly 40kHz.

The travel time between the last impulse being sent to the transponder and the first wave being received is 4625*6/16=1734microseconds, which is the time it takes sound to travel 59cm. The distance between the bottom of each can is 58cm.

Now, it probably takes a few waves to get the transponder to get up to power, and I note that the echo signal (the yellow) line in the oscilloscope trace begins after a slight pause from the last of the 8 waves. This could easily be a tuning issue.

Anyway, this is what happens when I graph that middle number while turning the fan on and off:

The vertical jumps are about 67 units when the microphone decides to skip the first few waves, probably because they've been weakened by the noise from the fan. It's a bit of a mess.

A much better result is if we plot the result as points several times, shifting in Y by multiples of 67 units. Then you can see how it's supposed to line up, if we got the wave front right.
The difference is about 29units, which means the sound arrived 29*6/16=10.8microseconds earlier below its usual 4625microseconds of travel in stationary air, which is a difference of about 0.2% in speed, meaning about 0.75m/s for the air from the fan.

So this is all looking very much like I could build something good here, subject to the problem of guessing the correct wavefront of the multiple coming through, but that's something I feel I can bodge in software. It would help if I used a shorter gap between my transponders.

It also seems plausible I should make a 3D anemometer from this. This could be done from a single 5V arduino mini if I'm careful with the pin count, even with controlling the microphones directly. It's all good fun.

The final result would be a cheap contraption (about £30 in parts) that I could bolt on to a spar poking out the nose of my glider in order to directly measure the airspeed glide angle and side slipping, which would be pretty neat.

In the meantime I've determined to shelve this project and get back to some of the triangular machine tool stuff.

I also haven't done anything productive with any of the data I have collected so far from the glider, so I feel I should do that before I allow myself to build any more devices like this sort of thing.


  • 1. Alfin Pangaribuan replies at 22nd July 2019, 1:45 am :

    Heyyy, i’m alfin.
    I interest with ur project about anemometer ultrasonic.
    how can i contact you? I want discuse about that.
    Thank you.

  • 2. Julian replies at 27th July 2019, 3:22 pm :

    I don’t know much more than I’ve put here. but you can get me on

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <blockquote cite=""> <code> <em> <strong>